From 0562cd7303ef0d0fccd40509ef0ba64df5578796 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 14 Sep 2020 15:30:47 +0200 Subject: [PATCH 001/197] ground state min eigensolver interface --- .../molecular_ground_state_energy.py | 4 + qiskit/chemistry/core/chemistry_operator.py | 9 +- .../ExcitedStateCalculation.py | 0 .../AdaptVQEGroundStateCalculation.py | 12 +++ .../GroundStateCalculation.py | 91 +++++++++++++++++++ .../MinEigensolverGroundStateCalculation.py | 72 +++++++++++++++ ...bitalOptimizationGroundStateCalculation.py | 12 +++ 7 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py create mode 100644 qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py create mode 100644 qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py create mode 100644 qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py create mode 100644 qiskit/chemistry/ground_state_calculation/OrbitalOptimizationGroundStateCalculation.py diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index 15a55c23b3..1bc044d2d5 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + # This code is part of Qiskit. # # (C) Copyright IBM 2020. @@ -29,6 +31,8 @@ class MolecularGroundStateEnergy: """ Molecular ground state energy chemistry application """ + #TODO this needs to be deprecated in view of the new Ground State interface + def __init__(self, driver: BaseDriver, solver: Optional[MinimumEigensolver] = None, diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index 37008fef1a..16f0e7cdc6 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + # This code is part of Qiskit. # # (C) Copyright IBM 2018, 2020 @@ -16,7 +18,7 @@ """ from abc import ABC, abstractmethod import logging -from typing import Union, List, Tuple, Optional, cast +from typing import Union, List, Tuple, Optional import numpy as np from qiskit.aqua.algorithms import MinimumEigensolverResult, EigensolverResult, AlgorithmResult @@ -153,6 +155,9 @@ class MolecularGroundStateResult(MolecularChemistryResult): Energies are in Hartree and dipole moments in A.U unless otherwise stated. """ + #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 """ @@ -233,7 +238,7 @@ 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)) + edm = tuple(-1 * x if x is not None else None for x in edm) return _dipole_tuple_add(edm, self.nuclear_dipole_moment) @property diff --git a/qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py b/qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py new file mode 100644 index 0000000000..10698d9467 --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py @@ -0,0 +1,12 @@ +#imports + + +class OrbitalOptimizationVQE(GroundStateCalculation): # same for VQEAdapt, ... + def __init__(self, params_for_mapping): + super().__init__(params_for_mapping) + def compute_ground_state(driver) -> GroundStateCalculationResult: + op = self._transform(driver) + # different implementation similar to VQE + result = None + # construct GroundStateCalculationResult + return ground_state_calculation_result diff --git a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py new file mode 100644 index 0000000000..3d030448f7 --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +# 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. + + +from abc import ABC, abstractmethod +from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, + ChemistryOperator, MolecularGroundStateResult) +from qiskit.aqua.operators import Z2Symmetries + + +# class GroundStateCalculationResult(): +# def __init__(self, results_parameters): +# self._results_parameters = results_parameters +# # param1 +# # param2 +# # param3 +# # add getters... cf. other results classes + + +class GroundStateCalculation(ABC): + """GroundStateCalculation""" + + 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 the qubit operators + that are computed. 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. + See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core + processing behind this class. + """ + self._transformation = transformation + self._qubit_mapping = qubit_mapping + self._two_qubit_reduction = two_qubit_reduction + self._freeze_core = freeze_core + self._orbital_reduction = orbital_reduction + self._z2symmetry_reduction = z2symmetry_reduction + + def _transform(self, driver): + """ + + Args: + driver: + + Returns: + + """ + # takes driver, applies specified mapping, returns qubit operator + + q_molecule = self.driver.run() + core = Hamiltonian(transformation=self._transformation, + qubit_mapping=self._qubit_mapping, + two_qubit_reduction=self._two_qubit_reduction, + freeze_core=self._freeze_core, + orbital_reduction=self._orbital_reduction, + z2symmetry_reduction=self._z2symmetry_reduction) + operator, aux_operators = core.run(q_molecule) + + return operator, aux_operators + + @abstractmethod + def compute_ground_state(driver) -> MolecularGroundStateResult: + + raise NotImplementedError() \ No newline at end of file diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py new file mode 100644 index 0000000000..6bbf4d7bba --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- + +# 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 Aqua minimum eigensolver """ + +from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, + ChemistryOperator, MolecularGroundStateResult) + + + +class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): + + def __init__(self, + solver: Optional[MinimumEigensolver] = None, + 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: + + self._solver = solver + super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, + z2symmetry_reduction) + + def compute_ground_state(driver) -> GroundStateCalculationResult: + + operator, aux_operators = self._transform(driver) + + aux_operators = aux_operators if self.solver.supports_aux_operators() else None + + raw_gs_result = self._solver.compute_minimum_eigenstate(operator, aux_operators) + + return core.process_algorithm_result(raw_gs_result) + + @staticmethod + def get_default_solver(quantum_instance: Union[QuantumInstance, BaseBackend]) ->\ + Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]]: + """ + Get the default solver callback that can be used with :meth:`compute_energy` + Args: + quantum_instance: A Backend/Quantum Instance for the solver to run on + + Returns: + Default solver callback + """ + def cb_default_solver(num_particles, num_orbitals, + qubit_mapping, two_qubit_reduction, z2_symmetries): + """ Default solver """ + 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) + vqe.quantum_instance = quantum_instance + return vqe + return cb_default_solver \ No newline at end of file diff --git a/qiskit/chemistry/ground_state_calculation/OrbitalOptimizationGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/OrbitalOptimizationGroundStateCalculation.py new file mode 100644 index 0000000000..26b06350eb --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/OrbitalOptimizationGroundStateCalculation.py @@ -0,0 +1,12 @@ +#imports + + +class AdaptVQE(GroundStateCalculation): # same for VQEAdapt, ... + def __init__(self, params_for_mapping): + super().__init__(params_for_mapping) + def compute_ground_state(driver) -> GroundStateCalculationResult: + op = self._transform(driver) + # different implementation similar to VQE + result = None + # construct GroundStateCalculationResult + return ground_state_calculation_result From 9460c4c793fb5981df21d5601160c69fb8f3f043 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 14 Sep 2020 15:40:06 +0200 Subject: [PATCH 002/197] fix small things style --- .../GroundStateCalculation.py | 13 ++-------- .../MinEigensolverGroundStateCalculation.py | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py index 3d030448f7..db799042db 100644 --- a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py @@ -18,16 +18,6 @@ ChemistryOperator, MolecularGroundStateResult) from qiskit.aqua.operators import Z2Symmetries - -# class GroundStateCalculationResult(): -# def __init__(self, results_parameters): -# self._results_parameters = results_parameters -# # param1 -# # param2 -# # param3 -# # add getters... cf. other results classes - - class GroundStateCalculation(ABC): """GroundStateCalculation""" @@ -86,6 +76,7 @@ def _transform(self, driver): return operator, aux_operators @abstractmethod - def compute_ground_state(driver) -> MolecularGroundStateResult: + def compute_ground_state(self, + driver: BaseDriver) -> MolecularGroundStateResult: raise NotImplementedError() \ No newline at end of file diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index 6bbf4d7bba..672533934c 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -20,6 +20,9 @@ class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): + """ + MinimumEigensolverGroundStateCalculation + """ def __init__(self, solver: Optional[MinimumEigensolver] = None, @@ -29,12 +32,31 @@ def __init__(self, freeze_core: bool = False, orbital_reduction: Optional[List[int]] = None, z2symmetry_reduction: Optional[Union[str, List[int]]] = None) -> None: + """ + + Args: + solver: + transformation: + qubit_mapping: + two_qubit_reduction: + freeze_core: + orbital_reduction: + z2symmetry_reduction: + """ self._solver = solver super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, z2symmetry_reduction) - def compute_ground_state(driver) -> GroundStateCalculationResult: + def compute_ground_state(self, driver) -> GroundStateCalculationResult: + """ + + Compute Ground State properties + + Returns: + GroundStateCalculationResult + + """ operator, aux_operators = self._transform(driver) From dd86427d88fb03314713eefd7d205789ffe25315 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 16 Sep 2020 10:54:04 +0200 Subject: [PATCH 003/197] added the molecule object with the right properties --- qiskit/chemistry/molecule.py | 306 +++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 qiskit/chemistry/molecule.py diff --git a/qiskit/chemistry/molecule.py b/qiskit/chemistry/molecule.py new file mode 100644 index 0000000000..89246143cf --- /dev/null +++ b/qiskit/chemistry/molecule.py @@ -0,0 +1,306 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 an interface for a generic molecule. +It defines the composing atoms (with properties like masses, and nuclear spin), +and allows for changing the molecular geometry through given degrees of freedom +(e.g. bond-stretching, angle-bending, etc.). +""" +import copy + +import numpy as np +import scipy.linalg +from qiskit.chemistry.drivers import PySCFDriver, UnitsType, HFMethodType + + +class Molecule: + + def __init__(self, + geometry, + degrees_of_freedom=None, + masses=None, + spins=None, + basis_set='sto3g', + hf_method=HFMethodType.RHF, + ): + """ + Constructor. + + Args: + geometry ([str, [float]]): 2d list containing atom string names + to generate PySCF molecule strings as keys and list of 3 + floats representing Cartesian coordinates as values, + in units of **Angstroms**. + + degrees_of_freedom ([callable]): List of functions taking a + perturbation value and geometry and returns a perturbed + geometry. Helper functions for typical perturbations are + provided and can be used by the form + itertools.partial(Molecule.stretching_potential, + {'atom_pair': (1, 2)) + to specify the desired degree of freedom. + + masses([float]): The list of masses of each atom. + If provided, must be the same length as number of atoms + in geometry. + """ + self._geometry = geometry + self._degrees_of_freedom = degrees_of_freedom + + if masses is not None and not len(masses) == len(self._geometry): + raise ValueError( + 'Length of masses must match length of geometries, ' + 'found {} and {} respectively'.format( + len(masses), + len(self._geometry) + ) + ) + + self._masses = masses + + if spins is not None and not len(spins) == len(self._geometry): + raise ValueError( + 'Length of spins must match length of geometries, ' + 'found {} and {} respectively'.format( + len(spins), + len(self._geometry) + ) + ) + + self._spins = spins + self._basis_set = basis_set + self._hf_method = hf_method + + @classmethod + def __distance_modifier(cls, function, parameter, geometry, atom_pair): + """ + Args: + atom_pair(tuple(int)): A tuple with two integers, indexing + which atoms from the starting geometry should be moved + apart. **Atom1 is moved away from Atom2, while Atom2 + remains stationary.** + function: a function of two parameters (current distance, + extra parameter) returning the new distance + parameter(float): The extra parameter of the function above. + geometry(([str, [float]])): The initial geometry to perturb. + """ + a1, a2 = atom_pair + startingCoord1 = np.array(geometry[a1][1]) + coord2 = np.array(geometry[a2][1]) + + startingDistanceVector = startingCoord1 - coord2 + startingL2distance = np.linalg.norm(startingDistanceVector) + newL2distance = function(startingL2distance, parameter) + newDistanceVector = startingDistanceVector * ( + newL2distance / startingL2distance + ) + newCoord1 = coord2 + newDistanceVector + + ending_geometry = copy.deepcopy(geometry) + ending_geometry[a1][1] = newCoord1.tolist() + return ending_geometry + + @classmethod + def absolute_distance(cls, distance, geometry, atom_pair): + """ + Args: + atom_pair(tuple(int)): A tuple with two integers, + indexing which atoms from the starting geometry should be + moved apart. **Atom1 is moved away (at the given distance) + from Atom2, while Atom2 remains stationary.** + distance(float): The (new) distance between the two atoms. + geometry(([str, [float]])): The initial geometry to perturb. + """ + + def func(x, d): return d + + return cls.__distance_modifier(func, distance, geometry, atom_pair) + + @classmethod + def absolute_stretching(cls, perturbation, geometry, atom_pair): + """ + Args: + atom_pair(tuple(int)): A tuple with two integers, + indexing which atoms from the starting geometry should be + stretched apart. **Atom1 is stretched away from Atom2, while + Atom2 remains stationary.** + perturbation(float): The magnitude of the stretch. + (New distance = stretch + old distance) + geometry(([str, [float]])): The initial geometry to perturb. + """ + + def func(x, d): return x + d + + return cls.__distance_modifier(func, perturbation, geometry, + atom_pair) + + @classmethod + def relative_stretching(cls, perturbation, geometry, atom_pair): + """ + Args: + atom_pair(tuple(int)): A tuple with two integers, indexing which + atoms from the starting geometry should be stretched apart. + **Atom1 is stretched away from Atom2, while Atom2 remains + stationary.** + perturbation(float): The magnitude of the stretch. + (New distance = stretch * old distance) + geometry(([str, [float]])): The initial geometry to perturb. + """ + + def func(x, d): return x * d + + return cls.__distance_modifier(func, perturbation, geometry, + atom_pair) + + @classmethod + def __bend_modifier(cls, function, parameter, geometry, atom_trio): + """ + Args: + atom_trio(tuple(int)): A tuple with three integers, indexing + which atoms from the starting geometry should be bent apart. + **Atom1 is bent *away* from Atom3 by an angle whose vertex + is Atom2, while Atom2 and Atom3 remain stationary.** + function: a function of two parameters (current angle, + extra parameter) returning the new angle + parameter(float): The extra parameter of the function above. + geometry(([str, [float]])): The initial geometry to perturb. + """ + a1, a2, a3 = atom_trio + startingCoord1 = np.array(geometry[a1][1]) + coord2 = np.array(geometry[a2][1]) + coord3 = np.array(geometry[a3][1]) + + distanceVec1to2 = startingCoord1 - coord2 + distanceVec3to2 = coord3 - coord2 + rot_axis = np.cross(distanceVec1to2, distanceVec3to2) + # If atoms are linear, choose the rotation direction randomly, + # but still along the correct plane + # Maybe this is a bad idea if we end up here on some + # existing bending path. + # It'd be good to fix this later to remember the axis in some way. + if np.linalg.norm(rot_axis) == 0: + nudged_vec = copy.deepcopy(distanceVec1to2) + nudged_vec[0] += .01 + rot_axis = np.cross(nudged_vec, distanceVec3to2) + rot_unit_axis = rot_axis / np.linalg.norm(rot_axis) + startingAngle = np.arcsin( + np.linalg.norm(rot_axis) / ( + np.linalg.norm(distanceVec1to2) + * np.linalg.norm(distanceVec3to2) + ) + ) + newAngle = function(startingAngle, parameter) + perturbation = newAngle - startingAngle + rot_matrix = scipy.linalg.expm( + np.cross( + np.eye(3), + rot_unit_axis * + perturbation)) + newCoord1 = rot_matrix @ startingCoord1 + + ending_geometry = copy.deepcopy(geometry) + ending_geometry[a1][1] = newCoord1.tolist() + return ending_geometry + + @classmethod + def absolute_angle(cls, angle, geometry, atom_trio): + """ + Args: + atom_trio(tuple(int)): A tuple with three integers, indexing + which atoms from the starting geometry should be bent apart. + **Atom1 is bent *away* from Atom3 by an angle whose vertex + is Atom2 and equal to **angle**, while Atom2 and Atom3 + remain stationary.** + angle(float): The magnitude of the perturbation in **radians**. + **Positive bend is always in the direction toward Atom3.** + the direction of increasing the starting angle.** + geometry(([str, [float]])): The initial geometry to perturb. + """ + + def func(x, d): return d + + return cls.__bend_modifier(func, angle, geometry, atom_trio) + + @classmethod + def absolute_bending(cls, bend, geometry, atom_trio): + """ + Args: + atom_trio(tuple(int)): A tuple with three integers, indexing + which atoms from the starting geometry should be bent apart. + **Atom1 is bent *away* from Atom3 by an angle whose vertex + is Atom2 and equal to the initial angle **plus** bend, + while Atom2 and Atom3 remain stationary.** + bend(float): The magnitude of the perturbation in **radians**. + **Positive bend is always in the direction toward Atom3.** + the direction of increasing the starting angle.** + geometry(([str, [float]])): The initial geometry to perturb. + """ + + def func(x, d): return x + d + + return cls.__bend_modifier(func, bend, geometry, atom_trio) + + @classmethod + def relative_bending(cls, bend, geometry, atom_trio): + """ + Args: + atom_trio(tuple(int)): A tuple with three integers, + indexing which atoms from the starting geometry + should be bent apart. **Atom1 is bent *away* from Atom3 + by an angle whose vertex is Atom2 and equal to the initial + angle **times** bend, while Atom2 and Atom3 + remain stationary.** + bend(float): The magnitude of the perturbation in **radians**. + **Positive bend is always in the direction toward Atom3.** + the direction of increasing the starting angle.** + geometry(([str, [float]])): The initial geometry to perturb. + """ + + def func(x, d): return x * d + + return cls.__bend_modifier(func, bend, geometry, atom_trio) + + def get_perturbed_geom(self, perturbations=None): + if not perturbations or not self._degrees_of_freedom: + return self._geometry + geometry = copy.deepcopy(self._geometry) + for per, dof in zip(perturbations, self._degrees_of_freedom): + geometry = dof(per, geometry) + return geometry + + @classmethod + def get_geometry_str(cls, geometry): + return '; '.join([name + ' ' + ', '.join(map(str, coord)) + for (name, coord) in geometry]) + + @property + def geometry_str(self): + return get_geometry_str(geometry) + + @property + def basis_set(self): + return self._basis_set + + @property + def hf_method(self): + return self._hf_method + + @property + def spins(self): + return self._spins + + @property + def masses(self): + return self._masses + From f12e88fc57e872d3df696ed46935964a1255c7a2 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 16 Sep 2020 10:58:39 +0200 Subject: [PATCH 004/197] minor fix molecule --- qiskit/chemistry/molecule.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/molecule.py b/qiskit/chemistry/molecule.py index 89246143cf..7cffa24203 100644 --- a/qiskit/chemistry/molecule.py +++ b/qiskit/chemistry/molecule.py @@ -2,15 +2,16 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2019, 2020 +# (C) Copyright IBM 2020, 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. +# of this source tree or aexxont 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 an interface for a generic molecule. It defines the composing atoms (with properties like masses, and nuclear spin), @@ -25,6 +26,9 @@ class Molecule: + """ + Molecule class + """ def __init__(self, geometry, From 0faa09c64a5a518421fab006f22c7b841005b4ff Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 16 Sep 2020 14:58:06 +0200 Subject: [PATCH 005/197] excited state calculation added --- .../EEExcitedStatesCalculation.py | 0 .../ExcitedStateCalculation.py | 37 +++++++++++++++++++ .../VQEExcitedStatesCalculation.py | 0 .../MinEigensolverGroundStateCalculation.py | 2 - 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 qiskit/chemistry/excited_state_calculation/EEExcitedStatesCalculation.py create mode 100644 qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py diff --git a/qiskit/chemistry/excited_state_calculation/EEExcitedStatesCalculation.py b/qiskit/chemistry/excited_state_calculation/EEExcitedStatesCalculation.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py b/qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py index e69de29bb2..5f1ba9c044 100644 --- a/qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py +++ b/qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +# 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. + +from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, + ChemistryOperator) +from qiskit.chemistry.core import MolecularExcitedStatesResult + +class ExcitedStateCalculation(GroundStateCalculation): + + 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: + + super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, + z2symmetry_reduction) + + @abstractmethod + def compute_excited_states(self, + driver: BaseDriver) -> MolecularExcitedStatesResult: + + raise NotImplementedError() diff --git a/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py b/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index 672533934c..a1281df078 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -17,8 +17,6 @@ from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, ChemistryOperator, MolecularGroundStateResult) - - class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): """ MinimumEigensolverGroundStateCalculation From c1525bd185a540029f526d507ce6902f5af3a1b6 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 16 Sep 2020 17:53:51 +0200 Subject: [PATCH 006/197] EOMvqe added --- ...ulation.py => ExcitedStatesCalculation.py} | 8 +- .../VQEExcitedStatesCalculation.py | 80 +++++++++++++++++++ 2 files changed, 84 insertions(+), 4 deletions(-) rename qiskit/chemistry/excited_state_calculation/{ExcitedStateCalculation.py => ExcitedStatesCalculation.py} (88%) diff --git a/qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py b/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py similarity index 88% rename from qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py rename to qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py index 5f1ba9c044..84cbf3e6a0 100644 --- a/qiskit/chemistry/excited_state_calculation/ExcitedStateCalculation.py +++ b/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py @@ -17,7 +17,8 @@ ChemistryOperator) from qiskit.chemistry.core import MolecularExcitedStatesResult -class ExcitedStateCalculation(GroundStateCalculation): + +class ExcitedStatesCalculation(GroundStateCalculation): def __init__(self, transformation: TransformationType = TransformationType.FULL, @@ -32,6 +33,5 @@ def __init__(self, @abstractmethod def compute_excited_states(self, - driver: BaseDriver) -> MolecularExcitedStatesResult: - - raise NotImplementedError() + driver: BaseDriver) -> MolecularExcitedStatesResult: + raise NotImplementedError() diff --git a/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py b/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py index e69de29bb2..787086cede 100644 --- a/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py +++ b/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +# 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. + +from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, + ChemistryOperator) +from qiskit.chemistry.core import MolecularExcitedStatesResult +from qiskit.chemistry.excited_states_calculation import ExcitedStatesCalculation + + +class VQEQeomExcitedStatesCalculation(ExcitedStatesCalculation): + """ + VQEQeomExcitesStatesCalculation + """ + + def __init__(self, + variational_form: var_form, + optimizer: optimizer, + backend: backend = BasicAer.get_backend('statevector_simulator'), + intial_state: intitial_state = None, + 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: + solver: + transformation: + qubit_mapping: + two_qubit_reduction: + freeze_core: + orbital_reduction: + z2symmetry_reduction: + """ + + self._variational_form = variational_form + self._optimizer = optimizer + self._intial_state = intial_state + self._backend = backend + super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, + z2symmetry_reduction) + + def compute_excited_states(self, driver) -> MolecularExcitedStatesCalculationResult: + """ + + Compute Excited States result + + Returns: + MolecularExcitedStatesCalculationResult + + """ + + operator, aux_operators = self._transform(driver) + + #TODO This should not be like this. We need to implement a ".compute_eigenstates()" function + #TODO Check: Make sure that the driver here exposes _num_orbitals and _num_particles + + eom_vqe = QEomVQE(operator, var_form, optimizer, num_orbitals=driver._num_orbitals, + num_particles=driver._num_particles, qubit_mapping=self._qubit_mapping, + two_qubit_reduction=self._two_qubit_reduction, + z2_symmetries=self._z2_symmetries, untapered_op=operator) + + quantum_instance = QuantumInstance(backend) + raw_es_result = eom_vqe.run(quantum_instance) + + return core.process_algorithm_result(raw_es_result) From 7f7d8b8e3eb9f7cb9917cea040d0e53769b8d6a5 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 17 Sep 2020 12:09:30 +0200 Subject: [PATCH 007/197] ground state interface minimum eigensolver --- .../AdaptVQEGroundStateCalculation.py | 5 +++-- .../GroundStateCalculation.py | 11 ++++++++++- .../MinEigensolverGroundStateCalculation.py | 19 +++++++++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py index 10698d9467..c31c9f23ab 100644 --- a/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py @@ -1,10 +1,11 @@ #imports -class OrbitalOptimizationVQE(GroundStateCalculation): # same for VQEAdapt, ... +class AdaptVQE(GroundStateCalculation): # same for VQEAdapt, ... def __init__(self, params_for_mapping): super().__init__(params_for_mapping) - def compute_ground_state(driver) -> GroundStateCalculationResult: + + def compute_ground_state(driver) -> MolecularGroundStateCalculationResult: op = self._transform(driver) # different implementation similar to VQE result = None diff --git a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py index db799042db..12cf1a7ac8 100644 --- a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py @@ -65,18 +65,27 @@ def _transform(self, driver): # takes driver, applies specified mapping, returns qubit operator q_molecule = self.driver.run() + core = Hamiltonian(transformation=self._transformation, qubit_mapping=self._qubit_mapping, two_qubit_reduction=self._two_qubit_reduction, freeze_core=self._freeze_core, orbital_reduction=self._orbital_reduction, z2symmetry_reduction=self._z2symmetry_reduction) + + self._core = core + operator, aux_operators = core.run(q_molecule) return operator, aux_operators + @property + def molecule_info(self): + + return self._core.molecule_info + @abstractmethod def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: - raise NotImplementedError() \ No newline at end of file + raise NotImplementedError() diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index a1281df078..9b9d707a31 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -16,6 +16,7 @@ from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, ChemistryOperator, MolecularGroundStateResult) +from qiskit.chemistry import QiskitChemistryError class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): """ @@ -46,7 +47,7 @@ def __init__(self, super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, z2symmetry_reduction) - def compute_ground_state(self, driver) -> GroundStateCalculationResult: + def compute_ground_state(self, driver, callback = None) -> GroundStateCalculationResult: """ Compute Ground State properties @@ -56,14 +57,24 @@ def compute_ground_state(self, driver) -> GroundStateCalculationResult: """ + if self._solver is None and callback is None: + raise QiskitChemistryError('Minimum Eigensolvaer was not provided') + operator, aux_operators = self._transform(driver) - + + if callback is not None: + num_particles = self._core.molecule_info[ChemistryOperator.INFO_NUM_PARTICLES] + num_orbitals = self._core.molecule_info[ChemistryOperator.INFO_NUM_ORBITALS] + self._solver = callback(num_particles, num_orbitals, + self._qubit_mapping.value, self._two_qubit_reduction, + self._z2_symmetries) + aux_operators = aux_operators if self.solver.supports_aux_operators() else None raw_gs_result = self._solver.compute_minimum_eigenstate(operator, aux_operators) return core.process_algorithm_result(raw_gs_result) - + @staticmethod def get_default_solver(quantum_instance: Union[QuantumInstance, BaseBackend]) ->\ Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]]: @@ -89,4 +100,4 @@ def cb_default_solver(num_particles, num_orbitals, vqe = VQE(var_form=var_form) vqe.quantum_instance = quantum_instance return vqe - return cb_default_solver \ No newline at end of file + return cb_default_solver From 362843b19ff199e1b824b4318b14eee1d5c3da66 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 17 Sep 2020 15:25:00 +0200 Subject: [PATCH 008/197] Add MinimumEigensolver interface to VQEAdapt --- .../minimum_eigen_solvers/vqe_adapt.py | 125 +++++++++++++----- 1 file changed, 94 insertions(+), 31 deletions(-) diff --git a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py index ed6dfacb77..1504e7858d 100644 --- a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py +++ b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py @@ -24,26 +24,28 @@ from qiskit import ClassicalRegister from qiskit.aqua import QuantumInstance, AquaError from qiskit.aqua.algorithms import VQAlgorithm, VQE, VQEResult -from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.aqua.operators import WeightedPauliOperator -from qiskit.aqua.operators import LegacyBaseOperator +from qiskit.aqua.algorithms.minimum_eigen_solvers import (MinimumEigensolver, + MinimumEigensolverResult) from qiskit.aqua.components.optimizers import Optimizer from qiskit.aqua.components.variational_forms import VariationalForm +from qiskit.aqua.operators import OperatorBase, LegacyBaseOperator, WeightedPauliOperator from qiskit.aqua.utils.validation import validate_min +from qiskit.chemistry.components.variational_forms import UCCSD logger = logging.getLogger(__name__) -class VQEAdapt(VQAlgorithm): +class VQEAdapt(VQAlgorithm, MinimumEigensolver): """ The Adaptive VQE algorithm. See https://arxiv.org/abs/1812.11173 """ - # TODO make re-usable, implement MinimumEignesolver interface - def __init__(self, operator: LegacyBaseOperator, - var_form_base: VariationalForm, optimizer: Optimizer, + def __init__(self, + operator: LegacyBaseOperator = None, + var_form_base: VariationalForm = None, + optimizer: Optimizer = None, initial_point: Optional[np.ndarray] = None, excitation_pool: Optional[List[WeightedPauliOperator]] = None, threshold: float = 1e-5, @@ -69,24 +71,27 @@ def __init__(self, operator: LegacyBaseOperator, Raises: ValueError: if var_form_base is not an instance of UCCSD. - See also: qiskit/chemistry/components/variational_forms/uccsd_adapt.py + See also: qiskit/chemistry/components/variational_forms/uccsd.py """ validate_min('threshold', threshold, 1e-15) validate_min('delta', delta, 1e-5) + + if var_form_base is None or not isinstance(var_form_base, UCCSD): + raise ValueError("var_form_base has to be an instance of UCCSD.") + + self._quantum_instance = None super().__init__(var_form=var_form_base, optimizer=optimizer, initial_point=initial_point, quantum_instance=quantum_instance) + self._ret = None # type: Dict self._optimizer.set_max_evals_grouped(max_evals_grouped) if initial_point is None: self._initial_point = var_form_base.preferred_init_points self._operator = operator - if not isinstance(var_form_base, UCCSD): - raise ValueError("var_form_base has to be an instance of UCCSD.") - self._var_form_base = var_form_base - self._var_form_base.manage_hopping_operators() - self._excitation_pool = self._var_form_base.excitation_pool \ + self._var_form.manage_hopping_operators() + self._excitation_pool = self._var_form.excitation_pool \ if excitation_pool is None else excitation_pool self._threshold = threshold self._delta = delta @@ -98,21 +103,78 @@ def __init__(self, operator: LegacyBaseOperator, for aux_op in aux_operators: self._aux_operators.append(aux_op) - def _compute_gradients(self, excitation_pool, theta, delta, - var_form, operator, optimizer): + @property + def operator(self) -> Optional[OperatorBase]: + """ Returns operator """ + return self._operator + + @operator.setter + def operator(self, operator: Union[OperatorBase, LegacyBaseOperator]) -> None: + """ set operator """ + self._operator = operator + + @property + def aux_operators(self) -> Optional[List[Optional[OperatorBase]]]: + """ Returns aux operators """ + return self._aux_operators + + @aux_operators.setter + def aux_operators(self, + aux_operators: Optional[ + Union[OperatorBase, + LegacyBaseOperator, + List[Optional[Union[OperatorBase, + LegacyBaseOperator]]]]]) -> None: + """ Set aux operators """ + if aux_operators is None: + aux_operators = [] + elif not isinstance(aux_operators, list): + aux_operators = [aux_operators] + + self._aux_operators = aux_operators # type: List + + @property + def quantum_instance(self) -> Optional[QuantumInstance]: + """ Returns quantum instance """ + return self._quantum_instance + + @quantum_instance.setter + def quantum_instance(self, quantum_instance) -> None: + """ set quantum instance """ + self._quantum_instance = quantum_instance + + def supports_aux_operators(self) -> bool: + return True + + def compute_minimum_eigenvalue( + self, + operator: Optional[Union[OperatorBase, LegacyBaseOperator]] = None, + aux_operators: Optional[List[Optional[Union[OperatorBase, + LegacyBaseOperator]]]] = None + ) -> MinimumEigensolverResult: + super().compute_minimum_eigenvalue(operator, aux_operators) + return self._run() + + def _compute_gradients(self, + excitation_pool: List, + theta: float, + delta: float, + var_form: VariationalForm, + operator: LegacyBaseOperator, + optimizer: Optimizer) -> List: """ Computes the gradients for all available excitation operators. Args: - excitation_pool (list): pool of excitation operators - theta (list): list of (up to now) optimal parameters - delta (float): finite difference step size (for gradient computation) - var_form (VariationalForm): current variational form - operator (LegacyBaseOperator): system Hamiltonian - optimizer (Optimizer): classical optimizer algorithm + excitation_pool: pool of excitation operators + theta: list of (up to now) optimal parameters + delta: finite difference step size (for gradient computation) + var_form: current variational form + operator: system Hamiltonian + optimizer: classical optimizer algorithm Returns: - list: List of pairs consisting of gradient and excitation operator. + List of pairs consisting of gradient and excitation operator. """ res = [] # compute gradients for all excitation in operator pool @@ -143,9 +205,10 @@ def _run(self) -> 'VQEAdaptResult': Raises: AquaError: wrong setting of operator and backend. """ + if self.operator is None: + raise AquaError("The operator was never provided.") + self._ret = {} # TODO should be eliminated - # self._operator = VQE._config_the_best_mode(self, self._operator, - # self._quantum_instance.backend) self._quantum_instance.circuit_summary = True threshold_satisfied = False @@ -160,7 +223,7 @@ def _run(self) -> 'VQEAdaptResult': logger.info('--- Iteration #%s ---', str(iteration)) # compute gradients cur_grads = self._compute_gradients(self._excitation_pool, theta, self._delta, - self._var_form_base, self._operator, + self._var_form, self._operator, self._optimizer) # pick maximum gradient max_grad_index, max_grad = max(enumerate(cur_grads), @@ -186,11 +249,11 @@ def _run(self) -> 'VQEAdaptResult': logger.info("Final maximum gradient: %s", str(np.abs(max_grad[0]))) alternating_sequence = True break - # add new excitation to self._var_form_base - self._var_form_base.push_hopping_operator(max_grad[1]) + # add new excitation to self._var_form + self._var_form.push_hopping_operator(max_grad[1]) theta.append(0.0) # run VQE on current Ansatz - algorithm = VQE(self._operator, self._var_form_base, self._optimizer, + algorithm = VQE(self._operator, self._var_form, self._optimizer, initial_point=theta) vqe_result = algorithm.run(self._quantum_instance) self._ret['opt_params'] = vqe_result.optimal_point @@ -203,7 +266,7 @@ def _run(self) -> 'VQEAdaptResult': # once finished evaluate auxiliary operators if any if self._aux_operators is not None and self._aux_operators: - algorithm = VQE(self._operator, self._var_form_base, self._optimizer, + algorithm = VQE(self._operator, self._var_form, self._optimizer, initial_point=theta, aux_operators=self._aux_operators) vqe_result = algorithm.run(self._quantum_instance) self._ret['opt_params'] = vqe_result.optimal_point @@ -233,7 +296,7 @@ def _check_cyclicity(indices: List) -> bool: Auxiliary function to check for cycles in the indices of the selected excitations. Returns: - bool: Whether repeating sequences of indices have been detected. + Whether repeating sequences of indices have been detected. """ cycle_regex = re.compile(r"(\b.+ .+\b)( \b\1\b)+") # reg-ex explanation: @@ -261,7 +324,7 @@ def get_optimal_circuit(self): if 'opt_params' not in self._ret: raise AquaError("Cannot find optimal circuit before running the " "algorithm to find optimal params.") - return self._var_form_base.construct_circuit(self._ret['opt_params']) + return self._var_form.construct_circuit(self._ret['opt_params']) def get_optimal_vector(self): # pylint: disable=import-outside-toplevel From 199c97b69b90bfb81007e1d2f04aa8de5a7fe595 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 17 Sep 2020 16:25:45 +0200 Subject: [PATCH 009/197] Implement the AdaptVQEGroundStateCalculation class This properly implements the AdaptVQEGroundStateCalculation class. The idea of this class is to allow easy use of the VQEAdapt algorithm which was previously extended to support the MinimumEigensolver interface. This Calculation class is solely for the use with this algorithm which is why it does not support setting a different solver. Instead, it requires setting a QuantumInstance object through its initializer. The changes which this requires include the following: - fixing the GroundStateCalculation class - fixing the MinEigensolverGroundStateCalculation class Furthermore, a very basic unittest for this change was added. --- .../AdaptVQEGroundStateCalculation.py | 120 ++++++++++++++++-- .../GroundStateCalculation.py | 113 ++++++++++------- .../MinEigensolverGroundStateCalculation.py | 87 ++++++++----- .../ground_state_calculation/__init__.py | 21 +++ ...t_adaptive_vqe_ground_state_calculation.py | 48 +++++++ 5 files changed, 307 insertions(+), 82 deletions(-) create mode 100644 qiskit/chemistry/ground_state_calculation/__init__.py create mode 100644 test/chemistry/test_adaptive_vqe_ground_state_calculation.py diff --git a/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py index c31c9f23ab..e9a5a0ce2e 100644 --- a/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py @@ -1,13 +1,113 @@ -#imports +# 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. +""" +A ground state calculation employing the VQEAdapt algorithm. +""" -class AdaptVQE(GroundStateCalculation): # same for VQEAdapt, ... - def __init__(self, params_for_mapping): - super().__init__(params_for_mapping) +import logging +from typing import List, Optional, Callable, Union - def compute_ground_state(driver) -> MolecularGroundStateCalculationResult: - op = self._transform(driver) - # different implementation similar to VQE - result = None - # construct GroundStateCalculationResult - return ground_state_calculation_result +from qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import MinimumEigensolver +from qiskit.aqua.operators import Z2Symmetries +from qiskit.chemistry.algorithms import VQEAdapt +from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.chemistry.components.variational_forms import UCCSD +from qiskit.chemistry.core import (TransformationType, QubitMappingType, ChemistryOperator, + MolecularGroundStateResult) +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from qiskit.providers import BaseBackend + +logger = logging.getLogger(__name__) + + +class AdaptVQEGroundStateCalculation(GroundStateCalculation): + """A ground state calculation employing the VQEAdapt algorithm.""" + def __init__(self, + quantum_instance: Union[QuantumInstance, BaseBackend], + 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: + quantum_instance: a quantum instance + 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 the qubit operators + that are computed. 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. + See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core + processing behind this class. + """ + + super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, + orbital_reduction, z2symmetry_reduction) + + self._quantum_instance = quantum_instance + # the solver object is used internally in order to be consistent with the + # GroundStateCalculation implementation + self._solver = None + + def compute_ground_state(self, + driver: BaseDriver, + callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], + MinimumEigensolver]] = None + ) -> MolecularGroundStateResult: + """Compute the ground state energy of the molecule that was supplied via the driver. + + Args: + driver: A chemistry driver. + callback: This argument will be ignored and is only provided for compatibility reasons! + + Returns: + A molecular ground state result. + """ + operator, aux_operators = self._transform(driver) + + if callback is not None: + logger.warning("The `callback` option is only provided for compatibility reasons and \ + has no effect in this context!") + + # gather required data from molecule info + num_particles = self.molecule_info[ChemistryOperator.INFO_NUM_PARTICLES] + num_orbitals = self.molecule_info[ChemistryOperator.INFO_NUM_ORBITALS] + z2_symmetries = self.molecule_info[ChemistryOperator.INFO_Z2SYMMETRIES] + + # contract variational form base + initial_state = HartreeFock(num_orbitals, num_particles, self._qubit_mapping.value, + self._two_qubit_reduction, z2_symmetries.sq_list) + var_form_base = UCCSD(num_orbitals=num_orbitals, + num_particles=num_particles, + initial_state=initial_state, + qubit_mapping=self._qubit_mapping.value, + two_qubit_reduction=self._two_qubit_reduction, + z2_symmetries=z2_symmetries) + + # initialize the adaptive VQE algorithm with the specified quantum instance + self._solver = VQEAdapt(var_form_base=var_form_base) + self._solver.quantum_instance = self._quantum_instance + + # run the algorithm and post-process the result + raw_gs_result = self._solver.compute_minimum_eigenvalue(operator, aux_operators) + return self._core.process_algorithm_result(raw_gs_result) diff --git a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py index 12cf1a7ac8..828aca29a1 100644 --- a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # This code is part of Qiskit. # # (C) Copyright IBM 2020. @@ -12,14 +10,23 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. +""" +A ground state calculation interface. +""" from abc import ABC, abstractmethod -from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, - ChemistryOperator, MolecularGroundStateResult) +from typing import List, Optional, Union, Tuple, Dict, Callable + +from qiskit.aqua.operators import LegacyBaseOperator +from qiskit.aqua.algorithms import MinimumEigensolver from qiskit.aqua.operators import Z2Symmetries +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, + MolecularGroundStateResult) + class GroundStateCalculation(ABC): - """GroundStateCalculation""" + """The ground state calculation interface.""" def __init__(self, transformation: TransformationType = TransformationType.FULL, @@ -28,24 +35,23 @@ def __init__(self, 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 the qubit operators - that are computed. 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. - See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core - processing behind this class. - """ + """ + 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 the qubit operators + that are computed. 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. + See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core + processing behind this class. + """ self._transformation = transformation self._qubit_mapping = qubit_mapping self._two_qubit_reduction = two_qubit_reduction @@ -53,39 +59,60 @@ def __init__(self, self._orbital_reduction = orbital_reduction self._z2symmetry_reduction = z2symmetry_reduction - def _transform(self, driver): - """ + # this is to provide access to the internal Hamiltonian object in derived classes + self._core = None + + def _transform(self, driver: BaseDriver) -> Tuple[LegacyBaseOperator, List]: + """Constructs a qubit operator for a given chemical problem. Args: - driver: + driver: A chemical driver instance encoding the molecular problem. Returns: - + The qubit operator and auxiliary operator list transformed based on the specified + mapping. """ - # takes driver, applies specified mapping, returns qubit operator - - q_molecule = self.driver.run() + q_molecule = driver.run() - core = Hamiltonian(transformation=self._transformation, - qubit_mapping=self._qubit_mapping, - two_qubit_reduction=self._two_qubit_reduction, - freeze_core=self._freeze_core, - orbital_reduction=self._orbital_reduction, - z2symmetry_reduction=self._z2symmetry_reduction) + self._core = Hamiltonian(transformation=self._transformation, + qubit_mapping=self._qubit_mapping, + two_qubit_reduction=self._two_qubit_reduction, + freeze_core=self._freeze_core, + orbital_reduction=self._orbital_reduction, + z2symmetry_reduction=self._z2symmetry_reduction) - self._core = core - - operator, aux_operators = core.run(q_molecule) + operator, aux_operators = self._core.run(q_molecule) return operator, aux_operators @property - def molecule_info(self): - + def molecule_info(self) -> Dict: + """Returns the molecular info stored in the core Hamiltonian.""" return self._core.molecule_info - + @abstractmethod def compute_ground_state(self, - driver: BaseDriver) -> MolecularGroundStateResult: + driver: BaseDriver, + callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], + MinimumEigensolver]] = None + ) -> MolecularGroundStateResult: + """ + Compute the ground state energy of the molecule that was supplied via the driver. + + Args: + driver: a chemical driver + callback: If not None will be called with the following values + num_particles, num_orbitals, qubit_mapping, two_qubit_reduction, z2_symmetries + in that order. This information can then be used to setup chemistry + specific component(s) that are needed by the chosen MinimumEigensolver. + The MinimumEigensolver can then be built and returned from this callback + for use as the solver here. + + Returns: + A molecular ground state result + Raises: + QiskitChemistryError: If no MinimumEigensolver was given and no callback is being + used that could supply one instead. + """ raise NotImplementedError() diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index 9b9d707a31..da4c937678 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # This code is part of Qiskit. # # (C) Copyright IBM 2020. @@ -14,9 +12,19 @@ """ Ground state computation using Aqua minimum eigensolver """ -from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, - ChemistryOperator, MolecularGroundStateResult) +from typing import List, Optional, Callable, Union + +from qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import MinimumEigensolver, VQE +from qiskit.aqua.operators import Z2Symmetries from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.chemistry.components.variational_forms import UCCSD +from qiskit.chemistry.core import (TransformationType, QubitMappingType, ChemistryOperator, + MolecularGroundStateResult) +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from qiskit.providers import BaseBackend class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): """ @@ -32,49 +40,70 @@ def __init__(self, orbital_reduction: Optional[List[int]] = None, z2symmetry_reduction: Optional[Union[str, List[int]]] = None) -> None: """ - Args: - solver: - transformation: - qubit_mapping: - two_qubit_reduction: - freeze_core: - orbital_reduction: - z2symmetry_reduction: + solver: a minimum eigensolver + 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 the qubit operators + that are computed. 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. + See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core + processing behind this class. """ self._solver = solver - super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, - z2symmetry_reduction) + super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, + orbital_reduction, z2symmetry_reduction) - def compute_ground_state(self, driver, callback = None) -> GroundStateCalculationResult: - """ + def compute_ground_state(self, + driver: BaseDriver, + callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], + MinimumEigensolver]] = None + ) -> MolecularGroundStateResult: + """Compute Ground State properties. - Compute Ground State properties + Args: + driver: A chemistry driver. + callback: If not None will be called with the following values + num_particles, num_orbitals, qubit_mapping, two_qubit_reduction, z2_symmetries + in that order. This information can then be used to setup chemistry + specific component(s) that are needed by the chosen MinimumEigensolver. + The MinimumEigensolver can then be built and returned from this callback + for use as the solver here. Returns: - GroundStateCalculationResult - + A molecular ground state result + Raises: + QiskitChemistryError: If no MinimumEigensolver was given and no callback is being + used that could supply one instead. """ if self._solver is None and callback is None: raise QiskitChemistryError('Minimum Eigensolvaer was not provided') - + operator, aux_operators = self._transform(driver) - + if callback is not None: - num_particles = self._core.molecule_info[ChemistryOperator.INFO_NUM_PARTICLES] - num_orbitals = self._core.molecule_info[ChemistryOperator.INFO_NUM_ORBITALS] + num_particles = self.molecule_info[ChemistryOperator.INFO_NUM_PARTICLES] + num_orbitals = self.molecule_info[ChemistryOperator.INFO_NUM_ORBITALS] + z2_symmetries = self.molecule_info[ChemistryOperator.INFO_Z2SYMMETRIES] self._solver = callback(num_particles, num_orbitals, - self._qubit_mapping.value, self._two_qubit_reduction, - self._z2_symmetries) - - aux_operators = aux_operators if self.solver.supports_aux_operators() else None + self._qubit_mapping.value, self._two_qubit_reduction, + z2_symmetries) + + aux_operators = aux_operators if self._solver.supports_aux_operators() else None raw_gs_result = self._solver.compute_minimum_eigenstate(operator, aux_operators) - return core.process_algorithm_result(raw_gs_result) - + return self._core.process_algorithm_result(raw_gs_result) + @staticmethod def get_default_solver(quantum_instance: Union[QuantumInstance, BaseBackend]) ->\ Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]]: diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py new file mode 100644 index 0000000000..c2ac500fbe --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -0,0 +1,21 @@ +# 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. + +"""TODO""" + +from .GroundStateCalculation import GroundStateCalculation +from .AdaptVQEGroundStateCalculation import AdaptVQEGroundStateCalculation +from .MinEigensolverGroundStateCalculation import MinimumEigensolverGroundStateCalculation + +__all__ = ['GroundStateCalculation', + 'AdaptVQEGroundStateCalculation', + 'MinimumEigensolverGroundStateCalculation'] diff --git a/test/chemistry/test_adaptive_vqe_ground_state_calculation.py b/test/chemistry/test_adaptive_vqe_ground_state_calculation.py new file mode 100644 index 0000000000..06f981e95a --- /dev/null +++ b/test/chemistry/test_adaptive_vqe_ground_state_calculation.py @@ -0,0 +1,48 @@ +# 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 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.chemistry import QiskitChemistryError +from qiskit.chemistry.ground_state_calculation import AdaptVQEGroundStateCalculation + + +class TestAdaptVQEGroundStateCalculation(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.137306 + + self.qinst = QuantumInstance(BasicAer.get_backend('statevector_simulator')) + + def test_default(self): + """ Default execution """ + calc = AdaptVQEGroundStateCalculation(self.qinst) + res = calc.compute_ground_state(self.driver) + self.assertAlmostEqual(res.energy, self.expected, places=6) + +if __name__ == '__main__': + unittest.main() From d82e76e0f7d5f516d15a18437d322fdebd4a1c16 Mon Sep 17 00:00:00 2001 From: Stefan Woerner Date: Fri, 18 Sep 2020 12:30:23 +0200 Subject: [PATCH 010/197] comments --- .../ExcitedStatesCalculation.py | 5 +- .../GroundStateCalculation.py | 69 +-- .../MinEigensolverGroundStateCalculation.py | 97 ++- .../bosonic_transformation.py | 16 + .../fermionic_transformation.py | 559 ++++++++++++++++++ .../qubit_operator_transformation.py | 15 + 6 files changed, 644 insertions(+), 117 deletions(-) create mode 100644 qiskit/chemistry/qubit_transformations/bosonic_transformation.py create mode 100644 qiskit/chemistry/qubit_transformations/fermionic_transformation.py create mode 100644 qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py diff --git a/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py b/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py index 84cbf3e6a0..56df865980 100644 --- a/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py +++ b/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py @@ -18,10 +18,9 @@ from qiskit.chemistry.core import MolecularExcitedStatesResult -class ExcitedStatesCalculation(GroundStateCalculation): +class ExcitedStatesCalculation(): # should not derive from GSC, might make sense to have an abstract parent class for GSC and ESC that is responsible for conversion - def __init__(self, - transformation: TransformationType = TransformationType.FULL, + def __init__(self, transformation: TransformationType = TransformationType.FULL, qubit_mapping: QubitMappingType = QubitMappingType.PARITY, two_qubit_reduction: bool = True, freeze_core: bool = False, diff --git a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py index 828aca29a1..3b86f76aac 100644 --- a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py @@ -23,76 +23,23 @@ from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, MolecularGroundStateResult) +from qiskit.chemistry.qubit_transformations.qubit_operator_transformation import \ + QubitOperatorTransformation class GroundStateCalculation(ABC): """The ground state calculation interface.""" - 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: + def __init__(self, transformation: QubitOperatorTransformation) -> 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 the qubit operators - that are computed. 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. - See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core - processing behind this class. + transformation: transformation from driver to qubit operator (and aux. operators) """ self._transformation = transformation - self._qubit_mapping = qubit_mapping - self._two_qubit_reduction = two_qubit_reduction - self._freeze_core = freeze_core - self._orbital_reduction = orbital_reduction - self._z2symmetry_reduction = z2symmetry_reduction - - # this is to provide access to the internal Hamiltonian object in derived classes - self._core = None - - def _transform(self, driver: BaseDriver) -> Tuple[LegacyBaseOperator, List]: - """Constructs a qubit operator for a given chemical problem. - - Args: - driver: A chemical driver instance encoding the molecular problem. - - Returns: - The qubit operator and auxiliary operator list transformed based on the specified - mapping. - """ - q_molecule = driver.run() - - self._core = Hamiltonian(transformation=self._transformation, - qubit_mapping=self._qubit_mapping, - two_qubit_reduction=self._two_qubit_reduction, - freeze_core=self._freeze_core, - orbital_reduction=self._orbital_reduction, - z2symmetry_reduction=self._z2symmetry_reduction) - - operator, aux_operators = self._core.run(q_molecule) - - return operator, aux_operators - - @property - def molecule_info(self) -> Dict: - """Returns the molecular info stored in the core Hamiltonian.""" - return self._core.molecule_info @abstractmethod def compute_ground_state(self, - driver: BaseDriver, + driver: BaseDriver, additional_ops, callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]] = None ) -> MolecularGroundStateResult: @@ -114,5 +61,9 @@ def compute_ground_state(self, QiskitChemistryError: If no MinimumEigensolver was given and no callback is being used that could supply one instead. """ - raise NotImplementedError() + + @abstractmethod + def returns_groundstate() -> bool: + # needs to return whether this calculation only returns groundstate energies or also groundstates + raise NotImplementedError diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index da4c937678..20c68af741 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -31,41 +31,20 @@ class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): MinimumEigensolverGroundStateCalculation """ - def __init__(self, - solver: Optional[MinimumEigensolver] = None, - 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: + def __init__(self, solver: Optional[MinimumEigensolver] = None, transformation) -> None: """ Args: solver: a minimum eigensolver - 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 the qubit operators - that are computed. 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. - See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core - processing behind this class. + transformation: """ self._solver = solver - super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, - orbital_reduction, z2symmetry_reduction) + super().__init__(transformation) def compute_ground_state(self, driver: BaseDriver, callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], - MinimumEigensolver]] = None + MinimumEigensolver]] = None # WOR: callback should be in constructor, and possibly follow an interface ) -> MolecularGroundStateResult: """Compute Ground State properties. @@ -86,9 +65,9 @@ def compute_ground_state(self, """ if self._solver is None and callback is None: - raise QiskitChemistryError('Minimum Eigensolvaer was not provided') + raise QiskitChemistryError('Minimum Eigensolver was not provided') - operator, aux_operators = self._transform(driver) + operator, aux_operators = self._transformation.transform(driver) if callback is not None: num_particles = self.molecule_info[ChemistryOperator.INFO_NUM_PARTICLES] @@ -102,31 +81,39 @@ def compute_ground_state(self, raw_gs_result = self._solver.compute_minimum_eigenstate(operator, aux_operators) - return self._core.process_algorithm_result(raw_gs_result) - - @staticmethod - def get_default_solver(quantum_instance: Union[QuantumInstance, BaseBackend]) ->\ - Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]]: - """ - Get the default solver callback that can be used with :meth:`compute_energy` - Args: - quantum_instance: A Backend/Quantum Instance for the solver to run on - - Returns: - Default solver callback - """ - def cb_default_solver(num_particles, num_orbitals, - qubit_mapping, two_qubit_reduction, z2_symmetries): - """ Default solver """ - 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) - vqe.quantum_instance = quantum_instance - return vqe - return cb_default_solver + # WOR: where should this post processing be coming from? + gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], groundstate) # gs = array/circuit+params + gsc_result.raw_result = raw_gs_results + + return + # (energy, aux_values, groundsntate) + + + class MesFactory(): + + + def get_default_solver(quantum_instance: Union[QuantumInstance, BaseBackend]) ->\ + Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]]: + """ + Get the default solver callback that can be used with :meth:`compute_energy` + Args: + quantum_instance: A Backend/Quantum Instance for the solver to run on + + Returns: + Default solver callback + """ + def cb_default_solver(num_particles, num_orbitals, + qubit_mapping, two_qubit_reduction, z2_symmetries): + """ Default solver """ + 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) + vqe.quantum_instance = quantum_instance + return vqe + return cb_default_solver diff --git a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py new file mode 100644 index 0000000000..e513ac75b4 --- /dev/null +++ b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py @@ -0,0 +1,16 @@ +from qiskit.chemistry.drivers import BaseDriver +from typing import Tuple, List +from qiskit.aqua.operators.legacy import WeightedPauliOperator +from .qubit_operator_transformation import QubitOperatorTransformation + +class BosonicTransformation(QubitOperatorTransformation): + + def __init__(self, h, basis): + pass + + def transform(driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + raise NotImplementedError() + # take code from bosonic operator + + def interpret(...): + pass \ No newline at end of file diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py new file mode 100644 index 0000000000..b0370c90b7 --- /dev/null +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -0,0 +1,559 @@ +# 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. +""" +This module implements a molecular Hamiltonian operator, representing the +energy of the electrons and nuclei in a molecule. +""" +import warnings +from typing import Optional, List, Union, cast +import logging +from enum import Enum + +import numpy as np +from qiskit.aqua.algorithms import MinimumEigensolverResult, EigensolverResult +from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator +from qiskit.chemistry import QMolecule, QiskitChemistryError +from qiskit.chemistry.fermionic_operator import FermionicOperator +from .chemistry_operator import (ChemistryOperator, + MolecularGroundStateResult, + DipoleTuple) +from .qubit_operator_transformation import QubitOperatorTransformation +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(QubitOperatorTransformation, ChemistryOperator): + """ + A molecular Hamiltonian operator, representing the + energy of the electrons and nuclei in a molecule. + """ + + 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 + qubit_mapping = qubit_mapping.value + orbital_reduction = orbital_reduction if orbital_reduction is not None else [] + super().__init__() + self._transformation = transformation + self._qubit_mapping = qubit_mapping + 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 + + # 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 + + def transform(driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + q_molecule = driver.run() + ops, aux_ops = self._do_transform(q_molecule) + return ops, aux_ops + + def _do_transform(self, qmolecule): + 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[(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] + + fer_op = FermionicOperator(h1=qmolecule.one_body_integrals, h2=qmolecule.two_body_integrals) + fer_op, self._energy_shift, did_shift = \ + Hamiltonian._try_reduce_fermionic_operator(fer_op, freeze_list, remove_list) + if did_shift: + logger.info("Frozen orbital energy shift: %s", self._energy_shift) + 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) + logger.debug('Converting to qubit using %s mapping', self._qubit_mapping) + qubit_op = Hamiltonian._map_fermionic_operator_to_qubit(fer_op, + self._qubit_mapping, new_nel, + self._two_qubit_reduction) + qubit_op.name = 'Electronic Hamiltonian' + + logger.debug(' num paulis: %s, num qubits: %s', len(qubit_op.paulis), qubit_op.num_qubits) + + aux_ops = [] + + def _add_aux_op(aux_op, name): + aux_qop = Hamiltonian._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) + + 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') + + if qmolecule.has_dipole_integrals(): + def _dipole_op(dipole_integrals, axis): + 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.append(op_dipole_x) + aux_ops.append(op_dipole_y) + aux_ops.append(op_dipole_z) + + 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._add_molecule_info(self.INFO_NUM_PARTICLES, [new_num_alpha, new_num_beta]) + self._add_molecule_info(self.INFO_NUM_ORBITALS, new_nspinorbs) + self._add_molecule_info(self.INFO_TWO_QUBIT_REDUCTION, + self._two_qubit_reduction + if self._qubit_mapping == 'parity' else False) + + 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._add_molecule_info(self.INFO_Z2SYMMETRIES, z2symmetries) + + logger.debug('Processing complete ready to run algorithm') + return qubit_op, aux_ops + + def _process_z2symmetry_reduction(self, qubit_op, aux_ops): + + 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 = Hamiltonian._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 = Hamiltonian._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[self.INFO_NUM_ORBITALS], + qubit_mapping=self._qubit_mapping, + two_qubit_reduction=self._two_qubit_reduction, + num_particles=self._molecule_info[self.INFO_NUM_PARTICLES]) + z2_symmetries = Hamiltonian._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: + z2_aux_ops.append(z2_symmetries.taper(aux_op).chop(chop_to) if aux_op is not None + else None) + + return z2_qubit_op, z2_aux_ops, z2_symmetries + + @staticmethod + def _check_commutes(cliffords, operator): + 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 + + @staticmethod + def _pick_sector(z2_symmetries, hf_str): + """ + 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 (Z2Symmetries): the z2 symmetries object. + hf_str (numpy.ndarray): Hartree-Fock bit string (the last index is for qubit 0). + + Returns: + Z2Symmetries: 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 + + # Called by public superclass method process_algorithm_result to complete specific processing + def _process_algorithm_result(self, algo_result): + if isinstance(algo_result, MinimumEigensolverResult): + return self._process_algorithm_result_ground_state(algo_result) + elif isinstance(algo_result, EigensolverResult): + return self._process_algorithm_result_deprecated(algo_result) + # TODO return self._process_algorithm_result_excited_states(algo_result) + else: + return self._process_algorithm_result_deprecated(algo_result) + + def _process_algorithm_result_ground_state(self, algo_result: MinimumEigensolverResult) \ + -> MolecularGroundStateResult: + mgsr = MolecularGroundStateResult() + mgsr.algorithm_result = algo_result + mgsr.hartree_fock_energy = self._hf_energy + mgsr.nuclear_repulsion_energy = self._nuclear_repulsion_energy + if self._nuclear_dipole_moment is not None: + mgsr.nuclear_dipole_moment = tuple(x for x in self._nuclear_dipole_moment) + mgsr.computed_electronic_energy = algo_result.eigenvalue.real + mgsr.ph_extracted_energy = self._ph_energy_shift + mgsr.frozen_extracted_energy = self._energy_shift + aux_ops_vals = algo_result.aux_operator_eigenvalues + if aux_ops_vals is not None: + # Dipole results if dipole aux ops were present + dipole_idx = 3 + if len(aux_ops_vals) > dipole_idx: + mgsr.reverse_dipole_sign = self._reverse_dipole_sign + dipm = [] + for i in range(dipole_idx, dipole_idx+3): # Gets X, Y and Z components + dipm.append(aux_ops_vals[i][0].real if aux_ops_vals[i] is not None else None) + mgsr.computed_dipole_moment = cast(DipoleTuple, tuple(dipm)) + mgsr.ph_extracted_dipole_moment = (self._ph_x_dipole_shift, + self._ph_y_dipole_shift, + self._ph_z_dipole_shift) + mgsr.frozen_extracted_dipole_moment = (self._x_dipole_shift, + self._y_dipole_shift, + self._z_dipole_shift) + # The first 3 entries are num particles, total angular momentum and magnetization + mgsr.num_particles = aux_ops_vals[0][0].real \ + if aux_ops_vals[0] is not None else None + mgsr.total_angular_momentum = aux_ops_vals[1][0].real \ + if aux_ops_vals[1] is not None else None + mgsr.magnetization = aux_ops_vals[2][0].real \ + if aux_ops_vals[2] is not None else None + return mgsr + + def _process_algorithm_result_deprecated(self, algo_result): + warnings.warn('Processing a dictionary result is deprecated,' + ' pass a (minimum) eigensolver result now.', DeprecationWarning) + # pylint: disable=len-as-condition + result = {} + + # Ground state energy + egse = algo_result['energy'] + self._energy_shift + self._ph_energy_shift + result['energy'] = egse + lines = ['=== GROUND STATE ENERGY ==='] + lines.append(' ') + lines.append('* Electronic ground state energy (Hartree): {}'.format(round(egse, 12))) + lines.append(' - computed part: {}'.format(round(algo_result['energy'], 12))) + lines.append(' - frozen energy part: {}'.format(round(self._energy_shift, 12))) + lines.append(' - particle hole part: {}'.format(round(self._ph_energy_shift, 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._nuclear_repulsion_energy + egse, 12))) + if 'aux_ops' in algo_result and len(algo_result['aux_ops']) > 0: + aux_ops = algo_result['aux_ops'][0] + num_particles = aux_ops[0][0] + spin_squared = aux_ops[1][0] + spin = (-1.0 + np.sqrt(1 + 4 * spin_squared)) / 2 + m = aux_ops[2][0] + lines.append( + ' Measured:: Num particles: {:.3f}, S: {:.3f}, M: {:.5f}'.format( + num_particles, spin, m)) + result['energy'] = self._nuclear_repulsion_energy + egse + result['nuclear_repulsion_energy'] = self._nuclear_repulsion_energy + if self._hf_energy is not None: + result['hf_energy'] = self._hf_energy + + # Excited states list - it includes ground state too + if 'energies' in algo_result: + exsce = \ + [x + self._energy_shift + self._ph_energy_shift for x in algo_result['energies']] + exste = [x + self._nuclear_repulsion_energy for x in exsce] + result['energies'] = exste + if len(exsce) > 1: + lines.append(' ') + lines.append('=== EXCITED STATES ===') + lines.append(' ') + lines.append( + '> Excited states energies (plus ground): {}'.format( + [round(x, 12) for x in exste])) + lines.append( + ' - computed: {}'.format([round(x, 12) for x in algo_result['energies']])) + if 'cond_number' in algo_result: # VQKE condition num for eigen vals + lines.append(' - cond num: {}'.format(algo_result['cond_number'])) + + if 'aux_ops' in algo_result and len(algo_result['aux_ops']) > 0: + lines.append( + ' ......................................................................') + lines.append( + ' ###: Total Energy, Computed, # particles, S M') + for i in range(len(algo_result['aux_ops'])): + aux_ops = algo_result['aux_ops'][i] + num_particles = aux_ops[0][0] + spin_squared = aux_ops[1][0] + spin = (-1.0 + np.sqrt(1 + 4 * spin_squared)) / 2 + m = aux_ops[2][0] + lines.append( + ' {:>3}: {: 16.12f}, {: 16.12f}, {:5.3f}, {:5.3f}, {:8.5f}'. + format(i, exste[i], algo_result['energies'][i], num_particles, spin, m)) + else: + result['energies'] = [result['energy']] + + # Dipole computation + dipole_idx = 3 + if 'aux_ops' in algo_result and len(algo_result['aux_ops']) > 0 and \ + len(algo_result['aux_ops'][0]) > dipole_idx: + dipole_moments_x = algo_result['aux_ops'][0][dipole_idx + 0][0] + dipole_moments_y = algo_result['aux_ops'][0][dipole_idx + 1][0] + dipole_moments_z = algo_result['aux_ops'][0][dipole_idx + 2][0] + + _elec_dipole = \ + np.array([dipole_moments_x + self._x_dipole_shift + self._ph_x_dipole_shift, + dipole_moments_y + self._y_dipole_shift + self._ph_y_dipole_shift, + dipole_moments_z + self._z_dipole_shift + self._ph_z_dipole_shift]) + lines.append(' ') + lines.append('=== DIPOLE MOMENT ===') + lines.append(' ') + lines.append('* Electronic dipole moment (a.u.): {}'.format( + Hamiltonian._dipole_to_string(_elec_dipole))) + lines.append(' - computed part: {}'.format( + Hamiltonian._dipole_to_string([dipole_moments_x, + dipole_moments_y, dipole_moments_z]))) + lines.append(' - frozen energy part: {}'.format( + Hamiltonian._dipole_to_string([self._x_dipole_shift, + self._y_dipole_shift, self._z_dipole_shift]))) + lines.append(' - particle hole part: {}'.format( + Hamiltonian._dipole_to_string([self._ph_x_dipole_shift, + self._ph_y_dipole_shift, self._ph_z_dipole_shift]))) + if self._nuclear_dipole_moment is not None: + if self._reverse_dipole_sign: + _elec_dipole = -_elec_dipole + dipole_moment = self._nuclear_dipole_moment + _elec_dipole + total_dipole_moment = np.sqrt(np.sum(np.power(dipole_moment, 2))) + lines.append('~ Nuclear dipole moment (a.u.): {}'.format( + Hamiltonian._dipole_to_string(self._nuclear_dipole_moment))) + lines.append('> Dipole moment (a.u.): {} Total: {}'.format( + Hamiltonian._dipole_to_string(dipole_moment), + Hamiltonian._float_to_string(total_dipole_moment))) + lines.append(' (debye): {} Total: {}'.format( + Hamiltonian._dipole_to_string(dipole_moment / QMolecule.DEBYE), + Hamiltonian._float_to_string(total_dipole_moment / QMolecule.DEBYE))) + result['nuclear_dipole_moment'] = self._nuclear_dipole_moment + result['electronic_dipole_moment'] = _elec_dipole + result['dipole_moment'] = dipole_moment + result['total_dipole_moment'] = total_dipole_moment + + return lines, result + + @staticmethod + def _try_reduce_fermionic_operator(fer_op, freeze_list, remove_list): + # 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, qubit_mapping, num_particles, two_qubit_reduction): + 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 + + @staticmethod + def _dipole_to_string(_dipole): + dips = [round(x, 8) for x in _dipole] + value = '[' + for i, _ in enumerate(dips): + value += Hamiltonian._float_to_string(dips[i]) + value += ' ' if i < len(dips) - 1 else ']' + return value + + @staticmethod + def _float_to_string(value, precision=8): + return '0.0' if value == 0 else ('{:.' + str(precision) + 'f}').format(value).rstrip('0') diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py new file mode 100644 index 0000000000..52a4ba3ca9 --- /dev/null +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -0,0 +1,15 @@ +from abc import ABC, abstractmethod +from qiskit.chemistry.drivers import BaseDriver +from typing import Tuple, List +from qiskit.aqua.operators.legacy import WeightedPauliOperator + + +class QubitOperatorTransformation(ABC): + + @abstractmethod + def transform(driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + raise NotImplementedError() + + @abstractmethod + def interpret(value, aux_values, circuit, params=None) -> GroundStateResult: # might be fermionic / bosonic + raise NotImplementedError() From e0cd53cf36a082065b2ed09afec1526b985e5be1 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Fri, 18 Sep 2020 14:02:21 +0200 Subject: [PATCH 011/197] initial bopes sampler --- qiskit/chemistry/bopes_sampler.py | 368 ++++++++++++++++++ qiskit/chemistry/bopes_sampler.py~ | 355 +++++++++++++++++ .../ExcitedStatesCalculation.py | 28 +- 3 files changed, 742 insertions(+), 9 deletions(-) create mode 100644 qiskit/chemistry/bopes_sampler.py create mode 100644 qiskit/chemistry/bopes_sampler.py~ diff --git a/qiskit/chemistry/bopes_sampler.py b/qiskit/chemistry/bopes_sampler.py new file mode 100644 index 0000000000..1b7e13df8f --- /dev/null +++ b/qiskit/chemistry/bopes_sampler.py @@ -0,0 +1,368 @@ +# -*- coding: utf-8 -*- + +# 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. +"""The calculation of points on the Born-Oppenheimer Potential Energy Surface (BOPES).""" + +import logging +from typing import Optional, Tuple, List, Dict + +import numpy as np +import pandas as pd +from qiskit.aqua import AquaError +from qiskit.aqua.algorithms import VQAlgorithm, VQE, MinimumEigensolver + +from .energy_surface_spline import EnergySurfaceBase +from .extrapolator import Extrapolator +from .molecule import Molecule + +logger = logging.getLogger(__name__) + +class BOPESSampler: + + def __init__(self, + driver, + GroundStateCalculation)->None: + + self._driver = driver + self._GroundStateCalculation = GroundStateCalculation + + #TODO link with the BOPES Sampler and the Extrapolators + + def run_points(self, points): + + #TODO driver needs to provide molecule and perturbed geometry + + for point in points: + + point_geometry = self.driver.molecule.get_perturbed_geometry() + driver = self.driver(point_geometry) + + + + + +class MGSE: + """Class to evaluate the Born-Oppenheimer Potential Energy Surface (BOPES). + + # TODO merge with existing Molecular GSE? + """ + + def __init__(self, + molecule: Molecule, + min_eigensolver: MinimumEigensolver, + tolerance: float = 1e-3, + resample: bool = True, + bootstrap: bool = True, + num_bootstrap: Optional[int] = None, + extrapolators: Optional[List[Extrapolator]] = None) -> None: + + """ + Args: + molecule: Molecule object of interest. + min_eigensolver: The specific eigensolver method to use to find minimum + eigenvalue/energy. + tolerance: Tolerance desired for minimum energy. + resample: Whether to resample final energy to reduce sampling error below + tolerance. + bootstrap: Whether to warm-start the solve of variational minimum eigensolvers. + num_bootstrap: Number of previous points for extrapolation + and bootstrapping. If None and a list of extrapolators is defined, + all prev points will be used except the first two points will be used for + bootstrapping. If no extrapolator is defined and bootstrap is True, + all previous points will be used for bootstrapping. + extrapolators: Extrapolator objects that define space/window and method to extrapolate + variational parameters. First and second elements refer to the wrapper and internal + extrapolators + + Raises: + AquaError: If ``num_boostrap`` is an integer smaller than 2. + """ + self._molecule = molecule + self._min_eigensolver = min_eigensolver + self._tolerance = tolerance + self._resample = resample + self._bootstrap = bootstrap + self._results = None # minimal DataFrame of [points, energies] + self._results_full = None # whole dict-of-dict-of-results + self._points_optparams = None + self._num_bootstrap = num_bootstrap + self._extrapolator_wrap = None + + # set wrapper and internal extrapolators + if extrapolators: + # todo: assumed len(extrapolators) == 2 + self._extrapolator_wrap = extrapolators[0] # wrapper + self._extrapolator_wrap.extrapolator = extrapolators[1] # internal extrapolator + # set default number of bootstrapping points to 2 + if num_bootstrap is None: + self._num_bootstrap = 2 + self._extrapolator_wrap.window = 0 + elif num_bootstrap >= 2: + self._num_bootstrap = num_bootstrap + self._extrapolator_wrap.window = num_bootstrap # window for extrapolator + else: + raise AquaError( + 'num_bootstrap must be None or an integer greater than or equal to 2') + + if isinstance(self._min_eigensolver, VQAlgorithm): + # Save initial point passed to min_eigensolver; + # this will be used when NOT bootstrapping + self._initial_point = self._min_eigensolver.initial_point + + if logger.isEnabledFor(logging.DEBUG): + mo_string = str(self._molecule) + me_string = str(self._min_eigensolver) + log_string = "\nConstructing BOPES Sampler with:" + \ + "\nMolecule: {}".format(mo_string) + \ + "\nMin Eigensolver: {}".format(me_string) + logger.info(log_string) + + def run(self, points: List[float], reps: int = 1) -> pd.DataFrame: + """Run the sampler at the given points, potentially with repetitions. + + Args: + points: The points along the degrees of freedom to evaluate. + reps: Number of independent repetitions of this overall calculation. + + Returns: + The results as pandas dataframe. + """ + self._results = pd.DataFrame() + self._results_full = dict() + for i in range(reps): + logger.info('Repetition %s of %s', i + 1, reps) + results, results_full = self.run_points(points) + self._results_full[i] = results_full + self._results = self._results.append(results) + return self._results + + def run_points(self, points: List[float]) -> Tuple[pd.DataFrame, Dict[float, dict]]: + """Run the sampler at the given points. + + Args: + points: the points along the single degree of freedom to evaluate + + Returns: + The results for all points. + """ + results = pd.DataFrame() + results_full = dict() + if isinstance(self._min_eigensolver, VQAlgorithm): + # Save optimal parameters if its a variational algorithm. + # We deliberately empty this out so that any repetitions of this + # run remain independent of each other. + # Set initial point to default + self._points_optparams = dict() + self._min_eigensolver.initial_point = self._initial_point + + # Iterate over the points + for i, point in enumerate(points): + logger.info('Point %s of %s', i + 1, len(points)) + + try: + result = self._run_single_point(point) # execute single point here + except (Exception) as e: + logger.warning("Point {} failed with exception {}".format(point, e)) + + results_full[point] = result + + dataframe = pd.DataFrame(result, columns=['point', 'energy'], index=[i]) + # todo: optimizer_evals is present only in some of the result classes + dataframe['optimizer_evals'] = result.get('optimizer_evals') + # for i, param in enumerate(result['optimal_point']): + # df['optimal_param_' + str(i) + ''] = param + results = results.append(dataframe) + # end loop + return results, results_full + + def _run_single_point(self, point: float) -> dict: + """Run the sampler at the given single point + + Args: + point: The value of the degree of freedom to evaluate. + + Returns: + Results for a single point. + """ + # get Hamiltonian + hamiltonian_op = self._molecule.get_qubitop_hamiltonian([point]) + + # Warm start the solver; + # find closest previously run point and take optimal parameters + if isinstance(self._min_eigensolver, VQAlgorithm) and self._bootstrap: + prev_points = list(self._points_optparams.keys()) + prev_params = list(self._points_optparams.values()) + n_pp = len(prev_points) + # set number of points to bootstrap + if self._extrapolator_wrap is None: + n_boot = len(prev_points) # bootstrap all points + else: + n_boot = self._num_bootstrap + + # Set initial params if prev_points not empty + if prev_points: + if n_pp <= n_boot: + distances = np.array(point) - \ + np.array(prev_points).reshape(n_pp, -1) + # find min 'distance' from point to previous points + min_index = np.argmin(np.linalg.norm(distances, axis=1)) + # update initial point + self._min_eigensolver.initial_point = prev_params[min_index] + else: # extrapolate using saved parameters + opt_params = self._points_optparams + param_sets = self._extrapolator_wrap.extrapolate(points=[point], + param_dict=opt_params) + # update initial point, note param_set is a list + self._min_eigensolver.initial_point = param_sets.get( + point) # param set is a dictionary + + logger.info("Degree of Freedom value: %s", point) + logger.info("Hamiltonian:\n %s", hamiltonian_op) + logger.info("Starting Minimum Eigenvalue solve...") + + # Find minimum eigenvalue + results = dict(self._min_eigensolver.compute_minimum_eigenvalue(hamiltonian_op)) + if self._resample: + final_energy, extra_evals = self._resampler() + results['eigenvalue'] = final_energy + results['cost_function_evals'] += extra_evals + else: + logger.info("Not resampling final energy") + + logger.info("Finished Minimum Eigenvalue solve") + logger.info("Minimum energy: %s", results['eigenvalue']) + + # Customize results dictionary + results['point'] = point + results['energy'] = np.real(results['eigenvalue']) + # Save optimal point to bootstrap + if isinstance(self._min_eigensolver, VQAlgorithm): + # at every point evaluation, the optimal params are updated + optimal_params = self._min_eigensolver.optimal_params + self._points_optparams[point] = optimal_params + return results + + def _resampler(self) -> Tuple[float, int]: + """Resample energy to mitigate sampling error/other noise. + + Will re-evaluate energy enough times to get standard deviation below ``self._tolerance``. + + Returns: + A tuple containing the resampled energy and the number of additional evaluations made. + + Raises: + TypeError: If the min_eigensolver is not the VQE. + AquaError: If there's a mismatch in the objective energy and the the mean of the + callback. + """ + # I only know how to make this work with VQE + if not isinstance(self._min_eigensolver, VQE): + raise TypeError('Currently only the VQE is handled as minimum eigensolver.') + # logger.info("NOT resampling (minimum eigensolver is not VQE)") + # return + + optimal_parameters = self._min_eigensolver.optimal_params + + # resampling is better if we can use a callback; + callback_preserver = { + 'eval_count': None, + 'params': None, + 'mean': None, + 'std': None} + + def callback(eval_count, params, mean, std): + callback_preserver['eval_count'] = eval_count + callback_preserver['params'] = params + callback_preserver['mean'] = mean + callback_preserver['std'] = std + + original_shots = self._min_eigensolver.quantum_instance.run_config.shots + original_callback = self._min_eigensolver._callback + self._min_eigensolver._callback = callback + + # Evaluate energy one more time, at optimal parameters + # and get back standard deviation estimate (from callback) + # Calculate how many times we need to re-sample the objective + # in order to get an objective estimate with std deviation below desired tolerance + # (Averaging objective n times has variance (objective variance)/n) + extra_evals = 1 + objective_val = self._min_eigensolver._energy_evaluation(optimal_parameters) + n_repeat = (callback_preserver['std'] / self._tolerance) ** 2 + if not np.isclose(objective_val, callback_preserver['mean'], 1e-7): + raise AquaError("Mismatch in objective/energy in callback") + + logger.info("Objective std dev: %s, repeats: %.2f", callback_preserver['std'], n_repeat) + + # oval = [] + # for i in range(40): + # oval.append(self._min_eigensolver._energy_evaluation(optimal_parameters)) + # print("Empirical std: {}".format(np.sqrt(np.var(oval, ddof=1)))) + # print("Calced std: {}".format(callback_preserver['std'])) + + if n_repeat > 1: + total_shots = int(n_repeat * original_shots) + # System limits; + # max_shots = 8192 is a hard shot limit for hardware + # max_reps controls total size of job/circuits sent + # (depending on circuit complexity may be larger/smaller) + max_shots = 8192 + max_reps = 128 + total_shots = min(total_shots, max_shots * max_reps) + rounded_evals = np.round(total_shots / original_shots, decimals=2) + extra_evals += rounded_evals + logger.info("Resampling objective %s times", rounded_evals) + # Shot limit per call is 8192 (max_shots), + # so break up total shots in some number of chunks. + # If total shots is exactly divisible by 8192, great! what luck. + # If not, take the ceiling of the quotient - + # thats the number of chunks we'd have to do with at most 8192 shots each. + # Then determine shots per chunk for that number of chunks we'd + # have to do anyway + n_repeat_chunk = np.ceil(total_shots / max_shots) + chunk_shots = int(total_shots / n_repeat_chunk) + rep_param = np.repeat(np.reshape( + optimal_parameters, (1, -1)), n_repeat_chunk, axis=0).reshape(-1) + # Update shot count for resampling + self._min_eigensolver.quantum_instance.set_config(shots=chunk_shots) + # Final return value is mean of all function evaluations + objective_val = np.mean(self._min_eigensolver._energy_evaluation(rep_param)) + # Note that callback_preserver['eval_count'] counts this last + # call as "one" evaluation + # else + # std deviation already below desired tolerance + # use the value already calculated + resampled_energy = objective_val + + # set things back to normal + self._min_eigensolver._callback = original_callback + self._min_eigensolver.quantum_instance.set_config(shots=original_shots) + return resampled_energy, extra_evals + + def fit_to_surface(self, energy_surface: EnergySurfaceBase, dofs: List[int], + **kwargs) -> None: + """Fit the sampled energy points to the energy surface. + + Args: + energy_surface: An energy surface object. + dofs: A list of the degree-of-freedom dimensions to use as the independent + variables in the potential function fit. + **kwargs: Arguments to pass through to the potential's ``fit_to_data`` function. + """ + points_all_dofs = self._results['point'].to_numpy() + if len(points_all_dofs.shape) == 1: + points = points_all_dofs.tolist() + else: + points = points_all_dofs[:, dofs].tolist() + + energies = self._results['energy'].to_list() + energy_surface.fit_to_data(xdata=points, ydata=energies, **kwargs) diff --git a/qiskit/chemistry/bopes_sampler.py~ b/qiskit/chemistry/bopes_sampler.py~ new file mode 100644 index 0000000000..e5b235744c --- /dev/null +++ b/qiskit/chemistry/bopes_sampler.py~ @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- + +# 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. +"""The calculation of points on the Born-Oppenheimer Potential Energy Surface (BOPES).""" + +import logging +from typing import Optional, Tuple, List, Dict + +import numpy as np +import pandas as pd +from qiskit.aqua import AquaError +from qiskit.aqua.algorithms import VQAlgorithm, VQE, MinimumEigensolver + +from .energy_surface_spline import EnergySurfaceBase +from .extrapolator import Extrapolator +from .molecule import Molecule + +logger = logging.getLogger(__name__) + + +# suppress depreciation warnings +# TODO remove this!! +# warnings.filterwarnings("ignore", category=DeprecationWarning) + + +class MGSE: + """Class to evaluate the Born-Oppenheimer Potential Energy Surface (BOPES). + + # TODO merge with existing Molecular GSE? + """ + + def __init__(self, + molecule: Molecule, + min_eigensolver: MinimumEigensolver, + tolerance: float = 1e-3, + resample: bool = True, + bootstrap: bool = True, + num_bootstrap: Optional[int] = None, + # todo: replace with a single WindowExtrapolator? It is very likely. + extrapolators: Optional[List[Extrapolator]] = None) -> None: + """ + Args: + molecule: Molecule object of interest. + min_eigensolver: The specific eigensolver method to use to find minimum + eigenvalue/energy. + tolerance: Tolerance desired for minimum energy. + resample: Whether to resample final energy to reduce sampling error below + tolerance. + bootstrap: Whether to warm-start the solve of variational minimum eigensolvers. + num_bootstrap: Number of previous points for extrapolation + and bootstrapping. If None and a list of extrapolators is defined, + all prev points will be used except the first two points will be used for + bootstrapping. If no extrapolator is defined and bootstrap is True, + all previous points will be used for bootstrapping. + extrapolators: Extrapolator objects that define space/window and method to extrapolate + variational parameters. First and second elements refer to the wrapper and internal + extrapolators + + Raises: + AquaError: If ``num_boostrap`` is an integer smaller than 2. + """ + self._molecule = molecule + self._min_eigensolver = min_eigensolver + self._tolerance = tolerance + self._resample = resample + self._bootstrap = bootstrap + self._results = None # minimal DataFrame of [points, energies] + self._results_full = None # whole dict-of-dict-of-results + self._points_optparams = None + self._num_bootstrap = num_bootstrap + self._extrapolator_wrap = None + + # set wrapper and internal extrapolators + if extrapolators: + # todo: assumed len(extrapolators) == 2 + self._extrapolator_wrap = extrapolators[0] # wrapper + self._extrapolator_wrap.extrapolator = extrapolators[1] # internal extrapolator + # set default number of bootstrapping points to 2 + if num_bootstrap is None: + self._num_bootstrap = 2 + self._extrapolator_wrap.window = 0 + elif num_bootstrap >= 2: + self._num_bootstrap = num_bootstrap + self._extrapolator_wrap.window = num_bootstrap # window for extrapolator + else: + raise AquaError( + 'num_bootstrap must be None or an integer greater than or equal to 2') + + if isinstance(self._min_eigensolver, VQAlgorithm): + # Save initial point passed to min_eigensolver; + # this will be used when NOT bootstrapping + self._initial_point = self._min_eigensolver.initial_point + + if logger.isEnabledFor(logging.DEBUG): + mo_string = str(self._molecule) + me_string = str(self._min_eigensolver) + log_string = "\nConstructing BOPES Sampler with:" + \ + "\nMolecule: {}".format(mo_string) + \ + "\nMin Eigensolver: {}".format(me_string) + logger.info(log_string) + + def run(self, points: List[float], reps: int = 1) -> pd.DataFrame: + """Run the sampler at the given points, potentially with repetitions. + + Args: + points: The points along the degrees of freedom to evaluate. + reps: Number of independent repetitions of this overall calculation. + + Returns: + The results as pandas dataframe. + """ + self._results = pd.DataFrame() + self._results_full = dict() + for i in range(reps): + logger.info('Repetition %s of %s', i + 1, reps) + results, results_full = self.run_points(points) + self._results_full[i] = results_full + self._results = self._results.append(results) + return self._results + + def run_points(self, points: List[float]) -> Tuple[pd.DataFrame, Dict[float, dict]]: + """Run the sampler at the given points. + + Args: + points: the points along the single degree of freedom to evaluate + + Returns: + The results for all points. + """ + results = pd.DataFrame() + results_full = dict() + if isinstance(self._min_eigensolver, VQAlgorithm): + # Save optimal parameters if its a variational algorithm. + # We deliberately empty this out so that any repetitions of this + # run remain independent of each other. + # Set initial point to default + self._points_optparams = dict() + self._min_eigensolver.initial_point = self._initial_point + + # Iterate over the points + for i, point in enumerate(points): + logger.info('Point %s of %s', i + 1, len(points)) + + # Run the single point + # try: + result = self._run_single_point(point) # execute single point here + # except (Exception) as e: + # logger.warning("Point {} failed with exception {}".format(point, e)) + # # Any bootstrapping/warm start will be less effective, + # # but just keep on trucking + # continue + # Record result + results_full[point] = result + # results = results.append(pd.DataFrame(result, columns=['point', 'energy'], index=[i])) + # NOTE added other options for dataframe output + dataframe = pd.DataFrame(result, columns=['point', 'energy'], index=[i]) + # todo: optimizer_evals is present only in some of the result classes + dataframe['optimizer_evals'] = result.get('optimizer_evals') + # for i, param in enumerate(result['optimal_point']): + # df['optimal_param_' + str(i) + ''] = param + results = results.append(dataframe) + # end loop + return results, results_full + + def _run_single_point(self, point: float) -> dict: + """Run the sampler at the given single point + + Args: + point: The value of the degree of freedom to evaluate. + + Returns: + Results for a single point. + """ + # get Hamiltonian + hamiltonian_op = self._molecule.get_qubitop_hamiltonian([point]) + + # Warm start the solver; + # find closest previously run point and take optimal parameters + if isinstance(self._min_eigensolver, VQAlgorithm) and self._bootstrap: + prev_points = list(self._points_optparams.keys()) + prev_params = list(self._points_optparams.values()) + n_pp = len(prev_points) + # set number of points to bootstrap + if self._extrapolator_wrap is None: + n_boot = len(prev_points) # bootstrap all points + else: + n_boot = self._num_bootstrap + + # Set initial params if prev_points not empty + if prev_points: + if n_pp <= n_boot: + distances = np.array(point) - \ + np.array(prev_points).reshape(n_pp, -1) + # find min 'distance' from point to previous points + min_index = np.argmin(np.linalg.norm(distances, axis=1)) + # update initial point + self._min_eigensolver.initial_point = prev_params[min_index] + else: # extrapolate using saved parameters + opt_params = self._points_optparams + param_sets = self._extrapolator_wrap.extrapolate(points=[point], + param_dict=opt_params) + # update initial point, note param_set is a list + self._min_eigensolver.initial_point = param_sets.get( + point) # param set is a dictionary + + logger.info("Degree of Freedom value: %s", point) + logger.info("Hamiltonian:\n %s", hamiltonian_op) + logger.info("Starting Minimum Eigenvalue solve...") + + # Find minimum eigenvalue + results = dict(self._min_eigensolver.compute_minimum_eigenvalue(hamiltonian_op)) + if self._resample: + final_energy, extra_evals = self._resampler() + results['eigenvalue'] = final_energy + results['cost_function_evals'] += extra_evals + else: + logger.info("Not resampling final energy") + + logger.info("Finished Minimum Eigenvalue solve") + logger.info("Minimum energy: %s", results['eigenvalue']) + + # Customize results dictionary + results['point'] = point + results['energy'] = np.real(results['eigenvalue']) + # Save optimal point to bootstrap + if isinstance(self._min_eigensolver, VQAlgorithm): + # at every point evaluation, the optimal params are updated + optimal_params = self._min_eigensolver.optimal_params + self._points_optparams[point] = optimal_params + return results + + def _resampler(self) -> Tuple[float, int]: + """Resample energy to mitigate sampling error/other noise. + + Will re-evaluate energy enough times to get standard deviation below ``self._tolerance``. + + Returns: + A tuple containing the resampled energy and the number of additional evaluations made. + + Raises: + TypeError: If the min_eigensolver is not the VQE. + AquaError: If there's a mismatch in the objective energy and the the mean of the + callback. + """ + # I only know how to make this work with VQE + if not isinstance(self._min_eigensolver, VQE): + raise TypeError('Currently only the VQE is handled as minimum eigensolver.') + # logger.info("NOT resampling (minimum eigensolver is not VQE)") + # return + + optimal_parameters = self._min_eigensolver.optimal_params + + # resampling is better if we can use a callback; + callback_preserver = { + 'eval_count': None, + 'params': None, + 'mean': None, + 'std': None} + + def callback(eval_count, params, mean, std): + callback_preserver['eval_count'] = eval_count + callback_preserver['params'] = params + callback_preserver['mean'] = mean + callback_preserver['std'] = std + + original_shots = self._min_eigensolver.quantum_instance.run_config.shots + original_callback = self._min_eigensolver._callback + self._min_eigensolver._callback = callback + + # Evaluate energy one more time, at optimal parameters + # and get back standard deviation estimate (from callback) + # Calculate how many times we need to re-sample the objective + # in order to get an objective estimate with std deviation below desired tolerance + # (Averaging objective n times has variance (objective variance)/n) + extra_evals = 1 + objective_val = self._min_eigensolver._energy_evaluation(optimal_parameters) + n_repeat = (callback_preserver['std'] / self._tolerance) ** 2 + if not np.isclose(objective_val, callback_preserver['mean'], 1e-7): + raise AquaError("Mismatch in objective/energy in callback") + + logger.info("Objective std dev: %s, repeats: %.2f", callback_preserver['std'], n_repeat) + + # oval = [] + # for i in range(40): + # oval.append(self._min_eigensolver._energy_evaluation(optimal_parameters)) + # print("Empirical std: {}".format(np.sqrt(np.var(oval, ddof=1)))) + # print("Calced std: {}".format(callback_preserver['std'])) + + if n_repeat > 1: + total_shots = int(n_repeat * original_shots) + # System limits; + # max_shots = 8192 is a hard shot limit for hardware + # max_reps controls total size of job/circuits sent + # (depending on circuit complexity may be larger/smaller) + max_shots = 8192 + max_reps = 128 + total_shots = min(total_shots, max_shots * max_reps) + rounded_evals = np.round(total_shots / original_shots, decimals=2) + extra_evals += rounded_evals + logger.info("Resampling objective %s times", rounded_evals) + # Shot limit per call is 8192 (max_shots), + # so break up total shots in some number of chunks. + # If total shots is exactly divisible by 8192, great! what luck. + # If not, take the ceiling of the quotient - + # thats the number of chunks we'd have to do with at most 8192 shots each. + # Then determine shots per chunk for that number of chunks we'd + # have to do anyway + n_repeat_chunk = np.ceil(total_shots / max_shots) + chunk_shots = int(total_shots / n_repeat_chunk) + rep_param = np.repeat(np.reshape( + optimal_parameters, (1, -1)), n_repeat_chunk, axis=0).reshape(-1) + # Update shot count for resampling + self._min_eigensolver.quantum_instance.set_config(shots=chunk_shots) + # Final return value is mean of all function evaluations + objective_val = np.mean(self._min_eigensolver._energy_evaluation(rep_param)) + # Note that callback_preserver['eval_count'] counts this last + # call as "one" evaluation + # else + # std deviation already below desired tolerance + # use the value already calculated + resampled_energy = objective_val + + # set things back to normal + self._min_eigensolver._callback = original_callback + self._min_eigensolver.quantum_instance.set_config(shots=original_shots) + return resampled_energy, extra_evals + + def fit_to_surface(self, energy_surface: EnergySurfaceBase, dofs: List[int], + **kwargs) -> None: + """Fit the sampled energy points to the energy surface. + + Args: + energy_surface: An energy surface object. + dofs: A list of the degree-of-freedom dimensions to use as the independent + variables in the potential function fit. + **kwargs: Arguments to pass through to the potential's ``fit_to_data`` function. + """ + points_all_dofs = self._results['point'].to_numpy() + if len(points_all_dofs.shape) == 1: + points = points_all_dofs.tolist() + else: + points = points_all_dofs[:, dofs].tolist() + + energies = self._results['energy'].to_list() + energy_surface.fit_to_data(xdata=points, ydata=energies, **kwargs) diff --git a/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py b/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py index 84cbf3e6a0..62498739c5 100644 --- a/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py +++ b/qiskit/chemistry/excited_state_calculation/ExcitedStatesCalculation.py @@ -12,25 +12,35 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. +from abc import ABC from qiskit.chemistry.ground_state_calculation import GroundStateCalculation from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, ChemistryOperator) from qiskit.chemistry.core import MolecularExcitedStatesResult -class ExcitedStatesCalculation(GroundStateCalculation): +class ExcitedStatesCalculation(ABC): 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: + ground_state_calculation : GroundStateCalculation)-> None: + + #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: - super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, - z2symmetry_reduction) + self._ground_state_calc = GroundStateCalculation + + #.__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, + # z2symmetry_reduction) + + def get_qubit_ops(self, driver): + + self._ground_state_calc._transform(driver) + @abstractmethod def compute_excited_states(self, driver: BaseDriver) -> MolecularExcitedStatesResult: From d10f5f1d5634b06ee25eab6d35a63f26b25d35cf Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 21 Sep 2020 14:54:45 +0200 Subject: [PATCH 012/197] naming --- qiskit/chemistry/core/chemistry_operator.py | 2 +- qiskit/chemistry/core/hamiltonian.py | 2 +- .../qubit_transformations/fermionic_transformation.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index 16f0e7cdc6..e1fe44fa2d 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -48,7 +48,7 @@ def __init__(self): self._molecule_info = {} @abstractmethod - def run(self, qmolecule): + def _do_transform(self, qmolecule): """ Convert the qmolecule, according to the ChemistryOperator, into an Operator that can be given to a QuantumAlgorithm diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index 89a2e0265e..30e5f8ec6d 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -114,7 +114,7 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - def run(self, qmolecule): + def _do_tranform(self, qmolecule): logger.debug('Processing started...') # Save these values for later combination with the quantum computation result self._hf_energy = qmolecule.hf_energy diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 5aaa1786e4..9522c2ee9f 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -121,10 +121,10 @@ def __init__(self, def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: q_molecule = driver.run() - ops, aux_ops = self.run(q_molecule)#_do_transform(q_molecule) + ops, aux_ops = self._do_transform(q_molecule)#_do_transform(q_molecule) return ops, aux_ops - def run(self,qmolecule): #TODO CHANGE NAMING IN CHEMISTRY OPERATOR # _do_transform(self, qmolecule): + def _do_transform(self,qmolecule): logger.debug('Processing started...') # Save these values for later combination with the quantum computation result self._hf_energy = qmolecule.hf_energy @@ -206,7 +206,7 @@ def run(self,qmolecule): #TODO CHANGE NAMING IN CHEMISTRY OPERATOR # _do_transfo qubit_op = FermionicTransformation._map_fermionic_operator_to_qubit(fer_op, self._qubit_mapping, new_nel, self._two_qubit_reduction) - qubit_op.name = 'Fermionic Transformation' + qubit_op.name = 'Fermionic Operator' logger.debug(' num paulis: %s, num qubits: %s', len(qubit_op.paulis), qubit_op.num_qubits) From a83e3540097069fa000c6a56f3b1c61af4ad78d6 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 21 Sep 2020 16:45:04 +0200 Subject: [PATCH 013/197] compute eigenvalue MinEigensolver works --- .../GroundStateCalculation.py | 8 +-- .../MinEigensolverGroundStateCalculation.py | 67 ++++++++++--------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py index 3b86f76aac..26aeda9527 100644 --- a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py @@ -63,7 +63,7 @@ def compute_ground_state(self, """ raise NotImplementedError() - @abstractmethod - def returns_groundstate() -> bool: - # needs to return whether this calculation only returns groundstate energies or also groundstates - raise NotImplementedError + #@abstractmethod + #def returns_groundstate() -> bool: + # # needs to return whether this calculation only returns groundstate energies or also groundstates + # raise NotImplementedError diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index 20c68af741..6502a83079 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -31,7 +31,7 @@ class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): MinimumEigensolverGroundStateCalculation """ - def __init__(self, solver: Optional[MinimumEigensolver] = None, transformation) -> None: + def __init__(self, transformation, solver: Optional[MinimumEigensolver] = None) -> None: """ Args: solver: a minimum eigensolver @@ -70,50 +70,51 @@ def compute_ground_state(self, operator, aux_operators = self._transformation.transform(driver) if callback is not None: - num_particles = self.molecule_info[ChemistryOperator.INFO_NUM_PARTICLES] - num_orbitals = self.molecule_info[ChemistryOperator.INFO_NUM_ORBITALS] - z2_symmetries = self.molecule_info[ChemistryOperator.INFO_Z2SYMMETRIES] - self._solver = callback(num_particles, num_orbitals, - self._qubit_mapping.value, self._two_qubit_reduction, - z2_symmetries) + #TODO We should expose all these as properties from the transformation + num_particles = self._transformation.molecule_info['num_particles'] + num_orbitals = self._transformation.molecule_info['num_orbitals'] + z2_symmetries = self._transformation.molecule_info['z2symmetries'] + self._solver = callback(num_particles, num_orbitals,self._transformation._qubit_mapping, self._transformation._two_qubit_reduction, z2_symmetries) aux_operators = aux_operators if self._solver.supports_aux_operators() else None - raw_gs_result = self._solver.compute_minimum_eigenstate(operator, aux_operators) + raw_gs_result = self._solver.compute_minimum_eigenvalue(operator, aux_operators) - # WOR: where should this post processing be coming from? - gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], groundstate) # gs = array/circuit+params - gsc_result.raw_result = raw_gs_results + # TODO WOR: where should this post processing be coming from? + #gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], groundstate) # gs = array/circuit+params + #gsc_result.raw_result = raw_gs_results - return + return raw_gs_result['energy'] # (energy, aux_values, groundsntate) - class MesFactory(): + #class MesFactory(): + + def get_default_solver(quantum_instance: Union[QuantumInstance, BaseBackend]) ->\ + Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]]: - - def get_default_solver(quantum_instance: Union[QuantumInstance, BaseBackend]) ->\ - Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]]: - """ - Get the default solver callback that can be used with :meth:`compute_energy` + """ + Get the default solver callback that can be used with :meth:`compute_energy` Args: quantum_instance: A Backend/Quantum Instance for the solver to run on Returns: Default solver callback - """ - def cb_default_solver(num_particles, num_orbitals, - qubit_mapping, two_qubit_reduction, z2_symmetries): - """ Default solver """ - initial_state = HartreeFock(num_orbitals, num_particles, qubit_mapping, + """ + def cb_default_solver(num_particles, num_orbitals, + qubit_mapping, two_qubit_reduction, z2_symmetries): + """ Default solver """ + 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) - vqe.quantum_instance = quantum_instance - return vqe - return cb_default_solver + 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) + vqe.quantum_instance = quantum_instance + return vqe + + return cb_default_solver From e73d4aa22842d9f7b42315106ed6cdb47fca6160 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 21 Sep 2020 18:14:57 +0200 Subject: [PATCH 014/197] return raw results until I make the GroundStateResult --- .../MinEigensolverGroundStateCalculation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index 6502a83079..734b1dcbc2 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -84,7 +84,7 @@ def compute_ground_state(self, #gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], groundstate) # gs = array/circuit+params #gsc_result.raw_result = raw_gs_results - return raw_gs_result['energy'] + return raw_gs_result # (energy, aux_values, groundsntate) From 1b45a9242f4dd3e63454f3ec0fab789190dc57dd Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 21 Sep 2020 19:49:02 +0200 Subject: [PATCH 015/197] now the fermionic transformation returns a molecular ground state result working with mineigensolver result --- .../MinEigensolverGroundStateCalculation.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index 734b1dcbc2..7057b86748 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -46,6 +46,8 @@ def compute_ground_state(self, callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]] = None # WOR: callback should be in constructor, and possibly follow an interface ) -> MolecularGroundStateResult: + #TODO MolecularGroundStateResult should become generic for bosonic and fermionic Hamiltonains + """Compute Ground State properties. Args: @@ -81,10 +83,11 @@ def compute_ground_state(self, raw_gs_result = self._solver.compute_minimum_eigenvalue(operator, aux_operators) # TODO WOR: where should this post processing be coming from? + # The post processing is now in the tranformation so that it is fermionic or bosonic #gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], groundstate) # gs = array/circuit+params - #gsc_result.raw_result = raw_gs_results - return raw_gs_result + #TODO rename process algorithm result to interpret + return self._transformation.process_algorithm_result(raw_gs_result) # (energy, aux_values, groundsntate) From 1019641907d93084e7bf0475a8bf9b53f98fd5af Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 21 Sep 2020 19:50:03 +0200 Subject: [PATCH 016/197] added TODOs --- .../MinEigensolverGroundStateCalculation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py index 7057b86748..9bab8c7c66 100644 --- a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py +++ b/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py @@ -87,6 +87,8 @@ def compute_ground_state(self, #gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], groundstate) # gs = array/circuit+params #TODO rename process algorithm result to interpret + #TODO we need the process_algorith_result also for the bosonic transformation + return self._transformation.process_algorithm_result(raw_gs_result) # (energy, aux_values, groundsntate) From 65ec2cc6854b3acdfb059b4c2c2e5a7709318394 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Tue, 22 Sep 2020 13:20:16 +0200 Subject: [PATCH 017/197] rename GSC files --- ...dStateCalculation.py => adapt_vqe_ground_state_calculation.py} | 0 .../{GroundStateCalculation.py => ground_state_calculation.py} | 0 ...rGroundStateCalculation.py => mse_ground_state_calculation.py} | 0 ...lation.py => orbital_optimization_ground_state_calculation.py} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename qiskit/chemistry/ground_state_calculation/{AdaptVQEGroundStateCalculation.py => adapt_vqe_ground_state_calculation.py} (100%) rename qiskit/chemistry/ground_state_calculation/{GroundStateCalculation.py => ground_state_calculation.py} (100%) rename qiskit/chemistry/ground_state_calculation/{MinEigensolverGroundStateCalculation.py => mse_ground_state_calculation.py} (100%) rename qiskit/chemistry/ground_state_calculation/{OrbitalOptimizationGroundStateCalculation.py => orbital_optimization_ground_state_calculation.py} (100%) diff --git a/qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py similarity index 100% rename from qiskit/chemistry/ground_state_calculation/AdaptVQEGroundStateCalculation.py rename to qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py diff --git a/qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py similarity index 100% rename from qiskit/chemistry/ground_state_calculation/GroundStateCalculation.py rename to qiskit/chemistry/ground_state_calculation/ground_state_calculation.py diff --git a/qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/mse_ground_state_calculation.py similarity index 100% rename from qiskit/chemistry/ground_state_calculation/MinEigensolverGroundStateCalculation.py rename to qiskit/chemistry/ground_state_calculation/mse_ground_state_calculation.py diff --git a/qiskit/chemistry/ground_state_calculation/OrbitalOptimizationGroundStateCalculation.py b/qiskit/chemistry/ground_state_calculation/orbital_optimization_ground_state_calculation.py similarity index 100% rename from qiskit/chemistry/ground_state_calculation/OrbitalOptimizationGroundStateCalculation.py rename to qiskit/chemistry/ground_state_calculation/orbital_optimization_ground_state_calculation.py From 98756e2ef64b6892a0c42ebd7cce856ad8d3cded Mon Sep 17 00:00:00 2001 From: Cryoris Date: Tue, 22 Sep 2020 14:00:04 +0200 Subject: [PATCH 018/197] add init to qubit_transformations --- .../qubit_transformations/__init__.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 qiskit/chemistry/qubit_transformations/__init__.py diff --git a/qiskit/chemistry/qubit_transformations/__init__.py b/qiskit/chemistry/qubit_transformations/__init__.py new file mode 100644 index 0000000000..393f47a714 --- /dev/null +++ b/qiskit/chemistry/qubit_transformations/__init__.py @@ -0,0 +1,21 @@ +# 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. + +"""Qubit operator transformation module.""" + +from .bosonic_transformation import BosonicTransformation +from .fermionic_transformation import FermionicTransformation +from .qubit_operator_transformation import QubitOperatorTransformation + +__all__ = ['BosonicTransformation', + 'FermionicTransformation', + 'QubitOperatorTransformation'] From 945ec048f9bc8b2322232ec28b8075ce805786c3 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Tue, 22 Sep 2020 14:04:50 +0200 Subject: [PATCH 019/197] fix lint in bosonic_trafo and qubit_op_trafo --- .../bosonic_transformation.py | 28 ++++++++++++--- .../qubit_operator_transformation.py | 36 ++++++++++++++++--- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py index e513ac75b4..d0580e60e5 100644 --- a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py @@ -1,16 +1,36 @@ -from qiskit.chemistry.drivers import BaseDriver +# 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. + +"""TODO""" + from typing import Tuple, List + +from qiskit.chemistry.drivers import BaseDriver from qiskit.aqua.operators.legacy import WeightedPauliOperator from .qubit_operator_transformation import QubitOperatorTransformation + class BosonicTransformation(QubitOperatorTransformation): + """TODO""" def __init__(self, h, basis): pass - def transform(driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + def transform(self, driver: BaseDriver + ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + """TODO""" raise NotImplementedError() # take code from bosonic operator - def interpret(...): - pass \ No newline at end of file + def interpret(self): + """TODO""" + pass diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 61a522e30b..292c351144 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -1,15 +1,41 @@ +# 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. + +"""TODO""" + from abc import ABC, abstractmethod -from qiskit.chemistry.drivers import BaseDriver from typing import Tuple, List + from qiskit.aqua.operators.legacy import WeightedPauliOperator +from qiskit.aqua.algorithms import MinimumEigensolverResult +from qiskit.chemistry.core import MolecularGroundStateResult +from qiskit.chemistry.drivers import BaseDriver class QubitOperatorTransformation(ABC): + """TODO""" + + @abstractmethod + def transform(self, driver: BaseDriver + ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + """TODO""" + raise NotImplementedError @abstractmethod - def transform(driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: - raise NotImplementedError() + def interpret(self, raw_gs_result: MinimumEigensolverResult) -> MolecularGroundStateResult: + """TODO""" + raise NotImplementedError - #@abstractmethod - #def interpret(value, aux_values, circuit, params=None): # -> GroundStateResult: # might be fermionic / bosonic + # @abstractmethod + # def interpret(value, aux_values, circuit, params=None): + # -> GroundStateResult: # might be fermionic / bosonic # raise NotImplementedError() From cd0b613b0e6d251418652ed8eff5a6a33aca5e1b Mon Sep 17 00:00:00 2001 From: Cryoris Date: Tue, 22 Sep 2020 14:05:36 +0200 Subject: [PATCH 020/197] update (MES)GSC to new structure --- .../ground_state_calculation.py | 60 ++++----- .../ground_state_calculation/mes_factory.py | 60 +++++++++ .../mes_ground_state_calculation.py | 73 ++++++++++ .../mse_ground_state_calculation.py | 125 ------------------ 4 files changed, 159 insertions(+), 159 deletions(-) create mode 100644 qiskit/chemistry/ground_state_calculation/mes_factory.py create mode 100644 qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py delete mode 100644 qiskit/chemistry/ground_state_calculation/mse_ground_state_calculation.py diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index 26aeda9527..4556ac46cc 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -10,21 +10,13 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -A ground state calculation interface. -""" +"""The ground state calculation interface.""" from abc import ABC, abstractmethod -from typing import List, Optional, Union, Tuple, Dict, Callable -from qiskit.aqua.operators import LegacyBaseOperator -from qiskit.aqua.algorithms import MinimumEigensolver -from qiskit.aqua.operators import Z2Symmetries from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, - MolecularGroundStateResult) -from qiskit.chemistry.qubit_transformations.qubit_operator_transformation import \ - QubitOperatorTransformation +from qiskit.chemistry.core import MolecularGroundStateResult +from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation class GroundStateCalculation(ABC): @@ -37,33 +29,33 @@ def __init__(self, transformation: QubitOperatorTransformation) -> None: """ self._transformation = transformation - @abstractmethod - def compute_ground_state(self, - driver: BaseDriver, additional_ops, - callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], - MinimumEigensolver]] = None - ) -> MolecularGroundStateResult: + @property + def transformation(self) -> QubitOperatorTransformation: + """Return the tranformation used obtain a qubit operator from the molecule. + + Returns: + The transformation. """ - Compute the ground state energy of the molecule that was supplied via the driver. + return self._transformation + + @abstractmethod + def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: + """Compute the ground state energy of the molecule that was supplied via the driver. Args: - driver: a chemical driver - callback: If not None will be called with the following values - num_particles, num_orbitals, qubit_mapping, two_qubit_reduction, z2_symmetries - in that order. This information can then be used to setup chemistry - specific component(s) that are needed by the chosen MinimumEigensolver. - The MinimumEigensolver can then be built and returned from this callback - for use as the solver here. + driver: TODO Returns: - A molecular ground state result - Raises: - QiskitChemistryError: If no MinimumEigensolver was given and no callback is being - used that could supply one instead. + A molecular ground state result TODO """ - raise NotImplementedError() + raise NotImplementedError - #@abstractmethod - #def returns_groundstate() -> bool: - # # needs to return whether this calculation only returns groundstate energies or also groundstates - # raise NotImplementedError + @abstractmethod + def returns_groundstate(self) -> bool: + """Whether this class returns only the groundstate energy or also the groundstate itself. + + Returns: + True, if this class also returns the ground state in the results object. + False otherwise. + """ + raise NotImplementedError diff --git a/qiskit/chemistry/ground_state_calculation/mes_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factory.py new file mode 100644 index 0000000000..4f69c0a950 --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/mes_factory.py @@ -0,0 +1,60 @@ +# 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 qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import MinimumEigensolver, VQE +from qiskit.chemistry.components.variational_forms import UCCSD +from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation +from qiskit.chemistry.components.initial_states import HartreeFock + + +class MESFactory: + """A factory to construct a minimum eigensolver suitable for a qubit operator transformation. + """ + + def __init__(self, quantum_instance: QuantumInstance) -> None: + """ + Args: + quantum_instance: The quantum instance used in the minimum eigensolver. + """ + self._quantum_instance = quantum_instance + + def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEigensolver: + """Returns a minimum eigensolver, based on the qubit operator transformation. + + By default the VQE with a UCCSD wavefunction ansatz is returned. + + Args: + transformation: The qubit operator transformation. + + Returns: + A minimum eigensolver 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._molecule_info['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) + return vqe diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py new file mode 100644 index 0000000000..be0c0dd5d4 --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -0,0 +1,73 @@ +# 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 + +from qiskit.aqua.algorithms import MinimumEigensolver +from qiskit.chemistry.core import MolecularGroundStateResult +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation + +from .mes_factory import MESFactory + + +class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): + """TODO""" + + def __init__(self, transformation: QubitOperatorTransformation, + solver: Union[MinimumEigensolver, MESFactory]) -> None: + """ + Args: + transformation: TODO + solver: TODO + """ + super().__init__(transformation) + self._solver = solver + + @property + def solver(self) -> Union[MinimumEigensolver, MESFactory]: + """Get the minimum eigensolver or factory.""" + return self._solver + + def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: + # TODO MolecularGroundStateResult should become generic for bosonic and fermionic + # Hamiltonains + """Compute Ground State properties. + + Args: + driver: A chemistry driver. + + Returns: + A molecular ground state result + """ + operator, aux_operators = self.transformation.transform(driver) + + if isinstance(solver, MESFactory): + # this must be called after transformation.transform + solver = self.solver.get_solver(self.transformation) # TODO and driver? + else: + solver = self.solver + + # TODO shouldn't this rather raise a warning? + aux_operators = aux_operators if self.solver.supports_aux_operators() else None + + raw_gs_result = self.solver.compute_minimum_eigenvalue(operator, aux_operators) + + # TODO WOR: where should this post processing be coming from? + # The post processing is now in the tranformation so that it is fermionic or bosonic + # gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], + # groundstate) # gs = array/circuit+params + return self.transformation.interpret(raw_gs_result) + # (energy, aux_values, groundsntate) diff --git a/qiskit/chemistry/ground_state_calculation/mse_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mse_ground_state_calculation.py deleted file mode 100644 index 9bab8c7c66..0000000000 --- a/qiskit/chemistry/ground_state_calculation/mse_ground_state_calculation.py +++ /dev/null @@ -1,125 +0,0 @@ -# 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 Aqua minimum eigensolver """ - -from typing import List, Optional, Callable, Union - -from qiskit.aqua import QuantumInstance -from qiskit.aqua.algorithms import MinimumEigensolver, VQE -from qiskit.aqua.operators import Z2Symmetries -from qiskit.chemistry import QiskitChemistryError -from qiskit.chemistry.components.initial_states import HartreeFock -from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry.core import (TransformationType, QubitMappingType, ChemistryOperator, - MolecularGroundStateResult) -from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.ground_state_calculation import GroundStateCalculation -from qiskit.providers import BaseBackend - -class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): - """ - MinimumEigensolverGroundStateCalculation - """ - - def __init__(self, transformation, solver: Optional[MinimumEigensolver] = None) -> None: - """ - Args: - solver: a minimum eigensolver - transformation: - """ - - self._solver = solver - super().__init__(transformation) - - def compute_ground_state(self, - driver: BaseDriver, - callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], - MinimumEigensolver]] = None # WOR: callback should be in constructor, and possibly follow an interface - ) -> MolecularGroundStateResult: - #TODO MolecularGroundStateResult should become generic for bosonic and fermionic Hamiltonains - - """Compute Ground State properties. - - Args: - driver: A chemistry driver. - callback: If not None will be called with the following values - num_particles, num_orbitals, qubit_mapping, two_qubit_reduction, z2_symmetries - in that order. This information can then be used to setup chemistry - specific component(s) that are needed by the chosen MinimumEigensolver. - The MinimumEigensolver can then be built and returned from this callback - for use as the solver here. - - Returns: - A molecular ground state result - Raises: - QiskitChemistryError: If no MinimumEigensolver was given and no callback is being - used that could supply one instead. - """ - - if self._solver is None and callback is None: - raise QiskitChemistryError('Minimum Eigensolver was not provided') - - operator, aux_operators = self._transformation.transform(driver) - - if callback is not None: - #TODO We should expose all these as properties from the transformation - num_particles = self._transformation.molecule_info['num_particles'] - num_orbitals = self._transformation.molecule_info['num_orbitals'] - z2_symmetries = self._transformation.molecule_info['z2symmetries'] - self._solver = callback(num_particles, num_orbitals,self._transformation._qubit_mapping, self._transformation._two_qubit_reduction, z2_symmetries) - - aux_operators = aux_operators if self._solver.supports_aux_operators() else None - - raw_gs_result = self._solver.compute_minimum_eigenvalue(operator, aux_operators) - - # TODO WOR: where should this post processing be coming from? - # The post processing is now in the tranformation so that it is fermionic or bosonic - #gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], groundstate) # gs = array/circuit+params - - #TODO rename process algorithm result to interpret - #TODO we need the process_algorith_result also for the bosonic transformation - - return self._transformation.process_algorithm_result(raw_gs_result) - # (energy, aux_values, groundsntate) - - - #class MesFactory(): - - def get_default_solver(quantum_instance: Union[QuantumInstance, BaseBackend]) ->\ - Optional[Callable[[List, int, str, bool, Z2Symmetries], MinimumEigensolver]]: - - """ - Get the default solver callback that can be used with :meth:`compute_energy` - Args: - quantum_instance: A Backend/Quantum Instance for the solver to run on - - Returns: - Default solver callback - """ - def cb_default_solver(num_particles, num_orbitals, - qubit_mapping, two_qubit_reduction, z2_symmetries): - """ Default solver """ - 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) - vqe.quantum_instance = quantum_instance - return vqe - - return cb_default_solver From 00cf578c626b4731b53567c854fd02f22435cb04 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Tue, 22 Sep 2020 14:12:49 +0200 Subject: [PATCH 021/197] rm empty orbital optimization file --- .../orbital_optimization_ground_state_calculation.py | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 qiskit/chemistry/ground_state_calculation/orbital_optimization_ground_state_calculation.py diff --git a/qiskit/chemistry/ground_state_calculation/orbital_optimization_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/orbital_optimization_ground_state_calculation.py deleted file mode 100644 index 26b06350eb..0000000000 --- a/qiskit/chemistry/ground_state_calculation/orbital_optimization_ground_state_calculation.py +++ /dev/null @@ -1,12 +0,0 @@ -#imports - - -class AdaptVQE(GroundStateCalculation): # same for VQEAdapt, ... - def __init__(self, params_for_mapping): - super().__init__(params_for_mapping) - def compute_ground_state(driver) -> GroundStateCalculationResult: - op = self._transform(driver) - # different implementation similar to VQE - result = None - # construct GroundStateCalculationResult - return ground_state_calculation_result From 1c6105dac445fbe1ca92a96e1e300a905cf47831 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 10:26:30 +0200 Subject: [PATCH 022/197] Fix init --- qiskit/chemistry/ground_state_calculation/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index c2ac500fbe..0e4485ab35 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -12,10 +12,12 @@ """TODO""" -from .GroundStateCalculation import GroundStateCalculation -from .AdaptVQEGroundStateCalculation import AdaptVQEGroundStateCalculation -from .MinEigensolverGroundStateCalculation import MinimumEigensolverGroundStateCalculation +from .ground_state_calculation import GroundStateCalculation +from .adapt_vqe_ground_state_calculation import AdaptVQEGroundStateCalculation +from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation +from .mes_factory import MESFactory __all__ = ['GroundStateCalculation', 'AdaptVQEGroundStateCalculation', - 'MinimumEigensolverGroundStateCalculation'] + 'MinimumEigensolverGroundStateCalculation', + 'MESFactory'] From 5d193f66f8d5729c9f94f0e8b9ab83fdab9f0d4a Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 10:27:02 +0200 Subject: [PATCH 023/197] Implement returns_groundstate and fix some lints --- .../mes_ground_state_calculation.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index be0c0dd5d4..09e4d8bf5f 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -41,6 +41,10 @@ def solver(self) -> Union[MinimumEigensolver, MESFactory]: """Get the minimum eigensolver or factory.""" return self._solver + def returns_groundstate(self) -> bool: + """TODO""" + return False + def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: # TODO MolecularGroundStateResult should become generic for bosonic and fermionic # Hamiltonains @@ -54,16 +58,16 @@ def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult """ operator, aux_operators = self.transformation.transform(driver) - if isinstance(solver, MESFactory): + if isinstance(self._solver, MESFactory): # this must be called after transformation.transform - solver = self.solver.get_solver(self.transformation) # TODO and driver? + solver = self._solver.get_solver(self.transformation) # TODO and driver? else: - solver = self.solver + solver = self._solver # TODO shouldn't this rather raise a warning? - aux_operators = aux_operators if self.solver.supports_aux_operators() else None + aux_operators = aux_operators if solver.supports_aux_operators() else None - raw_gs_result = self.solver.compute_minimum_eigenvalue(operator, aux_operators) + raw_gs_result = solver.compute_minimum_eigenvalue(operator, aux_operators) # TODO WOR: where should this post processing be coming from? # The post processing is now in the tranformation so that it is fermionic or bosonic From bd65fffe2a41c508ec4cefd9fc1683f95a2eafdb Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 10:27:27 +0200 Subject: [PATCH 024/197] Rewrite AdaptVQEGSCalc. deriving from MESGSCalc. --- .../adapt_vqe_ground_state_calculation.py | 93 ++++++------------- 1 file changed, 28 insertions(+), 65 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py index e9a5a0ce2e..ba4793ea53 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py @@ -15,99 +15,62 @@ """ import logging -from typing import List, Optional, Callable, Union from qiskit.aqua import QuantumInstance from qiskit.aqua.algorithms import MinimumEigensolver -from qiskit.aqua.operators import Z2Symmetries from qiskit.chemistry.algorithms import VQEAdapt from qiskit.chemistry.components.initial_states import HartreeFock from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry.core import (TransformationType, QubitMappingType, ChemistryOperator, - MolecularGroundStateResult) -from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.ground_state_calculation import GroundStateCalculation -from qiskit.providers import BaseBackend +from qiskit.chemistry.ground_state_calculation import (MinimumEigensolverGroundStateCalculation, + MESFactory) +from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation logger = logging.getLogger(__name__) -class AdaptVQEGroundStateCalculation(GroundStateCalculation): +class AdaptVQEGroundStateCalculation(MinimumEigensolverGroundStateCalculation): """A ground state calculation employing the VQEAdapt algorithm.""" + def __init__(self, - quantum_instance: Union[QuantumInstance, BaseBackend], - 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: + transformation: QubitOperatorTransformation, + quantum_instance: QuantumInstance) -> None: """ Args: - quantum_instance: a quantum instance - 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 the qubit operators - that are computed. 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. - See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core - processing behind this class. + transformation: TODO + quantum_instance: TODO """ - super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, - orbital_reduction, z2symmetry_reduction) + super().__init__(transformation, AdaptVQEFactory(quantum_instance)) + - self._quantum_instance = quantum_instance - # the solver object is used internally in order to be consistent with the - # GroundStateCalculation implementation - self._solver = None +class AdaptVQEFactory(MESFactory): + """TODO""" - def compute_ground_state(self, - driver: BaseDriver, - callback: Optional[Callable[[List, int, str, bool, Z2Symmetries], - MinimumEigensolver]] = None - ) -> MolecularGroundStateResult: - """Compute the ground state energy of the molecule that was supplied via the driver. + def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEigensolver: + """TODO Args: - driver: A chemistry driver. - callback: This argument will be ignored and is only provided for compatibility reasons! + transformation: TODO Returns: - A molecular ground state result. + TODO """ - operator, aux_operators = self._transform(driver) - - if callback is not None: - logger.warning("The `callback` option is only provided for compatibility reasons and \ - has no effect in this context!") - - # gather required data from molecule info - num_particles = self.molecule_info[ChemistryOperator.INFO_NUM_PARTICLES] - num_orbitals = self.molecule_info[ChemistryOperator.INFO_NUM_ORBITALS] - z2_symmetries = self.molecule_info[ChemistryOperator.INFO_Z2SYMMETRIES] + num_orbitals = transformation._molecule_info['num_orbitals'] + num_particles = transformation._molecule_info['num_particles'] + qubit_mapping = transformation._molecule_info['qubit_mapping'] + two_qubit_reduction = transformation._molecule_info['two_qubit_reduction'] + z2_symmetries = transformation._molecule_info['z2_symmetries'] # contract variational form base - initial_state = HartreeFock(num_orbitals, num_particles, self._qubit_mapping.value, - self._two_qubit_reduction, z2_symmetries.sq_list) + initial_state = HartreeFock(num_orbitals, num_particles, qubit_mapping.value, + two_qubit_reduction, z2_symmetries.sq_list) var_form_base = UCCSD(num_orbitals=num_orbitals, num_particles=num_particles, initial_state=initial_state, - qubit_mapping=self._qubit_mapping.value, - two_qubit_reduction=self._two_qubit_reduction, + qubit_mapping=qubit_mapping.value, + two_qubit_reduction=two_qubit_reduction, z2_symmetries=z2_symmetries) # initialize the adaptive VQE algorithm with the specified quantum instance - self._solver = VQEAdapt(var_form_base=var_form_base) - self._solver.quantum_instance = self._quantum_instance - - # run the algorithm and post-process the result - raw_gs_result = self._solver.compute_minimum_eigenvalue(operator, aux_operators) - return self._core.process_algorithm_result(raw_gs_result) + vqe = VQEAdapt(var_form_base=var_form_base, quantum_instance=self._quantum_instance) + return vqe From 90c5f608be5b53f60df2c6618b415b709b6655a5 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 10:29:54 +0200 Subject: [PATCH 025/197] Add TODO about deprecating qiskit.chemistry.algorithms module --- .../adapt_vqe_ground_state_calculation.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py index ba4793ea53..9745cad6e8 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py @@ -74,3 +74,8 @@ def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEige # initialize the adaptive VQE algorithm with the specified quantum instance vqe = VQEAdapt(var_form_base=var_form_base, quantum_instance=self._quantum_instance) return vqe + + +# TODO we can think about moving all of the VQEAdapt code (currently in +# qiskit.chemistry.algorithms.minimum_eigen_solvers) into this class and therefore deprecate the +# qiskit.chemistry.algorithms module (if we do the same with the QEOM code) From 9049480b8694c6d7dcb8fbb7430c82186906a42a Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 16:31:34 +0200 Subject: [PATCH 026/197] Revert "Add MinimumEigensolver interface to VQEAdapt" This reverts commit 362843b19ff199e1b824b4318b14eee1d5c3da66. --- .../minimum_eigen_solvers/vqe_adapt.py | 125 +++++------------- 1 file changed, 31 insertions(+), 94 deletions(-) diff --git a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py index 1504e7858d..ed6dfacb77 100644 --- a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py +++ b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py @@ -24,28 +24,26 @@ from qiskit import ClassicalRegister from qiskit.aqua import QuantumInstance, AquaError from qiskit.aqua.algorithms import VQAlgorithm, VQE, VQEResult -from qiskit.aqua.algorithms.minimum_eigen_solvers import (MinimumEigensolver, - MinimumEigensolverResult) +from qiskit.chemistry.components.variational_forms import UCCSD +from qiskit.aqua.operators import WeightedPauliOperator +from qiskit.aqua.operators import LegacyBaseOperator from qiskit.aqua.components.optimizers import Optimizer from qiskit.aqua.components.variational_forms import VariationalForm -from qiskit.aqua.operators import OperatorBase, LegacyBaseOperator, WeightedPauliOperator from qiskit.aqua.utils.validation import validate_min -from qiskit.chemistry.components.variational_forms import UCCSD logger = logging.getLogger(__name__) -class VQEAdapt(VQAlgorithm, MinimumEigensolver): +class VQEAdapt(VQAlgorithm): """ The Adaptive VQE algorithm. See https://arxiv.org/abs/1812.11173 """ - def __init__(self, - operator: LegacyBaseOperator = None, - var_form_base: VariationalForm = None, - optimizer: Optimizer = None, + # TODO make re-usable, implement MinimumEignesolver interface + def __init__(self, operator: LegacyBaseOperator, + var_form_base: VariationalForm, optimizer: Optimizer, initial_point: Optional[np.ndarray] = None, excitation_pool: Optional[List[WeightedPauliOperator]] = None, threshold: float = 1e-5, @@ -71,27 +69,24 @@ def __init__(self, Raises: ValueError: if var_form_base is not an instance of UCCSD. - See also: qiskit/chemistry/components/variational_forms/uccsd.py + See also: qiskit/chemistry/components/variational_forms/uccsd_adapt.py """ validate_min('threshold', threshold, 1e-15) validate_min('delta', delta, 1e-5) - - if var_form_base is None or not isinstance(var_form_base, UCCSD): - raise ValueError("var_form_base has to be an instance of UCCSD.") - - self._quantum_instance = None super().__init__(var_form=var_form_base, optimizer=optimizer, initial_point=initial_point, quantum_instance=quantum_instance) - self._ret = None # type: Dict self._optimizer.set_max_evals_grouped(max_evals_grouped) if initial_point is None: self._initial_point = var_form_base.preferred_init_points self._operator = operator - self._var_form.manage_hopping_operators() - self._excitation_pool = self._var_form.excitation_pool \ + if not isinstance(var_form_base, UCCSD): + raise ValueError("var_form_base has to be an instance of UCCSD.") + self._var_form_base = var_form_base + self._var_form_base.manage_hopping_operators() + self._excitation_pool = self._var_form_base.excitation_pool \ if excitation_pool is None else excitation_pool self._threshold = threshold self._delta = delta @@ -103,78 +98,21 @@ def __init__(self, for aux_op in aux_operators: self._aux_operators.append(aux_op) - @property - def operator(self) -> Optional[OperatorBase]: - """ Returns operator """ - return self._operator - - @operator.setter - def operator(self, operator: Union[OperatorBase, LegacyBaseOperator]) -> None: - """ set operator """ - self._operator = operator - - @property - def aux_operators(self) -> Optional[List[Optional[OperatorBase]]]: - """ Returns aux operators """ - return self._aux_operators - - @aux_operators.setter - def aux_operators(self, - aux_operators: Optional[ - Union[OperatorBase, - LegacyBaseOperator, - List[Optional[Union[OperatorBase, - LegacyBaseOperator]]]]]) -> None: - """ Set aux operators """ - if aux_operators is None: - aux_operators = [] - elif not isinstance(aux_operators, list): - aux_operators = [aux_operators] - - self._aux_operators = aux_operators # type: List - - @property - def quantum_instance(self) -> Optional[QuantumInstance]: - """ Returns quantum instance """ - return self._quantum_instance - - @quantum_instance.setter - def quantum_instance(self, quantum_instance) -> None: - """ set quantum instance """ - self._quantum_instance = quantum_instance - - def supports_aux_operators(self) -> bool: - return True - - def compute_minimum_eigenvalue( - self, - operator: Optional[Union[OperatorBase, LegacyBaseOperator]] = None, - aux_operators: Optional[List[Optional[Union[OperatorBase, - LegacyBaseOperator]]]] = None - ) -> MinimumEigensolverResult: - super().compute_minimum_eigenvalue(operator, aux_operators) - return self._run() - - def _compute_gradients(self, - excitation_pool: List, - theta: float, - delta: float, - var_form: VariationalForm, - operator: LegacyBaseOperator, - optimizer: Optimizer) -> List: + def _compute_gradients(self, excitation_pool, theta, delta, + var_form, operator, optimizer): """ Computes the gradients for all available excitation operators. Args: - excitation_pool: pool of excitation operators - theta: list of (up to now) optimal parameters - delta: finite difference step size (for gradient computation) - var_form: current variational form - operator: system Hamiltonian - optimizer: classical optimizer algorithm + excitation_pool (list): pool of excitation operators + theta (list): list of (up to now) optimal parameters + delta (float): finite difference step size (for gradient computation) + var_form (VariationalForm): current variational form + operator (LegacyBaseOperator): system Hamiltonian + optimizer (Optimizer): classical optimizer algorithm Returns: - List of pairs consisting of gradient and excitation operator. + list: List of pairs consisting of gradient and excitation operator. """ res = [] # compute gradients for all excitation in operator pool @@ -205,10 +143,9 @@ def _run(self) -> 'VQEAdaptResult': Raises: AquaError: wrong setting of operator and backend. """ - if self.operator is None: - raise AquaError("The operator was never provided.") - self._ret = {} # TODO should be eliminated + # self._operator = VQE._config_the_best_mode(self, self._operator, + # self._quantum_instance.backend) self._quantum_instance.circuit_summary = True threshold_satisfied = False @@ -223,7 +160,7 @@ def _run(self) -> 'VQEAdaptResult': logger.info('--- Iteration #%s ---', str(iteration)) # compute gradients cur_grads = self._compute_gradients(self._excitation_pool, theta, self._delta, - self._var_form, self._operator, + self._var_form_base, self._operator, self._optimizer) # pick maximum gradient max_grad_index, max_grad = max(enumerate(cur_grads), @@ -249,11 +186,11 @@ def _run(self) -> 'VQEAdaptResult': logger.info("Final maximum gradient: %s", str(np.abs(max_grad[0]))) alternating_sequence = True break - # add new excitation to self._var_form - self._var_form.push_hopping_operator(max_grad[1]) + # add new excitation to self._var_form_base + self._var_form_base.push_hopping_operator(max_grad[1]) theta.append(0.0) # run VQE on current Ansatz - algorithm = VQE(self._operator, self._var_form, self._optimizer, + algorithm = VQE(self._operator, self._var_form_base, self._optimizer, initial_point=theta) vqe_result = algorithm.run(self._quantum_instance) self._ret['opt_params'] = vqe_result.optimal_point @@ -266,7 +203,7 @@ def _run(self) -> 'VQEAdaptResult': # once finished evaluate auxiliary operators if any if self._aux_operators is not None and self._aux_operators: - algorithm = VQE(self._operator, self._var_form, self._optimizer, + algorithm = VQE(self._operator, self._var_form_base, self._optimizer, initial_point=theta, aux_operators=self._aux_operators) vqe_result = algorithm.run(self._quantum_instance) self._ret['opt_params'] = vqe_result.optimal_point @@ -296,7 +233,7 @@ def _check_cyclicity(indices: List) -> bool: Auxiliary function to check for cycles in the indices of the selected excitations. Returns: - Whether repeating sequences of indices have been detected. + bool: Whether repeating sequences of indices have been detected. """ cycle_regex = re.compile(r"(\b.+ .+\b)( \b\1\b)+") # reg-ex explanation: @@ -324,7 +261,7 @@ def get_optimal_circuit(self): if 'opt_params' not in self._ret: raise AquaError("Cannot find optimal circuit before running the " "algorithm to find optimal params.") - return self._var_form.construct_circuit(self._ret['opt_params']) + return self._var_form_base.construct_circuit(self._ret['opt_params']) def get_optimal_vector(self): # pylint: disable=import-outside-toplevel From 3045616febbcde507f9145e14fac61cd56ec849d Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 16:37:57 +0200 Subject: [PATCH 027/197] Properly deprecate VQEAdapt --- .../algorithms/minimum_eigen_solvers/vqe_adapt.py | 7 +++++-- test/chemistry/test_vqe_uccsd_adapt.py | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py index ed6dfacb77..430cb71298 100644 --- a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py +++ b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py @@ -35,8 +35,7 @@ class VQEAdapt(VQAlgorithm): - """ - The Adaptive VQE algorithm. + """DEPRECATED. The Adaptive VQE algorithm. See https://arxiv.org/abs/1812.11173 """ @@ -71,6 +70,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.ground_state_calculation.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/test/chemistry/test_vqe_uccsd_adapt.py b/test/chemistry/test_vqe_uccsd_adapt.py index 7df98b7e82..0316dc88cc 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,6 +79,8 @@ 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) result = algorithm.run(backend) From 5b85722ffc043e1c402c347abc8dc5240de0b314 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 17:12:11 +0200 Subject: [PATCH 028/197] Port VQEAdapt to AdaptVQE (based on a GroundStateCalculation) This properly refactors the old qiskit.chemistry.algorithms.minimum_eigen_solver.VQEAdapt into the new qiskit.chemistry.ground_state_calculation.AdaptVQE class which implements the new GroundStateCalculation interface. The name change is intentional because the new name is in-line with the literature on AdaptVQE. The old name was presumably intentional to highlight its relation to VQE. The unittests for VQEAdapt are ported accordingly. --- .../ground_state_calculation/__init__.py | 4 +- .../adapt_vqe_ground_state_calculation.py | 280 +++++++++++++++--- ...t_adaptive_vqe_ground_state_calculation.py | 82 ++++- 3 files changed, 313 insertions(+), 53 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index 0e4485ab35..11a941638d 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -13,11 +13,11 @@ """TODO""" from .ground_state_calculation import GroundStateCalculation -from .adapt_vqe_ground_state_calculation import AdaptVQEGroundStateCalculation +from .adapt_vqe_ground_state_calculation import AdaptVQE from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation from .mes_factory import MESFactory __all__ = ['GroundStateCalculation', - 'AdaptVQEGroundStateCalculation', + 'AdaptVQE', 'MinimumEigensolverGroundStateCalculation', 'MESFactory'] diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py index 9745cad6e8..3ce212bdd0 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py @@ -11,71 +11,261 @@ # that they have been altered from the originals. """ -A ground state calculation employing the VQEAdapt algorithm. +A ground state calculation employing the AdaptVQE algorithm. """ +from typing import Optional, List import logging +import re +import warnings +import numpy as np -from qiskit.aqua import QuantumInstance -from qiskit.aqua.algorithms import MinimumEigensolver -from qiskit.chemistry.algorithms import VQEAdapt -from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.aqua import AquaError +from qiskit.aqua.algorithms import VQEResult +from qiskit.aqua.operators import LegacyBaseOperator, WeightedPauliOperator +from qiskit.aqua.utils.validation import validate_min from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry.ground_state_calculation import (MinimumEigensolverGroundStateCalculation, - MESFactory) -from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation +from qiskit.chemistry.core import MolecularGroundStateResult +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.qubit_transformations import FermionicTransformation +from .ground_state_calculation import GroundStateCalculation +from .mes_factory import MESFactory logger = logging.getLogger(__name__) -class AdaptVQEGroundStateCalculation(MinimumEigensolverGroundStateCalculation): - """A ground state calculation employing the VQEAdapt algorithm.""" +class AdaptVQE(GroundStateCalculation): + """A ground state calculation employing the AdaptVQE algorithm.""" def __init__(self, - transformation: QubitOperatorTransformation, - quantum_instance: QuantumInstance) -> None: + transformation: FermionicTransformation, + solver: MESFactory, + threshold: float = 1e-5, + delta: float = 1, + max_iterations: Optional[int] = None, + # TODO allow overriding the excitation pool + ) -> None: """ Args: - transformation: TODO - quantum_instance: TODO + transformation: a fermionic driver to operator transformation strategy. + solver: a minimum eigensolver factory which uses the 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, AdaptVQEFactory(quantum_instance)) + super().__init__(transformation) + self._solver = solver + self._threshold = threshold + self._delta = delta + self._max_iterations = max_iterations -class AdaptVQEFactory(MESFactory): - """TODO""" + def returns_groundstate(self) -> bool: + """TODO""" + return False - def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEigensolver: - """TODO + def _compute_gradients(self, + excitation_pool: List[WeightedPauliOperator], + theta: List[float], + var_form: UCCSD, + operator: LegacyBaseOperator, + ) -> List: + """ + Computes the gradients for all available excitation operators. Args: - transformation: TODO + excitation_pool: pool of excitation operators + theta: list of (up to now) optimal parameters + var_form: current variational form + operator: system Hamiltonian + + 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 + var_form.push_hopping_operator(exc) + # construct auxiliary VQE instance + vqe = self._solver.get_solver(self._transformation) + vqe.operator = operator + vqe.var_form = var_form + vqe.initial_point = var_form.preferred_init_points + # 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 + 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: - TODO + Whether repeating sequences of indices have been detected. """ - num_orbitals = transformation._molecule_info['num_orbitals'] - num_particles = transformation._molecule_info['num_particles'] - qubit_mapping = transformation._molecule_info['qubit_mapping'] - two_qubit_reduction = transformation._molecule_info['two_qubit_reduction'] - z2_symmetries = transformation._molecule_info['z2_symmetries'] - - # contract variational form base - initial_state = HartreeFock(num_orbitals, num_particles, qubit_mapping.value, - two_qubit_reduction, z2_symmetries.sq_list) - var_form_base = UCCSD(num_orbitals=num_orbitals, - num_particles=num_particles, - initial_state=initial_state, - qubit_mapping=qubit_mapping.value, - two_qubit_reduction=two_qubit_reduction, - z2_symmetries=z2_symmetries) - - # initialize the adaptive VQE algorithm with the specified quantum instance - vqe = VQEAdapt(var_form_base=var_form_base, quantum_instance=self._quantum_instance) - return vqe - - -# TODO we can think about moving all of the VQEAdapt code (currently in -# qiskit.chemistry.algorithms.minimum_eigen_solvers) into this class and therefore deprecate the -# qiskit.chemistry.algorithms module (if we do the same with the QEOM code) + 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 compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: + """Computes the ground state. + + Args: + driver: a chemistry driver. + Raises: + AquaError: if a variational form other than UCCSD is provided or if the algorithm + finishes due to an unforeseen reason. + Returns: + A ground state result. + """ + operator, aux_operators = self._transformation.transform(driver) + + vqe = self._solver.get_solver(self._transformation) + var_form = vqe.var_form + if not isinstance(var_form, UCCSD): + raise AquaError("The AdaptVQE algorithm requires the use of the UCCSD variational form") + + var_form.manage_hopping_operators() + excitation_pool = var_form.excitation_pool + + threshold_satisfied = False + alternating_sequence = False + max_iterations_exceeded = False + prev_op_indices = [] + theta = [] # type: List + max_grad = (0, 0) + iteration = 0 + while self._max_iterations is None or iteration < self._max_iterations: + iteration += 1 + logger.info('--- Iteration #%s ---', str(iteration)) + # compute gradients + cur_grads = self._compute_gradients(excitation_pool, theta, var_form, operator) + # pick maximum gradient + max_grad_index, max_grad = max(enumerate(cur_grads), + key=lambda item: np.abs(item[1][0])) + # store maximum gradient's index for cycle detection + prev_op_indices.append(max_grad_index) + # log gradients + gradlog = "\nGradients in iteration #{}".format(str(iteration)) + gradlog += "\nID: Excitation Operator: Gradient <(*) maximum>" + for i, grad in enumerate(cur_grads): + gradlog += '\n{}: {}: {}'.format(str(i), str(grad[1]), str(grad[0])) + if grad[1] == max_grad[1]: + gradlog += '\t(*)' + logger.info(gradlog) + if np.abs(max_grad[0]) < self._threshold: + logger.info("Adaptive VQE terminated succesfully with a final maximum gradient: %s", + str(np.abs(max_grad[0]))) + threshold_satisfied = True + break + # check indices of picked gradients for cycles + if 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 + var_form.push_hopping_operator(max_grad[1]) + theta.append(0.0) + # run VQE on current Ansatz + vqe.var_form = var_form + 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 and aux_operators: + vqe.compute_minimum_eigenvalue(operator, aux_operators) + + if threshold_satisfied: + finishing_criterion = 'Threshold converged' + elif alternating_sequence: + finishing_criterion = 'Aborted due to cyclicity' + elif max_iterations_exceeded: + finishing_criterion = 'Maximum number of iterations reached' + else: + raise AquaError('The algorithm finished due to an unforeseen reason!') + + # extend VQE returned information with additional outputs + raw_result = AdaptVQEResult() + raw_result.combine(raw_vqe_result) + raw_result.num_iterations = iteration + raw_result.final_max_gradient = max_grad[0] + raw_result.finishing_criterion = finishing_criterion + + logger.info('The final energy is: %s', str(raw_result.optimal_value.real)) + return raw_result + # TODO: return self.transformation.interpret(raw_result) + + +class AdaptVQEResult(VQEResult): + """ 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 + + def __getitem__(self, key: object) -> object: + if key == 'final_max_grad': + warnings.warn('final_max_grad deprecated, use final_max_gradient property.', + DeprecationWarning) + return super().__getitem__('final_max_gradient') + + return super().__getitem__(key) diff --git a/test/chemistry/test_adaptive_vqe_ground_state_calculation.py b/test/chemistry/test_adaptive_vqe_ground_state_calculation.py index 06f981e95a..3866169ff9 100644 --- a/test/chemistry/test_adaptive_vqe_ground_state_calculation.py +++ b/test/chemistry/test_adaptive_vqe_ground_state_calculation.py @@ -17,12 +17,18 @@ 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.ground_state_calculation import AdaptVQEGroundStateCalculation +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.ground_state_calculation import AdaptVQE, MESFactory +from qiskit.chemistry.qubit_transformations import FermionicTransformation -class TestAdaptVQEGroundStateCalculation(QiskitChemistryTestCase): +class TestAdaptVQE(QiskitChemistryTestCase): """ Test Adaptive VQE Ground State Calculation """ + def setUp(self): super().setUp() @@ -34,15 +40,79 @@ def setUp(self): self.skipTest('PYSCF driver does not appear to be installed') return - self.expected = -1.137306 + self.expected = -1.85727503 - self.qinst = QuantumInstance(BasicAer.get_backend('statevector_simulator')) + self.transformation = FermionicTransformation() def test_default(self): """ Default execution """ - calc = AdaptVQEGroundStateCalculation(self.qinst) + solver = MESFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + calc = AdaptVQE(self.transformation, solver) + res = calc.compute_ground_state(self.driver) + self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) + + def test_custom_minimum_eigensolver(self): + """ Test custom MES """ + solver = MESFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + + def get_custom_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['z2symmetries'] + 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 + + # pylint: disable=no-value-for-parameter + solver.get_solver = get_custom_solver.__get__(solver, MESFactory) + + calc = AdaptVQE(self.transformation, solver) res = calc.compute_ground_state(self.driver) - self.assertAlmostEqual(res.energy, self.expected, places=6) + self.assertAlmostEqual(res.eigenvalue.real, 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() From cc03798c99ce6996b3be1eb617cf85b5556199d1 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 17:16:53 +0200 Subject: [PATCH 029/197] Some minor corrections/updates --- qiskit/chemistry/ground_state_calculation/mes_factory.py | 4 ++-- .../qubit_transformations/fermionic_transformation.py | 3 +++ .../qubit_transformations/qubit_operator_transformation.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factory.py index 4f69c0a950..26b763db90 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factory.py @@ -45,9 +45,9 @@ def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEige num_orbitals = transformation._molecule_info['num_orbitals'] num_particles = transformation._molecule_info['num_particles'] - qubit_mapping = transformation._molecule_info['qubit_mapping'] + qubit_mapping = transformation._qubit_mapping two_qubit_reduction = transformation._molecule_info['two_qubit_reduction'] - z2_symmetries = transformation._molecule_info['z2_symmetries'] + z2_symmetries = transformation._molecule_info['z2symmetries'] initial_state = HartreeFock(num_orbitals, num_particles, qubit_mapping, two_qubit_reduction, z2_symmetries.sq_list) var_form = UCCSD(num_orbitals=num_orbitals, diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 9522c2ee9f..1d6745fb99 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -369,6 +369,9 @@ def _pick_sector(z2_symmetries, hf_str): z2_symmetries.tapering_values = taper_coef return z2_symmetries + def interpret(self, raw_gs_result: MinimumEigensolverResult) -> MolecularGroundStateResult: + return self._process_algorithm_result_ground_state(raw_gs_result) + # Called by public superclass method process_algorithm_result to complete specific processing def _process_algorithm_result(self, algo_result): if isinstance(algo_result, MinimumEigensolverResult): diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 292c351144..c97d379b4b 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -31,7 +31,7 @@ def transform(self, driver: BaseDriver raise NotImplementedError @abstractmethod - def interpret(self, raw_gs_result: MinimumEigensolverResult) -> MolecularGroundStateResult: + def interpret(self, value: float, state: List[float], aux_values: dict) -> MolecularGroundStateResult: """TODO""" raise NotImplementedError From d8b5f07c2bb222d87921eb67fc4e6256d828a856 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 17:18:35 +0200 Subject: [PATCH 030/197] Rename AdaptVQE files --- qiskit/chemistry/ground_state_calculation/__init__.py | 2 +- .../{adapt_vqe_ground_state_calculation.py => adapt_vqe.py} | 0 ...aptive_vqe_ground_state_calculation.py => test_adapt_vqe.py} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename qiskit/chemistry/ground_state_calculation/{adapt_vqe_ground_state_calculation.py => adapt_vqe.py} (100%) rename test/chemistry/{test_adaptive_vqe_ground_state_calculation.py => test_adapt_vqe.py} (100%) diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index 11a941638d..78e691f106 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -13,7 +13,7 @@ """TODO""" from .ground_state_calculation import GroundStateCalculation -from .adapt_vqe_ground_state_calculation import AdaptVQE +from .adapt_vqe import AdaptVQE from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation from .mes_factory import MESFactory diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py similarity index 100% rename from qiskit/chemistry/ground_state_calculation/adapt_vqe_ground_state_calculation.py rename to qiskit/chemistry/ground_state_calculation/adapt_vqe.py diff --git a/test/chemistry/test_adaptive_vqe_ground_state_calculation.py b/test/chemistry/test_adapt_vqe.py similarity index 100% rename from test/chemistry/test_adaptive_vqe_ground_state_calculation.py rename to test/chemistry/test_adapt_vqe.py From c60bc56e83e5aba513a33c780bc789df5d8ce020 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 24 Sep 2020 17:57:38 +0200 Subject: [PATCH 031/197] Add optional custom_excitation_pool argument to AdaptVQE computation method --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 3ce212bdd0..2eaa96f10d 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -43,7 +43,6 @@ def __init__(self, threshold: float = 1e-5, delta: float = 1, max_iterations: Optional[int] = None, - # TODO allow overriding the excitation pool ) -> None: """ Args: @@ -133,16 +132,21 @@ def _check_cyclicity(indices: List[int]) -> bool: # nature of the algorithm. return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) - def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: + # pylint: disable=arguments-differ + def compute_ground_state(self, driver: BaseDriver, + custom_excitation_pool: List[WeightedPauliOperator] = None + ) -> MolecularGroundStateResult: """Computes the ground state. Args: driver: a chemistry driver. + custom_excitation_pool: list of excitation operators to choose from. Raises: AquaError: if a variational form other than UCCSD is provided or if the algorithm finishes due to an unforeseen reason. Returns: A ground state result. + TODO replace with FermionicGroundStateResult """ operator, aux_operators = self._transformation.transform(driver) @@ -152,7 +156,7 @@ def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult raise AquaError("The AdaptVQE algorithm requires the use of the UCCSD variational form") var_form.manage_hopping_operators() - excitation_pool = var_form.excitation_pool + excitation_pool = custom_excitation_pool or var_form.excitation_pool threshold_satisfied = False alternating_sequence = False From dce955a54aae0091caef2c116b4039326bc6834f Mon Sep 17 00:00:00 2001 From: Cryoris Date: Mon, 28 Sep 2020 15:30:36 +0200 Subject: [PATCH 032/197] Work on FermionicTransformation and related objs - add interpet method to QubitOperatorTransformation and FermionicTransformation - remove deprecated method `_process_..._deprecated` - deprecate Hamiltonian and ChemistryOperator --- qiskit/chemistry/core/chemistry_operator.py | 10 +- qiskit/chemistry/core/hamiltonian.py | 5 +- .../fermionic_transformation.py | 185 ++++-------------- .../qubit_operator_transformation.py | 9 +- 4 files changed, 50 insertions(+), 159 deletions(-) diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index e1fe44fa2d..fb305af0e0 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -16,7 +16,9 @@ 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 import numpy as np @@ -45,6 +47,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 @@ -155,8 +161,8 @@ class MolecularGroundStateResult(MolecularChemistryResult): Energies are in Hartree and dipole moments in A.U unless otherwise stated. """ - #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) + # 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]: diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index 30e5f8ec6d..c421dd6636 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -82,6 +82,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 +117,7 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - def _do_tranform(self, qmolecule): + def _do_transform(self, qmolecule): logger.debug('Processing started...') # Save these values for later combination with the quantum computation result self._hf_energy = qmolecule.hf_energy diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 1d6745fb99..44d3e30bdc 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -13,21 +13,18 @@ This module implements a molecular Hamiltonian operator, representing the 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 -from typing import List, Optional, Callable, Union, Tuple - import numpy as np -from qiskit.aqua.algorithms import MinimumEigensolverResult, EigensolverResult +from qiskit.aqua.algorithms import MinimumEigensolverResult from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator -from qiskit.chemistry import QMolecule, QiskitChemistryError +from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.fermionic_operator import FermionicOperator from qiskit.chemistry.core.chemistry_operator import (ChemistryOperator, - MolecularGroundStateResult, - DipoleTuple) + MolecularGroundStateResult, + DipoleTuple) from qiskit.chemistry.drivers import BaseDriver from .qubit_operator_transformation import QubitOperatorTransformation @@ -119,12 +116,13 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, + List[WeightedPauliOperator]]: q_molecule = driver.run() - ops, aux_ops = self._do_transform(q_molecule)#_do_transform(q_molecule) + ops, aux_ops = self._do_transform(q_molecule) # _do_transform(q_molecule) return ops, aux_ops - def _do_transform(self,qmolecule): + def _do_transform(self, qmolecule): logger.debug('Processing started...') # Save these values for later combination with the quantum computation result self._hf_energy = qmolecule.hf_energy @@ -203,9 +201,9 @@ def _do_transform(self,qmolecule): self._ph_energy_shift = -ph_shift logger.info("Particle hole energy shift: %s", self._ph_energy_shift) 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 = 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) @@ -213,10 +211,9 @@ def _do_transform(self,qmolecule): aux_ops = [] def _add_aux_op(aux_op, name): - aux_qop = FermionicTransformation._map_fermionic_operator_to_qubit(aux_op, - self._qubit_mapping, - new_nel, - self._two_qubit_reduction) + 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) @@ -369,38 +366,33 @@ def _pick_sector(z2_symmetries, hf_str): z2_symmetries.tapering_values = taper_coef return z2_symmetries - def interpret(self, raw_gs_result: MinimumEigensolverResult) -> MolecularGroundStateResult: - return self._process_algorithm_result_ground_state(raw_gs_result) + # pylint: disable=unused-argument + def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list + ) -> MolecularGroundStateResult: + """Interpret eigenvalue and eigenstate of qubit Hamiltonian w.r.t. driver. - # Called by public superclass method process_algorithm_result to complete specific processing - def _process_algorithm_result(self, algo_result): - if isinstance(algo_result, MinimumEigensolverResult): - return self._process_algorithm_result_ground_state(algo_result) - elif isinstance(algo_result, EigensolverResult): - return self._process_algorithm_result_deprecated(algo_result) - # TODO return self._process_algorithm_result_excited_states(algo_result) - else: - return self._process_algorithm_result_deprecated(algo_result) + Args: + TODO - def _process_algorithm_result_ground_state(self, algo_result: MinimumEigensolverResult) \ - -> MolecularGroundStateResult: + Returns: + TODO + """ mgsr = MolecularGroundStateResult() - mgsr.algorithm_result = algo_result mgsr.hartree_fock_energy = self._hf_energy mgsr.nuclear_repulsion_energy = self._nuclear_repulsion_energy if self._nuclear_dipole_moment is not None: mgsr.nuclear_dipole_moment = tuple(x for x in self._nuclear_dipole_moment) - mgsr.computed_electronic_energy = algo_result.eigenvalue.real + mgsr.computed_electronic_energy = eigenvalue.real mgsr.ph_extracted_energy = self._ph_energy_shift mgsr.frozen_extracted_energy = self._energy_shift - aux_ops_vals = algo_result.aux_operator_eigenvalues + aux_ops_vals = aux_values if aux_ops_vals is not None: # Dipole results if dipole aux ops were present dipole_idx = 3 if len(aux_ops_vals) > dipole_idx: mgsr.reverse_dipole_sign = self._reverse_dipole_sign dipm = [] - for i in range(dipole_idx, dipole_idx+3): # Gets X, Y and Z components + for i in range(dipole_idx, dipole_idx + 3): # Gets X, Y and Z components dipm.append(aux_ops_vals[i][0].real if aux_ops_vals[i] is not None else None) mgsr.computed_dipole_moment = cast(DipoleTuple, tuple(dipm)) mgsr.ph_extracted_dipole_moment = (self._ph_x_dipole_shift, @@ -418,120 +410,15 @@ def _process_algorithm_result_ground_state(self, algo_result: MinimumEigensolver if aux_ops_vals[2] is not None else None return mgsr - def _process_algorithm_result_deprecated(self, algo_result): - warnings.warn('Processing a dictionary result is deprecated,' - ' pass a (minimum) eigensolver result now.', DeprecationWarning) - # pylint: disable=len-as-condition - result = {} - - # Ground state energy - egse = algo_result['energy'] + self._energy_shift + self._ph_energy_shift - result['energy'] = egse - lines = ['=== GROUND STATE ENERGY ==='] - lines.append(' ') - lines.append('* Electronic ground state energy (Hartree): {}'.format(round(egse, 12))) - lines.append(' - computed part: {}'.format(round(algo_result['energy'], 12))) - lines.append(' - frozen energy part: {}'.format(round(self._energy_shift, 12))) - lines.append(' - particle hole part: {}'.format(round(self._ph_energy_shift, 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._nuclear_repulsion_energy + egse, 12))) - if 'aux_ops' in algo_result and len(algo_result['aux_ops']) > 0: - aux_ops = algo_result['aux_ops'][0] - num_particles = aux_ops[0][0] - spin_squared = aux_ops[1][0] - spin = (-1.0 + np.sqrt(1 + 4 * spin_squared)) / 2 - m = aux_ops[2][0] - lines.append( - ' Measured:: Num particles: {:.3f}, S: {:.3f}, M: {:.5f}'.format( - num_particles, spin, m)) - result['energy'] = self._nuclear_repulsion_energy + egse - result['nuclear_repulsion_energy'] = self._nuclear_repulsion_energy - if self._hf_energy is not None: - result['hf_energy'] = self._hf_energy - - # Excited states list - it includes ground state too - if 'energies' in algo_result: - exsce = \ - [x + self._energy_shift + self._ph_energy_shift for x in algo_result['energies']] - exste = [x + self._nuclear_repulsion_energy for x in exsce] - result['energies'] = exste - if len(exsce) > 1: - lines.append(' ') - lines.append('=== EXCITED STATES ===') - lines.append(' ') - lines.append( - '> Excited states energies (plus ground): {}'.format( - [round(x, 12) for x in exste])) - lines.append( - ' - computed: {}'.format([round(x, 12) for x in algo_result['energies']])) - if 'cond_number' in algo_result: # VQKE condition num for eigen vals - lines.append(' - cond num: {}'.format(algo_result['cond_number'])) - - if 'aux_ops' in algo_result and len(algo_result['aux_ops']) > 0: - lines.append( - ' ......................................................................') - lines.append( - ' ###: Total Energy, Computed, # particles, S M') - for i in range(len(algo_result['aux_ops'])): - aux_ops = algo_result['aux_ops'][i] - num_particles = aux_ops[0][0] - spin_squared = aux_ops[1][0] - spin = (-1.0 + np.sqrt(1 + 4 * spin_squared)) / 2 - m = aux_ops[2][0] - lines.append( - ' {:>3}: {: 16.12f}, {: 16.12f}, {:5.3f}, {:5.3f}, {:8.5f}'. - format(i, exste[i], algo_result['energies'][i], num_particles, spin, m)) - else: - result['energies'] = [result['energy']] - - # Dipole computation - dipole_idx = 3 - if 'aux_ops' in algo_result and len(algo_result['aux_ops']) > 0 and \ - len(algo_result['aux_ops'][0]) > dipole_idx: - dipole_moments_x = algo_result['aux_ops'][0][dipole_idx + 0][0] - dipole_moments_y = algo_result['aux_ops'][0][dipole_idx + 1][0] - dipole_moments_z = algo_result['aux_ops'][0][dipole_idx + 2][0] - - _elec_dipole = \ - np.array([dipole_moments_x + self._x_dipole_shift + self._ph_x_dipole_shift, - dipole_moments_y + self._y_dipole_shift + self._ph_y_dipole_shift, - dipole_moments_z + self._z_dipole_shift + self._ph_z_dipole_shift]) - lines.append(' ') - lines.append('=== DIPOLE MOMENT ===') - lines.append(' ') - lines.append('* Electronic dipole moment (a.u.): {}'.format( - FermionicTransformation._dipole_to_string(_elec_dipole))) - lines.append(' - computed part: {}'.format( - FermionicTransformation._dipole_to_string([dipole_moments_x, - dipole_moments_y, dipole_moments_z]))) - lines.append(' - frozen energy part: {}'.format( - FermionicTransformation._dipole_to_string([self._x_dipole_shift, - self._y_dipole_shift, self._z_dipole_shift]))) - lines.append(' - particle hole part: {}'.format( - FermionicTransformation._dipole_to_string([self._ph_x_dipole_shift, - self._ph_y_dipole_shift, self._ph_z_dipole_shift]))) - if self._nuclear_dipole_moment is not None: - if self._reverse_dipole_sign: - _elec_dipole = -_elec_dipole - dipole_moment = self._nuclear_dipole_moment + _elec_dipole - total_dipole_moment = np.sqrt(np.sum(np.power(dipole_moment, 2))) - lines.append('~ Nuclear dipole moment (a.u.): {}'.format( - FermionicTransformation._dipole_to_string(self._nuclear_dipole_moment))) - lines.append('> Dipole moment (a.u.): {} Total: {}'.format( - FermionicTransformation._dipole_to_string(dipole_moment), - FermionicTransformation._float_to_string(total_dipole_moment))) - lines.append(' (debye): {} Total: {}'.format( - FermionicTransformation._dipole_to_string(dipole_moment / QMolecule.DEBYE), - FermionicTransformation._float_to_string(total_dipole_moment / QMolecule.DEBYE))) - result['nuclear_dipole_moment'] = self._nuclear_dipole_moment - result['electronic_dipole_moment'] = _elec_dipole - result['dipole_moment'] = dipole_moment - result['total_dipole_moment'] = total_dipole_moment - - return lines, result + # Called by public superclass method process_algorithm_result to complete specific processing + def _process_algorithm_result(self, algo_result): + if isinstance(algo_result, MinimumEigensolverResult): + msgr = self.interpret(algo_result.eigenvalue, algo_result.eigenstate, + algo_result.aux_operator_eigenvalues) + msgr.algorithm_result = algo_result + return msgr + raise ValueError('_process_algorithm_result should be passed a MinimumEigensolverResult ' + 'all other types have been deprecated and removed.') @staticmethod def _try_reduce_fermionic_operator(fer_op, freeze_list, remove_list): diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index c97d379b4b..3df01ef0d7 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -16,7 +16,6 @@ from typing import Tuple, List from qiskit.aqua.operators.legacy import WeightedPauliOperator -from qiskit.aqua.algorithms import MinimumEigensolverResult from qiskit.chemistry.core import MolecularGroundStateResult from qiskit.chemistry.drivers import BaseDriver @@ -31,11 +30,7 @@ def transform(self, driver: BaseDriver raise NotImplementedError @abstractmethod - def interpret(self, value: float, state: List[float], aux_values: dict) -> MolecularGroundStateResult: + def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list + ) -> MolecularGroundStateResult: """TODO""" raise NotImplementedError - - # @abstractmethod - # def interpret(value, aux_values, circuit, params=None): - # -> GroundStateResult: # might be fermionic / bosonic - # raise NotImplementedError() From 46b4c5d9bf89c69c9616f725d14a4dcf28dbdb13 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 28 Sep 2020 19:04:42 +0200 Subject: [PATCH 033/197] documentation added --- .../ground_state_calculation.py | 8 +- .../mes_ground_state_calculation.py | 25 +++-- .../fermionic_transformation.py | 99 ++++++++++++++++++- .../qubit_operator_transformation.py | 8 +- 4 files changed, 117 insertions(+), 23 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index 4556ac46cc..e8b6a18fbf 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -20,7 +20,7 @@ class GroundStateCalculation(ABC): - """The ground state calculation interface.""" + """The ground state calculation interface""" def __init__(self, transformation: QubitOperatorTransformation) -> None: """ @@ -41,12 +41,12 @@ def transformation(self) -> QubitOperatorTransformation: @abstractmethod def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. - + Args: - driver: TODO + driver: BaseDriver Returns: - A molecular ground state result TODO + A molecular ground state result """ raise NotImplementedError diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 09e4d8bf5f..cb1e165e42 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -24,14 +24,15 @@ class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): - """TODO""" + """Ground state computation using a minimum eigensolver.""" def __init__(self, transformation: QubitOperatorTransformation, solver: Union[MinimumEigensolver, MESFactory]) -> None: """ + Args: - transformation: TODO - solver: TODO + transformation: Qubit Operator Transformation + solver: Minimum Eigensolver or MESFactory object """ super().__init__(transformation) self._solver = solver @@ -39,39 +40,37 @@ def __init__(self, transformation: QubitOperatorTransformation, @property def solver(self) -> Union[MinimumEigensolver, MESFactory]: """Get the minimum eigensolver or factory.""" + return self._solver def returns_groundstate(self) -> bool: - """TODO""" + """TODO + whether the eigensolver returns the ground state or only ground state energy.""" + return False - def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: - # TODO MolecularGroundStateResult should become generic for bosonic and fermionic - # Hamiltonains + def compute_ground_state(self, driver: BaseDriver) : """Compute Ground State properties. Args: driver: A chemistry driver. Returns: - A molecular ground state result + Ground state result TODO """ operator, aux_operators = self.transformation.transform(driver) if isinstance(self._solver, MESFactory): # this must be called after transformation.transform - solver = self._solver.get_solver(self.transformation) # TODO and driver? + solver = self._solver.get_solver(self.transformation) else: solver = self._solver - # TODO shouldn't this rather raise a warning? aux_operators = aux_operators if solver.supports_aux_operators() else None raw_gs_result = solver.compute_minimum_eigenvalue(operator, aux_operators) - # TODO WOR: where should this post processing be coming from? # The post processing is now in the tranformation so that it is fermionic or bosonic # gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], # groundstate) # gs = array/circuit+params - return self.transformation.interpret(raw_gs_result) - # (energy, aux_values, groundsntate) + return self.transformation.interpret(raw_gs_result) \ No newline at end of file diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 44d3e30bdc..b16d7f7fa9 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -118,11 +118,30 @@ def __init__(self, def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + """ + Transformation to qubit operator from the driver + + Args: + driver:BaseDriver + + Returns: + qubit operator, auxiliary operators + """ q_molecule = driver.run() ops, aux_ops = self._do_transform(q_molecule) # _do_transform(q_molecule) return ops, aux_ops - def _do_transform(self, qmolecule): + def _do_transform(self, qmolecule)-> Tuple[WeightedPauliOperator, + List[WeightedPauliOperator]]: + """ + + Args: + qmolecule (Qmolecule): qmolecule + + 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 @@ -211,6 +230,16 @@ def _do_transform(self, qmolecule): aux_ops = [] def _add_aux_op(aux_op, name): + """ + Add auxiliary operators + + Args: + aux_op: auxiliary operators + name: name + + Returns: + + """ aux_qop = FermionicTransformation._map_fermionic_operator_to_qubit( aux_op, self._qubit_mapping, new_nel, self._two_qubit_reduction ) @@ -227,6 +256,16 @@ def _add_aux_op(aux_op, name): if qmolecule.has_dipole_integrals(): def _dipole_op(dipole_integrals, axis): + """ + Dipole operators + + Args: + dipole_integrals: dipole intergrals + 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_, @@ -281,7 +320,16 @@ def _dipole_op(dipole_integrals, axis): return qubit_op, aux_ops def _process_z2symmetry_reduction(self, qubit_op, aux_ops): + """ + 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) + """ z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) if z2_symmetries.is_empty(): logger.debug('No Z2 symmetries found') @@ -337,6 +385,16 @@ def _process_z2symmetry_reduction(self, qubit_op, aux_ops): @staticmethod def _check_commutes(cliffords, operator): + """ + Check commutations + + Args: + cliffords : cliffords + operator (Qubit Operator): qubit operator + + Returns: + Boolean: does_commute + """ commutes = [] for clifford in cliffords: commutes.append(operator.commute_with(clifford)) @@ -375,7 +433,7 @@ def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list TODO Returns: - TODO + GroundState Result TODO """ mgsr = MolecularGroundStateResult() mgsr.hartree_fock_energy = self._hf_energy @@ -412,6 +470,14 @@ def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list # Called by public superclass method process_algorithm_result to complete specific processing def _process_algorithm_result(self, algo_result): + """ + + Args: + algo_resul (AlgorithmResult): AlgorithmResult + + Returns: + Ground state calculation result + """ if isinstance(algo_result, MinimumEigensolverResult): msgr = self.interpret(algo_result.eigenvalue, algo_result.eigenstate, algo_result.aux_operator_eigenvalues) @@ -422,6 +488,17 @@ def _process_algorithm_result(self, algo_result): @staticmethod def _try_reduce_fermionic_operator(fer_op, freeze_list, remove_list): + """ + Trying to reduce the fermionic operator w.r.t to freeze and remove list if provided + + Args: + fer_op (FermionicOperator) : fermionic operator + freeze_list (list): freeze list of orbitals + remove_list (list): remove list of orbitals + + Returns: + (fermionic_operator, energy_shift, did_shift) + """ # pylint: disable=len-as-condition did_shift = False energy_shift = 0.0 @@ -434,6 +511,18 @@ def _try_reduce_fermionic_operator(fer_op, freeze_list, remove_list): @staticmethod def _map_fermionic_operator_to_qubit(fer_op, qubit_mapping, num_particles, two_qubit_reduction): + """ + + Args: + fer_op (FermionicOperator): Fermionic Operator + qubit_mapping (QubitMappingType): fermion to qubit mapping + num_particles (integer): number of particles + two_qubit_reduction (boolean): 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) @@ -441,6 +530,9 @@ def _map_fermionic_operator_to_qubit(fer_op, qubit_mapping, num_particles, two_q @staticmethod def _dipole_to_string(_dipole): + """ + Dipole values to strings + """ dips = [round(x, 8) for x in _dipole] value = '[' for i, _ in enumerate(dips): @@ -450,4 +542,7 @@ def _dipole_to_string(_dipole): @staticmethod def _float_to_string(value, precision=8): + """ + Float to string for results + """ return '0.0' if value == 0 else ('{:.' + str(precision) + 'f}').format(value).rstrip('0') diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 3df01ef0d7..261352b189 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""TODO""" +"""Base class for transformation to qubit operators for chemistry problems""" from abc import ABC, abstractmethod from typing import Tuple, List @@ -21,16 +21,16 @@ class QubitOperatorTransformation(ABC): - """TODO""" + """Base class for transformation to qubit operators for chemistry problems""" @abstractmethod def transform(self, driver: BaseDriver ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: - """TODO""" + """transforms to qubit operators """ raise NotImplementedError @abstractmethod def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list ) -> MolecularGroundStateResult: - """TODO""" + """interprets the results of the ground state calculation""" raise NotImplementedError From ec1d697170cf59203fa0ee444b47ea8177b27cf9 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 28 Sep 2020 20:21:31 +0200 Subject: [PATCH 034/197] fixes in ground state --- .../mes_ground_state_calculation.py | 11 ++++++----- .../qubit_transformations/fermionic_transformation.py | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index cb1e165e42..cb61d9c4aa 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -68,9 +68,10 @@ def compute_ground_state(self, driver: BaseDriver) : aux_operators = aux_operators if solver.supports_aux_operators() else None - raw_gs_result = solver.compute_minimum_eigenvalue(operator, aux_operators) + self.raw_gs_result = solver.compute_minimum_eigenvalue(operator, aux_operators) - # The post processing is now in the tranformation so that it is fermionic or bosonic - # gsc_result = self._transformation.interpret(raw_gs_result['energy'], r['aux_values'], - # groundstate) # gs = array/circuit+params - return self.transformation.interpret(raw_gs_result) \ No newline at end of file + eigenvalue = raw_gs_result.eigenvalue + eigenstate = raw_gs_result.eigenstate + aux_values = raw_gs_result.aux_operator_eigenvalues + + return self.raw_gs_result, self.transformation.interpret(eigenvalue, eigenstate, aux_values) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index b16d7f7fa9..cc73fded76 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 @@ -425,8 +425,10 @@ def _pick_sector(z2_symmetries, hf_str): return z2_symmetries # pylint: disable=unused-argument - def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list + def interpret(self, + eigenvalue: float, eigenstate: List[float], aux_values: list ) -> MolecularGroundStateResult: + """Interpret eigenvalue and eigenstate of qubit Hamiltonian w.r.t. driver. Args: @@ -435,6 +437,7 @@ def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list Returns: GroundState Result TODO """ + mgsr = MolecularGroundStateResult() mgsr.hartree_fock_energy = self._hf_energy mgsr.nuclear_repulsion_energy = self._nuclear_repulsion_energy From 1133cb1665a31c8eb1abec909f71e1a90048415a Mon Sep 17 00:00:00 2001 From: Cryoris Date: Tue, 29 Sep 2020 11:26:32 +0200 Subject: [PATCH 035/197] patch MES result to GSC result instead of returning a tuple --- .../ground_state_calculation.py | 2 +- .../mes_ground_state_calculation.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index e8b6a18fbf..d32c533450 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -41,7 +41,7 @@ def transformation(self) -> QubitOperatorTransformation: @abstractmethod def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. - + Args: driver: BaseDriver diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index cb61d9c4aa..e1b4f912a0 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -49,7 +49,7 @@ def returns_groundstate(self) -> bool: return False - def compute_ground_state(self, driver: BaseDriver) : + def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: """Compute Ground State properties. Args: @@ -68,10 +68,12 @@ def compute_ground_state(self, driver: BaseDriver) : aux_operators = aux_operators if solver.supports_aux_operators() else None - self.raw_gs_result = solver.compute_minimum_eigenvalue(operator, aux_operators) + raw_gs_result = solver.compute_minimum_eigenvalue(operator, aux_operators) eigenvalue = raw_gs_result.eigenvalue eigenstate = raw_gs_result.eigenstate aux_values = raw_gs_result.aux_operator_eigenvalues - - return self.raw_gs_result, self.transformation.interpret(eigenvalue, eigenstate, aux_values) + + result = self.transformation.interpret(eigenvalue, eigenstate, aux_values) + result.raw_gs_result = raw_gs_result # add results from minimum eigensolver + return result From e3d9bf4e619821d8d44c57b7630e9267c793f351 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 29 Sep 2020 13:06:47 +0200 Subject: [PATCH 036/197] files fixes for GSC interface --- qiskit/chemistry/bopes_sampler.py | 368 ------------------ qiskit/chemistry/bopes_sampler.py~ | 355 ----------------- .../EEExcitedStatesCalculation.py | 0 .../VQEExcitedStatesCalculation.py | 80 ---- qiskit/chemistry/molecule.py | 310 --------------- 5 files changed, 1113 deletions(-) delete mode 100644 qiskit/chemistry/bopes_sampler.py delete mode 100644 qiskit/chemistry/bopes_sampler.py~ delete mode 100644 qiskit/chemistry/excited_state_calculation/EEExcitedStatesCalculation.py delete mode 100644 qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py delete mode 100644 qiskit/chemistry/molecule.py diff --git a/qiskit/chemistry/bopes_sampler.py b/qiskit/chemistry/bopes_sampler.py deleted file mode 100644 index 1b7e13df8f..0000000000 --- a/qiskit/chemistry/bopes_sampler.py +++ /dev/null @@ -1,368 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. -"""The calculation of points on the Born-Oppenheimer Potential Energy Surface (BOPES).""" - -import logging -from typing import Optional, Tuple, List, Dict - -import numpy as np -import pandas as pd -from qiskit.aqua import AquaError -from qiskit.aqua.algorithms import VQAlgorithm, VQE, MinimumEigensolver - -from .energy_surface_spline import EnergySurfaceBase -from .extrapolator import Extrapolator -from .molecule import Molecule - -logger = logging.getLogger(__name__) - -class BOPESSampler: - - def __init__(self, - driver, - GroundStateCalculation)->None: - - self._driver = driver - self._GroundStateCalculation = GroundStateCalculation - - #TODO link with the BOPES Sampler and the Extrapolators - - def run_points(self, points): - - #TODO driver needs to provide molecule and perturbed geometry - - for point in points: - - point_geometry = self.driver.molecule.get_perturbed_geometry() - driver = self.driver(point_geometry) - - - - - -class MGSE: - """Class to evaluate the Born-Oppenheimer Potential Energy Surface (BOPES). - - # TODO merge with existing Molecular GSE? - """ - - def __init__(self, - molecule: Molecule, - min_eigensolver: MinimumEigensolver, - tolerance: float = 1e-3, - resample: bool = True, - bootstrap: bool = True, - num_bootstrap: Optional[int] = None, - extrapolators: Optional[List[Extrapolator]] = None) -> None: - - """ - Args: - molecule: Molecule object of interest. - min_eigensolver: The specific eigensolver method to use to find minimum - eigenvalue/energy. - tolerance: Tolerance desired for minimum energy. - resample: Whether to resample final energy to reduce sampling error below - tolerance. - bootstrap: Whether to warm-start the solve of variational minimum eigensolvers. - num_bootstrap: Number of previous points for extrapolation - and bootstrapping. If None and a list of extrapolators is defined, - all prev points will be used except the first two points will be used for - bootstrapping. If no extrapolator is defined and bootstrap is True, - all previous points will be used for bootstrapping. - extrapolators: Extrapolator objects that define space/window and method to extrapolate - variational parameters. First and second elements refer to the wrapper and internal - extrapolators - - Raises: - AquaError: If ``num_boostrap`` is an integer smaller than 2. - """ - self._molecule = molecule - self._min_eigensolver = min_eigensolver - self._tolerance = tolerance - self._resample = resample - self._bootstrap = bootstrap - self._results = None # minimal DataFrame of [points, energies] - self._results_full = None # whole dict-of-dict-of-results - self._points_optparams = None - self._num_bootstrap = num_bootstrap - self._extrapolator_wrap = None - - # set wrapper and internal extrapolators - if extrapolators: - # todo: assumed len(extrapolators) == 2 - self._extrapolator_wrap = extrapolators[0] # wrapper - self._extrapolator_wrap.extrapolator = extrapolators[1] # internal extrapolator - # set default number of bootstrapping points to 2 - if num_bootstrap is None: - self._num_bootstrap = 2 - self._extrapolator_wrap.window = 0 - elif num_bootstrap >= 2: - self._num_bootstrap = num_bootstrap - self._extrapolator_wrap.window = num_bootstrap # window for extrapolator - else: - raise AquaError( - 'num_bootstrap must be None or an integer greater than or equal to 2') - - if isinstance(self._min_eigensolver, VQAlgorithm): - # Save initial point passed to min_eigensolver; - # this will be used when NOT bootstrapping - self._initial_point = self._min_eigensolver.initial_point - - if logger.isEnabledFor(logging.DEBUG): - mo_string = str(self._molecule) - me_string = str(self._min_eigensolver) - log_string = "\nConstructing BOPES Sampler with:" + \ - "\nMolecule: {}".format(mo_string) + \ - "\nMin Eigensolver: {}".format(me_string) - logger.info(log_string) - - def run(self, points: List[float], reps: int = 1) -> pd.DataFrame: - """Run the sampler at the given points, potentially with repetitions. - - Args: - points: The points along the degrees of freedom to evaluate. - reps: Number of independent repetitions of this overall calculation. - - Returns: - The results as pandas dataframe. - """ - self._results = pd.DataFrame() - self._results_full = dict() - for i in range(reps): - logger.info('Repetition %s of %s', i + 1, reps) - results, results_full = self.run_points(points) - self._results_full[i] = results_full - self._results = self._results.append(results) - return self._results - - def run_points(self, points: List[float]) -> Tuple[pd.DataFrame, Dict[float, dict]]: - """Run the sampler at the given points. - - Args: - points: the points along the single degree of freedom to evaluate - - Returns: - The results for all points. - """ - results = pd.DataFrame() - results_full = dict() - if isinstance(self._min_eigensolver, VQAlgorithm): - # Save optimal parameters if its a variational algorithm. - # We deliberately empty this out so that any repetitions of this - # run remain independent of each other. - # Set initial point to default - self._points_optparams = dict() - self._min_eigensolver.initial_point = self._initial_point - - # Iterate over the points - for i, point in enumerate(points): - logger.info('Point %s of %s', i + 1, len(points)) - - try: - result = self._run_single_point(point) # execute single point here - except (Exception) as e: - logger.warning("Point {} failed with exception {}".format(point, e)) - - results_full[point] = result - - dataframe = pd.DataFrame(result, columns=['point', 'energy'], index=[i]) - # todo: optimizer_evals is present only in some of the result classes - dataframe['optimizer_evals'] = result.get('optimizer_evals') - # for i, param in enumerate(result['optimal_point']): - # df['optimal_param_' + str(i) + ''] = param - results = results.append(dataframe) - # end loop - return results, results_full - - def _run_single_point(self, point: float) -> dict: - """Run the sampler at the given single point - - Args: - point: The value of the degree of freedom to evaluate. - - Returns: - Results for a single point. - """ - # get Hamiltonian - hamiltonian_op = self._molecule.get_qubitop_hamiltonian([point]) - - # Warm start the solver; - # find closest previously run point and take optimal parameters - if isinstance(self._min_eigensolver, VQAlgorithm) and self._bootstrap: - prev_points = list(self._points_optparams.keys()) - prev_params = list(self._points_optparams.values()) - n_pp = len(prev_points) - # set number of points to bootstrap - if self._extrapolator_wrap is None: - n_boot = len(prev_points) # bootstrap all points - else: - n_boot = self._num_bootstrap - - # Set initial params if prev_points not empty - if prev_points: - if n_pp <= n_boot: - distances = np.array(point) - \ - np.array(prev_points).reshape(n_pp, -1) - # find min 'distance' from point to previous points - min_index = np.argmin(np.linalg.norm(distances, axis=1)) - # update initial point - self._min_eigensolver.initial_point = prev_params[min_index] - else: # extrapolate using saved parameters - opt_params = self._points_optparams - param_sets = self._extrapolator_wrap.extrapolate(points=[point], - param_dict=opt_params) - # update initial point, note param_set is a list - self._min_eigensolver.initial_point = param_sets.get( - point) # param set is a dictionary - - logger.info("Degree of Freedom value: %s", point) - logger.info("Hamiltonian:\n %s", hamiltonian_op) - logger.info("Starting Minimum Eigenvalue solve...") - - # Find minimum eigenvalue - results = dict(self._min_eigensolver.compute_minimum_eigenvalue(hamiltonian_op)) - if self._resample: - final_energy, extra_evals = self._resampler() - results['eigenvalue'] = final_energy - results['cost_function_evals'] += extra_evals - else: - logger.info("Not resampling final energy") - - logger.info("Finished Minimum Eigenvalue solve") - logger.info("Minimum energy: %s", results['eigenvalue']) - - # Customize results dictionary - results['point'] = point - results['energy'] = np.real(results['eigenvalue']) - # Save optimal point to bootstrap - if isinstance(self._min_eigensolver, VQAlgorithm): - # at every point evaluation, the optimal params are updated - optimal_params = self._min_eigensolver.optimal_params - self._points_optparams[point] = optimal_params - return results - - def _resampler(self) -> Tuple[float, int]: - """Resample energy to mitigate sampling error/other noise. - - Will re-evaluate energy enough times to get standard deviation below ``self._tolerance``. - - Returns: - A tuple containing the resampled energy and the number of additional evaluations made. - - Raises: - TypeError: If the min_eigensolver is not the VQE. - AquaError: If there's a mismatch in the objective energy and the the mean of the - callback. - """ - # I only know how to make this work with VQE - if not isinstance(self._min_eigensolver, VQE): - raise TypeError('Currently only the VQE is handled as minimum eigensolver.') - # logger.info("NOT resampling (minimum eigensolver is not VQE)") - # return - - optimal_parameters = self._min_eigensolver.optimal_params - - # resampling is better if we can use a callback; - callback_preserver = { - 'eval_count': None, - 'params': None, - 'mean': None, - 'std': None} - - def callback(eval_count, params, mean, std): - callback_preserver['eval_count'] = eval_count - callback_preserver['params'] = params - callback_preserver['mean'] = mean - callback_preserver['std'] = std - - original_shots = self._min_eigensolver.quantum_instance.run_config.shots - original_callback = self._min_eigensolver._callback - self._min_eigensolver._callback = callback - - # Evaluate energy one more time, at optimal parameters - # and get back standard deviation estimate (from callback) - # Calculate how many times we need to re-sample the objective - # in order to get an objective estimate with std deviation below desired tolerance - # (Averaging objective n times has variance (objective variance)/n) - extra_evals = 1 - objective_val = self._min_eigensolver._energy_evaluation(optimal_parameters) - n_repeat = (callback_preserver['std'] / self._tolerance) ** 2 - if not np.isclose(objective_val, callback_preserver['mean'], 1e-7): - raise AquaError("Mismatch in objective/energy in callback") - - logger.info("Objective std dev: %s, repeats: %.2f", callback_preserver['std'], n_repeat) - - # oval = [] - # for i in range(40): - # oval.append(self._min_eigensolver._energy_evaluation(optimal_parameters)) - # print("Empirical std: {}".format(np.sqrt(np.var(oval, ddof=1)))) - # print("Calced std: {}".format(callback_preserver['std'])) - - if n_repeat > 1: - total_shots = int(n_repeat * original_shots) - # System limits; - # max_shots = 8192 is a hard shot limit for hardware - # max_reps controls total size of job/circuits sent - # (depending on circuit complexity may be larger/smaller) - max_shots = 8192 - max_reps = 128 - total_shots = min(total_shots, max_shots * max_reps) - rounded_evals = np.round(total_shots / original_shots, decimals=2) - extra_evals += rounded_evals - logger.info("Resampling objective %s times", rounded_evals) - # Shot limit per call is 8192 (max_shots), - # so break up total shots in some number of chunks. - # If total shots is exactly divisible by 8192, great! what luck. - # If not, take the ceiling of the quotient - - # thats the number of chunks we'd have to do with at most 8192 shots each. - # Then determine shots per chunk for that number of chunks we'd - # have to do anyway - n_repeat_chunk = np.ceil(total_shots / max_shots) - chunk_shots = int(total_shots / n_repeat_chunk) - rep_param = np.repeat(np.reshape( - optimal_parameters, (1, -1)), n_repeat_chunk, axis=0).reshape(-1) - # Update shot count for resampling - self._min_eigensolver.quantum_instance.set_config(shots=chunk_shots) - # Final return value is mean of all function evaluations - objective_val = np.mean(self._min_eigensolver._energy_evaluation(rep_param)) - # Note that callback_preserver['eval_count'] counts this last - # call as "one" evaluation - # else - # std deviation already below desired tolerance - # use the value already calculated - resampled_energy = objective_val - - # set things back to normal - self._min_eigensolver._callback = original_callback - self._min_eigensolver.quantum_instance.set_config(shots=original_shots) - return resampled_energy, extra_evals - - def fit_to_surface(self, energy_surface: EnergySurfaceBase, dofs: List[int], - **kwargs) -> None: - """Fit the sampled energy points to the energy surface. - - Args: - energy_surface: An energy surface object. - dofs: A list of the degree-of-freedom dimensions to use as the independent - variables in the potential function fit. - **kwargs: Arguments to pass through to the potential's ``fit_to_data`` function. - """ - points_all_dofs = self._results['point'].to_numpy() - if len(points_all_dofs.shape) == 1: - points = points_all_dofs.tolist() - else: - points = points_all_dofs[:, dofs].tolist() - - energies = self._results['energy'].to_list() - energy_surface.fit_to_data(xdata=points, ydata=energies, **kwargs) diff --git a/qiskit/chemistry/bopes_sampler.py~ b/qiskit/chemistry/bopes_sampler.py~ deleted file mode 100644 index e5b235744c..0000000000 --- a/qiskit/chemistry/bopes_sampler.py~ +++ /dev/null @@ -1,355 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. -"""The calculation of points on the Born-Oppenheimer Potential Energy Surface (BOPES).""" - -import logging -from typing import Optional, Tuple, List, Dict - -import numpy as np -import pandas as pd -from qiskit.aqua import AquaError -from qiskit.aqua.algorithms import VQAlgorithm, VQE, MinimumEigensolver - -from .energy_surface_spline import EnergySurfaceBase -from .extrapolator import Extrapolator -from .molecule import Molecule - -logger = logging.getLogger(__name__) - - -# suppress depreciation warnings -# TODO remove this!! -# warnings.filterwarnings("ignore", category=DeprecationWarning) - - -class MGSE: - """Class to evaluate the Born-Oppenheimer Potential Energy Surface (BOPES). - - # TODO merge with existing Molecular GSE? - """ - - def __init__(self, - molecule: Molecule, - min_eigensolver: MinimumEigensolver, - tolerance: float = 1e-3, - resample: bool = True, - bootstrap: bool = True, - num_bootstrap: Optional[int] = None, - # todo: replace with a single WindowExtrapolator? It is very likely. - extrapolators: Optional[List[Extrapolator]] = None) -> None: - """ - Args: - molecule: Molecule object of interest. - min_eigensolver: The specific eigensolver method to use to find minimum - eigenvalue/energy. - tolerance: Tolerance desired for minimum energy. - resample: Whether to resample final energy to reduce sampling error below - tolerance. - bootstrap: Whether to warm-start the solve of variational minimum eigensolvers. - num_bootstrap: Number of previous points for extrapolation - and bootstrapping. If None and a list of extrapolators is defined, - all prev points will be used except the first two points will be used for - bootstrapping. If no extrapolator is defined and bootstrap is True, - all previous points will be used for bootstrapping. - extrapolators: Extrapolator objects that define space/window and method to extrapolate - variational parameters. First and second elements refer to the wrapper and internal - extrapolators - - Raises: - AquaError: If ``num_boostrap`` is an integer smaller than 2. - """ - self._molecule = molecule - self._min_eigensolver = min_eigensolver - self._tolerance = tolerance - self._resample = resample - self._bootstrap = bootstrap - self._results = None # minimal DataFrame of [points, energies] - self._results_full = None # whole dict-of-dict-of-results - self._points_optparams = None - self._num_bootstrap = num_bootstrap - self._extrapolator_wrap = None - - # set wrapper and internal extrapolators - if extrapolators: - # todo: assumed len(extrapolators) == 2 - self._extrapolator_wrap = extrapolators[0] # wrapper - self._extrapolator_wrap.extrapolator = extrapolators[1] # internal extrapolator - # set default number of bootstrapping points to 2 - if num_bootstrap is None: - self._num_bootstrap = 2 - self._extrapolator_wrap.window = 0 - elif num_bootstrap >= 2: - self._num_bootstrap = num_bootstrap - self._extrapolator_wrap.window = num_bootstrap # window for extrapolator - else: - raise AquaError( - 'num_bootstrap must be None or an integer greater than or equal to 2') - - if isinstance(self._min_eigensolver, VQAlgorithm): - # Save initial point passed to min_eigensolver; - # this will be used when NOT bootstrapping - self._initial_point = self._min_eigensolver.initial_point - - if logger.isEnabledFor(logging.DEBUG): - mo_string = str(self._molecule) - me_string = str(self._min_eigensolver) - log_string = "\nConstructing BOPES Sampler with:" + \ - "\nMolecule: {}".format(mo_string) + \ - "\nMin Eigensolver: {}".format(me_string) - logger.info(log_string) - - def run(self, points: List[float], reps: int = 1) -> pd.DataFrame: - """Run the sampler at the given points, potentially with repetitions. - - Args: - points: The points along the degrees of freedom to evaluate. - reps: Number of independent repetitions of this overall calculation. - - Returns: - The results as pandas dataframe. - """ - self._results = pd.DataFrame() - self._results_full = dict() - for i in range(reps): - logger.info('Repetition %s of %s', i + 1, reps) - results, results_full = self.run_points(points) - self._results_full[i] = results_full - self._results = self._results.append(results) - return self._results - - def run_points(self, points: List[float]) -> Tuple[pd.DataFrame, Dict[float, dict]]: - """Run the sampler at the given points. - - Args: - points: the points along the single degree of freedom to evaluate - - Returns: - The results for all points. - """ - results = pd.DataFrame() - results_full = dict() - if isinstance(self._min_eigensolver, VQAlgorithm): - # Save optimal parameters if its a variational algorithm. - # We deliberately empty this out so that any repetitions of this - # run remain independent of each other. - # Set initial point to default - self._points_optparams = dict() - self._min_eigensolver.initial_point = self._initial_point - - # Iterate over the points - for i, point in enumerate(points): - logger.info('Point %s of %s', i + 1, len(points)) - - # Run the single point - # try: - result = self._run_single_point(point) # execute single point here - # except (Exception) as e: - # logger.warning("Point {} failed with exception {}".format(point, e)) - # # Any bootstrapping/warm start will be less effective, - # # but just keep on trucking - # continue - # Record result - results_full[point] = result - # results = results.append(pd.DataFrame(result, columns=['point', 'energy'], index=[i])) - # NOTE added other options for dataframe output - dataframe = pd.DataFrame(result, columns=['point', 'energy'], index=[i]) - # todo: optimizer_evals is present only in some of the result classes - dataframe['optimizer_evals'] = result.get('optimizer_evals') - # for i, param in enumerate(result['optimal_point']): - # df['optimal_param_' + str(i) + ''] = param - results = results.append(dataframe) - # end loop - return results, results_full - - def _run_single_point(self, point: float) -> dict: - """Run the sampler at the given single point - - Args: - point: The value of the degree of freedom to evaluate. - - Returns: - Results for a single point. - """ - # get Hamiltonian - hamiltonian_op = self._molecule.get_qubitop_hamiltonian([point]) - - # Warm start the solver; - # find closest previously run point and take optimal parameters - if isinstance(self._min_eigensolver, VQAlgorithm) and self._bootstrap: - prev_points = list(self._points_optparams.keys()) - prev_params = list(self._points_optparams.values()) - n_pp = len(prev_points) - # set number of points to bootstrap - if self._extrapolator_wrap is None: - n_boot = len(prev_points) # bootstrap all points - else: - n_boot = self._num_bootstrap - - # Set initial params if prev_points not empty - if prev_points: - if n_pp <= n_boot: - distances = np.array(point) - \ - np.array(prev_points).reshape(n_pp, -1) - # find min 'distance' from point to previous points - min_index = np.argmin(np.linalg.norm(distances, axis=1)) - # update initial point - self._min_eigensolver.initial_point = prev_params[min_index] - else: # extrapolate using saved parameters - opt_params = self._points_optparams - param_sets = self._extrapolator_wrap.extrapolate(points=[point], - param_dict=opt_params) - # update initial point, note param_set is a list - self._min_eigensolver.initial_point = param_sets.get( - point) # param set is a dictionary - - logger.info("Degree of Freedom value: %s", point) - logger.info("Hamiltonian:\n %s", hamiltonian_op) - logger.info("Starting Minimum Eigenvalue solve...") - - # Find minimum eigenvalue - results = dict(self._min_eigensolver.compute_minimum_eigenvalue(hamiltonian_op)) - if self._resample: - final_energy, extra_evals = self._resampler() - results['eigenvalue'] = final_energy - results['cost_function_evals'] += extra_evals - else: - logger.info("Not resampling final energy") - - logger.info("Finished Minimum Eigenvalue solve") - logger.info("Minimum energy: %s", results['eigenvalue']) - - # Customize results dictionary - results['point'] = point - results['energy'] = np.real(results['eigenvalue']) - # Save optimal point to bootstrap - if isinstance(self._min_eigensolver, VQAlgorithm): - # at every point evaluation, the optimal params are updated - optimal_params = self._min_eigensolver.optimal_params - self._points_optparams[point] = optimal_params - return results - - def _resampler(self) -> Tuple[float, int]: - """Resample energy to mitigate sampling error/other noise. - - Will re-evaluate energy enough times to get standard deviation below ``self._tolerance``. - - Returns: - A tuple containing the resampled energy and the number of additional evaluations made. - - Raises: - TypeError: If the min_eigensolver is not the VQE. - AquaError: If there's a mismatch in the objective energy and the the mean of the - callback. - """ - # I only know how to make this work with VQE - if not isinstance(self._min_eigensolver, VQE): - raise TypeError('Currently only the VQE is handled as minimum eigensolver.') - # logger.info("NOT resampling (minimum eigensolver is not VQE)") - # return - - optimal_parameters = self._min_eigensolver.optimal_params - - # resampling is better if we can use a callback; - callback_preserver = { - 'eval_count': None, - 'params': None, - 'mean': None, - 'std': None} - - def callback(eval_count, params, mean, std): - callback_preserver['eval_count'] = eval_count - callback_preserver['params'] = params - callback_preserver['mean'] = mean - callback_preserver['std'] = std - - original_shots = self._min_eigensolver.quantum_instance.run_config.shots - original_callback = self._min_eigensolver._callback - self._min_eigensolver._callback = callback - - # Evaluate energy one more time, at optimal parameters - # and get back standard deviation estimate (from callback) - # Calculate how many times we need to re-sample the objective - # in order to get an objective estimate with std deviation below desired tolerance - # (Averaging objective n times has variance (objective variance)/n) - extra_evals = 1 - objective_val = self._min_eigensolver._energy_evaluation(optimal_parameters) - n_repeat = (callback_preserver['std'] / self._tolerance) ** 2 - if not np.isclose(objective_val, callback_preserver['mean'], 1e-7): - raise AquaError("Mismatch in objective/energy in callback") - - logger.info("Objective std dev: %s, repeats: %.2f", callback_preserver['std'], n_repeat) - - # oval = [] - # for i in range(40): - # oval.append(self._min_eigensolver._energy_evaluation(optimal_parameters)) - # print("Empirical std: {}".format(np.sqrt(np.var(oval, ddof=1)))) - # print("Calced std: {}".format(callback_preserver['std'])) - - if n_repeat > 1: - total_shots = int(n_repeat * original_shots) - # System limits; - # max_shots = 8192 is a hard shot limit for hardware - # max_reps controls total size of job/circuits sent - # (depending on circuit complexity may be larger/smaller) - max_shots = 8192 - max_reps = 128 - total_shots = min(total_shots, max_shots * max_reps) - rounded_evals = np.round(total_shots / original_shots, decimals=2) - extra_evals += rounded_evals - logger.info("Resampling objective %s times", rounded_evals) - # Shot limit per call is 8192 (max_shots), - # so break up total shots in some number of chunks. - # If total shots is exactly divisible by 8192, great! what luck. - # If not, take the ceiling of the quotient - - # thats the number of chunks we'd have to do with at most 8192 shots each. - # Then determine shots per chunk for that number of chunks we'd - # have to do anyway - n_repeat_chunk = np.ceil(total_shots / max_shots) - chunk_shots = int(total_shots / n_repeat_chunk) - rep_param = np.repeat(np.reshape( - optimal_parameters, (1, -1)), n_repeat_chunk, axis=0).reshape(-1) - # Update shot count for resampling - self._min_eigensolver.quantum_instance.set_config(shots=chunk_shots) - # Final return value is mean of all function evaluations - objective_val = np.mean(self._min_eigensolver._energy_evaluation(rep_param)) - # Note that callback_preserver['eval_count'] counts this last - # call as "one" evaluation - # else - # std deviation already below desired tolerance - # use the value already calculated - resampled_energy = objective_val - - # set things back to normal - self._min_eigensolver._callback = original_callback - self._min_eigensolver.quantum_instance.set_config(shots=original_shots) - return resampled_energy, extra_evals - - def fit_to_surface(self, energy_surface: EnergySurfaceBase, dofs: List[int], - **kwargs) -> None: - """Fit the sampled energy points to the energy surface. - - Args: - energy_surface: An energy surface object. - dofs: A list of the degree-of-freedom dimensions to use as the independent - variables in the potential function fit. - **kwargs: Arguments to pass through to the potential's ``fit_to_data`` function. - """ - points_all_dofs = self._results['point'].to_numpy() - if len(points_all_dofs.shape) == 1: - points = points_all_dofs.tolist() - else: - points = points_all_dofs[:, dofs].tolist() - - energies = self._results['energy'].to_list() - energy_surface.fit_to_data(xdata=points, ydata=energies, **kwargs) diff --git a/qiskit/chemistry/excited_state_calculation/EEExcitedStatesCalculation.py b/qiskit/chemistry/excited_state_calculation/EEExcitedStatesCalculation.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py b/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py deleted file mode 100644 index 787086cede..0000000000 --- a/qiskit/chemistry/excited_state_calculation/VQEExcitedStatesCalculation.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -from qiskit.chemistry.ground_state_calculation import GroundStateCalculation -from qiskit.chemistry.core import (Hamiltonian, TransformationType, QubitMappingType, - ChemistryOperator) -from qiskit.chemistry.core import MolecularExcitedStatesResult -from qiskit.chemistry.excited_states_calculation import ExcitedStatesCalculation - - -class VQEQeomExcitedStatesCalculation(ExcitedStatesCalculation): - """ - VQEQeomExcitesStatesCalculation - """ - - def __init__(self, - variational_form: var_form, - optimizer: optimizer, - backend: backend = BasicAer.get_backend('statevector_simulator'), - intial_state: intitial_state = None, - 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: - solver: - transformation: - qubit_mapping: - two_qubit_reduction: - freeze_core: - orbital_reduction: - z2symmetry_reduction: - """ - - self._variational_form = variational_form - self._optimizer = optimizer - self._intial_state = intial_state - self._backend = backend - super().__init__(transformation, qubit_mapping, two_qubit_reduction, freeze_core, orbital_reduction, - z2symmetry_reduction) - - def compute_excited_states(self, driver) -> MolecularExcitedStatesCalculationResult: - """ - - Compute Excited States result - - Returns: - MolecularExcitedStatesCalculationResult - - """ - - operator, aux_operators = self._transform(driver) - - #TODO This should not be like this. We need to implement a ".compute_eigenstates()" function - #TODO Check: Make sure that the driver here exposes _num_orbitals and _num_particles - - eom_vqe = QEomVQE(operator, var_form, optimizer, num_orbitals=driver._num_orbitals, - num_particles=driver._num_particles, qubit_mapping=self._qubit_mapping, - two_qubit_reduction=self._two_qubit_reduction, - z2_symmetries=self._z2_symmetries, untapered_op=operator) - - quantum_instance = QuantumInstance(backend) - raw_es_result = eom_vqe.run(quantum_instance) - - return core.process_algorithm_result(raw_es_result) diff --git a/qiskit/chemistry/molecule.py b/qiskit/chemistry/molecule.py deleted file mode 100644 index 7cffa24203..0000000000 --- a/qiskit/chemistry/molecule.py +++ /dev/null @@ -1,310 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 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 aexxont 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 an interface for a generic molecule. -It defines the composing atoms (with properties like masses, and nuclear spin), -and allows for changing the molecular geometry through given degrees of freedom -(e.g. bond-stretching, angle-bending, etc.). -""" -import copy - -import numpy as np -import scipy.linalg -from qiskit.chemistry.drivers import PySCFDriver, UnitsType, HFMethodType - - -class Molecule: - """ - Molecule class - """ - - def __init__(self, - geometry, - degrees_of_freedom=None, - masses=None, - spins=None, - basis_set='sto3g', - hf_method=HFMethodType.RHF, - ): - """ - Constructor. - - Args: - geometry ([str, [float]]): 2d list containing atom string names - to generate PySCF molecule strings as keys and list of 3 - floats representing Cartesian coordinates as values, - in units of **Angstroms**. - - degrees_of_freedom ([callable]): List of functions taking a - perturbation value and geometry and returns a perturbed - geometry. Helper functions for typical perturbations are - provided and can be used by the form - itertools.partial(Molecule.stretching_potential, - {'atom_pair': (1, 2)) - to specify the desired degree of freedom. - - masses([float]): The list of masses of each atom. - If provided, must be the same length as number of atoms - in geometry. - """ - self._geometry = geometry - self._degrees_of_freedom = degrees_of_freedom - - if masses is not None and not len(masses) == len(self._geometry): - raise ValueError( - 'Length of masses must match length of geometries, ' - 'found {} and {} respectively'.format( - len(masses), - len(self._geometry) - ) - ) - - self._masses = masses - - if spins is not None and not len(spins) == len(self._geometry): - raise ValueError( - 'Length of spins must match length of geometries, ' - 'found {} and {} respectively'.format( - len(spins), - len(self._geometry) - ) - ) - - self._spins = spins - self._basis_set = basis_set - self._hf_method = hf_method - - @classmethod - def __distance_modifier(cls, function, parameter, geometry, atom_pair): - """ - Args: - atom_pair(tuple(int)): A tuple with two integers, indexing - which atoms from the starting geometry should be moved - apart. **Atom1 is moved away from Atom2, while Atom2 - remains stationary.** - function: a function of two parameters (current distance, - extra parameter) returning the new distance - parameter(float): The extra parameter of the function above. - geometry(([str, [float]])): The initial geometry to perturb. - """ - a1, a2 = atom_pair - startingCoord1 = np.array(geometry[a1][1]) - coord2 = np.array(geometry[a2][1]) - - startingDistanceVector = startingCoord1 - coord2 - startingL2distance = np.linalg.norm(startingDistanceVector) - newL2distance = function(startingL2distance, parameter) - newDistanceVector = startingDistanceVector * ( - newL2distance / startingL2distance - ) - newCoord1 = coord2 + newDistanceVector - - ending_geometry = copy.deepcopy(geometry) - ending_geometry[a1][1] = newCoord1.tolist() - return ending_geometry - - @classmethod - def absolute_distance(cls, distance, geometry, atom_pair): - """ - Args: - atom_pair(tuple(int)): A tuple with two integers, - indexing which atoms from the starting geometry should be - moved apart. **Atom1 is moved away (at the given distance) - from Atom2, while Atom2 remains stationary.** - distance(float): The (new) distance between the two atoms. - geometry(([str, [float]])): The initial geometry to perturb. - """ - - def func(x, d): return d - - return cls.__distance_modifier(func, distance, geometry, atom_pair) - - @classmethod - def absolute_stretching(cls, perturbation, geometry, atom_pair): - """ - Args: - atom_pair(tuple(int)): A tuple with two integers, - indexing which atoms from the starting geometry should be - stretched apart. **Atom1 is stretched away from Atom2, while - Atom2 remains stationary.** - perturbation(float): The magnitude of the stretch. - (New distance = stretch + old distance) - geometry(([str, [float]])): The initial geometry to perturb. - """ - - def func(x, d): return x + d - - return cls.__distance_modifier(func, perturbation, geometry, - atom_pair) - - @classmethod - def relative_stretching(cls, perturbation, geometry, atom_pair): - """ - Args: - atom_pair(tuple(int)): A tuple with two integers, indexing which - atoms from the starting geometry should be stretched apart. - **Atom1 is stretched away from Atom2, while Atom2 remains - stationary.** - perturbation(float): The magnitude of the stretch. - (New distance = stretch * old distance) - geometry(([str, [float]])): The initial geometry to perturb. - """ - - def func(x, d): return x * d - - return cls.__distance_modifier(func, perturbation, geometry, - atom_pair) - - @classmethod - def __bend_modifier(cls, function, parameter, geometry, atom_trio): - """ - Args: - atom_trio(tuple(int)): A tuple with three integers, indexing - which atoms from the starting geometry should be bent apart. - **Atom1 is bent *away* from Atom3 by an angle whose vertex - is Atom2, while Atom2 and Atom3 remain stationary.** - function: a function of two parameters (current angle, - extra parameter) returning the new angle - parameter(float): The extra parameter of the function above. - geometry(([str, [float]])): The initial geometry to perturb. - """ - a1, a2, a3 = atom_trio - startingCoord1 = np.array(geometry[a1][1]) - coord2 = np.array(geometry[a2][1]) - coord3 = np.array(geometry[a3][1]) - - distanceVec1to2 = startingCoord1 - coord2 - distanceVec3to2 = coord3 - coord2 - rot_axis = np.cross(distanceVec1to2, distanceVec3to2) - # If atoms are linear, choose the rotation direction randomly, - # but still along the correct plane - # Maybe this is a bad idea if we end up here on some - # existing bending path. - # It'd be good to fix this later to remember the axis in some way. - if np.linalg.norm(rot_axis) == 0: - nudged_vec = copy.deepcopy(distanceVec1to2) - nudged_vec[0] += .01 - rot_axis = np.cross(nudged_vec, distanceVec3to2) - rot_unit_axis = rot_axis / np.linalg.norm(rot_axis) - startingAngle = np.arcsin( - np.linalg.norm(rot_axis) / ( - np.linalg.norm(distanceVec1to2) - * np.linalg.norm(distanceVec3to2) - ) - ) - newAngle = function(startingAngle, parameter) - perturbation = newAngle - startingAngle - rot_matrix = scipy.linalg.expm( - np.cross( - np.eye(3), - rot_unit_axis * - perturbation)) - newCoord1 = rot_matrix @ startingCoord1 - - ending_geometry = copy.deepcopy(geometry) - ending_geometry[a1][1] = newCoord1.tolist() - return ending_geometry - - @classmethod - def absolute_angle(cls, angle, geometry, atom_trio): - """ - Args: - atom_trio(tuple(int)): A tuple with three integers, indexing - which atoms from the starting geometry should be bent apart. - **Atom1 is bent *away* from Atom3 by an angle whose vertex - is Atom2 and equal to **angle**, while Atom2 and Atom3 - remain stationary.** - angle(float): The magnitude of the perturbation in **radians**. - **Positive bend is always in the direction toward Atom3.** - the direction of increasing the starting angle.** - geometry(([str, [float]])): The initial geometry to perturb. - """ - - def func(x, d): return d - - return cls.__bend_modifier(func, angle, geometry, atom_trio) - - @classmethod - def absolute_bending(cls, bend, geometry, atom_trio): - """ - Args: - atom_trio(tuple(int)): A tuple with three integers, indexing - which atoms from the starting geometry should be bent apart. - **Atom1 is bent *away* from Atom3 by an angle whose vertex - is Atom2 and equal to the initial angle **plus** bend, - while Atom2 and Atom3 remain stationary.** - bend(float): The magnitude of the perturbation in **radians**. - **Positive bend is always in the direction toward Atom3.** - the direction of increasing the starting angle.** - geometry(([str, [float]])): The initial geometry to perturb. - """ - - def func(x, d): return x + d - - return cls.__bend_modifier(func, bend, geometry, atom_trio) - - @classmethod - def relative_bending(cls, bend, geometry, atom_trio): - """ - Args: - atom_trio(tuple(int)): A tuple with three integers, - indexing which atoms from the starting geometry - should be bent apart. **Atom1 is bent *away* from Atom3 - by an angle whose vertex is Atom2 and equal to the initial - angle **times** bend, while Atom2 and Atom3 - remain stationary.** - bend(float): The magnitude of the perturbation in **radians**. - **Positive bend is always in the direction toward Atom3.** - the direction of increasing the starting angle.** - geometry(([str, [float]])): The initial geometry to perturb. - """ - - def func(x, d): return x * d - - return cls.__bend_modifier(func, bend, geometry, atom_trio) - - def get_perturbed_geom(self, perturbations=None): - if not perturbations or not self._degrees_of_freedom: - return self._geometry - geometry = copy.deepcopy(self._geometry) - for per, dof in zip(perturbations, self._degrees_of_freedom): - geometry = dof(per, geometry) - return geometry - - @classmethod - def get_geometry_str(cls, geometry): - return '; '.join([name + ' ' + ', '.join(map(str, coord)) - for (name, coord) in geometry]) - - @property - def geometry_str(self): - return get_geometry_str(geometry) - - @property - def basis_set(self): - return self._basis_set - - @property - def hf_method(self): - return self._hf_method - - @property - def spins(self): - return self._spins - - @property - def masses(self): - return self._masses - From 03d6c30a8a32d725b426f93efc83a251d079ba9a Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Tue, 29 Sep 2020 13:15:15 +0200 Subject: [PATCH 037/197] rm utf8 headers --- qiskit/chemistry/applications/molecular_ground_state_energy.py | 2 -- qiskit/chemistry/core/chemistry_operator.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index 1bc044d2d5..b263eb3447 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # This code is part of Qiskit. # # (C) Copyright IBM 2020. diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index fb305af0e0..f10d08578f 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # This code is part of Qiskit. # # (C) Copyright IBM 2018, 2020 From 72b271609a4914369701a1463a6008cf9f60c123 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Tue, 29 Sep 2020 09:00:04 -0400 Subject: [PATCH 038/197] fix spell, lint, mypy --- .../molecular_ground_state_energy.py | 2 +- qiskit/chemistry/core/chemistry_operator.py | 5 +- qiskit/chemistry/core/hamiltonian.py | 6 +- .../ground_state_calculation.py | 4 +- .../bosonic_transformation.py | 4 +- .../fermionic_transformation.py | 77 +++++++++++-------- 6 files changed, 60 insertions(+), 38 deletions(-) diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index b263eb3447..d7aedd8128 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -29,7 +29,7 @@ class MolecularGroundStateEnergy: """ Molecular ground state energy chemistry application """ - #TODO this needs to be deprecated in view of the new Ground State interface + # TODO this needs to be deprecated in view of the new Ground State interface def __init__(self, driver: BaseDriver, diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index f10d08578f..b5f98f24db 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -18,7 +18,7 @@ from abc import ABC, abstractmethod import warnings import logging -from typing import Union, List, Tuple, Optional +from typing import Union, List, Tuple, Optional, cast import numpy as np from qiskit.aqua.algorithms import MinimumEigensolverResult, EigensolverResult, AlgorithmResult @@ -242,7 +242,8 @@ def dipole_moment(self) -> Optional[DipoleTuple]: """ Returns dipole moment """ edm = self.electronic_dipole_moment if self.reverse_dipole_sign: - edm = tuple(-1 * x if x is not None else None for x in edm) + edm = cast(Tuple[Optional[float], Optional[float], Optional[float]], + tuple(-1 * x if x is not None else None for x in edm)) return _dipole_tuple_add(edm, self.nuclear_dipole_moment) @property diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index c421dd6636..9d0450f1cc 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 @@ -117,6 +117,10 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 + def run(self, molecule: QMolecule) -> Tuple[WeightedPauliOperator, WeightedPauliOperator]: + """ run method""" + raise NotImplementedError() + def _do_transform(self, qmolecule): logger.debug('Processing started...') # Save these values for later combination with the quantum computation result diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index d32c533450..a7350fe0f8 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -31,7 +31,7 @@ def __init__(self, transformation: QubitOperatorTransformation) -> None: @property def transformation(self) -> QubitOperatorTransformation: - """Return the tranformation used obtain a qubit operator from the molecule. + """Return the transformation used obtain a qubit operator from the molecule. Returns: The transformation. @@ -52,7 +52,7 @@ def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult @abstractmethod def returns_groundstate(self) -> bool: - """Whether this class returns only the groundstate energy or also the groundstate itself. + """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. diff --git a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py index d0580e60e5..0ad01b20c1 100644 --- a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py @@ -16,6 +16,7 @@ from qiskit.chemistry.drivers import BaseDriver from qiskit.aqua.operators.legacy import WeightedPauliOperator +from qiskit.chemistry.core import MolecularGroundStateResult from .qubit_operator_transformation import QubitOperatorTransformation @@ -31,6 +32,7 @@ def transform(self, driver: BaseDriver raise NotImplementedError() # take code from bosonic operator - def interpret(self): + def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list + ) -> MolecularGroundStateResult: """TODO""" pass diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index cc73fded76..df75ed5e1c 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -20,7 +20,7 @@ import numpy as np from qiskit.aqua.algorithms import MinimumEigensolverResult from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator -from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry import QiskitChemistryError, QMolecule from qiskit.chemistry.fermionic_operator import FermionicOperator from qiskit.chemistry.core.chemistry_operator import (ChemistryOperator, MolecularGroundStateResult, @@ -122,7 +122,7 @@ def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, Transformation to qubit operator from the driver Args: - driver:BaseDriver + driver: Base Driver Returns: qubit operator, auxiliary operators @@ -131,12 +131,12 @@ def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, ops, aux_ops = self._do_transform(q_molecule) # _do_transform(q_molecule) return ops, aux_ops - def _do_transform(self, qmolecule)-> Tuple[WeightedPauliOperator, - List[WeightedPauliOperator]]: + def _do_transform(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, + List[WeightedPauliOperator]]: """ Args: - qmolecule (Qmolecule): qmolecule + qmolecule: qmolecule Returns: (qubit operator, auxiliary operators) @@ -182,7 +182,8 @@ def _do_transform(self, qmolecule)-> Tuple[WeightedPauliOperator, 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] @@ -229,7 +230,7 @@ def _do_transform(self, qmolecule)-> Tuple[WeightedPauliOperator, aux_ops = [] - def _add_aux_op(aux_op, name): + def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: """ Add auxiliary operators @@ -237,8 +238,6 @@ def _add_aux_op(aux_op, name): aux_op: auxiliary operators name: name - Returns: - """ aux_qop = FermionicTransformation._map_fermionic_operator_to_qubit( aux_op, self._qubit_mapping, new_nel, self._two_qubit_reduction @@ -255,12 +254,13 @@ def _add_aux_op(aux_op, name): _add_aux_op(fer_op.total_magnetization(), 'Magnetization') if qmolecule.has_dipole_integrals(): - def _dipole_op(dipole_integrals, axis): + def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ + -> Tuple[WeightedPauliOperator, float, float]: """ Dipole operators Args: - dipole_integrals: dipole intergrals + dipole_integrals: dipole integrals axis: axis for dipole moment calculation Returns: @@ -319,7 +319,9 @@ def _dipole_op(dipole_integrals, axis): logger.debug('Processing complete ready to run algorithm') return qubit_op, aux_ops - def _process_z2symmetry_reduction(self, qubit_op, aux_ops): + def _process_z2symmetry_reduction(self, + qubit_op: WeightedPauliOperator, + aux_ops: WeightedPauliOperator) -> Tuple: """ Implement z2 symmetries in the qubit operator @@ -329,6 +331,9 @@ def _process_z2symmetry_reduction(self, qubit_op, aux_ops): 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(): @@ -356,7 +361,7 @@ def _process_z2symmetry_reduction(self, qubit_op, aux_ops): if self._z2symmetry_reduction == 'auto': hf_state = HartreeFock(num_orbitals=self._molecule_info[self.INFO_NUM_ORBITALS], - qubit_mapping=self._qubit_mapping, + qubit_mapping=self._qubit_mapping.value, two_qubit_reduction=self._two_qubit_reduction, num_particles=self._molecule_info[self.INFO_NUM_PARTICLES]) z2_symmetries = FermionicTransformation._pick_sector(z2_symmetries, hf_state.bitstr) @@ -384,13 +389,14 @@ def _process_z2symmetry_reduction(self, qubit_op, aux_ops): return z2_qubit_op, z2_aux_ops, z2_symmetries @staticmethod - def _check_commutes(cliffords, operator): + def _check_commutes(cliffords: List[WeightedPauliOperator], + operator: WeightedPauliOperator) -> bool: """ Check commutations Args: cliffords : cliffords - operator (Qubit Operator): qubit operator + operator: qubit operator Returns: Boolean: does_commute @@ -403,17 +409,17 @@ def _check_commutes(cliffords, operator): return does_commute @staticmethod - def _pick_sector(z2_symmetries, hf_str): + 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 (Z2Symmetries): the z2 symmetries object. - hf_str (numpy.ndarray): Hartree-Fock bit string (the last index is for qubit 0). + z2_symmetries: the z2 symmetries object. + hf_str: Hartree-Fock bit string (the last index is for qubit 0). Returns: - Z2Symmetries: the original z2 symmetries filled with the correct tapering values. + the original z2 symmetries filled with the correct tapering values. """ # Finding all the symmetries using the find_Z2_symmetries: taper_coef = [] @@ -437,7 +443,7 @@ def interpret(self, Returns: GroundState Result TODO """ - + mgsr = MolecularGroundStateResult() mgsr.hartree_fock_energy = self._hf_energy mgsr.nuclear_repulsion_energy = self._nuclear_repulsion_energy @@ -472,14 +478,18 @@ def interpret(self, return mgsr # Called by public superclass method process_algorithm_result to complete specific processing - def _process_algorithm_result(self, algo_result): + def _process_algorithm_result(self, algo_result: MinimumEigensolverResult) \ + -> MolecularGroundStateResult: """ Args: - algo_resul (AlgorithmResult): AlgorithmResult + algo_result: Algorithm Result Returns: Ground state calculation result + + Raises: + ValueError: Invalid input """ if isinstance(algo_result, MinimumEigensolverResult): msgr = self.interpret(algo_result.eigenvalue, algo_result.eigenstate, @@ -490,14 +500,16 @@ def _process_algorithm_result(self, algo_result): 'all other types have been deprecated and removed.') @staticmethod - def _try_reduce_fermionic_operator(fer_op, freeze_list, remove_list): + 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 (FermionicOperator) : fermionic operator - freeze_list (list): freeze list of orbitals - remove_list (list): remove list of orbitals + fer_op: fermionic operator + freeze_list: freeze list of orbitals + remove_list: remove list of orbitals Returns: (fermionic_operator, energy_shift, did_shift) @@ -513,14 +525,17 @@ def _try_reduce_fermionic_operator(fer_op, freeze_list, remove_list): return fer_op, energy_shift, did_shift @staticmethod - def _map_fermionic_operator_to_qubit(fer_op, qubit_mapping, num_particles, two_qubit_reduction): + def _map_fermionic_operator_to_qubit(fer_op: FermionicOperator, + qubit_mapping: QubitMappingType, + num_particles: List[int], + two_qubit_reduction: bool) -> WeightedPauliOperator: """ Args: - fer_op (FermionicOperator): Fermionic Operator - qubit_mapping (QubitMappingType): fermion to qubit mapping - num_particles (integer): number of particles - two_qubit_reduction (boolean): two qubit reduction + fer_op: Fermionic Operator + qubit_mapping: fermionic to qubit mapping + num_particles: number of particles + two_qubit_reduction: two qubit reduction Returns: qubit operator From 0ee83faf72edae86c66894cd5f2fe9b21d6b4890 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 29 Sep 2020 18:13:50 +0200 Subject: [PATCH 039/197] Remove `custom_excitation_pool` from AdaptVQE `compute_ground_state` function Rather than ignoring the arguments given by the interface, we can remove this additional argument in favor of enabling the user to set this custom excitation pool through the `MESFactory`. An example on how to achieve such a thing can be seen in the corresponding unittest. --- .../components/variational_forms/uccsd.py | 22 ++++++++++--------- .../ground_state_calculation/adapt_vqe.py | 9 ++------ test/chemistry/test_adapt_vqe.py | 19 ++++++++++++++++ 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/qiskit/chemistry/components/variational_forms/uccsd.py b/qiskit/chemistry/components/variational_forms/uccsd.py index a2cc61d08b..914846bcb7 100644 --- a/qiskit/chemistry/components/variational_forms/uccsd.py +++ b/qiskit/chemistry/components/variational_forms/uccsd.py @@ -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/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 2eaa96f10d..b0a201f586 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -64,7 +64,6 @@ def __init__(self, self._max_iterations = max_iterations def returns_groundstate(self) -> bool: - """TODO""" return False def _compute_gradients(self, @@ -132,15 +131,11 @@ def _check_cyclicity(indices: List[int]) -> bool: # nature of the algorithm. return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) - # pylint: disable=arguments-differ - def compute_ground_state(self, driver: BaseDriver, - custom_excitation_pool: List[WeightedPauliOperator] = None - ) -> MolecularGroundStateResult: + def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: """Computes the ground state. Args: driver: a chemistry driver. - custom_excitation_pool: list of excitation operators to choose from. Raises: AquaError: if a variational form other than UCCSD is provided or if the algorithm finishes due to an unforeseen reason. @@ -156,7 +151,7 @@ def compute_ground_state(self, driver: BaseDriver, raise AquaError("The AdaptVQE algorithm requires the use of the UCCSD variational form") var_form.manage_hopping_operators() - excitation_pool = custom_excitation_pool or var_form.excitation_pool + excitation_pool = var_form.excitation_pool threshold_satisfied = False alternating_sequence = False diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index 3866169ff9..6b279ae13f 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -80,6 +80,25 @@ def get_custom_solver(self, transformation): res = calc.compute_ground_state(self.driver) self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) + def test_custom_excitation_pool(self): + """ Test custom excitation pool """ + class CustomMESFactory(MESFactory): + """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. + custom_excitation_pool = [solver.var_form._hopping_ops[2]] + solver.var_form.excitation_pool = custom_excitation_pool + return solver + + solver = CustomMESFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + calc = AdaptVQE(self.transformation, solver) + res = calc.compute_ground_state(self.driver) + self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) + def test_vqe_adapt_check_cyclicity(self): """ VQEAdapt index cycle detection """ param_list = [ From 66e74b882863a07e27e8bbd2cf66fe4764615a48 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Tue, 29 Sep 2020 12:52:05 -0400 Subject: [PATCH 040/197] fix mypy --- qiskit/chemistry/components/variational_forms/uccsd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/components/variational_forms/uccsd.py b/qiskit/chemistry/components/variational_forms/uccsd.py index 914846bcb7..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 From 1f42a6c35b7b7fbddc66b63f0884da95011306c1 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Tue, 29 Sep 2020 13:51:02 -0400 Subject: [PATCH 041/197] fix lint --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2ce1fde545..79ae5f2a53 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -91,7 +91,7 @@ jobs: if: ${{ !cancelled() }} shell: bash Lint: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: matrix: python-version: [3.8] From cde79fcdefb810431c09fa809e4ef051c7b708f0 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 08:51:19 +0200 Subject: [PATCH 042/197] Re-enable DeprecationWarnings after VQEAdapt has been initialized --- test/chemistry/test_vqe_uccsd_adapt.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/chemistry/test_vqe_uccsd_adapt.py b/test/chemistry/test_vqe_uccsd_adapt.py index 0316dc88cc..af2eee5387 100644 --- a/test/chemistry/test_vqe_uccsd_adapt.py +++ b/test/chemistry/test_vqe_uccsd_adapt.py @@ -83,12 +83,15 @@ def test_vqe_adapt(self): 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) From 74e26ea658123a5fbfdef242a997a4e5f040204d Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 09:00:46 +0200 Subject: [PATCH 043/197] Fixes several minor nit-picks - copyright years - faulty renames - faulty merge side-effects - missing setters --- qiskit/chemistry/core/chemistry_operator.py | 5 ++--- qiskit/chemistry/core/hamiltonian.py | 2 +- qiskit/chemistry/ground_state_calculation/__init__.py | 2 +- .../chemistry/ground_state_calculation/adapt_vqe.py | 2 +- .../ground_state_calculation.py | 11 ++++++----- .../mes_ground_state_calculation.py | 8 ++++++-- qiskit/chemistry/qubit_transformations/__init__.py | 2 +- .../qubit_transformations/bosonic_transformation.py | 2 +- .../qubit_transformations/fermionic_transformation.py | 5 ++--- .../qubit_operator_transformation.py | 2 +- test/chemistry/test_adapt_vqe.py | 2 +- 11 files changed, 23 insertions(+), 20 deletions(-) diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index b5f98f24db..b1036b89a2 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -52,7 +52,7 @@ def __init__(self): self._molecule_info = {} @abstractmethod - def _do_transform(self, qmolecule): + def run(self, qmolecule): """ Convert the qmolecule, according to the ChemistryOperator, into an Operator that can be given to a QuantumAlgorithm @@ -242,8 +242,7 @@ def dipole_moment(self) -> Optional[DipoleTuple]: """ Returns dipole moment """ edm = self.electronic_dipole_moment if self.reverse_dipole_sign: - edm = cast(Tuple[Optional[float], Optional[float], Optional[float]], - tuple(-1 * x if x is not None else None for x in edm)) + 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 diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index 9d0450f1cc..10655ec644 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -117,7 +117,7 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - def run(self, molecule: QMolecule) -> Tuple[WeightedPauliOperator, WeightedPauliOperator]: + def run(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, WeightedPauliOperator]: """ run method""" raise NotImplementedError() diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index 78e691f106..886d92c8de 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index b0a201f586..f3b0509b21 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index a7350fe0f8..eebe65ff48 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -31,13 +31,14 @@ def __init__(self, transformation: QubitOperatorTransformation) -> None: @property def transformation(self) -> QubitOperatorTransformation: - """Return the transformation used obtain a qubit operator from the molecule. - - Returns: - The transformation. - """ + """Returns the transformation used to obtain a qubit operator from the molecule.""" return self._transformation + @transformation.setter + def transformation(self, transformation: QubitOperatorTransformation) -> None: + """Sets the transformation used to obtain a qubit operator from the molecule.""" + self._transformation = transformation + @abstractmethod def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index e1b4f912a0..54bc7441f1 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -39,10 +39,14 @@ def __init__(self, transformation: QubitOperatorTransformation, @property def solver(self) -> Union[MinimumEigensolver, MESFactory]: - """Get the minimum eigensolver or factory.""" - + """Returns the minimum eigensolver or factory.""" return self._solver + @solver.setter + def solver(self, solver: Union[MinimumEigensolver, MESFactory]) -> None: + """Sets the minimum eigensolver or factory.""" + self._solver = solver + def returns_groundstate(self) -> bool: """TODO whether the eigensolver returns the ground state or only ground state energy.""" diff --git a/qiskit/chemistry/qubit_transformations/__init__.py b/qiskit/chemistry/qubit_transformations/__init__.py index 393f47a714..79778b7b71 100644 --- a/qiskit/chemistry/qubit_transformations/__init__.py +++ b/qiskit/chemistry/qubit_transformations/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 diff --git a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py index 0ad01b20c1..25bc8adbfa 100644 --- a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index df75ed5e1c..4c71b1c3f8 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -22,8 +22,7 @@ from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator from qiskit.chemistry import QiskitChemistryError, QMolecule from qiskit.chemistry.fermionic_operator import FermionicOperator -from qiskit.chemistry.core.chemistry_operator import (ChemistryOperator, - MolecularGroundStateResult, +from qiskit.chemistry.core.chemistry_operator import (MolecularGroundStateResult, DipoleTuple) from qiskit.chemistry.drivers import BaseDriver @@ -46,7 +45,7 @@ class QubitMappingType(Enum): BRAVYI_KITAEV = 'bravyi_kitaev' -class FermionicTransformation(QubitOperatorTransformation, ChemistryOperator): +class FermionicTransformation(QubitOperatorTransformation): """ A molecular Hamiltonian operator, representing the energy of the electrons and nuclei in a molecule. diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 261352b189..6276549d5c 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index 6b279ae13f..5060c08d59 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 From eb97fb76d1df53cf56a89a4ebbcae35a8b49ce7f Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 30 Sep 2020 09:20:12 +0200 Subject: [PATCH 044/197] make MESFactory an interface only --- .../ground_state_calculation/__init__.py | 8 +++-- .../ground_state_calculation/adapt_vqe.py | 2 +- .../mes_factories/__init__.py | 20 +++++++++++ .../mes_factories/mes_factory.py | 35 +++++++++++++++++++ .../vqe_uccsd_factory.py} | 2 +- .../mes_ground_state_calculation.py | 4 +-- 6 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py create mode 100644 qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py rename qiskit/chemistry/ground_state_calculation/{mes_factory.py => mes_factories/vqe_uccsd_factory.py} (99%) diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index 886d92c8de..ce8da55d84 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -10,14 +10,16 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""TODO""" +"""Ground state calculation algorithms.""" from .ground_state_calculation import GroundStateCalculation from .adapt_vqe import AdaptVQE from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation -from .mes_factory import MESFactory +from .mes_factories import MESFactory, VQEUCCSDFactory __all__ = ['GroundStateCalculation', 'AdaptVQE', 'MinimumEigensolverGroundStateCalculation', - 'MESFactory'] + 'MESFactory', + 'VQEUCCSDFactory' + ] diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index f3b0509b21..f8925f62f4 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -29,7 +29,7 @@ from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.qubit_transformations import FermionicTransformation from .ground_state_calculation import GroundStateCalculation -from .mes_factory import MESFactory +from .mes_factories import MESFactory logger = logging.getLogger(__name__) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py b/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py new file mode 100644 index 0000000000..a4ace8313b --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py @@ -0,0 +1,20 @@ +# 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. + +"""Factories that create a minimum eigensolver based on a qubit transformation.""" + +from .mes_factory import MESFactory +from .vqe_uccsd_factory import VQEUCCSDFactory + +__all__ = ['MESFactory', + 'VQEUCCSDFactory' + ] diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py new file mode 100644 index 0000000000..5ecc8da841 --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py @@ -0,0 +1,35 @@ +# 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 qiskit.chemistry.qubit_transformations import QubitOperatorTransformation + + +class MESFactory(ABC): + """A factory to construct a minimum eigensolver suitable for a qubit operator transformation. + """ + + @abstractmethod + def get_solver(self, transformation: QubitOperatorTransformation) -> 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 diff --git a/qiskit/chemistry/ground_state_calculation/mes_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py similarity index 99% rename from qiskit/chemistry/ground_state_calculation/mes_factory.py rename to qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py index 26b763db90..d7d31e1a2f 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py @@ -19,7 +19,7 @@ from qiskit.chemistry.components.initial_states import HartreeFock -class MESFactory: +class VQEUCCSDFactory: """A factory to construct a minimum eigensolver suitable for a qubit operator transformation. """ diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 54bc7441f1..cca8985a76 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -20,7 +20,7 @@ from qiskit.chemistry.ground_state_calculation import GroundStateCalculation from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation -from .mes_factory import MESFactory +from .mes_factories import MESFactory class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): @@ -32,7 +32,7 @@ def __init__(self, transformation: QubitOperatorTransformation, Args: transformation: Qubit Operator Transformation - solver: Minimum Eigensolver or MESFactory object + solver: Minimum Eigensolver or MESFactory object, e.g. the VQEUCCSDFactory. """ super().__init__(transformation) self._solver = solver From 346f28d652f9bc5e62e441a2a5879cffe7ae0d7d Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 30 Sep 2020 09:29:52 +0200 Subject: [PATCH 045/197] fix copyright years --- .../ground_state_calculation/mes_factories/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py b/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py index a4ace8313b..9e85c0cf18 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 From 0e5abdd28f669ae9e907e77cff4649a40f11ab5e Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 30 Sep 2020 09:37:02 +0200 Subject: [PATCH 046/197] derive from MESFactory and update docs --- .../mes_factories/vqe_uccsd_factory.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py index d7d31e1a2f..102f50a79e 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py @@ -18,10 +18,11 @@ from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation from qiskit.chemistry.components.initial_states import HartreeFock +from .mes_factory import MESFactory -class VQEUCCSDFactory: - """A factory to construct a minimum eigensolver suitable for a qubit operator transformation. - """ + +class VQEUCCSDFactory(MESFactory): + """A factory to construct a VQE minimum eigensolver with UCCSD ansatz wavefunction.""" def __init__(self, quantum_instance: QuantumInstance) -> None: """ @@ -31,15 +32,13 @@ def __init__(self, quantum_instance: QuantumInstance) -> None: self._quantum_instance = quantum_instance def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEigensolver: - """Returns a minimum eigensolver, based on the qubit operator transformation. - - By default the VQE with a UCCSD wavefunction ansatz is returned. + """Returns a VQE with a UCCSD wavefunction ansatz, based on ``transformation``. Args: transformation: The qubit operator transformation. Returns: - A minimum eigensolver suitable to compute the ground state of the molecule transformed + A VQE suitable to compute the ground state of the molecule transformed by ``transformation``. """ From c93c4faacc2041b1adae56231ee4030dc610eddf Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 30 Sep 2020 10:19:42 +0200 Subject: [PATCH 047/197] update docstrings and add molecule_info --- .../fermionic_transformation.py | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 4c71b1c3f8..b8b6a7d40a 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -9,10 +9,12 @@ # 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 tranformation from a fermionic problem to a qubit operator. + +The problem is described in a driver. """ -This module implements a molecular Hamiltonian operator, representing the -energy of the electrons and nuclei in a molecule. -""" + from typing import Optional, List, Union, cast, Tuple import logging from enum import Enum @@ -46,10 +48,7 @@ class QubitMappingType(Enum): class FermionicTransformation(QubitOperatorTransformation): - """ - A molecular Hamiltonian operator, representing the - energy of the electrons and nuclei in a molecule. - """ + """A tranformation from a fermionic problem, represented by a driver, to a qubit operator.""" def __init__(self, transformation: TransformationType = TransformationType.FULL, @@ -115,6 +114,8 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 + self._molecule_info = {} + def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: """ @@ -303,17 +304,16 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ logger.info('Molecule num spin orbitals: %s, remaining for processing: %s', nspinorbs, new_nspinorbs) - self._add_molecule_info(self.INFO_NUM_PARTICLES, [new_num_alpha, new_num_beta]) - self._add_molecule_info(self.INFO_NUM_ORBITALS, new_nspinorbs) - self._add_molecule_info(self.INFO_TWO_QUBIT_REDUCTION, - self._two_qubit_reduction - if self._qubit_mapping == 'parity' else False) + 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._add_molecule_info(self.INFO_Z2SYMMETRIES, z2symmetries) + self._molecule_info['z2_symmetries'] = z2symmetries logger.debug('Processing complete ready to run algorithm') return qubit_op, aux_ops @@ -359,10 +359,10 @@ def _process_z2symmetry_reduction(self, 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[self.INFO_NUM_ORBITALS], + hf_state = HartreeFock(num_orbitals=self._molecule_info['num_orbitals'], qubit_mapping=self._qubit_mapping.value, two_qubit_reduction=self._two_qubit_reduction, - num_particles=self._molecule_info[self.INFO_NUM_PARTICLES]) + 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): @@ -433,7 +433,6 @@ def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: np.ndarray) -> Z2Symmetrie def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list ) -> MolecularGroundStateResult: - """Interpret eigenvalue and eigenstate of qubit Hamiltonian w.r.t. driver. Args: From d97c224e7773b3ba05344389d023a60049c4ee78 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 30 Sep 2020 10:49:10 +0200 Subject: [PATCH 048/197] consistent naming in ``ground(_)state`` --- .../chemistry/ground_state_calculation/adapt_vqe.py | 2 +- .../ground_state_calculation.py | 2 +- .../mes_ground_state_calculation.py | 2 +- test/chemistry/test_adapt_vqe.py | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index f8925f62f4..67510f3642 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -131,7 +131,7 @@ def _check_cyclicity(indices: List[int]) -> bool: # nature of the algorithm. return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) - def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> MolecularGroundStateResult: """Computes the ground state. Args: diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index eebe65ff48..a746cb12bd 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -40,7 +40,7 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: self._transformation = transformation @abstractmethod - def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> MolecularGroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. Args: diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index cca8985a76..953d380eae 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -53,7 +53,7 @@ def returns_groundstate(self) -> bool: return False - def compute_ground_state(self, driver: BaseDriver) -> MolecularGroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> MolecularGroundStateResult: """Compute Ground State properties. Args: diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index 5060c08d59..771936b122 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -22,7 +22,7 @@ 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.ground_state_calculation import AdaptVQE, MESFactory +from qiskit.chemistry.ground_state_calculation import AdaptVQE, VQEUCCSDFactory, MESFactory from qiskit.chemistry.qubit_transformations import FermionicTransformation @@ -46,14 +46,14 @@ def setUp(self): def test_default(self): """ Default execution """ - solver = MESFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) calc = AdaptVQE(self.transformation, solver) - res = calc.compute_ground_state(self.driver) + res = calc.compute_groundstate(self.driver) self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) def test_custom_minimum_eigensolver(self): """ Test custom MES """ - solver = MESFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) def get_custom_solver(self, transformation): num_orbitals = transformation._molecule_info['num_orbitals'] @@ -77,7 +77,7 @@ def get_custom_solver(self, transformation): solver.get_solver = get_custom_solver.__get__(solver, MESFactory) calc = AdaptVQE(self.transformation, solver) - res = calc.compute_ground_state(self.driver) + res = calc.compute_groundstate(self.driver) self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) def test_custom_excitation_pool(self): @@ -96,7 +96,7 @@ def get_solver(self, transformation): solver = CustomMESFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) calc = AdaptVQE(self.transformation, solver) - res = calc.compute_ground_state(self.driver) + res = calc.compute_groundstate(self.driver) self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) def test_vqe_adapt_check_cyclicity(self): From 1534184e1ba962d3c4931e2df6ee03cb58b8a0ab Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 30 Sep 2020 12:41:06 +0200 Subject: [PATCH 049/197] fix spell --- .../chemistry/qubit_transformations/fermionic_transformation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index b8b6a7d40a..5243892fa2 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -48,7 +48,7 @@ class QubitMappingType(Enum): class FermionicTransformation(QubitOperatorTransformation): - """A tranformation from a fermionic problem, represented by a driver, to a qubit operator.""" + """A transformation from a fermionic problem, represented by a driver, to a qubit operator.""" def __init__(self, transformation: TransformationType = TransformationType.FULL, From 0d1d523612f85e8785789c3ee1a5eb7f369f1b11 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 30 Sep 2020 12:56:16 +0200 Subject: [PATCH 050/197] more spell, deprecate MGSE --- .../chemistry/applications/molecular_ground_state_energy.py | 4 +++- .../qubit_transformations/fermionic_transformation.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index d7aedd8128..09443ebe00 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -29,7 +29,9 @@ class MolecularGroundStateEnergy: """ Molecular ground state energy chemistry application """ - # TODO this needs to be deprecated in view of the new Ground State interface + 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) def __init__(self, driver: BaseDriver, diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 5243892fa2..2711eb8bf1 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""This module implements a tranformation from a fermionic problem to a qubit operator. +"""This module implements a transformation from a fermionic problem to a qubit operator. The problem is described in a driver. """ From 229af18f766d0999a524b2c6b80e0ea56e12162f Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 30 Sep 2020 12:58:13 +0200 Subject: [PATCH 051/197] fix deprecation --- .../applications/molecular_ground_state_energy.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index 09443ebe00..d83bb498c8 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -29,10 +29,6 @@ class MolecularGroundStateEnergy: """ Molecular ground state energy chemistry application """ - 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) - def __init__(self, driver: BaseDriver, solver: Optional[MinimumEigensolver] = None, @@ -62,6 +58,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 From 3d0a72854acd9ffd860d9332f95d115e1d321052 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 30 Sep 2020 13:32:52 +0200 Subject: [PATCH 052/197] more style --- .../applications/molecular_ground_state_energy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index d83bb498c8..1def8993af 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -59,10 +59,10 @@ def __init__(self, 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) - + 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 From c67567ab7ac8a7de9b99ea6725e0a07af345c2c4 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 30 Sep 2020 13:47:21 +0200 Subject: [PATCH 053/197] test fixes --- qiskit/chemistry/applications/molecular_ground_state_energy.py | 2 ++ qiskit/chemistry/core/hamiltonian.py | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index 1def8993af..ff029544cc 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -12,6 +12,8 @@ """ Molecular ground state energy chemistry application """ +import warnings +import logging from typing import List, Optional, Callable, Union from qiskit.providers import BaseBackend diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index 10655ec644..966d11c4fb 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -119,9 +119,6 @@ def __init__(self, def run(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, WeightedPauliOperator]: """ run method""" - raise NotImplementedError() - - def _do_transform(self, qmolecule): logger.debug('Processing started...') # Save these values for later combination with the quantum computation result self._hf_energy = qmolecule.hf_energy From 6d3d254f75c1e90021f126d9a4f7aa6114177f4a Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 30 Sep 2020 15:11:31 +0200 Subject: [PATCH 054/197] fixes --- qiskit/chemistry/applications/molecular_ground_state_energy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index ff029544cc..6af912eb9e 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -13,7 +13,6 @@ """ Molecular ground state energy chemistry application """ import warnings -import logging from typing import List, Optional, Callable, Union from qiskit.providers import BaseBackend From 36d0045eac789ed3adf9f84689b464b0c87fc9a6 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 14:48:49 +0200 Subject: [PATCH 055/197] [WIP] basic Result-interfaces This adds the following interfaces: - qiskit.chemistry.ChemistryResult(AlgorithmResult) - qiskit.chemistry.ground_state_calculation.GroundStateResult(ChemistryResult) - qiskit.chemistry.ground_state_calculation.FermionicGroundStateResult(GroundStateResult) The logic is as follows: ChemistryResult and GroundStateResult are empty interfaces which define a hierarchy for the result classes. They provide a useful granularity for type checking. Once more subclasses are added we can extract common functionality up into the stack as needed. The FermionicGroundStateResult currently is just a port of the old MolecularChemistryResult and MolecularGroundStateResult. Both of these classes are deprecated in this commit. Since this class is currently specific to the GroundStateCalculation it is part of that module. This is, however, subject to change. --- qiskit/chemistry/__init__.py | 2 + qiskit/chemistry/chemistry_result.py | 19 + qiskit/chemistry/core/chemistry_operator.py | 14 + .../ground_state_calculation/__init__.py | 6 +- .../fermionic_ground_state_result.py | 340 ++++++++++++++++++ .../ground_state_calculation.py | 8 +- .../fermionic_transformation.py | 39 +- .../qubit_operator_transformation.py | 4 +- 8 files changed, 407 insertions(+), 25 deletions(-) create mode 100644 qiskit/chemistry/chemistry_result.py create mode 100644 qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py diff --git a/qiskit/chemistry/__init__.py b/qiskit/chemistry/__init__.py index 9b62687e0f..269d205386 100644 --- a/qiskit/chemistry/__init__.py +++ b/qiskit/chemistry/__init__.py @@ -155,6 +155,7 @@ """ from .qiskit_chemistry_error import QiskitChemistryError +from .chemistry_result import ChemistryResult from .qmolecule import QMolecule from .bosonic_operator import BosonicOperator from .fermionic_operator import FermionicOperator @@ -163,6 +164,7 @@ set_qiskit_chemistry_logging) __all__ = ['QiskitChemistryError', + 'ChemistryResult', 'QMolecule', 'BosonicOperator', 'FermionicOperator', diff --git a/qiskit/chemistry/chemistry_result.py b/qiskit/chemistry/chemistry_result.py new file mode 100644 index 0000000000..aa8e2f40e1 --- /dev/null +++ b/qiskit/chemistry/chemistry_result.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. + +"""The chemistry result interface.""" + +from qiskit.aqua.algorithms.algorithm_result import AlgorithmResult + + +class ChemistryResult(AlgorithmResult): + """The chemistry result interface.""" diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index b1036b89a2..111ec41fa4 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -111,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.ground_state_calculation.' + 'FermionicGroundStateResult instead.', DeprecationWarning, stacklevel=2) + @property def algorithm_result(self) -> AlgorithmResult: """ Returns raw algorithm result """ @@ -162,6 +169,13 @@ class MolecularGroundStateResult(MolecularChemistryResult): # 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) + 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.ground_state_calculation.' + '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/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index ce8da55d84..5087394f51 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -12,12 +12,16 @@ """Ground state calculation algorithms.""" -from .ground_state_calculation import GroundStateCalculation +from .ground_state_calculation import GroundStateCalculation, GroundStateResult +from .fermionic_ground_state_result import DipoleTuple, FermionicGroundStateResult from .adapt_vqe import AdaptVQE from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation from .mes_factories import MESFactory, VQEUCCSDFactory __all__ = ['GroundStateCalculation', + 'GroundStateResult', + 'DipoleTuple', + 'FermionicGroundStateResult', 'AdaptVQE', 'MinimumEigensolverGroundStateCalculation', 'MESFactory', diff --git a/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py b/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py new file mode 100644 index 0000000000..c5eefd4ce3 --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py @@ -0,0 +1,340 @@ +# 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 fermionic ground state result.""" + +from typing import List, Tuple, Optional, cast +import logging +import numpy as np + +from qiskit.aqua.algorithms import AlgorithmResult +from qiskit.chemistry import QMolecule +from .ground_state_calculation import GroundStateResult + +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 FermionicGroundStateResult(GroundStateResult): + """The fermionic ground state result.""" + + @property + def algorithm_result(self) -> AlgorithmResult: + """ Returns raw algorithm result """ + return self.get('algorithm_result') + + @algorithm_result.setter + def algorithm_result(self, value: AlgorithmResult) -> None: + """ Sets raw algorithm result """ + self.data['algorithm_result'] = value + + @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 """ + 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 """ + 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/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index a746cb12bd..c35430110b 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -14,11 +14,15 @@ from abc import ABC, abstractmethod +from qiskit.chemistry import ChemistryResult from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.core import MolecularGroundStateResult from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation +class GroundStateResult(ChemistryResult): + """The ground state result interface.""" + + class GroundStateCalculation(ABC): """The ground state calculation interface""" @@ -40,7 +44,7 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: self._transformation = transformation @abstractmethod - def compute_groundstate(self, driver: BaseDriver) -> MolecularGroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> GroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. Args: diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 2711eb8bf1..3524082784 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -24,9 +24,8 @@ from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator from qiskit.chemistry import QiskitChemistryError, QMolecule from qiskit.chemistry.fermionic_operator import FermionicOperator -from qiskit.chemistry.core.chemistry_operator import (MolecularGroundStateResult, - DipoleTuple) from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.ground_state_calculation import DipoleTuple, FermionicGroundStateResult from .qubit_operator_transformation import QubitOperatorTransformation from ..components.initial_states import HartreeFock @@ -432,7 +431,7 @@ def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: np.ndarray) -> Z2Symmetrie # pylint: disable=unused-argument def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list - ) -> MolecularGroundStateResult: + ) -> FermionicGroundStateResult: """Interpret eigenvalue and eigenstate of qubit Hamiltonian w.r.t. driver. Args: @@ -442,49 +441,49 @@ def interpret(self, GroundState Result TODO """ - mgsr = MolecularGroundStateResult() - mgsr.hartree_fock_energy = self._hf_energy - mgsr.nuclear_repulsion_energy = self._nuclear_repulsion_energy + fgsr = FermionicGroundStateResult() + fgsr.hartree_fock_energy = self._hf_energy + fgsr.nuclear_repulsion_energy = self._nuclear_repulsion_energy if self._nuclear_dipole_moment is not None: - mgsr.nuclear_dipole_moment = tuple(x for x in self._nuclear_dipole_moment) - mgsr.computed_electronic_energy = eigenvalue.real - mgsr.ph_extracted_energy = self._ph_energy_shift - mgsr.frozen_extracted_energy = self._energy_shift + fgsr.nuclear_dipole_moment = tuple(x for x in self._nuclear_dipole_moment) + fgsr.computed_electronic_energy = eigenvalue.real + fgsr.ph_extracted_energy = self._ph_energy_shift + fgsr.frozen_extracted_energy = self._energy_shift aux_ops_vals = aux_values if aux_ops_vals is not None: # Dipole results if dipole aux ops were present dipole_idx = 3 if len(aux_ops_vals) > dipole_idx: - mgsr.reverse_dipole_sign = self._reverse_dipole_sign + fgsr.reverse_dipole_sign = self._reverse_dipole_sign dipm = [] for i in range(dipole_idx, dipole_idx + 3): # Gets X, Y and Z components dipm.append(aux_ops_vals[i][0].real if aux_ops_vals[i] is not None else None) - mgsr.computed_dipole_moment = cast(DipoleTuple, tuple(dipm)) - mgsr.ph_extracted_dipole_moment = (self._ph_x_dipole_shift, + fgsr.computed_dipole_moment = cast(DipoleTuple, tuple(dipm)) + fgsr.ph_extracted_dipole_moment = (self._ph_x_dipole_shift, self._ph_y_dipole_shift, self._ph_z_dipole_shift) - mgsr.frozen_extracted_dipole_moment = (self._x_dipole_shift, + fgsr.frozen_extracted_dipole_moment = (self._x_dipole_shift, self._y_dipole_shift, self._z_dipole_shift) # The first 3 entries are num particles, total angular momentum and magnetization - mgsr.num_particles = aux_ops_vals[0][0].real \ + fgsr.num_particles = aux_ops_vals[0][0].real \ if aux_ops_vals[0] is not None else None - mgsr.total_angular_momentum = aux_ops_vals[1][0].real \ + fgsr.total_angular_momentum = aux_ops_vals[1][0].real \ if aux_ops_vals[1] is not None else None - mgsr.magnetization = aux_ops_vals[2][0].real \ + fgsr.magnetization = aux_ops_vals[2][0].real \ if aux_ops_vals[2] is not None else None - return mgsr + return fgsr # Called by public superclass method process_algorithm_result to complete specific processing def _process_algorithm_result(self, algo_result: MinimumEigensolverResult) \ - -> MolecularGroundStateResult: + -> FermionicGroundStateResult: """ Args: algo_result: Algorithm Result Returns: - Ground state calculation result + Fermionic ground state calculation result Raises: ValueError: Invalid input diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 6276549d5c..3e7d81dcce 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -16,7 +16,7 @@ from typing import Tuple, List from qiskit.aqua.operators.legacy import WeightedPauliOperator -from qiskit.chemistry.core import MolecularGroundStateResult +from qiskit.chemistry import ChemistryResult from qiskit.chemistry.drivers import BaseDriver @@ -31,6 +31,6 @@ def transform(self, driver: BaseDriver @abstractmethod def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list - ) -> MolecularGroundStateResult: + ) -> ChemistryResult: """interprets the results of the ground state calculation""" raise NotImplementedError From f67079ae6ccca5316e96587dafec940be707c34e Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 14:55:12 +0200 Subject: [PATCH 056/197] Remove BosonicTransformation from this PR The BosonicTransformation will be added in a separate PR aiming to refactor the respective modules. --- .../qubit_transformations/__init__.py | 4 +- .../bosonic_transformation.py | 38 ------------------- 2 files changed, 1 insertion(+), 41 deletions(-) delete mode 100644 qiskit/chemistry/qubit_transformations/bosonic_transformation.py diff --git a/qiskit/chemistry/qubit_transformations/__init__.py b/qiskit/chemistry/qubit_transformations/__init__.py index 79778b7b71..e733291d25 100644 --- a/qiskit/chemistry/qubit_transformations/__init__.py +++ b/qiskit/chemistry/qubit_transformations/__init__.py @@ -12,10 +12,8 @@ """Qubit operator transformation module.""" -from .bosonic_transformation import BosonicTransformation from .fermionic_transformation import FermionicTransformation from .qubit_operator_transformation import QubitOperatorTransformation -__all__ = ['BosonicTransformation', - 'FermionicTransformation', +__all__ = ['FermionicTransformation', 'QubitOperatorTransformation'] diff --git a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py b/qiskit/chemistry/qubit_transformations/bosonic_transformation.py deleted file mode 100644 index 25bc8adbfa..0000000000 --- a/qiskit/chemistry/qubit_transformations/bosonic_transformation.py +++ /dev/null @@ -1,38 +0,0 @@ -# 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. - -"""TODO""" - -from typing import Tuple, List - -from qiskit.chemistry.drivers import BaseDriver -from qiskit.aqua.operators.legacy import WeightedPauliOperator -from qiskit.chemistry.core import MolecularGroundStateResult -from .qubit_operator_transformation import QubitOperatorTransformation - - -class BosonicTransformation(QubitOperatorTransformation): - """TODO""" - - def __init__(self, h, basis): - pass - - def transform(self, driver: BaseDriver - ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: - """TODO""" - raise NotImplementedError() - # take code from bosonic operator - - def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list - ) -> MolecularGroundStateResult: - """TODO""" - pass From f5b177d2bd61923c45abfa1777d0e8153c5804fa Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 15:00:17 +0200 Subject: [PATCH 057/197] AdaptVQE actually supports aux_operators --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 67510f3642..07b576c977 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -64,7 +64,7 @@ def __init__(self, self._max_iterations = max_iterations def returns_groundstate(self) -> bool: - return False + return True def _compute_gradients(self, excitation_pool: List[WeightedPauliOperator], From 71bdbd6ce8ea3b2f478a30864146857870e2de76 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 16:23:59 +0200 Subject: [PATCH 058/197] [wip] resolve cyclic import problems Having a `Result` class in either the `ground_state_calculations` or `qubit_operator_transformations` modules leads to a coupling of the two modules that is too tight. I.e. we run into cyclic imports whenever we try to run any code because the `QubitOpTransformation` class needs to load the `Result` class while at the same time it needs to be available in the `GroundStateCalculation` class. Thus, when an additional coupling in between these two modules is created, we have cyclic import. Thus, we drop one level of granularity and only provide a single result-interface on the `qiskit.chemistry` level, here, called `ChemistryResult` (open for discussion). I also moved the `DipoleTuple` to this level to simplify its usage in the other classes. --- qiskit/chemistry/__init__.py | 3 ++- qiskit/chemistry/chemistry_result.py | 10 +++++++++- qiskit/chemistry/core/chemistry_operator.py | 2 +- .../chemistry/ground_state_calculation/__init__.py | 6 ++---- .../fermionic_ground_state_result.py | 13 +++---------- .../ground_state_calculation.py | 7 ++----- .../fermionic_transformation.py | 4 ++-- 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/qiskit/chemistry/__init__.py b/qiskit/chemistry/__init__.py index 269d205386..075c42c6ac 100644 --- a/qiskit/chemistry/__init__.py +++ b/qiskit/chemistry/__init__.py @@ -155,7 +155,7 @@ """ from .qiskit_chemistry_error import QiskitChemistryError -from .chemistry_result import ChemistryResult +from .chemistry_result import ChemistryResult, DipoleTuple from .qmolecule import QMolecule from .bosonic_operator import BosonicOperator from .fermionic_operator import FermionicOperator @@ -165,6 +165,7 @@ __all__ = ['QiskitChemistryError', 'ChemistryResult', + 'DipoleTuple', 'QMolecule', 'BosonicOperator', 'FermionicOperator', diff --git a/qiskit/chemistry/chemistry_result.py b/qiskit/chemistry/chemistry_result.py index aa8e2f40e1..9ecb89eda5 100644 --- a/qiskit/chemistry/chemistry_result.py +++ b/qiskit/chemistry/chemistry_result.py @@ -12,7 +12,15 @@ """The chemistry result interface.""" -from qiskit.aqua.algorithms.algorithm_result import AlgorithmResult +from typing import Tuple, Optional + +from qiskit.aqua.algorithms import AlgorithmResult + +# 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 ChemistryResult(AlgorithmResult): diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index 111ec41fa4..989e1213b9 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -18,7 +18,7 @@ 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 diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index 5087394f51..ca13f8c3e8 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -12,15 +12,13 @@ """Ground state calculation algorithms.""" -from .ground_state_calculation import GroundStateCalculation, GroundStateResult -from .fermionic_ground_state_result import DipoleTuple, FermionicGroundStateResult +from .ground_state_calculation import GroundStateCalculation +from .fermionic_ground_state_result import FermionicGroundStateResult from .adapt_vqe import AdaptVQE from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation from .mes_factories import MESFactory, VQEUCCSDFactory __all__ = ['GroundStateCalculation', - 'GroundStateResult', - 'DipoleTuple', 'FermionicGroundStateResult', 'AdaptVQE', 'MinimumEigensolverGroundStateCalculation', diff --git a/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py b/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py index c5eefd4ce3..270b379b39 100644 --- a/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py +++ b/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py @@ -12,24 +12,17 @@ """The fermionic ground state result.""" -from typing import List, Tuple, Optional, cast +from typing import List, Optional, cast import logging import numpy as np from qiskit.aqua.algorithms import AlgorithmResult -from qiskit.chemistry import QMolecule -from .ground_state_calculation import GroundStateResult +from qiskit.chemistry import QMolecule, ChemistryResult, DipoleTuple 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 FermionicGroundStateResult(GroundStateResult): +class FermionicGroundStateResult(ChemistryResult): """The fermionic ground state result.""" @property diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index c35430110b..e073231c42 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -16,11 +16,8 @@ from qiskit.chemistry import ChemistryResult from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation - -class GroundStateResult(ChemistryResult): - """The ground state result interface.""" +from ..qubit_transformations.qubit_operator_transformation import QubitOperatorTransformation class GroundStateCalculation(ABC): @@ -44,7 +41,7 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: self._transformation = transformation @abstractmethod - def compute_groundstate(self, driver: BaseDriver) -> GroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> ChemistryResult: """Compute the ground state energy of the molecule that was supplied via the driver. Args: diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 3524082784..ac6a0c22a6 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -22,13 +22,13 @@ import numpy as np from qiskit.aqua.algorithms import MinimumEigensolverResult from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator -from qiskit.chemistry import QiskitChemistryError, QMolecule +from qiskit.chemistry import QiskitChemistryError, QMolecule, DipoleTuple from qiskit.chemistry.fermionic_operator import FermionicOperator from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.ground_state_calculation import DipoleTuple, FermionicGroundStateResult from .qubit_operator_transformation import QubitOperatorTransformation from ..components.initial_states import HartreeFock +from ..ground_state_calculation.fermionic_ground_state_result import FermionicGroundStateResult logger = logging.getLogger(__name__) From c1c9dfa374e245a853a1c0aa8d2e5ce204c31d66 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 16:29:16 +0200 Subject: [PATCH 059/197] Make AdaptVQE use the FermionicGSResult --- .../ground_state_calculation/adapt_vqe.py | 25 ++++++++++--------- .../mes_factories/vqe_uccsd_factory.py | 2 +- test/chemistry/test_adapt_vqe.py | 16 ++++++------ 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 07b576c977..ae968d7277 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -21,15 +21,15 @@ import numpy as np from qiskit.aqua import AquaError -from qiskit.aqua.algorithms import VQEResult +from qiskit.aqua.algorithms import VQEResult, VQE from qiskit.aqua.operators import LegacyBaseOperator, WeightedPauliOperator from qiskit.aqua.utils.validation import validate_min from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry.core import MolecularGroundStateResult from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.qubit_transformations import FermionicTransformation from .ground_state_calculation import GroundStateCalculation -from .mes_factories import MESFactory +from .fermionic_ground_state_result import FermionicGroundStateResult +from .mes_factories import VQEUCCSDFactory logger = logging.getLogger(__name__) @@ -39,7 +39,7 @@ class AdaptVQE(GroundStateCalculation): def __init__(self, transformation: FermionicTransformation, - solver: MESFactory, + solver: VQEUCCSDFactory, threshold: float = 1e-5, delta: float = 1, max_iterations: Optional[int] = None, @@ -47,7 +47,7 @@ def __init__(self, """ Args: transformation: a fermionic driver to operator transformation strategy. - solver: a minimum eigensolver factory which uses the UCCSD variational form. + 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. @@ -131,21 +131,22 @@ def _check_cyclicity(indices: List[int]) -> bool: # nature of the algorithm. return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) - def compute_groundstate(self, driver: BaseDriver) -> MolecularGroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: """Computes the ground state. Args: driver: a chemistry driver. Raises: - AquaError: if a variational form other than UCCSD is provided or if the algorithm - finishes due to an unforeseen reason. + 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: - A ground state result. - TODO replace with FermionicGroundStateResult + A fermionic ground state result. """ operator, aux_operators = self._transformation.transform(driver) vqe = self._solver.get_solver(self._transformation) + if not isinstance(vqe, VQE): + raise AquaError("The AdaptVQE algorithm requires the use of the VQE solver") var_form = vqe.var_form if not isinstance(var_form, UCCSD): raise AquaError("The AdaptVQE algorithm requires the use of the UCCSD variational form") @@ -224,8 +225,8 @@ def compute_groundstate(self, driver: BaseDriver) -> MolecularGroundStateResult: raw_result.finishing_criterion = finishing_criterion logger.info('The final energy is: %s', str(raw_result.optimal_value.real)) - return raw_result - # TODO: return self.transformation.interpret(raw_result) + return self.transformation.interpret(raw_result.eigenvalue, raw_result.eigenstate, + raw_result.aux_operator_eigenvalues) class AdaptVQEResult(VQEResult): diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py index 102f50a79e..85afa90d4f 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py @@ -46,7 +46,7 @@ def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEige 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['z2symmetries'] + 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, diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index 771936b122..45646b7146 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -22,7 +22,7 @@ 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.ground_state_calculation import AdaptVQE, VQEUCCSDFactory, MESFactory +from qiskit.chemistry.ground_state_calculation import AdaptVQE, VQEUCCSDFactory from qiskit.chemistry.qubit_transformations import FermionicTransformation @@ -49,7 +49,7 @@ def test_default(self): solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) calc = AdaptVQE(self.transformation, solver) res = calc.compute_groundstate(self.driver) - self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) + self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) def test_custom_minimum_eigensolver(self): """ Test custom MES """ @@ -60,7 +60,7 @@ def get_custom_solver(self, transformation): 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['z2symmetries'] + 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, @@ -74,15 +74,15 @@ def get_custom_solver(self, transformation): return vqe # pylint: disable=no-value-for-parameter - solver.get_solver = get_custom_solver.__get__(solver, MESFactory) + solver.get_solver = get_custom_solver.__get__(solver, VQEUCCSDFactory) calc = AdaptVQE(self.transformation, solver) res = calc.compute_groundstate(self.driver) - self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) + self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) def test_custom_excitation_pool(self): """ Test custom excitation pool """ - class CustomMESFactory(MESFactory): + class CustomFactory(VQEUCCSDFactory): """A custom MES factory.""" def get_solver(self, transformation): @@ -94,10 +94,10 @@ def get_solver(self, transformation): solver.var_form.excitation_pool = custom_excitation_pool return solver - solver = CustomMESFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + solver = CustomFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) calc = AdaptVQE(self.transformation, solver) res = calc.compute_groundstate(self.driver) - self.assertAlmostEqual(res.eigenvalue.real, self.expected, places=6) + self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) def test_vqe_adapt_check_cyclicity(self): """ VQEAdapt index cycle detection """ From 1d9413a9904412082d9d827db08eabae8ba3c7cd Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 16:42:47 +0200 Subject: [PATCH 060/197] Use FermionicGroundStateResult in MinimumEigensolverGroundStateCalculation --- .../mes_ground_state_calculation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 953d380eae..df6fb97f69 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -15,9 +15,9 @@ from typing import Union from qiskit.aqua.algorithms import MinimumEigensolver -from qiskit.chemistry.core import MolecularGroundStateResult from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from qiskit.chemistry.ground_state_calculation import (GroundStateCalculation, + FermionicGroundStateResult) from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation from .mes_factories import MESFactory @@ -53,7 +53,7 @@ def returns_groundstate(self) -> bool: return False - def compute_groundstate(self, driver: BaseDriver) -> MolecularGroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: """Compute Ground State properties. Args: From 1f0da501b7056fef81385d2036509697a9f6be93 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 18:10:07 +0200 Subject: [PATCH 061/197] Fix mypy --- qiskit/chemistry/core/hamiltonian.py | 3 ++- .../qubit_transformations/fermionic_transformation.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index 966d11c4fb..e6a1c91af6 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -159,7 +159,8 @@ def run(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, WeightedPaul 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/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index ac6a0c22a6..cfe76b29e3 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -15,7 +15,7 @@ The problem is described in a driver. """ -from typing import Optional, List, Union, cast, Tuple +from typing import Optional, List, Union, cast, Tuple, Dict import logging from enum import Enum @@ -113,7 +113,7 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - self._molecule_info = {} + self._molecule_info = {} # type: Dict def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: From e751b7967bd3b97ce30beb63d3d7ffd8d67ea75d Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 18:14:05 +0200 Subject: [PATCH 062/197] Filter DeprecationWarnings in unittests This filters all of the DeprecationWarnigns introduced in this PR. In 3 months from now we can remove these tests once the corresponding code is also removed. This commit also adds TODO labels to all of these unittest files which will have to be migrated to work within the new framework. --- test/chemistry/test_app_mgse.py | 21 ++++++++++ test/chemistry/test_core_hamiltonian.py | 19 +++++++++ .../test_core_hamiltonian_orb_reduce.py | 15 +++++++ .../test_core_hamiltonian_symmetries.py | 39 +++++++++++++++++++ test/chemistry/test_driver_methods.py | 4 ++ test/chemistry/test_end2end_with_vqe.py | 4 ++ .../test_initial_state_hartree_fock.py | 5 +++ test/chemistry/test_qeom_ee.py | 11 ++++++ test/chemistry/test_qeom_vqe.py | 11 ++++++ test/chemistry/test_swaprz.py | 7 ++++ test/chemistry/test_symmetries.py | 7 ++++ test/chemistry/test_uccsd_advanced.py | 13 +++++++ test/chemistry/test_uccsd_hartree_fock.py | 16 ++++++++ 13 files changed, 172 insertions(+) diff --git a/test/chemistry/test_app_mgse.py b/test/chemistry/test_app_mgse.py index 40ad190274..ab28643745 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 @@ -29,6 +30,8 @@ from qiskit.chemistry.core import QubitMappingType from qiskit.chemistry.drivers import PySCFDriver, UnitsType +# TODO Ground state interface PR + class TestAppMGSE(QiskitChemistryTestCase): """Test molecular ground state energy application """ @@ -53,6 +56,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 +75,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 +99,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 +115,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 +138,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 +154,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 +168,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 +188,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 +212,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 +235,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_core_hamiltonian.py b/test/chemistry/test_core_hamiltonian.py index 73498c7987..7972b051d5 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 @@ -20,6 +21,8 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType +# TODO Ground state interface PR + class TestCoreHamiltonian(QiskitChemistryTestCase): """core/hamiltonian Driver tests.""" @@ -58,11 +61,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 +75,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 +89,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 +104,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 +118,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 +132,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 +146,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 +162,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..896612e93f 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 @@ -20,6 +21,8 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry import QiskitChemistryError +# TODO Ground state interface PR + class TestCoreHamiltonianOrbReduce(QiskitChemistryTestCase): """core/hamiltonian Driver tests.""" @@ -58,11 +61,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 +75,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 +89,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 +103,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 +117,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 +132,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..a043dda340 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 @@ -25,6 +26,8 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry import QiskitChemistryError +# TODO Ground state interface PR + class TestCoreHamiltonianSymmetries(QiskitChemistryTestCase): """ Core hamiltonian Driver symmetry tests. """ @@ -53,138 +56,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 +239,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.py b/test/chemistry/test_driver_methods.py index 907736babd..eac4268f3b 100644 --- a/test/chemistry/test_driver_methods.py +++ b/test/chemistry/test_driver_methods.py @@ -12,10 +12,12 @@ """ Test Driver Methods """ +import warnings from test.chemistry import QiskitChemistryTestCase from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.aqua.algorithms import NumPyMinimumEigensolver +# TODO Ground state interface PR class TestDriverMethods(QiskitChemistryTestCase): """Common driver tests. For H2 @ 0.735, sto3g""" @@ -39,6 +41,7 @@ def _run_driver(driver, transformation=TransformationType.FULL, freeze_core=True): qmolecule = driver.run() + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=transformation, qubit_mapping=qubit_mapping, two_qubit_reduction=two_qubit_reduction, @@ -49,6 +52,7 @@ def _run_driver(driver, transformation=TransformationType.FULL, npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) return result def _assert_energy(self, result, mol): diff --git a/test/chemistry/test_end2end_with_vqe.py b/test/chemistry/test_end2end_with_vqe.py index 81dd39f20e..e3b916e88d 100644 --- a/test/chemistry/test_end2end_with_vqe.py +++ b/test/chemistry/test_end2end_with_vqe.py @@ -25,6 +25,8 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType +# TODO Ground state interface PR + @ddt class TestEnd2End(QiskitChemistryTestCase): @@ -35,11 +37,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 diff --git a/test/chemistry/test_initial_state_hartree_fock.py b/test/chemistry/test_initial_state_hartree_fock.py index 700e860114..95d776dd94 100644 --- a/test/chemistry/test_initial_state_hartree_fock.py +++ b/test/chemistry/test_initial_state_hartree_fock.py @@ -12,6 +12,7 @@ """ Test Initial State HartreeFock """ +import warnings import unittest from test.chemistry import QiskitChemistryTestCase import numpy as np @@ -22,6 +23,8 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType +# TODO Ground state interface PR + @ddt class TestInitialStateHartreeFock(QiskitChemistryTestCase): @@ -94,11 +97,13 @@ def test_hf_value(self, mapping): except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') qmolecule = driver.run() + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=mapping, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(qmolecule) qubit_op = op_converter.to_matrix_operator(qubit_op) diff --git a/test/chemistry/test_qeom_ee.py b/test/chemistry/test_qeom_ee.py index 3d7a1de41d..a6a8b13d4b 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 @@ -24,6 +25,8 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry.algorithms import QEomEE +# TODO Ground state interface PR + class TestEomEE(QiskitAquaTestCase): """Test case for Eom EE.""" @@ -35,11 +38,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 +56,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 +78,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 +100,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..ecfc124ac0 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 @@ -30,6 +31,8 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock +# TODO Ground state interface PR + class TestEomVQE(QiskitAquaTestCase): """Test Eom VQE.""" @@ -43,11 +46,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 +64,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 +98,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 +139,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..0a62315fbe 100644 --- a/test/chemistry/test_swaprz.py +++ b/test/chemistry/test_swaprz.py @@ -12,6 +12,7 @@ """Test of ExcitationPreserving from the circuit library.""" +import warnings from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.circuit.library import ExcitationPreserving @@ -22,6 +23,8 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.core import Hamiltonian, QubitMappingType +# TODO Ground state interface PR + class TestExcitationPreserving(QiskitChemistryTestCase): """The ExcitationPresering wavefunction was design to preserve the excitation of the system. @@ -42,8 +45,10 @@ def test_excitation_preserving(self): driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) qmolecule = driver.run() + warnings.filterwarnings('ignore', category=DeprecationWarning) operator = Hamiltonian(qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = operator.run(qmolecule) optimizer = SLSQP(maxiter=100) @@ -58,5 +63,7 @@ def test_excitation_preserving(self): result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'), seed_simulator=aqua_globals.random_seed, seed_transpiler=aqua_globals.random_seed)) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = operator.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) diff --git a/test/chemistry/test_symmetries.py b/test/chemistry/test_symmetries.py index 6fa6eb6c80..37e28bb5f6 100644 --- a/test/chemistry/test_symmetries.py +++ b/test/chemistry/test_symmetries.py @@ -12,6 +12,7 @@ """ Test of Symmetry UCCSD processing """ +import warnings from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.aqua import QuantumInstance @@ -24,6 +25,8 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock +# TODO Ground state interface PR + class TestSymmetries(QiskitChemistryTestCase): """Test for symmetry processing.""" @@ -39,11 +42,13 @@ def setUp(self): except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') 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=True, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) self.qubit_op, _ = self.core.run(self.qmolecule) self.z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) @@ -97,6 +102,8 @@ def test_tapered_op(self): algo_result = algo.run(quantum_instance) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(algo_result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) diff --git a/test/chemistry/test_uccsd_advanced.py b/test/chemistry/test_uccsd_advanced.py index a7ad73d483..4b5e11f7c6 100644 --- a/test/chemistry/test_uccsd_advanced.py +++ b/test/chemistry/test_uccsd_advanced.py @@ -12,6 +12,7 @@ """ Test of UCCSD and HartreeFock Aqua extensions """ +import warnings from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.aqua import QuantumInstance @@ -24,6 +25,8 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import Hamiltonian, QubitMappingType, TransformationType +# TODO Ground state interface PR + # pylint: disable=invalid-name @@ -40,11 +43,13 @@ def setUp(self): spin=0, basis='631g') self.qmolecule = self.driver.run() + warnings.filterwarnings('ignore', category=DeprecationWarning) self.core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=True, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) self.qubit_op, _ = self.core.run(self.qmolecule) z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) @@ -99,7 +104,9 @@ def test_uccsd_hf_qpUCCD(self): algo = VQE(self.qubit_op, var_form, optimizer) result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy_pUCCD, places=6) def test_uccsd_hf_qUCCD0(self): @@ -125,7 +132,9 @@ def test_uccsd_hf_qUCCD0(self): algo = VQE(self.qubit_op, var_form, optimizer) result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0, places=6) def test_uccsd_hf_qUCCD0full(self): @@ -152,7 +161,9 @@ def test_uccsd_hf_qUCCD0full(self): algo = VQE(self.qubit_op, var_form, optimizer) result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0full, places=6) def test_uccsd_hf_qUCCSD(self): @@ -184,7 +195,9 @@ def test_uccsd_hf_qUCCSD(self): algo = VQE(self.the_tapered_op, var_form, optimizer) result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy_UCCSD, places=6) def test_uccsd_hf_excitations(self): diff --git a/test/chemistry/test_uccsd_hartree_fock.py b/test/chemistry/test_uccsd_hartree_fock.py index f01b271c3a..cf7c713564 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -11,6 +11,8 @@ # that they have been altered from the originals. """ Test of UCCSD and HartreeFock Aqua extensions """ + +import warnings from test.chemistry import QiskitChemistryTestCase from ddt import ddt, idata, unpack @@ -25,6 +27,8 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.core import Hamiltonian, QubitMappingType +# TODO Ground state interface PR + @ddt class TestUCCSDHartreeFock(QiskitChemistryTestCase): @@ -39,8 +43,10 @@ def setUp(self): driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) qmolecule = driver.run() + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True) + warnings.filterwarnings('always', category=DeprecationWarning) self.qubit_op, _ = core.run(qmolecule) self.core = core @@ -60,7 +66,9 @@ def test_uccsd_hf(self): backend = BasicAer.get_backend('statevector_simulator') algo = VQE(self.qubit_op, self.var_form, self.optimizer) result = algo.run(QuantumInstance(backend)) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) def test_uccsd_hf_qasm(self): @@ -71,7 +79,9 @@ def test_uccsd_hf_qasm(self): result = algo.run(QuantumInstance(backend, seed_simulator=aqua_globals.random_seed, seed_transpiler=aqua_globals.random_seed)) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, -1.138, places=2) def test_uccsd_hf_aer_statevector(self): @@ -85,7 +95,9 @@ def test_uccsd_hf_aer_statevector(self): backend = Aer.get_backend('statevector_simulator') algo = VQE(self.qubit_op, self.var_form, self.optimizer) result = algo.run(QuantumInstance(backend)) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) def test_uccsd_hf_aer_qasm(self): @@ -102,7 +114,9 @@ def test_uccsd_hf_aer_qasm(self): result = algo.run(QuantumInstance(backend, seed_simulator=aqua_globals.random_seed, seed_transpiler=aqua_globals.random_seed)) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, -1.138, places=2) def test_uccsd_hf_aer_qasm_snapshot(self): @@ -116,7 +130,9 @@ def test_uccsd_hf_aer_qasm_snapshot(self): backend = Aer.get_backend('qasm_simulator') algo = VQE(self.qubit_op, self.var_form, self.optimizer, expectation=AerPauliExpectation()) result = algo.run(QuantumInstance(backend)) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = self.core.process_algorithm_result(result) + warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) EXCITATION_RESULTS = \ From 85c97d0801a3745ab11e9b81be379d3b06744ed8 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 18:14:26 +0200 Subject: [PATCH 063/197] Add TODO in AdaptVQE unittest --- test/chemistry/test_adapt_vqe.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index 45646b7146..f0ca4a5d59 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -82,6 +82,7 @@ def get_custom_solver(self, transformation): def test_custom_excitation_pool(self): """ Test custom excitation pool """ + # TODO rewrite this unittest once we have reworked how AdaptVQE will handle the solver class CustomFactory(VQEUCCSDFactory): """A custom MES factory.""" @@ -90,6 +91,7 @@ def get_solver(self, 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 From dd6bbb3b9f4b317c2d34fa0991e4d1becf19ed97 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 30 Sep 2020 18:14:39 +0200 Subject: [PATCH 064/197] Add missing unittest imports I noticed that in some test files the unittest module is not imported and the `__main__` is not set accordingly. If we do not want to mix these changes into this PR, we can simply revert this commit. --- test/chemistry/test_chc_vscf.py | 6 ++++++ test/chemistry/test_driver_methods.py | 7 +++++++ test/chemistry/test_driver_methods_fcidump.py | 6 ++++++ test/chemistry/test_driver_methods_gaussian.py | 6 ++++++ test/chemistry/test_driver_methods_psi4.py | 6 ++++++ test/chemistry/test_driver_methods_pyquante.py | 6 ++++++ test/chemistry/test_driver_methods_pyscf.py | 6 ++++++ test/chemistry/test_particle_hole.py | 6 ++++++ test/chemistry/test_swaprz.py | 6 ++++++ test/chemistry/test_symmetries.py | 6 ++++++ test/chemistry/test_uccsd_advanced.py | 6 ++++++ test/chemistry/test_uccsd_hartree_fock.py | 6 ++++++ 12 files changed, 73 insertions(+) diff --git a/test/chemistry/test_chc_vscf.py b/test/chemistry/test_chc_vscf.py index 4303c100f3..66de877529 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 @@ -76,3 +78,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_driver_methods.py b/test/chemistry/test_driver_methods.py index eac4268f3b..d6f021314a 100644 --- a/test/chemistry/test_driver_methods.py +++ b/test/chemistry/test_driver_methods.py @@ -13,12 +13,15 @@ """ Test Driver Methods """ import warnings +import unittest + from test.chemistry import QiskitChemistryTestCase from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.aqua.algorithms import NumPyMinimumEigensolver # TODO Ground state interface PR + class TestDriverMethods(QiskitChemistryTestCase): """Common driver tests. For H2 @ 0.735, sto3g""" @@ -61,3 +64,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_fcidump.py b/test/chemistry/test_driver_methods_fcidump.py index cb82532237..9edbeee64b 100644 --- a/test/chemistry/test_driver_methods_fcidump.py +++ b/test/chemistry/test_driver_methods_fcidump.py @@ -12,6 +12,8 @@ """ Test Driver Methods FCIDump """ +import unittest + from test.chemistry import QiskitChemistryTestCase from test.chemistry.test_driver_methods 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..9601b510a3 100644 --- a/test/chemistry/test_driver_methods_gaussian.py +++ b/test/chemistry/test_driver_methods_gaussian.py @@ -12,6 +12,8 @@ """ Test Driver Methods Gaussian """ +import unittest + from test.chemistry.test_driver_methods 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_psi4.py b/test/chemistry/test_driver_methods_psi4.py index ab638f1b1a..537af69f90 100644 --- a/test/chemistry/test_driver_methods_psi4.py +++ b/test/chemistry/test_driver_methods_psi4.py @@ -12,6 +12,8 @@ """ Test Driver Methods PSI4 """ +import unittest + from test.chemistry.test_driver_methods 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..57cb787cda 100644 --- a/test/chemistry/test_driver_methods_pyquante.py +++ b/test/chemistry/test_driver_methods_pyquante.py @@ -12,6 +12,8 @@ """ Test Driver Methods Pyquante """ +import unittest + from test.chemistry.test_driver_methods 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..5b9a11c59f 100644 --- a/test/chemistry/test_driver_methods_pyscf.py +++ b/test/chemistry/test_driver_methods_pyscf.py @@ -12,6 +12,8 @@ """ Test Driver Methods PySCF """ +import unittest + from test.chemistry.test_driver_methods import TestDriverMethods from qiskit.chemistry.drivers import PySCFDriver, UnitsType, HFMethodType from qiskit.chemistry.core import TransformationType, QubitMappingType @@ -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_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_swaprz.py b/test/chemistry/test_swaprz.py index 0a62315fbe..9e1005746a 100644 --- a/test/chemistry/test_swaprz.py +++ b/test/chemistry/test_swaprz.py @@ -13,6 +13,8 @@ """Test of ExcitationPreserving from the circuit library.""" import warnings +import unittest + from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.circuit.library import ExcitationPreserving @@ -67,3 +69,7 @@ def test_excitation_preserving(self): result = operator.process_algorithm_result(result) warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_symmetries.py b/test/chemistry/test_symmetries.py index 37e28bb5f6..e56a862d1b 100644 --- a/test/chemistry/test_symmetries.py +++ b/test/chemistry/test_symmetries.py @@ -13,6 +13,8 @@ """ Test of Symmetry UCCSD processing """ import warnings +import unittest + from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.aqua import QuantumInstance @@ -107,3 +109,7 @@ def test_tapered_op(self): warnings.filterwarnings('always', category=DeprecationWarning) 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 4b5e11f7c6..2e75552882 100644 --- a/test/chemistry/test_uccsd_advanced.py +++ b/test/chemistry/test_uccsd_advanced.py @@ -13,6 +13,8 @@ """ Test of UCCSD and HartreeFock Aqua extensions """ import warnings +import unittest + from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.aqua import QuantumInstance @@ -325,3 +327,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 cf7c713564..8774375430 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -13,6 +13,8 @@ """ Test of UCCSD and HartreeFock Aqua extensions """ import warnings +import unittest + from test.chemistry import QiskitChemistryTestCase from ddt import ddt, idata, unpack @@ -185,3 +187,7 @@ def test_uccsd_excitations(self, expected_result_idx, num_orbitals, num_particle excitation_type=excitation_type) self.assertListEqual(list(excitations), self.EXCITATION_RESULTS[expected_result_idx]) + + +if __name__ == '__main__': + unittest.main() From 04ff1ffcc3efb93a5a2b0fcbd668d066b77792bf Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Wed, 30 Sep 2020 17:40:09 +0100 Subject: [PATCH 065/197] mypy fix --- .../qubit_transformations/fermionic_transformation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index ac6a0c22a6..adeaa4b496 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -15,7 +15,7 @@ The problem is described in a driver. """ -from typing import Optional, List, Union, cast, Tuple +from typing import Optional, List, Union, cast, Tuple, Dict, Any import logging from enum import Enum @@ -113,7 +113,7 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - self._molecule_info = {} + self._molecule_info: Dict[str, Any] = {} def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: From 5e23835f702ba89375bd545156bea689cc48ed07 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 1 Oct 2020 10:13:36 +0100 Subject: [PATCH 066/197] unused code --- .../fermionic_transformation.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index adeaa4b496..e764ee06af 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -542,22 +542,3 @@ def _map_fermionic_operator_to_qubit(fer_op: FermionicOperator, if qubit_mapping == 'parity' and two_qubit_reduction: qubit_op = Z2Symmetries.two_qubit_reduction(qubit_op, num_particles) return qubit_op - - @staticmethod - def _dipole_to_string(_dipole): - """ - Dipole values to strings - """ - dips = [round(x, 8) for x in _dipole] - value = '[' - for i, _ in enumerate(dips): - value += FermionicTransformation._float_to_string(dips[i]) - value += ' ' if i < len(dips) - 1 else ']' - return value - - @staticmethod - def _float_to_string(value, precision=8): - """ - Float to string for results - """ - return '0.0' if value == 0 else ('{:.' + str(precision) + 'f}').format(value).rstrip('0') From affac2194d93a5308583d438c8162cd1a9b173d2 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 1 Oct 2020 15:28:39 +0200 Subject: [PATCH 067/197] Create qiskit.chemistry.results module --- qiskit/chemistry/__init__.py | 3 -- qiskit/chemistry/chemistry_result.py | 27 ----------- .../ground_state_calculation/__init__.py | 2 - .../ground_state_calculation/adapt_vqe.py | 3 +- .../ground_state_calculation.py | 4 +- .../mes_ground_state_calculation.py | 4 +- qiskit/chemistry/results/__init__.py | 24 ++++++++++ .../fermionic_result.py} | 19 ++++++-- qiskit/chemistry/results/state_result.py | 45 +++++++++++++++++++ 9 files changed, 91 insertions(+), 40 deletions(-) delete mode 100644 qiskit/chemistry/chemistry_result.py create mode 100644 qiskit/chemistry/results/__init__.py rename qiskit/chemistry/{ground_state_calculation/fermionic_ground_state_result.py => results/fermionic_result.py} (95%) create mode 100644 qiskit/chemistry/results/state_result.py diff --git a/qiskit/chemistry/__init__.py b/qiskit/chemistry/__init__.py index 075c42c6ac..9b62687e0f 100644 --- a/qiskit/chemistry/__init__.py +++ b/qiskit/chemistry/__init__.py @@ -155,7 +155,6 @@ """ from .qiskit_chemistry_error import QiskitChemistryError -from .chemistry_result import ChemistryResult, DipoleTuple from .qmolecule import QMolecule from .bosonic_operator import BosonicOperator from .fermionic_operator import FermionicOperator @@ -164,8 +163,6 @@ set_qiskit_chemistry_logging) __all__ = ['QiskitChemistryError', - 'ChemistryResult', - 'DipoleTuple', 'QMolecule', 'BosonicOperator', 'FermionicOperator', diff --git a/qiskit/chemistry/chemistry_result.py b/qiskit/chemistry/chemistry_result.py deleted file mode 100644 index 9ecb89eda5..0000000000 --- a/qiskit/chemistry/chemistry_result.py +++ /dev/null @@ -1,27 +0,0 @@ -# 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 chemistry result interface.""" - -from typing import Tuple, Optional - -from qiskit.aqua.algorithms import AlgorithmResult - -# 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 ChemistryResult(AlgorithmResult): - """The chemistry result interface.""" diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index ca13f8c3e8..ce8da55d84 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -13,13 +13,11 @@ """Ground state calculation algorithms.""" from .ground_state_calculation import GroundStateCalculation -from .fermionic_ground_state_result import FermionicGroundStateResult from .adapt_vqe import AdaptVQE from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation from .mes_factories import MESFactory, VQEUCCSDFactory __all__ = ['GroundStateCalculation', - 'FermionicGroundStateResult', 'AdaptVQE', 'MinimumEigensolverGroundStateCalculation', 'MESFactory', diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index ae968d7277..57cb682196 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -27,8 +27,9 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.results import FermionicGroundStateResult + from .ground_state_calculation import GroundStateCalculation -from .fermionic_ground_state_result import FermionicGroundStateResult from .mes_factories import VQEUCCSDFactory logger = logging.getLogger(__name__) diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index e073231c42..f5ecd70c1a 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -14,8 +14,8 @@ from abc import ABC, abstractmethod -from qiskit.chemistry import ChemistryResult from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.results import GroundStateResult from ..qubit_transformations.qubit_operator_transformation import QubitOperatorTransformation @@ -41,7 +41,7 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: self._transformation = transformation @abstractmethod - def compute_groundstate(self, driver: BaseDriver) -> ChemistryResult: + def compute_groundstate(self, driver: BaseDriver) -> GroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. Args: diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index df6fb97f69..55ec77f03e 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -16,9 +16,9 @@ from qiskit.aqua.algorithms import MinimumEigensolver from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.ground_state_calculation import (GroundStateCalculation, - FermionicGroundStateResult) +from qiskit.chemistry.ground_state_calculation import GroundStateCalculation from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation +from qiskit.chemistry.results import FermionicGroundStateResult from .mes_factories import MESFactory diff --git a/qiskit/chemistry/results/__init__.py b/qiskit/chemistry/results/__init__.py new file mode 100644 index 0000000000..d29ba520f9 --- /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 .fermionic_result import DipoleTuple, FermionicResult, FermionicGroundStateResult +from .state_result import StateResult, GroundStateResult + + +__all__ = ['DipoleTuple', + 'StateResult', + 'GroundStateResult', + 'FermionicResult', + 'FermionicGroundStateResult', + ] diff --git a/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py b/qiskit/chemistry/results/fermionic_result.py similarity index 95% rename from qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py rename to qiskit/chemistry/results/fermionic_result.py index 270b379b39..e55f801fba 100644 --- a/qiskit/chemistry/ground_state_calculation/fermionic_ground_state_result.py +++ b/qiskit/chemistry/results/fermionic_result.py @@ -12,17 +12,26 @@ """The fermionic ground state result.""" -from typing import List, Optional, cast +from typing import List, Optional, Tuple, cast + import logging import numpy as np from qiskit.aqua.algorithms import AlgorithmResult -from qiskit.chemistry import QMolecule, ChemistryResult, DipoleTuple +from qiskit.chemistry import QMolecule + +from .state_result import StateResult, GroundStateResult 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 FermionicGroundStateResult(ChemistryResult): +class FermionicResult(StateResult): """The fermionic ground state result.""" @property @@ -331,3 +340,7 @@ def _float_to_string(value: Optional[float], precision: int = 8) -> str: return 'None' else: return '0.0' if value == 0 else ('{:.' + str(precision) + 'f}').format(value).rstrip('0') + + +class FermionicGroundStateResult(FermionicResult, GroundStateResult): + """The fermionic ground state result.""" diff --git a/qiskit/chemistry/results/state_result.py b/qiskit/chemistry/results/state_result.py new file mode 100644 index 0000000000..7f098f247b --- /dev/null +++ b/qiskit/chemistry/results/state_result.py @@ -0,0 +1,45 @@ +# 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. + +"""State results module.""" + +from typing import Optional +import numpy as np + +from qiskit.aqua.algorithms import AlgorithmResult + + +class StateResult(AlgorithmResult): + """The state result interface.""" + + @property + def aux_values(self) -> Optional[np.ndarray]: + """ return aux operator eigen values """ + return self.get('aux_values') + + @aux_values.setter + def aux_values(self, value: np.ndarray) -> None: + """ set aux operator eigen values """ + self.data['aux_values'] = 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 + + +class GroundStateResult(StateResult): + """The ground state result interface.""" From df8d717c8806d382abf8357baac362342022aa86 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 1 Oct 2020 16:13:53 +0200 Subject: [PATCH 068/197] Replace interpret() with add_context() in QubitOperatorTransformations Instead of an interpret() function, the QubitOperatorTransformation interface defines an add_context() function which is used to augment the given StateResult object with information based on the transformation's context. --- .../fermionic_transformation.py | 77 ++++++------------- .../qubit_operator_transformation.py | 11 ++- 2 files changed, 29 insertions(+), 59 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index e764ee06af..bf84028c5b 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -20,15 +20,14 @@ from enum import Enum import numpy as np -from qiskit.aqua.algorithms import MinimumEigensolverResult from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator -from qiskit.chemistry import QiskitChemistryError, QMolecule, DipoleTuple +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, FermionicResult from .qubit_operator_transformation import QubitOperatorTransformation from ..components.initial_states import HartreeFock -from ..ground_state_calculation.fermionic_ground_state_result import FermionicGroundStateResult logger = logging.getLogger(__name__) @@ -428,73 +427,41 @@ def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: np.ndarray) -> Z2Symmetrie z2_symmetries.tapering_values = taper_coef return z2_symmetries - # pylint: disable=unused-argument - def interpret(self, - eigenvalue: float, eigenstate: List[float], aux_values: list - ) -> FermionicGroundStateResult: - """Interpret eigenvalue and eigenstate of qubit Hamiltonian w.r.t. driver. + def add_context(self, result: FermionicResult) -> None: + """Adds contextual information to the state result object. Args: - TODO - - Returns: - GroundState Result TODO + result: a state result object. """ - - fgsr = FermionicGroundStateResult() - fgsr.hartree_fock_energy = self._hf_energy - fgsr.nuclear_repulsion_energy = self._nuclear_repulsion_energy + result.hartree_fock_energy = self._hf_energy + result.nuclear_repulsion_energy = self._nuclear_repulsion_energy if self._nuclear_dipole_moment is not None: - fgsr.nuclear_dipole_moment = tuple(x for x in self._nuclear_dipole_moment) - fgsr.computed_electronic_energy = eigenvalue.real - fgsr.ph_extracted_energy = self._ph_energy_shift - fgsr.frozen_extracted_energy = self._energy_shift - aux_ops_vals = aux_values + 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 + aux_ops_vals = result.aux_values if aux_ops_vals is not None: # Dipole results if dipole aux ops were present dipole_idx = 3 if len(aux_ops_vals) > dipole_idx: - fgsr.reverse_dipole_sign = self._reverse_dipole_sign + result.reverse_dipole_sign = self._reverse_dipole_sign dipm = [] for i in range(dipole_idx, dipole_idx + 3): # Gets X, Y and Z components dipm.append(aux_ops_vals[i][0].real if aux_ops_vals[i] is not None else None) - fgsr.computed_dipole_moment = cast(DipoleTuple, tuple(dipm)) - fgsr.ph_extracted_dipole_moment = (self._ph_x_dipole_shift, - self._ph_y_dipole_shift, - self._ph_z_dipole_shift) - fgsr.frozen_extracted_dipole_moment = (self._x_dipole_shift, - self._y_dipole_shift, - self._z_dipole_shift) + result.computed_dipole_moment = cast(DipoleTuple, tuple(dipm)) + 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) # The first 3 entries are num particles, total angular momentum and magnetization - fgsr.num_particles = aux_ops_vals[0][0].real \ + result.num_particles = aux_ops_vals[0][0].real \ if aux_ops_vals[0] is not None else None - fgsr.total_angular_momentum = aux_ops_vals[1][0].real \ + result.total_angular_momentum = aux_ops_vals[1][0].real \ if aux_ops_vals[1] is not None else None - fgsr.magnetization = aux_ops_vals[2][0].real \ + result.magnetization = aux_ops_vals[2][0].real \ if aux_ops_vals[2] is not None else None - return fgsr - - # Called by public superclass method process_algorithm_result to complete specific processing - def _process_algorithm_result(self, algo_result: MinimumEigensolverResult) \ - -> FermionicGroundStateResult: - """ - - Args: - algo_result: Algorithm Result - - Returns: - Fermionic ground state calculation result - - Raises: - ValueError: Invalid input - """ - if isinstance(algo_result, MinimumEigensolverResult): - msgr = self.interpret(algo_result.eigenvalue, algo_result.eigenstate, - algo_result.aux_operator_eigenvalues) - msgr.algorithm_result = algo_result - return msgr - raise ValueError('_process_algorithm_result should be passed a MinimumEigensolverResult ' - 'all other types have been deprecated and removed.') @staticmethod def _try_reduce_fermionic_operator(fer_op: FermionicOperator, diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 3e7d81dcce..c00fc7486c 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -16,8 +16,8 @@ from typing import Tuple, List from qiskit.aqua.operators.legacy import WeightedPauliOperator -from qiskit.chemistry import ChemistryResult from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.results import StateResult class QubitOperatorTransformation(ABC): @@ -30,7 +30,10 @@ def transform(self, driver: BaseDriver raise NotImplementedError @abstractmethod - def interpret(self, eigenvalue: float, eigenstate: List[float], aux_values: list - ) -> ChemistryResult: - """interprets the results of the ground state calculation""" + def add_context(self, result: StateResult) -> None: + """Adds contextual information to the state result object. + + Args: + result: a state result object. + """ raise NotImplementedError From 7a8f1cda7ee23c7a2af2249241ced00c516b7bc6 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 1 Oct 2020 16:15:43 +0200 Subject: [PATCH 069/197] Fix GroundStateCalculation classes to work with the results interface --- .../ground_state_calculation/adapt_vqe.py | 25 +++++++++++-------- .../mes_ground_state_calculation.py | 13 +++++----- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 57cb682196..3a8e17eba8 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -21,7 +21,7 @@ import numpy as np from qiskit.aqua import AquaError -from qiskit.aqua.algorithms import VQEResult, VQE +from qiskit.aqua.algorithms import VQE from qiskit.aqua.operators import LegacyBaseOperator, WeightedPauliOperator from qiskit.aqua.utils.validation import validate_min from qiskit.chemistry.components.variational_forms import UCCSD @@ -132,7 +132,7 @@ def _check_cyclicity(indices: List[int]) -> bool: # nature of the algorithm. return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) - def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> 'AdaptVQEResult': """Computes the ground state. Args: @@ -219,18 +219,21 @@ def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: raise AquaError('The algorithm finished due to an unforeseen reason!') # extend VQE returned information with additional outputs - raw_result = AdaptVQEResult() - raw_result.combine(raw_vqe_result) - raw_result.num_iterations = iteration - raw_result.final_max_gradient = max_grad[0] - raw_result.finishing_criterion = finishing_criterion + result = AdaptVQEResult() + result.raw_result = raw_vqe_result + result.computed_electronic_energy = raw_vqe_result.eigenvalue.real + result.aux_values = raw_vqe_result.aux_operator_eigenvalues + result.num_iterations = iteration + result.final_max_gradient = max_grad[0] + result.finishing_criterion = finishing_criterion - logger.info('The final energy is: %s', str(raw_result.optimal_value.real)) - return self.transformation.interpret(raw_result.eigenvalue, raw_result.eigenstate, - raw_result.aux_operator_eigenvalues) + self.transformation.add_context(result) + logger.info('The final energy is: %s', str(result.computed_electronic_energy)) + return result -class AdaptVQEResult(VQEResult): + +class AdaptVQEResult(FermionicGroundStateResult): """ AdaptVQE Result.""" @property diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 55ec77f03e..006d25fce5 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -72,12 +72,11 @@ def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: aux_operators = aux_operators if solver.supports_aux_operators() else None - raw_gs_result = solver.compute_minimum_eigenvalue(operator, aux_operators) + raw_mes_result = solver.compute_minimum_eigenvalue(operator, aux_operators) - eigenvalue = raw_gs_result.eigenvalue - eigenstate = raw_gs_result.eigenstate - aux_values = raw_gs_result.aux_operator_eigenvalues - - result = self.transformation.interpret(eigenvalue, eigenstate, aux_values) - result.raw_gs_result = raw_gs_result # add results from minimum eigensolver + result = FermionicGroundStateResult() + result.raw_result = raw_mes_result + result.computed_electronic_energy = raw_mes_result.eigenvalue.real + result.aux_values = raw_mes_result.aux_operator_eigenvalues + self.transformation.add_context(result) return result From a07d36fbec1f5085df61b895526c7e710058b0c6 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 1 Oct 2020 16:22:08 +0200 Subject: [PATCH 070/197] Revert faulty change in github workflow --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a6ef7302b4..29c9f4b05d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -100,7 +100,7 @@ jobs: if: ${{ !cancelled() }} shell: bash Lint: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: matrix: python-version: [3.8] From 3de3c905524ff0d66cd22419e38c5699a6a56f3e Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 5 Oct 2020 19:17:02 +0200 Subject: [PATCH 071/197] unitests WIP first commit --- .../fermionic_transformation.py | 3 +- test/chemistry/#test_molecule.py# | 84 ++++ test/chemistry/test_core_hamiltonian.py | 3 - .../test_fermionic_transformation.py | 184 +++++++++ .../test_initial_state_hartree_fock.py | 3 - .../test_initial_state_hartree_fock_gsc.py | 121 ++++++ test/chemistry/test_swaprz.py | 3 - test/chemistry/test_swaprz_gsc.py | 77 ++++ test/chemistry/test_uccsd_advanced.py | 2 - test/chemistry/test_uccsd_advanced_gsc.py | 362 ++++++++++++++++++ test/chemistry/test_uccsd_hartree_fock.py | 3 - test/chemistry/test_uccsd_hartree_fock_gsc.py | 158 ++++++++ 12 files changed, 988 insertions(+), 15 deletions(-) create mode 100644 test/chemistry/#test_molecule.py# create mode 100644 test/chemistry/test_fermionic_transformation.py create mode 100644 test/chemistry/test_initial_state_hartree_fock_gsc.py create mode 100644 test/chemistry/test_swaprz_gsc.py create mode 100644 test/chemistry/test_uccsd_advanced_gsc.py create mode 100644 test/chemistry/test_uccsd_hartree_fock_gsc.py diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index bf84028c5b..a64edbbcde 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -127,6 +127,7 @@ def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, """ q_molecule = driver.run() ops, aux_ops = self._do_transform(q_molecule) # _do_transform(q_molecule) + return ops, aux_ops def _do_transform(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, @@ -358,7 +359,7 @@ def _process_z2symmetry_reduction(self, if self._z2symmetry_reduction == 'auto': hf_state = HartreeFock(num_orbitals=self._molecule_info['num_orbitals'], - qubit_mapping=self._qubit_mapping.value, + 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) diff --git a/test/chemistry/#test_molecule.py# b/test/chemistry/#test_molecule.py# new file mode 100644 index 0000000000..4ef4eb713b --- /dev/null +++ b/test/chemistry/#test_molecule.py# @@ -0,0 +1,84 @@ +import unittest +from functools import partial + +import numpy as np +from qiskit.aqua.operators.weighted_pauli_operator import WeightedPauliOperator + +from qiskit.chemistry.molecule import Molecule + + +class TestMolecule(unittest.TestCase): + def test_construct(self): + stretch = partial( + Molecule.absolute_stretching, + kwargs={'atom_pair': (1, 0)}) + + m = Molecule(geometry=[['H', [0., 0., 0.]], ['H', [0., 0., 1.]]], + degrees_of_freedom=[stretch], + masses=[1, 1]) + + m = Molecule(geometry=[['H', [0., 0., 0.]], ['H', [0., 0., 1.]]], + degrees_of_freedom=[stretch]) + + def test_stretch(self): + geom = Molecule.absolute_stretching(atom_pair=(1, 0), + perturbation=2, + geometry=[['H', [0., 0., 0.]], [ + 'H', [0., 0., 1.]]] + ) + self.assertListEqual(geom[1][1], [0., 0., 3.]) + geom = Molecule.absolute_stretching(atom_pair=(1, 0), + perturbation=-.1, + geometry=geom + ) + self.assertListEqual(geom[1][1], [0., 0., 3. - .1]) + + def test_bend(self): + geom = Molecule.absolute_bending(atom_trio=(1, 0, 2), + bend=np.pi / 2, + geometry=[['H', [0., 0., 0.]], + ['H', [0., 0., 1.]], + ['Li', [0., 1., -1.]], + ] + ) + self.assertListEqual(geom[1][1], [0., 1., 0.]) + geom = Molecule.absolute_bending(atom_trio=(1, 0, 2), + bend=-np.pi / 4, + geometry=geom + ) + np.testing.assert_array_almost_equal( + geom[1][1], [0., np.sqrt(2) / 2, np.sqrt(2) / 2]) + geom = Molecule.absolute_bending(atom_trio=(2, 0, 1), + bend=-np.pi / 4, + geometry=geom + ) + np.testing.assert_array_almost_equal(geom[2][1], [0., 0., -np.sqrt(2)]) + + # Test linear case + geom = Molecule.absolute_bending(atom_trio=(1, 0, 2), + bend=np.pi / 2, + geometry=[['H', [0., 0., 0.]], + ['H', [0., 0., 1.]], + ['Li', [0., 0., -1.]], + ] + ) + self.assertListEqual(geom[1][1], [1., 0., 0.]) + + def test_get_perturbations(self): + stretch1 = partial(Molecule.absolute_stretching, atom_pair=(1, 0)) + bend = partial(Molecule.absolute_bending, atom_trio=(1, 0, 2)) + stretch2 = partial(Molecule.absolute_stretching, atom_pair=(0, 1)) + + m = Molecule(geometry=[['H', [0., 0., 0.]], + ['H', [0., 0., 1.]], + ['Li', [0., 1., -1.]], + ], + degrees_of_freedom=[stretch1, bend, stretch2], + masses=[1, 1, 1]) + geom = m.get_perturbed_geom([2, np.pi / 2, -.5]) + np.testing.assert_array_almost_equal(geom[0][1], [0.0, 0.5, 0.0]) + np.testing.assert_array_almost_equal(geom[1][1], [0., 3., 0.]) + np.testing.assert_array_almost_equal(geom[2][1], [0., 1., -1.]) + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_core_hamiltonian.py b/test/chemistry/test_core_hamiltonian.py index 7972b051d5..563bb9c133 100644 --- a/test/chemistry/test_core_hamiltonian.py +++ b/test/chemistry/test_core_hamiltonian.py @@ -21,9 +21,6 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType -# TODO Ground state interface PR - - class TestCoreHamiltonian(QiskitChemistryTestCase): """core/hamiltonian Driver tests.""" diff --git a/test/chemistry/test_fermionic_transformation.py b/test/chemistry/test_fermionic_transformation.py new file mode 100644 index 0000000000..38fa19b7a3 --- /dev/null +++ b/test/chemistry/test_fermionic_transformation.py @@ -0,0 +1,184 @@ +# 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 warnings +import unittest + +from test.chemistry import QiskitChemistryTestCase +from qiskit.aqua.operators import WeightedPauliOperator +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_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, WeightedPauliOperator)) + self.assertIsNotNone(qubit_op) + self.assertEqual(qubit_op.num_qubits, num_qubits) + self.assertEqual(len(qubit_op.to_dict()['paulis']), num_paulis) + + def test_output(self): + """ output test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=False, + orbital_reduction=[]) + + warnings.filterwarnings('always', category=DeprecationWarning) + 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 """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + warnings.filterwarnings('always', category=DeprecationWarning) + 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 """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=True, + freeze_core=False, + orbital_reduction=[]) + + warnings.filterwarnings('always', category=DeprecationWarning) + 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 """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + warnings.filterwarnings('always', category=DeprecationWarning) + 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 """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.BRAVYI_KITAEV, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + warnings.filterwarnings('always', category=DeprecationWarning) + 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 """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation(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, _ = 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 """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=[]) + + warnings.filterwarnings('always', category=DeprecationWarning) + 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!) + """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation(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, _ = fermionic_transformation.transform(self.driver) + 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) + +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 46e675d8fb..99c0b25f09 100644 --- a/test/chemistry/test_initial_state_hartree_fock.py +++ b/test/chemistry/test_initial_state_hartree_fock.py @@ -23,9 +23,6 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType -# TODO Ground state interface PR - - @ddt class TestInitialStateHartreeFock(QiskitChemistryTestCase): """ Initial State HartreeFock tests """ diff --git a/test/chemistry/test_initial_state_hartree_fock_gsc.py b/test/chemistry/test_initial_state_hartree_fock_gsc.py new file mode 100644 index 0000000000..a03904e38a --- /dev/null +++ b/test/chemistry/test_initial_state_hartree_fock_gsc.py @@ -0,0 +1,121 @@ +# 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 Initial State HartreeFock """ + +import warnings +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.chemistry import QiskitChemistryError +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType +from qiskit.chemistry.core import QubitMappingType, TransformationType +from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation + +@ddt +class TestInitialStateHartreeFock(QiskitChemistryTestCase): + """ Initial State HartreeFock tests """ + + def test_qubits_4_jw_h2(self): + """ qubits 4 jw h2 test """ + hrfo = HartreeFock(4, [1, 1], 'jordan_wigner', False) + cct = hrfo.construct_circuit('vector') + np.testing.assert_array_equal(cct, [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) + + def test_qubits_4_py_h2(self): + """ qubits 4 py h2 test """ + hrfo = HartreeFock(4, [1, 1], 'parity', False) + cct = hrfo.construct_circuit('vector') + np.testing.assert_array_equal(cct, [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) + + def test_qubits_4_bk_h2(self): + """ qubits 4 bk h2 test """ + hrfo = HartreeFock(4, [1, 1], 'bravyi_kitaev', False) + cct = hrfo.construct_circuit('vector') + np.testing.assert_array_equal(cct, [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) + + def test_qubits_2_py_h2(self): + """ qubits 2 py h2 test """ + hrfo = HartreeFock(4, 2, 'parity', True) + cct = hrfo.construct_circuit('vector') + np.testing.assert_array_equal(cct, [0.0, 1.0, 0.0, 0.0]) + + def test_qubits_2_py_h2_cct(self): + """ qubits 2 py h2 cct test """ + hrfo = HartreeFock(4, [1, 1], 'parity', True) + cct = hrfo.construct_circuit('circuit') + self.assertEqual(cct.qasm(), 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[2];\n' + 'x q[0];\n') + + def test_qubits_6_py_lih_cct(self): + """ qubits 6 py lih cct test """ + hrfo = HartreeFock(10, [1, 1], 'parity', True, [1, 2]) + cct = hrfo.construct_circuit('circuit') + self.assertEqual(cct.qasm(), 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[6];\n' + 'x q[0];\n' + 'x q[1];\n') + + def test_qubits_10_bk_lih_bitstr(self): + """ qubits 10 bk lih bitstr test """ + hrfo = HartreeFock(10, [1, 1], 'bravyi_kitaev', False) + bitstr = hrfo.bitstr + np.testing.assert_array_equal(bitstr, + [False, False, False, False, True, + False, True, False, True, True]) + + @idata([ + [QubitMappingType.JORDAN_WIGNER], + [QubitMappingType.PARITY], + [QubitMappingType.BRAVYI_KITAEV] + ]) + @unpack + def test_hf_value(self, mapping): + """ hf value test """ + try: + 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') + + fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=mapping, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(driver) + + qubit_op = op_converter.to_matrix_operator(qubit_op) + 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 \ + + fermionic_transformation._nuclear_repulsion_energy + + self.assertAlmostEqual(fermionic_transformation._hf_energy, hf_energy, places=6) + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_swaprz.py b/test/chemistry/test_swaprz.py index 9e1005746a..00144ec2ef 100644 --- a/test/chemistry/test_swaprz.py +++ b/test/chemistry/test_swaprz.py @@ -25,9 +25,6 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.core import Hamiltonian, QubitMappingType -# TODO Ground state interface PR - - class TestExcitationPreserving(QiskitChemistryTestCase): """The ExcitationPresering wavefunction was design to preserve the excitation of the system. diff --git a/test/chemistry/test_swaprz_gsc.py b/test/chemistry/test_swaprz_gsc.py new file mode 100644 index 0000000000..57e5e41528 --- /dev/null +++ b/test/chemistry/test_swaprz_gsc.py @@ -0,0 +1,77 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 ExcitationPreserving from the circuit library.""" + +import warnings +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.drivers import HDF5Driver +from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation + +class TestExcitationPreserving(QiskitChemistryTestCase): + """The ExcitationPresering wavefunction was design to preserve the excitation of the system. + + We test it here from chemistry with JORDAN_WIGNER mapping (then the number of particles + is preserved) and HartreeFock initial state to set it up. This facilitates testing + ExcitationPreserving using these chemistry components/problem to ensure its correct operation. + """ + + def setUp(self): + super().setUp() + self.seed = 50 + aqua_globals.random_seed = self.seed + self.reference_energy = -1.137305593252385 + + def test_excitation_preserving(self): + """Test the excitation preserving wavefunction on a chemistry example.""" + + warnings.filterwarnings('ignore', category=DeprecationWarning) + self.driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) + fermionic_transformation = FermionicTransformation(qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False) + + warnings.filterwarnings('always', category=DeprecationWarning) + qubit_op, _ = fermionic_transformation.transform(self.driver) + + optimizer = SLSQP(maxiter=100) + 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) + + 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 = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + self.assertAlmostEqual(result.energy, self.reference_energy, places=4) + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_uccsd_advanced.py b/test/chemistry/test_uccsd_advanced.py index 2e75552882..ef171809f8 100644 --- a/test/chemistry/test_uccsd_advanced.py +++ b/test/chemistry/test_uccsd_advanced.py @@ -27,8 +27,6 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import Hamiltonian, QubitMappingType, TransformationType -# TODO Ground state interface PR - # pylint: disable=invalid-name diff --git a/test/chemistry/test_uccsd_advanced_gsc.py b/test/chemistry/test_uccsd_advanced_gsc.py new file mode 100644 index 0000000000..fad1b6f6f0 --- /dev/null +++ b/test/chemistry/test_uccsd_advanced_gsc.py @@ -0,0 +1,362 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 UCCSD and HartreeFock Aqua extensions """ + +import warnings +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.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 QubitMappingType, TransformationType +from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation + +# pylint: disable=invalid-name + + +class TestUCCSDHartreeFock(QiskitChemistryTestCase): + """Test for these aqua extensions.""" + + def setUp(self): + super().setUp() + try: + self.molecule = "H 0.000000 0.000000 0.735000;H 0.000000 0.000000 0.000000" + self.driver = PySCFDriver(atom=self.molecule, + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='631g') + + self.fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=[]) + + warnings.filterwarnings('always', category=DeprecationWarning) + self.qubit_op, _ = self.fermionic_transformation.transform(self.driver) + + 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.z2_symmetries = z2_symmetries + self.the_tapered_op = tapered_ops[smallest_idx] + + self.reference_energy_pUCCD = -1.1434447924298028 + self.reference_energy_UCCD0 = -1.1476045878481704 + self.reference_energy_UCCD0full = -1.1515491334334347 + # reference energy of UCCSD/VQE with tapering everywhere + self.reference_energy_UCCSD = -1.1516142309717594 + # reference energy of UCCSD/VQE when no tapering on excitations is used + self.reference_energy_UCCSD_no_tap_exc = -1.1516142309717594 + # excitations for succ + self.reference_singlet_double_excitations = [[0, 1, 4, 5], [0, 1, 4, 6], [0, 1, 4, 7], + [0, 2, 4, 6], [0, 2, 4, 7], [0, 3, 4, 7]] + # groups for succ_full + self.reference_singlet_groups = [[[0, 1, 4, 5]], [[0, 1, 4, 6], [0, 2, 4, 5]], + [[0, 1, 4, 7], [0, 3, 4, 5]], [[0, 2, 4, 6]], + [[0, 2, 4, 7], [0, 3, 4, 6]], [[0, 3, 4, 7]]] + except QiskitChemistryError: + self.skipTest('PYSCF driver does not appear to be installed') + + def test_uccsd_hf_qpUCCD(self): + """ paired uccd test """ + + optimizer = SLSQP(maxiter=100) + + 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 = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + 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.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 = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0, places=6) + + def test_uccsd_hf_qUCCD0full(self): + """ singlet full uccd test """ + + optimizer = SLSQP(maxiter=100) + + 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 = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + 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' + ) + + warnings.filterwarnings('always', category=DeprecationWarning) + qubit_op, _ = fermionic_transformation.transform(self.driver) + + # optimizer + optimizer = SLSQP(maxiter=100) + + # initial state + 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=qubit_op.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=qubit_op.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'))) + + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + 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.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.the_tapered_op.z2_symmetries.sq_list) + + # check singlet excitations + 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.the_tapered_op.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( + double_excitations_singlet, self.reference_singlet_double_excitations) + self.assertEqual(res, True) + + # check grouped singlet excitations + 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.the_tapered_op.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( + double_excitations_singlet_grouped, self.reference_singlet_groups) + self.assertEqual(res_groups, True) + + @staticmethod + def pop_el_when_matched(list1, list2): + """ + Compares if in list1 and list2 one of excitations is the same (regardless of permutations of + its elements). When same excitation is found, it returns the 2 lists without that excitation + . + + Args: + list1 (list): list of excitations (e.g. [[0, 2, 4, 6], [0, 2, 4, 7]]) + list2 (list): list of excitations + + Returns: + list: list1 with one popped element if match was found + list: list2 with one popped element if match was found + """ + counter = 0 + for i, exc1 in enumerate(list1): + for j, exc2 in enumerate(list2): + for ind1 in exc1: + for ind2 in exc2: + if ind1 == ind2: + counter += 1 + if counter == len(exc1) and counter == len(exc2): + list1.pop(i) + list2.pop(j) + break + break + return list1, list2 + + @staticmethod + def excitation_lists_comparator(list1, list2): + """ + Compares if list1 and list2 contain same excitations (regardless of permutations of + its elements). Only works provided all indices for an excitation are different. + + Args: + list1 (list): list of excitations (e.g. [[0, 2, 4, 6], [0, 2, 4, 7]]) + list2 (list): list of excitations + + Returns: + bool: True or False, if list1 and list2 contain the same excitations + """ + if len(list1) != len(list2): + return False + + number_el = len(list1) + + for _ in range(number_el): + list1, list2 = TestUCCSDHartreeFock.pop_el_when_matched(list1, list2) + + return bool(len(list1) or len(list2) in [0]) + + @staticmethod + def group_excitation_lists_comparator(glist1, glist2): + """ + Compares if list1 and list2 contain same excitations (regardless of permutations of + its elements). Only works provided all indices for an excitation are different. + + Args: + glist1 (list): list of excitations (e.g. [[0, 2, 4, 6], [0, 2, 4, 7]]) + glist2 (list): list of excitations + + Returns: + bool: True or False, if list1 and list2 contain the same excitations + """ + if len(glist1) != len(glist2): + return False + + number_groups = len(glist1) + counter = 0 + for _, gr1 in enumerate(glist1): + for _, gr2 in enumerate(glist2): + res = TestUCCSDHartreeFock.excitation_lists_comparator(gr1, gr2) + if res is True: + 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 8774375430..f0fd8c218a 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -29,9 +29,6 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.core import Hamiltonian, QubitMappingType -# TODO Ground state interface PR - - @ddt class TestUCCSDHartreeFock(QiskitChemistryTestCase): """Test for these aqua extensions.""" diff --git a/test/chemistry/test_uccsd_hartree_fock_gsc.py b/test/chemistry/test_uccsd_hartree_fock_gsc.py new file mode 100644 index 0000000000..c2f07866df --- /dev/null +++ b/test/chemistry/test_uccsd_hartree_fock_gsc.py @@ -0,0 +1,158 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 UCCSD and HartreeFock Aqua extensions """ + +import warnings +import unittest + +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 +from qiskit.aqua.components.optimizers import SLSQP, SPSA +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.drivers import HDF5Driver +from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation + +class TestUCCSDHartreeFock(QiskitChemistryTestCase): + """Test for these aqua extensions.""" + + def setUp(self): + super().setUp() + self.reference_energy = -1.1373060356951838 + + self.seed = 700 + aqua_globals.random_seed = self.seed + + self.driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) + fermionic_transformation = FermionicTransformation(qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False) + + warnings.filterwarnings('always', category=DeprecationWarning) + self.qubit_op, _ = fermionic_transformation.transform(self.driver) + self.fermionic_transformation = fermionic_transformation + + self.optimizer = SLSQP(maxiter=100) + 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') + solver = VQE(var_form=self.var_form , optimizer=self.optimizer , + quantum_instance=QuantumInstance(backend = backend)) + + gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(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) + 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 = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + warnings.filterwarnings('always', category=DeprecationWarning) + self.assertAlmostEqual(result.energy, -1.138, places=2) + + def test_uccsd_hf_aer_statevector(self): + """ uccsd hf test with Aer statevector """ + try: + # pylint: disable=import-outside-toplevel + from qiskit import Aer + except Exception as ex: # pylint: disable=broad-except + self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) + return + backend = Aer.get_backend('statevector_simulator') + solver = VQE(var_form=self.var_form, optimizer=self.optimizer, + quantum_instance=QuantumInstance(backend=backend)) + + gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + self.assertAlmostEqual(result.energy, self.reference_energy, places=6) + + def test_uccsd_hf_aer_qasm(self): + """ uccsd hf test with Aer qasm_simulator. """ + try: + # pylint: disable=import-outside-toplevel + from qiskit import Aer + except Exception as ex: # pylint: disable=broad-except + self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) + return + backend = Aer.get_backend('qasm_simulator') + optimizer = SPSA(maxiter=200, last_avg=5) + 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 = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + self.assertAlmostEqual(result.energy, -1.138, places=2) + + def test_uccsd_hf_aer_qasm_snapshot(self): + """ uccsd hf test with Aer qasm_simulator snapshot. """ + try: + # pylint: disable=import-outside-toplevel + from qiskit import Aer + except Exception as ex: # pylint: disable=broad-except + self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) + return + backend = Aer.get_backend('qasm_simulator') + optimizer = SPSA(maxiter=200, last_avg=5) + solver = VQE(var_form=self.var_form, optimizer=self.optimizer, + expectation=AerPauliExpectation(), + quantum_instance=QuantumInstance(backend=backend)) + + gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + warnings.filterwarnings('ignore', category=DeprecationWarning) + warnings.filterwarnings('always', category=DeprecationWarning) + self.assertAlmostEqual(result.energy, self.reference_energy, places=6) + +if __name__ == '__main__': + unittest.main() From 68009118267637eb75c361bc81091fb0c2eeb53a Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 5 Oct 2020 19:23:56 +0200 Subject: [PATCH 072/197] remove deprecation suppressing from new tests --- test/chemistry/test_fermionic_transformation.py | 16 ---------------- test/chemistry/test_swaprz_gsc.py | 4 ---- test/chemistry/test_uccsd_advanced_gsc.py | 11 ----------- test/chemistry/test_uccsd_hartree_fock_gsc.py | 8 -------- 4 files changed, 39 deletions(-) diff --git a/test/chemistry/test_fermionic_transformation.py b/test/chemistry/test_fermionic_transformation.py index 38fa19b7a3..ac6400da1d 100644 --- a/test/chemistry/test_fermionic_transformation.py +++ b/test/chemistry/test_fermionic_transformation.py @@ -59,14 +59,12 @@ def _validate_input_object(self, qubit_op, num_qubits=4, num_paulis=15): def test_output(self): """ output test """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=False, orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = fermionic_transformation.transform(self.driver) self._validate_vars(fermionic_transformation) self._validate_info(fermionic_transformation, actual_two_qubit_reduction=True) @@ -74,14 +72,12 @@ def test_output(self): def test_jordan_wigner(self): """ jordan wigner test """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = fermionic_transformation.transform(self.driver) self._validate_vars(fermionic_transformation) self._validate_info(fermionic_transformation) @@ -89,14 +85,12 @@ def test_jordan_wigner(self): def test_jordan_wigner_2q(self): """ jordan wigner 2q test """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=True, freeze_core=False, orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = fermionic_transformation.transform(self.driver) self._validate_vars(fermionic_transformation) # Reported effective 2 qubit reduction should be false @@ -105,14 +99,12 @@ def test_jordan_wigner_2q(self): def test_parity(self): """ parity test """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = fermionic_transformation.transform(self.driver) self._validate_vars(fermionic_transformation) self._validate_info(fermionic_transformation) @@ -120,14 +112,12 @@ def test_parity(self): def test_bravyi_kitaev(self): """ bravyi kitaev test """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.BRAVYI_KITAEV, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = fermionic_transformation.transform(self.driver) self._validate_vars(fermionic_transformation) self._validate_info(fermionic_transformation) @@ -135,14 +125,12 @@ def test_bravyi_kitaev(self): def test_particle_hole(self): """ particle hole test """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation(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, _ = fermionic_transformation.transform(self.driver) self._validate_vars(fermionic_transformation, ph_energy_shift=-1.83696799) self._validate_info(fermionic_transformation) @@ -150,14 +138,12 @@ 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) fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=True, orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = fermionic_transformation.transform(self.driver) self._validate_vars(fermionic_transformation) self._validate_info(fermionic_transformation) @@ -167,14 +153,12 @@ def test_orbital_reduction(self): """ orbital reduction test --- Remove virtual orbital just for test purposes (not sensible!) """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation(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, _ = fermionic_transformation.transform(self.driver) self._validate_vars(fermionic_transformation) self._validate_info(fermionic_transformation, num_orbitals=2) diff --git a/test/chemistry/test_swaprz_gsc.py b/test/chemistry/test_swaprz_gsc.py index 57e5e41528..599ff9f4a3 100644 --- a/test/chemistry/test_swaprz_gsc.py +++ b/test/chemistry/test_swaprz_gsc.py @@ -44,12 +44,10 @@ def setUp(self): def test_excitation_preserving(self): """Test the excitation preserving wavefunction on a chemistry example.""" - warnings.filterwarnings('ignore', category=DeprecationWarning) self.driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) fermionic_transformation = FermionicTransformation(qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=False) - warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = fermionic_transformation.transform(self.driver) optimizer = SLSQP(maxiter=100) @@ -69,8 +67,6 @@ def test_excitation_preserving(self): result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('ignore', category=DeprecationWarning) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=4) if __name__ == '__main__': diff --git a/test/chemistry/test_uccsd_advanced_gsc.py b/test/chemistry/test_uccsd_advanced_gsc.py index fad1b6f6f0..4d0fb5f997 100644 --- a/test/chemistry/test_uccsd_advanced_gsc.py +++ b/test/chemistry/test_uccsd_advanced_gsc.py @@ -12,7 +12,6 @@ """ Test of UCCSD and HartreeFock Aqua extensions """ -import warnings import unittest from test.chemistry import QiskitChemistryTestCase @@ -52,7 +51,6 @@ def setUp(self): freeze_core=True, orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) self.qubit_op, _ = self.fermionic_transformation.transform(self.driver) z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) @@ -115,8 +113,6 @@ def test_uccsd_hf_qpUCCD(self): result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('ignore', category=DeprecationWarning) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy_pUCCD, places=6) def test_uccsd_hf_qUCCD0(self): @@ -147,8 +143,6 @@ def test_uccsd_hf_qUCCD0(self): result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('ignore', category=DeprecationWarning) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0, places=6) def test_uccsd_hf_qUCCD0full(self): @@ -180,8 +174,6 @@ def test_uccsd_hf_qUCCD0full(self): result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('ignore', category=DeprecationWarning) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0full, places=6) def test_uccsd_hf_qUCCSD(self): @@ -195,7 +187,6 @@ def test_uccsd_hf_qUCCSD(self): z2symmetry_reduction = 'auto' ) - warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = fermionic_transformation.transform(self.driver) # optimizer @@ -228,8 +219,6 @@ def test_uccsd_hf_qUCCSD(self): result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('ignore', category=DeprecationWarning) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy_UCCSD, places=6) def test_uccsd_hf_excitations(self): diff --git a/test/chemistry/test_uccsd_hartree_fock_gsc.py b/test/chemistry/test_uccsd_hartree_fock_gsc.py index c2f07866df..628d0d23ad 100644 --- a/test/chemistry/test_uccsd_hartree_fock_gsc.py +++ b/test/chemistry/test_uccsd_hartree_fock_gsc.py @@ -45,7 +45,6 @@ def setUp(self): fermionic_transformation = FermionicTransformation(qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=False) - warnings.filterwarnings('always', category=DeprecationWarning) self.qubit_op, _ = fermionic_transformation.transform(self.driver) self.fermionic_transformation = fermionic_transformation @@ -86,7 +85,6 @@ def test_uccsd_hf_qasm(self): result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, -1.138, places=2) def test_uccsd_hf_aer_statevector(self): @@ -105,8 +103,6 @@ def test_uccsd_hf_aer_statevector(self): result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('ignore', category=DeprecationWarning) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) def test_uccsd_hf_aer_qasm(self): @@ -129,8 +125,6 @@ def test_uccsd_hf_aer_qasm(self): result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('ignore', category=DeprecationWarning) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, -1.138, places=2) def test_uccsd_hf_aer_qasm_snapshot(self): @@ -150,8 +144,6 @@ def test_uccsd_hf_aer_qasm_snapshot(self): gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) result = gsc.compute_groundstate(self.driver) - warnings.filterwarnings('ignore', category=DeprecationWarning) - warnings.filterwarnings('always', category=DeprecationWarning) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) if __name__ == '__main__': From a5704bd8796f6ad7830d8363186236dded468a41 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Tue, 6 Oct 2020 14:45:23 +0200 Subject: [PATCH 073/197] support additional aux ops and conv to dict --- .../ground_state_calculation/adapt_vqe.py | 20 +++-- .../ground_state_calculation.py | 8 +- .../mes_ground_state_calculation.py | 30 +++++-- .../fermionic_transformation.py | 89 ++++++++++++------- .../qubit_operator_transformation.py | 7 +- qiskit/chemistry/results/state_result.py | 7 +- 6 files changed, 105 insertions(+), 56 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 3a8e17eba8..3523732767 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -10,11 +10,9 @@ # 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. -""" +"""A ground state calculation employing the AdaptVQE algorithm.""" -from typing import Optional, List +from typing import Optional, List, Dict, Any import logging import re import warnings @@ -132,18 +130,24 @@ def _check_cyclicity(indices: List[int]) -> bool: # nature of the algorithm. return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) - def compute_groundstate(self, driver: BaseDriver) -> 'AdaptVQEResult': + def compute_groundstate(self, driver: BaseDriver, + additional_operators: Optional[Dict[str, Any]] = None + ) -> 'AdaptVQEResult': """Computes the ground state. Args: driver: a chemistry driver. + additional_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: A fermionic ground state result. """ - operator, aux_operators = self._transformation.transform(driver) + operator, aux_operators = self._transformation.transform(driver, additional_operators) vqe = self._solver.get_solver(self._transformation) if not isinstance(vqe, VQE): @@ -207,7 +211,7 @@ def compute_groundstate(self, driver: BaseDriver) -> 'AdaptVQEResult': # once finished evaluate auxiliary operators if any if aux_operators is not None and aux_operators: - vqe.compute_minimum_eigenvalue(operator, aux_operators) + vqe.compute_minimum_eigenvalue(operator, list(aux_operators.values())) if threshold_satisfied: finishing_criterion = 'Threshold converged' @@ -222,7 +226,7 @@ def compute_groundstate(self, driver: BaseDriver) -> 'AdaptVQEResult': result = AdaptVQEResult() result.raw_result = raw_vqe_result result.computed_electronic_energy = raw_vqe_result.eigenvalue.real - result.aux_values = raw_vqe_result.aux_operator_eigenvalues + result.aux_values = dict(zip(aux_operators.keys(), raw_vqe_result.aux_operator_eigenvalues)) result.num_iterations = iteration result.final_max_gradient = max_grad[0] result.finishing_criterion = finishing_criterion diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index f5ecd70c1a..9233019ee7 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -13,6 +13,7 @@ """The ground state calculation interface.""" from abc import ABC, abstractmethod +from typing import Dict, Any, Optional from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.results import GroundStateResult @@ -41,11 +42,16 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: self._transformation = transformation @abstractmethod - def compute_groundstate(self, driver: BaseDriver) -> GroundStateResult: + def compute_groundstate(self, driver: BaseDriver, + additional_operators: Optional[Dict[str, Any]] = None + ) -> GroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. Args: driver: BaseDriver + additional_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 molecular ground state result diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 006d25fce5..149d934594 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -12,9 +12,10 @@ """Ground state computation using a minimum eigensolver.""" -from typing import Union +from typing import Union, Dict, Any, Optional from qiskit.aqua.algorithms import MinimumEigensolver +from qiskit.chemistry import FermionicOperator from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.ground_state_calculation import GroundStateCalculation from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation @@ -53,16 +54,30 @@ def returns_groundstate(self) -> bool: return False - def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: + def compute_groundstate(self, driver: BaseDriver, + additional_operators: Optional[Dict[str, Any]] = None + ) -> FermionicGroundStateResult: """Compute Ground State properties. Args: driver: A chemistry driver. + additional_operators: Additional auxiliary ``FermionicOperator``s to evaluate at the + ground state. + + Raises: + ValueError: If an operator in ``additional_operators`` is not of type + ``FermionicOperator``. Returns: Ground state result TODO """ - operator, aux_operators = self.transformation.transform(driver) + if any(not isinstance(op, FermionicOperator) for op in additional_operators.values()): + raise ValueError('The additional operators must be of type FermionicOperator.') + + # 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, additional_operators) if isinstance(self._solver, MESFactory): # this must be called after transformation.transform @@ -70,13 +85,16 @@ def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: else: solver = self._solver - aux_operators = aux_operators if solver.supports_aux_operators() else None + # convert aux_operators to a list for the minimum eigensolver + mes_aux_ops = list(aux_operators.values()) if solver.supports_aux_operators() else None + raw_mes_result = solver.compute_minimum_eigenvalue(operator, mes_aux_ops) - raw_mes_result = solver.compute_minimum_eigenvalue(operator, aux_operators) + # convert the aux_values back to a dictionary + aux_values = dict(zip(aux_operators.keys(), raw_mes_result.aux_operator_eigenvalues)) result = FermionicGroundStateResult() result.raw_result = raw_mes_result result.computed_electronic_energy = raw_mes_result.eigenvalue.real - result.aux_values = raw_mes_result.aux_operator_eigenvalues + result.aux_values = aux_values self.transformation.add_context(result) return result diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index a64edbbcde..499f9c02a7 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -114,28 +114,30 @@ def __init__(self, self._molecule_info: Dict[str, Any] = {} - def transform(self, driver: BaseDriver) -> Tuple[WeightedPauliOperator, - List[WeightedPauliOperator]]: - """ - Transformation to qubit operator from the driver + def transform(self, driver: BaseDriver, + additional_operators: Optional[Dict[str, Any]] = None + ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: + """Transformation to qubit operator from the driver Args: driver: Base Driver + additional_operators: Additional ``FermionicOperator``s to map to a qubit operator. Returns: qubit operator, auxiliary operators """ q_molecule = driver.run() - ops, aux_ops = self._do_transform(q_molecule) # _do_transform(q_molecule) + ops, aux_ops = self._do_transform(q_molecule, additional_operators) return ops, aux_ops - def _do_transform(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, - List[WeightedPauliOperator]]: + def _do_transform(self, qmolecule: QMolecule, + additional_operators: Optional[Dict[str, Any]] = None + ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: """ - Args: qmolecule: qmolecule + additional_operators: Additional ``FermionicOperator``s to map to a qubit operator. Returns: (qubit operator, auxiliary operators) @@ -227,7 +229,7 @@ def _do_transform(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, logger.debug(' num paulis: %s, num qubits: %s', len(qubit_op.paulis), qubit_op.num_qubits) - aux_ops = [] + aux_ops = {} def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: """ @@ -241,10 +243,10 @@ def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: 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) + aux_ops[name] = aux_qop logger.debug(' num paulis: %s', aux_qop.paulis) + # add standard auxiliary operators 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') @@ -252,6 +254,11 @@ def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: logger.debug('Creating aux op for Magnetization') _add_aux_op(fer_op.total_magnetization(), 'Magnetization') + # add user specified auxiliary operators + if additional_operators is not None: + for name, aux_op in additional_operators.items(): + _add_aux_op(aux_op, name) + if qmolecule.has_dipole_integrals(): def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ -> Tuple[WeightedPauliOperator, float, float]: @@ -292,9 +299,8 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ op_dipole_z, self._z_dipole_shift, self._ph_z_dipole_shift = \ _dipole_op(qmolecule.z_dipole_integrals, 'z') - aux_ops.append(op_dipole_x) - aux_ops.append(op_dipole_y) - aux_ops.append(op_dipole_z) + for op_dipole in [op_dipole_x, op_dipole_y, op_dipole_z]: + aux_ops[op_dipole.name] = op_dipole logger.info('Molecule num electrons: %s, remaining for processing: %s', [num_alpha, num_beta], new_nel) @@ -352,10 +358,10 @@ def _process_z2symmetry_reduction(self, 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): + for name, aux_op in aux_ops.items(): commutes = FermionicTransformation._check_commutes(symmetry_ops, aux_op) if not commutes: - aux_ops[i] = None # Discard since no meaningful measurement can be done + aux_ops[name] = None # Discard since no meaningful measurement can be done if self._z2symmetry_reduction == 'auto': hf_state = HartreeFock(num_orbitals=self._molecule_info['num_orbitals'], @@ -379,10 +385,12 @@ def _process_z2symmetry_reduction(self, 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: - z2_aux_ops.append(z2_symmetries.taper(aux_op).chop(chop_to) if aux_op is not None - else None) + z2_aux_ops = {} + for name, aux_op in aux_ops.items(): + if aux_op is None: + z2_aux_ops[name] = None + else: + z2_aux_ops[name] = z2_symmetries.taper(aux_op).chop(chop_to) return z2_qubit_op, z2_aux_ops, z2_symmetries @@ -443,26 +451,39 @@ def add_context(self, result: FermionicResult) -> None: aux_ops_vals = result.aux_values if aux_ops_vals is not None: # Dipole results if dipole aux ops were present - dipole_idx = 3 - if len(aux_ops_vals) > dipole_idx: - result.reverse_dipole_sign = self._reverse_dipole_sign - dipm = [] - for i in range(dipole_idx, dipole_idx + 3): # Gets X, Y and Z components - dipm.append(aux_ops_vals[i][0].real if aux_ops_vals[i] is not None else None) - result.computed_dipole_moment = cast(DipoleTuple, tuple(dipm)) + dipole_names = ['Dipole ' + axis for axis in ['x', 'y', 'z']] + if all(name in result.aux_values for name in dipole_names): + # extract dipole moment in each axis + dipole_moment = [] + for name in dipole_names: + moment = result.aux_values[name] + if moment is not None: + dipole_moment += [moment.real[0]] + else: + dipole_moment += [None] + + 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) - # The first 3 entries are num particles, total angular momentum and magnetization - result.num_particles = aux_ops_vals[0][0].real \ - if aux_ops_vals[0] is not None else None - result.total_angular_momentum = aux_ops_vals[1][0].real \ - if aux_ops_vals[1] is not None else None - result.magnetization = aux_ops_vals[2][0].real \ - if aux_ops_vals[2] is not None else None + + if 'Number of Particles' in result.aux_values: + result.num_particles = result.aux_values['Number of Particles'][0].real + else: + result.num_particles = None + + if 'S^2' in result.aux_values: + result.total_angular_momentum = result.aux_values['S^2'][0].real + else: + result.total_angular_momentum = None + + if 'Magnetization' in result.aux_values: + result.magnetization = result.aux_values['Magnetization'][0].real + else: + result.magnetization = None @staticmethod def _try_reduce_fermionic_operator(fer_op: FermionicOperator, diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index c00fc7486c..fbd515d461 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -13,7 +13,7 @@ """Base class for transformation to qubit operators for chemistry problems""" from abc import ABC, abstractmethod -from typing import Tuple, List +from typing import Tuple, Dict, Any, Optional from qiskit.aqua.operators.legacy import WeightedPauliOperator from qiskit.chemistry.drivers import BaseDriver @@ -24,8 +24,9 @@ class QubitOperatorTransformation(ABC): """Base class for transformation to qubit operators for chemistry problems""" @abstractmethod - def transform(self, driver: BaseDriver - ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + def transform(self, driver: BaseDriver, + additional_operators: Optional[Dict[str, Any]] = None + ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: """transforms to qubit operators """ raise NotImplementedError diff --git a/qiskit/chemistry/results/state_result.py b/qiskit/chemistry/results/state_result.py index 7f098f247b..afdcff0212 100644 --- a/qiskit/chemistry/results/state_result.py +++ b/qiskit/chemistry/results/state_result.py @@ -12,8 +12,7 @@ """State results module.""" -from typing import Optional -import numpy as np +from typing import Optional, Dict from qiskit.aqua.algorithms import AlgorithmResult @@ -22,12 +21,12 @@ class StateResult(AlgorithmResult): """The state result interface.""" @property - def aux_values(self) -> Optional[np.ndarray]: + def aux_values(self) -> Optional[Dict[str, float]]: """ return aux operator eigen values """ return self.get('aux_values') @aux_values.setter - def aux_values(self, value: np.ndarray) -> None: + def aux_values(self, value: Dict[str, float]) -> None: """ set aux operator eigen values """ self.data['aux_values'] = value From c2cd78b1c222cc69f304390ed12574c7b8411cb6 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Tue, 6 Oct 2020 16:02:44 +0200 Subject: [PATCH 074/197] fix aux_ops=None --- .../minimum_eigen_solvers/minimum_eigen_solver.py | 2 +- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 9 ++++++--- .../mes_ground_state_calculation.py | 5 +++-- 3 files changed, 10 insertions(+), 6 deletions(-) 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..ccb6d7682a 100644 --- a/qiskit/aqua/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py +++ b/qiskit/aqua/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py @@ -52,7 +52,7 @@ 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: diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 3523732767..8a877d6f02 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -210,8 +210,11 @@ def compute_groundstate(self, driver: BaseDriver, 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 and aux_operators: - vqe.compute_minimum_eigenvalue(operator, list(aux_operators.values())) + if aux_operators is not None: + aux_result = vqe.compute_minimum_eigenvalue(operator, list(aux_operators.values())) + aux_values = dict(zip(aux_operators.keys(), aux_result.aux_operator_eigenvalues)) + else: + aux_values = None if threshold_satisfied: finishing_criterion = 'Threshold converged' @@ -226,7 +229,7 @@ def compute_groundstate(self, driver: BaseDriver, result = AdaptVQEResult() result.raw_result = raw_vqe_result result.computed_electronic_energy = raw_vqe_result.eigenvalue.real - result.aux_values = dict(zip(aux_operators.keys(), raw_vqe_result.aux_operator_eigenvalues)) + result.aux_values = aux_values result.num_iterations = iteration result.final_max_gradient = max_grad[0] result.finishing_criterion = finishing_criterion diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 149d934594..702d4efe73 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -71,8 +71,9 @@ def compute_groundstate(self, driver: BaseDriver, Returns: Ground state result TODO """ - if any(not isinstance(op, FermionicOperator) for op in additional_operators.values()): - raise ValueError('The additional operators must be of type FermionicOperator.') + if additional_operators is not None: + if any(not isinstance(op, FermionicOperator) for op in additional_operators.values()): + raise ValueError('The additional operators must be of type FermionicOperator.') # get the operator and auxiliary operators, and transform the provided auxiliary operators # note that ``aux_operators`` contains not only the transformed ``aux_operators`` passed From 9070c6e363cdfe4a5c28965c2c01c3da59b4fa18 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Tue, 6 Oct 2020 19:07:23 +0100 Subject: [PATCH 075/197] lint, style --- test/chemistry/test_core_hamiltonian.py | 1 + .../test_fermionic_transformation.py | 100 +++---- .../test_initial_state_hartree_fock.py | 1 + .../test_initial_state_hartree_fock_gsc.py | 11 +- test/chemistry/test_swaprz.py | 1 + test/chemistry/test_swaprz_gsc.py | 32 +-- test/chemistry/test_uccsd_advanced_gsc.py | 246 ++++++++++-------- test/chemistry/test_uccsd_hartree_fock.py | 1 + test/chemistry/test_uccsd_hartree_fock_gsc.py | 36 +-- 9 files changed, 231 insertions(+), 198 deletions(-) diff --git a/test/chemistry/test_core_hamiltonian.py b/test/chemistry/test_core_hamiltonian.py index 563bb9c133..227ffa3f9c 100644 --- a/test/chemistry/test_core_hamiltonian.py +++ b/test/chemistry/test_core_hamiltonian.py @@ -21,6 +21,7 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType + class TestCoreHamiltonian(QiskitChemistryTestCase): """core/hamiltonian Driver tests.""" diff --git a/test/chemistry/test_fermionic_transformation.py b/test/chemistry/test_fermionic_transformation.py index ac6400da1d..c81bddcde5 100644 --- a/test/chemistry/test_fermionic_transformation.py +++ b/test/chemistry/test_fermionic_transformation.py @@ -12,16 +12,16 @@ """ Test Fermionic Transformation """ -import warnings import unittest from test.chemistry import QiskitChemistryTestCase from qiskit.aqua.operators import WeightedPauliOperator from qiskit.chemistry import QiskitChemistryError -from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.qubit_transformations import FermionicTransformation + class TestFermionicTransformation(QiskitChemistryTestCase): """Fermionic Transformation tests.""" @@ -47,9 +47,10 @@ def _validate_info(self, fermionic_transformation, num_particles=None, 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}) + 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, WeightedPauliOperator)) @@ -59,11 +60,12 @@ def _validate_input_object(self, qubit_op, num_qubits=4, num_paulis=15): 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=[]) + 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) @@ -72,11 +74,12 @@ def test_output(self): 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=[]) + 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) @@ -85,11 +88,12 @@ def test_jordan_wigner(self): 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=[]) + 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) @@ -99,11 +103,12 @@ def test_jordan_wigner_2q(self): 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=[]) + 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) @@ -112,11 +117,12 @@ def test_parity(self): 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=[]) + 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) @@ -125,11 +131,12 @@ def test_bravyi_kitaev(self): 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=[]) + 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) @@ -138,11 +145,12 @@ def test_particle_hole(self): 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=[]) + 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) @@ -153,16 +161,18 @@ 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]) + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[-1]) qubit_op, _ = fermionic_transformation.transform(self.driver) 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) + 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 99c0b25f09..aae7bbaec7 100644 --- a/test/chemistry/test_initial_state_hartree_fock.py +++ b/test/chemistry/test_initial_state_hartree_fock.py @@ -23,6 +23,7 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType + @ddt class TestInitialStateHartreeFock(QiskitChemistryTestCase): """ Initial State HartreeFock tests """ diff --git a/test/chemistry/test_initial_state_hartree_fock_gsc.py b/test/chemistry/test_initial_state_hartree_fock_gsc.py index a03904e38a..893ffee214 100644 --- a/test/chemistry/test_initial_state_hartree_fock_gsc.py +++ b/test/chemistry/test_initial_state_hartree_fock_gsc.py @@ -12,21 +12,19 @@ """ Test Initial State HartreeFock """ -import warnings 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.chemistry import QiskitChemistryError from qiskit.chemistry.drivers import PySCFDriver, UnitsType -from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType -from qiskit.chemistry.core import QubitMappingType, TransformationType -from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry.qubit_transformations import FermionicTransformation + @ddt class TestInitialStateHartreeFock(QiskitChemistryTestCase): """ Initial State HartreeFock tests """ @@ -110,12 +108,13 @@ def test_hf_value(self, mapping): hrfo = HartreeFock(fermionic_transformation._molecule_info['num_orbitals'], fermionic_transformation._molecule_info['num_particles'], mapping.value, - two_qubit_reduction = False) + two_qubit_reduction=False) qc = hrfo.construct_circuit('vector') hf_energy = qubit_op.evaluate_with_statevector(qc)[0].real \ - + fermionic_transformation._nuclear_repulsion_energy + + fermionic_transformation._nuclear_repulsion_energy self.assertAlmostEqual(fermionic_transformation._hf_energy, hf_energy, places=6) + if __name__ == '__main__': unittest.main() diff --git a/test/chemistry/test_swaprz.py b/test/chemistry/test_swaprz.py index 00144ec2ef..6a33b7047c 100644 --- a/test/chemistry/test_swaprz.py +++ b/test/chemistry/test_swaprz.py @@ -25,6 +25,7 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.core import Hamiltonian, QubitMappingType + class TestExcitationPreserving(QiskitChemistryTestCase): """The ExcitationPresering wavefunction was design to preserve the excitation of the system. diff --git a/test/chemistry/test_swaprz_gsc.py b/test/chemistry/test_swaprz_gsc.py index 599ff9f4a3..414603e335 100644 --- a/test/chemistry/test_swaprz_gsc.py +++ b/test/chemistry/test_swaprz_gsc.py @@ -12,20 +12,20 @@ """Test of ExcitationPreserving from the circuit library.""" -import warnings 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.ground_state_calculation import MinimumEigensolverGroundStateCalculation -from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.circuit.library import ExcitationPreserving + class TestExcitationPreserving(QiskitChemistryTestCase): """The ExcitationPresering wavefunction was design to preserve the excitation of the system. @@ -44,30 +44,32 @@ def setUp(self): def test_excitation_preserving(self): """Test the excitation preserving wavefunction on a chemistry example.""" - self.driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) + driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) fermionic_transformation = FermionicTransformation(qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=False) - qubit_op, _ = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(driver) optimizer = SLSQP(maxiter=100) - 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) + 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) - 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)) + 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 = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.compute_groundstate(driver) self.assertAlmostEqual(result.energy, self.reference_energy, places=4) + if __name__ == '__main__': unittest.main() diff --git a/test/chemistry/test_uccsd_advanced_gsc.py b/test/chemistry/test_uccsd_advanced_gsc.py index 4d0fb5f997..345a9149b1 100644 --- a/test/chemistry/test_uccsd_advanced_gsc.py +++ b/test/chemistry/test_uccsd_advanced_gsc.py @@ -24,11 +24,11 @@ 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 QubitMappingType, TransformationType from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry.qubit_transformations import FermionicTransformation + # pylint: disable=invalid-name @@ -45,11 +45,12 @@ def setUp(self): spin=0, basis='631g') - self.fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=[]) + 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) @@ -89,25 +90,28 @@ def test_uccsd_hf_qpUCCD(self): optimizer = SLSQP(maxiter=100) - 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'))) + 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 = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) @@ -119,25 +123,28 @@ def test_uccsd_hf_qUCCD0(self): """ singlet uccd test """ optimizer = SLSQP(maxiter=100) - 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' - ) + 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'))) + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) @@ -150,25 +157,28 @@ def test_uccsd_hf_qUCCD0full(self): optimizer = SLSQP(maxiter=100) - 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' - ) + 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'))) + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) @@ -179,13 +189,14 @@ def test_uccsd_hf_qUCCD0full(self): 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' - ) + 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) @@ -193,27 +204,30 @@ def test_uccsd_hf_qUCCSD(self): optimizer = SLSQP(maxiter=100) # initial state - 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=qubit_op.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=qubit_op.z2_symmetries, - shallow_circuit_concat=False, - method_doubles='ucc', - excitation_type='sd', - skip_commute_test=True) + 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=qubit_op.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=qubit_op.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'))) + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) @@ -225,25 +239,27 @@ def test_uccsd_hf_excitations(self): """ uccsd tapering test using all double excitations """ # initial state - 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.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.the_tapered_op.z2_symmetries.sq_list) # check singlet excitations - 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.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.the_tapered_op.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( @@ -251,18 +267,19 @@ def test_uccsd_hf_excitations(self): self.assertEqual(res, True) # check grouped singlet excitations - 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.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.the_tapered_op.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( @@ -347,5 +364,6 @@ def group_excitation_lists_comparator(glist1, glist2): 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 f0fd8c218a..8f0c167a7c 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -29,6 +29,7 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.core import Hamiltonian, QubitMappingType + @ddt class TestUCCSDHartreeFock(QiskitChemistryTestCase): """Test for these aqua extensions.""" diff --git a/test/chemistry/test_uccsd_hartree_fock_gsc.py b/test/chemistry/test_uccsd_hartree_fock_gsc.py index 628d0d23ad..5d2ac09a1b 100644 --- a/test/chemistry/test_uccsd_hartree_fock_gsc.py +++ b/test/chemistry/test_uccsd_hartree_fock_gsc.py @@ -12,13 +12,9 @@ """ Test of UCCSD and HartreeFock Aqua extensions """ -import warnings import unittest 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 @@ -26,11 +22,12 @@ 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.ground_state_calculation import MinimumEigensolverGroundStateCalculation -from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry.qubit_transformations import FermionicTransformation + class TestUCCSDHartreeFock(QiskitChemistryTestCase): """Test for these aqua extensions.""" @@ -49,21 +46,23 @@ def setUp(self): self.fermionic_transformation = fermionic_transformation self.optimizer = SLSQP(maxiter=100) - 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) + 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') - solver = VQE(var_form=self.var_form , optimizer=self.optimizer , - quantum_instance=QuantumInstance(backend = backend)) + solver = VQE(var_form=self.var_form, optimizer=self.optimizer, + quantum_instance=QuantumInstance(backend=backend)) gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) @@ -116,7 +115,7 @@ def test_uccsd_hf_aer_qasm(self): backend = Aer.get_backend('qasm_simulator') optimizer = SPSA(maxiter=200, last_avg=5) solver = VQE(var_form=self.var_form, optimizer=optimizer, - expectation = PauliExpectation(), + expectation=PauliExpectation(), quantum_instance=QuantumInstance(backend=backend, seed_simulator=aqua_globals.random_seed, seed_transpiler=aqua_globals.random_seed)) @@ -137,7 +136,7 @@ def test_uccsd_hf_aer_qasm_snapshot(self): return backend = Aer.get_backend('qasm_simulator') optimizer = SPSA(maxiter=200, last_avg=5) - solver = VQE(var_form=self.var_form, optimizer=self.optimizer, + solver = VQE(var_form=self.var_form, optimizer=optimizer, expectation=AerPauliExpectation(), quantum_instance=QuantumInstance(backend=backend)) @@ -146,5 +145,6 @@ def test_uccsd_hf_aer_qasm_snapshot(self): result = gsc.compute_groundstate(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) + if __name__ == '__main__': unittest.main() From 699751b9c8350089c2897f10aea9cdd02d833507 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 6 Oct 2020 20:22:29 +0200 Subject: [PATCH 076/197] more test --- .../test_core_hamiltonian_orb_reduce.py | 3 - .../test_core_hamiltonian_orb_reduce_gsc.py | 146 +++++++++++ .../test_core_hamiltonian_symmetries.py | 3 - .../test_core_hamiltonian_symmetries_gsc.py | 246 ++++++++++++++++++ test/chemistry/test_driver_methods.py | 3 - test/chemistry/test_driver_methods_gsc.py | 65 +++++ test/chemistry/test_symmetries.py | 5 +- test/chemistry/test_symmetries_gsc.py | 108 ++++++++ test/chemistry/test_uccsd_advanced_gsc.py | 15 +- 9 files changed, 575 insertions(+), 19 deletions(-) create mode 100644 test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py create mode 100644 test/chemistry/test_core_hamiltonian_symmetries_gsc.py create mode 100644 test/chemistry/test_driver_methods_gsc.py create mode 100644 test/chemistry/test_symmetries_gsc.py diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce.py b/test/chemistry/test_core_hamiltonian_orb_reduce.py index 896612e93f..75c1632b43 100644 --- a/test/chemistry/test_core_hamiltonian_orb_reduce.py +++ b/test/chemistry/test_core_hamiltonian_orb_reduce.py @@ -21,9 +21,6 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry import QiskitChemistryError -# TODO Ground state interface PR - - class TestCoreHamiltonianOrbReduce(QiskitChemistryTestCase): """core/hamiltonian Driver tests.""" diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py new file mode 100644 index 0000000000..7fb718bfeb --- /dev/null +++ b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py @@ -0,0 +1,146 @@ +# 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 WeightedPauliOperator +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation + +class TestCoreHamiltonianOrbReduce(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, WeightedPauliOperator)) + self.assertIsNotNone(qubit_op) + self.assertEqual(qubit_op.num_qubits, num_qubits) + self.assertEqual(len(qubit_op.to_dict()['paulis']), 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() \ No newline at end of file diff --git a/test/chemistry/test_core_hamiltonian_symmetries.py b/test/chemistry/test_core_hamiltonian_symmetries.py index a043dda340..4c14e55e8e 100644 --- a/test/chemistry/test_core_hamiltonian_symmetries.py +++ b/test/chemistry/test_core_hamiltonian_symmetries.py @@ -26,9 +26,6 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry import QiskitChemistryError -# TODO Ground state interface PR - - class TestCoreHamiltonianSymmetries(QiskitChemistryTestCase): """ Core hamiltonian Driver symmetry tests. """ diff --git a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py new file mode 100644 index 0000000000..45ba7ad863 --- /dev/null +++ b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py @@ -0,0 +1,246 @@ +# 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 Core Hamiltonian Symmetry Reduction """ + +import warnings +import unittest +from test.chemistry import QiskitChemistryTestCase +import numpy as np + +from qiskit import BasicAer +from qiskit.aqua.algorithms import NumPyMinimumEigensolver, VQE +from qiskit.aqua.components.optimizers import SLSQP +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, TransformationType, QubitMappingType +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation + + +class TestCoreHamiltonianSymmetries(QiskitChemistryTestCase): + """ Core hamiltonian Driver symmetry 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_result(self, result, symm=True): + self.assertAlmostEqual(result.energy, -7.882324378883, places=3) + ref_dipole = (0.0, 0.0, -1.81741795) + if not symm: + np.testing.assert_almost_equal(result.dipole_moment, ref_dipole, decimal=2) + else: + self.assertIsNone(result.dipole_moment[0]) + self.assertIsNone(result.dipole_moment[1]) + self.assertAlmostEqual(result.dipole_moment[2], ref_dipole[2], places=2) + + def test_no_symmetry(self): + """ No symmetry reduction """ + fermionic_transformation = FermionicTransformation\ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=None) + + qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 12) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result, False) + + def test_auto_symmetry(self): + """ Auto symmetry reduction """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation \ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction='auto') + qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 8) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) + + def test_given_symmetry(self): + """ Supplied symmetry reduction """ + warnings.filterwarnings('ignore', category=DeprecationWarning) + fermionic_transformation = FermionicTransformation\ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 1, 1, 1]) + qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 8) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) + + def test_given_symmetry_fail_len(self): + """ Supplied symmetry reduction invalid len """ + with self.assertRaises(QiskitChemistryError): + fermionic_transformation = FermionicTransformation\ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 1, 1]) + + _, _ = fermionic_transformation.transform(self.driver) + + + def test_given_symmetry_fail_values(self): + """ Supplied symmetry reduction invalid values """ + with self.assertRaises(QiskitChemistryError): + fermionic_transformation = FermionicTransformation\ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 0, 1, 1]) + + _, _ = fermionic_transformation.transform(self.driver) + + def test_auto_symmetry_freeze_core(self): + """ Auto symmetry reduction, with freeze core """ + fermionic_transformation = FermionicTransformation \ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) + + def test_auto_freeze_core_parity(self): + """ Auto symmetry reduction, with freeze core and parity mapping """ + + fermionic_transformation = FermionicTransformation\ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.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 """ + fermionic_transformation = FermionicTransformation\ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) + + def test_auto_ph_freeze_core_parity_2(self): + """ Auto symmetry reduction, with freeze core, parity and two q reduction """ + fermionic_transformation = FermionicTransformation \ + (transformation=TransformationType.PARTICLE_HOLE, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) + + def test_vqe_auto_symmetry_freeze_core(self): + """ Auto symmetry reduction, with freeze core using VQE """ + fermionic_transformation = FermionicTransformation\ + (transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + num_orbitals = fermionic_transformation._molecule_info['num_orbitals'] + num_particles = fermionic_transformation._molecule_info['num_particles'] + qubit_mapping = 'jordan_wigner' + two_qubit_reduction = fermionic_transformation._two_qubit_reduction + z2_symmetries = qubit_op.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) + + solver = VQE(var_form=var_form, optimizer=SLSQP(maxiter=500), + quantum_instance = BasicAer.get_backend('statevector_simulator')) + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_driver_methods.py b/test/chemistry/test_driver_methods.py index d6f021314a..f71c7c6235 100644 --- a/test/chemistry/test_driver_methods.py +++ b/test/chemistry/test_driver_methods.py @@ -19,9 +19,6 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.aqua.algorithms import NumPyMinimumEigensolver -# TODO Ground state interface PR - - class TestDriverMethods(QiskitChemistryTestCase): """Common driver tests. For H2 @ 0.735, sto3g""" diff --git a/test/chemistry/test_driver_methods_gsc.py b/test/chemistry/test_driver_methods_gsc.py new file mode 100644 index 0000000000..2b6b441454 --- /dev/null +++ b/test/chemistry/test_driver_methods_gsc.py @@ -0,0 +1,65 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 Driver Methods """ + +import warnings +import unittest + +from test.chemistry import QiskitChemistryTestCase +from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType +from qiskit.aqua.algorithms import NumPyMinimumEigensolver + +class TestDriverMethods(QiskitChemistryTestCase): + """Common driver tests. For H2 @ 0.735, sto3g""" + + def setUp(self): + super().setUp() + self.lih = 'LI 0 0 0; H 0 0 1.6' + self.o_h = 'O 0 0 0; H 0 0 0.9697' + self.ref_energies = { + 'lih': -7.882, + 'oh': -74.387 + } + self.ref_dipoles = { + 'lih': 1.818, + 'oh': 0.4615 + } + + @staticmethod + def _run_driver(driver, transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, + freeze_core=True): + + fermionic_transformation = FermionicTransformation\ + (transformation=transformation, + qubit_mapping=qubit_mapping, + two_qubit_reduction=two_qubit_reduction, + freeze_core=freeze_core, + orbital_reduction=[]) + + solver = NumPyMinimumEigensolver() + + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + return result + + def _assert_energy(self, result, mol): + self.assertAlmostEqual(self.ref_energies[mol], result.energy, places=3) + + 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_symmetries.py b/test/chemistry/test_symmetries.py index e56a862d1b..10535cad3e 100644 --- a/test/chemistry/test_symmetries.py +++ b/test/chemistry/test_symmetries.py @@ -27,9 +27,6 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock -# TODO Ground state interface PR - - class TestSymmetries(QiskitChemistryTestCase): """Test for symmetry processing.""" @@ -78,7 +75,7 @@ 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] + the_tapered_op = tapered_ops[smallest_idxtest_symmetries.py] optimizer = SLSQP(maxiter=1000) diff --git a/test/chemistry/test_symmetries_gsc.py b/test/chemistry/test_symmetries_gsc.py new file mode 100644 index 0000000000..8e6f6794bf --- /dev/null +++ b/test/chemistry/test_symmetries_gsc.py @@ -0,0 +1,108 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 Symmetry UCCSD processing """ + +import unittest + +from test.chemistry import QiskitChemistryTestCase +from qiskit import BasicAer +from qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import VQE +from qiskit.aqua.components.optimizers import SLSQP +from qiskit.chemistry import QiskitChemistryError +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.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation + + + +class TestSymmetries(QiskitChemistryTestCase): + """Test for symmetry processing.""" + + def setUp(self): + super().setUp() + try: + 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.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 + + def test_symmetries(self): + """ symmetries test """ + labels = [symm.to_label() for symm in self.z2_symmetries.symmetries] + self.assertSequenceEqual(labels, ['ZIZIZIZI', 'ZZIIZZII']) + + def test_sq_paulis(self): + """ sq paulis test """ + labels = [sq.to_label() for sq in self.z2_symmetries.sq_paulis] + self.assertSequenceEqual(labels, ['IIIIIIXI', 'IIIIIXII']) + + def test_cliffords(self): + """ clifford test """ + self.assertEqual(2, len(self.z2_symmetries.cliffords)) + + def test_sq_list(self): + """ sq list test """ + self.assertSequenceEqual(self.z2_symmetries.sq_list, [1, 2]) + + def test_tapered_op(self): + """ tapered op test """ + + 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.qubit_op.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.qubit_op.z2_symmetries) + + solver = VQE(var_form=var_form, optimizer=optimizer, + quantum_instance=QuantumInstance(backend=BasicAer.get_backend('statevector_simulator'))) + + gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + + result = gsc.compute_groundstate(self.driver) + + self.assertAlmostEqual(result.energy, self.reference_energy, places=6) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_uccsd_advanced_gsc.py b/test/chemistry/test_uccsd_advanced_gsc.py index 4d0fb5f997..e2f0013028 100644 --- a/test/chemistry/test_uccsd_advanced_gsc.py +++ b/test/chemistry/test_uccsd_advanced_gsc.py @@ -45,15 +45,18 @@ def setUp(self): spin=0, basis='631g') - self.fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=[]) + 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) - z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) + z2_symmetries = fermionic_transformation.\ + _molecule_info.pop('z2_symmetries') + tapered_ops = z2_symmetries.taper(self.qubit_op) smallest_eig_value = 99999999999999 smallest_idx = -1 From d72be75710724069fb6c36935018d4eb8b3df355 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 6 Oct 2020 21:38:58 +0200 Subject: [PATCH 077/197] fixes in test --- .../fermionic_transformation.py | 4 +- .../test_core_hamiltonian_orb_reduce_gsc.py | 5 +- .../test_core_hamiltonian_symmetries_gsc.py | 21 ++++---- test/chemistry/test_driver_methods_gsc.py | 5 +- test/chemistry/test_symmetries.py | 2 +- test/chemistry/test_symmetries_gsc.py | 48 ++++++++++--------- test/chemistry/test_uccsd_advanced_gsc.py | 11 ++--- test/chemistry/test_uccsd_hartree_fock_gsc.py | 2 +- 8 files changed, 47 insertions(+), 51 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index a64edbbcde..5140fbcbde 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -50,7 +50,7 @@ class FermionicTransformation(QubitOperatorTransformation): def __init__(self, transformation: TransformationType = TransformationType.FULL, - qubit_mapping: QubitMappingType = QubitMappingType.PARITY, + qubit_mapping: str = QubitMappingType.PARITY, two_qubit_reduction: bool = True, freeze_core: bool = False, orbital_reduction: Optional[List[int]] = None, @@ -58,7 +58,7 @@ def __init__(self, """ Args: transformation: full or particle_hole - qubit_mapping: jordan_wigner, parity or bravyi_kitaev + 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 diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py index 7fb718bfeb..3dfd2496dc 100644 --- a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py +++ b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py @@ -137,10 +137,11 @@ def test_freeze_core_all_reduction_ph(self): 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_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() \ No newline at end of file + unittest.main() diff --git a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py index 45ba7ad863..b65913db90 100644 --- a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py +++ b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py @@ -12,7 +12,6 @@ """ Test Core Hamiltonian Symmetry Reduction """ -import warnings import unittest from test.chemistry import QiskitChemistryTestCase import numpy as np @@ -23,8 +22,6 @@ 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, TransformationType, QubitMappingType -from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry.qubit_transformations import FermionicTransformation @@ -65,7 +62,7 @@ def test_no_symmetry(self): orbital_reduction=None, z2symmetry_reduction=None) - qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 12) solver = NumPyMinimumEigensolver() gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) @@ -74,7 +71,6 @@ def test_no_symmetry(self): def test_auto_symmetry(self): """ Auto symmetry reduction """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation \ (transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, @@ -82,7 +78,7 @@ def test_auto_symmetry(self): freeze_core=False, orbital_reduction=None, z2symmetry_reduction='auto') - qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 8) solver = NumPyMinimumEigensolver() gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) @@ -92,7 +88,6 @@ def test_auto_symmetry(self): def test_given_symmetry(self): """ Supplied symmetry reduction """ - warnings.filterwarnings('ignore', category=DeprecationWarning) fermionic_transformation = FermionicTransformation\ (transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, @@ -100,7 +95,7 @@ def test_given_symmetry(self): freeze_core=False, orbital_reduction=None, z2symmetry_reduction=[1, 1, 1, 1]) - qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 8) solver = NumPyMinimumEigensolver() gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) @@ -145,7 +140,7 @@ def test_auto_symmetry_freeze_core(self): orbital_reduction=None, z2symmetry_reduction='auto') - qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) solver = NumPyMinimumEigensolver() gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) @@ -164,7 +159,7 @@ def test_auto_freeze_core_parity(self): orbital_reduction=None, z2symmetry_reduction='auto') - qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) solver = NumPyMinimumEigensolver() gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) @@ -182,7 +177,7 @@ def test_auto_freeze_core_parity_2(self): orbital_reduction=None, z2symmetry_reduction='auto') - qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) solver = NumPyMinimumEigensolver() gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) @@ -200,7 +195,7 @@ def test_auto_ph_freeze_core_parity_2(self): orbital_reduction=None, z2symmetry_reduction='auto') - qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) solver = NumPyMinimumEigensolver() gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) @@ -218,7 +213,7 @@ def test_vqe_auto_symmetry_freeze_core(self): orbital_reduction=None, z2symmetry_reduction='auto') - qubit_op, aux_ops = fermionic_transformation.transform(self.driver) + qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) num_orbitals = fermionic_transformation._molecule_info['num_orbitals'] num_particles = fermionic_transformation._molecule_info['num_particles'] diff --git a/test/chemistry/test_driver_methods_gsc.py b/test/chemistry/test_driver_methods_gsc.py index 2b6b441454..b05de41d43 100644 --- a/test/chemistry/test_driver_methods_gsc.py +++ b/test/chemistry/test_driver_methods_gsc.py @@ -12,11 +12,10 @@ """ Test Driver Methods """ -import warnings import unittest from test.chemistry import QiskitChemistryTestCase -from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType +from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.aqua.algorithms import NumPyMinimumEigensolver class TestDriverMethods(QiskitChemistryTestCase): @@ -51,7 +50,7 @@ def _run_driver(driver, transformation=TransformationType.FULL, gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.compute_groundstate(driver) return result def _assert_energy(self, result, mol): diff --git a/test/chemistry/test_symmetries.py b/test/chemistry/test_symmetries.py index 10535cad3e..4a90733b20 100644 --- a/test/chemistry/test_symmetries.py +++ b/test/chemistry/test_symmetries.py @@ -75,7 +75,7 @@ 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_idxtest_symmetries.py] + the_tapered_op = tapered_ops[smallest_idx] optimizer = SLSQP(maxiter=1000) diff --git a/test/chemistry/test_symmetries_gsc.py b/test/chemistry/test_symmetries_gsc.py index 8e6f6794bf..94939b3a70 100644 --- a/test/chemistry/test_symmetries_gsc.py +++ b/test/chemistry/test_symmetries_gsc.py @@ -28,7 +28,6 @@ from qiskit.chemistry.qubit_transformations import FermionicTransformation - class TestSymmetries(QiskitChemistryTestCase): """Test for symmetry processing.""" @@ -36,19 +35,20 @@ def setUp(self): super().setUp() try: self.driver = PySCFDriver(atom='Li .0 .0 .0; H .0 .0 1.6', - unit=UnitsType.ANGSTROM, - charge=0, - spin=0, - basis='sto3g') + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='sto3g') except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') - self.fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=[], - z2symmetry_reduction='auto') + 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) @@ -78,24 +78,28 @@ def test_tapered_op(self): """ tapered op test """ 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.qubit_op.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'], + 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.qubit_op.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, + two_qubit_reduction= + self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=self.qubit_op.z2_symmetries) solver = VQE(var_form=var_form, optimizer=optimizer, - quantum_instance=QuantumInstance(backend=BasicAer.get_backend('statevector_simulator'))) + quantum_instance=QuantumInstance \ + (backend=BasicAer.get_backend('statevector_simulator'))) gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) diff --git a/test/chemistry/test_uccsd_advanced_gsc.py b/test/chemistry/test_uccsd_advanced_gsc.py index 6e80c5343b..69bc8a7783 100644 --- a/test/chemistry/test_uccsd_advanced_gsc.py +++ b/test/chemistry/test_uccsd_advanced_gsc.py @@ -44,20 +44,17 @@ def setUp(self): charge=0, spin=0, basis='631g') - - self.fermionic_transformation = FermionicTransformation( - transformation=TransformationType.FULL, + 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) - z2_symmetries = fermionic_transformation.\ - _molecule_info.pop('z2_symmetries') - + 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): diff --git a/test/chemistry/test_uccsd_hartree_fock_gsc.py b/test/chemistry/test_uccsd_hartree_fock_gsc.py index 5d2ac09a1b..1f42acf202 100644 --- a/test/chemistry/test_uccsd_hartree_fock_gsc.py +++ b/test/chemistry/test_uccsd_hartree_fock_gsc.py @@ -143,7 +143,7 @@ def test_uccsd_hf_aer_qasm_snapshot(self): gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) result = gsc.compute_groundstate(self.driver) - self.assertAlmostEqual(result.energy, self.reference_energy, places=6) + self.assertAlmostEqual(result.energy, self.reference_energy, places=3) if __name__ == '__main__': From ac6db57def4dd797db1e67faed18f3a2215de33e Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Tue, 6 Oct 2020 16:08:03 -0400 Subject: [PATCH 078/197] fix style --- .../test_core_hamiltonian_orb_reduce.py | 1 + .../test_core_hamiltonian_orb_reduce_gsc.py | 86 +++++----- .../test_core_hamiltonian_symmetries.py | 1 + .../test_core_hamiltonian_symmetries_gsc.py | 151 +++++++++--------- test/chemistry/test_driver_methods.py | 1 + test/chemistry/test_driver_methods_gsc.py | 16 +- test/chemistry/test_symmetries.py | 1 + test/chemistry/test_symmetries_gsc.py | 25 ++- test/chemistry/test_uccsd_advanced_gsc.py | 12 +- 9 files changed, 151 insertions(+), 143 deletions(-) diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce.py b/test/chemistry/test_core_hamiltonian_orb_reduce.py index 75c1632b43..8573c43d61 100644 --- a/test/chemistry/test_core_hamiltonian_orb_reduce.py +++ b/test/chemistry/test_core_hamiltonian_orb_reduce.py @@ -21,6 +21,7 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry import QiskitChemistryError + class TestCoreHamiltonianOrbReduce(QiskitChemistryTestCase): """core/hamiltonian Driver tests.""" diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py index 3dfd2496dc..0eb4ec3ff3 100644 --- a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py +++ b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py @@ -21,6 +21,7 @@ from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry.qubit_transformations import FermionicTransformation + class TestCoreHamiltonianOrbReduce(QiskitChemistryTestCase): """core/hamiltonian Driver tests.""" @@ -28,10 +29,10 @@ 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') + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='sto3g') except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') @@ -58,12 +59,12 @@ def _validate_input_object(self, qubit_op, num_qubits=12, num_paulis=631): 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=[]) + 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) @@ -72,12 +73,12 @@ def test_output(self): 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=[]) + 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) @@ -86,12 +87,12 @@ def test_parity(self): 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=[]) + 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) @@ -100,12 +101,12 @@ def test_freeze_core(self): 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]) + 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) @@ -114,12 +115,12 @@ def test_freeze_core_orb_reduction(self): 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]) + 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) @@ -129,19 +130,20 @@ def test_freeze_core_all_reduction(self): 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]) + 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_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_core_hamiltonian_symmetries.py b/test/chemistry/test_core_hamiltonian_symmetries.py index 4c14e55e8e..ef56f1af30 100644 --- a/test/chemistry/test_core_hamiltonian_symmetries.py +++ b/test/chemistry/test_core_hamiltonian_symmetries.py @@ -26,6 +26,7 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry import QiskitChemistryError + class TestCoreHamiltonianSymmetries(QiskitChemistryTestCase): """ Core hamiltonian Driver symmetry tests. """ diff --git a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py index b65913db90..5294e18665 100644 --- a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py +++ b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py @@ -35,10 +35,10 @@ 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') + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='sto3g') except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') @@ -54,13 +54,13 @@ def _validate_result(self, result, symm=True): def test_no_symmetry(self): """ No symmetry reduction """ - fermionic_transformation = FermionicTransformation\ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=None) + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=None) qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 12) @@ -71,13 +71,13 @@ def test_no_symmetry(self): def test_auto_symmetry(self): """ Auto symmetry reduction """ - fermionic_transformation = FermionicTransformation \ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction='auto') + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction='auto') qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 8) solver = NumPyMinimumEigensolver() @@ -88,13 +88,13 @@ def test_auto_symmetry(self): def test_given_symmetry(self): """ Supplied symmetry reduction """ - fermionic_transformation = FermionicTransformation\ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 1, 1, 1]) + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 1, 1, 1]) qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 8) solver = NumPyMinimumEigensolver() @@ -106,39 +106,38 @@ def test_given_symmetry(self): def test_given_symmetry_fail_len(self): """ Supplied symmetry reduction invalid len """ with self.assertRaises(QiskitChemistryError): - fermionic_transformation = FermionicTransformation\ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 1, 1]) + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 1, 1]) _, _ = fermionic_transformation.transform(self.driver) - def test_given_symmetry_fail_values(self): """ Supplied symmetry reduction invalid values """ with self.assertRaises(QiskitChemistryError): - fermionic_transformation = FermionicTransformation\ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 0, 1, 1]) + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 0, 1, 1]) _, _ = fermionic_transformation.transform(self.driver) def test_auto_symmetry_freeze_core(self): """ Auto symmetry reduction, with freeze core """ - fermionic_transformation = FermionicTransformation \ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) @@ -151,13 +150,13 @@ def test_auto_symmetry_freeze_core(self): def test_auto_freeze_core_parity(self): """ Auto symmetry reduction, with freeze core and parity mapping """ - fermionic_transformation = FermionicTransformation\ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) @@ -169,13 +168,13 @@ def test_auto_freeze_core_parity(self): def test_auto_freeze_core_parity_2(self): """ Auto symmetry reduction, with freeze core, parity and two q reduction """ - fermionic_transformation = FermionicTransformation\ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) @@ -187,13 +186,13 @@ def test_auto_freeze_core_parity_2(self): def test_auto_ph_freeze_core_parity_2(self): """ Auto symmetry reduction, with freeze core, parity and two q reduction """ - fermionic_transformation = FermionicTransformation \ - (transformation=TransformationType.PARTICLE_HOLE, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.PARTICLE_HOLE, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) @@ -205,13 +204,13 @@ def test_auto_ph_freeze_core_parity_2(self): def test_vqe_auto_symmetry_freeze_core(self): """ Auto symmetry reduction, with freeze core using VQE """ - fermionic_transformation = FermionicTransformation\ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) @@ -230,7 +229,7 @@ def test_vqe_auto_symmetry_freeze_core(self): z2_symmetries=z2_symmetries) solver = VQE(var_form=var_form, optimizer=SLSQP(maxiter=500), - quantum_instance = BasicAer.get_backend('statevector_simulator')) + quantum_instance=BasicAer.get_backend('statevector_simulator')) gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) result = gsc.compute_groundstate(self.driver) self._validate_result(result) diff --git a/test/chemistry/test_driver_methods.py b/test/chemistry/test_driver_methods.py index f71c7c6235..2ff3e531a8 100644 --- a/test/chemistry/test_driver_methods.py +++ b/test/chemistry/test_driver_methods.py @@ -19,6 +19,7 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.aqua.algorithms import NumPyMinimumEigensolver + class TestDriverMethods(QiskitChemistryTestCase): """Common driver tests. For H2 @ 0.735, sto3g""" diff --git a/test/chemistry/test_driver_methods_gsc.py b/test/chemistry/test_driver_methods_gsc.py index b05de41d43..51fafd26e1 100644 --- a/test/chemistry/test_driver_methods_gsc.py +++ b/test/chemistry/test_driver_methods_gsc.py @@ -16,8 +16,11 @@ from test.chemistry import QiskitChemistryTestCase from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation from qiskit.aqua.algorithms import NumPyMinimumEigensolver + class TestDriverMethods(QiskitChemistryTestCase): """Common driver tests. For H2 @ 0.735, sto3g""" @@ -39,12 +42,12 @@ def _run_driver(driver, transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=True): - fermionic_transformation = FermionicTransformation\ - (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() @@ -60,5 +63,6 @@ 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_symmetries.py b/test/chemistry/test_symmetries.py index 4a90733b20..88d16b6dc2 100644 --- a/test/chemistry/test_symmetries.py +++ b/test/chemistry/test_symmetries.py @@ -27,6 +27,7 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock + class TestSymmetries(QiskitChemistryTestCase): """Test for symmetry processing.""" diff --git a/test/chemistry/test_symmetries_gsc.py b/test/chemistry/test_symmetries_gsc.py index 94939b3a70..0f71fe1ed7 100644 --- a/test/chemistry/test_symmetries_gsc.py +++ b/test/chemistry/test_symmetries_gsc.py @@ -85,21 +85,20 @@ def test_tapered_op(self): num_particles=self.fermionic_transformation._molecule_info['num_particles'], sq_list=self.qubit_op.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.qubit_op.z2_symmetries) + 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.qubit_op.z2_symmetries) solver = VQE(var_form=var_form, optimizer=optimizer, - quantum_instance=QuantumInstance \ - (backend=BasicAer.get_backend('statevector_simulator'))) + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) diff --git a/test/chemistry/test_uccsd_advanced_gsc.py b/test/chemistry/test_uccsd_advanced_gsc.py index 69bc8a7783..f849ed6461 100644 --- a/test/chemistry/test_uccsd_advanced_gsc.py +++ b/test/chemistry/test_uccsd_advanced_gsc.py @@ -44,12 +44,12 @@ def setUp(self): charge=0, spin=0, basis='631g') - self.fermionic_transformation = FermionicTransformation\ - (transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=[]) + 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) z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) From 4bc64049d5bc0243d4f16e2695170995045ff9a8 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Tue, 6 Oct 2020 16:22:48 -0400 Subject: [PATCH 079/197] fix mypy --- .../qubit_transformations/fermionic_transformation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 5140fbcbde..2e13f9b6b7 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -50,7 +50,7 @@ class FermionicTransformation(QubitOperatorTransformation): def __init__(self, transformation: TransformationType = TransformationType.FULL, - qubit_mapping: str = QubitMappingType.PARITY, + qubit_mapping: QubitMappingType = QubitMappingType.PARITY, two_qubit_reduction: bool = True, freeze_core: bool = False, orbital_reduction: Optional[List[int]] = None, @@ -359,7 +359,7 @@ def _process_z2symmetry_reduction(self, if self._z2symmetry_reduction == 'auto': hf_state = HartreeFock(num_orbitals=self._molecule_info['num_orbitals'], - qubit_mapping=self._qubit_mapping, + qubit_mapping=self._qubit_mapping.value, two_qubit_reduction=self._two_qubit_reduction, num_particles=self._molecule_info['num_particles']) z2_symmetries = FermionicTransformation._pick_sector(z2_symmetries, hf_state.bitstr) From f5f19141aab3f2065b7d7c86e3135a4fb395e28d Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Tue, 6 Oct 2020 16:57:15 -0400 Subject: [PATCH 080/197] fix mypy --- .../qubit_transformations/fermionic_transformation.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 2e13f9b6b7..4c89e7888e 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -81,11 +81,10 @@ def __init__(self, QiskitChemistryError: Invalid symmetry reduction """ transformation = transformation.value - qubit_mapping = qubit_mapping.value orbital_reduction = orbital_reduction if orbital_reduction is not None else [] super().__init__() self._transformation = transformation - self._qubit_mapping = qubit_mapping + self._qubit_mapping = qubit_mapping.value self._two_qubit_reduction = two_qubit_reduction self._freeze_core = freeze_core self._orbital_reduction = orbital_reduction @@ -359,7 +358,7 @@ def _process_z2symmetry_reduction(self, if self._z2symmetry_reduction == 'auto': hf_state = HartreeFock(num_orbitals=self._molecule_info['num_orbitals'], - qubit_mapping=self._qubit_mapping.value, + 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) @@ -491,7 +490,7 @@ def _try_reduce_fermionic_operator(fer_op: FermionicOperator, @staticmethod def _map_fermionic_operator_to_qubit(fer_op: FermionicOperator, - qubit_mapping: QubitMappingType, + qubit_mapping: str, num_particles: List[int], two_qubit_reduction: bool) -> WeightedPauliOperator: """ From 74ce02556a79a0da145fc55b37c76099f864be2f Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 6 Oct 2020 23:00:14 +0200 Subject: [PATCH 081/197] fixes in fermionic trafo --- .../chemistry/qubit_transformations/fermionic_transformation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 2e13f9b6b7..4694d864b7 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -359,7 +359,7 @@ def _process_z2symmetry_reduction(self, if self._z2symmetry_reduction == 'auto': hf_state = HartreeFock(num_orbitals=self._molecule_info['num_orbitals'], - qubit_mapping=self._qubit_mapping.value, + 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) From b72756245b4fb1123ede415beb2d066c64f30cf4 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 7 Oct 2020 10:55:03 +0200 Subject: [PATCH 082/197] remove editor created redundant file --- test/chemistry/#test_molecule.py# | 84 ------------------------------- 1 file changed, 84 deletions(-) delete mode 100644 test/chemistry/#test_molecule.py# diff --git a/test/chemistry/#test_molecule.py# b/test/chemistry/#test_molecule.py# deleted file mode 100644 index 4ef4eb713b..0000000000 --- a/test/chemistry/#test_molecule.py# +++ /dev/null @@ -1,84 +0,0 @@ -import unittest -from functools import partial - -import numpy as np -from qiskit.aqua.operators.weighted_pauli_operator import WeightedPauliOperator - -from qiskit.chemistry.molecule import Molecule - - -class TestMolecule(unittest.TestCase): - def test_construct(self): - stretch = partial( - Molecule.absolute_stretching, - kwargs={'atom_pair': (1, 0)}) - - m = Molecule(geometry=[['H', [0., 0., 0.]], ['H', [0., 0., 1.]]], - degrees_of_freedom=[stretch], - masses=[1, 1]) - - m = Molecule(geometry=[['H', [0., 0., 0.]], ['H', [0., 0., 1.]]], - degrees_of_freedom=[stretch]) - - def test_stretch(self): - geom = Molecule.absolute_stretching(atom_pair=(1, 0), - perturbation=2, - geometry=[['H', [0., 0., 0.]], [ - 'H', [0., 0., 1.]]] - ) - self.assertListEqual(geom[1][1], [0., 0., 3.]) - geom = Molecule.absolute_stretching(atom_pair=(1, 0), - perturbation=-.1, - geometry=geom - ) - self.assertListEqual(geom[1][1], [0., 0., 3. - .1]) - - def test_bend(self): - geom = Molecule.absolute_bending(atom_trio=(1, 0, 2), - bend=np.pi / 2, - geometry=[['H', [0., 0., 0.]], - ['H', [0., 0., 1.]], - ['Li', [0., 1., -1.]], - ] - ) - self.assertListEqual(geom[1][1], [0., 1., 0.]) - geom = Molecule.absolute_bending(atom_trio=(1, 0, 2), - bend=-np.pi / 4, - geometry=geom - ) - np.testing.assert_array_almost_equal( - geom[1][1], [0., np.sqrt(2) / 2, np.sqrt(2) / 2]) - geom = Molecule.absolute_bending(atom_trio=(2, 0, 1), - bend=-np.pi / 4, - geometry=geom - ) - np.testing.assert_array_almost_equal(geom[2][1], [0., 0., -np.sqrt(2)]) - - # Test linear case - geom = Molecule.absolute_bending(atom_trio=(1, 0, 2), - bend=np.pi / 2, - geometry=[['H', [0., 0., 0.]], - ['H', [0., 0., 1.]], - ['Li', [0., 0., -1.]], - ] - ) - self.assertListEqual(geom[1][1], [1., 0., 0.]) - - def test_get_perturbations(self): - stretch1 = partial(Molecule.absolute_stretching, atom_pair=(1, 0)) - bend = partial(Molecule.absolute_bending, atom_trio=(1, 0, 2)) - stretch2 = partial(Molecule.absolute_stretching, atom_pair=(0, 1)) - - m = Molecule(geometry=[['H', [0., 0., 0.]], - ['H', [0., 0., 1.]], - ['Li', [0., 1., -1.]], - ], - degrees_of_freedom=[stretch1, bend, stretch2], - masses=[1, 1, 1]) - geom = m.get_perturbed_geom([2, np.pi / 2, -.5]) - np.testing.assert_array_almost_equal(geom[0][1], [0.0, 0.5, 0.0]) - np.testing.assert_array_almost_equal(geom[1][1], [0., 3., 0.]) - np.testing.assert_array_almost_equal(geom[2][1], [0., 1., -1.]) - -if __name__ == '__main__': - unittest.main() From dc6f853444e5c97d5789290163b6ed8166554306 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 7 Oct 2020 11:00:57 +0200 Subject: [PATCH 083/197] resolved tests --- test/chemistry/test_app_mgse.py | 2 -- test/chemistry/test_end2end_with_vqe.py | 3 --- test/chemistry/test_qeom_ee.py | 2 +- test/chemistry/test_qeom_vqe.py | 2 +- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/test/chemistry/test_app_mgse.py b/test/chemistry/test_app_mgse.py index ab28643745..ed6823210f 100644 --- a/test/chemistry/test_app_mgse.py +++ b/test/chemistry/test_app_mgse.py @@ -30,8 +30,6 @@ from qiskit.chemistry.core import QubitMappingType from qiskit.chemistry.drivers import PySCFDriver, UnitsType -# TODO Ground state interface PR - class TestAppMGSE(QiskitChemistryTestCase): """Test molecular ground state energy application """ diff --git a/test/chemistry/test_end2end_with_vqe.py b/test/chemistry/test_end2end_with_vqe.py index e3b916e88d..13cdae6a9a 100644 --- a/test/chemistry/test_end2end_with_vqe.py +++ b/test/chemistry/test_end2end_with_vqe.py @@ -25,8 +25,6 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType -# TODO Ground state interface PR - @ddt class TestEnd2End(QiskitChemistryTestCase): @@ -69,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_qeom_ee.py b/test/chemistry/test_qeom_ee.py index a6a8b13d4b..eafcacc504 100644 --- a/test/chemistry/test_qeom_ee.py +++ b/test/chemistry/test_qeom_ee.py @@ -25,7 +25,7 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry.algorithms import QEomEE -# TODO Ground state interface PR +# TODO Update this test after ESC is done class TestEomEE(QiskitAquaTestCase): diff --git a/test/chemistry/test_qeom_vqe.py b/test/chemistry/test_qeom_vqe.py index ecfc124ac0..68ac1265df 100644 --- a/test/chemistry/test_qeom_vqe.py +++ b/test/chemistry/test_qeom_vqe.py @@ -31,7 +31,7 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock -# TODO Ground state interface PR +# TODO Update test after ESC is done class TestEomVQE(QiskitAquaTestCase): From cc49925dc657ba8106370d638f130822b400ca68 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 7 Oct 2020 11:04:51 +0200 Subject: [PATCH 084/197] docstring fix --- .../ground_state_calculation/ground_state_calculation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index f5ecd70c1a..de32fa55eb 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -43,6 +43,7 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: @abstractmethod def compute_groundstate(self, driver: BaseDriver) -> GroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. + This will return the ground state calculation result. Args: driver: BaseDriver From 37f71f8d42f10429a912ad919c556dbca38e847e Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 7 Oct 2020 11:07:14 +0200 Subject: [PATCH 085/197] fix error & specify type hint --- .../mes_ground_state_calculation.py | 9 +++++---- .../qubit_transformations/fermionic_transformation.py | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 702d4efe73..deb2a91e2f 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -61,11 +61,12 @@ def compute_groundstate(self, driver: BaseDriver, Args: driver: A chemistry driver. - additional_operators: Additional auxiliary ``FermionicOperator``s to evaluate at the - ground state. + additional_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: - ValueError: If an operator in ``additional_operators`` is not of type + NotImplementedError: If an operator in ``additional_operators`` is not of type ``FermionicOperator``. Returns: @@ -73,7 +74,7 @@ def compute_groundstate(self, driver: BaseDriver, """ if additional_operators is not None: if any(not isinstance(op, FermionicOperator) for op in additional_operators.values()): - raise ValueError('The additional operators must be of type FermionicOperator.') + 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 diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 499f9c02a7..d88cd841b1 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -115,7 +115,7 @@ def __init__(self, self._molecule_info: Dict[str, Any] = {} def transform(self, driver: BaseDriver, - additional_operators: Optional[Dict[str, Any]] = None + additional_operators: Optional[Dict[str, FermionicOperator]] = None ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: """Transformation to qubit operator from the driver @@ -132,7 +132,7 @@ def transform(self, driver: BaseDriver, return ops, aux_ops def _do_transform(self, qmolecule: QMolecule, - additional_operators: Optional[Dict[str, Any]] = None + additional_operators: Optional[Dict[str, FermionicOperator]] = None ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: """ Args: @@ -458,7 +458,7 @@ def add_context(self, result: FermionicResult) -> None: for name in dipole_names: moment = result.aux_values[name] if moment is not None: - dipole_moment += [moment.real[0]] + dipole_moment += [moment[0].real] else: dipole_moment += [None] From b9da2bf730556c51023dc6c67a32fed3a6cd15fb Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 7 Oct 2020 11:11:43 +0200 Subject: [PATCH 086/197] change typehint Any -> FermionicOp --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 8a877d6f02..7a09fc55c9 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -131,7 +131,7 @@ def _check_cyclicity(indices: List[int]) -> bool: return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) def compute_groundstate(self, driver: BaseDriver, - additional_operators: Optional[Dict[str, Any]] = None + additional_operators: Optional[Dict[str, FermionicOperator]] = None ) -> 'AdaptVQEResult': """Computes the ground state. From 534de137ae73c2f24bc6660f3725d26d9bbc2048 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 7 Oct 2020 11:12:14 +0200 Subject: [PATCH 087/197] add missing imports --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 7a09fc55c9..20a450906a 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -12,7 +12,7 @@ """A ground state calculation employing the AdaptVQE algorithm.""" -from typing import Optional, List, Dict, Any +from typing import Optional, List, Dict import logging import re import warnings @@ -22,6 +22,7 @@ from qiskit.aqua.algorithms import VQE from qiskit.aqua.operators import LegacyBaseOperator, WeightedPauliOperator from qiskit.aqua.utils.validation import validate_min +from qiskit.chemistry import FermionicOperator from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.qubit_transformations import FermionicTransformation From 37e54b38aaa1624b5c962a605dd3e3471579b6a0 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 7 Oct 2020 11:30:04 +0200 Subject: [PATCH 088/197] spell --- test/chemistry/test_qeom_ee.py | 2 +- test/chemistry/test_qeom_vqe.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/chemistry/test_qeom_ee.py b/test/chemistry/test_qeom_ee.py index eafcacc504..30414e3414 100644 --- a/test/chemistry/test_qeom_ee.py +++ b/test/chemistry/test_qeom_ee.py @@ -25,7 +25,7 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry.algorithms import QEomEE -# TODO Update this test after ESC is done +# TODO Update this test after excited states PR is done class TestEomEE(QiskitAquaTestCase): diff --git a/test/chemistry/test_qeom_vqe.py b/test/chemistry/test_qeom_vqe.py index 68ac1265df..3c7591f0db 100644 --- a/test/chemistry/test_qeom_vqe.py +++ b/test/chemistry/test_qeom_vqe.py @@ -31,7 +31,7 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock -# TODO Update test after ESC is done +# TODO Update test after excited states PR is done class TestEomVQE(QiskitAquaTestCase): From 65fbe056934326800262fc7756a6014246ecf593 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 7 Oct 2020 12:26:55 +0200 Subject: [PATCH 089/197] suggestions from code review --- .../fermionic_transformation.py | 11 ++++++----- .../qubit_operator_transformation.py | 12 +++++++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 1f9b1a9747..63032e8556 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -116,14 +116,14 @@ def __init__(self, def transform(self, driver: BaseDriver, additional_operators: Optional[Dict[str, FermionicOperator]] = None ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: - """Transformation to qubit operator from the driver + """Transformation from the ``driver`` to a qubit operator. Args: - driver: Base Driver - additional_operators: Additional ``FermionicOperator``s to map to a qubit operator. + driver: A driver encoding the molecule information. + additional_operators: Additional auxiliary ``FermionicOperator``s to evaluate. Returns: - qubit operator, auxiliary operators + A qubit operator and a dictionary of auxiliary operators. """ q_molecule = driver.run() ops, aux_ops = self._do_transform(q_molecule, additional_operators) @@ -242,6 +242,7 @@ def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: 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[name] = aux_qop logger.debug(' num paulis: %s', aux_qop.paulis) @@ -451,7 +452,7 @@ def add_context(self, result: FermionicResult) -> None: if aux_ops_vals is not None: # Dipole results if dipole aux ops were present dipole_names = ['Dipole ' + axis for axis in ['x', 'y', 'z']] - if all(name in result.aux_values for name in dipole_names): + if all(name in result.aux_values.keys() for name in dipole_names): # extract dipole moment in each axis dipole_moment = [] for name in dipole_names: diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index fbd515d461..5b488308ee 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -27,7 +27,17 @@ class QubitOperatorTransformation(ABC): def transform(self, driver: BaseDriver, additional_operators: Optional[Dict[str, Any]] = None ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: - """transforms to qubit operators """ + """Transformation from the ``driver`` to a qubit operator. + + Args: + driver: A driver encoding the molecule information. + additional_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 @abstractmethod From e8b78f1daaa0dfa7ada9a580d74abbebc17b5f15 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 7 Oct 2020 12:27:59 +0200 Subject: [PATCH 090/197] consistent use in .keys() --- .../qubit_transformations/fermionic_transformation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 63032e8556..e8ccaf275d 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -470,17 +470,17 @@ def add_context(self, result: FermionicResult) -> None: self._y_dipole_shift, self._z_dipole_shift) - if 'Number of Particles' in result.aux_values: + if 'Number of Particles' in result.aux_values.keys(): result.num_particles = result.aux_values['Number of Particles'][0].real else: result.num_particles = None - if 'S^2' in result.aux_values: + if 'S^2' in result.aux_values.keys(): result.total_angular_momentum = result.aux_values['S^2'][0].real else: result.total_angular_momentum = None - if 'Magnetization' in result.aux_values: + if 'Magnetization' in result.aux_values.keys(): result.magnetization = result.aux_values['Magnetization'][0].real else: result.magnetization = None From 5cf77027832baa2ec806c546112c2a9b62b83727 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Wed, 7 Oct 2020 13:58:11 +0100 Subject: [PATCH 091/197] type hints, minor improvements --- .../ground_state_calculation/adapt_vqe.py | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 3a8e17eba8..921daf0fa0 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -14,7 +14,7 @@ A ground state calculation employing the AdaptVQE algorithm. """ -from typing import Optional, List +from typing import Optional, List, Tuple import logging import re import warnings @@ -72,7 +72,7 @@ def _compute_gradients(self, theta: List[float], var_form: UCCSD, operator: LegacyBaseOperator, - ) -> List: + ) -> List[Tuple[float, WeightedPauliOperator]]: """ Computes the gradients for all available excitation operators. @@ -158,9 +158,9 @@ def compute_groundstate(self, driver: BaseDriver) -> 'AdaptVQEResult': threshold_satisfied = False alternating_sequence = False max_iterations_exceeded = False - prev_op_indices = [] - theta = [] # type: List - max_grad = (0, 0) + 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 @@ -173,15 +173,17 @@ def compute_groundstate(self, driver: BaseDriver) -> 'AdaptVQEResult': # store maximum gradient's index for cycle detection prev_op_indices.append(max_grad_index) # log gradients - gradlog = "\nGradients in iteration #{}".format(str(iteration)) - gradlog += "\nID: Excitation Operator: Gradient <(*) maximum>" - for i, grad in enumerate(cur_grads): - gradlog += '\n{}: {}: {}'.format(str(i), str(grad[1]), str(grad[0])) - if grad[1] == max_grad[1]: - gradlog += '\t(*)' - logger.info(gradlog) + if 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 succesfully with a final maximum gradient: %s", + logger.info("Adaptive VQE terminated successfully " + "with a final maximum gradient: %s", str(np.abs(max_grad[0]))) threshold_satisfied = True break From 19b704a7663e9d16be07e1f3278c9d4c5df9ab76 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 7 Oct 2020 15:11:05 +0200 Subject: [PATCH 092/197] Rethink the qiskit.chemistry.results After some discussion we decided to rethink parts of the qiskit.chemistry.results interfaces. We want to minimize the differentiation between grround and excited states and let only the concrete `QubitOperatorTransformation` handle the fermionic and bosonic differentiation. Thus, I apply the following changes: * provide a single `EigenstateResult` which can be either a ground or excited state * derive `ElectronicStructureResult` (prev. `FermionicResult`) from this * use the `FermionicTransformation.interpret()` method to map a general `EigenstateResult` to the type specific for the transformation With the last change we also align the `qiskit.chemistry` module further with the `qiskit.optimization` module where we also use `interpret()` rather than `add_context()` methods (which was the name we intended to use prior to this commit). --- .../ground_state_calculation/adapt_vqe.py | 17 ++++++++-------- .../ground_state_calculation.py | 4 ++-- .../mes_ground_state_calculation.py | 14 ++++++------- .../fermionic_transformation.py | 17 +++++++++++----- .../qubit_operator_transformation.py | 11 ++++++---- qiskit/chemistry/results/__init__.py | 10 ++++------ .../{state_result.py => eigenstate_result.py} | 20 ++++++++++++------- ...sult.py => electronic_structure_result.py} | 16 +++++++-------- 8 files changed, 62 insertions(+), 47 deletions(-) rename qiskit/chemistry/results/{state_result.py => eigenstate_result.py} (74%) rename qiskit/chemistry/results/{fermionic_result.py => electronic_structure_result.py} (97%) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 921daf0fa0..67f621f46b 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -27,7 +27,7 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.results import FermionicGroundStateResult +from qiskit.chemistry.results import ElectronicStructureResult from .ground_state_calculation import GroundStateCalculation from .mes_factories import VQEUCCSDFactory @@ -221,21 +221,22 @@ def compute_groundstate(self, driver: BaseDriver) -> 'AdaptVQEResult': raise AquaError('The algorithm finished due to an unforeseen reason!') # extend VQE returned information with additional outputs - result = AdaptVQEResult() - result.raw_result = raw_vqe_result - result.computed_electronic_energy = raw_vqe_result.eigenvalue.real - result.aux_values = raw_vqe_result.aux_operator_eigenvalues + eigenstate_result = ElectronicStructureResult() + eigenstate_result.raw_result = raw_vqe_result + eigenstate_result.eigenvalue = raw_vqe_result.eigenvalue + eigenstate_result.aux_values = raw_vqe_result.aux_operator_eigenvalues + electronic_result = self.transformation.interpret(eigenstate_result) + + result = AdaptVQEResult(electronic_result.data) result.num_iterations = iteration result.final_max_gradient = max_grad[0] result.finishing_criterion = finishing_criterion - self.transformation.add_context(result) - logger.info('The final energy is: %s', str(result.computed_electronic_energy)) return result -class AdaptVQEResult(FermionicGroundStateResult): +class AdaptVQEResult(ElectronicStructureResult): """ AdaptVQE Result.""" @property diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index de32fa55eb..e512ffc357 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -15,7 +15,7 @@ from abc import ABC, abstractmethod from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.results import GroundStateResult +from qiskit.chemistry.results import EigenstateResult from ..qubit_transformations.qubit_operator_transformation import QubitOperatorTransformation @@ -41,7 +41,7 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: self._transformation = transformation @abstractmethod - def compute_groundstate(self, driver: BaseDriver) -> GroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> EigenstateResult: """Compute the ground state energy of the molecule that was supplied via the driver. This will return the ground state calculation result. diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 006d25fce5..6d15076304 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -18,7 +18,7 @@ from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.ground_state_calculation import GroundStateCalculation from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation -from qiskit.chemistry.results import FermionicGroundStateResult +from qiskit.chemistry.results import EigenstateResult from .mes_factories import MESFactory @@ -53,7 +53,7 @@ def returns_groundstate(self) -> bool: return False - def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: + def compute_groundstate(self, driver: BaseDriver) -> EigenstateResult: """Compute Ground State properties. Args: @@ -74,9 +74,9 @@ def compute_groundstate(self, driver: BaseDriver) -> FermionicGroundStateResult: raw_mes_result = solver.compute_minimum_eigenvalue(operator, aux_operators) - result = FermionicGroundStateResult() - result.raw_result = raw_mes_result - result.computed_electronic_energy = raw_mes_result.eigenvalue.real - result.aux_values = raw_mes_result.aux_operator_eigenvalues - self.transformation.add_context(result) + eigenstate_result = EigenstateResult() + eigenstate_result.raw_result = raw_mes_result + eigenstate_result.eigenvalue = raw_mes_result.eigenvalue + eigenstate_result.aux_values = raw_mes_result.aux_operator_eigenvalues + result = self.transformation.interpret(eigenstate_result) return result diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 4c89e7888e..bc380ee15f 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -24,7 +24,7 @@ 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, FermionicResult +from qiskit.chemistry.results import DipoleTuple, EigenstateResult, ElectronicStructureResult from .qubit_operator_transformation import QubitOperatorTransformation from ..components.initial_states import HartreeFock @@ -427,19 +427,24 @@ def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: np.ndarray) -> Z2Symmetrie z2_symmetries.tapering_values = taper_coef return z2_symmetries - def add_context(self, result: FermionicResult) -> None: - """Adds contextual information to the state result object. + def interpret(self, eigenstate_result: EigenstateResult) -> ElectronicStructureResult: + """Interprets an EigenstateResult in the context of this transformation. Args: - result: a state result object. + eigenstate_result: an eigenstate result object. + + Returns: + An electronic structure result. """ + result = ElectronicStructureResult(eigenstate_result.data) + result.computed_electronic_energy = eigenstate_result.eigenvalue.real 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 - aux_ops_vals = result.aux_values + aux_ops_vals = eigenstate_result.aux_values if aux_ops_vals is not None: # Dipole results if dipole aux ops were present dipole_idx = 3 @@ -463,6 +468,8 @@ def add_context(self, result: FermionicResult) -> None: result.magnetization = aux_ops_vals[2][0].real \ if aux_ops_vals[2] is not None else None + return result + @staticmethod def _try_reduce_fermionic_operator(fer_op: FermionicOperator, freeze_list: List, diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index c00fc7486c..9765e536d7 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -17,7 +17,7 @@ from qiskit.aqua.operators.legacy import WeightedPauliOperator from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.results import StateResult +from qiskit.chemistry.results import EigenstateResult class QubitOperatorTransformation(ABC): @@ -30,10 +30,13 @@ def transform(self, driver: BaseDriver raise NotImplementedError @abstractmethod - def add_context(self, result: StateResult) -> None: - """Adds contextual information to the state result object. + def interpret(self, eigenstate_result: EigenstateResult) -> EigenstateResult: + """Interprets an EigenstateResult in the context of this transformation. Args: - result: a state result object. + eigenstate_result: an eigenstate result object. + + Returns: + An "interpreted" eigenstate result. """ raise NotImplementedError diff --git a/qiskit/chemistry/results/__init__.py b/qiskit/chemistry/results/__init__.py index d29ba520f9..cf0197e58c 100644 --- a/qiskit/chemistry/results/__init__.py +++ b/qiskit/chemistry/results/__init__.py @@ -12,13 +12,11 @@ """Chemistry results module.""" -from .fermionic_result import DipoleTuple, FermionicResult, FermionicGroundStateResult -from .state_result import StateResult, GroundStateResult +from .electronic_structure_result import DipoleTuple, ElectronicStructureResult +from .eigenstate_result import EigenstateResult __all__ = ['DipoleTuple', - 'StateResult', - 'GroundStateResult', - 'FermionicResult', - 'FermionicGroundStateResult', + 'EigenstateResult', + 'ElectronicStructureResult', ] diff --git a/qiskit/chemistry/results/state_result.py b/qiskit/chemistry/results/eigenstate_result.py similarity index 74% rename from qiskit/chemistry/results/state_result.py rename to qiskit/chemistry/results/eigenstate_result.py index 7f098f247b..4e39d0665e 100644 --- a/qiskit/chemistry/results/state_result.py +++ b/qiskit/chemistry/results/eigenstate_result.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""State results module.""" +"""Eigenstate results module.""" from typing import Optional import numpy as np @@ -18,8 +18,18 @@ from qiskit.aqua.algorithms import AlgorithmResult -class StateResult(AlgorithmResult): - """The state result interface.""" +class EigenstateResult(AlgorithmResult): + """The eigenstate result interface.""" + + @property + def eigenvalue(self) -> Optional[complex]: + """ returns eigen value """ + return self.get('eigenvalue') + + @eigenvalue.setter + def eigenvalue(self, value: complex) -> None: + """ set eigen value """ + self.data['eigenvalue'] = value @property def aux_values(self) -> Optional[np.ndarray]: @@ -39,7 +49,3 @@ def raw_result(self) -> Optional[AlgorithmResult]: @raw_result.setter def raw_result(self, result: AlgorithmResult) -> None: self.data['raw_result'] = result - - -class GroundStateResult(StateResult): - """The ground state result interface.""" diff --git a/qiskit/chemistry/results/fermionic_result.py b/qiskit/chemistry/results/electronic_structure_result.py similarity index 97% rename from qiskit/chemistry/results/fermionic_result.py rename to qiskit/chemistry/results/electronic_structure_result.py index e55f801fba..72d3c6588e 100644 --- a/qiskit/chemistry/results/fermionic_result.py +++ b/qiskit/chemistry/results/electronic_structure_result.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""The fermionic ground state result.""" +"""The electronic structure result.""" from typing import List, Optional, Tuple, cast @@ -20,7 +20,7 @@ from qiskit.aqua.algorithms import AlgorithmResult from qiskit.chemistry import QMolecule -from .state_result import StateResult, GroundStateResult +from .eigenstate_result import EigenstateResult logger = logging.getLogger(__name__) @@ -31,8 +31,8 @@ DipoleTuple = Tuple[Optional[float], Optional[float], Optional[float]] -class FermionicResult(StateResult): - """The fermionic ground state result.""" +class ElectronicStructureResult(EigenstateResult): + """The electronic structure result.""" @property def algorithm_result(self) -> AlgorithmResult: @@ -80,12 +80,16 @@ def nuclear_dipole_moment(self, value: DipoleTuple) -> None: @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) @@ -340,7 +344,3 @@ def _float_to_string(value: Optional[float], precision: int = 8) -> str: return 'None' else: return '0.0' if value == 0 else ('{:.' + str(precision) + 'f}').format(value).rstrip('0') - - -class FermionicGroundStateResult(FermionicResult, GroundStateResult): - """The fermionic ground state result.""" From 7361c90f53cfb3155302514f1ea968ff528a9e6b Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 7 Oct 2020 15:16:46 +0200 Subject: [PATCH 093/197] Remove unneeded DeprecationWarning from AdaptVQEResult --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 67f621f46b..30b03f06c8 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -268,11 +268,3 @@ def finishing_criterion(self) -> str: def finishing_criterion(self, value: str) -> None: """ Sets finishing criterion """ self.data['finishing_criterion'] = value - - def __getitem__(self, key: object) -> object: - if key == 'final_max_grad': - warnings.warn('final_max_grad deprecated, use final_max_gradient property.', - DeprecationWarning) - return super().__getitem__('final_max_gradient') - - return super().__getitem__(key) From a877e988cdb5436435867feb6817c16c147b7103 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 7 Oct 2020 15:22:12 +0200 Subject: [PATCH 094/197] Update some docstrings --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 4 +++- .../ground_state_calculation/ground_state_calculation.py | 6 +++--- .../ground_state_calculation/mes_factories/mes_factory.py | 3 +-- .../mes_ground_state_calculation.py | 6 ++++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 30b03f06c8..06f72df0bd 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -141,7 +141,9 @@ def compute_groundstate(self, driver: BaseDriver) -> 'AdaptVQEResult': 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: - A fermionic ground state result. + 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) diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index e512ffc357..5b210bf30f 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -43,13 +43,13 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: @abstractmethod def compute_groundstate(self, driver: BaseDriver) -> EigenstateResult: """Compute the ground state energy of the molecule that was supplied via the driver. - This will return the ground state calculation result. Args: - driver: BaseDriver + driver: a chemistry driver object which defines the chemical problem that is to be + solved by this calculation. Returns: - A molecular ground state result + An eigenstate result. """ raise NotImplementedError diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py index 5ecc8da841..ad75cf7565 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py @@ -18,8 +18,7 @@ class MESFactory(ABC): - """A factory to construct a minimum eigensolver suitable for a qubit operator transformation. - """ + """A factory to construct a minimum eigensolver based on a qubit operator transformation.""" @abstractmethod def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEigensolver: diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 6d15076304..3c44460371 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -57,10 +57,12 @@ def compute_groundstate(self, driver: BaseDriver) -> EigenstateResult: """Compute Ground State properties. Args: - driver: A chemistry driver. + driver: a chemistry driver object which defines the chemical problem that is to be + solved by this calculation. Returns: - Ground state result TODO + An eigenstate result. Depending on the transformation this can be an electronic + structure or bosonic result. """ operator, aux_operators = self.transformation.transform(driver) From a127cf3297355f16cd3ffa4eae84cc3985414f8a Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 7 Oct 2020 10:00:40 -0400 Subject: [PATCH 095/197] fix lint --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 06f72df0bd..bcdbde4f79 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -17,7 +17,6 @@ from typing import Optional, List, Tuple import logging import re -import warnings import numpy as np from qiskit.aqua import AquaError From af6a9b835b4791871db334c3792567c2d07a187a Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 7 Oct 2020 17:48:15 +0200 Subject: [PATCH 096/197] revert back to lists of aux_ops --- .../ground_state_calculation/adapt_vqe.py | 11 +- .../ground_state_calculation.py | 6 +- .../mes_ground_state_calculation.py | 23 ++-- .../fermionic_transformation.py | 122 ++++++++++-------- .../qubit_operator_transformation.py | 8 +- 5 files changed, 97 insertions(+), 73 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 20a450906a..ec3e0f6988 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -12,7 +12,7 @@ """A ground state calculation employing the AdaptVQE algorithm.""" -from typing import Optional, List, Dict +from typing import Optional, List, Union import logging import re import warnings @@ -132,13 +132,14 @@ def _check_cyclicity(indices: List[int]) -> bool: return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) def compute_groundstate(self, driver: BaseDriver, - additional_operators: Optional[Dict[str, FermionicOperator]] = None + aux_operators: Optional[List[Union[WeightedPauliOperator, + FermionicOperator]]] = None ) -> 'AdaptVQEResult': """Computes the ground state. Args: driver: a chemistry driver. - additional_operators: Additional auxiliary ``FermionicOperator``s to evaluate at the + aux_operators: Additional auxiliary ``FermionicOperator``s to evaluate at the ground state. Raises: @@ -148,7 +149,7 @@ def compute_groundstate(self, driver: BaseDriver, Returns: A fermionic ground state result. """ - operator, aux_operators = self._transformation.transform(driver, additional_operators) + operator, aux_operators = self._transformation.transform(driver, aux_operators) vqe = self._solver.get_solver(self._transformation) if not isinstance(vqe, VQE): @@ -213,7 +214,7 @@ def compute_groundstate(self, driver: BaseDriver, # once finished evaluate auxiliary operators if any if aux_operators is not None: aux_result = vqe.compute_minimum_eigenvalue(operator, list(aux_operators.values())) - aux_values = dict(zip(aux_operators.keys(), aux_result.aux_operator_eigenvalues)) + aux_values = aux_result.aux_operator_eigenvalues else: aux_values = None diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index ecf1adbb7d..54fb56d7be 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -13,7 +13,7 @@ """The ground state calculation interface.""" from abc import ABC, abstractmethod -from typing import Dict, Any, Optional +from typing import List, Any, Optional from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.results import GroundStateResult @@ -43,14 +43,14 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: @abstractmethod def compute_groundstate(self, driver: BaseDriver, - additional_operators: Optional[Dict[str, Any]] = None + aux_operators: Optional[List[Any]] = None ) -> GroundStateResult: """Compute the ground state energy of the molecule that was supplied via the driver. This will return the ground state calculation result. Args: driver: BaseDriver - additional_operators: Additional auxiliary operators to evaluate. Must be of type + 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. diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index deb2a91e2f..0c862563fa 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -12,9 +12,10 @@ """Ground state computation using a minimum eigensolver.""" -from typing import Union, Dict, Any, Optional +from typing import Union, List, Any, Optional from qiskit.aqua.algorithms import MinimumEigensolver +from qiskit.aqua.operators import WeightedPauliOperator from qiskit.chemistry import FermionicOperator from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.ground_state_calculation import GroundStateCalculation @@ -55,31 +56,32 @@ def returns_groundstate(self) -> bool: return False def compute_groundstate(self, driver: BaseDriver, - additional_operators: Optional[Dict[str, Any]] = None + aux_operators: Optional[List[Any]] = None ) -> FermionicGroundStateResult: """Compute Ground State properties. Args: driver: A chemistry driver. - additional_operators: Additional auxiliary operators to evaluate at the ground state. + 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 ``additional_operators`` is not of type + NotImplementedError: If an operator in ``aux_operators`` is not of type ``FermionicOperator``. Returns: Ground state result TODO """ - if additional_operators is not None: - if any(not isinstance(op, FermionicOperator) for op in additional_operators.values()): + 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, additional_operators) + operator, aux_operators = self.transformation.transform(driver, aux_operators) if isinstance(self._solver, MESFactory): # this must be called after transformation.transform @@ -88,11 +90,12 @@ def compute_groundstate(self, driver: BaseDriver, solver = self._solver # convert aux_operators to a list for the minimum eigensolver - mes_aux_ops = list(aux_operators.values()) if solver.supports_aux_operators() else None - raw_mes_result = solver.compute_minimum_eigenvalue(operator, mes_aux_ops) + if not solver.supports_aux_operators(): + aux_operators = None + raw_mes_result = solver.compute_minimum_eigenvalue(operator, aux_operators) # convert the aux_values back to a dictionary - aux_values = dict(zip(aux_operators.keys(), raw_mes_result.aux_operator_eigenvalues)) + aux_values = raw_mes_result.aux_operator_eigenvalues result = FermionicGroundStateResult() result.raw_result = raw_mes_result diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index e8ccaf275d..a59f7af056 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -93,6 +93,7 @@ def __init__(self, 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 @@ -114,29 +115,31 @@ def __init__(self, self._molecule_info: Dict[str, Any] = {} def transform(self, driver: BaseDriver, - additional_operators: Optional[Dict[str, FermionicOperator]] = None - ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: + aux_operators: Optional[List[FermionicOperator]] = None + ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: """Transformation from the ``driver`` to a qubit operator. Args: driver: A driver encoding the molecule information. - additional_operators: Additional auxiliary ``FermionicOperator``s to evaluate. + 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, additional_operators) + ops, aux_ops = self._do_transform(q_molecule, aux_operators) return ops, aux_ops def _do_transform(self, qmolecule: QMolecule, - additional_operators: Optional[Dict[str, FermionicOperator]] = None - ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: + aux_operators: Optional[List[Union[FermionicOperator, + WeightedPauliOperator]]] = None + ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: """ Args: qmolecule: qmolecule - additional_operators: Additional ``FermionicOperator``s to map to a qubit operator. + aux_operators: Additional ``FermionicOperator``s to map to a qubit operator. + Objects of type ``WeightedPauliOperator`` undergo no transformation. Returns: (qubit operator, auxiliary operators) @@ -228,7 +231,8 @@ def _do_transform(self, qmolecule: QMolecule, logger.debug(' num paulis: %s, num qubits: %s', len(qubit_op.paulis), qubit_op.num_qubits) - aux_ops = {} + aux_ops = [] # list of the aux operators + apply_reductions = [] # list of bools specifying whether to apply reductions or not def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: """ @@ -239,14 +243,22 @@ def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: 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[name] = aux_qop + if not isinstance(aux_op, WeightedPauliOperator): + aux_qop = FermionicTransformation._map_fermionic_operator_to_qubit( + aux_op, self._qubit_mapping, new_nel, self._two_qubit_reduction + ) + aux_qop.name = name + apply_reduction = True + else: + aux_qop = aux_op + apply_reduction = False + + aux_ops.append(aux_qop) + apply_reductions.append(apply_reduction) logger.debug(' num paulis: %s', aux_qop.paulis) - # add standard auxiliary operators + # 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') @@ -254,12 +266,10 @@ def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: logger.debug('Creating aux op for Magnetization') _add_aux_op(fer_op.total_magnetization(), 'Magnetization') - # add user specified auxiliary operators - if additional_operators is not None: - for name, aux_op in additional_operators.items(): - _add_aux_op(aux_op, name) - + # 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]: """ @@ -299,8 +309,13 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ op_dipole_z, self._z_dipole_shift, self._ph_z_dipole_shift = \ _dipole_op(qmolecule.z_dipole_integrals, 'z') - for op_dipole in [op_dipole_x, op_dipole_y, op_dipole_z]: - aux_ops[op_dipole.name] = op_dipole + aux_ops += [op_dipole_x, op_dipole_y, op_dipole_z] + apply_reductions += 3 * [True] + + # add user specified auxiliary operators + if aux_operators is not None: + for name, aux_op in aux_operators.items(): + _add_aux_op(aux_op, name) logger.info('Molecule num electrons: %s, remaining for processing: %s', [num_alpha, num_beta], new_nel) @@ -317,7 +332,8 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ 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) + qubit_op, aux_ops, z2symmetries = self._process_z2symmetry_reduction(qubit_op, aux_ops, + apply_reductions) self._molecule_info['z2_symmetries'] = z2symmetries logger.debug('Processing complete ready to run algorithm') @@ -325,13 +341,15 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ def _process_z2symmetry_reduction(self, qubit_op: WeightedPauliOperator, - aux_ops: WeightedPauliOperator) -> Tuple: + aux_ops: List[WeightedPauliOperator], + apply_reductions: List[bool]) -> Tuple: """ Implement z2 symmetries in the qubit operator Args: qubit_op : qubit operator aux_ops: auxiliary operators + apply_reductions: whether to apply reductions on the aux_ops Returns: (z2_qubit_op, z2_aux_ops, z2_symmetries) @@ -358,10 +376,10 @@ def _process_z2symmetry_reduction(self, if not commutes: raise QiskitChemistryError('Z2 symmetry failure main operator must commute ' 'with symmetries found from it') - for name, aux_op in aux_ops.items(): + for i, aux_op in enumerate(aux_ops): commutes = FermionicTransformation._check_commutes(symmetry_ops, aux_op) if not commutes: - aux_ops[name] = None # Discard since no meaningful measurement can be done + 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'], @@ -385,12 +403,14 @@ def _process_z2symmetry_reduction(self, 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 name, aux_op in aux_ops.items(): + z2_aux_ops = [] + for aux_op, apply_reduction in zip(aux_ops, apply_reductions): if aux_op is None: - z2_aux_ops[name] = None + z2_aux_ops += [None] + elif apply_reduction: + z2_aux_ops += [z2_symmetries.taper(aux_op).chop(chop_to)] else: - z2_aux_ops[name] = z2_symmetries.taper(aux_op).chop(chop_to) + z2_aux_ops += [aux_op] return z2_qubit_op, z2_aux_ops, z2_symmetries @@ -448,15 +468,30 @@ def add_context(self, result: FermionicResult) -> 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 - aux_ops_vals = result.aux_values - if aux_ops_vals is not None: - # Dipole results if dipole aux ops were present - dipole_names = ['Dipole ' + axis for axis in ['x', 'y', 'z']] - if all(name in result.aux_values.keys() for name in dipole_names): + if result.aux_values is not None: + # the first three values are hardcoded to number of particles, angular momentum + # and magnetization in this order + if result.aux_values[0] is not None: + result.num_particles = result.aux_values[0][0].real + else: + result.num_particles = None + + if result.aux_values[1] is not None: + result.total_angular_momentum = result.aux_values[1][0].real + else: + result.total_angular_momentum = None + + if result.aux_values[2] is not None: + result.magnetization = result.aux_values[2][0].real + else: + result.magnetization = None + + # the next three are hardcoded to Dipole moments, if they are set + if len(result.aux_values) >= 6 and self._has_dipole_moments: + # check if the names match # extract dipole moment in each axis dipole_moment = [] - for name in dipole_names: - moment = result.aux_values[name] + for moment in result.aux_values[3:6]: if moment is not None: dipole_moment += [moment[0].real] else: @@ -470,21 +505,6 @@ def add_context(self, result: FermionicResult) -> None: self._y_dipole_shift, self._z_dipole_shift) - if 'Number of Particles' in result.aux_values.keys(): - result.num_particles = result.aux_values['Number of Particles'][0].real - else: - result.num_particles = None - - if 'S^2' in result.aux_values.keys(): - result.total_angular_momentum = result.aux_values['S^2'][0].real - else: - result.total_angular_momentum = None - - if 'Magnetization' in result.aux_values.keys(): - result.magnetization = result.aux_values['Magnetization'][0].real - else: - result.magnetization = None - @staticmethod def _try_reduce_fermionic_operator(fer_op: FermionicOperator, freeze_list: List, diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 5b488308ee..4d2931fd16 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -13,7 +13,7 @@ """Base class for transformation to qubit operators for chemistry problems""" from abc import ABC, abstractmethod -from typing import Tuple, Dict, Any, Optional +from typing import Tuple, List, Any, Optional from qiskit.aqua.operators.legacy import WeightedPauliOperator from qiskit.chemistry.drivers import BaseDriver @@ -25,13 +25,13 @@ class QubitOperatorTransformation(ABC): @abstractmethod def transform(self, driver: BaseDriver, - additional_operators: Optional[Dict[str, Any]] = None - ) -> Tuple[WeightedPauliOperator, Dict[str, WeightedPauliOperator]]: + aux_operators: Optional[List[Any]] = None + ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: """Transformation from the ``driver`` to a qubit operator. Args: driver: A driver encoding the molecule information. - additional_operators: Additional auxiliary operators to evaluate. Must be of type + 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. From d08d75008ada84c9d6a0cfeeb32fa7f2c6acf2f1 Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Thu, 8 Oct 2020 09:40:56 +0200 Subject: [PATCH 097/197] Update qiskit/chemistry/ground_state_calculation/adapt_vqe.py Co-authored-by: Max Rossmannek --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index f78c244506..fb90d0afaa 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -216,7 +216,7 @@ def compute_groundstate(self, driver: BaseDriver, # once finished evaluate auxiliary operators if any if aux_operators is not None: - aux_result = vqe.compute_minimum_eigenvalue(operator, list(aux_operators.values())) + aux_result = vqe.compute_minimum_eigenvalue(operator, aux_operators) aux_values = aux_result.aux_operator_eigenvalues else: aux_values = None From 7d5799ab9988a82fad0b338231a305c176ad5dc5 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Thu, 8 Oct 2020 09:47:00 +0200 Subject: [PATCH 098/197] apply suggestions from code review --- .../mes_ground_state_calculation.py | 3 ++- qiskit/chemistry/results/eigenstate_result.py | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index c52e7fa58e..86810e8bca 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -91,9 +91,10 @@ def compute_groundstate(self, driver: BaseDriver, else: solver = self._solver - # convert aux_operators to a list for the minimum eigensolver + # 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) eigenstate_result = EigenstateResult() diff --git a/qiskit/chemistry/results/eigenstate_result.py b/qiskit/chemistry/results/eigenstate_result.py index 00139f7f79..4e39d0665e 100644 --- a/qiskit/chemistry/results/eigenstate_result.py +++ b/qiskit/chemistry/results/eigenstate_result.py @@ -12,7 +12,8 @@ """Eigenstate results module.""" -from typing import Optional, Dict +from typing import Optional +import numpy as np from qiskit.aqua.algorithms import AlgorithmResult @@ -31,12 +32,12 @@ def eigenvalue(self, value: complex) -> None: self.data['eigenvalue'] = value @property - def aux_values(self) -> Optional[Dict[str, float]]: + def aux_values(self) -> Optional[np.ndarray]: """ return aux operator eigen values """ return self.get('aux_values') @aux_values.setter - def aux_values(self, value: Dict[str, float]) -> None: + def aux_values(self, value: np.ndarray) -> None: """ set aux operator eigen values """ self.data['aux_values'] = value From d0f39e8a1d92726ff014e876e977d9a14972c2e3 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Thu, 8 Oct 2020 10:03:13 +0200 Subject: [PATCH 099/197] change typehint to List[float] --- qiskit/chemistry/results/eigenstate_result.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/results/eigenstate_result.py b/qiskit/chemistry/results/eigenstate_result.py index 4e39d0665e..b1cb402290 100644 --- a/qiskit/chemistry/results/eigenstate_result.py +++ b/qiskit/chemistry/results/eigenstate_result.py @@ -12,8 +12,7 @@ """Eigenstate results module.""" -from typing import Optional -import numpy as np +from typing import Optional, List from qiskit.aqua.algorithms import AlgorithmResult @@ -32,12 +31,12 @@ def eigenvalue(self, value: complex) -> None: self.data['eigenvalue'] = value @property - def aux_values(self) -> Optional[np.ndarray]: + def aux_values(self) -> Optional[List[float]]: """ return aux operator eigen values """ return self.get('aux_values') @aux_values.setter - def aux_values(self, value: np.ndarray) -> None: + def aux_values(self, value: List[float]) -> None: """ set aux operator eigen values """ self.data['aux_values'] = value From b56b1b5dc9679202704e2009b4956d0eddb7b4c7 Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Thu, 8 Oct 2020 10:24:35 +0200 Subject: [PATCH 100/197] Update qiskit/chemistry/core/hamiltonian.py Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- qiskit/chemistry/core/hamiltonian.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index e6a1c91af6..8f7a828969 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -117,7 +117,7 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - def run(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, WeightedPauliOperator]: + 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 From 5a5f1be0740cea25b8feab07a9549b55766d7255 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 8 Oct 2020 11:41:02 +0200 Subject: [PATCH 101/197] test fixes after code review --- qiskit/chemistry/core/hamiltonian.py | 3 +- .../test_core_hamiltonian_orb_reduce_gsc.py | 2 +- .../test_core_hamiltonian_symmetries_gsc.py | 2 +- test/chemistry/test_driver_methods.py | 68 ----------- test/chemistry/test_swaprz.py | 73 ----------- test/chemistry/test_symmetries.py | 113 ------------------ 6 files changed, 4 insertions(+), 257 deletions(-) delete mode 100644 test/chemistry/test_driver_methods.py delete mode 100644 test/chemistry/test_swaprz.py delete mode 100644 test/chemistry/test_symmetries.py diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index e6a1c91af6..aeb46867b2 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -117,7 +117,8 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - def run(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, WeightedPauliOperator]: + def run(self, qmolecule: QMolecule) \ + -> Tuple[WeightedPauliOperator, WeightedPauliOperator]: """ run method""" logger.debug('Processing started...') # Save these values for later combination with the quantum computation result diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py index 0eb4ec3ff3..bf811ad104 100644 --- a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py +++ b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py @@ -22,7 +22,7 @@ from qiskit.chemistry.qubit_transformations import FermionicTransformation -class TestCoreHamiltonianOrbReduce(QiskitChemistryTestCase): +class TestFermionicTransformationOrbReduce(QiskitChemistryTestCase): """core/hamiltonian Driver tests.""" def setUp(self): diff --git a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py index 5294e18665..58b2d3c572 100644 --- a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py +++ b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py @@ -28,7 +28,7 @@ from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation -class TestCoreHamiltonianSymmetries(QiskitChemistryTestCase): +class TestFermionicTransforationSymmetries(QiskitChemistryTestCase): """ Core hamiltonian Driver symmetry tests. """ def setUp(self): diff --git a/test/chemistry/test_driver_methods.py b/test/chemistry/test_driver_methods.py deleted file mode 100644 index 2ff3e531a8..0000000000 --- a/test/chemistry/test_driver_methods.py +++ /dev/null @@ -1,68 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 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 Driver Methods """ - -import warnings -import unittest - -from test.chemistry import QiskitChemistryTestCase -from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType -from qiskit.aqua.algorithms import NumPyMinimumEigensolver - - -class TestDriverMethods(QiskitChemistryTestCase): - """Common driver tests. For H2 @ 0.735, sto3g""" - - def setUp(self): - super().setUp() - self.lih = 'LI 0 0 0; H 0 0 1.6' - self.o_h = 'O 0 0 0; H 0 0 0.9697' - self.ref_energies = { - 'lih': -7.882, - 'oh': -74.387 - } - self.ref_dipoles = { - 'lih': 1.818, - 'oh': 0.4615 - } - - @staticmethod - def _run_driver(driver, transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, - freeze_core=True): - qmolecule = driver.run() - - warnings.filterwarnings('ignore', category=DeprecationWarning) - core = Hamiltonian(transformation=transformation, - qubit_mapping=qubit_mapping, - two_qubit_reduction=two_qubit_reduction, - freeze_core=freeze_core, - orbital_reduction=[]) - - qubit_op, aux_ops = core.run(qmolecule) - - npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) - result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) - warnings.filterwarnings('always', category=DeprecationWarning) - return result - - def _assert_energy(self, result, mol): - self.assertAlmostEqual(self.ref_energies[mol], result.energy, places=3) - - 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_swaprz.py b/test/chemistry/test_swaprz.py deleted file mode 100644 index 6a33b7047c..0000000000 --- a/test/chemistry/test_swaprz.py +++ /dev/null @@ -1,73 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 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 ExcitationPreserving from the circuit library.""" - -import warnings -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.drivers import HDF5Driver -from qiskit.chemistry.core import Hamiltonian, QubitMappingType - - -class TestExcitationPreserving(QiskitChemistryTestCase): - """The ExcitationPresering wavefunction was design to preserve the excitation of the system. - - We test it here from chemistry with JORDAN_WIGNER mapping (then the number of particles - is preserved) and HartreeFock initial state to set it up. This facilitates testing - ExcitationPreserving using these chemistry components/problem to ensure its correct operation. - """ - - def setUp(self): - super().setUp() - self.seed = 50 - aqua_globals.random_seed = self.seed - self.reference_energy = -1.137305593252385 - - 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() - warnings.filterwarnings('ignore', category=DeprecationWarning) - operator = Hamiltonian(qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False) - warnings.filterwarnings('always', category=DeprecationWarning) - qubit_op, _ = operator.run(qmolecule) - - 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) - - 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)) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = operator.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - self.assertAlmostEqual(result.energy, self.reference_energy, places=6) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/chemistry/test_symmetries.py b/test/chemistry/test_symmetries.py deleted file mode 100644 index 88d16b6dc2..0000000000 --- a/test/chemistry/test_symmetries.py +++ /dev/null @@ -1,113 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 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 Symmetry UCCSD processing """ - -import warnings -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 - - -class TestSymmetries(QiskitChemistryTestCase): - """Test for symmetry processing.""" - - 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') - except QiskitChemistryError: - self.skipTest('PYSCF driver does not appear to be installed') - 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=True, - orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) - self.qubit_op, _ = self.core.run(self.qmolecule) - self.z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) - - self.reference_energy = -7.882096489442 - - def test_symmetries(self): - """ symmetries test """ - labels = [symm.to_label() for symm in self.z2_symmetries.symmetries] - self.assertSequenceEqual(labels, ['ZIZIZIZI', 'ZZIIZZII']) - - def test_sq_paulis(self): - """ sq paulis test """ - labels = [sq.to_label() for sq in self.z2_symmetries.sq_paulis] - self.assertSequenceEqual(labels, ['IIIIIIXI', 'IIIIIXII']) - - def test_cliffords(self): - """ clifford test """ - self.assertEqual(2, len(self.z2_symmetries.cliffords)) - - def test_sq_list(self): - """ sq list test """ - self.assertSequenceEqual(self.z2_symmetries.sq_list, [1, 2]) - - 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.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) - - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(algo_result) - warnings.filterwarnings('always', category=DeprecationWarning) - - self.assertAlmostEqual(result.energy, self.reference_energy, places=6) - - -if __name__ == '__main__': - unittest.main() From 63196b509647292fb84dd68198ce90dd67a8d6fe Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 8 Oct 2020 13:08:10 +0200 Subject: [PATCH 102/197] Rename aux_values to aux_operator_eigenvalues This naming is consistent with the EigensolverResult and MinimumEigensolverResult classes. --- .../ground_state_calculation/adapt_vqe.py | 2 +- .../mes_ground_state_calculation.py | 2 +- .../fermionic_transformation.py | 18 +++++++++--------- qiskit/chemistry/results/eigenstate_result.py | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index fb90d0afaa..18b5b1e8c1 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -234,7 +234,7 @@ def compute_groundstate(self, driver: BaseDriver, eigenstate_result = ElectronicStructureResult() eigenstate_result.raw_result = raw_vqe_result eigenstate_result.eigenvalue = raw_vqe_result.eigenvalue - eigenstate_result.aux_values = aux_values + eigenstate_result.aux_operator_eigenvalues = aux_values electronic_result = self.transformation.interpret(eigenstate_result) result = AdaptVQEResult(electronic_result.data) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 86810e8bca..19a4d2f9f5 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -100,6 +100,6 @@ def compute_groundstate(self, driver: BaseDriver, eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_mes_result eigenstate_result.eigenvalue = raw_mes_result.eigenvalue - eigenstate_result.aux_values = raw_mes_result.aux_operator_eigenvalues + eigenstate_result.aux_operator_eigenvalues = raw_mes_result.aux_operator_eigenvalues result = self.transformation.interpret(eigenstate_result) return result diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index bc28051d1c..1841f818ae 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -473,30 +473,30 @@ def interpret(self, eigenstate_result: EigenstateResult) -> ElectronicStructureR 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_values is not None: + 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_values[0] is not None: - result.num_particles = result.aux_values[0][0].real + 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_values[1] is not None: - result.total_angular_momentum = result.aux_values[1][0].real + 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_values[2] is not None: - result.magnetization = result.aux_values[2][0].real + 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_values) >= 6 and self._has_dipole_moments: + 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_values[3:6]: + for moment in result.aux_operator_eigenvalues[3:6]: if moment is not None: dipole_moment += [moment[0].real] else: diff --git a/qiskit/chemistry/results/eigenstate_result.py b/qiskit/chemistry/results/eigenstate_result.py index b1cb402290..d8689ab4a3 100644 --- a/qiskit/chemistry/results/eigenstate_result.py +++ b/qiskit/chemistry/results/eigenstate_result.py @@ -31,14 +31,14 @@ def eigenvalue(self, value: complex) -> None: self.data['eigenvalue'] = value @property - def aux_values(self) -> Optional[List[float]]: + def aux_operator_eigenvalues(self) -> Optional[List[float]]: """ return aux operator eigen values """ - return self.get('aux_values') + return self.get('aux_operator_eigenvalues') - @aux_values.setter - def aux_values(self, value: List[float]) -> None: + @aux_operator_eigenvalues.setter + def aux_operator_eigenvalues(self, value: List[float]) -> None: """ set aux operator eigen values """ - self.data['aux_values'] = value + self.data['aux_operator_eigenvalues'] = value @property def raw_result(self) -> Optional[AlgorithmResult]: From fb916b4230604422acb598c1f9ba0c47087bfd7e Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 8 Oct 2020 17:39:26 +0200 Subject: [PATCH 103/197] Remove leftover from dict-style aux_operators --- .../qubit_transformations/fermionic_transformation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 1841f818ae..add462545a 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -314,8 +314,8 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ # add user specified auxiliary operators if aux_operators is not None: - for name, aux_op in aux_operators.items(): - _add_aux_op(aux_op, name) + for aux_op in aux_operators: + _add_aux_op(aux_op, aux_op.name) logger.info('Molecule num electrons: %s, remaining for processing: %s', [num_alpha, num_beta], new_nel) From e3fc416b8cf465e2a08c4c60ad447b5ded6f11b0 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 8 Oct 2020 17:40:47 +0200 Subject: [PATCH 104/197] Add an `evaluate_operators` method to the GroundStateCalculation This method can be used to evaluate any additional auxiliary operators after the GSC has finished. This will be necessary especially in the excited state calculations and is in general a nice option for the user to have. The method is similar to how the `VQE` evaluates auxiliary operators internally. However, it can also deal with `MinimumEigensolver`s which do not use a `QuantumInstance`. A curiousity arose because the `VQE` appears to wrap the auxiliary operator results into lists multiple times. In order to ensure interoperability the same is done here. The `AdaptVQE` now derives off of the `MinimumEigensolverGSC` because there was no obvious drawback to this and the benefit of reusing this new method was welcome. Unittests have been added to test the new functionality. --- .../ground_state_calculation/adapt_vqe.py | 10 +- .../ground_state_calculation.py | 31 ++++- .../mes_ground_state_calculation.py | 71 ++++++++++- test/chemistry/test_mes_gsc_calculation.py | 111 ++++++++++++++++++ 4 files changed, 214 insertions(+), 9 deletions(-) create mode 100644 test/chemistry/test_mes_gsc_calculation.py diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 18b5b1e8c1..245b30427f 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -28,12 +28,12 @@ from qiskit.aqua import AquaError from .mes_factories import VQEUCCSDFactory -from .ground_state_calculation import GroundStateCalculation +from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation logger = logging.getLogger(__name__) -class AdaptVQE(GroundStateCalculation): +class AdaptVQE(MinimumEigensolverGroundStateCalculation): """A ground state calculation employing the AdaptVQE algorithm.""" def __init__(self, @@ -55,9 +55,8 @@ def __init__(self, validate_min('threshold', threshold, 1e-15) validate_min('delta', delta, 1e-5) - super().__init__(transformation) + super().__init__(transformation, solver) - self._solver = solver self._threshold = threshold self._delta = delta self._max_iterations = max_iterations @@ -216,8 +215,7 @@ def compute_groundstate(self, driver: BaseDriver, # once finished evaluate auxiliary operators if any if aux_operators is not None: - aux_result = vqe.compute_minimum_eigenvalue(operator, aux_operators) - aux_values = aux_result.aux_operator_eigenvalues + aux_values = self.evaluate_operators(raw_vqe_result.eigenstate, aux_operators) else: aux_values = None diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index 10f1c2e2a8..3e9388eee5 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -13,8 +13,15 @@ """The ground state calculation interface.""" from abc import ABC, abstractmethod -from typing import List, Any, Optional +from typing import Dict, List, Any, 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 qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.results import EigenstateResult @@ -68,3 +75,25 @@ def returns_groundstate(self) -> bool: 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/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 19a4d2f9f5..e352d36c30 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -12,10 +12,16 @@ """Ground state computation using a minimum eigensolver.""" -from typing import Union, List, Any, Optional +from typing import Union, List, Any, 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 WeightedPauliOperator +from qiskit.aqua.operators import OperatorBase, WeightedPauliOperator, StateFn, CircuitSampler from qiskit.chemistry import FermionicOperator from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.ground_state_calculation import GroundStateCalculation @@ -103,3 +109,64 @@ def compute_groundstate(self, driver: BaseDriver, eigenstate_result.aux_operator_eigenvalues = raw_mes_result.aux_operator_eigenvalues result = self.transformation.interpret(eigenstate_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) + + 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 not isinstance(op, OperatorBase): + 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: + sampler = CircuitSampler(quantum_instance) + result = sampler.convert(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/test/chemistry/test_mes_gsc_calculation.py b/test/chemistry/test_mes_gsc_calculation.py new file mode 100644 index 0000000000..64d5ad3ea1 --- /dev/null +++ b/test/chemistry/test_mes_gsc_calculation.py @@ -0,0 +1,111 @@ +# 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.qubit_transformations import FermionicTransformation +from qiskit.chemistry.qubit_transformations.fermionic_transformation import QubitMappingType +from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.ground_state_calculation.mes_factories import VQEUCCSDFactory + + +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_vqe_uccsd(self): + """ Test VQE UCCSD case """ + solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + calc = MinimumEigensolverGroundStateCalculation(self.transformation, solver) + res = calc.compute_groundstate(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 = MinimumEigensolverGroundStateCalculation(self.transformation, solver) + res = calc.compute_groundstate(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 = [op for op in aux_ops if op.name.lower() == 'number of particles'][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 = [op for op in aux_ops if op.name.lower() in list(expected_results.keys())] + + # 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 = [op for op in aux_ops if op.name.lower() in list(expected_results.keys())] + # 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() From 842ca9c3080f0cb6807d7f64fcd16e37d6d392db Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Thu, 8 Oct 2020 14:26:03 -0400 Subject: [PATCH 105/197] fix lint --- .pylintdict | 1 + qiskit/chemistry/core/hamiltonian.py | 3 ++- test/chemistry/test_driver_methods_fcidump.py | 2 +- test/chemistry/test_driver_methods_gaussian.py | 2 +- test/chemistry/test_driver_methods_psi4.py | 2 +- test/chemistry/test_driver_methods_pyquante.py | 2 +- test/chemistry/test_driver_methods_pyscf.py | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.pylintdict b/.pylintdict index 1c389d0b4d..d8864f6769 100644 --- a/.pylintdict +++ b/.pylintdict @@ -54,6 +54,7 @@ bmod bobyqa bohr bool +bools boolean bosonic bpa diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index 8f7a828969..847be15fcb 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -117,7 +117,8 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - def run(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + 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 diff --git a/test/chemistry/test_driver_methods_fcidump.py b/test/chemistry/test_driver_methods_fcidump.py index 9edbeee64b..f2451bdb3b 100644 --- a/test/chemistry/test_driver_methods_fcidump.py +++ b/test/chemistry/test_driver_methods_fcidump.py @@ -15,7 +15,7 @@ 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 diff --git a/test/chemistry/test_driver_methods_gaussian.py b/test/chemistry/test_driver_methods_gaussian.py index 9601b510a3..167710e8d3 100644 --- a/test/chemistry/test_driver_methods_gaussian.py +++ b/test/chemistry/test_driver_methods_gaussian.py @@ -14,7 +14,7 @@ import unittest -from test.chemistry.test_driver_methods import TestDriverMethods +from test.chemistry.test_driver_methods_gsc import TestDriverMethods from qiskit.chemistry.drivers import GaussianDriver from qiskit.chemistry import QiskitChemistryError diff --git a/test/chemistry/test_driver_methods_psi4.py b/test/chemistry/test_driver_methods_psi4.py index 537af69f90..db52bb7e50 100644 --- a/test/chemistry/test_driver_methods_psi4.py +++ b/test/chemistry/test_driver_methods_psi4.py @@ -14,7 +14,7 @@ import unittest -from test.chemistry.test_driver_methods import TestDriverMethods +from test.chemistry.test_driver_methods_gsc import TestDriverMethods from qiskit.chemistry.drivers import PSI4Driver from qiskit.chemistry import QiskitChemistryError diff --git a/test/chemistry/test_driver_methods_pyquante.py b/test/chemistry/test_driver_methods_pyquante.py index 57cb787cda..d7dde77b0e 100644 --- a/test/chemistry/test_driver_methods_pyquante.py +++ b/test/chemistry/test_driver_methods_pyquante.py @@ -14,7 +14,7 @@ import unittest -from test.chemistry.test_driver_methods import TestDriverMethods +from test.chemistry.test_driver_methods_gsc import TestDriverMethods from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.drivers import PyQuanteDriver, UnitsType, BasisType, HFMethodType diff --git a/test/chemistry/test_driver_methods_pyscf.py b/test/chemistry/test_driver_methods_pyscf.py index 5b9a11c59f..5535b29ccc 100644 --- a/test/chemistry/test_driver_methods_pyscf.py +++ b/test/chemistry/test_driver_methods_pyscf.py @@ -14,7 +14,7 @@ import unittest -from test.chemistry.test_driver_methods import TestDriverMethods +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 From 783a0e46c1bc477e384327f7d7940adea971d609 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Fri, 9 Oct 2020 08:46:21 +0200 Subject: [PATCH 106/197] Remove erronous TODO --- qiskit/chemistry/core/chemistry_operator.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index 989e1213b9..26841248c3 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -166,9 +166,6 @@ class MolecularGroundStateResult(MolecularChemistryResult): Energies are in Hartree and dipole moments in A.U unless otherwise stated. """ - # 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) - def __init__(self, a_dict: Optional[Dict] = None) -> None: super().__init__(a_dict) warnings.warn('The qiskit.chemistry.chemistry_operator.MolecularGroundStateResult object ' From acdd705e169d4154133ea097936261a67d65c010 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Fri, 9 Oct 2020 08:49:39 +0200 Subject: [PATCH 107/197] Enforce a FermionicTransformation in the VQEUCCSDFactory --- .../mes_factories/vqe_uccsd_factory.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py index 85afa90d4f..cd406d6eac 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py @@ -15,7 +15,7 @@ from qiskit.aqua import QuantumInstance from qiskit.aqua.algorithms import MinimumEigensolver, VQE from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation +from qiskit.chemistry.qubit_transformations import FermionicTransformation from qiskit.chemistry.components.initial_states import HartreeFock from .mes_factory import MESFactory @@ -31,11 +31,12 @@ def __init__(self, quantum_instance: QuantumInstance) -> None: """ self._quantum_instance = quantum_instance - def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEigensolver: + def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensolver: """Returns a VQE with a UCCSD wavefunction ansatz, based on ``transformation``. + This works only with a ``FermionicTransformation``. Args: - transformation: The qubit operator transformation. + transformation: a fermionic qubit operator transformation. Returns: A VQE suitable to compute the ground state of the molecule transformed From 7ab4a39482fb51f5f7bf15cd8eb3a24d0794b615 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Fri, 9 Oct 2020 09:08:24 +0200 Subject: [PATCH 108/197] Do not enforce VQEUCCSDFactory type in AdaptVQE Since we check for the VQE and UCCSD types anyways we shouldn't force the user to subclass this factory. --- .../ground_state_calculation/adapt_vqe.py | 4 +- test/chemistry/test_adapt_vqe.py | 42 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 245b30427f..559fa7e08d 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -27,7 +27,7 @@ from qiskit.aqua.algorithms import VQE from qiskit.aqua import AquaError -from .mes_factories import VQEUCCSDFactory +from .mes_factories import MESFactory from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation logger = logging.getLogger(__name__) @@ -38,7 +38,7 @@ class AdaptVQE(MinimumEigensolverGroundStateCalculation): def __init__(self, transformation: FermionicTransformation, - solver: VQEUCCSDFactory, + solver: MESFactory, threshold: float = 1e-5, delta: float = 1, max_iterations: Optional[int] = None, diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index f0ca4a5d59..414be1092c 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -53,28 +53,28 @@ def test_default(self): def test_custom_minimum_eigensolver(self): """ Test custom MES """ - solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + class CustomFactory(VQEUCCSDFactory): + """A custom MESFactory""" - def get_custom_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 - - # pylint: disable=no-value-for-parameter - solver.get_solver = get_custom_solver.__get__(solver, VQEUCCSDFactory) + 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.compute_groundstate(self.driver) From 960ec9570c842efec9c01f0827f845a38c2ad9b7 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Fri, 9 Oct 2020 16:16:28 +0200 Subject: [PATCH 109/197] Reuse the VQE object during AdaptVQE --- .../ground_state_calculation/adapt_vqe.py | 36 +++++++++---------- test/chemistry/test_adapt_vqe.py | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 559fa7e08d..b0f3cc88a3 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -67,8 +67,7 @@ def returns_groundstate(self) -> bool: def _compute_gradients(self, excitation_pool: List[WeightedPauliOperator], theta: List[float], - var_form: UCCSD, - operator: LegacyBaseOperator, + vqe: VQE, ) -> List[Tuple[float, WeightedPauliOperator]]: """ Computes the gradients for all available excitation operators. @@ -76,8 +75,7 @@ def _compute_gradients(self, Args: excitation_pool: pool of excitation operators theta: list of (up to now) optimal parameters - var_form: current variational form - operator: system Hamiltonian + vqe: the variational quantum eigensolver instance used for solving Returns: List of pairs consisting of gradient and excitation operator. @@ -86,12 +84,14 @@ def _compute_gradients(self, # compute gradients for all excitation in operator pool for exc in excitation_pool: # push next excitation to variational form - var_form.push_hopping_operator(exc) - # construct auxiliary VQE instance - vqe = self._solver.get_solver(self._transformation) - vqe.operator = operator - vqe.var_form = var_form - vqe.initial_point = var_form.preferred_init_points + 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)) @@ -99,7 +99,7 @@ def _compute_gradients(self, gradient = (energy_results[0] - energy_results[1]) / (2 * self._delta) res.append((np.abs(gradient), exc)) # pop excitation from variational form - var_form.pop_hopping_operator() + vqe.var_form.pop_hopping_operator() return res @@ -152,14 +152,14 @@ def compute_groundstate(self, driver: BaseDriver, 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") - var_form = vqe.var_form - if not isinstance(var_form, UCCSD): + if not isinstance(vqe.var_form, UCCSD): raise AquaError("The AdaptVQE algorithm requires the use of the UCCSD variational form") - var_form.manage_hopping_operators() - excitation_pool = var_form.excitation_pool + vqe.var_form.manage_hopping_operators() + excitation_pool = vqe.var_form.excitation_pool threshold_satisfied = False alternating_sequence = False @@ -172,7 +172,8 @@ def compute_groundstate(self, driver: BaseDriver, iteration += 1 logger.info('--- Iteration #%s ---', str(iteration)) # compute gradients - cur_grads = self._compute_gradients(excitation_pool, theta, var_form, operator) + + 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])) @@ -200,10 +201,9 @@ def compute_groundstate(self, driver: BaseDriver, alternating_sequence = True break # add new excitation to self._var_form - var_form.push_hopping_operator(max_grad[1]) + vqe.var_form.push_hopping_operator(max_grad[1]) theta.append(0.0) # run VQE on current Ansatz - vqe.var_form = var_form vqe.initial_point = theta raw_vqe_result = vqe.compute_minimum_eigenvalue(operator) theta = raw_vqe_result.optimal_point.tolist() diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index 414be1092c..58a3dd66fd 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -82,7 +82,7 @@ def get_solver(self, transformation): def test_custom_excitation_pool(self): """ Test custom excitation pool """ - # TODO rewrite this unittest once we have reworked how AdaptVQE will handle the solver + class CustomFactory(VQEUCCSDFactory): """A custom MES factory.""" From 0f2807653c40e2f1829ddda4508db7af319a0033 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Fri, 9 Oct 2020 16:38:50 +0200 Subject: [PATCH 110/197] Expose molecule_info and qubit_mapping publicly --- .../mes_factories/vqe_uccsd_factory.py | 10 +-- .../fermionic_transformation.py | 10 +++ test/chemistry/test_adapt_vqe.py | 10 +-- .../test_core_hamiltonian_orb_reduce_gsc.py | 4 +- .../test_core_hamiltonian_symmetries_gsc.py | 4 +- .../test_fermionic_transformation.py | 4 +- .../test_initial_state_hartree_fock_gsc.py | 4 +- test/chemistry/test_swaprz_gsc.py | 6 +- test/chemistry/test_symmetries_gsc.py | 14 ++-- test/chemistry/test_uccsd_advanced_gsc.py | 66 +++++++++---------- test/chemistry/test_uccsd_hartree_fock_gsc.py | 12 ++-- 11 files changed, 77 insertions(+), 67 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py index cd406d6eac..ab9c8fedb0 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py @@ -42,12 +42,12 @@ def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensol 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'] - 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, diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index add462545a..6a414c145b 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -114,6 +114,16 @@ def __init__(self, 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[WeightedPauliOperator, List[WeightedPauliOperator]]: diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index 58a3dd66fd..db10d57551 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -57,11 +57,11 @@ 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'] + 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, diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py index bf811ad104..40db42ad84 100644 --- a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py +++ b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py @@ -44,9 +44,9 @@ def _validate_vars(self, fermionic_transformation, energy_shift=0.0, ph_energy_s 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') + z2symmetries = fermionic_transformation.molecule_info.pop('z2_symmetries') self.assertEqual(z2symmetries.is_empty(), True) - self.assertEqual(fermionic_transformation._molecule_info, + self.assertEqual(fermionic_transformation.molecule_info, {'num_particles': num_particles, 'num_orbitals': num_orbitals, 'two_qubit_reduction': actual_two_qubit_reduction}) diff --git a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py index 58b2d3c572..6c694be97f 100644 --- a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py +++ b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py @@ -214,8 +214,8 @@ def test_vqe_auto_symmetry_freeze_core(self): qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) - num_orbitals = fermionic_transformation._molecule_info['num_orbitals'] - num_particles = fermionic_transformation._molecule_info['num_particles'] + num_orbitals = fermionic_transformation.molecule_info['num_orbitals'] + num_particles = fermionic_transformation.molecule_info['num_particles'] qubit_mapping = 'jordan_wigner' two_qubit_reduction = fermionic_transformation._two_qubit_reduction z2_symmetries = qubit_op.z2_symmetries diff --git a/test/chemistry/test_fermionic_transformation.py b/test/chemistry/test_fermionic_transformation.py index c81bddcde5..1d6e8e3bd1 100644 --- a/test/chemistry/test_fermionic_transformation.py +++ b/test/chemistry/test_fermionic_transformation.py @@ -45,9 +45,9 @@ def _validate_vars(self, fermionic_transformation, energy_shift=0.0, ph_energy_s 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') + z2symmetries = fermionic_transformation.molecule_info.pop('z2_symmetries') self.assertEqual(z2symmetries.is_empty(), True) - self.assertEqual(fermionic_transformation._molecule_info, + self.assertEqual(fermionic_transformation.molecule_info, {'num_particles': num_particles, 'num_orbitals': num_orbitals, 'two_qubit_reduction': actual_two_qubit_reduction}) diff --git a/test/chemistry/test_initial_state_hartree_fock_gsc.py b/test/chemistry/test_initial_state_hartree_fock_gsc.py index 893ffee214..27d307e932 100644 --- a/test/chemistry/test_initial_state_hartree_fock_gsc.py +++ b/test/chemistry/test_initial_state_hartree_fock_gsc.py @@ -105,8 +105,8 @@ def test_hf_value(self, mapping): qubit_op, _ = fermionic_transformation.transform(driver) qubit_op = op_converter.to_matrix_operator(qubit_op) - hrfo = HartreeFock(fermionic_transformation._molecule_info['num_orbitals'], - fermionic_transformation._molecule_info['num_particles'], + 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') diff --git a/test/chemistry/test_swaprz_gsc.py b/test/chemistry/test_swaprz_gsc.py index 414603e335..7bf00798d1 100644 --- a/test/chemistry/test_swaprz_gsc.py +++ b/test/chemistry/test_swaprz_gsc.py @@ -52,9 +52,9 @@ def test_excitation_preserving(self): optimizer = SLSQP(maxiter=100) initial_state = HartreeFock( - fermionic_transformation._molecule_info['num_orbitals'], - fermionic_transformation._molecule_info['num_particles'], - qubit_mapping=fermionic_transformation._qubit_mapping, + 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) diff --git a/test/chemistry/test_symmetries_gsc.py b/test/chemistry/test_symmetries_gsc.py index 0f71fe1ed7..6f24f15b73 100644 --- a/test/chemistry/test_symmetries_gsc.py +++ b/test/chemistry/test_symmetries_gsc.py @@ -52,7 +52,7 @@ def setUp(self): self.qubit_op, _ = self.fermionic_transformation.transform(self.driver) - self.z2_symmetries = self.fermionic_transformation._molecule_info.pop('z2_symmetries') + self.z2_symmetries = self.fermionic_transformation.molecule_info.pop('z2_symmetries') self.reference_energy = -7.882096489442 @@ -79,19 +79,19 @@ def test_tapered_op(self): optimizer = SLSQP(maxiter=1000) init_state = HartreeFock( - num_orbitals=self.fermionic_transformation._molecule_info['num_orbitals'], - qubit_mapping=self.fermionic_transformation._qubit_mapping, + 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'], + num_particles=self.fermionic_transformation.molecule_info['num_particles'], sq_list=self.qubit_op.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'], + 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, + qubit_mapping=self.fermionic_transformation.qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=self.qubit_op.z2_symmetries) diff --git a/test/chemistry/test_uccsd_advanced_gsc.py b/test/chemistry/test_uccsd_advanced_gsc.py index f849ed6461..b5f3540442 100644 --- a/test/chemistry/test_uccsd_advanced_gsc.py +++ b/test/chemistry/test_uccsd_advanced_gsc.py @@ -90,17 +90,17 @@ def test_uccsd_hf_qpUCCD(self): optimizer = SLSQP(maxiter=100) initial_state = HartreeFock( - self.fermionic_transformation._molecule_info['num_orbitals'], - self.fermionic_transformation._molecule_info['num_particles'], - qubit_mapping=self.fermionic_transformation._qubit_mapping, + 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'], + 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, + qubit_mapping=self.fermionic_transformation.qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, shallow_circuit_concat=False, @@ -123,17 +123,17 @@ def test_uccsd_hf_qUCCD0(self): optimizer = SLSQP(maxiter=100) initial_state = HartreeFock( - self.fermionic_transformation._molecule_info['num_orbitals'], - self.fermionic_transformation._molecule_info['num_particles'], - qubit_mapping=self.fermionic_transformation._qubit_mapping, + 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'], + 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, + qubit_mapping=self.fermionic_transformation.qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, shallow_circuit_concat=False, @@ -157,17 +157,17 @@ def test_uccsd_hf_qUCCD0full(self): optimizer = SLSQP(maxiter=100) initial_state = HartreeFock( - self.fermionic_transformation._molecule_info['num_orbitals'], - self.fermionic_transformation._molecule_info['num_particles'], - qubit_mapping=self.fermionic_transformation._qubit_mapping, + 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'], + 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, + qubit_mapping=self.fermionic_transformation.qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, shallow_circuit_concat=False, @@ -204,18 +204,18 @@ def test_uccsd_hf_qUCCSD(self): # initial state init_state = HartreeFock( - num_orbitals=fermionic_transformation._molecule_info['num_orbitals'], - qubit_mapping=fermionic_transformation._qubit_mapping, + 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'], + num_particles=fermionic_transformation.molecule_info['num_particles'], sq_list=qubit_op.z2_symmetries.sq_list) var_form = UCCSD( - num_orbitals=fermionic_transformation._molecule_info['num_orbitals'], - num_particles=fermionic_transformation._molecule_info['num_particles'], + 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, + qubit_mapping=fermionic_transformation.qubit_mapping, two_qubit_reduction=fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=qubit_op.z2_symmetries, @@ -239,19 +239,19 @@ def test_uccsd_hf_excitations(self): # initial state init_state = HartreeFock( - num_orbitals=self.fermionic_transformation._molecule_info['num_orbitals'], - qubit_mapping=self.fermionic_transformation._qubit_mapping, + 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'], + num_particles=self.fermionic_transformation.molecule_info['num_particles'], sq_list=self.the_tapered_op.z2_symmetries.sq_list) # check singlet excitations var_form = UCCSD( - num_orbitals=self.fermionic_transformation._molecule_info['num_orbitals'], - num_particles=self.fermionic_transformation._molecule_info['num_particles'], + 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, + qubit_mapping=self.fermionic_transformation.qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=self.the_tapered_op.z2_symmetries, @@ -267,11 +267,11 @@ def test_uccsd_hf_excitations(self): # check grouped singlet excitations var_form = UCCSD( - num_orbitals=self.fermionic_transformation._molecule_info['num_orbitals'], - num_particles=self.fermionic_transformation._molecule_info['num_particles'], + 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, + qubit_mapping=self.fermionic_transformation.qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=self.the_tapered_op.z2_symmetries, diff --git a/test/chemistry/test_uccsd_hartree_fock_gsc.py b/test/chemistry/test_uccsd_hartree_fock_gsc.py index 1f42acf202..a15356a163 100644 --- a/test/chemistry/test_uccsd_hartree_fock_gsc.py +++ b/test/chemistry/test_uccsd_hartree_fock_gsc.py @@ -47,15 +47,15 @@ def setUp(self): self.optimizer = SLSQP(maxiter=100) initial_state = HartreeFock( - fermionic_transformation._molecule_info['num_orbitals'], - fermionic_transformation._molecule_info['num_particles'], - qubit_mapping=fermionic_transformation._qubit_mapping, + 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'], + 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, + qubit_mapping=fermionic_transformation.qubit_mapping, two_qubit_reduction=fermionic_transformation._two_qubit_reduction) def test_uccsd_hf(self): From 9cf03278bcba5cf3dff201c2887eac5a2d18c786 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Fri, 9 Oct 2020 12:04:40 -0400 Subject: [PATCH 111/197] fix lint --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index b0f3cc88a3..2019b9d0b6 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -23,7 +23,7 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry import FermionicOperator from qiskit.aqua.utils.validation import validate_min -from qiskit.aqua.operators import LegacyBaseOperator, WeightedPauliOperator +from qiskit.aqua.operators import WeightedPauliOperator from qiskit.aqua.algorithms import VQE from qiskit.aqua import AquaError From 14444745a5db5f8288deb4134a703af9bcbd21e9 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 12 Oct 2020 14:25:43 +0200 Subject: [PATCH 112/197] fixes in tests --- .../test_initial_state_hartree_fock.py | 117 ------- test/chemistry/test_uccsd_advanced.py | 331 ------------------ test/chemistry/test_uccsd_hartree_fock.py | 191 ---------- 3 files changed, 639 deletions(-) delete mode 100644 test/chemistry/test_initial_state_hartree_fock.py delete mode 100644 test/chemistry/test_uccsd_advanced.py delete mode 100644 test/chemistry/test_uccsd_hartree_fock.py diff --git a/test/chemistry/test_initial_state_hartree_fock.py b/test/chemistry/test_initial_state_hartree_fock.py deleted file mode 100644 index aae7bbaec7..0000000000 --- a/test/chemistry/test_initial_state_hartree_fock.py +++ /dev/null @@ -1,117 +0,0 @@ -# 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 Initial State HartreeFock """ - -import warnings -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.chemistry import QiskitChemistryError -from qiskit.chemistry.drivers import PySCFDriver, UnitsType -from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType - - -@ddt -class TestInitialStateHartreeFock(QiskitChemistryTestCase): - """ Initial State HartreeFock tests """ - - def test_qubits_4_jw_h2(self): - """ qubits 4 jw h2 test """ - hrfo = HartreeFock(4, [1, 1], 'jordan_wigner', False) - cct = hrfo.construct_circuit('vector') - np.testing.assert_array_equal(cct, [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) - - def test_qubits_4_py_h2(self): - """ qubits 4 py h2 test """ - hrfo = HartreeFock(4, [1, 1], 'parity', False) - cct = hrfo.construct_circuit('vector') - np.testing.assert_array_equal(cct, [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) - - def test_qubits_4_bk_h2(self): - """ qubits 4 bk h2 test """ - hrfo = HartreeFock(4, [1, 1], 'bravyi_kitaev', False) - cct = hrfo.construct_circuit('vector') - np.testing.assert_array_equal(cct, [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) - - def test_qubits_2_py_h2(self): - """ qubits 2 py h2 test """ - hrfo = HartreeFock(4, 2, 'parity', True) - cct = hrfo.construct_circuit('vector') - np.testing.assert_array_equal(cct, [0.0, 1.0, 0.0, 0.0]) - - def test_qubits_2_py_h2_cct(self): - """ qubits 2 py h2 cct test """ - hrfo = HartreeFock(4, [1, 1], 'parity', True) - cct = hrfo.construct_circuit('circuit') - self.assertEqual(cct.qasm(), 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[2];\n' - 'x q[0];\n') - - def test_qubits_6_py_lih_cct(self): - """ qubits 6 py lih cct test """ - hrfo = HartreeFock(10, [1, 1], 'parity', True, [1, 2]) - cct = hrfo.construct_circuit('circuit') - self.assertEqual(cct.qasm(), 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[6];\n' - 'x q[0];\n' - 'x q[1];\n') - - def test_qubits_10_bk_lih_bitstr(self): - """ qubits 10 bk lih bitstr test """ - hrfo = HartreeFock(10, [1, 1], 'bravyi_kitaev', False) - bitstr = hrfo.bitstr - np.testing.assert_array_equal(bitstr, - [False, False, False, False, True, - False, True, False, True, True]) - - @idata([ - [QubitMappingType.JORDAN_WIGNER], - [QubitMappingType.PARITY], - [QubitMappingType.BRAVYI_KITAEV] - ]) - @unpack - def test_hf_value(self, mapping): - """ hf value test """ - try: - 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') - qmolecule = driver.run() - warnings.filterwarnings('ignore', category=DeprecationWarning) - core = Hamiltonian(transformation=TransformationType.FULL, - qubit_mapping=mapping, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) - - 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) - qc = hrfo.construct_circuit('vector') - hf_energy = qubit_op.evaluate_with_statevector(qc)[0].real + core._nuclear_repulsion_energy - - self.assertAlmostEqual(qmolecule.hf_energy, hf_energy, places=8) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/chemistry/test_uccsd_advanced.py b/test/chemistry/test_uccsd_advanced.py deleted file mode 100644 index ef171809f8..0000000000 --- a/test/chemistry/test_uccsd_advanced.py +++ /dev/null @@ -1,331 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 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 UCCSD and HartreeFock Aqua extensions """ - -import warnings -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.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 - -# pylint: disable=invalid-name - - -class TestUCCSDHartreeFock(QiskitChemistryTestCase): - """Test for these aqua extensions.""" - - def setUp(self): - super().setUp() - try: - self.molecule = "H 0.000000 0.000000 0.735000;H 0.000000 0.000000 0.000000" - self.driver = PySCFDriver(atom=self.molecule, - unit=UnitsType.ANGSTROM, - charge=0, - spin=0, - basis='631g') - self.qmolecule = self.driver.run() - warnings.filterwarnings('ignore', category=DeprecationWarning) - self.core = Hamiltonian(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=[]) - warnings.filterwarnings('always', category=DeprecationWarning) - 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.reference_energy_pUCCD = -1.1434447924298028 - self.reference_energy_UCCD0 = -1.1476045878481704 - self.reference_energy_UCCD0full = -1.1515491334334347 - # reference energy of UCCSD/VQE with tapering everywhere - self.reference_energy_UCCSD = -1.1516142309717594 - # reference energy of UCCSD/VQE when no tapering on excitations is used - self.reference_energy_UCCSD_no_tap_exc = -1.1516142309717594 - # excitations for succ - self.reference_singlet_double_excitations = [[0, 1, 4, 5], [0, 1, 4, 6], [0, 1, 4, 7], - [0, 2, 4, 6], [0, 2, 4, 7], [0, 3, 4, 7]] - # groups for succ_full - self.reference_singlet_groups = [[[0, 1, 4, 5]], [[0, 1, 4, 6], [0, 2, 4, 5]], - [[0, 1, 4, 7], [0, 3, 4, 5]], [[0, 2, 4, 6]], - [[0, 2, 4, 7], [0, 3, 4, 6]], [[0, 3, 4, 7]]] - except QiskitChemistryError: - self.skipTest('PYSCF driver does not appear to be installed') - - 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'))) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - 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'))) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0, places=6) - - def test_uccsd_hf_qUCCD0full(self): - """ singlet full 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_full', - excitation_type='d' - ) - - algo = VQE(self.qubit_op, var_form, optimizer) - result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0full, places=6) - - def test_uccsd_hf_qUCCSD(self): - """ uccsd tapering test using all double excitations """ - - # 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'))) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - 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) - - # 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) - - double_excitations_singlet = var_form._double_excitations - res = TestUCCSDHartreeFock.excitation_lists_comparator( - double_excitations_singlet, self.reference_singlet_double_excitations) - 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) - - double_excitations_singlet_grouped = var_form._double_excitations_grouped - res_groups = TestUCCSDHartreeFock.group_excitation_lists_comparator( - double_excitations_singlet_grouped, self.reference_singlet_groups) - self.assertEqual(res_groups, True) - - @staticmethod - def pop_el_when_matched(list1, list2): - """ - Compares if in list1 and list2 one of excitations is the same (regardless of permutations of - its elements). When same excitation is found, it returns the 2 lists without that excitation - . - - Args: - list1 (list): list of excitations (e.g. [[0, 2, 4, 6], [0, 2, 4, 7]]) - list2 (list): list of excitations - - Returns: - list: list1 with one popped element if match was found - list: list2 with one popped element if match was found - """ - counter = 0 - for i, exc1 in enumerate(list1): - for j, exc2 in enumerate(list2): - for ind1 in exc1: - for ind2 in exc2: - if ind1 == ind2: - counter += 1 - if counter == len(exc1) and counter == len(exc2): - list1.pop(i) - list2.pop(j) - break - break - return list1, list2 - - @staticmethod - def excitation_lists_comparator(list1, list2): - """ - Compares if list1 and list2 contain same excitations (regardless of permutations of - its elements). Only works provided all indices for an excitation are different. - - Args: - list1 (list): list of excitations (e.g. [[0, 2, 4, 6], [0, 2, 4, 7]]) - list2 (list): list of excitations - - Returns: - bool: True or False, if list1 and list2 contain the same excitations - """ - if len(list1) != len(list2): - return False - - number_el = len(list1) - - for _ in range(number_el): - list1, list2 = TestUCCSDHartreeFock.pop_el_when_matched(list1, list2) - - return bool(len(list1) or len(list2) in [0]) - - @staticmethod - def group_excitation_lists_comparator(glist1, glist2): - """ - Compares if list1 and list2 contain same excitations (regardless of permutations of - its elements). Only works provided all indices for an excitation are different. - - Args: - glist1 (list): list of excitations (e.g. [[0, 2, 4, 6], [0, 2, 4, 7]]) - glist2 (list): list of excitations - - Returns: - bool: True or False, if list1 and list2 contain the same excitations - """ - if len(glist1) != len(glist2): - return False - - number_groups = len(glist1) - counter = 0 - for _, gr1 in enumerate(glist1): - for _, gr2 in enumerate(glist2): - res = TestUCCSDHartreeFock.excitation_lists_comparator(gr1, gr2) - if res is True: - 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 deleted file mode 100644 index 8f0c167a7c..0000000000 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ /dev/null @@ -1,191 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 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 UCCSD and HartreeFock Aqua extensions """ - -import warnings -import unittest - -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 -from qiskit.aqua.components.optimizers import SLSQP, SPSA -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.drivers import HDF5Driver -from qiskit.chemistry.core import Hamiltonian, QubitMappingType - - -@ddt -class TestUCCSDHartreeFock(QiskitChemistryTestCase): - """Test for these aqua extensions.""" - - def setUp(self): - super().setUp() - self.reference_energy = -1.1373060356951838 - - self.seed = 700 - aqua_globals.random_seed = self.seed - - driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) - qmolecule = driver.run() - warnings.filterwarnings('ignore', category=DeprecationWarning) - core = Hamiltonian(qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True) - warnings.filterwarnings('always', category=DeprecationWarning) - self.qubit_op, _ = core.run(qmolecule) - self.core = core - - 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) - - 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)) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - 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)) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - self.assertAlmostEqual(result.energy, -1.138, places=2) - - def test_uccsd_hf_aer_statevector(self): - """ uccsd hf test with Aer statevector """ - try: - # pylint: disable=import-outside-toplevel - from qiskit import Aer - except Exception as ex: # pylint: disable=broad-except - 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)) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - self.assertAlmostEqual(result.energy, self.reference_energy, places=6) - - def test_uccsd_hf_aer_qasm(self): - """ uccsd hf test with Aer qasm_simulator. """ - try: - # pylint: disable=import-outside-toplevel - from qiskit import Aer - except Exception as ex: # pylint: disable=broad-except - self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) - 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)) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - self.assertAlmostEqual(result.energy, -1.138, places=2) - - def test_uccsd_hf_aer_qasm_snapshot(self): - """ uccsd hf test with Aer qasm_simulator snapshot. """ - try: - # pylint: disable=import-outside-toplevel - from qiskit import Aer - except Exception as ex: # pylint: disable=broad-except - 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)) - warnings.filterwarnings('ignore', category=DeprecationWarning) - result = self.core.process_algorithm_result(result) - warnings.filterwarnings('always', category=DeprecationWarning) - self.assertAlmostEqual(result.energy, self.reference_energy, places=6) - - EXCITATION_RESULTS = \ - [[[[0, 1], [0, 2], [3, 4], [3, 5]], - [[0, 1, 3, 4], [0, 1, 3, 5], [0, 2, 3, 4], [0, 2, 3, 5]]], # 0 full: 6 orbs, 2 particles - [[[0, 2], [3, 5]], [[0, 2, 3, 5]]], # 1 limited active space - [[[0, 1], [0, 2], [3, 4], [3, 5]], []], # 2 singles only - [[], [[0, 1, 3, 4], [0, 1, 3, 5], [0, 2, 3, 4], [0, 2, 3, 5]]], # 3 doubles only - [[[0, 1], [3, 4]], []], # 4 singles only limited active space - [[[0, 2], [1, 2], [3, 5], [4, 5]], - [[0, 2, 3, 5], [0, 2, 4, 5], [1, 2, 3, 5], [1, 2, 4, 5]]], # 5 full: 6 orbs, 4 particles - [[[1, 2], [4, 5]], [[1, 2, 4, 5]]], # 6 limited active space - [[[0, 2], [0, 3], [1, 2], [1, 3], [4, 6], [4, 7], [5, 6], [5, 7]], # 7 - [[0, 2, 4, 6], [0, 2, 4, 7], [0, 2, 5, 6], [0, 2, 5, 7], [0, 3, 4, 6], [0, 3, 4, 7], - [0, 3, 5, 6], [0, 3, 5, 7], [1, 2, 4, 6], [1, 2, 4, 7], [1, 2, 5, 6], [1, 2, 5, 7], - [1, 3, 4, 6], [1, 3, 4, 7], [1, 3, 5, 6], [1, 3, 5, 7], [0, 2, 1, 3], [4, 6, 5, 7]]], - [[[0, 2], [0, 3], [1, 2], [1, 3], [4, 6], [4, 7], [5, 6], [5, 7]], # 8 No same spins - [[0, 2, 4, 6], [0, 2, 4, 7], [0, 2, 5, 6], [0, 2, 5, 7], [0, 3, 4, 6], [0, 3, 4, 7], - [0, 3, 5, 6], [0, 3, 5, 7], [1, 2, 4, 6], [1, 2, 4, 7], [1, 2, 5, 6], [1, 2, 5, 7], - [1, 3, 4, 6], [1, 3, 4, 7], [1, 3, 5, 6], [1, 3, 5, 7]]], - ] - - @idata([[0, 6, 2], - [0, 6, 2, [0], [0, 1]], # Full active space - [1, 6, 2, [0], [1]], # Restrict active space - [0, 6, 2, [0], [0, 1], False], - [2, 6, 2, None, None, True, 'both', 'ucc', 's'], - [3, 6, 2, None, [0, 1], True, 'both', 'ucc', 'd'], - [4, 6, 2, [0], [0], False, 'both', 'ucc', 's'], - [5, 6, 4], - [5, 6, 4, [0, 1], [0]], # Full active space - [6, 6, 4, [1], [0]], # Restrict active space - [7, 8, 4], - [8, 8, 4, None, None, False], - ]) - @unpack - def test_uccsd_excitations(self, expected_result_idx, num_orbitals, num_particles, - active_occupied=None, active_unoccupied=None, - same_spin_doubles=True, - method_singles='both', method_doubles='ucc', - excitation_type='sd' - ): - """ Test generated excitation lists in conjunction with active space """ - - excitations = UCCSD.compute_excitation_lists( - num_orbitals=num_orbitals, num_particles=num_particles, - active_occ_list=active_occupied, active_unocc_list=active_unoccupied, - same_spin_doubles=same_spin_doubles, - method_singles=method_singles, method_doubles=method_doubles, - excitation_type=excitation_type) - - self.assertListEqual(list(excitations), self.EXCITATION_RESULTS[expected_result_idx]) - - -if __name__ == '__main__': - unittest.main() From b0e877b8d62a7ee42e602d0c5e2805efb2781d01 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 12 Oct 2020 14:38:23 +0200 Subject: [PATCH 113/197] fixes in tests and revert to molecule_methods --- ...est_fermionic_transformation_orb_reduce.py | 149 ++++++++++++++++++ ....py => test_initial_state_hartree_fock.py} | 0 .../{test_swaprz_gsc.py => test_swaprz.py} | 2 +- ...t_symmetries_gsc.py => test_symmetries.py} | 4 +- ...advanced_gsc.py => test_uccsd_advanced.py} | 22 +-- ...fock_gsc.py => test_uccsd_hartree_fock.py} | 4 +- 6 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 test/chemistry/test_fermionic_transformation_orb_reduce.py rename test/chemistry/{test_initial_state_hartree_fock_gsc.py => test_initial_state_hartree_fock.py} (100%) rename test/chemistry/{test_swaprz_gsc.py => test_swaprz.py} (97%) rename test/chemistry/{test_symmetries_gsc.py => test_symmetries.py} (96%) rename test/chemistry/{test_uccsd_advanced_gsc.py => test_uccsd_advanced.py} (95%) rename test/chemistry/{test_uccsd_hartree_fock_gsc.py => test_uccsd_hartree_fock.py} (98%) 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..40db42ad84 --- /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 WeightedPauliOperator +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_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, WeightedPauliOperator)) + self.assertIsNotNone(qubit_op) + self.assertEqual(qubit_op.num_qubits, num_qubits) + self.assertEqual(len(qubit_op.to_dict()['paulis']), 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_gsc.py b/test/chemistry/test_initial_state_hartree_fock.py similarity index 100% rename from test/chemistry/test_initial_state_hartree_fock_gsc.py rename to test/chemistry/test_initial_state_hartree_fock.py diff --git a/test/chemistry/test_swaprz_gsc.py b/test/chemistry/test_swaprz.py similarity index 97% rename from test/chemistry/test_swaprz_gsc.py rename to test/chemistry/test_swaprz.py index 7bf00798d1..8046276bd9 100644 --- a/test/chemistry/test_swaprz_gsc.py +++ b/test/chemistry/test_swaprz.py @@ -54,7 +54,7 @@ def test_excitation_preserving(self): initial_state = HartreeFock( fermionic_transformation.molecule_info['num_orbitals'], fermionic_transformation.molecule_info['num_particles'], - qubit_mapping=fermionic_transformation.qubit_mapping, + qubit_mapping=fermionic_transformation._qubit_mapping, two_qubit_reduction=fermionic_transformation._two_qubit_reduction) wavefunction = ExcitationPreserving(qubit_op.num_qubits, initial_state=initial_state) diff --git a/test/chemistry/test_symmetries_gsc.py b/test/chemistry/test_symmetries.py similarity index 96% rename from test/chemistry/test_symmetries_gsc.py rename to test/chemistry/test_symmetries.py index 6f24f15b73..de9b84289d 100644 --- a/test/chemistry/test_symmetries_gsc.py +++ b/test/chemistry/test_symmetries.py @@ -80,7 +80,7 @@ def test_tapered_op(self): optimizer = SLSQP(maxiter=1000) init_state = HartreeFock( num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], - qubit_mapping=self.fermionic_transformation.qubit_mapping, + 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.qubit_op.z2_symmetries.sq_list) @@ -91,7 +91,7 @@ def test_tapered_op(self): active_occupied=None, active_unoccupied=None, initial_state=init_state, - qubit_mapping=self.fermionic_transformation.qubit_mapping, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=self.qubit_op.z2_symmetries) diff --git a/test/chemistry/test_uccsd_advanced_gsc.py b/test/chemistry/test_uccsd_advanced.py similarity index 95% rename from test/chemistry/test_uccsd_advanced_gsc.py rename to test/chemistry/test_uccsd_advanced.py index b5f3540442..7d45e10c45 100644 --- a/test/chemistry/test_uccsd_advanced_gsc.py +++ b/test/chemistry/test_uccsd_advanced.py @@ -92,7 +92,7 @@ def test_uccsd_hf_qpUCCD(self): initial_state = HartreeFock( self.fermionic_transformation.molecule_info['num_orbitals'], self.fermionic_transformation.molecule_info['num_particles'], - qubit_mapping=self.fermionic_transformation.qubit_mapping, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction) var_form = UCCSD( @@ -100,7 +100,7 @@ def test_uccsd_hf_qpUCCD(self): 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, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, shallow_circuit_concat=False, @@ -125,7 +125,7 @@ def test_uccsd_hf_qUCCD0(self): initial_state = HartreeFock( self.fermionic_transformation.molecule_info['num_orbitals'], self.fermionic_transformation.molecule_info['num_particles'], - qubit_mapping=self.fermionic_transformation.qubit_mapping, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction) var_form = UCCSD( @@ -133,7 +133,7 @@ def test_uccsd_hf_qUCCD0(self): 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, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, shallow_circuit_concat=False, @@ -159,7 +159,7 @@ def test_uccsd_hf_qUCCD0full(self): initial_state = HartreeFock( self.fermionic_transformation.molecule_info['num_orbitals'], self.fermionic_transformation.molecule_info['num_particles'], - qubit_mapping=self.fermionic_transformation.qubit_mapping, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction) var_form = UCCSD( @@ -167,7 +167,7 @@ def test_uccsd_hf_qUCCD0full(self): 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, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, shallow_circuit_concat=False, @@ -205,7 +205,7 @@ def test_uccsd_hf_qUCCSD(self): # initial state init_state = HartreeFock( num_orbitals=fermionic_transformation.molecule_info['num_orbitals'], - qubit_mapping=fermionic_transformation.qubit_mapping, + qubit_mapping=fermionic_transformation._qubit_mapping, two_qubit_reduction=fermionic_transformation._two_qubit_reduction, num_particles=fermionic_transformation.molecule_info['num_particles'], sq_list=qubit_op.z2_symmetries.sq_list) @@ -215,7 +215,7 @@ def test_uccsd_hf_qUCCSD(self): num_particles=fermionic_transformation.molecule_info['num_particles'], active_occupied=None, active_unoccupied=None, initial_state=init_state, - qubit_mapping=fermionic_transformation.qubit_mapping, + qubit_mapping=fermionic_transformation._qubit_mapping, two_qubit_reduction=fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=qubit_op.z2_symmetries, @@ -240,7 +240,7 @@ def test_uccsd_hf_excitations(self): # initial state init_state = HartreeFock( num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], - qubit_mapping=self.fermionic_transformation.qubit_mapping, + 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.the_tapered_op.z2_symmetries.sq_list) @@ -251,7 +251,7 @@ def test_uccsd_hf_excitations(self): 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, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=self.the_tapered_op.z2_symmetries, @@ -271,7 +271,7 @@ def test_uccsd_hf_excitations(self): 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, + qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, z2_symmetries=self.the_tapered_op.z2_symmetries, diff --git a/test/chemistry/test_uccsd_hartree_fock_gsc.py b/test/chemistry/test_uccsd_hartree_fock.py similarity index 98% rename from test/chemistry/test_uccsd_hartree_fock_gsc.py rename to test/chemistry/test_uccsd_hartree_fock.py index a15356a163..80e297bef0 100644 --- a/test/chemistry/test_uccsd_hartree_fock_gsc.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -49,13 +49,13 @@ def setUp(self): initial_state = HartreeFock( fermionic_transformation.molecule_info['num_orbitals'], fermionic_transformation.molecule_info['num_particles'], - qubit_mapping=fermionic_transformation.qubit_mapping, + 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, + qubit_mapping=fermionic_transformation._qubit_mapping, two_qubit_reduction=fermionic_transformation._two_qubit_reduction) def test_uccsd_hf(self): From 26d0105cfeba038b05403937a425aa5062f66729 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Mon, 12 Oct 2020 14:41:08 +0200 Subject: [PATCH 114/197] remove two duplicate tests --- .../test_core_hamiltonian_orb_reduce_gsc.py | 149 ----------- .../test_core_hamiltonian_symmetries_gsc.py | 240 ------------------ 2 files changed, 389 deletions(-) delete mode 100644 test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py delete mode 100644 test/chemistry/test_core_hamiltonian_symmetries_gsc.py diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py b/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py deleted file mode 100644 index 40db42ad84..0000000000 --- a/test/chemistry/test_core_hamiltonian_orb_reduce_gsc.py +++ /dev/null @@ -1,149 +0,0 @@ -# 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 WeightedPauliOperator -from qiskit.chemistry.drivers import PySCFDriver, UnitsType -from qiskit.chemistry import QiskitChemistryError -from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.qubit_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, WeightedPauliOperator)) - self.assertIsNotNone(qubit_op) - self.assertEqual(qubit_op.num_qubits, num_qubits) - self.assertEqual(len(qubit_op.to_dict()['paulis']), 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_core_hamiltonian_symmetries_gsc.py b/test/chemistry/test_core_hamiltonian_symmetries_gsc.py deleted file mode 100644 index 6c694be97f..0000000000 --- a/test/chemistry/test_core_hamiltonian_symmetries_gsc.py +++ /dev/null @@ -1,240 +0,0 @@ -# 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 Core Hamiltonian Symmetry Reduction """ - -import unittest -from test.chemistry import QiskitChemistryTestCase -import numpy as np - -from qiskit import BasicAer -from qiskit.aqua.algorithms import NumPyMinimumEigensolver, VQE -from qiskit.aqua.components.optimizers import SLSQP -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 import QiskitChemistryError -from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation - - -class TestFermionicTransforationSymmetries(QiskitChemistryTestCase): - """ Core hamiltonian Driver symmetry 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_result(self, result, symm=True): - self.assertAlmostEqual(result.energy, -7.882324378883, places=3) - ref_dipole = (0.0, 0.0, -1.81741795) - if not symm: - np.testing.assert_almost_equal(result.dipole_moment, ref_dipole, decimal=2) - else: - self.assertIsNone(result.dipole_moment[0]) - self.assertIsNone(result.dipole_moment[1]) - self.assertAlmostEqual(result.dipole_moment[2], ref_dipole[2], places=2) - - def test_no_symmetry(self): - """ No symmetry reduction """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=None) - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 12) - solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) - self._validate_result(result, False) - - def test_auto_symmetry(self): - """ Auto symmetry reduction """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction='auto') - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 8) - solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) - - def test_given_symmetry(self): - """ Supplied symmetry reduction """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 1, 1, 1]) - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 8) - solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) - - def test_given_symmetry_fail_len(self): - """ Supplied symmetry reduction invalid len """ - with self.assertRaises(QiskitChemistryError): - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 1, 1]) - - _, _ = fermionic_transformation.transform(self.driver) - - def test_given_symmetry_fail_values(self): - """ Supplied symmetry reduction invalid values """ - with self.assertRaises(QiskitChemistryError): - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 0, 1, 1]) - - _, _ = fermionic_transformation.transform(self.driver) - - def test_auto_symmetry_freeze_core(self): - """ Auto symmetry reduction, with freeze core """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) - - def test_auto_freeze_core_parity(self): - """ Auto symmetry reduction, with freeze core and parity mapping """ - - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.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 """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) - - def test_auto_ph_freeze_core_parity_2(self): - """ Auto symmetry reduction, with freeze core, parity and two q reduction """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.PARTICLE_HOLE, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) - - def test_vqe_auto_symmetry_freeze_core(self): - """ Auto symmetry reduction, with freeze core using VQE """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - num_orbitals = fermionic_transformation.molecule_info['num_orbitals'] - num_particles = fermionic_transformation.molecule_info['num_particles'] - qubit_mapping = 'jordan_wigner' - two_qubit_reduction = fermionic_transformation._two_qubit_reduction - z2_symmetries = qubit_op.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) - - solver = VQE(var_form=var_form, optimizer=SLSQP(maxiter=500), - quantum_instance=BasicAer.get_backend('statevector_simulator')) - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) - - -if __name__ == '__main__': - unittest.main() From 97a39336944a7eea1a2dae27ca375f505fa61eab Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Mon, 12 Oct 2020 16:42:56 +0200 Subject: [PATCH 115/197] Expose some VQE arguments in the VQEUCCSDFactory As discussed, we now expose some of the VQE arguments through this factory in order to allow greater flexibility for the user. --- .../mes_factories/vqe_uccsd_factory.py | 89 ++++++++++++++++++- .../mes_ground_state_calculation.py | 10 ++- qiskit/chemistry/results/eigenstate_result.py | 8 ++ test/chemistry/test_adapt_vqe.py | 2 + 4 files changed, 105 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py index ab9c8fedb0..1dce4ad022 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py @@ -12,8 +12,13 @@ """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 qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.qubit_transformations import FermionicTransformation from qiskit.chemistry.components.initial_states import HartreeFock @@ -24,12 +29,87 @@ class VQEUCCSDFactory(MESFactory): """A factory to construct a VQE minimum eigensolver with UCCSD ansatz wavefunction.""" - def __init__(self, quantum_instance: QuantumInstance) -> None: + 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._optimizer + + @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, transformation: FermionicTransformation) -> MinimumEigensolver: """Returns a VQE with a UCCSD wavefunction ansatz, based on ``transformation``. @@ -56,5 +136,10 @@ def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensol 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) + 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 diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index e352d36c30..c61bd01c85 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -162,8 +162,14 @@ def _eval_op(self, state, op, quantum_instance): exp = ~StateFn(op) @ state # if quantum_instance is not None: - sampler = CircuitSampler(quantum_instance) - result = sampler.convert(exp).eval() + 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() diff --git a/qiskit/chemistry/results/eigenstate_result.py b/qiskit/chemistry/results/eigenstate_result.py index d8689ab4a3..f5605eb99b 100644 --- a/qiskit/chemistry/results/eigenstate_result.py +++ b/qiskit/chemistry/results/eigenstate_result.py @@ -30,6 +30,14 @@ def eigenvalue(self, value: complex) -> None: """ set eigen value """ self.data['eigenvalue'] = value + # @eigenstates.setter + + # @eigenenergies.setter + + # @groundenergy.setter + + # @groundstates.setter + @property def aux_operator_eigenvalues(self) -> Optional[List[float]]: """ return aux operator eigen values """ diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py index db10d57551..d18d946a40 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -53,6 +53,8 @@ def test_default(self): 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""" From b0d100d71e11408751fffeacd4558ffe6419450a Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Mon, 12 Oct 2020 11:52:52 -0400 Subject: [PATCH 116/197] fix spell --- .pylintdict | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pylintdict b/.pylintdict index d8864f6769..b92493ebeb 100644 --- a/.pylintdict +++ b/.pylintdict @@ -243,6 +243,8 @@ gogolin goldfarb gridpoints grinko +groundenergy +groundstates grover gset gsls From 20b3bcf36dc994032a87ed2571d2a587404a4be1 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 13 Oct 2020 00:04:52 +0200 Subject: [PATCH 117/197] reverted due to missing part of the test uccsd_HF --- test/chemistry/test_uccsd_hartree_fock.py | 60 +++++++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/test/chemistry/test_uccsd_hartree_fock.py b/test/chemistry/test_uccsd_hartree_fock.py index 80e297bef0..7d0f84c0c0 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -14,6 +14,8 @@ import unittest +from ddt import ddt, idata, unpack + from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.aqua import QuantumInstance, aqua_globals @@ -27,7 +29,7 @@ from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation from qiskit.chemistry.qubit_transformations import FermionicTransformation - +@ddt class TestUCCSDHartreeFock(QiskitChemistryTestCase): """Test for these aqua extensions.""" @@ -145,6 +147,56 @@ def test_uccsd_hf_aer_qasm_snapshot(self): result = gsc.compute_groundstate(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy, places=3) - -if __name__ == '__main__': - unittest.main() + EXCITATION_RESULTS = \ + [[[[0, 1], [0, 2], [3, 4], [3, 5]], + [[0, 1, 3, 4], [0, 1, 3, 5], [0, 2, 3, 4], [0, 2, 3, 5]]], # 0 full: 6 orbs, 2 particles + [[[0, 2], [3, 5]], [[0, 2, 3, 5]]], # 1 limited active space + [[[0, 1], [0, 2], [3, 4], [3, 5]], []], # 2 singles only + [[], [[0, 1, 3, 4], [0, 1, 3, 5], [0, 2, 3, 4], [0, 2, 3, 5]]], # 3 doubles only + [[[0, 1], [3, 4]], []], # 4 singles only limited active space + [[[0, 2], [1, 2], [3, 5], [4, 5]], + [[0, 2, 3, 5], [0, 2, 4, 5], [1, 2, 3, 5], [1, 2, 4, 5]]], # 5 full: 6 orbs, 4 particles + [[[1, 2], [4, 5]], [[1, 2, 4, 5]]], # 6 limited active space + [[[0, 2], [0, 3], [1, 2], [1, 3], [4, 6], [4, 7], [5, 6], [5, 7]], # 7 + [[0, 2, 4, 6], [0, 2, 4, 7], [0, 2, 5, 6], [0, 2, 5, 7], [0, 3, 4, 6], [0, 3, 4, 7], + [0, 3, 5, 6], [0, 3, 5, 7], [1, 2, 4, 6], [1, 2, 4, 7], [1, 2, 5, 6], [1, 2, 5, 7], + [1, 3, 4, 6], [1, 3, 4, 7], [1, 3, 5, 6], [1, 3, 5, 7], [0, 2, 1, 3], [4, 6, 5, 7]]], + [[[0, 2], [0, 3], [1, 2], [1, 3], [4, 6], [4, 7], [5, 6], [5, 7]], # 8 No same spins + [[0, 2, 4, 6], [0, 2, 4, 7], [0, 2, 5, 6], [0, 2, 5, 7], [0, 3, 4, 6], [0, 3, 4, 7], + [0, 3, 5, 6], [0, 3, 5, 7], [1, 2, 4, 6], [1, 2, 4, 7], [1, 2, 5, 6], [1, 2, 5, 7], + [1, 3, 4, 6], [1, 3, 4, 7], [1, 3, 5, 6], [1, 3, 5, 7]]], + ] + + @idata([[0, 6, 2], + [0, 6, 2, [0], [0, 1]], # Full active space + [1, 6, 2, [0], [1]], # Restrict active space + [0, 6, 2, [0], [0, 1], False], + [2, 6, 2, None, None, True, 'both', 'ucc', 's'], + [3, 6, 2, None, [0, 1], True, 'both', 'ucc', 'd'], + [4, 6, 2, [0], [0], False, 'both', 'ucc', 's'], + [5, 6, 4], + [5, 6, 4, [0, 1], [0]], # Full active space + [6, 6, 4, [1], [0]], # Restrict active space + [7, 8, 4], + [8, 8, 4, None, None, False], + ]) + @unpack + def test_uccsd_excitations(self, expected_result_idx, num_orbitals, num_particles, + active_occupied=None, active_unoccupied=None, + same_spin_doubles=True, + method_singles='both', method_doubles='ucc', + excitation_type='sd' + ): + """ Test generated excitation lists in conjunction with active space """ + + excitations = UCCSD.compute_excitation_lists( + num_orbitals=num_orbitals, num_particles=num_particles, + active_occ_list=active_occupied, active_unocc_list=active_unoccupied, + same_spin_doubles=same_spin_doubles, + method_singles=method_singles, method_doubles=method_doubles, + excitation_type=excitation_type) + + self.assertListEqual(list(excitations), self.EXCITATION_RESULTS[expected_result_idx]) + +# if __name__ == '__main__': +# unittest.main() From 1e2679b4228271a81ebf868118b3587e096db941 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 13 Oct 2020 00:07:10 +0200 Subject: [PATCH 118/197] remove test EOM todos --- test/chemistry/test_qeom_ee.py | 2 -- test/chemistry/test_qeom_vqe.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/test/chemistry/test_qeom_ee.py b/test/chemistry/test_qeom_ee.py index 30414e3414..1d6a813369 100644 --- a/test/chemistry/test_qeom_ee.py +++ b/test/chemistry/test_qeom_ee.py @@ -25,8 +25,6 @@ from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry.algorithms import QEomEE -# TODO Update this test after excited states PR is done - class TestEomEE(QiskitAquaTestCase): """Test case for Eom EE.""" diff --git a/test/chemistry/test_qeom_vqe.py b/test/chemistry/test_qeom_vqe.py index 3c7591f0db..d17cfc147a 100644 --- a/test/chemistry/test_qeom_vqe.py +++ b/test/chemistry/test_qeom_vqe.py @@ -31,8 +31,6 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock -# TODO Update test after excited states PR is done - class TestEomVQE(QiskitAquaTestCase): """Test Eom VQE.""" From 8bae6ea567cd25bc68a1bb25bdeca9b729b7c39d Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 13 Oct 2020 00:40:22 +0200 Subject: [PATCH 119/197] lint --- test/chemistry/test_uccsd_hartree_fock.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/chemistry/test_uccsd_hartree_fock.py b/test/chemistry/test_uccsd_hartree_fock.py index 7d0f84c0c0..666567c3ae 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -12,10 +12,6 @@ """ Test of UCCSD and HartreeFock Aqua extensions """ -import unittest - -from ddt import ddt, idata, unpack - from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.aqua import QuantumInstance, aqua_globals @@ -28,6 +24,8 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation from qiskit.chemistry.qubit_transformations import FermionicTransformation +from ddt import ddt, idata, unpack + @ddt class TestUCCSDHartreeFock(QiskitChemistryTestCase): @@ -198,5 +196,4 @@ def test_uccsd_excitations(self, expected_result_idx, num_orbitals, num_particle self.assertListEqual(list(excitations), self.EXCITATION_RESULTS[expected_result_idx]) -# if __name__ == '__main__': -# unittest.main() + From 0c9b39c0fec9359965c7864c4df3bf445b875f5d Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 13 Oct 2020 00:42:00 +0200 Subject: [PATCH 120/197] more lint --- test/chemistry/test_uccsd_hartree_fock.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/chemistry/test_uccsd_hartree_fock.py b/test/chemistry/test_uccsd_hartree_fock.py index 666567c3ae..ba75e74fb7 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -195,5 +195,3 @@ def test_uccsd_excitations(self, expected_result_idx, num_orbitals, num_particle excitation_type=excitation_type) self.assertListEqual(list(excitations), self.EXCITATION_RESULTS[expected_result_idx]) - - From f8233ba055ce4db9b52e0231f9492c9246362ceb Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Tue, 13 Oct 2020 01:18:44 +0200 Subject: [PATCH 121/197] more lint --- test/chemistry/test_uccsd_hartree_fock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/chemistry/test_uccsd_hartree_fock.py b/test/chemistry/test_uccsd_hartree_fock.py index ba75e74fb7..719fc1cf17 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -13,6 +13,7 @@ """ Test of UCCSD and HartreeFock Aqua extensions """ 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 @@ -24,7 +25,6 @@ from qiskit.chemistry.drivers import HDF5Driver from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation from qiskit.chemistry.qubit_transformations import FermionicTransformation -from ddt import ddt, idata, unpack @ddt From a9698d5f5ef16ee49ba5e8faab7f05e43a76d5f8 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 08:49:24 +0200 Subject: [PATCH 122/197] Prepare EigenstateResult to handle ground and excited states --- .../ground_state_calculation/adapt_vqe.py | 6 +-- .../mes_ground_state_calculation.py | 2 +- .../fermionic_transformation.py | 2 +- qiskit/chemistry/results/eigenstate_result.py | 52 ++++++++++++++----- .../results/electronic_structure_result.py | 11 ---- 5 files changed, 45 insertions(+), 28 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 2019b9d0b6..7a5def35d9 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -17,7 +17,7 @@ from typing import Optional, List, Tuple, Union import numpy as np -from qiskit.chemistry.results import ElectronicStructureResult +from qiskit.chemistry.results import EigenstateResult, ElectronicStructureResult from qiskit.chemistry.qubit_transformations import FermionicTransformation from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.components.variational_forms import UCCSD @@ -229,9 +229,9 @@ def compute_groundstate(self, driver: BaseDriver, raise AquaError('The algorithm finished due to an unforeseen reason!') # extend VQE returned information with additional outputs - eigenstate_result = ElectronicStructureResult() + eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_vqe_result - eigenstate_result.eigenvalue = raw_vqe_result.eigenvalue + eigenstate_result.eigenenergies = np.asarray([raw_vqe_result.eigenvalue]) eigenstate_result.aux_operator_eigenvalues = aux_values electronic_result = self.transformation.interpret(eigenstate_result) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index c61bd01c85..2768eb3a39 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -105,7 +105,7 @@ def compute_groundstate(self, driver: BaseDriver, eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_mes_result - eigenstate_result.eigenvalue = raw_mes_result.eigenvalue + eigenstate_result.eigenenergies = np.asarray([raw_mes_result.eigenvalue]) eigenstate_result.aux_operator_eigenvalues = raw_mes_result.aux_operator_eigenvalues result = self.transformation.interpret(eigenstate_result) return result diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 6a414c145b..85d4225139 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -476,7 +476,7 @@ def interpret(self, eigenstate_result: EigenstateResult) -> ElectronicStructureR An electronic structure result. """ result = ElectronicStructureResult(eigenstate_result.data) - result.computed_electronic_energy = eigenstate_result.eigenvalue.real + 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: diff --git a/qiskit/chemistry/results/eigenstate_result.py b/qiskit/chemistry/results/eigenstate_result.py index f5605eb99b..a891803ea6 100644 --- a/qiskit/chemistry/results/eigenstate_result.py +++ b/qiskit/chemistry/results/eigenstate_result.py @@ -12,31 +12,59 @@ """Eigenstate results module.""" -from typing import Optional, List +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 eigenvalue(self) -> Optional[complex]: - """ returns eigen value """ - return self.get('eigenvalue') + def eigenenergies(self) -> Optional[np.ndarray]: + """ returns eigen energies """ + return self.get('eigenenergies') - @eigenvalue.setter - def eigenvalue(self, value: complex) -> None: - """ set eigen value """ - self.data['eigenvalue'] = value + @eigenenergies.setter + def eigenenergies(self, value: np.ndarray) -> None: + """ set eigen energies """ + self.data['eigenenergies'] = value - # @eigenstates.setter + @property + def eigenstates(self) -> Optional[List[Union[str, dict, Result, list, np.ndarray, Statevector, + QuantumCircuit, Instruction, OperatorBase]]]: + """ returns eigen states """ + return self.get('eigenstates') - # @eigenenergies.setter + @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 - # @groundenergy.setter + @property + def groundenergy(self) -> Optional[float]: + """ returns ground energy """ + energies = self.get('eigenenergies') + if energies: + return energies[0].real + return None - # @groundstates.setter + @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]]: diff --git a/qiskit/chemistry/results/electronic_structure_result.py b/qiskit/chemistry/results/electronic_structure_result.py index 72d3c6588e..06b92fd748 100644 --- a/qiskit/chemistry/results/electronic_structure_result.py +++ b/qiskit/chemistry/results/electronic_structure_result.py @@ -17,7 +17,6 @@ import logging import numpy as np -from qiskit.aqua.algorithms import AlgorithmResult from qiskit.chemistry import QMolecule from .eigenstate_result import EigenstateResult @@ -34,16 +33,6 @@ class ElectronicStructureResult(EigenstateResult): """The electronic structure result.""" - @property - def algorithm_result(self) -> AlgorithmResult: - """ Returns raw algorithm result """ - return self.get('algorithm_result') - - @algorithm_result.setter - def algorithm_result(self, value: AlgorithmResult) -> None: - """ Sets raw algorithm result """ - self.data['algorithm_result'] = value - @property def hartree_fock_energy(self) -> float: """ Returns Hartree-Fock energy """ From 8cc1d6b9ca1be5e2c55830060e20a417940c6551 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 08:54:39 +0200 Subject: [PATCH 123/197] Deprecate the Enums in the chemistry.core module --- qiskit/chemistry/core/hamiltonian.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index 847be15fcb..fab4cb5b0e 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -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' From fa7f436560dcdce0f8e99e9939cd1ad53939186d Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 09:39:50 +0200 Subject: [PATCH 124/197] Also populate eigenstates in ground state calculations --- qiskit/chemistry/ground_state_calculation/adapt_vqe.py | 1 + .../ground_state_calculation/mes_ground_state_calculation.py | 1 + 2 files changed, 2 insertions(+) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 7a5def35d9..6172be5471 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -232,6 +232,7 @@ def compute_groundstate(self, driver: BaseDriver, eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_vqe_result eigenstate_result.eigenenergies = np.asarray([raw_vqe_result.eigenvalue]) + eigenstate_result.eigenstates = [raw_vqe_result.eigenstate] eigenstate_result.aux_operator_eigenvalues = aux_values electronic_result = self.transformation.interpret(eigenstate_result) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 2768eb3a39..24ae50c039 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -106,6 +106,7 @@ def compute_groundstate(self, driver: BaseDriver, eigenstate_result = EigenstateResult() eigenstate_result.raw_result = raw_mes_result eigenstate_result.eigenenergies = np.asarray([raw_mes_result.eigenvalue]) + eigenstate_result.eigenstates = [raw_mes_result.eigenstate] eigenstate_result.aux_operator_eigenvalues = raw_mes_result.aux_operator_eigenvalues result = self.transformation.interpret(eigenstate_result) return result From 1622eb85f1f18be60e3b5e48642f4b194995fe73 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 09:56:00 +0200 Subject: [PATCH 125/197] Add factory for NumPyMinimumEigensolver --- .../mes_factories/__init__.py | 2 + .../numpy_minimum_eigensolver_factory.py | 53 +++++++++++++++++++ test/chemistry/test_mes_gsc_calculation.py | 10 +++- 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py b/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py index 9e85c0cf18..dd26d9ade1 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py @@ -13,8 +13,10 @@ """Factories that create a minimum eigensolver based on a qubit transformation.""" from .mes_factory import MESFactory +from .numpy_minimum_eigensolver_factory import NumPyMinimumEigensolverFactory from .vqe_uccsd_factory import VQEUCCSDFactory __all__ = ['MESFactory', + 'NumPyMinimumEigensolverFactory', 'VQEUCCSDFactory' ] diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py new file mode 100644 index 0000000000..9505259452 --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py @@ -0,0 +1,53 @@ +# 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 qiskit.chemistry.qubit_transformations import FermionicTransformation + +from .mes_factory import MESFactory + + +class NumPyMinimumEigensolverFactory(MESFactory): + """A factory to construct a NumPyMinimumEigensolver.""" + + def __init__(self, + filter_criterion: Callable[[Union[List, np.ndarray], float, Optional[List[float]]], + bool] = None) -> 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. + """ + self._filter_criterion = filter_criterion + + def get_solver(self, 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 NumPyMinimumEigensolver suitable to compute the ground state of the molecule + transformed by ``transformation``. + """ + npme = NumPyMinimumEigensolver(filter_criterion=self._filter_criterion) + return npme diff --git a/test/chemistry/test_mes_gsc_calculation.py b/test/chemistry/test_mes_gsc_calculation.py index 64d5ad3ea1..b75737a51b 100644 --- a/test/chemistry/test_mes_gsc_calculation.py +++ b/test/chemistry/test_mes_gsc_calculation.py @@ -23,7 +23,8 @@ from qiskit.chemistry.qubit_transformations import FermionicTransformation from qiskit.chemistry.qubit_transformations.fermionic_transformation import QubitMappingType from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation -from qiskit.chemistry.ground_state_calculation.mes_factories import VQEUCCSDFactory +from qiskit.chemistry.ground_state_calculation.mes_factories import (VQEUCCSDFactory, + NumPyMinimumEigensolverFactory) class TestMESGSCCalculation(QiskitChemistryTestCase): @@ -44,6 +45,13 @@ def setUp(self): self.transformation = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER) + def test_npme(self): + """ Test NumPyMinimumEigensolver """ + solver = NumPyMinimumEigensolverFactory() + calc = MinimumEigensolverGroundStateCalculation(self.transformation, solver) + res = calc.compute_groundstate(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'))) From 7ed65080e53308979a20d43ea0b3386e87230c7e Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 11:40:42 +0200 Subject: [PATCH 126/197] Implement `_filter_criterion` in FermionicTransformation --- .../numpy_minimum_eigensolver_factory.py | 18 +++++++++++++++++- .../fermionic_transformation.py | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py index 9505259452..d94be981d9 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py @@ -38,6 +38,18 @@ def __init__(self, """ self._filter_criterion = 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 + def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensolver: """Returns a VQE with a UCCSD wavefunction ansatz, based on ``transformation``. This works only with a ``FermionicTransformation``. @@ -49,5 +61,9 @@ def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensol A NumPyMinimumEigensolver suitable to compute the ground state of the molecule transformed by ``transformation``. """ - npme = NumPyMinimumEigensolver(filter_criterion=self._filter_criterion) + filter_criterion = self._filter_criterion + if not filter_criterion and hasattr(transformation, '_eigensolver_filter_criterion'): + filter_criterion = transformation._eigensolver_filter_criterion + + npme = NumPyMinimumEigensolver(filter_criterion=filter_criterion) return npme diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 85d4225139..bbd01f8285 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -444,6 +444,12 @@ def _check_commutes(cliffords: List[WeightedPauliOperator], logger.debug(' \'%s\' commutes: %s, %s', operator.name, does_commute, commutes) return does_commute + # pylint: disable=unused-argument + def _eigensolver_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) + @staticmethod def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: np.ndarray) -> Z2Symmetries: """ From dfcdb7afbafaf0477bbed82ab9f0a8201943da79 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 12:02:01 +0200 Subject: [PATCH 127/197] Extract default filter_criterion into interface Rather than checking for a private method being implemented we can obtain the default filter_criterion cleanly. This makes sense because any transformation in theory could implement a default filter. However, we return None in case it is not implemented. --- .../numpy_minimum_eigensolver_factory.py | 4 +-- .../fermionic_transformation.py | 25 ++++++++++++++----- .../qubit_operator_transformation.py | 12 ++++++++- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py index d94be981d9..f51add707b 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py @@ -62,8 +62,8 @@ def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensol transformed by ``transformation``. """ filter_criterion = self._filter_criterion - if not filter_criterion and hasattr(transformation, '_eigensolver_filter_criterion'): - filter_criterion = transformation._eigensolver_filter_criterion + if not filter_criterion: + filter_criterion = transformation.get_default_filter_criterion() npme = NumPyMinimumEigensolver(filter_criterion=filter_criterion) return npme diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index bbd01f8285..0822ed8f81 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -15,7 +15,8 @@ The problem is described in a driver. """ -from typing import Optional, List, Union, cast, Tuple, Dict, Any +from functools import partial +from typing import Optional, List, Union, cast, Tuple, Dict, Any, Callable import logging from enum import Enum @@ -444,11 +445,23 @@ def _check_commutes(cliffords: List[WeightedPauliOperator], logger.debug(' \'%s\' commutes: %s, %s', operator.name, does_commute, commutes) return does_commute - # pylint: disable=unused-argument - def _eigensolver_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) + 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: diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 904ac39f35..26e2697272 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -13,7 +13,9 @@ """Base class for transformation to qubit operators for chemistry problems""" from abc import ABC, abstractmethod -from typing import Tuple, List, Any, Optional +from typing import Tuple, List, Any, Optional, Union, Callable + +import numpy as np from qiskit.aqua.operators.legacy import WeightedPauliOperator from qiskit.chemistry.drivers import BaseDriver @@ -40,6 +42,14 @@ def transform(self, driver: BaseDriver, """ 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, eigenstate_result: EigenstateResult) -> EigenstateResult: """Interprets an EigenstateResult in the context of this transformation. From 634538f545ca0c200d8109c5cf5d0c47a7f091b1 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 13:16:23 +0200 Subject: [PATCH 128/197] Make use of default filter criterion configurable --- .../numpy_minimum_eigensolver_factory.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py index f51add707b..ce3872f363 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py @@ -26,7 +26,8 @@ class NumPyMinimumEigensolverFactory(MESFactory): def __init__(self, filter_criterion: Callable[[Union[List, np.ndarray], float, Optional[List[float]]], - bool] = None) -> None: + bool] = None, + use_default_filter_criterion: bool = False) -> None: """ Args: filter_criterion: callable that allows to filter eigenvalues/eigenstates. The minimum @@ -35,8 +36,11 @@ def __init__(self, `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]]], @@ -50,6 +54,16 @@ def filter_criterion(self, value: Callable[[Union[List, np.ndarray], float, """ 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: FermionicTransformation) -> MinimumEigensolver: """Returns a VQE with a UCCSD wavefunction ansatz, based on ``transformation``. This works only with a ``FermionicTransformation``. @@ -62,7 +76,7 @@ def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensol transformed by ``transformation``. """ filter_criterion = self._filter_criterion - if not 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) From ce3e3fb782803d7722a3c3211d40e1f2d23811fd Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 13:17:31 +0200 Subject: [PATCH 129/197] Check state's type before making at a StateFn --- .../ground_state_calculation/mes_ground_state_calculation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 24ae50c039..979e4b582a 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -133,7 +133,8 @@ def evaluate_operators(self, # try to get a QuantumInstance from the solver quantum_instance = getattr(self._solver, 'quantum_instance', None) - state = StateFn(state) + 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. From fdfb05798f97b3ae35f97d1e624fba0cd16f9039 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 13:24:26 +0200 Subject: [PATCH 130/197] Use `supports_aux_ops` in `returns_groundstate` --- qiskit/aqua/algorithms/eigen_solvers/eigen_solver.py | 3 ++- qiskit/aqua/algorithms/eigen_solvers/numpy_eigen_solver.py | 3 ++- .../minimum_eigen_solvers/minimum_eigen_solver.py | 3 ++- .../minimum_eigen_solvers/numpy_minimum_eigen_solver.py | 5 +++-- qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py | 3 ++- .../ground_state_calculation/mes_factories/mes_factory.py | 6 ++++++ .../mes_factories/numpy_minimum_eigensolver_factory.py | 3 +++ .../mes_factories/vqe_uccsd_factory.py | 3 +++ .../mes_ground_state_calculation.py | 6 ++---- 9 files changed, 25 insertions(+), 10 deletions(-) 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 ccb6d7682a..2d4720dc55 100644 --- a/qiskit/aqua/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py +++ b/qiskit/aqua/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py @@ -55,7 +55,8 @@ def compute_minimum_eigenvalue( 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/ground_state_calculation/mes_factories/mes_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py index ad75cf7565..dea11cce46 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py @@ -32,3 +32,9 @@ def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEige 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/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py index ce3872f363..97a89974ff 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py @@ -81,3 +81,6 @@ def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensol npme = NumPyMinimumEigensolver(filter_criterion=filter_criterion) return npme + + def supports_aux_operators(self): + return NumPyMinimumEigensolver.supports_aux_operators() diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py index 1dce4ad022..a40548d740 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py @@ -143,3 +143,6 @@ def get_solver(self, transformation: FermionicTransformation) -> MinimumEigensol 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/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 979e4b582a..45bb796783 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -56,10 +56,8 @@ def solver(self, solver: Union[MinimumEigensolver, MESFactory]) -> None: self._solver = solver def returns_groundstate(self) -> bool: - """TODO - whether the eigensolver returns the ground state or only ground state energy.""" - - return False + """Whether the eigensolver returns the ground state or only ground state energy.""" + return self._solver.supports_aux_operators() def compute_groundstate(self, driver: BaseDriver, aux_operators: Optional[List[Any]] = None From 43382e2b0f9302b4eec5b0337b496ddc980bcaa1 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 13:29:18 +0200 Subject: [PATCH 131/197] Replace Any type-hint where not really necessary The reason for making it a union of lists rather than a list of a union is to ensure that no mixed lists are allowed. --- .../ground_state_calculation/ground_state_calculation.py | 6 ++++-- .../mes_ground_state_calculation.py | 7 ++++--- .../qubit_transformations/qubit_operator_transformation.py | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py index 3e9388eee5..6730ada4e5 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py @@ -13,7 +13,7 @@ """The ground state calculation interface.""" from abc import ABC, abstractmethod -from typing import Dict, List, Any, Optional, Union +from typing import Dict, List, Optional, Union import numpy as np @@ -22,6 +22,7 @@ from qiskit.quantum_info import Statevector from qiskit.result import Result from qiskit.aqua.operators import OperatorBase, WeightedPauliOperator +from qiskit.chemistry import FermionicOperator, BosonicOperator from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.results import EigenstateResult @@ -50,7 +51,8 @@ def transformation(self, transformation: QubitOperatorTransformation) -> None: @abstractmethod def compute_groundstate(self, driver: BaseDriver, - aux_operators: Optional[List[Any]] = None + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None ) -> EigenstateResult: """Compute the ground state energy of the molecule that was supplied via the driver. diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 45bb796783..5674369ee9 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -12,7 +12,7 @@ """Ground state computation using a minimum eigensolver.""" -from typing import Union, List, Any, Optional, Dict +from typing import Union, List, Optional, Dict import numpy as np @@ -22,7 +22,7 @@ from qiskit.result import Result from qiskit.aqua.algorithms import MinimumEigensolver from qiskit.aqua.operators import OperatorBase, WeightedPauliOperator, StateFn, CircuitSampler -from qiskit.chemistry import FermionicOperator +from qiskit.chemistry import FermionicOperator, BosonicOperator from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.ground_state_calculation import GroundStateCalculation from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation @@ -60,7 +60,8 @@ def returns_groundstate(self) -> bool: return self._solver.supports_aux_operators() def compute_groundstate(self, driver: BaseDriver, - aux_operators: Optional[List[Any]] = None + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None ) -> EigenstateResult: """Compute Ground State properties. diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 26e2697272..f32d7f8943 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -13,11 +13,12 @@ """Base class for transformation to qubit operators for chemistry problems""" from abc import ABC, abstractmethod -from typing import Tuple, List, Any, Optional, Union, Callable +from typing import Tuple, List, Optional, Union, Callable import numpy as np from qiskit.aqua.operators.legacy import WeightedPauliOperator +from qiskit.chemistry import FermionicOperator, BosonicOperator from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.results import EigenstateResult @@ -27,7 +28,8 @@ class QubitOperatorTransformation(ABC): @abstractmethod def transform(self, driver: BaseDriver, - aux_operators: Optional[List[Any]] = None + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: """Transformation from the ``driver`` to a qubit operator. From ec268ab8bb2046fc3a7e1618c42f5a234025aa27 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 13:38:20 +0200 Subject: [PATCH 132/197] Do not use legacy Operator in new interface The WeightedPauliOp is a legacy operator and should not be used in this new interface. Instead, we now convert the operators to the OperatorFlow before returning them to ensure we only use the new operator types. --- .../qubit_transformations/fermionic_transformation.py | 9 +++++++-- .../qubit_operator_transformation.py | 4 ++-- test/chemistry/test_mes_gsc_calculation.py | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 0822ed8f81..42d6aefe24 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -21,7 +21,7 @@ from enum import Enum import numpy as np -from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator +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 @@ -127,7 +127,7 @@ def qubit_mapping(self) -> str: def transform(self, driver: BaseDriver, aux_operators: Optional[List[FermionicOperator]] = None - ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + ) -> Tuple[OperatorBase, List[OperatorBase]]: """Transformation from the ``driver`` to a qubit operator. Args: @@ -140,6 +140,11 @@ def transform(self, driver: BaseDriver, 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, diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index f32d7f8943..0c3afce25e 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -17,7 +17,7 @@ import numpy as np -from qiskit.aqua.operators.legacy import WeightedPauliOperator +from qiskit.aqua.operators import OperatorBase from qiskit.chemistry import FermionicOperator, BosonicOperator from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.results import EigenstateResult @@ -30,7 +30,7 @@ class QubitOperatorTransformation(ABC): def transform(self, driver: BaseDriver, aux_operators: Optional[Union[List[FermionicOperator], List[BosonicOperator]]] = None - ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + ) -> Tuple[OperatorBase, List[OperatorBase]]: """Transformation from the ``driver`` to a qubit operator. Args: diff --git a/test/chemistry/test_mes_gsc_calculation.py b/test/chemistry/test_mes_gsc_calculation.py index b75737a51b..67b0af82e8 100644 --- a/test/chemistry/test_mes_gsc_calculation.py +++ b/test/chemistry/test_mes_gsc_calculation.py @@ -74,7 +74,7 @@ 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 = [op for op in aux_ops if op.name.lower() == 'number of particles'][0] + 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) @@ -88,7 +88,7 @@ def test_eval_op_list(self): expected_results = {'number of particles': 2, 's^2': 0, 'magnetization': 0} - add_aux_op = [op for op in aux_ops if op.name.lower() in list(expected_results.keys())] + 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) @@ -104,7 +104,7 @@ def test_eval_op_dict(self): expected_results = {'number of particles': 2, 's^2': 0, 'magnetization': 0} - add_aux_op = [op for op in aux_ops if op.name.lower() in list(expected_results.keys())] + 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)) From 2bce4e62484f2cbcff73d0ced30daa5ab596dcea Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 17:22:42 +0200 Subject: [PATCH 133/197] Except (Minimum)EigensolverResults in interpret() To provide more user-flexibility and better compatibility with the Aqua result classes, the interpret() method is able to handle (Minimum)EigensolverResult classes as well as the EigenstateResult. --- .../ground_state_calculation/adapt_vqe.py | 9 ++------ .../mes_ground_state_calculation.py | 7 +----- .../fermionic_transformation.py | 22 +++++++++++++++++-- .../qubit_operator_transformation.py | 6 +++-- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 6172be5471..46476ef3d9 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -218,6 +218,7 @@ def compute_groundstate(self, driver: BaseDriver, 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' @@ -228,13 +229,7 @@ def compute_groundstate(self, driver: BaseDriver, else: raise AquaError('The algorithm finished due to an unforeseen reason!') - # extend VQE returned information with additional outputs - eigenstate_result = EigenstateResult() - eigenstate_result.raw_result = raw_vqe_result - eigenstate_result.eigenenergies = np.asarray([raw_vqe_result.eigenvalue]) - eigenstate_result.eigenstates = [raw_vqe_result.eigenstate] - eigenstate_result.aux_operator_eigenvalues = aux_values - electronic_result = self.transformation.interpret(eigenstate_result) + electronic_result = self.transformation.interpret(raw_vqe_result) result = AdaptVQEResult(electronic_result.data) result.num_iterations = iteration diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index 5674369ee9..d60dd8049c 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -102,12 +102,7 @@ def compute_groundstate(self, driver: BaseDriver, raw_mes_result = solver.compute_minimum_eigenvalue(operator, aux_operators) - eigenstate_result = EigenstateResult() - eigenstate_result.raw_result = raw_mes_result - eigenstate_result.eigenenergies = np.asarray([raw_mes_result.eigenvalue]) - eigenstate_result.eigenstates = [raw_mes_result.eigenstate] - eigenstate_result.aux_operator_eigenvalues = raw_mes_result.aux_operator_eigenvalues - result = self.transformation.interpret(eigenstate_result) + result = self.transformation.interpret(raw_mes_result) return result def evaluate_operators(self, diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 42d6aefe24..2e262d28f0 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -21,6 +21,7 @@ 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 @@ -490,15 +491,32 @@ def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: np.ndarray) -> Z2Symmetrie z2_symmetries.tapering_values = taper_coef return z2_symmetries - def interpret(self, eigenstate_result: EigenstateResult) -> ElectronicStructureResult: + def interpret(self, raw_result: Union[EigenstateResult, EigensolverResult, + MinimumEigensolverResult]) -> ElectronicStructureResult: """Interprets an EigenstateResult in the context of this transformation. Args: - eigenstate_result: an eigenstate result object. + 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 diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py index 0c3afce25e..ed614f86c2 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py @@ -17,6 +17,7 @@ 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 @@ -53,11 +54,12 @@ def get_default_filter_criterion(self) -> Optional[Callable[[Union[List, np.ndar return None @abstractmethod - def interpret(self, eigenstate_result: EigenstateResult) -> EigenstateResult: + def interpret(self, raw_result: Union[EigenstateResult, EigensolverResult, + MinimumEigensolverResult]) -> EigenstateResult: """Interprets an EigenstateResult in the context of this transformation. Args: - eigenstate_result: an eigenstate result object. + raw_result: an eigenstate result object. Returns: An "interpreted" eigenstate result. From f10e41f7d637d33bbd94bcf18f8d8de699d3ae03 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Tue, 13 Oct 2020 17:34:54 +0200 Subject: [PATCH 134/197] Fix tests to work with OperatorBase rather than WeightedPauliOperator --- .../ground_state_calculation/adapt_vqe.py | 2 +- .../mes_ground_state_calculation.py | 2 +- .../test_fermionic_transformation.py | 6 ++-- ...est_fermionic_transformation_orb_reduce.py | 6 ++-- .../test_initial_state_hartree_fock.py | 6 ++-- test/chemistry/test_symmetries.py | 4 +-- test/chemistry/test_uccsd_advanced.py | 33 +++++-------------- 7 files changed, 21 insertions(+), 38 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py index 46476ef3d9..cbdc8cd1e3 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/ground_state_calculation/adapt_vqe.py @@ -17,7 +17,7 @@ from typing import Optional, List, Tuple, Union import numpy as np -from qiskit.chemistry.results import EigenstateResult, ElectronicStructureResult +from qiskit.chemistry.results import ElectronicStructureResult from qiskit.chemistry.qubit_transformations import FermionicTransformation from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.components.variational_forms import UCCSD diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py index d60dd8049c..5e0168d573 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py @@ -146,7 +146,7 @@ def evaluate_operators(self, return results def _eval_op(self, state, op, quantum_instance): - if not isinstance(op, OperatorBase): + if isinstance(op, WeightedPauliOperator): op = op.to_opflow() # if the operator is empty we simply return 0 diff --git a/test/chemistry/test_fermionic_transformation.py b/test/chemistry/test_fermionic_transformation.py index 1d6e8e3bd1..d93f1b901e 100644 --- a/test/chemistry/test_fermionic_transformation.py +++ b/test/chemistry/test_fermionic_transformation.py @@ -15,7 +15,7 @@ import unittest from test.chemistry import QiskitChemistryTestCase -from qiskit.aqua.operators import WeightedPauliOperator +from qiskit.aqua.operators import OperatorBase from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry.drivers import PySCFDriver, UnitsType @@ -53,10 +53,10 @@ def _validate_info(self, fermionic_transformation, num_particles=None, '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, WeightedPauliOperator)) + self.assertTrue(isinstance(qubit_op, OperatorBase)) self.assertIsNotNone(qubit_op) self.assertEqual(qubit_op.num_qubits, num_qubits) - self.assertEqual(len(qubit_op.to_dict()['paulis']), num_paulis) + self.assertEqual(len(qubit_op.oplist), num_paulis) def test_output(self): """ output test """ diff --git a/test/chemistry/test_fermionic_transformation_orb_reduce.py b/test/chemistry/test_fermionic_transformation_orb_reduce.py index 40db42ad84..17f625e6ef 100644 --- a/test/chemistry/test_fermionic_transformation_orb_reduce.py +++ b/test/chemistry/test_fermionic_transformation_orb_reduce.py @@ -15,7 +15,7 @@ import unittest from test.chemistry import QiskitChemistryTestCase -from qiskit.aqua.operators import WeightedPauliOperator +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 @@ -52,10 +52,10 @@ def _validate_info(self, fermionic_transformation, num_particles=None, '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, WeightedPauliOperator)) + self.assertTrue(isinstance(qubit_op, OperatorBase)) self.assertIsNotNone(qubit_op) self.assertEqual(qubit_op.num_qubits, num_qubits) - self.assertEqual(len(qubit_op.to_dict()['paulis']), num_paulis) + self.assertEqual(len(qubit_op.oplist), num_paulis) def test_output(self): """ output test """ diff --git a/test/chemistry/test_initial_state_hartree_fock.py b/test/chemistry/test_initial_state_hartree_fock.py index 27d307e932..1261c8c688 100644 --- a/test/chemistry/test_initial_state_hartree_fock.py +++ b/test/chemistry/test_initial_state_hartree_fock.py @@ -18,7 +18,7 @@ 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 TransformationType, QubitMappingType @@ -104,13 +104,13 @@ def test_hf_value(self, mapping): qubit_op, _ = fermionic_transformation.transform(driver) - qubit_op = op_converter.to_matrix_operator(qubit_op) 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 \ + exp = ~StateFn(qubit_op) @ StateFn(qc) + hf_energy = exp.eval().real \ + fermionic_transformation._nuclear_repulsion_energy self.assertAlmostEqual(fermionic_transformation._hf_energy, hf_energy, places=6) diff --git a/test/chemistry/test_symmetries.py b/test/chemistry/test_symmetries.py index de9b84289d..5a4b07bfc9 100644 --- a/test/chemistry/test_symmetries.py +++ b/test/chemistry/test_symmetries.py @@ -83,7 +83,7 @@ def test_tapered_op(self): 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.qubit_op.z2_symmetries.sq_list) + sq_list=self.z2_symmetries.sq_list) var_form = UCCSD( num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], @@ -94,7 +94,7 @@ def test_tapered_op(self): qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, - z2_symmetries=self.qubit_op.z2_symmetries) + z2_symmetries=self.z2_symmetries) solver = VQE(var_form=var_form, optimizer=optimizer, quantum_instance=QuantumInstance( diff --git a/test/chemistry/test_uccsd_advanced.py b/test/chemistry/test_uccsd_advanced.py index 7d45e10c45..418ae04aab 100644 --- a/test/chemistry/test_uccsd_advanced.py +++ b/test/chemistry/test_uccsd_advanced.py @@ -17,8 +17,7 @@ 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 @@ -52,21 +51,6 @@ def setUp(self): orbital_reduction=[]) self.qubit_op, _ = self.fermionic_transformation.transform(self.driver) - 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.z2_symmetries = z2_symmetries - self.the_tapered_op = tapered_ops[smallest_idx] - self.reference_energy_pUCCD = -1.1434447924298028 self.reference_energy_UCCD0 = -1.1476045878481704 self.reference_energy_UCCD0full = -1.1515491334334347 @@ -208,7 +192,7 @@ def test_uccsd_hf_qUCCSD(self): qubit_mapping=fermionic_transformation._qubit_mapping, two_qubit_reduction=fermionic_transformation._two_qubit_reduction, num_particles=fermionic_transformation.molecule_info['num_particles'], - sq_list=qubit_op.z2_symmetries.sq_list) + sq_list=fermionic_transformation.molecule_info['z2_symmetries'].sq_list) var_form = UCCSD( num_orbitals=fermionic_transformation.molecule_info['num_orbitals'], @@ -218,7 +202,7 @@ def test_uccsd_hf_qUCCSD(self): qubit_mapping=fermionic_transformation._qubit_mapping, two_qubit_reduction=fermionic_transformation._two_qubit_reduction, num_time_slices=1, - z2_symmetries=qubit_op.z2_symmetries, + z2_symmetries=fermionic_transformation.molecule_info['z2_symmetries'], shallow_circuit_concat=False, method_doubles='ucc', excitation_type='sd', @@ -228,9 +212,8 @@ def test_uccsd_hf_qUCCSD(self): quantum_instance=QuantumInstance( backend=BasicAer.get_backend('statevector_simulator'))) - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - - result = gsc.compute_groundstate(self.driver) + 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) @@ -243,7 +226,7 @@ def test_uccsd_hf_excitations(self): 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.the_tapered_op.z2_symmetries.sq_list) + sq_list=self.fermionic_transformation.molecule_info['z2_symmetries'].sq_list) # check singlet excitations var_form = UCCSD( @@ -254,7 +237,7 @@ def test_uccsd_hf_excitations(self): qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, - z2_symmetries=self.the_tapered_op.z2_symmetries, + z2_symmetries=self.fermionic_transformation.molecule_info['z2_symmetries'], shallow_circuit_concat=False, method_doubles='succ', excitation_type='d', @@ -274,7 +257,7 @@ def test_uccsd_hf_excitations(self): qubit_mapping=self.fermionic_transformation._qubit_mapping, two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, num_time_slices=1, - z2_symmetries=self.the_tapered_op.z2_symmetries, + z2_symmetries=self.fermionic_transformation.molecule_info['z2_symmetries'], shallow_circuit_concat=False, method_doubles='succ_full', excitation_type='d', From 850c79975c89339f77443b940c8259979afc06d3 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Wed, 14 Oct 2020 10:35:25 +0200 Subject: [PATCH 135/197] rm WPO support in trafo, add test for aux_ops --- .../fermionic_transformation.py | 65 +++++++++++-------- .../test_fermionic_transformation.py | 15 ++++- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index 2e262d28f0..dbcc384e9f 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -149,14 +149,12 @@ def transform(self, driver: BaseDriver, return ops, aux_ops def _do_transform(self, qmolecule: QMolecule, - aux_operators: Optional[List[Union[FermionicOperator, - WeightedPauliOperator]]] = None + aux_operators: Optional[List[FermionicOperator]] = None ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: """ Args: qmolecule: qmolecule aux_operators: Additional ``FermionicOperator``s to map to a qubit operator. - Objects of type ``WeightedPauliOperator`` undergo no transformation. Returns: (qubit operator, auxiliary operators) @@ -201,9 +199,8 @@ def _do_transform(self, qmolecule: QMolecule, 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)] + 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] @@ -231,15 +228,36 @@ def _do_transform(self, qmolecule: QMolecule, 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 = [ + aux_operators.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 @@ -249,7 +267,6 @@ def _do_transform(self, qmolecule: QMolecule, logger.debug(' num paulis: %s, num qubits: %s', len(qubit_op.paulis), qubit_op.num_qubits) aux_ops = [] # list of the aux operators - apply_reductions = [] # list of bools specifying whether to apply reductions or not def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: """ @@ -260,18 +277,12 @@ def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: name: name """ - if not isinstance(aux_op, WeightedPauliOperator): - aux_qop = FermionicTransformation._map_fermionic_operator_to_qubit( - aux_op, self._qubit_mapping, new_nel, self._two_qubit_reduction - ) - aux_qop.name = name - apply_reduction = True - else: - aux_qop = aux_op - apply_reduction = False + 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) - apply_reductions.append(apply_reduction) logger.debug(' num paulis: %s', aux_qop.paulis) # the first three operators are hardcoded to number of particles, angular momentum @@ -327,12 +338,15 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ _dipole_op(qmolecule.z_dipole_integrals, 'z') aux_ops += [op_dipole_x, op_dipole_y, op_dipole_z] - apply_reductions += 3 * [True] # add user specified auxiliary operators if aux_operators is not None: for aux_op in aux_operators: - _add_aux_op(aux_op, aux_op.name) + 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) @@ -349,8 +363,7 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ 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, - apply_reductions) + 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') @@ -358,15 +371,13 @@ def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ def _process_z2symmetry_reduction(self, qubit_op: WeightedPauliOperator, - aux_ops: List[WeightedPauliOperator], - apply_reductions: List[bool]) -> Tuple: + aux_ops: List[WeightedPauliOperator]) -> Tuple: """ Implement z2 symmetries in the qubit operator Args: qubit_op : qubit operator aux_ops: auxiliary operators - apply_reductions: whether to apply reductions on the aux_ops Returns: (z2_qubit_op, z2_aux_ops, z2_symmetries) @@ -421,13 +432,11 @@ def _process_z2symmetry_reduction(self, 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, apply_reduction in zip(aux_ops, apply_reductions): + for aux_op in aux_ops: if aux_op is None: z2_aux_ops += [None] - elif apply_reduction: - z2_aux_ops += [z2_symmetries.taper(aux_op).chop(chop_to)] else: - z2_aux_ops += [aux_op] + z2_aux_ops += [z2_symmetries.taper(aux_op).chop(chop_to)] return z2_qubit_op, z2_aux_ops, z2_symmetries diff --git a/test/chemistry/test_fermionic_transformation.py b/test/chemistry/test_fermionic_transformation.py index d93f1b901e..6a03804850 100644 --- a/test/chemistry/test_fermionic_transformation.py +++ b/test/chemistry/test_fermionic_transformation.py @@ -15,8 +15,8 @@ import unittest from test.chemistry import QiskitChemistryTestCase -from qiskit.aqua.operators import OperatorBase -from qiskit.chemistry import QiskitChemistryError +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.qubit_transformations import FermionicTransformation @@ -168,11 +168,20 @@ def test_orbital_reduction(self): freeze_core=False, orbital_reduction=[-1]) - qubit_op, _ = fermionic_transformation.transform(self.driver) + # 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() From 1df361dd7e3f50314c913d26c97181f10082b874 Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 14 Oct 2020 12:35:14 +0200 Subject: [PATCH 136/197] Fix docstring --- .../mes_factories/numpy_minimum_eigensolver_factory.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py index 97a89974ff..561685df74 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py +++ b/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py @@ -16,7 +16,7 @@ import numpy as np from qiskit.aqua.algorithms import MinimumEigensolver, NumPyMinimumEigensolver -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation from .mes_factory import MESFactory @@ -64,9 +64,9 @@ 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: FermionicTransformation) -> MinimumEigensolver: - """Returns a VQE with a UCCSD wavefunction ansatz, based on ``transformation``. - This works only with a ``FermionicTransformation``. + def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEigensolver: + """Returns a NumPyMinimumEigensolver which possibly uses the default filter criterion + provided by the ``transformation``. Args: transformation: a fermionic qubit operator transformation. From 946682cd3a57bd362e31180c323f0253394e08ce Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Wed, 14 Oct 2020 12:37:57 +0200 Subject: [PATCH 137/197] Fix aux_op particle-hole conversion --- .../chemistry/qubit_transformations/fermionic_transformation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py index dbcc384e9f..0ccebcf2c6 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/qubit_transformations/fermionic_transformation.py @@ -254,7 +254,7 @@ def _do_transform(self, qmolecule: QMolecule, # apply the same transformation for the aux operators if aux_operators is not None: aux_operators = [ - aux_operators.particle_hole_transformation(new_nel)[0] + op.particle_hole_transformation(new_nel)[0] for op in aux_operators ] From 3ec6cf9ff26c3c397f55528c658a67f587a6d35a Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 14 Oct 2020 13:16:27 +0200 Subject: [PATCH 138/197] added reno --- .../ground_state_interface-42576cb6658a46e0.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml diff --git a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml new file mode 100644 index 0000000000..635203a996 --- /dev/null +++ b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + Introduces ``qubit_transformations`` for the fermionic and bosonic transformation of a problem + instance. Transforms the fermionic (or bosonic) operator to qubit operator + Introduces ``ground_state_calculation`` for the calculation of ground state properties. The + calculation can be done either using an ``MinimumEigensolver`` or using ``AdaptVQE`` +deprecations: + - | + ``Core Hamiltonian`` class is deprecated in favor of the ``FermionicTransformation`` + ``Chemistry Operator`` class is deprecated in facor of the ``qubit_tranformations`` + ``minimum_eigen_solvers/vqe_adapt`` is also deprecate and moved as an implementation + of the ground_state_calculation interface + ``applications/molecular_ground_state_energy`` is deprecated in favor of ``ground_state_calculation`` \ No newline at end of file From 176fdfb81c0bf1f9b5b5c69603f46f089342e086 Mon Sep 17 00:00:00 2001 From: ISO Date: Wed, 14 Oct 2020 17:25:23 +0200 Subject: [PATCH 139/197] initial OOVQE commit --- .pylintdict | 1 + .../ground_state_calculation/__init__.py | 2 + .../ground_state_calculation/oovqe.py | 662 ++++++++++++++++++ test/chemistry/test_oovqe.py | 148 ++++ test/chemistry/test_oovqe_h4.hdf5 | Bin 0 -> 25928 bytes test/chemistry/test_oovqe_h4_uhf.hdf5 | Bin 0 -> 32072 bytes test/chemistry/test_oovqe_lih.hdf5 | Bin 0 -> 44624 bytes 7 files changed, 813 insertions(+) create mode 100644 qiskit/chemistry/ground_state_calculation/oovqe.py create mode 100644 test/chemistry/test_oovqe.py create mode 100644 test/chemistry/test_oovqe_h4.hdf5 create mode 100644 test/chemistry/test_oovqe_h4_uhf.hdf5 create mode 100644 test/chemistry/test_oovqe_lih.hdf5 diff --git a/.pylintdict b/.pylintdict index 1c389d0b4d..1630e9c9fe 100644 --- a/.pylintdict +++ b/.pylintdict @@ -459,6 +459,7 @@ online onodera onwards oom +oovqe operatorbase oplist oplus diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/ground_state_calculation/__init__.py index ce8da55d84..326fe06e88 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/ground_state_calculation/__init__.py @@ -14,11 +14,13 @@ from .ground_state_calculation import GroundStateCalculation from .adapt_vqe import AdaptVQE +from .oovqe import OOVQE from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation from .mes_factories import MESFactory, VQEUCCSDFactory __all__ = ['GroundStateCalculation', 'AdaptVQE', + 'OOVQE', 'MinimumEigensolverGroundStateCalculation', 'MESFactory', 'VQEUCCSDFactory' diff --git a/qiskit/chemistry/ground_state_calculation/oovqe.py b/qiskit/chemistry/ground_state_calculation/oovqe.py new file mode 100644 index 0000000000..e30de59cd9 --- /dev/null +++ b/qiskit/chemistry/ground_state_calculation/oovqe.py @@ -0,0 +1,662 @@ +# 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 Orbital-Optimized VQE (OOVQE) algorithm. +""" + +from typing import Optional, List, Union, Tuple +import logging +import copy +import numpy as np +from scipy.linalg import expm +from qiskit.chemistry import QMolecule +from qiskit.aqua import AquaError +from qiskit.aqua.algorithms import VQE +from qiskit.aqua.operators import LegacyBaseOperator +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.results import FermionicGroundStateResult +from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from .mes_factories import MESFactory + +logger = logging.getLogger(__name__) + + +class OOVQE(GroundStateCalculation): + r""" A ground state calculation employing the OOVQE algorithm. + The Variational Quantum Eigensolver (VQE) algorithm enhanced with the Orbital Optimization (OO). + The core of the approach resides in the optimization of orbitals through the + AO-to-MO coefficients matrix C. In the usual VQE, the latter remains constant throughout + the simulation. Here, its elements are modified according to C=Ce^(-kappa) where kappa is + an anti-hermitian matrix. This transformation preserves the spectrum but modifies the + amplitudes of the ground state of given operator such that in the end a given ansatz + can be closest to that ground state, producing larger overlap and lower eigenvalue than + conventional VQE. Kappa is parametrized and optimized inside the OOVQE in the same way as + the gate angles. Therefore, at each step of OOVQE the coefficient matrix C is modified and + the operator is recomputed, unlike usual VQE where operator remains constant. + Iterative OO refers to optimization in two steps, first the wavefunction and then the + orbitals. It allows for faster optimization as the operator is not recomputed when + wavefunction is optimized. It is recommended to use the iterative method on real device/qasm + simulator with noise to facilitate the convergence of the classical optimizer. + For more details of this method refer to: https://aip.scitation.org/doi/10.1063/1.5141835 + """ + + def __init__(self, + transformation: FermionicTransformation, + solver: MESFactory, + driver: BaseDriver, + initial_point: Optional[np.ndarray] = None, + orbital_rotation: Optional['OrbitalRotation'] = None, + qmolecule: Optional[QMolecule] = None, + bounds: Optional[np.ndarray] = None, + iterative_oo: bool = True, + iterative_oo_iterations: int = 2, + ): + + """ + Args: + transformation: a fermionic driver to operator transformation strategy. + solver: a factory for the VQE solver employing any custom variational form. + driver: a chemistry driver necessary to initialize the parameters of the algorithm. + 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. + orbital_rotation: instance of + :class:`~qiskit.chemistry.ground_state_calculation.OrbitalRotation` class + that creates the matrices that rotate the orbitals needed to produce the rotated + MO coefficients C as C = C0 * exp(-kappa). + qmolecule: instance of the :class:`~qiskit.chemistry.QMolecule` class which has methods + needed to recompute one-/two-electron/dipole integrals after orbital rotation + (C = C0 * exp(-kappa)). + bounds: bounds for variational form and orbital rotation + parameters given to a classical optimizer. + iterative_oo: when ``True`` optimize first the variational form and then the + orbitals, iteratively. Otherwise, the wavefunction ansatz and orbitals are + optimized simultaneously. + iterative_oo_iterations: number of iterations in the iterative procedure, + set larger to be sure to converge to the global minimum. + Raises: + AquaError: if the number of orbital optimization iterations is less or equal to zero. + """ + + super().__init__(transformation) + self._solver = solver + self._driver = driver + self._qmolecule = qmolecule + self.initial_point = initial_point + + # initialize the operator and vqe to get the right number of orbital rotations + self._set_operator_and_vqe() + + self._qmolecule_rotated = None + self._fixed_wavefunction_params = None + if orbital_rotation is None: + self._orbital_rotation = OrbitalRotation(num_qubits=self._vqe.var_form.num_qubits, + transformation=self._transformation, + qmolecule=self._qmolecule) + self._num_parameters_oovqe = \ + self._vqe.var_form._num_parameters + self._orbital_rotation.num_parameters + + if self.initial_point is None: + self._set_initial_point() + else: + if len(self.initial_point) is not self._num_parameters_oovqe: + raise AquaError('Number of parameters of OOVQE ({}) differs to the one given in ' + 'intitial_point ({})'.format(self._num_parameters_oovqe, + len(self.initial_point))) + + self._bounds = bounds + if self._bounds is None: + self._set_bounds(self._orbital_rotation.parameter_bound_value) + self._iterative_oo = iterative_oo + self._iterative_oo_iterations = iterative_oo_iterations + if self._iterative_oo_iterations < 1: + raise AquaError('Please set iterative_oo_iterations parameter to a positive number,' + ' got {} instead'.format(self._iterative_oo_iterations)) + + # copies to overcome incompatibilities with error checks in VQAlgorithm class + self.var_form_num_parameters = self._vqe.var_form.num_parameters + self.var_form_bounds = copy.copy(self._vqe.var_form._bounds) + + def returns_groundstate(self) -> bool: + return True + + def _set_operator_and_vqe(self): + """ Initializes the operators using provided driver of qmolecule.""" + + if self._qmolecule is None: + # in future, self._transformation.transform should return also qmolecule + # to avoid running the driver twice + self._qmolecule = self._driver.run() + operator, aux_operators = self._transformation._do_transform(self._qmolecule) + else: + operator, aux_operators = self._transformation._do_transform(self._qmolecule) + if operator is None: # type: ignore + raise AquaError("The operator was never provided.") + self._vqe = self._solver.get_solver(self._transformation) + if not isinstance(self._vqe, VQE): + raise AquaError("The OOVQE algorithm requires the use of the VQE solver") + self._vqe.operator = operator + self._vqe.aux_operators = aux_operators + + def _set_bounds(self, + bounds_var_form_val: tuple = (-2 * np.pi, 2 * np.pi), + bounds_oo_val: tuple = (-2 * np.pi, 2 * np.pi)) -> None: + """ Initializes the array of bounds of wavefunction and OO parameters. + Args: + bounds_var_form_val: pair of bounds between which the optimizer confines the + values of wavefunction parameters. + bounds_oo_val: pair of bounds between which the optimizer confines the values of + OO parameters. + Raises: + AquaError: Instantiate OrbitalRotation class and provide it to the + orbital_rotation keyword argument + """ + self._bounds = [] + bounds_var_form = [bounds_var_form_val for _ in range(self._vqe.var_form.num_parameters)] + self._bound_oo = \ + [bounds_oo_val for _ in range(self._orbital_rotation.num_parameters)] # type: List + self._bounds = bounds_var_form + self._bound_oo + self._bounds = np.array(self._bounds) + + def _set_initial_point(self, initial_pt_scalar: float = 1e-1) -> None: + """ Initializes the initial point for the algorithm if the user does not provide his own. + Args: + initial_pt_scalar: value of the initial parameters for wavefunction and orbital rotation + """ + self.initial_point = [initial_pt_scalar for _ in range(self._num_parameters_oovqe)] + + def _energy_evaluation_oo(self, parameters: np.ndarray) -> Union[float, List[float]]: + """ Evaluate energy at given parameters for the variational form and parameters for + given rotation of orbitals. + Args: + parameters: parameters for variational form and orbital rotations. + Returns: + energy of the hamiltonian of each parameter. + Raises: + AquaError: Instantiate OrbitalRotation class and provide it to the + orbital_rotation keyword argument + """ + + # slice parameter lists + if self._iterative_oo: + parameters_var_form = self._fixed_wavefunction_params + parameters_orb_rot = parameters + else: + parameters_var_form = parameters[:self.var_form_num_parameters] + parameters_orb_rot = parameters[self.var_form_num_parameters:] + + logger.info('Parameters of wavefunction are: \n%s', repr(parameters_var_form)) + logger.info('Parameters of orbital rotation are: \n%s', repr(parameters_orb_rot)) + + # rotate the orbitals + if self._orbital_rotation is None: + raise AquaError('Instantiate OrbitalRotation class and provide it to the ' + 'orbital_rotation keyword argument') + + self._orbital_rotation.orbital_rotation_matrix(parameters_orb_rot) + + # preserve original qmolecule and create a new one with rotated orbitals + self._qmolecule_rotated = copy.copy(self._qmolecule) + OOVQE._rotate_orbitals_in_qmolecule(self._qmolecule_rotated, self._orbital_rotation) + + # construct the qubit operator + operator, aux_operators = self._transformation._do_transform(self._qmolecule_rotated) + if isinstance(operator, LegacyBaseOperator): + operator = operator.to_opflow() + self._vqe.operator = operator + self._vqe.aux_operators = aux_operators + logger.debug('Orbital rotation parameters of matrix U at evaluation %d returned' + '\n %s', self._vqe._eval_count, repr(self._orbital_rotation.matrix_a)) + self._vqe.var_form._num_parameters = self.var_form_num_parameters + + # compute the energy on given state + mean_energy = self._vqe._energy_evaluation(parameters=parameters_var_form) + + return mean_energy + + def compute_groundstate(self, driver: BaseDriver = None) -> 'OOVQEResult': + """ Computes the ground state. + Args: + driver: a chemistry driver. + Raises: + AquaError: Wrong setting of operator and backend. + Returns: + A fermionic ground state result. + """ + + # algorithm requires to have driver to initialize correctly the variables, + # however here one is free to change the driver type + if driver is not None: + self._driver = driver + else: + raise AquaError('Please specify a driver as you instantiate OOVQE class.') + self._vqe._eval_count = 0 + + # initial orbital rotation starting point is provided + if self._orbital_rotation.matrix_a is not None and self._orbital_rotation.matrix_b is not \ + None: + self._qmolecule_rotated = copy.copy(self._qmolecule) + OOVQE._rotate_orbitals_in_qmolecule(self._qmolecule_rotated, self._orbital_rotation) + operator, aux_operators = self._transformation._do_transform(self._qmolecule_rotated) + self._vqe.operator = operator + self._vqe.aux_operators = aux_operators + + logger.info( + '\n\nSetting the initial value for OO matrices and rotating Hamiltonian \n') + logger.info('Optimising Orbital Coefficient Rotation Alpha: \n%s', + repr(self._orbital_rotation.matrix_a)) + logger.info('Optimising Orbital Coefficient Rotation Beta: \n%s', + repr(self._orbital_rotation.matrix_b)) + + # save the original number of parameters as we modify their number to bypass the + # error checks that are not tailored to OOVQE + + total_time = 0 + # iterative method + if self._iterative_oo: + for _ in range(self._iterative_oo_iterations): + # optimize wavefunction ansatz + logger.info('OOVQE: Ansatz optimization, orbitals fixed.') + self._vqe.var_form._num_parameters = self.var_form_num_parameters + if isinstance(self._vqe.operator, LegacyBaseOperator): # type: ignore + self._vqe.operator = self._vqe.operator.to_opflow() # type: ignore + self._vqe.var_form._bounds = self.var_form_bounds + vqresult_wavefun = self._vqe.find_minimum( + initial_point=self.initial_point[:self.var_form_num_parameters], + var_form=self._vqe.var_form, + cost_fn=self._vqe._energy_evaluation, + optimizer=self._vqe.optimizer) + self.initial_point[:self.var_form_num_parameters] = vqresult_wavefun.optimal_point + + # optimize orbitals + logger.info('OOVQE: Orbital optimization, ansatz fixed.') + self._vqe.var_form._bounds = self._bound_oo + self._vqe.var_form._num_parameters = self._orbital_rotation.num_parameters + self._fixed_wavefunction_params = vqresult_wavefun.optimal_point + vqresult = self._vqe.find_minimum( + initial_point=self.initial_point[self.var_form_num_parameters:], + var_form=self._vqe.var_form, + cost_fn=self._energy_evaluation_oo, + optimizer=self._vqe.optimizer) + self.initial_point[self.var_form_num_parameters:] = vqresult.optimal_point + total_time += vqresult.optimizer_time + else: + # simultaneous method (ansatz and orbitals are optimized at the same time) + self._vqe.var_form._bounds = self._bounds + self._vqe.var_form._num_parameters = len(self._bounds) + vqresult = self._vqe.find_minimum(initial_point=self.initial_point, + var_form=self._vqe.var_form, + cost_fn=self._energy_evaluation_oo, + optimizer=self._vqe.optimizer) + total_time += vqresult.optimizer_time + + # write original number of parameters to avoid errors due to parameter number mismatch + self._vqe.var_form._num_parameters = self.var_form_num_parameters + + # extend VQE returned information with additional outputs + result = OOVQEResult() + result.computed_electronic_energy = vqresult.optimal_value + result.num_optimizer_evals = vqresult.optimizer_evals + result.optimal_point = vqresult.optimal_point + if self._iterative_oo: + result.optimal_point_ansatz = self.initial_point[self.var_form_num_parameters:] + result.optimal_point_orbitals = self.initial_point[:self.var_form_num_parameters] + else: + result.optimal_point_ansatz = vqresult.optimal_point[:self.var_form_num_parameters] + result.optimal_point_orbitals = vqresult.optimal_point[self.var_form_num_parameters:] + result.total_time = total_time + result.eigenvalue = vqresult.optimal_value + 0j + + # copy parameters bypass the error checks that are not tailored to OOVQE + _ret_temp_params = copy.copy(vqresult.optimal_point) + self._vqe._ret = {} + self._vqe._ret['opt_params'] = vqresult.optimal_point[:self.var_form_num_parameters] + if self._iterative_oo: + self._vqe._ret['opt_params'] = vqresult_wavefun.optimal_point + result.eigenstate_vector = self._vqe.get_optimal_vector() + if not self._iterative_oo: + self._vqe._ret['opt_params'] = _ret_temp_params + + if self._vqe.aux_operators is not None: + # copy parameters bypass the error checks that are not tailored to OOVQE + self._vqe._ret['opt_params'] = vqresult.optimal_point[:self.var_form_num_parameters] + if self._iterative_oo: + self._vqe._ret['opt_params'] = vqresult_wavefun.optimal_point + self._vqe._eval_aux_ops() + result.aux_operator_eigenvalues = self._vqe._ret['aux_ops'][0] + if not self._iterative_oo: + self._vqe._ret['opt_params'] = _ret_temp_params + + result.cost_function_evals = self._vqe._eval_count + self.transformation.add_context(result) + + logger.info('Optimization complete in %s seconds.\nFound opt_params %s in %s evals', + result.total_time, result.optimal_point, self._vqe._eval_count) + + return result + + @staticmethod + def _rotate_orbitals_in_qmolecule(qmolecule: QMolecule, + orbital_rotation: 'OrbitalRotation') -> None: + """ Rotates the orbitals by applying a modified a anti-hermitian matrix + (orbital_rotation.matrix_a) onto the MO coefficients matrix and recomputes all the + quantities dependent on the MO coefficients. Be aware that qmolecule is modified + when this executes. + Args: + qmolecule: instance of QMolecule class + orbital_rotation: instance of OrbitalRotation class + """ + + # 1 and 2 electron integrals (required) from AO to MO basis + qmolecule.mo_coeff = np.matmul(qmolecule.mo_coeff, + orbital_rotation.matrix_a) + qmolecule.mo_onee_ints = qmolecule.oneeints2mo(qmolecule.hcore, + qmolecule.mo_coeff) + # support for unrestricted spins + if qmolecule.mo_coeff_b is not None: + qmolecule.mo_coeff_b = np.matmul(qmolecule.mo_coeff_b, + orbital_rotation.matrix_b) + qmolecule.mo_onee_ints_b = qmolecule.oneeints2mo(qmolecule.hcore, + qmolecule.mo_coeff) + + qmolecule.mo_eri_ints = qmolecule.twoeints2mo(qmolecule.eri, + qmolecule.mo_coeff) + if qmolecule.mo_coeff_b is not None: + mo_eri_b = qmolecule.twoeints2mo(qmolecule.eri, + qmolecule.mo_coeff_b) + norbs = qmolecule.mo_coeff.shape[0] + qmolecule.mo_eri_ints_bb = mo_eri_b.reshape(norbs, norbs, norbs, norbs) + qmolecule.mo_eri_ints_ba = qmolecule.twoeints2mo_general( + qmolecule.eri, qmolecule.mo_coeff_b, qmolecule.mo_coeff_b, qmolecule.mo_coeff, + qmolecule.mo_coeff) + qmolecule.mo_eri_ints_ba = qmolecule.mo_eri_ints_ba.reshape(norbs, norbs, + norbs, norbs) + # dipole integrals (if available) from AO to MO + if qmolecule.x_dip_ints is not None: + qmolecule.x_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.x_dip_ints, + qmolecule.mo_coeff) + qmolecule.y_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.y_dip_ints, + qmolecule.mo_coeff) + qmolecule.z_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.z_dip_ints, + qmolecule.mo_coeff) + # support for unrestricted spins + if qmolecule.mo_coeff_b is not None and qmolecule.x_dip_ints is not None: + qmolecule.x_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.x_dip_ints, + qmolecule.mo_coeff_b) + qmolecule.y_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.y_dip_ints, + qmolecule.mo_coeff_b) + qmolecule.z_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.z_dip_ints, + qmolecule.mo_coeff_b) + + +class OrbitalRotation: + r""" Class that regroups methods for creation of matrices that rotate the MOs. + It allows to create the unitary matrix U = exp(-kappa) that is parameterized with kappa's + elements. The parameters are the off-diagonal elements of the anti-hermitian matrix kappa. + """ + + def __init__(self, + num_qubits: int, + transformation: FermionicTransformation, + qmolecule: Optional[QMolecule] = None, + orbital_rotations: list = None, + orbital_rotations_beta: list = None, + parameters: list = None, + parameter_bounds: list = None, + parameter_initial_value: float = 0.1, + parameter_bound_value: Tuple[float, float] = (-2 * np.pi, 2 * np.pi)) -> None: + + """ + Args: + num_qubits: number of qubits necessary to simulate a particular system. + transformation: a fermionic driver to operator transformation strategy. + qmolecule: instance of the :class:`~qiskit.chemistry.QMolecule` class which has methods + needed to recompute one-/two-electron/dipole integrals after orbital rotation + (C = C0 * exp(-kappa)). It is not required but can be used if user wished to + provide custom integrals for instance. + orbital_rotations: list of alpha orbitals that are rotated (i.e. [[0,1], ...] the + 0-th orbital is rotated with 1-st, which corresponds to non-zero entry 01 of + the matrix kappa). + orbital_rotations_beta: list of beta orbitals that are rotated. + parameters: orbital rotation parameter list of matrix elements that rotate the MOs, + each associated to a pair of orbitals that are rotated + (non-zero elements in matrix kappa), or elements in the orbital_rotation(_beta) + lists. + parameter_bounds: parameter bounds + parameter_initial_value: initial value for all the parameters. + parameter_bound_value: value for the bounds on all the parameters + """ + + self._num_qubits = num_qubits + self._transformation = transformation + self._qmolecule = qmolecule + + self._orbital_rotations = orbital_rotations + self._orbital_rotations_beta = orbital_rotations_beta + self._parameter_initial_value = parameter_initial_value + self._parameter_bound_value = parameter_bound_value + self._parameters = parameters + if self._parameters is None: + self._create_parameter_list_for_orbital_rotations() + + self._num_parameters = len(self._parameters) + self._parameter_bounds = parameter_bounds + if self._parameter_bounds is None: + self._create_parameter_bounds() + + self._freeze_core = self._transformation._freeze_core + self._core_list = self._qmolecule.core_orbitals if self._freeze_core else None + + if self._transformation._two_qubit_reduction is True: + self._dim_kappa_matrix = int((self._num_qubits + 2) / 2) + else: + self._dim_kappa_matrix = int(self._num_qubits / 2) + + self._check_for_errors() + self._matrix_a = None + self._matrix_b = None + + def _check_for_errors(self) -> None: + """ Checks for errors such as incorrect number of parameters and indices of orbitals. """ + + # number of parameters check + if self._orbital_rotations_beta is None and self._orbital_rotations is not None: + if len(self._orbital_rotations) != len(self._parameters): + raise AquaError('Please specify same number of params ({}) as there are ' + 'orbital rotations ({})'.format(len(self._parameters), + len(self._orbital_rotations))) + elif self._orbital_rotations_beta is not None and self._orbital_rotations is not None: + if len(self._orbital_rotations) + len(self._orbital_rotations_beta) != len( + self._parameters): + raise AquaError('Please specify same number of params ({}) as there are ' + 'orbital rotations ({})'.format(len(self._parameters), + len(self._orbital_rotations))) + # indices of rotated orbitals check + for exc in self._orbital_rotations: + if exc[0] > (self._dim_kappa_matrix - 1): + raise AquaError('You specified entries that go outside ' + 'the orbital rotation matrix dimensions {}, '.format(exc[0])) + if exc[1] > (self._dim_kappa_matrix - 1): + raise AquaError('You specified entries that go outside ' + 'the orbital rotation matrix dimensions {}'.format(exc[1])) + if self._orbital_rotations_beta is not None: + for exc in self._orbital_rotations_beta: + if exc[0] > (self._dim_kappa_matrix - 1): + raise AquaError('You specified entries that go outside ' + 'the orbital rotation matrix dimensions {}'.format(exc[0])) + if exc[1] > (self._dim_kappa_matrix - 1): + raise AquaError('You specified entries that go outside ' + 'the orbital rotation matrix dimensions {}'.format(exc[1])) + + def _create_orbital_rotation_list(self) -> None: + """ Creates a list of indices of matrix kappa that denote the pairs of orbitals that + will be rotated. For instance, a list of pairs of orbital such as [[0,1], [0,2]]. """ + + if self._transformation._two_qubit_reduction: + half_as = int((self._num_qubits + 2) / 2) + else: + half_as = int(self._num_qubits / 2) + + self._orbital_rotations = [] + + for i in range(half_as): + for j in range(half_as): + if i < j: + self._orbital_rotations.append([i, j]) + + def _create_parameter_list_for_orbital_rotations(self) -> None: + """ Initializes the initial values of orbital rotation matrix kappa. """ + + # creates the indices of matrix kappa and prevent user from trying to rotate only betas + if self._orbital_rotations is None: + self._create_orbital_rotation_list() + elif self._orbital_rotations is None and self._orbital_rotations_beta is not None: + raise AquaError('Only beta orbitals labels (orbital_rotations_beta) have been provided.' + 'Please also specify the alpha orbitals (orbital_rotations) ' + 'that are rotated as well. Do not specify anything to have by default ' + 'all orbitals rotated.') + + if self._orbital_rotations_beta is not None: + num_parameters = len(self._orbital_rotations + self._orbital_rotations_beta) + else: + num_parameters = len(self._orbital_rotations) + self._parameters = [self._parameter_initial_value for _ in range(num_parameters)] + + def _create_parameter_bounds(self) -> None: + """ Create bounds for parameters. """ + self._parameter_bounds = [self._parameter_bound_value for _ in range(self._num_parameters)] + + def orbital_rotation_matrix(self, parameters: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: + """ Creates 2 matrices K_alpha, K_beta that rotate the orbitals through MO coefficient + C_alpha = C_RHF * U_alpha where U = e^(K_alpha), similarly for beta orbitals. """ + + self._parameters = parameters + k_matrix_alpha = np.zeros((self._dim_kappa_matrix, self._dim_kappa_matrix)) + k_matrix_beta = np.zeros((self._dim_kappa_matrix, self._dim_kappa_matrix)) + + # allows to selectively rotate pairs of orbitals + if self._orbital_rotations_beta is None: + for i, exc in enumerate(self._orbital_rotations): + k_matrix_alpha[exc[0]][exc[1]] = self._parameters[i] + k_matrix_alpha[exc[1]][exc[0]] = -self._parameters[i] + k_matrix_beta[exc[0]][exc[1]] = self._parameters[i] + k_matrix_beta[exc[1]][exc[0]] = -self._parameters[i] + else: + for i, exc in enumerate(self._orbital_rotations): + k_matrix_alpha[exc[0]][exc[1]] = self._parameters[i] + k_matrix_alpha[exc[1]][exc[0]] = -self._parameters[i] + + for j, exc in enumerate(self._orbital_rotations_beta): + k_matrix_beta[exc[0]][exc[1]] = self._parameters[j + len(self._orbital_rotations)] + k_matrix_beta[exc[1]][exc[0]] = -self._parameters[j + len(self._orbital_rotations)] + + if self._freeze_core: + half_as = int(self._dim_kappa_matrix + len(self._core_list)) + k_matrix_alpha_full = np.zeros((half_as, half_as)) + k_matrix_beta_full = np.zeros((half_as, half_as)) + # rotating only non-frozen part of orbitals + dim_full_k = k_matrix_alpha_full.shape[0] # pylint: disable=unsubscriptable-object + + if self._core_list is None: + raise AquaError('Give _core_list, the list of molecular spatial orbitals that are ' + 'frozen (e.g. [0] for the 1s or [0,1] for respectively Li2 or N2 ' + 'for example).') + lower = len(self._core_list) + upper = dim_full_k + k_matrix_alpha_full[lower:upper, lower:upper] = k_matrix_alpha + k_matrix_beta_full[lower:upper, lower:upper] = k_matrix_beta + self._matrix_a = expm(k_matrix_alpha_full) + self._matrix_b = expm(k_matrix_beta_full) + else: + self._matrix_a = expm(k_matrix_alpha) + self._matrix_b = expm(k_matrix_beta) + + return self._matrix_a, self._matrix_b + + @property + def matrix_a(self) -> np.ndarray: + """Returns matrix A.""" + return self._matrix_a + + @property + def matrix_b(self) -> np.ndarray: + """Returns matrix B. """ + return self._matrix_b + + @property + def num_parameters(self) -> int: + """Returns the number of parameters.""" + return self._num_parameters + + @property + def parameter_bound_value(self) -> Tuple[float, float]: + """Returns a value for the bounds on all the parameters.""" + return self._parameter_bound_value + + +class OOVQEResult(FermionicGroundStateResult): + r""" OOVQE Result. """ + + @property + def computed_electronic_energy(self) -> float: + """ Returns the ground state energy. """ + return self.get('computed_electronic_energy') + + @computed_electronic_energy.setter + def computed_electronic_energy(self, value: float) -> None: + """ Sets the ground state energy. """ + self.data['computed_electronic_energy'] = value + + @property + def num_optimizer_evals(self) -> int: + """ Returns the number of cost function evaluations in the optimizer """ + return self.get('num_optimizer_evals') + + @num_optimizer_evals.setter + def num_optimizer_evals(self, value: float) -> None: + """ Sets the number of cost function evaluations in the optimizer """ + self.data['num_optimizer_evals'] = value + + @property + def optimal_point(self) -> list: + """ Returns the optimal parameters. """ + return self.get('optimal_point') + + @num_optimizer_evals.setter + def optimal_point(self, value: list) -> None: + """ Sets the optimal parameters. """ + self.data['optimal_point'] = value + + @property + def optimal_point_ansatz(self) -> list: + """ Returns the optimal parameters for the . """ + return self.get('optimal_point_ansatz') + + @optimal_point_ansatz.setter + def optimal_point_ansatz(self, value: list) -> None: + """ Sets the optimal parameters for the ansatz. """ + self.data['optimal_point_ansatz'] = value + + @property + def optimal_point_orbitals(self) -> list: + """ Returns the optimal parameters of the orbitals. """ + return self.get('optimal_point_orbitals') + + @optimal_point_orbitals.setter + def optimal_point_orbitals(self, value: list) -> None: + """ Sets the optimal parameters of the orbitals. """ + self.data['optimal_point_orbitals'] = value + + def __getitem__(self, key: object) -> object: + return super().__getitem__(key) diff --git a/test/chemistry/test_oovqe.py b/test/chemistry/test_oovqe.py new file mode 100644 index 0000000000..f9b751cd42 --- /dev/null +++ b/test/chemistry/test_oovqe.py @@ -0,0 +1,148 @@ +# 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 OOVQE ground state calculations """ +import unittest +from test.chemistry import QiskitChemistryTestCase + +from qiskit.chemistry.drivers import HDF5Driver +from qiskit.providers.basicaer import BasicAer +from qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import VQE +from qiskit.chemistry.components.variational_forms import UCCSD +from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.aqua.components.optimizers import COBYLA +from qiskit.chemistry.ground_state_calculation import VQEUCCSDFactory, OOVQE +from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.qubit_transformations.fermionic_transformation import QubitMappingType + + +class TestOOVQE(QiskitChemistryTestCase): + """ Test OOVQE Ground State Calculation. """ + + def setUp(self): + super().setUp() + + file1 = 'test_oovqe_h4.hdf5' + file2 = 'test_oovqe_lih.hdf5' + file3 = 'test_oovqe_h4_uhf.hdf5' + + self.driver1 = HDF5Driver(hdf5_input=self.get_resource_path(file1)) + self.driver2 = HDF5Driver(hdf5_input=self.get_resource_path(file2)) + self.driver3 = HDF5Driver(hdf5_input=self.get_resource_path(file3)) + + self.energy1_rotation = -3.0104 + self.energy1 = -2.77 # energy of the VQE with pUCCD ansatz and LBFGSB optimizer + self.energy2 = -7.70 + self.energy3 = -2.50 + self.initial_point1 = [0.039374, -0.47225463, -0.61891996, 0.02598386, 0.79045546, + -0.04134567, 0.04944946, -0.02971617, -0.00374005, 0.77542149] + + self.seed = 50 + + self.optimizer = COBYLA(maxiter=1) + self.transformation1 = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False) + self.transformation2 = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True) + + def make_solver(self): + """ Instantiates a solver for the test of OOVQE. """ + + quantum_instance = QuantumInstance(BasicAer.get_backend('statevector_simulator'), + shots=1, + seed_simulator=self.seed, + seed_transpiler=self.seed) + solver = VQEUCCSDFactory(quantum_instance) + + def get_custom_solver(self, transformation): + """Customize the solver.""" + + 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) + # only paired doubles excitations + 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, + excitation_type='d', + same_spin_doubles=False, + method_doubles='pucc') + vqe = VQE(var_form=var_form, quantum_instance=self._quantum_instance, + optimizer=COBYLA(maxiter=1)) + return vqe + + # pylint: disable=no-value-for-parameter + solver.get_solver = get_custom_solver.__get__(solver, VQEUCCSDFactory) + return solver + + def test_orbital_rotations(self): + """ Test that orbital rotations are performed correctly. """ + + solver = self.make_solver() + calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=False, + initial_point=self.initial_point1) + calc._vqe.optimizer.set_options(maxiter=1) + algo_result = calc.compute_groundstate(self.driver1) + self.assertAlmostEqual(algo_result.computed_electronic_energy, self.energy1_rotation, 4) + + def test_oovqe(self): + """ Test the simultaneous optimization of orbitals and ansatz parameters with OOVQE using + BasicAer's statevector_simulator. """ + + solver = self.make_solver() + calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=False, + initial_point=self.initial_point1) + calc._vqe.optimizer.set_options(maxiter=3, rhobeg=0.01) + algo_result = calc.compute_groundstate(self.driver1) + self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1, 4) + + def test_iterative_oovqe(self): + """ Test the iterative OOVQE using BasicAer's statevector_simulator. """ + + solver = self.make_solver() + calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=True, + initial_point=self.initial_point1, iterative_oo_iterations=2) + calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) + algo_result = calc.compute_groundstate(self.driver1) + self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1) + + def test_oovqe_with_frozen_core(self): + """ Test the OOVQE with frozen core approximation. """ + + solver = self.make_solver() + calc = OOVQE(self.transformation2, solver, self.driver2, iterative_oo=False) + calc._vqe.optimizer.set_options(maxiter=2, rhobeg=1) + algo_result = calc.compute_groundstate(self.driver2) + self.assertLessEqual(algo_result.computed_electronic_energy + self.transformation2._energy_shift + + self.transformation2._nuclear_repulsion_energy, self.energy2) + + def test_oovqe_with_unrestricted_hf(self): + """ Test the OOVQE with unrestricted HF method. """ + + solver = self.make_solver() + calc = OOVQE(self.transformation1, solver, self.driver3, iterative_oo=False) + calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) + algo_result = calc.compute_groundstate(self.driver3) + self.assertLessEqual(algo_result.computed_electronic_energy, self.energy3) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_oovqe_h4.hdf5 b/test/chemistry/test_oovqe_h4.hdf5 new file mode 100644 index 0000000000000000000000000000000000000000..f5aa60b0cceacbf030b2363bdb07a4e70015f137 GIT binary patch literal 25928 zcmeHP4R}<=xjspNgjEm_HKh--gor^&2nKCPcXJj5Tp^_tLIMixZsL+GWOw6clbfLU zY(YuX@|bGj##>5*VoH0d4N57(!dbMG@KXikN4!CD6)eUJ2K8!8d9*WU=H0VUnOq$En{BkPy_p$cV6j*4Q6hwhQa5Pj400Ys=ws|Go;-Xuru(b3T|G>-d;iQ+IM zGFlJ@@jM0FU%JmE2p(S2waKjMKb;gM#0qnG7;{Fk6S-03G-)Qw7vIlPElUJnEIR?N zWiZPRxP_(Z95-iZFfRUfUe2#$N&+tKP#H2&W_U_zKr7ew!9%%$;@b zl(8v4N*QNyJMDU-mYk=hr>3TAQ|SLc)*7`bWA7e2E?q7dn>L;slVzL&N>1f4xq#s` zr4E~0KfzH@;&wUhn*2hGtH7#H)sz(593;uJl-Nr2C2nV0fu?Yw$!>KQIv>)z3Kwef zosKe-+gYR^YfVlaud!RoP5Bl1MOOXYsTv#cX(}kSmXzr2HiyM2m)fm%r>jCg-RZDu zc7Jwon)${rr58UNq*-78smK=)0$Pl;{QbF!In&+~-y52;-23_|F*d3tHa(+B+_iG) zn$J%+Nrk`v(_6Did6{q9SRW}*A9mq~_hd9lr$?_nMasohy#D-K<@(Z5L9Q>=@%n8W zSpA8tzH~pUFW!>O>I)BV`_H0(CLfe0o%>+Mo(+4YqGg4phGBK0BicJ`&68V1f7f9S zLraY0=zGpEGG~g$;0N)O7w%|yBxdGeaaP51>AOZNl?1280|$RXj% zbN|8Yv}(`+JNM_~^(6QK7|K?v-o9&P>*Vv^g=vq>*i~~;yyLFqkN%)(6@}HGtvOeB##_MciBnIm zYugjQqv-Fgr#9d8fw!`5>8azftqk+F#vHGI) z^tw?Q1N(jG$A8G}eflg)+^CPy3%@9;woIRYvp%kB#0qg-n^^f@r7M4@Ym+v(){R;E z_HG^Q$LDmO z6?D_TY+n`Qe@@bLW8^@2qlI!+KdEb>_~0OQ$@eqgak}dFz0lRA@X@#`BL9MHGj)-T#XRARf_jfxKAe~L=Ml$i_=F$#^HVv zr;UtD;>Gd9BjY&9LOp-)6b?Oy7Z?-6A($QWPTa`IxNlq9@w)o7N9(HN`Dpu|@xhcI z#a?HV??%`WE~QU4=(C+mU%y88Wu84O;eSKduZ2P7ot6#+HU$i08Qyv z0aGSq&6-TkvS-ewiChqV{l1L1pVgM}iT~@ACGLjBc9Otv^%f=dkY30cv1>l@bA+Z+#Rj^&R`(mA=2FYl!gcTQBDY zD?~N|Lg$~Vt{NH6Q<}27QHx1P@TM?@FZ}_|wy@CAYuh)INf6YOmhxTt_PE(}8E&C0S zfR?g$cJ(-A&v$!~-6uQZ$McggNgTIVr-cD<^8lWny*n}vB11~AWr^2OrqD4y0nwX)#Zy=TSOdS|T2*|1%5p2FWPetJo4xp zV(%Th-S>W7B^tON;9>voU)&m9yLg|tmHRJq^gkVyZV@#X%T7IUEC2awzx36onQFY+pBj(rz#sToz}pMFt8r8Ou-xe) z?Akvs$Q?;|Z*2N_U%C6oV$K(mFNVYiJnT2|{sABG13c{LU+@Z^5uciW^b_iXPvFrn zYW^SO?E+rSKXe71?c_Ws@b)4;=oE4PPrt0bWkjdIgGcD9ocAyMG;%!bsnK;U`@1}T*w=jU)s16_PStp|KQ&(M zPpwzAp3%S1EBL{6)p)f(HD2vctykc|TR8sFF5tmGbbxUKKfuF|@uZ%w5I=OGp0D5^ zJOht@hfbke_yHa|hwjzm1^l4h>hS{oU_1d2JH{!-GsY$Gup>UijrwXnVTWG9C-e+F z>}W6IN523MJFW{}piAfq{RMkx=|3F*;7!fHdOlP04_?7D+N>^24DbOR->e*<>hruT6fWYc<&%ZuyaDV4l9aCV2w& z7YF7|6NAf56DKI|sqvl#@q|l5^~~1G-C%t`mB-8k#WSJ(&EgH$zDu=-^3hp(PUsPO zX3w4a2zdV9Iw>-v#k|f6#Ff|L!9k+Q19a?+u4wb}cWCOOK4}=+Y~v zs}VgyS9q>$=F@otC;V)FM>F5C{zm4W(0u)qqx?73`*pH+&k?U6G(9}H{^6bMYWsC@ z3!c_5dW_;`l;2}?WitD5h8HN_EZeirR}6fnlKb-<4EEzY#_+`c@?*{S75xRcy}Cu2 z$rI8b^*FFC1g#!e4b?*uN|r zUHw-{#tVnZbsv}U!FGw^Yu~2nZ0hH42Uc48H^0vK@a*y1nV%C3FGxDq1^e@!f;Xf+ zfp}t`*)K!f#b<_ z6uW`a-{WKPh3LP*Tf)ansQ%|E{F^!d9!a+&khf}T zUn}#k3*=uwdw1#-d#l2~;b1WTz?9N;)&-v9;8?tsY|5`fU89vA^Y$5hmW&V-p8B(3hd+~wuPyHc+W8)}D z&$2!lUKuZ2mG+vC=xz!0BW&jJ(RhUC{qv8k|3_t=KkU~DK?qw29`g*zLjRs!I_NnF zkMY$+c`jFUPU0Ny*MG5MhyI1Pyvn>3z<<-*s(Fm-!w|@SzZbj+#ube(c-oS=S_nz@#D2WLy$2HqVKS!)$L}5E)m+i;GQ>aa~m!sw=hv#_z9{-*K=A zFaP_)+qtfof3!FBJ^^NdxUy*|ADuN1i#=kTf&Fq5*-+3O7@%D>M z$&3t*6qT@iAu~0lH_A(9mXu~_e)Y|qd7eF&WwA^HlX>5tbLN@MQTLd8Q+>1RW>lzLs}?0PUs_`J1`Qf0P(SeF?FBH*6f_|%pv=JId>@G< zM1-2!&4xbuSYI#Fkc@&bM%}|NltIzUz;V3nc!cLBo)Kq5eu22kmb~2TA3P5M+^9Bs zT)^E>5B-D#PBh@Ef_OzhFwX-4SJ|3hV7~!(@Nx*W$K5X7SC7kuGH!=_E(tMtgX2G$ zdD<6^{a$Wg%xumB!EyY_u33Udw{XCf>S}Ag8-&faGZZ?qAew~S7K~(N|HUn zKPE0ZDb^Yq`6lS311iP3hYHo=-;Ps#{=)ShVdpK-oyVAQQY z%dekp;h$IdqZw`>2#y%@;k!0$89wm8=FIp3=QnQo!hE59MMOx?3i-)Sy-Uv>t1$19 zAMZMX*8ehg*=8rL|Kcn;*sW)U*|zAjb+lfd5A9#$?d297l=kMf&|Zf2MZCS->YUPE z9uDnKtaxq_^B)p)yLl7!-}c)}YrE6_AMok+H1ogx_;ZmgzH!dIsWPpf7J6*bL$v>h zcTY6`iN<#Z+K2M?@_+L7g$pyb29sFVb4tcg5 zmuhLZ(tL}*)GlXEk$Jkr>oY2Ny)&5CuWH2WQCYadBG2J8HTurdX+(d23t})|ac}7q z?;-olLo;3sDY;**2fPz;m;+u0ysJJfyJ<}H-b%T7(>B9=X1pga3H9pr#(`P#VcV+p zEBe0evY#qj+>qfu1AEHzEq2YLIK-nJ{WSxB=m+s={|5Y#r{&4xx(qpK4&F7oe`3$s z^6~pm_?(J;TFx8zbxKf!nKIfDhxWpamv-%=I3pf$*v^PYJK{J&mF9!NBNt39DKw8f z_vWJozLU)Xv$JzQpRv+qM;zKaP5x~10*W)@5r_Vac(fz#t0Cn>dc5|7+->S3KCQ+V zNd5=^6Q9{GPudyh_jS+I`I`@v{-f=h<8Mo$rtP7@$13I9kGGz?WAx0htfLK^u8m$Q zq#U|yXv)w`seD~4ZkKlsIQ3$sJh)4*l1W{i!s5}7DSoQt?4a`*c0p(%z7czyQ#jQ* zFL2_wXXJ^)dH**h{(H)}^bOLOfXB|39eGju<)UM0mx$S6j@HYjot;%E&FH}UzuTG7 zE@0b?u<%1??)h-nbZJrl^g~Tf*`-f9E?4Y>B;NmvpHGqJH(n|nYc){uQ(5+KK+ru? zq=2d>r=G}pMK~VF`}YTde?F|${+mv1GIL%CN&C9OyJJP68SSrw;3sjrj5y5E4*s0b zPDVe7Lp%6$`RO$C{`3L1jjsNC4;wLJ`P1*nXdea(4eTx)`Ze02os51Ehj#GiM0;)J zw|%uL^10)_o4z0Yzbl+eE}6?8E^4vXze@i0QuwR~3N8p|zfpMu;Ihgd^WANw|#GB_9b)cH5=z9=lmcc zel6HL<(=ETue@QXC*E;0_Ehh(<8u|>&zqpeWVoSBj83q!sx)g-vdx}I=+QjE7R#Q8 zc*6U4C|)*mf3D(p*yzt2o|kT=26ij%`T2>yv``Y>u=A4p^V1SuQAhvf&sE;8_aqg! z)*L7dnxq@%$=C|3%UT#~{+{Klr>mR)7P|T)e3Vj2cwT2MfDeXJ)AhE_F84gWB!G_% zGIgN!S2qL$#`6%*wIFy;g(T1iRcoR(DK-NPiK+CtG&(6V$?8arXD6h-+I3Ev(19Vp z{5lO#d^9(i;mx3hJlL2#_#3>B^1N;j!@7kG`gLEonTCjJx5&@K2ClcFnFjui(cyZn zxZPNDW0$X;uD-f1b>-=Onen~w<<1FbTdpg|jV-SmcJz2o6aoXddyeYJ4IXu7_&mcC z&iHu-5`Fpg{6{~3I`uo(aTbP9r*ix}3eV}l_BDqW4%HzYYR2Jt;%BNE7YNk>ch-yp zl1U+a?&JwQ3M#_y@`T`a+;{vs)r|YSwO3zPKVH|my83)v{G0K?mG8wr&ZgfR;mK3Z z-fXaUJAeHCwLv59S>ze~>wfno#1$TY?ReU^qo$$eTQ2d^RQ5Szg5B$4I#H5>I zcRi%*>T9czn3@o2Pa0=SiH=WZF|cxUydy3e0%GNH))Xv~1bbwR-8x|c#*5|1-rNGd z_|v7$s`z9zMmyr!nq!HplV4Z&ue+YF)%>-;F6QsD_y6~D7K5rIv)=Z%XTW&>9|a>0 z`6s{dbYE}$e$*c&%6*;sDK2oG|MBmi^@jDx{a`%q`)OUi|Mw*p2t2S0j-dK{Fqlum zJXMdv@9xp>8^ZU_JmE}G;dj3$1h?Zp5zx72Ty51}eO(Q>u61?w`55vyMS8Yi$TlNR3aiIpS?GwiM+f z0_DDsezOj5*x0x1B9k)`#@XYQCQ3OyV=}hK{T%lzv|nlNu4jHnXncOdueaiCVEM}L z*T=&AM(IL#pWirL{k1Oe`@04ne{YccAdi5noc?~{U%YI()`8XguM-Ry@9#v2D!lKt z4e$4^Q(T|M{L0tqW>}{9^yTg^CJ|5Xpw zjH~Ol?z-Yk;W^_?8D`f3^lQ;9XTM+Jb$+COuZW@jsBPGg;Ve@sJfG)bjTH>E=GWEz zo+TZtY9)8)BT$LMcpe(p7nZMlotc3jQ8(_|UT3w(hkTvz5zh7D!&e3B;7ao=*H2(i z5Ds6!P;l1|k5?zMKT%jVcR}pJ9VgB2`SzSOq9Dgye$R}z=S4)Bdku(N@bUu-%^Sg< z{ruS6gpYTbTfUGtI`G};=9L#3A378=)`ds=>j@)wP5Jj$v+t(uDSdv;G+V$A;?cf# ze%l7Q6L*@6!GBs}(_>yC(PsZkXt{3D7$8B6Ms2^NUh(|lFQ(VuuE)kD*j1S|+_C`L@j(SBtQO}4+JB}CQ z$9X|K+Oc2c1$BwK!udk`wbH*Q{*gB$|Hl2y$UpLmJmYwc{Np?s_bcOmX56n_^I+Kj z(2lx7ogvS-Ut)Zy)7rZ~G&kY5Eu!x3#r=LKPFH`e3$@3GTqpTJzT;x-Nmg1=a{3|k zL|dXY#TG;C^oK+7(GFrwvMEsdxiKXTf+p9bo(5L^yaF(!3 zM@Mf49LE!e7Zv;4Co`HqKO6lO>HUb?0be9Eo_6YA@f%z?X~dAukAD2~R_Z_K*Z<40 z^L{NQ8awN;g!-e&QcClEkuQ-~XdHu8y!auncOI1P9{F?H{aM>&1uvG+e)lW*K%Md!eL?=>65SFGV#!#^pJhPmd2_2h*982@F!zm^|Q z{ImEuUN7{t&JI%5n?%mWcOS>pOU$Ix7 zYg9B&MqO>8^?9lvaSz~2q^j|mGf!V&xI$IWxfuT^lFBD_AI0pe6#wUX?+efNck+25 zuwI;>NUDA1o%>(S-mHWrZWJDO%?q>O`jV(WXS2)>@qqh)#>Mrl>V(2YHovHITDIVL z*GZXU_Vh2O>qUnr>!=@^oNS&dW?twL2|OJTW&@t`s@ieAX!QS;PS5$&k9K}R|KcYu z{p;5Y=-qV!3C)0nf%D)NtC;RpMTbO-qHT=VPP5U+c=V|zVU$ZV4 zG4Ig+SlUGWWhwmY_p5`vURl3HT}R?U=|`8Yay9;8KO#(L`HEe($9>~>5cT&N?MLee z*UuqgQTvM#?VB6`9Tf@czNF#JvQ1L$)RV~iVTr|$^Q*y&Ojk5H4+`)8oIRj4VmtRw z{m?swSxgV>wRTcY?OE#n6UF9@rPccVhcG+ee{}1D91u8ujYj7_DsN<)q}#__covUl z|FJOr^Zh~93H7Y(SGdoxo>cwMsY(CJIpI`wPW#36LP=EZPSjhGpzcc=KAl_j=Lp$K zv$HGcGdFVM0XAz;q6H&?WaQBpT#4>Mb~~snydQ;#IKwq z6^fqqx?=OI^~3gUn#3bgRgbLuiU%Ap3r}g!e-Zl6rFrFv(4c92f1*vw$Kv^Kn)Tre zXkP^Q9GW{6d+SQ=`JXG%reCXm5T7Tlo}xYf9nza!@%)c!IBd&A=r>zX?JRJ%AF;&F z<(98K^Ui#_zYW&lS3~_CX}qYM|FFImy6`btySNAN%V^H~ZTzYB{1+v(tNpU8ol=E& zB0K-hdi+j`XK6mo*GpSX&%!uF7oP5m8v9C$^V9J2qQKi{N==$-&VRyqtL%O6*OS6l z1v}XJ598Va`1w-4%bs6ylQR?geM7og>qmt7C#z_`mfxf@E9H+TmFFxCpNneXp8)?_ zyvo0hXC;1)*W339s;*cZIKPFi@$%>jgn`L;{^vS2yfg{oM*N!;?^Ntn+iH1ooM z4vr^TGu9f}>dLR5I=_o3jwOb# zQu+<+qdosWq4r#jAHa+Iq^y?O^FLow*F~w0AN@Xt`!kCl=U0noc9!-@j~F8dymNVuU{`b8bX#*{{^bfb^1r%7`Bk&Skm&Z)os4v96|oG zXgl=d)XY~+{ByjHe;U^s-8s0Ij+e(n^{L^fOi=eLu3L^PfcV)t!tHupF}}6?Jj1cH zQ&SZQEb^C5z%hRJ_RS3`d{;9h&EV zWxaEJC!HPcx18H(yW)t^?K8sSO10;Ijzq{I#m@cW^UrRn85?vcj^zRi6Lh{Ac?M zO%$)%PrubY1D-!E+XS5-0(ip4oBQ>dIK0m)((oLm@=1A9=Y{p-r0rN@Jhue?N2z$^ zpT2+5^UT&ofP&ZA>GS{G7}OQ|#k{cN(Us|-g91UG>^lEL7ca;2AD;gRI~TJQ`^x6p z^Pin>SsFj;dY@v$^S?}b&GIDgCwDvbLlfPfNB30jvw(kQk3ZJ4X;xUvSnc`$KJ`zN zbDyNzbJpxG0RD+n(D@;N7iHGZe?OQ8&;Nw^54(B)m?dE=zE^a`=T~`O5u(_Ye#Bj@ z9ZfmX7OS%V$vQl>^LqB2Xn7|>egyQlM=All&hBLTqe;BYWdGq`KLVV{E&?hAJcI& zh5vacwEGpt&qi_R8`Be+z~5deybfhI{~=ZX1omID$ff_+b@*H!k41UjUgy$3T}QZHXuPVO-ski9OC*)& z0u7%v@t+I)7i#d_TlM^NypjJ;r)tkZJpWhFxSZ4cmd1h(_6oC}!TpM~*$|1(4Y>mE zk&WB=Kg|A6bB(UVGJ_w)mq{_*`SbtfelOt%zgWiaON;RJ-b@^7eSSkQfPWvgW6Tc`R%D zXOjEpl7Wi5zu^1dH}kkfUHI;wJI3kiuXO=G?_L5OmAYo6`ko8He8}~Swd!$xV6GZp zeLY(7V`&HfdI*}`@^G+-PrFs=47d*srhlXMuS~V4E4fbUs`mIWH|6s#>ZZT8*IDiH zA=mRkm7xB2cg(lc>qE;&F8?7isaxxXq@M2(-Q<`sphsCB%YzrqBTW-xR|yx*gNnlT z?DG0aLVMb-w07?oHx6z7Y}RMNC6$s-+JTNsdt8(ezxG)F+d2)cl-f>O=2+p?YSZ^Q zCqL@)z(x7M`6uq5fA3F{H{j2JebMx<7JRbqHzE3Y^E0#F+9Zq*yZ5Cq+W)ZIdhB8S zkK62ZzUi{lF8j#)p7>?V^wYvy9bY&(wd%Aqv1>>9-NPfY0 zQ^#e`rUyN6(VRV}Z|g-#rv=2{1@_^M7QNc$ql<9guG_-u3-t`tGf>Y!Jp=U&)H6`W z7^uDbds70ZLew#G<38`?*7HG?pg!-I66>Nq_||fFuO)=&;>VNenVCAz$1tYatp zl5H4c38At6U+;c@&A#YHh+ zO>r@zf*=U#@sVQv*Zm8#fRrDJ$Cs2^n5Jb81N&KbX>HWO$iyYc&&Rx{mw~a7vFcC# zgPEQBHG|eh?OGdmEE(+U6=ZJd>k$|f;OA4)&C?~oLvC(dGSJ`4S9x%C3G@my4-E2a z>rv9v-N{EDmk=knP&aS6c{}5hUP_uy9>Ma! zKyx22Ul%`BtB>5rFCf&sx1X=PjiREnM ziF*exzs%{D>0aA@^{?F6G>-#XW8(RVao?`pXd27=TxmRi{MtK;l-m(u4Ia9g4SPFr z((P~8nq95Z>%@U~F+A6$dGA$g&nTAl9@Okqw};&Ru_?!HI8IJISNT;JgSOeJ*#lm; z*!;qqUzE_j#O0?88R4vje%!q|-##3|$M8*f`^ZVM35r+ttvC5Pp5=_*=&iW1^rph~ zzTJ_A42$`lXZzYd^m2ucG{t>14F+~J*9{{5bILiWE(f=fRFsv z^Pjk3IPSRhjfY|P8(f}OQFtXw_w=Gbod=Qp;C-sSoAME@nMdObi@e>Y3U7JUyi3Bb-ma7rV{d=O|>R zbNt#`EiY}+a(u#ynmgu83ch;=x7BEPl!9;;*ZN#`ZnBdT3q%ZoBT2>^kFLZ)x@mT8I$&LSNlX6R$MTe*Y6X3;ik=Vvu(ydweuLURIwbml_z^H z2^^K97|^lCupg?d;--|@+@jN=MZEd;%de`IPhhGhMcXrcO}gk3mOUq3|)4WH@%!n&$hUY#UUzhFUW zJrW|iPvTzGe4?Vb<)3+8bbeLlAK_OYVvns#myjN36=9ED)E|Fe+Bnm+o{p|2=!U}| zivIWMUtt09d`R<}G3r!g1m)^g?kf-Q2*m~8VC7=fCBP{_?jP)(J45Dg&YLFCfd)C z!io1Y$k3X{^M~#~WxVG&o8qItS${es1_L$hgvf^jsT=ou7@Yl`M-^$Nzp z$g<7YcalOoqQS1A6hgR8aYwIl!MOKrEqcGY@2?_q5gJ%C|s?xWbaz`Xr@Z9GC1O@bDT`YU{vsGUE zy6-o+2UUY~{$56SHV3^`c;$cU`iD@{CpoIyLx`aCoP^jf3hxhPVGrYcdVX&9AmMz2 zeVqIPT)l!^yaRKu<(|8E`+K?okbCYb5290^&Ttzdt zMSPv`g3l?R%OHXhNA>$B3_I#yOi3a4^T+#h=P$yaHRk`#1w-cdLJ8ykcljS@0dfBC z3=K_N2_K~Ldhzp7A8HuQm10(E*q5;y(R3 zik{t5K5vNU&ZKY#XwYjZg%GY&oTz44Fz!>e7QJ6}{i6M<=&_EuK@g=gJJI-g+gATNJ!FE_6s)fRy& z?^8ZmM+sZ*Q+7^)p+2sD-l{ID^N`T7)SvP$idS^Kut#-&7BWIwzx{FF1P~eh^TLm_4Y1GWHO@Yr zJ&u$X9N}}fhn-4Ht>S`Ti~Zq4&kOHAO*;wp3Ktv}qey*x3G$-(qYoR(MVI>J#!IeB z(#S1qUY}5V7Md8uZ_;zDS0|aP1l-iLu0sb0U690ozp~`C`wjM~wb$?Qibc9g_alDd z^=_`+#&m;wL_SY(EdCrCtfMwZhJUMeT`Vu1FBoWLP{MqsBt8lB7HPnBTUGO*$wY5SI>fK2k9LLR zThw-XU%ft+JwMh?w^OR_?z+H9N$9 zhJ<`>z%^L7xpLWNQCuA2l`i6oSdJU8OR3AY)nqrvaDaTRl4 zuKYvGqvOtfuhu?2YWMPtmg@b)e8uNd{cb_N_24V+=NVchengX4&g<*fo@O@lIMSnn zgnXXBZJRzUIpe#PdGR?+LwqK?5N?o$_$(FwJ~qyZ_1APx{0bYNBQQ^E(wF>&aP_id zqvu8|Uo4dPn{o4Ti;+hZYV8|Z`Nf7WlI|x#PWG|=YDX>Hkh*a%M|w!fN4T4Er?WGr zsLL6xVV)7o|2(p_qpR;`H9HV)A?Rtst)A;&=|H8~l5~h(Q6FOq$+xKO^uBt1Dtmsc zo#G|ppIC45lY?iUF0NK<)nD!p?`zMB_m(u$-<-fDK#xNb@|6cJxna|e1}UlB9OT(j z@s#Gla)|eYn<)`ri0{1;_sZ-sR?j~tYlSTb%fijVV)i*F^;wrf3dWu?uWlS&MIxw zj<|aAgU2QHyL-(dp@LgFHwAi(myqvQ;O;j#8Cm_R^#m(bI&Z_T3-I_e0mQn&Jl0`$1xyD!9FTK8@kGcibsehKJ zZxD{^8=CC%SGQA~&=gPJR}P~0zW5J~JMT;PQ{hN|P4*<*`^qV0r}v%TX`keM*YD?1 zmlDfY*!jJ%{U|SZQNHKG_1u2W3;R76CFFY!TxBhTbpzIBaCNY6?}>e9u|6)?FQa`S z+9#=l{QxnZ>XUm=hZDE|g8G5@x~6lfezc!QaYIap>@w@tDxG6FAN6%%@zP%WKQ?x%wxbG$Q2RHuB`-8L}O8b&x`N{9eE~T;0DYZXHINBei z`m@yY7Bo+b>(7y2Nd5VosQyfLk*bfU!SDSf>d%y)iSL8dxu$g@)s?08wQE28=a z(UX!_e7&&sjiR=bST3paqOxaE*@>Q)qPQ!T+f@{w-4VxWA6AT~eI}{+ClzPKbj0UU z{Ybt_$p6Is#POM)Q=oAq##0N$gK2VP7p!V*LJA z$hx;wKUxQD2Vb$i6){i$1bs;le+l{a0k>ktERVT|?sA{*`PBPm1YJJ&=Tr4mV$xIB zMfW^+>2iYpL|Hgff8O-dKF=*kC6jr&`31<8C#s|JCHRqEzVaY1H<_REi%Y#-{AKb0 z;fD#7@6{{6Ib81KtNgjl7vaysdoLwq^!{H+u2UEA=a05b&jLWoCTBE9xEG8g zKaO)P7-xaz6t{wL258nZEf`nSt$&|imHi_8ir#B;MmRqRgJ*3hYReZby)>sPm-HiE za$}OCE?nBPOW`|i?N{4Z?os?w@wVl_mx0XaVU69j`ns|6wc@H~Us%fp0Jrnpoy>u) z&vO-$bQfJZH;t=vw(;{Hns@y#a8qpS zoVUAb#gDg4uJgp-i~V$L(S{0VqqsZZYXEvnfSa;=e&q&3FL1)|S1n}@b-gOb4EUP0 zgnYzz9OwyFn$M=9pIx@{_ZoDf*ahfe1NlvjI`P63^56>utgtHEv->Lur|sBa02>^oSX;IC#pH8_oXwk&5skOMO)Z}?!1X7uZ>DP3`VP+%Sg@ToyDUb`)#o;25Bt2rZ_$?KeBo(<};yK8z3e0-=bpA0<=A>U5$T@QMKT}}9vsh#y*ZS9yL#_wtP zX(sN6fU{uV!cN$PiU%aUDh8{FP%#S(AN+t~Kk?(F$K zoi?kkWTrlvynXgtLyj-$tJQJb-9PNP1AK`d;m#j@Wc=L8noofKNzjAj3jkl?>xNBu zvXi^V>auGxEckPXSGpLN#W0Q*!p>xuW4(s%Kh|p^N4S_5p8fBZ3{`km-g$IRhi^D9 zJ|;S7aeMyq`8$t&9h3pVS7n<=LYdLw|7O{r=;GIKKL^0M*^`80F>&g#;8`yFV{ z+#hU84QM}#TL!*Fk8maG#n0UHz=%7eGVCL>@RDV%I}9t!Z-u_Z_b%vV0jG<8L05X0 zNII&=NRJJW&jWm;KyNH?rY-bM<_>XH1Y{T$@Hc#@p^5!e=g@`Z6=U4Zp*zy4F#Xm5LXY1$tq?eQTwYJ~1Pdn{^;< zTjj)FtYmkc#@TB-^6ZGqk>IdN+z#+H2E94J^}V}l*NfwU+zXZBt=PSonG-@1y!dY+ zUk3ORJt2OYvVQ3IQfu9J0aZ+S(u3qP2VbI>1Dvnc4wE+P)+p*D52JkJ;%i}^PC~pB z?gPg>5nl**8vbGkJ*-|`JuxdR8uOPUvzyZ6s_x+l>>%j125$U(UiZmaPd2#P?qt)% zdB8icmp`|R?%l+jPX=G2Hwd`W&vY#7A8E_F_h^!KRlh%fuTJ)hCbId8dhYF-*M4Kh z9NgMm-n*zjb4Ymc^XaR7SP$Sz#5RAxE)Hi6XUfX4Q4ahF=t1)R4!%V1nM&cVe4Q~> zf^ScpnV0WY!1%3+JVLOGH6wZB5O+%e7YBcSg!}eJ)nPERhMlrbc3T#aVa4i$-WlLx zejK{((3Z)(j_L+~&4xh#(cMocgxPjsEUsPuyCGIAc>H+9tWyJ7`ti%_`O5=Xn`=)4 zG89wz;Ok$lNpbGWuQ@yW+}zgvd73YYFVPzVoX(av^^e*_^1s2JTcAhjQ-XW};H#^W z$1x8l4+&3GocNCKg*=<$Y0_dLuWNw(pbF;cril0D5qEVles^LV(f!DQT~5Rsdg_EL zxNV?ExE4>k1Zvm)P7$bEbFh}ZEAQWPRP$vgZ1^m5+f6|?V-zjHw=4XQaPHIR8xCJF zP4TAzwE5`hnhS70#Ncel2a%cl}(}jWvKC#~~l_wFJGUz|9Q} z>EHeK9?lQzBbsNnBi}VaUZ+<>$e+t&p5BG|s2Jj|A@bt=7)LeWFPX4QGUycpt{n2` zN63q7X9)RoIPzE{@I3{3S-`ar<0f365U&OpA2;Cl%4 z<^Z=4`LjOa^CHCEme7OpT@%DVN5p5snPNT~jkrtk^gPB9`AaL%BitOs=jn*M#Sx!7 zAx;usidTdif;jt16y@X0^;sU#1{*Uqxzs144hG_ǵGq^_g+Sfet zV;^P}_v7pybw@H6pVHUX1+L;&f^R73)dy~@zPIkUa`e96eCB*KDqslrHQx;K5nrMw z*e8OGL%+z&)vM&b4byRg)3*NUBs^m>=n2(dV}5&PQ0SwRj(h;>CHBZWwJ?4KJ*^qZ z`xbe7U*NhS532}&7V!3Y^U)NcZ5 z8MZ1LzM}cGm(^YP(U5Nx_`U(XUxCv_zuA49V`pCO%F}v_w2Gzq_NYpo?v2NUg^%1QDsJ=mY66Ou2hk2Ul5pFf&3)L+IyanS$^$ns&xU;Bl zq@%tOhxPcyNH(X!EgRyhZu0K;MzsAb+_6daaN@Q{1I^CEy*{Q}`d{ z?L?3A=X=>g{@fFJ@qOfbhn_a7(EGI++nDq2NN*QMHnB#+58a&|^77~6$e;UGuWRJ` z+MXXN%Ad2rm*~Az`CUK0KJw>@h|m4Ch4u7F#Amv1LY&~3Gvc!ya1@^x|7m=-XTFHf zr$BEBaMci>DNYFXab|Tfj%Z#W`%!%MMSN}}iqE$u^tuz(usd5e|Mc^)h~BK{^7)bJ zYX&pIZAE-8z3);|oBs9k^zeav^}&}%+!5*`t=LY)=kHLz&PJWKKJu{Z@aORTLS0H` zAk<&9QD;cOy7(L97sqjbX&vBX z+hRTQGwA(>{In19uwC$HnpfR1f5ap15MziFrPHS^e#}e(X8)Ao=9r>jrwXZVo7~KQ8Wc z1T#kcnd*dZ^VOei^6JmqQOBhH9;!_ps0R5c ze-ZXYEO_#ZzNkOH#C~3D)GhQS>KiGj3s4<|>Kni1t8esUH1A|$Jx%qJeZa+H{ULmL zon2l&ITrcL_+u8chk4mD+n0;%4o5h!Y0nLl-OcS-Yt%OaFn*gnyWPjg!Ir0e4sm^h z##?dJH^?71Lyxae-xvqJ!ox}yJmFkX-v~wi+y{B_JM13`9K@CGt80{dS$gD_ODZ;ErR>^laH3r%3zRr-!nJh`Wm^ zUTL?Pzx%OGUj8he@0I}982K}e=U32UJme$3!g`2t(E zzS^aeT9bRQ{fN)BPvV$9X41SM3%)yW_b-PX@=uz>S_K`hfB$hKM)5Qd_c#2F_7k03 z{rL@`XAIoMn8*WeW9G6Bh`X`SgU@bxZ{S&9ejoT!{ja@5eC~|+Lh_Nn5Iu2xUW&Lo z1bUEs#FywT1Mc*=-L;!)&t|l*orC$k0p{ag$UABL()_NA`JL+J{+PG5k%v*eE*$e` zThMa{j@IF6$V+PA{u*GObp_vIphx+OO}_6BSjAEhcTXa|5Z{iVC(L)N7>~SQZT&^> zR^OY+<{(e$5Bc7L?_SU&Tr%c&+K=CledoQX4+-(M6;Jbt!8Kw3s6TMQ*bf+px(d~8 zY5W?a4wnLY!NAQz{Xlr=#Dd?Ac`O3!o(+vI+b;aA9WV13J+De=H@i}3~I+{9!p*RcJ=i~v+|y2 zpndyBsIP@$osxoiD+Bw3gqw@~p24V_QT?7r{BuAZvkB-80!|n87gyxt*@y=|SpSBB zFVS-XZcM)Y!8>M@Z;*V%_ZjHX{$L3D-M=c_U(&+{^7(@A9nAZ$fjiS&*dOeS`ZLv) zOJd(%8|yc!??!+gJ@-%o_2*R7pSxkbrH6UpH0rx$L67!RzCm5e3;BHz;^a-N_hsNq z^gMwZit!-SpRM^W$ge74o_>q@lIT(Wc^LXxP+b{%oPvCJz?br&&cH1}{dpJa8*e7A~x}Xr394eD`>-71xIz>BM`t z`2I%E>3#SyCLI$tMcVQl<~If6?nKWg-A0D=VV9tXHT;kAcA_Wb&kk%n@@IOksy5d8uTFf zV!&7Ul6MD2xQ&R_qL@-pT72y%}A{8*r}eYlsu@)u_Qc)!3Q zw^I@PsXrIpR7I734@T4XnSaFhmoF8>|GeI(q37+RVcQey3pVm*G-oChj1%^;WU`zE z1>@+k_@=81#@V6S^oN3R6o(v0p@QK>*81uE%5IsU#OLK#pUxhh%RfhZ6q%!aVnVUY zbo|oKr(@q-voJAtw)MBa_*xQA^fci%4!cp~=)AL%bTms+I7DIjl3wr2Rg?bjONaPs zqE}eGq~s_pJ(73823M=_R%BRE$AHvZcwxDgUlL7nXOOUz;%wxOs(6=c>BQ)A-~X&+7X5Zpzbl3*;ld7NAGC+Z$HJ25w!-Ywc9o zQ$c#nfP6#2m+Ybq+zo?aZ@lYmU?~Gq(5M$Qi|Y{EcVd~t>54<8y@QS% zmb2~wvo2abpUH>zpL4L+uc>*svpcmaH!oAn;?lr1XEvsvSF{*k=3xEwE$r6@e5=K# zoB8=KG6TCC-Qr9^?;>!a9q;O}0WTGiVTbiXvSzUZbsnBhUy#JN&$&0i`q)Fo0q|`J zdQ*X8ONJ^=o(t@#m25ZFHP)x&^~B2w^9S;I@)2L6R}45KvkM2h>~`)Ln_eqtbL1K} z26`-ne8l$#=n*b`NvP}o=F;Bt;~n4mCw)J+ z@5NEKhn`B*qMxUme-Y!1?o)`9va Date: Wed, 14 Oct 2020 18:33:09 +0200 Subject: [PATCH 140/197] minor fixes --- qiskit/chemistry/ground_state_calculation/oovqe.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/ground_state_calculation/oovqe.py b/qiskit/chemistry/ground_state_calculation/oovqe.py index e30de59cd9..74ad1b79e2 100644 --- a/qiskit/chemistry/ground_state_calculation/oovqe.py +++ b/qiskit/chemistry/ground_state_calculation/oovqe.py @@ -296,9 +296,9 @@ def compute_groundstate(self, driver: BaseDriver = None) -> 'OOVQEResult': self._vqe.var_form._bounds = self._bounds self._vqe.var_form._num_parameters = len(self._bounds) vqresult = self._vqe.find_minimum(initial_point=self.initial_point, - var_form=self._vqe.var_form, - cost_fn=self._energy_evaluation_oo, - optimizer=self._vqe.optimizer) + var_form=self._vqe.var_form, + cost_fn=self._energy_evaluation_oo, + optimizer=self._vqe.optimizer) total_time += vqresult.optimizer_time # write original number of parameters to avoid errors due to parameter number mismatch From 25da57ee778b1ef2d43e69d157c6f0cb71cc10d0 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Wed, 14 Oct 2020 18:10:59 +0100 Subject: [PATCH 141/197] fix style --- test/chemistry/test_oovqe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/chemistry/test_oovqe.py b/test/chemistry/test_oovqe.py index f9b751cd42..282be3138e 100644 --- a/test/chemistry/test_oovqe.py +++ b/test/chemistry/test_oovqe.py @@ -131,7 +131,8 @@ def test_oovqe_with_frozen_core(self): calc = OOVQE(self.transformation2, solver, self.driver2, iterative_oo=False) calc._vqe.optimizer.set_options(maxiter=2, rhobeg=1) algo_result = calc.compute_groundstate(self.driver2) - self.assertLessEqual(algo_result.computed_electronic_energy + self.transformation2._energy_shift + + self.assertLessEqual(algo_result.computed_electronic_energy + + self.transformation2._energy_shift + self.transformation2._nuclear_repulsion_energy, self.energy2) def test_oovqe_with_unrestricted_hf(self): From 8a1fe42edf0615254e756805e05ec5e425802fbb Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 14 Oct 2020 15:22:04 -0400 Subject: [PATCH 142/197] renaming --- .../ground_state_solvers}/__init__.py | 12 +++---- .../ground_state_solvers}/adapt_vqe.py | 28 ++++++++------- .../ground_state_eigensolver.py} | 34 +++++++++--------- .../ground_state_solver.py} | 27 +++++++------- .../__init__.py | 4 +-- .../minimum_eigensolver_factory.py} | 6 ++-- .../numpy_minimum_eigensolver_factory.py | 9 +++-- .../vqe_uccsd_factory.py | 10 +++--- .../ground_state_solvers}/oovqe.py | 35 ++++++++----------- .../minimum_eigen_solvers/vqe_adapt.py | 4 +-- qiskit/chemistry/core/chemistry_operator.py | 7 ++-- .../__init__.py | 4 +-- .../fermionic_transformation.py | 4 +-- .../transformation.py} | 2 +- ...ound_state_interface-42576cb6658a46e0.yaml | 10 +++--- test/chemistry/test_adapt_vqe.py | 10 +++--- test/chemistry/test_driver_methods_gsc.py | 8 ++--- .../test_fermionic_transformation.py | 2 +- ...est_fermionic_transformation_orb_reduce.py | 2 +- .../test_initial_state_hartree_fock.py | 2 +- test/chemistry/test_mes_gsc_calculation.py | 22 ++++++------ test/chemistry/test_oovqe.py | 16 ++++----- test/chemistry/test_swaprz.py | 8 ++--- test/chemistry/test_symmetries.py | 8 ++--- test/chemistry/test_uccsd_advanced.py | 16 ++++----- test/chemistry/test_uccsd_hartree_fock.py | 24 ++++++------- 26 files changed, 157 insertions(+), 157 deletions(-) rename qiskit/chemistry/{ground_state_calculation => algorithms/ground_state_solvers}/__init__.py (67%) rename qiskit/chemistry/{ground_state_calculation => algorithms/ground_state_solvers}/adapt_vqe.py (93%) rename qiskit/chemistry/{ground_state_calculation/mes_ground_state_calculation.py => algorithms/ground_state_solvers/ground_state_eigensolver.py} (85%) rename qiskit/chemistry/{ground_state_calculation/ground_state_calculation.py => algorithms/ground_state_solvers/ground_state_solver.py} (81%) rename qiskit/chemistry/{ground_state_calculation/mes_factories => algorithms/ground_state_solvers/minimum_eigensolver_factories}/__init__.py (87%) rename qiskit/chemistry/{ground_state_calculation/mes_factories/mes_factory.py => algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py} (87%) rename qiskit/chemistry/{ground_state_calculation/mes_factories => algorithms/ground_state_solvers/minimum_eigensolver_factories}/numpy_minimum_eigensolver_factory.py (92%) rename qiskit/chemistry/{ground_state_calculation/mes_factories => algorithms/ground_state_solvers/minimum_eigensolver_factories}/vqe_uccsd_factory.py (95%) rename qiskit/chemistry/{ground_state_calculation => algorithms/ground_state_solvers}/oovqe.py (97%) rename qiskit/chemistry/{qubit_transformations => transformations}/__init__.py (84%) rename qiskit/chemistry/{qubit_transformations => transformations}/fermionic_transformation.py (99%) rename qiskit/chemistry/{qubit_transformations/qubit_operator_transformation.py => transformations/transformation.py} (98%) diff --git a/qiskit/chemistry/ground_state_calculation/__init__.py b/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py similarity index 67% rename from qiskit/chemistry/ground_state_calculation/__init__.py rename to qiskit/chemistry/algorithms/ground_state_solvers/__init__.py index 326fe06e88..53a85df7e4 100644 --- a/qiskit/chemistry/ground_state_calculation/__init__.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py @@ -12,16 +12,16 @@ """Ground state calculation algorithms.""" -from .ground_state_calculation import GroundStateCalculation +from .ground_state_solver import GroundStateSolver from .adapt_vqe import AdaptVQE from .oovqe import OOVQE -from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation -from .mes_factories import MESFactory, VQEUCCSDFactory +from .ground_state_eigensolver import GroundStateEigensolver +from .minimum_eigensolver_factories import MinimumEigensolverFactory, VQEUCCSDFactory -__all__ = ['GroundStateCalculation', +__all__ = ['GroundStateSolver', 'AdaptVQE', 'OOVQE', - 'MinimumEigensolverGroundStateCalculation', - 'MESFactory', + 'GroundStateEigensolver', + 'MinimumEigensolverFactory', 'VQEUCCSDFactory' ] diff --git a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py b/qiskit/chemistry/algorithms/ground_state_solvers/adapt_vqe.py similarity index 93% rename from qiskit/chemistry/ground_state_calculation/adapt_vqe.py rename to qiskit/chemistry/algorithms/ground_state_solvers/adapt_vqe.py index cbdc8cd1e3..8dde351c28 100644 --- a/qiskit/chemistry/ground_state_calculation/adapt_vqe.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/adapt_vqe.py @@ -17,28 +17,29 @@ from typing import Optional, List, Tuple, Union import numpy as np -from qiskit.chemistry.results import ElectronicStructureResult -from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry import FermionicOperator 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 ...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 .mes_factories import MESFactory -from .mes_ground_state_calculation import MinimumEigensolverGroundStateCalculation +from .minimum_eigensolver_factories import MinimumEigensolverFactory +from .ground_state_eigensolver import GroundStateEigensolver logger = logging.getLogger(__name__) -class AdaptVQE(MinimumEigensolverGroundStateCalculation): +class AdaptVQE(GroundStateEigensolver): """A ground state calculation employing the AdaptVQE algorithm.""" def __init__(self, transformation: FermionicTransformation, - solver: MESFactory, + solver: MinimumEigensolverFactory, threshold: float = 1e-5, delta: float = 1, max_iterations: Optional[int] = None, @@ -129,10 +130,11 @@ def _check_cyclicity(indices: List[int]) -> bool: # nature of the algorithm. return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) - def compute_groundstate(self, driver: BaseDriver, - aux_operators: Optional[List[Union[WeightedPauliOperator, - FermionicOperator]]] = None - ) -> 'AdaptVQEResult': + def solve(self, + driver: BaseDriver, + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None) \ + -> Union[ElectronicStructureResult, 'VibronicStructureResult']: """Computes the ground state. Args: diff --git a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py similarity index 85% rename from qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py rename to qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py index 5e0168d573..516c5b3bc0 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_ground_state_calculation.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py @@ -22,20 +22,21 @@ from qiskit.result import Result from qiskit.aqua.algorithms import MinimumEigensolver from qiskit.aqua.operators import OperatorBase, WeightedPauliOperator, StateFn, CircuitSampler -from qiskit.chemistry import FermionicOperator, BosonicOperator -from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.ground_state_calculation import GroundStateCalculation -from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation -from qiskit.chemistry.results import EigenstateResult +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 .ground_state_solver import GroundStateSolver -from .mes_factories import MESFactory +from .minimum_eigensolver_factories import MinimumEigensolverFactory -class MinimumEigensolverGroundStateCalculation(GroundStateCalculation): +class GroundStateEigensolver(GroundStateSolver): """Ground state computation using a minimum eigensolver.""" - def __init__(self, transformation: QubitOperatorTransformation, - solver: Union[MinimumEigensolver, MESFactory]) -> None: + def __init__(self, transformation: Transformation, + solver: Union[MinimumEigensolver, MinimumEigensolverFactory]) -> None: """ Args: @@ -46,12 +47,12 @@ def __init__(self, transformation: QubitOperatorTransformation, self._solver = solver @property - def solver(self) -> Union[MinimumEigensolver, MESFactory]: + def solver(self) -> Union[MinimumEigensolver, MinimumEigensolverFactory]: """Returns the minimum eigensolver or factory.""" return self._solver @solver.setter - def solver(self, solver: Union[MinimumEigensolver, MESFactory]) -> None: + def solver(self, solver: Union[MinimumEigensolver, MinimumEigensolverFactory]) -> None: """Sets the minimum eigensolver or factory.""" self._solver = solver @@ -59,10 +60,11 @@ def returns_groundstate(self) -> bool: """Whether the eigensolver returns the ground state or only ground state energy.""" return self._solver.supports_aux_operators() - def compute_groundstate(self, driver: BaseDriver, - aux_operators: Optional[Union[List[FermionicOperator], - List[BosonicOperator]]] = None - ) -> EigenstateResult: + def solve(self, + driver: BaseDriver, + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None) \ + -> Union[ElectronicStructureResult, 'VibronicStructureResult']: """Compute Ground State properties. Args: @@ -90,7 +92,7 @@ def compute_groundstate(self, driver: BaseDriver, # by the user but also additional ones from the transformation operator, aux_operators = self.transformation.transform(driver, aux_operators) - if isinstance(self._solver, MESFactory): + if isinstance(self._solver, MinimumEigensolverFactory): # this must be called after transformation.transform solver = self._solver.get_solver(self.transformation) else: diff --git a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py similarity index 81% rename from qiskit/chemistry/ground_state_calculation/ground_state_calculation.py rename to qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py index 6730ada4e5..6206e01f87 100644 --- a/qiskit/chemistry/ground_state_calculation/ground_state_calculation.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py @@ -22,17 +22,17 @@ from qiskit.quantum_info import Statevector from qiskit.result import Result from qiskit.aqua.operators import OperatorBase, WeightedPauliOperator -from qiskit.chemistry import FermionicOperator, BosonicOperator -from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.results import EigenstateResult +from ...fermionic_operator import FermionicOperator +from ...bosonic_operator import BosonicOperator +from ...drivers.base_driver import BaseDriver +from ...results.electronic_structure_result import ElectronicStructureResult +from ...transformations.transformation import Transformation -from ..qubit_transformations.qubit_operator_transformation import QubitOperatorTransformation - -class GroundStateCalculation(ABC): +class GroundStateSolver(ABC): """The ground state calculation interface""" - def __init__(self, transformation: QubitOperatorTransformation) -> None: + def __init__(self, transformation: Transformation) -> None: """ Args: transformation: transformation from driver to qubit operator (and aux. operators) @@ -40,20 +40,21 @@ def __init__(self, transformation: QubitOperatorTransformation) -> None: self._transformation = transformation @property - def transformation(self) -> QubitOperatorTransformation: + 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: QubitOperatorTransformation) -> None: + def transformation(self, transformation: Transformation) -> None: """Sets the transformation used to obtain a qubit operator from the molecule.""" self._transformation = transformation @abstractmethod - def compute_groundstate(self, driver: BaseDriver, - aux_operators: Optional[Union[List[FermionicOperator], - List[BosonicOperator]]] = None - ) -> EigenstateResult: + 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: diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py similarity index 87% rename from qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py rename to qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py index dd26d9ade1..7a3086d33c 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/__init__.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py @@ -12,11 +12,11 @@ """Factories that create a minimum eigensolver based on a qubit transformation.""" -from .mes_factory import MESFactory +from .minimum_eigensolver_factory import MinimumEigensolverFactory from .numpy_minimum_eigensolver_factory import NumPyMinimumEigensolverFactory from .vqe_uccsd_factory import VQEUCCSDFactory -__all__ = ['MESFactory', +__all__ = ['MinimumEigensolverFactory', 'NumPyMinimumEigensolverFactory', 'VQEUCCSDFactory' ] diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py similarity index 87% rename from qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py rename to qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py index dea11cce46..cc188303ce 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/mes_factory.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py @@ -14,14 +14,14 @@ from abc import ABC, abstractmethod from qiskit.aqua.algorithms import MinimumEigensolver -from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation +from ....transformations.transformation import Transformation -class MESFactory(ABC): +class MinimumEigensolverFactory(ABC): """A factory to construct a minimum eigensolver based on a qubit operator transformation.""" @abstractmethod - def get_solver(self, transformation: QubitOperatorTransformation) -> MinimumEigensolver: + def get_solver(self, transformation: Transformation) -> MinimumEigensolver: """Returns a minimum eigensolver, based on the qubit operator transformation. Args: diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py similarity index 92% rename from qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py rename to qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py index 561685df74..994f695744 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/numpy_minimum_eigensolver_factory.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py @@ -16,12 +16,11 @@ import numpy as np from qiskit.aqua.algorithms import MinimumEigensolver, NumPyMinimumEigensolver -from qiskit.chemistry.qubit_transformations import QubitOperatorTransformation +from ....transformations.transformation import Transformation +from .minimum_eigensolver_factory import MinimumEigensolverFactory -from .mes_factory import MESFactory - -class NumPyMinimumEigensolverFactory(MESFactory): +class NumPyMinimumEigensolverFactory(MinimumEigensolverFactory): """A factory to construct a NumPyMinimumEigensolver.""" def __init__(self, @@ -64,7 +63,7 @@ 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: QubitOperatorTransformation) -> MinimumEigensolver: + def get_solver(self, transformation: Transformation) -> MinimumEigensolver: """Returns a NumPyMinimumEigensolver which possibly uses the default filter criterion provided by the ``transformation``. diff --git a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uccsd_factory.py similarity index 95% rename from qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py rename to qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uccsd_factory.py index a40548d740..58431a29e0 100644 --- a/qiskit/chemistry/ground_state_calculation/mes_factories/vqe_uccsd_factory.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uccsd_factory.py @@ -19,14 +19,14 @@ from qiskit.aqua.algorithms import MinimumEigensolver, VQE from qiskit.aqua.operators import ExpectationBase from qiskit.aqua.components.optimizers import Optimizer -from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.components.initial_states import HartreeFock +from ....components.variational_forms import UCCSD +from ....transformations.fermionic_transformation import FermionicTransformation +from ....components.initial_states import HartreeFock -from .mes_factory import MESFactory +from .minimum_eigensolver_factory import MinimumEigensolverFactory -class VQEUCCSDFactory(MESFactory): +class VQEUCCSDFactory(MinimumEigensolverFactory): """A factory to construct a VQE minimum eigensolver with UCCSD ansatz wavefunction.""" def __init__(self, diff --git a/qiskit/chemistry/ground_state_calculation/oovqe.py b/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py similarity index 97% rename from qiskit/chemistry/ground_state_calculation/oovqe.py rename to qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py index 74ad1b79e2..703b3959bb 100644 --- a/qiskit/chemistry/ground_state_calculation/oovqe.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py @@ -19,20 +19,22 @@ import copy import numpy as np from scipy.linalg import expm -from qiskit.chemistry import QMolecule from qiskit.aqua import AquaError from qiskit.aqua.algorithms import VQE from qiskit.aqua.operators import LegacyBaseOperator -from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.results import FermionicGroundStateResult -from qiskit.chemistry.ground_state_calculation import GroundStateCalculation -from .mes_factories import MESFactory +from .minimum_eigensolver_factories import MinimumEigensolverFactory +from ...fermionic_operator import FermionicOperator +from ...bosonic_operator import BosonicOperator +from ...drivers.base_driver import BaseDriver +from ...transformations.fermionic_transformation import FermionicTransformation +from ...results.electronic_structure_result import ElectronicStructureResult +from ...qmolecule import QMolecule +from .ground_state_solver import GroundStateSolver logger = logging.getLogger(__name__) -class OOVQE(GroundStateCalculation): +class OOVQE(GroundStateSolver): r""" A ground state calculation employing the OOVQE algorithm. The Variational Quantum Eigensolver (VQE) algorithm enhanced with the Orbital Optimization (OO). The core of the approach resides in the optimization of orbitals through the @@ -53,7 +55,7 @@ class OOVQE(GroundStateCalculation): def __init__(self, transformation: FermionicTransformation, - solver: MESFactory, + solver: MinimumEigensolverFactory, driver: BaseDriver, initial_point: Optional[np.ndarray] = None, orbital_rotation: Optional['OrbitalRotation'] = None, @@ -225,15 +227,11 @@ def _energy_evaluation_oo(self, parameters: np.ndarray) -> Union[float, List[flo return mean_energy - def compute_groundstate(self, driver: BaseDriver = None) -> 'OOVQEResult': - """ Computes the ground state. - Args: - driver: a chemistry driver. - Raises: - AquaError: Wrong setting of operator and backend. - Returns: - A fermionic ground state result. - """ + def solve(self, + driver: BaseDriver, + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None) \ + -> Union[ElectronicStructureResult, 'VibronicStructureResult']: # algorithm requires to have driver to initialize correctly the variables, # however here one is free to change the driver type @@ -657,6 +655,3 @@ def optimal_point_orbitals(self) -> list: def optimal_point_orbitals(self, value: list) -> None: """ Sets the optimal parameters of the orbitals. """ self.data['optimal_point_orbitals'] = value - - def __getitem__(self, key: object) -> object: - return super().__getitem__(key) diff --git a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py index fcbd0d97bb..14bc0b318b 100644 --- a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py +++ b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py @@ -74,8 +74,8 @@ def __init__(self, operator: LegacyBaseOperator, """ 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.ground_state_calculation.AdaptVQE ' - 'instead.', DeprecationWarning, stacklevel=2) + ' 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/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index 26841248c3..07ed9afa3a 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -115,7 +115,7 @@ 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.ground_state_calculation.' + ' release. You should use qiskit.chemistry.algorithms.ground_state_solvers.' 'FermionicGroundStateResult instead.', DeprecationWarning, stacklevel=2) @property @@ -170,8 +170,9 @@ 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.ground_state_calculation.' - 'FermionicGroundStateResult instead.', DeprecationWarning, stacklevel=2) + 'the release. You should use qiskit.chemistry.algorithms.' + 'ground_state_solvers.FermionicGroundStateResult instead.', + DeprecationWarning, stacklevel=2) @property def energy(self) -> Optional[float]: diff --git a/qiskit/chemistry/qubit_transformations/__init__.py b/qiskit/chemistry/transformations/__init__.py similarity index 84% rename from qiskit/chemistry/qubit_transformations/__init__.py rename to qiskit/chemistry/transformations/__init__.py index e733291d25..88a613bb9a 100644 --- a/qiskit/chemistry/qubit_transformations/__init__.py +++ b/qiskit/chemistry/transformations/__init__.py @@ -13,7 +13,7 @@ """Qubit operator transformation module.""" from .fermionic_transformation import FermionicTransformation -from .qubit_operator_transformation import QubitOperatorTransformation +from .transformation import Transformation __all__ = ['FermionicTransformation', - 'QubitOperatorTransformation'] + 'Transformation'] diff --git a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py b/qiskit/chemistry/transformations/fermionic_transformation.py similarity index 99% rename from qiskit/chemistry/qubit_transformations/fermionic_transformation.py rename to qiskit/chemistry/transformations/fermionic_transformation.py index 0ccebcf2c6..9677cc1e29 100644 --- a/qiskit/chemistry/qubit_transformations/fermionic_transformation.py +++ b/qiskit/chemistry/transformations/fermionic_transformation.py @@ -28,7 +28,7 @@ from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.results import DipoleTuple, EigenstateResult, ElectronicStructureResult -from .qubit_operator_transformation import QubitOperatorTransformation +from .transformation import Transformation from ..components.initial_states import HartreeFock logger = logging.getLogger(__name__) @@ -47,7 +47,7 @@ class QubitMappingType(Enum): BRAVYI_KITAEV = 'bravyi_kitaev' -class FermionicTransformation(QubitOperatorTransformation): +class FermionicTransformation(Transformation): """A transformation from a fermionic problem, represented by a driver, to a qubit operator.""" def __init__(self, diff --git a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py b/qiskit/chemistry/transformations/transformation.py similarity index 98% rename from qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py rename to qiskit/chemistry/transformations/transformation.py index ed614f86c2..6348257606 100644 --- a/qiskit/chemistry/qubit_transformations/qubit_operator_transformation.py +++ b/qiskit/chemistry/transformations/transformation.py @@ -24,7 +24,7 @@ from qiskit.chemistry.results import EigenstateResult -class QubitOperatorTransformation(ABC): +class Transformation(ABC): """Base class for transformation to qubit operators for chemistry problems""" @abstractmethod diff --git a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml index 635203a996..ac1d8e9f0c 100644 --- a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml +++ b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml @@ -1,14 +1,14 @@ --- features: - | - Introduces ``qubit_transformations`` for the fermionic and bosonic transformation of a problem + Introduces ``transformations`` for the fermionic and bosonic transformation of a problem instance. Transforms the fermionic (or bosonic) operator to qubit operator - Introduces ``ground_state_calculation`` for the calculation of ground state properties. The + Introduces ``ground_state_solvers`` for the calculation of ground state properties. The calculation can be done either using an ``MinimumEigensolver`` or using ``AdaptVQE`` deprecations: - | ``Core Hamiltonian`` class is deprecated in favor of the ``FermionicTransformation`` - ``Chemistry Operator`` class is deprecated in facor of the ``qubit_tranformations`` + ``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_calculation interface - ``applications/molecular_ground_state_energy`` is deprecated in favor of ``ground_state_calculation`` \ No newline at end of file + 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 index d18d946a40..573dd2bdea 100644 --- a/test/chemistry/test_adapt_vqe.py +++ b/test/chemistry/test_adapt_vqe.py @@ -22,8 +22,8 @@ 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.ground_state_calculation import AdaptVQE, VQEUCCSDFactory -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.algorithms.ground_state_solvers import AdaptVQE, VQEUCCSDFactory +from qiskit.chemistry.transformations import FermionicTransformation class TestAdaptVQE(QiskitChemistryTestCase): @@ -48,7 +48,7 @@ def test_default(self): """ Default execution """ solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) calc = AdaptVQE(self.transformation, solver) - res = calc.compute_groundstate(self.driver) + res = calc.solve(self.driver) self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) def test_custom_minimum_eigensolver(self): @@ -79,7 +79,7 @@ def get_solver(self, transformation): solver = CustomFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) calc = AdaptVQE(self.transformation, solver) - res = calc.compute_groundstate(self.driver) + res = calc.solve(self.driver) self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) def test_custom_excitation_pool(self): @@ -100,7 +100,7 @@ def get_solver(self, transformation): solver = CustomFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) calc = AdaptVQE(self.transformation, solver) - res = calc.compute_groundstate(self.driver) + res = calc.solve(self.driver) self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) def test_vqe_adapt_check_cyclicity(self): diff --git a/test/chemistry/test_driver_methods_gsc.py b/test/chemistry/test_driver_methods_gsc.py index 51fafd26e1..f10dfd9657 100644 --- a/test/chemistry/test_driver_methods_gsc.py +++ b/test/chemistry/test_driver_methods_gsc.py @@ -16,8 +16,8 @@ from test.chemistry import QiskitChemistryTestCase from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.transformations import FermionicTransformation +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver from qiskit.aqua.algorithms import NumPyMinimumEigensolver @@ -51,9 +51,9 @@ def _run_driver(driver, transformation=TransformationType.FULL, solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.compute_groundstate(driver) + result = gsc.solve(driver) return result def _assert_energy(self, result, mol): diff --git a/test/chemistry/test_fermionic_transformation.py b/test/chemistry/test_fermionic_transformation.py index 6a03804850..0c5345f826 100644 --- a/test/chemistry/test_fermionic_transformation.py +++ b/test/chemistry/test_fermionic_transformation.py @@ -19,7 +19,7 @@ from qiskit.chemistry import QiskitChemistryError, FermionicOperator from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry.drivers import PySCFDriver, UnitsType -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.transformations import FermionicTransformation class TestFermionicTransformation(QiskitChemistryTestCase): diff --git a/test/chemistry/test_fermionic_transformation_orb_reduce.py b/test/chemistry/test_fermionic_transformation_orb_reduce.py index 17f625e6ef..8a95edea1f 100644 --- a/test/chemistry/test_fermionic_transformation_orb_reduce.py +++ b/test/chemistry/test_fermionic_transformation_orb_reduce.py @@ -19,7 +19,7 @@ from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.transformations import FermionicTransformation class TestFermionicTransformationOrbReduce(QiskitChemistryTestCase): diff --git a/test/chemistry/test_initial_state_hartree_fock.py b/test/chemistry/test_initial_state_hartree_fock.py index 1261c8c688..72b59dfaeb 100644 --- a/test/chemistry/test_initial_state_hartree_fock.py +++ b/test/chemistry/test_initial_state_hartree_fock.py @@ -22,7 +22,7 @@ from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.transformations import FermionicTransformation @ddt diff --git a/test/chemistry/test_mes_gsc_calculation.py b/test/chemistry/test_mes_gsc_calculation.py index 67b0af82e8..ec64b030fd 100644 --- a/test/chemistry/test_mes_gsc_calculation.py +++ b/test/chemistry/test_mes_gsc_calculation.py @@ -20,11 +20,11 @@ from qiskit.aqua import QuantumInstance from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.drivers import PySCFDriver, UnitsType -from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.qubit_transformations.fermionic_transformation import QubitMappingType -from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation -from qiskit.chemistry.ground_state_calculation.mes_factories import (VQEUCCSDFactory, - NumPyMinimumEigensolverFactory) +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): @@ -48,22 +48,22 @@ def setUp(self): def test_npme(self): """ Test NumPyMinimumEigensolver """ solver = NumPyMinimumEigensolverFactory() - calc = MinimumEigensolverGroundStateCalculation(self.transformation, solver) - res = calc.compute_groundstate(self.driver) + 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 = MinimumEigensolverGroundStateCalculation(self.transformation, solver) - res = calc.compute_groundstate(self.driver) + 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 = MinimumEigensolverGroundStateCalculation(self.transformation, solver) - res = calc.compute_groundstate(self.driver) + 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 diff --git a/test/chemistry/test_oovqe.py b/test/chemistry/test_oovqe.py index 282be3138e..e7aa7a118c 100644 --- a/test/chemistry/test_oovqe.py +++ b/test/chemistry/test_oovqe.py @@ -21,9 +21,9 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock from qiskit.aqua.components.optimizers import COBYLA -from qiskit.chemistry.ground_state_calculation import VQEUCCSDFactory, OOVQE -from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.qubit_transformations.fermionic_transformation import QubitMappingType +from qiskit.chemistry.algorithms.ground_state_solvers import VQEUCCSDFactory, OOVQE +from qiskit.chemistry.transformations import FermionicTransformation +from qiskit.chemistry.transformations.fermionic_transformation import QubitMappingType class TestOOVQE(QiskitChemistryTestCase): @@ -100,7 +100,7 @@ def test_orbital_rotations(self): calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=False, initial_point=self.initial_point1) calc._vqe.optimizer.set_options(maxiter=1) - algo_result = calc.compute_groundstate(self.driver1) + algo_result = calc.solve(self.driver1) self.assertAlmostEqual(algo_result.computed_electronic_energy, self.energy1_rotation, 4) def test_oovqe(self): @@ -111,7 +111,7 @@ def test_oovqe(self): calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=False, initial_point=self.initial_point1) calc._vqe.optimizer.set_options(maxiter=3, rhobeg=0.01) - algo_result = calc.compute_groundstate(self.driver1) + algo_result = calc.solve(self.driver1) self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1, 4) def test_iterative_oovqe(self): @@ -121,7 +121,7 @@ def test_iterative_oovqe(self): calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=True, initial_point=self.initial_point1, iterative_oo_iterations=2) calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) - algo_result = calc.compute_groundstate(self.driver1) + algo_result = calc.solve(self.driver1) self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1) def test_oovqe_with_frozen_core(self): @@ -130,7 +130,7 @@ def test_oovqe_with_frozen_core(self): solver = self.make_solver() calc = OOVQE(self.transformation2, solver, self.driver2, iterative_oo=False) calc._vqe.optimizer.set_options(maxiter=2, rhobeg=1) - algo_result = calc.compute_groundstate(self.driver2) + algo_result = calc.solve(self.driver2) self.assertLessEqual(algo_result.computed_electronic_energy + self.transformation2._energy_shift + self.transformation2._nuclear_repulsion_energy, self.energy2) @@ -141,7 +141,7 @@ def test_oovqe_with_unrestricted_hf(self): solver = self.make_solver() calc = OOVQE(self.transformation1, solver, self.driver3, iterative_oo=False) calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) - algo_result = calc.compute_groundstate(self.driver3) + algo_result = calc.solve(self.driver3) self.assertLessEqual(algo_result.computed_electronic_energy, self.energy3) diff --git a/test/chemistry/test_swaprz.py b/test/chemistry/test_swaprz.py index 8046276bd9..4cf2419a0b 100644 --- a/test/chemistry/test_swaprz.py +++ b/test/chemistry/test_swaprz.py @@ -22,8 +22,8 @@ from qiskit.chemistry.components.initial_states import HartreeFock from qiskit.chemistry.core import QubitMappingType from qiskit.chemistry.drivers import HDF5Driver -from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver +from qiskit.chemistry.transformations import FermionicTransformation from qiskit.circuit.library import ExcitationPreserving @@ -64,9 +64,9 @@ def test_excitation_preserving(self): seed_simulator=aqua_globals.random_seed, seed_transpiler=aqua_globals.random_seed)) - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.compute_groundstate(driver) + result = gsc.solve(driver) self.assertAlmostEqual(result.energy, self.reference_energy, places=4) diff --git a/test/chemistry/test_symmetries.py b/test/chemistry/test_symmetries.py index 5a4b07bfc9..38a5a62aa9 100644 --- a/test/chemistry/test_symmetries.py +++ b/test/chemistry/test_symmetries.py @@ -23,9 +23,9 @@ 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.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.transformations import FermionicTransformation class TestSymmetries(QiskitChemistryTestCase): @@ -100,9 +100,9 @@ def test_tapered_op(self): quantum_instance=QuantumInstance( backend=BasicAer.get_backend('statevector_simulator'))) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) diff --git a/test/chemistry/test_uccsd_advanced.py b/test/chemistry/test_uccsd_advanced.py index 418ae04aab..6e13d0a8d8 100644 --- a/test/chemistry/test_uccsd_advanced.py +++ b/test/chemistry/test_uccsd_advanced.py @@ -23,9 +23,9 @@ 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.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.transformations import FermionicTransformation # pylint: disable=invalid-name @@ -96,9 +96,9 @@ def test_uccsd_hf_qpUCCD(self): quantum_instance=QuantumInstance( backend=BasicAer.get_backend('statevector_simulator'))) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy_pUCCD, places=6) @@ -129,9 +129,9 @@ def test_uccsd_hf_qUCCD0(self): quantum_instance=QuantumInstance( backend=BasicAer.get_backend('statevector_simulator'))) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0, places=6) @@ -163,9 +163,9 @@ def test_uccsd_hf_qUCCD0full(self): quantum_instance=QuantumInstance( backend=BasicAer.get_backend('statevector_simulator'))) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0full, places=6) diff --git a/test/chemistry/test_uccsd_hartree_fock.py b/test/chemistry/test_uccsd_hartree_fock.py index 719fc1cf17..5a94152f9e 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -23,8 +23,8 @@ from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.core import QubitMappingType from qiskit.chemistry.drivers import HDF5Driver -from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation -from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver +from qiskit.chemistry.transformations import FermionicTransformation @ddt @@ -64,9 +64,9 @@ def test_uccsd_hf(self): solver = VQE(var_form=self.var_form, optimizer=self.optimizer, quantum_instance=QuantumInstance(backend=backend)) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) @@ -80,9 +80,9 @@ def test_uccsd_hf_qasm(self): seed_simulator=aqua_globals.random_seed, seed_transpiler=aqua_globals.random_seed)) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, -1.138, places=2) @@ -98,9 +98,9 @@ def test_uccsd_hf_aer_statevector(self): solver = VQE(var_form=self.var_form, optimizer=self.optimizer, quantum_instance=QuantumInstance(backend=backend)) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy, places=6) @@ -120,9 +120,9 @@ def test_uccsd_hf_aer_qasm(self): seed_simulator=aqua_globals.random_seed, seed_transpiler=aqua_globals.random_seed)) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, -1.138, places=2) @@ -140,9 +140,9 @@ def test_uccsd_hf_aer_qasm_snapshot(self): expectation=AerPauliExpectation(), quantum_instance=QuantumInstance(backend=backend)) - gsc = MinimumEigensolverGroundStateCalculation(self.fermionic_transformation, solver) + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + result = gsc.solve(self.driver) self.assertAlmostEqual(result.energy, self.reference_energy, places=3) EXCITATION_RESULTS = \ From 386c435d92dad1ca9d432a58fdfca42b090af32d Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Wed, 14 Oct 2020 21:17:23 +0100 Subject: [PATCH 143/197] typo in the properties --- qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py b/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py index 703b3959bb..eee4380609 100644 --- a/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py @@ -631,7 +631,7 @@ def optimal_point(self) -> list: """ Returns the optimal parameters. """ return self.get('optimal_point') - @num_optimizer_evals.setter + @optimal_point.setter def optimal_point(self, value: list) -> None: """ Sets the optimal parameters. """ self.data['optimal_point'] = value From 4e435a8924762ee82b35160bc3487c465faea514 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Wed, 14 Oct 2020 21:25:54 +0100 Subject: [PATCH 144/197] fixes in OOVQE --- .../algorithms/ground_state_solvers/oovqe.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py b/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py index eee4380609..4ad604fcf2 100644 --- a/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py @@ -22,6 +22,8 @@ from qiskit.aqua import AquaError from qiskit.aqua.algorithms import VQE from qiskit.aqua.operators import LegacyBaseOperator + +from .ground_state_eigensolver import GroundStateEigensolver from .minimum_eigensolver_factories import MinimumEigensolverFactory from ...fermionic_operator import FermionicOperator from ...bosonic_operator import BosonicOperator @@ -34,7 +36,7 @@ logger = logging.getLogger(__name__) -class OOVQE(GroundStateSolver): +class OOVQE(GroundStateEigensolver): r""" A ground state calculation employing the OOVQE algorithm. The Variational Quantum Eigensolver (VQE) algorithm enhanced with the Orbital Optimization (OO). The core of the approach resides in the optimization of orbitals through the @@ -91,7 +93,8 @@ def __init__(self, AquaError: if the number of orbital optimization iterations is less or equal to zero. """ - super().__init__(transformation) + super().__init__(transformation, solver) + # todo: no need, initializer in the super class self._solver = solver self._driver = driver self._qmolecule = qmolecule @@ -337,7 +340,7 @@ def solve(self, self._vqe._ret['opt_params'] = _ret_temp_params result.cost_function_evals = self._vqe._eval_count - self.transformation.add_context(result) + self.transformation.interpret(result) logger.info('Optimization complete in %s seconds.\nFound opt_params %s in %s evals', result.total_time, result.optimal_point, self._vqe._eval_count) @@ -603,7 +606,7 @@ def parameter_bound_value(self) -> Tuple[float, float]: return self._parameter_bound_value -class OOVQEResult(FermionicGroundStateResult): +class OOVQEResult(ElectronicStructureResult): r""" OOVQE Result. """ @property From 578333e4546feb88d21ff64cfe236248e6139a1d Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 14 Oct 2020 22:38:50 +0200 Subject: [PATCH 145/197] update reno --- .../ground_state_interface-42576cb6658a46e0.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml index ac1d8e9f0c..75ae1a1443 100644 --- a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml +++ b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml @@ -2,9 +2,15 @@ features: - | Introduces ``transformations`` for the fermionic and bosonic transformation of a problem - instance. Transforms the fermionic (or bosonic) operator to qubit operator - Introduces ``ground_state_solvers`` for the calculation of ground state properties. The - calculation can be done either using an ``MinimumEigensolver`` or using ``AdaptVQE`` + 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 ``mes_factories`` where chemistry specific minimum + eigensolvers can also be initialised deprecations: - | ``Core Hamiltonian`` class is deprecated in favor of the ``FermionicTransformation`` From 85d5594b5420ed1536164bac8e54ee6051b0dd23 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 14 Oct 2020 22:41:14 +0200 Subject: [PATCH 146/197] update reno2 --- releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml index 75ae1a1443..131e7312e4 100644 --- a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml +++ b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml @@ -11,6 +11,7 @@ features: electronic_structure_result are also used for the algorithms. Introduces Minimum Eigensolver factories ``mes_factories`` where chemistry specific minimum eigensolvers can also 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`` From ba211311b3db45c373803628cf49d03e152f0a0f Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 14 Oct 2020 22:45:54 +0200 Subject: [PATCH 147/197] update reno 3 --- .../notes/ground_state_interface-42576cb6658a46e0.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml index 131e7312e4..d4862f9cc9 100644 --- a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml +++ b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml @@ -9,8 +9,8 @@ features: ``AdaptVQE`` Introduces ``chemistry/results`` where the eigenstate_result and the electronic_structure_result are also used for the algorithms. - Introduces Minimum Eigensolver factories ``mes_factories`` where chemistry specific minimum - eigensolvers can also be initialised + 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: - | From e25fb6f61dc60320edb73b22dc1c0baa8a64ad35 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 14 Oct 2020 16:51:45 -0400 Subject: [PATCH 148/197] fix spell --- .pylintdict | 1 + 1 file changed, 1 insertion(+) diff --git a/.pylintdict b/.pylintdict index bd3bc51823..dd264b70d0 100644 --- a/.pylintdict +++ b/.pylintdict @@ -461,6 +461,7 @@ onee online onodera onwards +OO oom oovqe operatorbase From de7ff100152d23512b1e32ce569f61bf6c5cd2f5 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Wed, 14 Oct 2020 23:53:15 +0200 Subject: [PATCH 149/197] moving oovqe to a separate PR --- .../algorithms/ground_state_solvers/oovqe.py | 660 ------------------ test/chemistry/test_oovqe.py | 149 ---- test/chemistry/test_oovqe_h4.hdf5 | Bin 25928 -> 0 bytes test/chemistry/test_oovqe_h4_uhf.hdf5 | Bin 32072 -> 0 bytes test/chemistry/test_oovqe_lih.hdf5 | Bin 44624 -> 0 bytes 5 files changed, 809 deletions(-) delete mode 100644 qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py delete mode 100644 test/chemistry/test_oovqe.py delete mode 100644 test/chemistry/test_oovqe_h4.hdf5 delete mode 100644 test/chemistry/test_oovqe_h4_uhf.hdf5 delete mode 100644 test/chemistry/test_oovqe_lih.hdf5 diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py b/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py deleted file mode 100644 index 4ad604fcf2..0000000000 --- a/qiskit/chemistry/algorithms/ground_state_solvers/oovqe.py +++ /dev/null @@ -1,660 +0,0 @@ -# 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 Orbital-Optimized VQE (OOVQE) algorithm. -""" - -from typing import Optional, List, Union, Tuple -import logging -import copy -import numpy as np -from scipy.linalg import expm -from qiskit.aqua import AquaError -from qiskit.aqua.algorithms import VQE -from qiskit.aqua.operators import LegacyBaseOperator - -from .ground_state_eigensolver import GroundStateEigensolver -from .minimum_eigensolver_factories import MinimumEigensolverFactory -from ...fermionic_operator import FermionicOperator -from ...bosonic_operator import BosonicOperator -from ...drivers.base_driver import BaseDriver -from ...transformations.fermionic_transformation import FermionicTransformation -from ...results.electronic_structure_result import ElectronicStructureResult -from ...qmolecule import QMolecule -from .ground_state_solver import GroundStateSolver - -logger = logging.getLogger(__name__) - - -class OOVQE(GroundStateEigensolver): - r""" A ground state calculation employing the OOVQE algorithm. - The Variational Quantum Eigensolver (VQE) algorithm enhanced with the Orbital Optimization (OO). - The core of the approach resides in the optimization of orbitals through the - AO-to-MO coefficients matrix C. In the usual VQE, the latter remains constant throughout - the simulation. Here, its elements are modified according to C=Ce^(-kappa) where kappa is - an anti-hermitian matrix. This transformation preserves the spectrum but modifies the - amplitudes of the ground state of given operator such that in the end a given ansatz - can be closest to that ground state, producing larger overlap and lower eigenvalue than - conventional VQE. Kappa is parametrized and optimized inside the OOVQE in the same way as - the gate angles. Therefore, at each step of OOVQE the coefficient matrix C is modified and - the operator is recomputed, unlike usual VQE where operator remains constant. - Iterative OO refers to optimization in two steps, first the wavefunction and then the - orbitals. It allows for faster optimization as the operator is not recomputed when - wavefunction is optimized. It is recommended to use the iterative method on real device/qasm - simulator with noise to facilitate the convergence of the classical optimizer. - For more details of this method refer to: https://aip.scitation.org/doi/10.1063/1.5141835 - """ - - def __init__(self, - transformation: FermionicTransformation, - solver: MinimumEigensolverFactory, - driver: BaseDriver, - initial_point: Optional[np.ndarray] = None, - orbital_rotation: Optional['OrbitalRotation'] = None, - qmolecule: Optional[QMolecule] = None, - bounds: Optional[np.ndarray] = None, - iterative_oo: bool = True, - iterative_oo_iterations: int = 2, - ): - - """ - Args: - transformation: a fermionic driver to operator transformation strategy. - solver: a factory for the VQE solver employing any custom variational form. - driver: a chemistry driver necessary to initialize the parameters of the algorithm. - 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. - orbital_rotation: instance of - :class:`~qiskit.chemistry.ground_state_calculation.OrbitalRotation` class - that creates the matrices that rotate the orbitals needed to produce the rotated - MO coefficients C as C = C0 * exp(-kappa). - qmolecule: instance of the :class:`~qiskit.chemistry.QMolecule` class which has methods - needed to recompute one-/two-electron/dipole integrals after orbital rotation - (C = C0 * exp(-kappa)). - bounds: bounds for variational form and orbital rotation - parameters given to a classical optimizer. - iterative_oo: when ``True`` optimize first the variational form and then the - orbitals, iteratively. Otherwise, the wavefunction ansatz and orbitals are - optimized simultaneously. - iterative_oo_iterations: number of iterations in the iterative procedure, - set larger to be sure to converge to the global minimum. - Raises: - AquaError: if the number of orbital optimization iterations is less or equal to zero. - """ - - super().__init__(transformation, solver) - # todo: no need, initializer in the super class - self._solver = solver - self._driver = driver - self._qmolecule = qmolecule - self.initial_point = initial_point - - # initialize the operator and vqe to get the right number of orbital rotations - self._set_operator_and_vqe() - - self._qmolecule_rotated = None - self._fixed_wavefunction_params = None - if orbital_rotation is None: - self._orbital_rotation = OrbitalRotation(num_qubits=self._vqe.var_form.num_qubits, - transformation=self._transformation, - qmolecule=self._qmolecule) - self._num_parameters_oovqe = \ - self._vqe.var_form._num_parameters + self._orbital_rotation.num_parameters - - if self.initial_point is None: - self._set_initial_point() - else: - if len(self.initial_point) is not self._num_parameters_oovqe: - raise AquaError('Number of parameters of OOVQE ({}) differs to the one given in ' - 'intitial_point ({})'.format(self._num_parameters_oovqe, - len(self.initial_point))) - - self._bounds = bounds - if self._bounds is None: - self._set_bounds(self._orbital_rotation.parameter_bound_value) - self._iterative_oo = iterative_oo - self._iterative_oo_iterations = iterative_oo_iterations - if self._iterative_oo_iterations < 1: - raise AquaError('Please set iterative_oo_iterations parameter to a positive number,' - ' got {} instead'.format(self._iterative_oo_iterations)) - - # copies to overcome incompatibilities with error checks in VQAlgorithm class - self.var_form_num_parameters = self._vqe.var_form.num_parameters - self.var_form_bounds = copy.copy(self._vqe.var_form._bounds) - - def returns_groundstate(self) -> bool: - return True - - def _set_operator_and_vqe(self): - """ Initializes the operators using provided driver of qmolecule.""" - - if self._qmolecule is None: - # in future, self._transformation.transform should return also qmolecule - # to avoid running the driver twice - self._qmolecule = self._driver.run() - operator, aux_operators = self._transformation._do_transform(self._qmolecule) - else: - operator, aux_operators = self._transformation._do_transform(self._qmolecule) - if operator is None: # type: ignore - raise AquaError("The operator was never provided.") - self._vqe = self._solver.get_solver(self._transformation) - if not isinstance(self._vqe, VQE): - raise AquaError("The OOVQE algorithm requires the use of the VQE solver") - self._vqe.operator = operator - self._vqe.aux_operators = aux_operators - - def _set_bounds(self, - bounds_var_form_val: tuple = (-2 * np.pi, 2 * np.pi), - bounds_oo_val: tuple = (-2 * np.pi, 2 * np.pi)) -> None: - """ Initializes the array of bounds of wavefunction and OO parameters. - Args: - bounds_var_form_val: pair of bounds between which the optimizer confines the - values of wavefunction parameters. - bounds_oo_val: pair of bounds between which the optimizer confines the values of - OO parameters. - Raises: - AquaError: Instantiate OrbitalRotation class and provide it to the - orbital_rotation keyword argument - """ - self._bounds = [] - bounds_var_form = [bounds_var_form_val for _ in range(self._vqe.var_form.num_parameters)] - self._bound_oo = \ - [bounds_oo_val for _ in range(self._orbital_rotation.num_parameters)] # type: List - self._bounds = bounds_var_form + self._bound_oo - self._bounds = np.array(self._bounds) - - def _set_initial_point(self, initial_pt_scalar: float = 1e-1) -> None: - """ Initializes the initial point for the algorithm if the user does not provide his own. - Args: - initial_pt_scalar: value of the initial parameters for wavefunction and orbital rotation - """ - self.initial_point = [initial_pt_scalar for _ in range(self._num_parameters_oovqe)] - - def _energy_evaluation_oo(self, parameters: np.ndarray) -> Union[float, List[float]]: - """ Evaluate energy at given parameters for the variational form and parameters for - given rotation of orbitals. - Args: - parameters: parameters for variational form and orbital rotations. - Returns: - energy of the hamiltonian of each parameter. - Raises: - AquaError: Instantiate OrbitalRotation class and provide it to the - orbital_rotation keyword argument - """ - - # slice parameter lists - if self._iterative_oo: - parameters_var_form = self._fixed_wavefunction_params - parameters_orb_rot = parameters - else: - parameters_var_form = parameters[:self.var_form_num_parameters] - parameters_orb_rot = parameters[self.var_form_num_parameters:] - - logger.info('Parameters of wavefunction are: \n%s', repr(parameters_var_form)) - logger.info('Parameters of orbital rotation are: \n%s', repr(parameters_orb_rot)) - - # rotate the orbitals - if self._orbital_rotation is None: - raise AquaError('Instantiate OrbitalRotation class and provide it to the ' - 'orbital_rotation keyword argument') - - self._orbital_rotation.orbital_rotation_matrix(parameters_orb_rot) - - # preserve original qmolecule and create a new one with rotated orbitals - self._qmolecule_rotated = copy.copy(self._qmolecule) - OOVQE._rotate_orbitals_in_qmolecule(self._qmolecule_rotated, self._orbital_rotation) - - # construct the qubit operator - operator, aux_operators = self._transformation._do_transform(self._qmolecule_rotated) - if isinstance(operator, LegacyBaseOperator): - operator = operator.to_opflow() - self._vqe.operator = operator - self._vqe.aux_operators = aux_operators - logger.debug('Orbital rotation parameters of matrix U at evaluation %d returned' - '\n %s', self._vqe._eval_count, repr(self._orbital_rotation.matrix_a)) - self._vqe.var_form._num_parameters = self.var_form_num_parameters - - # compute the energy on given state - mean_energy = self._vqe._energy_evaluation(parameters=parameters_var_form) - - return mean_energy - - def solve(self, - driver: BaseDriver, - aux_operators: Optional[Union[List[FermionicOperator], - List[BosonicOperator]]] = None) \ - -> Union[ElectronicStructureResult, 'VibronicStructureResult']: - - # algorithm requires to have driver to initialize correctly the variables, - # however here one is free to change the driver type - if driver is not None: - self._driver = driver - else: - raise AquaError('Please specify a driver as you instantiate OOVQE class.') - self._vqe._eval_count = 0 - - # initial orbital rotation starting point is provided - if self._orbital_rotation.matrix_a is not None and self._orbital_rotation.matrix_b is not \ - None: - self._qmolecule_rotated = copy.copy(self._qmolecule) - OOVQE._rotate_orbitals_in_qmolecule(self._qmolecule_rotated, self._orbital_rotation) - operator, aux_operators = self._transformation._do_transform(self._qmolecule_rotated) - self._vqe.operator = operator - self._vqe.aux_operators = aux_operators - - logger.info( - '\n\nSetting the initial value for OO matrices and rotating Hamiltonian \n') - logger.info('Optimising Orbital Coefficient Rotation Alpha: \n%s', - repr(self._orbital_rotation.matrix_a)) - logger.info('Optimising Orbital Coefficient Rotation Beta: \n%s', - repr(self._orbital_rotation.matrix_b)) - - # save the original number of parameters as we modify their number to bypass the - # error checks that are not tailored to OOVQE - - total_time = 0 - # iterative method - if self._iterative_oo: - for _ in range(self._iterative_oo_iterations): - # optimize wavefunction ansatz - logger.info('OOVQE: Ansatz optimization, orbitals fixed.') - self._vqe.var_form._num_parameters = self.var_form_num_parameters - if isinstance(self._vqe.operator, LegacyBaseOperator): # type: ignore - self._vqe.operator = self._vqe.operator.to_opflow() # type: ignore - self._vqe.var_form._bounds = self.var_form_bounds - vqresult_wavefun = self._vqe.find_minimum( - initial_point=self.initial_point[:self.var_form_num_parameters], - var_form=self._vqe.var_form, - cost_fn=self._vqe._energy_evaluation, - optimizer=self._vqe.optimizer) - self.initial_point[:self.var_form_num_parameters] = vqresult_wavefun.optimal_point - - # optimize orbitals - logger.info('OOVQE: Orbital optimization, ansatz fixed.') - self._vqe.var_form._bounds = self._bound_oo - self._vqe.var_form._num_parameters = self._orbital_rotation.num_parameters - self._fixed_wavefunction_params = vqresult_wavefun.optimal_point - vqresult = self._vqe.find_minimum( - initial_point=self.initial_point[self.var_form_num_parameters:], - var_form=self._vqe.var_form, - cost_fn=self._energy_evaluation_oo, - optimizer=self._vqe.optimizer) - self.initial_point[self.var_form_num_parameters:] = vqresult.optimal_point - total_time += vqresult.optimizer_time - else: - # simultaneous method (ansatz and orbitals are optimized at the same time) - self._vqe.var_form._bounds = self._bounds - self._vqe.var_form._num_parameters = len(self._bounds) - vqresult = self._vqe.find_minimum(initial_point=self.initial_point, - var_form=self._vqe.var_form, - cost_fn=self._energy_evaluation_oo, - optimizer=self._vqe.optimizer) - total_time += vqresult.optimizer_time - - # write original number of parameters to avoid errors due to parameter number mismatch - self._vqe.var_form._num_parameters = self.var_form_num_parameters - - # extend VQE returned information with additional outputs - result = OOVQEResult() - result.computed_electronic_energy = vqresult.optimal_value - result.num_optimizer_evals = vqresult.optimizer_evals - result.optimal_point = vqresult.optimal_point - if self._iterative_oo: - result.optimal_point_ansatz = self.initial_point[self.var_form_num_parameters:] - result.optimal_point_orbitals = self.initial_point[:self.var_form_num_parameters] - else: - result.optimal_point_ansatz = vqresult.optimal_point[:self.var_form_num_parameters] - result.optimal_point_orbitals = vqresult.optimal_point[self.var_form_num_parameters:] - result.total_time = total_time - result.eigenvalue = vqresult.optimal_value + 0j - - # copy parameters bypass the error checks that are not tailored to OOVQE - _ret_temp_params = copy.copy(vqresult.optimal_point) - self._vqe._ret = {} - self._vqe._ret['opt_params'] = vqresult.optimal_point[:self.var_form_num_parameters] - if self._iterative_oo: - self._vqe._ret['opt_params'] = vqresult_wavefun.optimal_point - result.eigenstate_vector = self._vqe.get_optimal_vector() - if not self._iterative_oo: - self._vqe._ret['opt_params'] = _ret_temp_params - - if self._vqe.aux_operators is not None: - # copy parameters bypass the error checks that are not tailored to OOVQE - self._vqe._ret['opt_params'] = vqresult.optimal_point[:self.var_form_num_parameters] - if self._iterative_oo: - self._vqe._ret['opt_params'] = vqresult_wavefun.optimal_point - self._vqe._eval_aux_ops() - result.aux_operator_eigenvalues = self._vqe._ret['aux_ops'][0] - if not self._iterative_oo: - self._vqe._ret['opt_params'] = _ret_temp_params - - result.cost_function_evals = self._vqe._eval_count - self.transformation.interpret(result) - - logger.info('Optimization complete in %s seconds.\nFound opt_params %s in %s evals', - result.total_time, result.optimal_point, self._vqe._eval_count) - - return result - - @staticmethod - def _rotate_orbitals_in_qmolecule(qmolecule: QMolecule, - orbital_rotation: 'OrbitalRotation') -> None: - """ Rotates the orbitals by applying a modified a anti-hermitian matrix - (orbital_rotation.matrix_a) onto the MO coefficients matrix and recomputes all the - quantities dependent on the MO coefficients. Be aware that qmolecule is modified - when this executes. - Args: - qmolecule: instance of QMolecule class - orbital_rotation: instance of OrbitalRotation class - """ - - # 1 and 2 electron integrals (required) from AO to MO basis - qmolecule.mo_coeff = np.matmul(qmolecule.mo_coeff, - orbital_rotation.matrix_a) - qmolecule.mo_onee_ints = qmolecule.oneeints2mo(qmolecule.hcore, - qmolecule.mo_coeff) - # support for unrestricted spins - if qmolecule.mo_coeff_b is not None: - qmolecule.mo_coeff_b = np.matmul(qmolecule.mo_coeff_b, - orbital_rotation.matrix_b) - qmolecule.mo_onee_ints_b = qmolecule.oneeints2mo(qmolecule.hcore, - qmolecule.mo_coeff) - - qmolecule.mo_eri_ints = qmolecule.twoeints2mo(qmolecule.eri, - qmolecule.mo_coeff) - if qmolecule.mo_coeff_b is not None: - mo_eri_b = qmolecule.twoeints2mo(qmolecule.eri, - qmolecule.mo_coeff_b) - norbs = qmolecule.mo_coeff.shape[0] - qmolecule.mo_eri_ints_bb = mo_eri_b.reshape(norbs, norbs, norbs, norbs) - qmolecule.mo_eri_ints_ba = qmolecule.twoeints2mo_general( - qmolecule.eri, qmolecule.mo_coeff_b, qmolecule.mo_coeff_b, qmolecule.mo_coeff, - qmolecule.mo_coeff) - qmolecule.mo_eri_ints_ba = qmolecule.mo_eri_ints_ba.reshape(norbs, norbs, - norbs, norbs) - # dipole integrals (if available) from AO to MO - if qmolecule.x_dip_ints is not None: - qmolecule.x_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.x_dip_ints, - qmolecule.mo_coeff) - qmolecule.y_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.y_dip_ints, - qmolecule.mo_coeff) - qmolecule.z_dip_mo_ints = qmolecule.oneeints2mo(qmolecule.z_dip_ints, - qmolecule.mo_coeff) - # support for unrestricted spins - if qmolecule.mo_coeff_b is not None and qmolecule.x_dip_ints is not None: - qmolecule.x_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.x_dip_ints, - qmolecule.mo_coeff_b) - qmolecule.y_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.y_dip_ints, - qmolecule.mo_coeff_b) - qmolecule.z_dip_mo_ints_b = qmolecule.oneeints2mo(qmolecule.z_dip_ints, - qmolecule.mo_coeff_b) - - -class OrbitalRotation: - r""" Class that regroups methods for creation of matrices that rotate the MOs. - It allows to create the unitary matrix U = exp(-kappa) that is parameterized with kappa's - elements. The parameters are the off-diagonal elements of the anti-hermitian matrix kappa. - """ - - def __init__(self, - num_qubits: int, - transformation: FermionicTransformation, - qmolecule: Optional[QMolecule] = None, - orbital_rotations: list = None, - orbital_rotations_beta: list = None, - parameters: list = None, - parameter_bounds: list = None, - parameter_initial_value: float = 0.1, - parameter_bound_value: Tuple[float, float] = (-2 * np.pi, 2 * np.pi)) -> None: - - """ - Args: - num_qubits: number of qubits necessary to simulate a particular system. - transformation: a fermionic driver to operator transformation strategy. - qmolecule: instance of the :class:`~qiskit.chemistry.QMolecule` class which has methods - needed to recompute one-/two-electron/dipole integrals after orbital rotation - (C = C0 * exp(-kappa)). It is not required but can be used if user wished to - provide custom integrals for instance. - orbital_rotations: list of alpha orbitals that are rotated (i.e. [[0,1], ...] the - 0-th orbital is rotated with 1-st, which corresponds to non-zero entry 01 of - the matrix kappa). - orbital_rotations_beta: list of beta orbitals that are rotated. - parameters: orbital rotation parameter list of matrix elements that rotate the MOs, - each associated to a pair of orbitals that are rotated - (non-zero elements in matrix kappa), or elements in the orbital_rotation(_beta) - lists. - parameter_bounds: parameter bounds - parameter_initial_value: initial value for all the parameters. - parameter_bound_value: value for the bounds on all the parameters - """ - - self._num_qubits = num_qubits - self._transformation = transformation - self._qmolecule = qmolecule - - self._orbital_rotations = orbital_rotations - self._orbital_rotations_beta = orbital_rotations_beta - self._parameter_initial_value = parameter_initial_value - self._parameter_bound_value = parameter_bound_value - self._parameters = parameters - if self._parameters is None: - self._create_parameter_list_for_orbital_rotations() - - self._num_parameters = len(self._parameters) - self._parameter_bounds = parameter_bounds - if self._parameter_bounds is None: - self._create_parameter_bounds() - - self._freeze_core = self._transformation._freeze_core - self._core_list = self._qmolecule.core_orbitals if self._freeze_core else None - - if self._transformation._two_qubit_reduction is True: - self._dim_kappa_matrix = int((self._num_qubits + 2) / 2) - else: - self._dim_kappa_matrix = int(self._num_qubits / 2) - - self._check_for_errors() - self._matrix_a = None - self._matrix_b = None - - def _check_for_errors(self) -> None: - """ Checks for errors such as incorrect number of parameters and indices of orbitals. """ - - # number of parameters check - if self._orbital_rotations_beta is None and self._orbital_rotations is not None: - if len(self._orbital_rotations) != len(self._parameters): - raise AquaError('Please specify same number of params ({}) as there are ' - 'orbital rotations ({})'.format(len(self._parameters), - len(self._orbital_rotations))) - elif self._orbital_rotations_beta is not None and self._orbital_rotations is not None: - if len(self._orbital_rotations) + len(self._orbital_rotations_beta) != len( - self._parameters): - raise AquaError('Please specify same number of params ({}) as there are ' - 'orbital rotations ({})'.format(len(self._parameters), - len(self._orbital_rotations))) - # indices of rotated orbitals check - for exc in self._orbital_rotations: - if exc[0] > (self._dim_kappa_matrix - 1): - raise AquaError('You specified entries that go outside ' - 'the orbital rotation matrix dimensions {}, '.format(exc[0])) - if exc[1] > (self._dim_kappa_matrix - 1): - raise AquaError('You specified entries that go outside ' - 'the orbital rotation matrix dimensions {}'.format(exc[1])) - if self._orbital_rotations_beta is not None: - for exc in self._orbital_rotations_beta: - if exc[0] > (self._dim_kappa_matrix - 1): - raise AquaError('You specified entries that go outside ' - 'the orbital rotation matrix dimensions {}'.format(exc[0])) - if exc[1] > (self._dim_kappa_matrix - 1): - raise AquaError('You specified entries that go outside ' - 'the orbital rotation matrix dimensions {}'.format(exc[1])) - - def _create_orbital_rotation_list(self) -> None: - """ Creates a list of indices of matrix kappa that denote the pairs of orbitals that - will be rotated. For instance, a list of pairs of orbital such as [[0,1], [0,2]]. """ - - if self._transformation._two_qubit_reduction: - half_as = int((self._num_qubits + 2) / 2) - else: - half_as = int(self._num_qubits / 2) - - self._orbital_rotations = [] - - for i in range(half_as): - for j in range(half_as): - if i < j: - self._orbital_rotations.append([i, j]) - - def _create_parameter_list_for_orbital_rotations(self) -> None: - """ Initializes the initial values of orbital rotation matrix kappa. """ - - # creates the indices of matrix kappa and prevent user from trying to rotate only betas - if self._orbital_rotations is None: - self._create_orbital_rotation_list() - elif self._orbital_rotations is None and self._orbital_rotations_beta is not None: - raise AquaError('Only beta orbitals labels (orbital_rotations_beta) have been provided.' - 'Please also specify the alpha orbitals (orbital_rotations) ' - 'that are rotated as well. Do not specify anything to have by default ' - 'all orbitals rotated.') - - if self._orbital_rotations_beta is not None: - num_parameters = len(self._orbital_rotations + self._orbital_rotations_beta) - else: - num_parameters = len(self._orbital_rotations) - self._parameters = [self._parameter_initial_value for _ in range(num_parameters)] - - def _create_parameter_bounds(self) -> None: - """ Create bounds for parameters. """ - self._parameter_bounds = [self._parameter_bound_value for _ in range(self._num_parameters)] - - def orbital_rotation_matrix(self, parameters: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: - """ Creates 2 matrices K_alpha, K_beta that rotate the orbitals through MO coefficient - C_alpha = C_RHF * U_alpha where U = e^(K_alpha), similarly for beta orbitals. """ - - self._parameters = parameters - k_matrix_alpha = np.zeros((self._dim_kappa_matrix, self._dim_kappa_matrix)) - k_matrix_beta = np.zeros((self._dim_kappa_matrix, self._dim_kappa_matrix)) - - # allows to selectively rotate pairs of orbitals - if self._orbital_rotations_beta is None: - for i, exc in enumerate(self._orbital_rotations): - k_matrix_alpha[exc[0]][exc[1]] = self._parameters[i] - k_matrix_alpha[exc[1]][exc[0]] = -self._parameters[i] - k_matrix_beta[exc[0]][exc[1]] = self._parameters[i] - k_matrix_beta[exc[1]][exc[0]] = -self._parameters[i] - else: - for i, exc in enumerate(self._orbital_rotations): - k_matrix_alpha[exc[0]][exc[1]] = self._parameters[i] - k_matrix_alpha[exc[1]][exc[0]] = -self._parameters[i] - - for j, exc in enumerate(self._orbital_rotations_beta): - k_matrix_beta[exc[0]][exc[1]] = self._parameters[j + len(self._orbital_rotations)] - k_matrix_beta[exc[1]][exc[0]] = -self._parameters[j + len(self._orbital_rotations)] - - if self._freeze_core: - half_as = int(self._dim_kappa_matrix + len(self._core_list)) - k_matrix_alpha_full = np.zeros((half_as, half_as)) - k_matrix_beta_full = np.zeros((half_as, half_as)) - # rotating only non-frozen part of orbitals - dim_full_k = k_matrix_alpha_full.shape[0] # pylint: disable=unsubscriptable-object - - if self._core_list is None: - raise AquaError('Give _core_list, the list of molecular spatial orbitals that are ' - 'frozen (e.g. [0] for the 1s or [0,1] for respectively Li2 or N2 ' - 'for example).') - lower = len(self._core_list) - upper = dim_full_k - k_matrix_alpha_full[lower:upper, lower:upper] = k_matrix_alpha - k_matrix_beta_full[lower:upper, lower:upper] = k_matrix_beta - self._matrix_a = expm(k_matrix_alpha_full) - self._matrix_b = expm(k_matrix_beta_full) - else: - self._matrix_a = expm(k_matrix_alpha) - self._matrix_b = expm(k_matrix_beta) - - return self._matrix_a, self._matrix_b - - @property - def matrix_a(self) -> np.ndarray: - """Returns matrix A.""" - return self._matrix_a - - @property - def matrix_b(self) -> np.ndarray: - """Returns matrix B. """ - return self._matrix_b - - @property - def num_parameters(self) -> int: - """Returns the number of parameters.""" - return self._num_parameters - - @property - def parameter_bound_value(self) -> Tuple[float, float]: - """Returns a value for the bounds on all the parameters.""" - return self._parameter_bound_value - - -class OOVQEResult(ElectronicStructureResult): - r""" OOVQE Result. """ - - @property - def computed_electronic_energy(self) -> float: - """ Returns the ground state energy. """ - return self.get('computed_electronic_energy') - - @computed_electronic_energy.setter - def computed_electronic_energy(self, value: float) -> None: - """ Sets the ground state energy. """ - self.data['computed_electronic_energy'] = value - - @property - def num_optimizer_evals(self) -> int: - """ Returns the number of cost function evaluations in the optimizer """ - return self.get('num_optimizer_evals') - - @num_optimizer_evals.setter - def num_optimizer_evals(self, value: float) -> None: - """ Sets the number of cost function evaluations in the optimizer """ - self.data['num_optimizer_evals'] = value - - @property - def optimal_point(self) -> list: - """ Returns the optimal parameters. """ - return self.get('optimal_point') - - @optimal_point.setter - def optimal_point(self, value: list) -> None: - """ Sets the optimal parameters. """ - self.data['optimal_point'] = value - - @property - def optimal_point_ansatz(self) -> list: - """ Returns the optimal parameters for the . """ - return self.get('optimal_point_ansatz') - - @optimal_point_ansatz.setter - def optimal_point_ansatz(self, value: list) -> None: - """ Sets the optimal parameters for the ansatz. """ - self.data['optimal_point_ansatz'] = value - - @property - def optimal_point_orbitals(self) -> list: - """ Returns the optimal parameters of the orbitals. """ - return self.get('optimal_point_orbitals') - - @optimal_point_orbitals.setter - def optimal_point_orbitals(self, value: list) -> None: - """ Sets the optimal parameters of the orbitals. """ - self.data['optimal_point_orbitals'] = value diff --git a/test/chemistry/test_oovqe.py b/test/chemistry/test_oovqe.py deleted file mode 100644 index e7aa7a118c..0000000000 --- a/test/chemistry/test_oovqe.py +++ /dev/null @@ -1,149 +0,0 @@ -# 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 OOVQE ground state calculations """ -import unittest -from test.chemistry import QiskitChemistryTestCase - -from qiskit.chemistry.drivers import HDF5Driver -from qiskit.providers.basicaer import BasicAer -from qiskit.aqua import QuantumInstance -from qiskit.aqua.algorithms import VQE -from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry.components.initial_states import HartreeFock -from qiskit.aqua.components.optimizers import COBYLA -from qiskit.chemistry.algorithms.ground_state_solvers import VQEUCCSDFactory, OOVQE -from qiskit.chemistry.transformations import FermionicTransformation -from qiskit.chemistry.transformations.fermionic_transformation import QubitMappingType - - -class TestOOVQE(QiskitChemistryTestCase): - """ Test OOVQE Ground State Calculation. """ - - def setUp(self): - super().setUp() - - file1 = 'test_oovqe_h4.hdf5' - file2 = 'test_oovqe_lih.hdf5' - file3 = 'test_oovqe_h4_uhf.hdf5' - - self.driver1 = HDF5Driver(hdf5_input=self.get_resource_path(file1)) - self.driver2 = HDF5Driver(hdf5_input=self.get_resource_path(file2)) - self.driver3 = HDF5Driver(hdf5_input=self.get_resource_path(file3)) - - self.energy1_rotation = -3.0104 - self.energy1 = -2.77 # energy of the VQE with pUCCD ansatz and LBFGSB optimizer - self.energy2 = -7.70 - self.energy3 = -2.50 - self.initial_point1 = [0.039374, -0.47225463, -0.61891996, 0.02598386, 0.79045546, - -0.04134567, 0.04944946, -0.02971617, -0.00374005, 0.77542149] - - self.seed = 50 - - self.optimizer = COBYLA(maxiter=1) - self.transformation1 = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False) - self.transformation2 = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=True) - - def make_solver(self): - """ Instantiates a solver for the test of OOVQE. """ - - quantum_instance = QuantumInstance(BasicAer.get_backend('statevector_simulator'), - shots=1, - seed_simulator=self.seed, - seed_transpiler=self.seed) - solver = VQEUCCSDFactory(quantum_instance) - - def get_custom_solver(self, transformation): - """Customize the solver.""" - - 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) - # only paired doubles excitations - 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, - excitation_type='d', - same_spin_doubles=False, - method_doubles='pucc') - vqe = VQE(var_form=var_form, quantum_instance=self._quantum_instance, - optimizer=COBYLA(maxiter=1)) - return vqe - - # pylint: disable=no-value-for-parameter - solver.get_solver = get_custom_solver.__get__(solver, VQEUCCSDFactory) - return solver - - def test_orbital_rotations(self): - """ Test that orbital rotations are performed correctly. """ - - solver = self.make_solver() - calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=False, - initial_point=self.initial_point1) - calc._vqe.optimizer.set_options(maxiter=1) - algo_result = calc.solve(self.driver1) - self.assertAlmostEqual(algo_result.computed_electronic_energy, self.energy1_rotation, 4) - - def test_oovqe(self): - """ Test the simultaneous optimization of orbitals and ansatz parameters with OOVQE using - BasicAer's statevector_simulator. """ - - solver = self.make_solver() - calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=False, - initial_point=self.initial_point1) - calc._vqe.optimizer.set_options(maxiter=3, rhobeg=0.01) - algo_result = calc.solve(self.driver1) - self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1, 4) - - def test_iterative_oovqe(self): - """ Test the iterative OOVQE using BasicAer's statevector_simulator. """ - - solver = self.make_solver() - calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=True, - initial_point=self.initial_point1, iterative_oo_iterations=2) - calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) - algo_result = calc.solve(self.driver1) - self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1) - - def test_oovqe_with_frozen_core(self): - """ Test the OOVQE with frozen core approximation. """ - - solver = self.make_solver() - calc = OOVQE(self.transformation2, solver, self.driver2, iterative_oo=False) - calc._vqe.optimizer.set_options(maxiter=2, rhobeg=1) - algo_result = calc.solve(self.driver2) - self.assertLessEqual(algo_result.computed_electronic_energy + - self.transformation2._energy_shift + - self.transformation2._nuclear_repulsion_energy, self.energy2) - - def test_oovqe_with_unrestricted_hf(self): - """ Test the OOVQE with unrestricted HF method. """ - - solver = self.make_solver() - calc = OOVQE(self.transformation1, solver, self.driver3, iterative_oo=False) - calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) - algo_result = calc.solve(self.driver3) - self.assertLessEqual(algo_result.computed_electronic_energy, self.energy3) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/chemistry/test_oovqe_h4.hdf5 b/test/chemistry/test_oovqe_h4.hdf5 deleted file mode 100644 index f5aa60b0cceacbf030b2363bdb07a4e70015f137..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25928 zcmeHP4R}<=xjspNgjEm_HKh--gor^&2nKCPcXJj5Tp^_tLIMixZsL+GWOw6clbfLU zY(YuX@|bGj##>5*VoH0d4N57(!dbMG@KXikN4!CD6)eUJ2K8!8d9*WU=H0VUnOq$En{BkPy_p$cV6j*4Q6hwhQa5Pj400Ys=ws|Go;-Xuru(b3T|G>-d;iQ+IM zGFlJ@@jM0FU%JmE2p(S2waKjMKb;gM#0qnG7;{Fk6S-03G-)Qw7vIlPElUJnEIR?N zWiZPRxP_(Z95-iZFfRUfUe2#$N&+tKP#H2&W_U_zKr7ew!9%%$;@b zl(8v4N*QNyJMDU-mYk=hr>3TAQ|SLc)*7`bWA7e2E?q7dn>L;slVzL&N>1f4xq#s` zr4E~0KfzH@;&wUhn*2hGtH7#H)sz(593;uJl-Nr2C2nV0fu?Yw$!>KQIv>)z3Kwef zosKe-+gYR^YfVlaud!RoP5Bl1MOOXYsTv#cX(}kSmXzr2HiyM2m)fm%r>jCg-RZDu zc7Jwon)${rr58UNq*-78smK=)0$Pl;{QbF!In&+~-y52;-23_|F*d3tHa(+B+_iG) zn$J%+Nrk`v(_6Did6{q9SRW}*A9mq~_hd9lr$?_nMasohy#D-K<@(Z5L9Q>=@%n8W zSpA8tzH~pUFW!>O>I)BV`_H0(CLfe0o%>+Mo(+4YqGg4phGBK0BicJ`&68V1f7f9S zLraY0=zGpEGG~g$;0N)O7w%|yBxdGeaaP51>AOZNl?1280|$RXj% zbN|8Yv}(`+JNM_~^(6QK7|K?v-o9&P>*Vv^g=vq>*i~~;yyLFqkN%)(6@}HGtvOeB##_MciBnIm zYugjQqv-Fgr#9d8fw!`5>8azftqk+F#vHGI) z^tw?Q1N(jG$A8G}eflg)+^CPy3%@9;woIRYvp%kB#0qg-n^^f@r7M4@Ym+v(){R;E z_HG^Q$LDmO z6?D_TY+n`Qe@@bLW8^@2qlI!+KdEb>_~0OQ$@eqgak}dFz0lRA@X@#`BL9MHGj)-T#XRARf_jfxKAe~L=Ml$i_=F$#^HVv zr;UtD;>Gd9BjY&9LOp-)6b?Oy7Z?-6A($QWPTa`IxNlq9@w)o7N9(HN`Dpu|@xhcI z#a?HV??%`WE~QU4=(C+mU%y88Wu84O;eSKduZ2P7ot6#+HU$i08Qyv z0aGSq&6-TkvS-ewiChqV{l1L1pVgM}iT~@ACGLjBc9Otv^%f=dkY30cv1>l@bA+Z+#Rj^&R`(mA=2FYl!gcTQBDY zD?~N|Lg$~Vt{NH6Q<}27QHx1P@TM?@FZ}_|wy@CAYuh)INf6YOmhxTt_PE(}8E&C0S zfR?g$cJ(-A&v$!~-6uQZ$McggNgTIVr-cD<^8lWny*n}vB11~AWr^2OrqD4y0nwX)#Zy=TSOdS|T2*|1%5p2FWPetJo4xp zV(%Th-S>W7B^tON;9>voU)&m9yLg|tmHRJq^gkVyZV@#X%T7IUEC2awzx36onQFY+pBj(rz#sToz}pMFt8r8Ou-xe) z?Akvs$Q?;|Z*2N_U%C6oV$K(mFNVYiJnT2|{sABG13c{LU+@Z^5uciW^b_iXPvFrn zYW^SO?E+rSKXe71?c_Ws@b)4;=oE4PPrt0bWkjdIgGcD9ocAyMG;%!bsnK;U`@1}T*w=jU)s16_PStp|KQ&(M zPpwzAp3%S1EBL{6)p)f(HD2vctykc|TR8sFF5tmGbbxUKKfuF|@uZ%w5I=OGp0D5^ zJOht@hfbke_yHa|hwjzm1^l4h>hS{oU_1d2JH{!-GsY$Gup>UijrwXnVTWG9C-e+F z>}W6IN523MJFW{}piAfq{RMkx=|3F*;7!fHdOlP04_?7D+N>^24DbOR->e*<>hruT6fWYc<&%ZuyaDV4l9aCV2w& z7YF7|6NAf56DKI|sqvl#@q|l5^~~1G-C%t`mB-8k#WSJ(&EgH$zDu=-^3hp(PUsPO zX3w4a2zdV9Iw>-v#k|f6#Ff|L!9k+Q19a?+u4wb}cWCOOK4}=+Y~v zs}VgyS9q>$=F@otC;V)FM>F5C{zm4W(0u)qqx?73`*pH+&k?U6G(9}H{^6bMYWsC@ z3!c_5dW_;`l;2}?WitD5h8HN_EZeirR}6fnlKb-<4EEzY#_+`c@?*{S75xRcy}Cu2 z$rI8b^*FFC1g#!e4b?*uN|r zUHw-{#tVnZbsv}U!FGw^Yu~2nZ0hH42Uc48H^0vK@a*y1nV%C3FGxDq1^e@!f;Xf+ zfp}t`*)K!f#b<_ z6uW`a-{WKPh3LP*Tf)ansQ%|E{F^!d9!a+&khf}T zUn}#k3*=uwdw1#-d#l2~;b1WTz?9N;)&-v9;8?tsY|5`fU89vA^Y$5hmW&V-p8B(3hd+~wuPyHc+W8)}D z&$2!lUKuZ2mG+vC=xz!0BW&jJ(RhUC{qv8k|3_t=KkU~DK?qw29`g*zLjRs!I_NnF zkMY$+c`jFUPU0Ny*MG5MhyI1Pyvn>3z<<-*s(Fm-!w|@SzZbj+#ube(c-oS=S_nz@#D2WLy$2HqVKS!)$L}5E)m+i;GQ>aa~m!sw=hv#_z9{-*K=A zFaP_)+qtfof3!FBJ^^NdxUy*|ADuN1i#=kTf&Fq5*-+3O7@%D>M z$&3t*6qT@iAu~0lH_A(9mXu~_e)Y|qd7eF&WwA^HlX>5tbLN@MQTLd8Q+>1RW>lzLs}?0PUs_`J1`Qf0P(SeF?FBH*6f_|%pv=JId>@G< zM1-2!&4xbuSYI#Fkc@&bM%}|NltIzUz;V3nc!cLBo)Kq5eu22kmb~2TA3P5M+^9Bs zT)^E>5B-D#PBh@Ef_OzhFwX-4SJ|3hV7~!(@Nx*W$K5X7SC7kuGH!=_E(tMtgX2G$ zdD<6^{a$Wg%xumB!EyY_u33Udw{XCf>S}Ag8-&faGZZ?qAew~S7K~(N|HUn zKPE0ZDb^Yq`6lS311iP3hYHo=-;Ps#{=)ShVdpK-oyVAQQY z%dekp;h$IdqZw`>2#y%@;k!0$89wm8=FIp3=QnQo!hE59MMOx?3i-)Sy-Uv>t1$19 zAMZMX*8ehg*=8rL|Kcn;*sW)U*|zAjb+lfd5A9#$?d297l=kMf&|Zf2MZCS->YUPE z9uDnKtaxq_^B)p)yLl7!-}c)}YrE6_AMok+H1ogx_;ZmgzH!dIsWPpf7J6*bL$v>h zcTY6`iN<#Z+K2M?@_+L7g$pyb29sFVb4tcg5 zmuhLZ(tL}*)GlXEk$Jkr>oY2Ny)&5CuWH2WQCYadBG2J8HTurdX+(d23t})|ac}7q z?;-olLo;3sDY;**2fPz;m;+u0ysJJfyJ<}H-b%T7(>B9=X1pga3H9pr#(`P#VcV+p zEBe0evY#qj+>qfu1AEHzEq2YLIK-nJ{WSxB=m+s={|5Y#r{&4xx(qpK4&F7oe`3$s z^6~pm_?(J;TFx8zbxKf!nKIfDhxWpamv-%=I3pf$*v^PYJK{J&mF9!NBNt39DKw8f z_vWJozLU)Xv$JzQpRv+qM;zKaP5x~10*W)@5r_Vac(fz#t0Cn>dc5|7+->S3KCQ+V zNd5=^6Q9{GPudyh_jS+I`I`@v{-f=h<8Mo$rtP7@$13I9kGGz?WAx0htfLK^u8m$Q zq#U|yXv)w`seD~4ZkKlsIQ3$sJh)4*l1W{i!s5}7DSoQt?4a`*c0p(%z7czyQ#jQ* zFL2_wXXJ^)dH**h{(H)}^bOLOfXB|39eGju<)UM0mx$S6j@HYjot;%E&FH}UzuTG7 zE@0b?u<%1??)h-nbZJrl^g~Tf*`-f9E?4Y>B;NmvpHGqJH(n|nYc){uQ(5+KK+ru? zq=2d>r=G}pMK~VF`}YTde?F|${+mv1GIL%CN&C9OyJJP68SSrw;3sjrj5y5E4*s0b zPDVe7Lp%6$`RO$C{`3L1jjsNC4;wLJ`P1*nXdea(4eTx)`Ze02os51Ehj#GiM0;)J zw|%uL^10)_o4z0Yzbl+eE}6?8E^4vXze@i0QuwR~3N8p|zfpMu;Ihgd^WANw|#GB_9b)cH5=z9=lmcc zel6HL<(=ETue@QXC*E;0_Ehh(<8u|>&zqpeWVoSBj83q!sx)g-vdx}I=+QjE7R#Q8 zc*6U4C|)*mf3D(p*yzt2o|kT=26ij%`T2>yv``Y>u=A4p^V1SuQAhvf&sE;8_aqg! z)*L7dnxq@%$=C|3%UT#~{+{Klr>mR)7P|T)e3Vj2cwT2MfDeXJ)AhE_F84gWB!G_% zGIgN!S2qL$#`6%*wIFy;g(T1iRcoR(DK-NPiK+CtG&(6V$?8arXD6h-+I3Ev(19Vp z{5lO#d^9(i;mx3hJlL2#_#3>B^1N;j!@7kG`gLEonTCjJx5&@K2ClcFnFjui(cyZn zxZPNDW0$X;uD-f1b>-=Onen~w<<1FbTdpg|jV-SmcJz2o6aoXddyeYJ4IXu7_&mcC z&iHu-5`Fpg{6{~3I`uo(aTbP9r*ix}3eV}l_BDqW4%HzYYR2Jt;%BNE7YNk>ch-yp zl1U+a?&JwQ3M#_y@`T`a+;{vs)r|YSwO3zPKVH|my83)v{G0K?mG8wr&ZgfR;mK3Z z-fXaUJAeHCwLv59S>ze~>wfno#1$TY?ReU^qo$$eTQ2d^RQ5Szg5B$4I#H5>I zcRi%*>T9czn3@o2Pa0=SiH=WZF|cxUydy3e0%GNH))Xv~1bbwR-8x|c#*5|1-rNGd z_|v7$s`z9zMmyr!nq!HplV4Z&ue+YF)%>-;F6QsD_y6~D7K5rIv)=Z%XTW&>9|a>0 z`6s{dbYE}$e$*c&%6*;sDK2oG|MBmi^@jDx{a`%q`)OUi|Mw*p2t2S0j-dK{Fqlum zJXMdv@9xp>8^ZU_JmE}G;dj3$1h?Zp5zx72Ty51}eO(Q>u61?w`55vyMS8Yi$TlNR3aiIpS?GwiM+f z0_DDsezOj5*x0x1B9k)`#@XYQCQ3OyV=}hK{T%lzv|nlNu4jHnXncOdueaiCVEM}L z*T=&AM(IL#pWirL{k1Oe`@04ne{YccAdi5noc?~{U%YI()`8XguM-Ry@9#v2D!lKt z4e$4^Q(T|M{L0tqW>}{9^yTg^CJ|5Xpw zjH~Ol?z-Yk;W^_?8D`f3^lQ;9XTM+Jb$+COuZW@jsBPGg;Ve@sJfG)bjTH>E=GWEz zo+TZtY9)8)BT$LMcpe(p7nZMlotc3jQ8(_|UT3w(hkTvz5zh7D!&e3B;7ao=*H2(i z5Ds6!P;l1|k5?zMKT%jVcR}pJ9VgB2`SzSOq9Dgye$R}z=S4)Bdku(N@bUu-%^Sg< z{ruS6gpYTbTfUGtI`G};=9L#3A378=)`ds=>j@)wP5Jj$v+t(uDSdv;G+V$A;?cf# ze%l7Q6L*@6!GBs}(_>yC(PsZkXt{3D7$8B6Ms2^NUh(|lFQ(VuuE)kD*j1S|+_C`L@j(SBtQO}4+JB}CQ z$9X|K+Oc2c1$BwK!udk`wbH*Q{*gB$|Hl2y$UpLmJmYwc{Np?s_bcOmX56n_^I+Kj z(2lx7ogvS-Ut)Zy)7rZ~G&kY5Eu!x3#r=LKPFH`e3$@3GTqpTJzT;x-Nmg1=a{3|k zL|dXY#TG;C^oK+7(GFrwvMEsdxiKXTf+p9bo(5L^yaF(!3 zM@Mf49LE!e7Zv;4Co`HqKO6lO>HUb?0be9Eo_6YA@f%z?X~dAukAD2~R_Z_K*Z<40 z^L{NQ8awN;g!-e&QcClEkuQ-~XdHu8y!auncOI1P9{F?H{aM>&1uvG+e)lW*K%Md!eL?=>65SFGV#!#^pJhPmd2_2h*982@F!zm^|Q z{ImEuUN7{t&JI%5n?%mWcOS>pOU$Ix7 zYg9B&MqO>8^?9lvaSz~2q^j|mGf!V&xI$IWxfuT^lFBD_AI0pe6#wUX?+efNck+25 zuwI;>NUDA1o%>(S-mHWrZWJDO%?q>O`jV(WXS2)>@qqh)#>Mrl>V(2YHovHITDIVL z*GZXU_Vh2O>qUnr>!=@^oNS&dW?twL2|OJTW&@t`s@ieAX!QS;PS5$&k9K}R|KcYu z{p;5Y=-qV!3C)0nf%D)NtC;RpMTbO-qHT=VPP5U+c=V|zVU$ZV4 zG4Ig+SlUGWWhwmY_p5`vURl3HT}R?U=|`8Yay9;8KO#(L`HEe($9>~>5cT&N?MLee z*UuqgQTvM#?VB6`9Tf@czNF#JvQ1L$)RV~iVTr|$^Q*y&Ojk5H4+`)8oIRj4VmtRw z{m?swSxgV>wRTcY?OE#n6UF9@rPccVhcG+ee{}1D91u8ujYj7_DsN<)q}#__covUl z|FJOr^Zh~93H7Y(SGdoxo>cwMsY(CJIpI`wPW#36LP=EZPSjhGpzcc=KAl_j=Lp$K zv$HGcGdFVM0XAz;q6H&?WaQBpT#4>Mb~~snydQ;#IKwq z6^fqqx?=OI^~3gUn#3bgRgbLuiU%Ap3r}g!e-Zl6rFrFv(4c92f1*vw$Kv^Kn)Tre zXkP^Q9GW{6d+SQ=`JXG%reCXm5T7Tlo}xYf9nza!@%)c!IBd&A=r>zX?JRJ%AF;&F z<(98K^Ui#_zYW&lS3~_CX}qYM|FFImy6`btySNAN%V^H~ZTzYB{1+v(tNpU8ol=E& zB0K-hdi+j`XK6mo*GpSX&%!uF7oP5m8v9C$^V9J2qQKi{N==$-&VRyqtL%O6*OS6l z1v}XJ598Va`1w-4%bs6ylQR?geM7og>qmt7C#z_`mfxf@E9H+TmFFxCpNneXp8)?_ zyvo0hXC;1)*W339s;*cZIKPFi@$%>jgn`L;{^vS2yfg{oM*N!;?^Ntn+iH1ooM z4vr^TGu9f}>dLR5I=_o3jwOb# zQu+<+qdosWq4r#jAHa+Iq^y?O^FLow*F~w0AN@Xt`!kCl=U0noc9!-@j~F8dymNVuU{`b8bX#*{{^bfb^1r%7`Bk&Skm&Z)os4v96|oG zXgl=d)XY~+{ByjHe;U^s-8s0Ij+e(n^{L^fOi=eLu3L^PfcV)t!tHupF}}6?Jj1cH zQ&SZQEb^C5z%hRJ_RS3`d{;9h&EV zWxaEJC!HPcx18H(yW)t^?K8sSO10;Ijzq{I#m@cW^UrRn85?vcj^zRi6Lh{Ac?M zO%$)%PrubY1D-!E+XS5-0(ip4oBQ>dIK0m)((oLm@=1A9=Y{p-r0rN@Jhue?N2z$^ zpT2+5^UT&ofP&ZA>GS{G7}OQ|#k{cN(Us|-g91UG>^lEL7ca;2AD;gRI~TJQ`^x6p z^Pin>SsFj;dY@v$^S?}b&GIDgCwDvbLlfPfNB30jvw(kQk3ZJ4X;xUvSnc`$KJ`zN zbDyNzbJpxG0RD+n(D@;N7iHGZe?OQ8&;Nw^54(B)m?dE=zE^a`=T~`O5u(_Ye#Bj@ z9ZfmX7OS%V$vQl>^LqB2Xn7|>egyQlM=All&hBLTqe;BYWdGq`KLVV{E&?hAJcI& zh5vacwEGpt&qi_R8`Be+z~5deybfhI{~=ZX1omID$ff_+b@*H!k41UjUgy$3T}QZHXuPVO-ski9OC*)& z0u7%v@t+I)7i#d_TlM^NypjJ;r)tkZJpWhFxSZ4cmd1h(_6oC}!TpM~*$|1(4Y>mE zk&WB=Kg|A6bB(UVGJ_w)mq{_*`SbtfelOt%zgWiaON;RJ-b@^7eSSkQfPWvgW6Tc`R%D zXOjEpl7Wi5zu^1dH}kkfUHI;wJI3kiuXO=G?_L5OmAYo6`ko8He8}~Swd!$xV6GZp zeLY(7V`&HfdI*}`@^G+-PrFs=47d*srhlXMuS~V4E4fbUs`mIWH|6s#>ZZT8*IDiH zA=mRkm7xB2cg(lc>qE;&F8?7isaxxXq@M2(-Q<`sphsCB%YzrqBTW-xR|yx*gNnlT z?DG0aLVMb-w07?oHx6z7Y}RMNC6$s-+JTNsdt8(ezxG)F+d2)cl-f>O=2+p?YSZ^Q zCqL@)z(x7M`6uq5fA3F{H{j2JebMx<7JRbqHzE3Y^E0#F+9Zq*yZ5Cq+W)ZIdhB8S zkK62ZzUi{lF8j#)p7>?V^wYvy9bY&(wd%Aqv1>>9-NPfY0 zQ^#e`rUyN6(VRV}Z|g-#rv=2{1@_^M7QNc$ql<9guG_-u3-t`tGf>Y!Jp=U&)H6`W z7^uDbds70ZLew#G<38`?*7HG?pg!-I66>Nq_||fFuO)=&;>VNenVCAz$1tYatp zl5H4c38At6U+;c@&A#YHh+ zO>r@zf*=U#@sVQv*Zm8#fRrDJ$Cs2^n5Jb81N&KbX>HWO$iyYc&&Rx{mw~a7vFcC# zgPEQBHG|eh?OGdmEE(+U6=ZJd>k$|f;OA4)&C?~oLvC(dGSJ`4S9x%C3G@my4-E2a z>rv9v-N{EDmk=knP&aS6c{}5hUP_uy9>Ma! zKyx22Ul%`BtB>5rFCf&sx1X=PjiREnM ziF*exzs%{D>0aA@^{?F6G>-#XW8(RVao?`pXd27=TxmRi{MtK;l-m(u4Ia9g4SPFr z((P~8nq95Z>%@U~F+A6$dGA$g&nTAl9@Okqw};&Ru_?!HI8IJISNT;JgSOeJ*#lm; z*!;qqUzE_j#O0?88R4vje%!q|-##3|$M8*f`^ZVM35r+ttvC5Pp5=_*=&iW1^rph~ zzTJ_A42$`lXZzYd^m2ucG{t>14F+~J*9{{5bILiWE(f=fRFsv z^Pjk3IPSRhjfY|P8(f}OQFtXw_w=Gbod=Qp;C-sSoAME@nMdObi@e>Y3U7JUyi3Bb-ma7rV{d=O|>R zbNt#`EiY}+a(u#ynmgu83ch;=x7BEPl!9;;*ZN#`ZnBdT3q%ZoBT2>^kFLZ)x@mT8I$&LSNlX6R$MTe*Y6X3;ik=Vvu(ydweuLURIwbml_z^H z2^^K97|^lCupg?d;--|@+@jN=MZEd;%de`IPhhGhMcXrcO}gk3mOUq3|)4WH@%!n&$hUY#UUzhFUW zJrW|iPvTzGe4?Vb<)3+8bbeLlAK_OYVvns#myjN36=9ED)E|Fe+Bnm+o{p|2=!U}| zivIWMUtt09d`R<}G3r!g1m)^g?kf-Q2*m~8VC7=fCBP{_?jP)(J45Dg&YLFCfd)C z!io1Y$k3X{^M~#~WxVG&o8qItS${es1_L$hgvf^jsT=ou7@Yl`M-^$Nzp z$g<7YcalOoqQS1A6hgR8aYwIl!MOKrEqcGY@2?_q5gJ%C|s?xWbaz`Xr@Z9GC1O@bDT`YU{vsGUE zy6-o+2UUY~{$56SHV3^`c;$cU`iD@{CpoIyLx`aCoP^jf3hxhPVGrYcdVX&9AmMz2 zeVqIPT)l!^yaRKu<(|8E`+K?okbCYb5290^&Ttzdt zMSPv`g3l?R%OHXhNA>$B3_I#yOi3a4^T+#h=P$yaHRk`#1w-cdLJ8ykcljS@0dfBC z3=K_N2_K~Ldhzp7A8HuQm10(E*q5;y(R3 zik{t5K5vNU&ZKY#XwYjZg%GY&oTz44Fz!>e7QJ6}{i6M<=&_EuK@g=gJJI-g+gATNJ!FE_6s)fRy& z?^8ZmM+sZ*Q+7^)p+2sD-l{ID^N`T7)SvP$idS^Kut#-&7BWIwzx{FF1P~eh^TLm_4Y1GWHO@Yr zJ&u$X9N}}fhn-4Ht>S`Ti~Zq4&kOHAO*;wp3Ktv}qey*x3G$-(qYoR(MVI>J#!IeB z(#S1qUY}5V7Md8uZ_;zDS0|aP1l-iLu0sb0U690ozp~`C`wjM~wb$?Qibc9g_alDd z^=_`+#&m;wL_SY(EdCrCtfMwZhJUMeT`Vu1FBoWLP{MqsBt8lB7HPnBTUGO*$wY5SI>fK2k9LLR zThw-XU%ft+JwMh?w^OR_?z+H9N$9 zhJ<`>z%^L7xpLWNQCuA2l`i6oSdJU8OR3AY)nqrvaDaTRl4 zuKYvGqvOtfuhu?2YWMPtmg@b)e8uNd{cb_N_24V+=NVchengX4&g<*fo@O@lIMSnn zgnXXBZJRzUIpe#PdGR?+LwqK?5N?o$_$(FwJ~qyZ_1APx{0bYNBQQ^E(wF>&aP_id zqvu8|Uo4dPn{o4Ti;+hZYV8|Z`Nf7WlI|x#PWG|=YDX>Hkh*a%M|w!fN4T4Er?WGr zsLL6xVV)7o|2(p_qpR;`H9HV)A?Rtst)A;&=|H8~l5~h(Q6FOq$+xKO^uBt1Dtmsc zo#G|ppIC45lY?iUF0NK<)nD!p?`zMB_m(u$-<-fDK#xNb@|6cJxna|e1}UlB9OT(j z@s#Gla)|eYn<)`ri0{1;_sZ-sR?j~tYlSTb%fijVV)i*F^;wrf3dWu?uWlS&MIxw zj<|aAgU2QHyL-(dp@LgFHwAi(myqvQ;O;j#8Cm_R^#m(bI&Z_T3-I_e0mQn&Jl0`$1xyD!9FTK8@kGcibsehKJ zZxD{^8=CC%SGQA~&=gPJR}P~0zW5J~JMT;PQ{hN|P4*<*`^qV0r}v%TX`keM*YD?1 zmlDfY*!jJ%{U|SZQNHKG_1u2W3;R76CFFY!TxBhTbpzIBaCNY6?}>e9u|6)?FQa`S z+9#=l{QxnZ>XUm=hZDE|g8G5@x~6lfezc!QaYIap>@w@tDxG6FAN6%%@zP%WKQ?x%wxbG$Q2RHuB`-8L}O8b&x`N{9eE~T;0DYZXHINBei z`m@yY7Bo+b>(7y2Nd5VosQyfLk*bfU!SDSf>d%y)iSL8dxu$g@)s?08wQE28=a z(UX!_e7&&sjiR=bST3paqOxaE*@>Q)qPQ!T+f@{w-4VxWA6AT~eI}{+ClzPKbj0UU z{Ybt_$p6Is#POM)Q=oAq##0N$gK2VP7p!V*LJA z$hx;wKUxQD2Vb$i6){i$1bs;le+l{a0k>ktERVT|?sA{*`PBPm1YJJ&=Tr4mV$xIB zMfW^+>2iYpL|Hgff8O-dKF=*kC6jr&`31<8C#s|JCHRqEzVaY1H<_REi%Y#-{AKb0 z;fD#7@6{{6Ib81KtNgjl7vaysdoLwq^!{H+u2UEA=a05b&jLWoCTBE9xEG8g zKaO)P7-xaz6t{wL258nZEf`nSt$&|imHi_8ir#B;MmRqRgJ*3hYReZby)>sPm-HiE za$}OCE?nBPOW`|i?N{4Z?os?w@wVl_mx0XaVU69j`ns|6wc@H~Us%fp0Jrnpoy>u) z&vO-$bQfJZH;t=vw(;{Hns@y#a8qpS zoVUAb#gDg4uJgp-i~V$L(S{0VqqsZZYXEvnfSa;=e&q&3FL1)|S1n}@b-gOb4EUP0 zgnYzz9OwyFn$M=9pIx@{_ZoDf*ahfe1NlvjI`P63^56>utgtHEv->Lur|sBa02>^oSX;IC#pH8_oXwk&5skOMO)Z}?!1X7uZ>DP3`VP+%Sg@ToyDUb`)#o;25Bt2rZ_$?KeBo(<};yK8z3e0-=bpA0<=A>U5$T@QMKT}}9vsh#y*ZS9yL#_wtP zX(sN6fU{uV!cN$PiU%aUDh8{FP%#S(AN+t~Kk?(F$K zoi?kkWTrlvynXgtLyj-$tJQJb-9PNP1AK`d;m#j@Wc=L8noofKNzjAj3jkl?>xNBu zvXi^V>auGxEckPXSGpLN#W0Q*!p>xuW4(s%Kh|p^N4S_5p8fBZ3{`km-g$IRhi^D9 zJ|;S7aeMyq`8$t&9h3pVS7n<=LYdLw|7O{r=;GIKKL^0M*^`80F>&g#;8`yFV{ z+#hU84QM}#TL!*Fk8maG#n0UHz=%7eGVCL>@RDV%I}9t!Z-u_Z_b%vV0jG<8L05X0 zNII&=NRJJW&jWm;KyNH?rY-bM<_>XH1Y{T$@Hc#@p^5!e=g@`Z6=U4Zp*zy4F#Xm5LXY1$tq?eQTwYJ~1Pdn{^;< zTjj)FtYmkc#@TB-^6ZGqk>IdN+z#+H2E94J^}V}l*NfwU+zXZBt=PSonG-@1y!dY+ zUk3ORJt2OYvVQ3IQfu9J0aZ+S(u3qP2VbI>1Dvnc4wE+P)+p*D52JkJ;%i}^PC~pB z?gPg>5nl**8vbGkJ*-|`JuxdR8uOPUvzyZ6s_x+l>>%j125$U(UiZmaPd2#P?qt)% zdB8icmp`|R?%l+jPX=G2Hwd`W&vY#7A8E_F_h^!KRlh%fuTJ)hCbId8dhYF-*M4Kh z9NgMm-n*zjb4Ymc^XaR7SP$Sz#5RAxE)Hi6XUfX4Q4ahF=t1)R4!%V1nM&cVe4Q~> zf^ScpnV0WY!1%3+JVLOGH6wZB5O+%e7YBcSg!}eJ)nPERhMlrbc3T#aVa4i$-WlLx zejK{((3Z)(j_L+~&4xh#(cMocgxPjsEUsPuyCGIAc>H+9tWyJ7`ti%_`O5=Xn`=)4 zG89wz;Ok$lNpbGWuQ@yW+}zgvd73YYFVPzVoX(av^^e*_^1s2JTcAhjQ-XW};H#^W z$1x8l4+&3GocNCKg*=<$Y0_dLuWNw(pbF;cril0D5qEVles^LV(f!DQT~5Rsdg_EL zxNV?ExE4>k1Zvm)P7$bEbFh}ZEAQWPRP$vgZ1^m5+f6|?V-zjHw=4XQaPHIR8xCJF zP4TAzwE5`hnhS70#Ncel2a%cl}(}jWvKC#~~l_wFJGUz|9Q} z>EHeK9?lQzBbsNnBi}VaUZ+<>$e+t&p5BG|s2Jj|A@bt=7)LeWFPX4QGUycpt{n2` zN63q7X9)RoIPzE{@I3{3S-`ar<0f365U&OpA2;Cl%4 z<^Z=4`LjOa^CHCEme7OpT@%DVN5p5snPNT~jkrtk^gPB9`AaL%BitOs=jn*M#Sx!7 zAx;usidTdif;jt16y@X0^;sU#1{*Uqxzs144hG_ǵGq^_g+Sfet zV;^P}_v7pybw@H6pVHUX1+L;&f^R73)dy~@zPIkUa`e96eCB*KDqslrHQx;K5nrMw z*e8OGL%+z&)vM&b4byRg)3*NUBs^m>=n2(dV}5&PQ0SwRj(h;>CHBZWwJ?4KJ*^qZ z`xbe7U*NhS532}&7V!3Y^U)NcZ5 z8MZ1LzM}cGm(^YP(U5Nx_`U(XUxCv_zuA49V`pCO%F}v_w2Gzq_NYpo?v2NUg^%1QDsJ=mY66Ou2hk2Ul5pFf&3)L+IyanS$^$ns&xU;Bl zq@%tOhxPcyNH(X!EgRyhZu0K;MzsAb+_6daaN@Q{1I^CEy*{Q}`d{ z?L?3A=X=>g{@fFJ@qOfbhn_a7(EGI++nDq2NN*QMHnB#+58a&|^77~6$e;UGuWRJ` z+MXXN%Ad2rm*~Az`CUK0KJw>@h|m4Ch4u7F#Amv1LY&~3Gvc!ya1@^x|7m=-XTFHf zr$BEBaMci>DNYFXab|Tfj%Z#W`%!%MMSN}}iqE$u^tuz(usd5e|Mc^)h~BK{^7)bJ zYX&pIZAE-8z3);|oBs9k^zeav^}&}%+!5*`t=LY)=kHLz&PJWKKJu{Z@aORTLS0H` zAk<&9QD;cOy7(L97sqjbX&vBX z+hRTQGwA(>{In19uwC$HnpfR1f5ap15MziFrPHS^e#}e(X8)Ao=9r>jrwXZVo7~KQ8Wc z1T#kcnd*dZ^VOei^6JmqQOBhH9;!_ps0R5c ze-ZXYEO_#ZzNkOH#C~3D)GhQS>KiGj3s4<|>Kni1t8esUH1A|$Jx%qJeZa+H{ULmL zon2l&ITrcL_+u8chk4mD+n0;%4o5h!Y0nLl-OcS-Yt%OaFn*gnyWPjg!Ir0e4sm^h z##?dJH^?71Lyxae-xvqJ!ox}yJmFkX-v~wi+y{B_JM13`9K@CGt80{dS$gD_ODZ;ErR>^laH3r%3zRr-!nJh`Wm^ zUTL?Pzx%OGUj8he@0I}982K}e=U32UJme$3!g`2t(E zzS^aeT9bRQ{fN)BPvV$9X41SM3%)yW_b-PX@=uz>S_K`hfB$hKM)5Qd_c#2F_7k03 z{rL@`XAIoMn8*WeW9G6Bh`X`SgU@bxZ{S&9ejoT!{ja@5eC~|+Lh_Nn5Iu2xUW&Lo z1bUEs#FywT1Mc*=-L;!)&t|l*orC$k0p{ag$UABL()_NA`JL+J{+PG5k%v*eE*$e` zThMa{j@IF6$V+PA{u*GObp_vIphx+OO}_6BSjAEhcTXa|5Z{iVC(L)N7>~SQZT&^> zR^OY+<{(e$5Bc7L?_SU&Tr%c&+K=CledoQX4+-(M6;Jbt!8Kw3s6TMQ*bf+px(d~8 zY5W?a4wnLY!NAQz{Xlr=#Dd?Ac`O3!o(+vI+b;aA9WV13J+De=H@i}3~I+{9!p*RcJ=i~v+|y2 zpndyBsIP@$osxoiD+Bw3gqw@~p24V_QT?7r{BuAZvkB-80!|n87gyxt*@y=|SpSBB zFVS-XZcM)Y!8>M@Z;*V%_ZjHX{$L3D-M=c_U(&+{^7(@A9nAZ$fjiS&*dOeS`ZLv) zOJd(%8|yc!??!+gJ@-%o_2*R7pSxkbrH6UpH0rx$L67!RzCm5e3;BHz;^a-N_hsNq z^gMwZit!-SpRM^W$ge74o_>q@lIT(Wc^LXxP+b{%oPvCJz?br&&cH1}{dpJa8*e7A~x}Xr394eD`>-71xIz>BM`t z`2I%E>3#SyCLI$tMcVQl<~If6?nKWg-A0D=VV9tXHT;kAcA_Wb&kk%n@@IOksy5d8uTFf zV!&7Ul6MD2xQ&R_qL@-pT72y%}A{8*r}eYlsu@)u_Qc)!3Q zw^I@PsXrIpR7I734@T4XnSaFhmoF8>|GeI(q37+RVcQey3pVm*G-oChj1%^;WU`zE z1>@+k_@=81#@V6S^oN3R6o(v0p@QK>*81uE%5IsU#OLK#pUxhh%RfhZ6q%!aVnVUY zbo|oKr(@q-voJAtw)MBa_*xQA^fci%4!cp~=)AL%bTms+I7DIjl3wr2Rg?bjONaPs zqE}eGq~s_pJ(73823M=_R%BRE$AHvZcwxDgUlL7nXOOUz;%wxOs(6=c>BQ)A-~X&+7X5Zpzbl3*;ld7NAGC+Z$HJ25w!-Ywc9o zQ$c#nfP6#2m+Ybq+zo?aZ@lYmU?~Gq(5M$Qi|Y{EcVd~t>54<8y@QS% zmb2~wvo2abpUH>zpL4L+uc>*svpcmaH!oAn;?lr1XEvsvSF{*k=3xEwE$r6@e5=K# zoB8=KG6TCC-Qr9^?;>!a9q;O}0WTGiVTbiXvSzUZbsnBhUy#JN&$&0i`q)Fo0q|`J zdQ*X8ONJ^=o(t@#m25ZFHP)x&^~B2w^9S;I@)2L6R}45KvkM2h>~`)Ln_eqtbL1K} z26`-ne8l$#=n*b`NvP}o=F;Bt;~n4mCw)J+ z@5NEKhn`B*qMxUme-Y!1?o)`9va Date: Thu, 15 Oct 2020 00:15:02 +0200 Subject: [PATCH 150/197] bopessampler added --- .../algorithms/pes_samplers/bopes_sampler.py | 207 ++++++++ .../pes_samplers/energy_surface_spline.py | 109 +++++ .../algorithms/pes_samplers/extrapolator.py | 453 ++++++++++++++++++ .../potentials/harmonic_potential.py | 242 ++++++++++ .../potentials/morse_potential.py | 202 ++++++++ .../pes_samplers/potentials/potential_base.py | 218 +++++++++ 6 files changed, 1431 insertions(+) create mode 100644 qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py create mode 100644 qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py create mode 100644 qiskit/chemistry/algorithms/pes_samplers/extrapolator.py create mode 100644 qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py create mode 100644 qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py create mode 100644 qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py diff --git a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py new file mode 100644 index 0000000000..2e3566e712 --- /dev/null +++ b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- + +# 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. +"""The calculation of points on the Born-Oppenheimer Potential Energy Surface (BOPES).""" + +import logging +from typing import Optional, List, Dict + +import numpy as np +from qiskit.aqua import AquaError +from qiskit.aqua.algorithms import VQAlgorithm +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from qiskit.chemistry.results.bopes_sampler_result import BOPESSamplerResult +from .energy_surface_spline import EnergySurfaceBase +from .extrapolator import Extrapolator, WindowExtrapolator +from .results import EigenstateResult + +logger = logging.getLogger(__name__) + + +class BOPESSampler: + """Class to evaluate the Born-Oppenheimer Potential Energy Surface (BOPES).""" + + def __init__(self, + gsc: GroundStateCalculation, + tolerance: float = 1e-3, + bootstrap: bool = True, + num_bootstrap: Optional[int] = None, + extrapolator: Optional[Extrapolator] = None) -> None: + """ + Args: + gsc: GroundStateCalculation + tolerance: Tolerance desired for minimum energy. + bootstrap: Whether to warm-start the solve of variational minimum eigensolvers. + num_bootstrap: Number of previous points for extrapolation + and bootstrapping. If None and a list of extrapolators is defined, + the first two points will be used for bootstrapping. + If no extrapolator is defined and bootstrap is True, + all previous points will be used for bootstrapping. + extrapolator: Extrapolator objects that define space/window + and method to extrapolate variational parameters. + + Raises: + AquaError: If ``num_boostrap`` is an integer smaller than 2, or + if ``num_boostrap`` is larger than 2 and the extrapolator is not an instance of + ``WindowExtrapolator``. + """ + + self._gsc = gsc + self._tolerance = tolerance + self._bootstrap = bootstrap + self._driver = None # type: Optional[BaseDriver] + self._points = None # type: Optional[List[float]] + self._energies = None # type: Optional[List[float]] + self._raw_results = None # type: Optional[Dict[float, EigenstateResult]] + self._points_optparams = None # type: Optional[Dict[float, List[float]]] + self._num_bootstrap = num_bootstrap + self._extrapolator = extrapolator + + if self._extrapolator: + if num_bootstrap is None: + # set default number of bootstrapping points to 2 + self._num_bootstrap = 2 + elif num_bootstrap >= 2: + if not isinstance(self._extrapolator, WindowExtrapolator): + raise AquaError( + 'If num_bootstrap >= 2 then the extrapolator must be an instance ' + 'of WindowExtrapolator, got {} instead'.format(self._extrapolator)) + self._num_bootstrap = num_bootstrap + self._extrapolator.window = num_bootstrap # window for extrapolator + else: + raise AquaError( + 'num_bootstrap must be None or an integer greater than or equal to 2') + + if isinstance(self._gsc.solver, VQAlgorithm): + # Save initial point passed to min_eigensolver; + # this will be used when NOT bootstrapping + self._initial_point = self._gsc.solver.initial_point + + def sample_surface(self, driver: BaseDriver, points: List[float]) -> BOPESSamplerResult: + """Run the sampler at the given points, potentially with repetitions. + + Args: + driver: BaseDriver specific for the problem. The driver should be based on + a Molecule object that has perturbations to be varied. + points: The points along the degrees of freedom to evaluate. + + Returns: + BOPES Sampler Result + + Raises: + AquaError: if the driver does not have a molecule specified. + """ + + self._driver = driver + + if self._driver.molecule is None: + raise AquaError('Please provide a molecule') + + # full dictionary of points + self._raw_results = self.run_points(points) + # create results dictionary with (point, energy) + self._points = list(self._raw_results.keys()) + self._energies = [] + for key in self._raw_results: + energy = self._raw_results[key]['computed_electronic_energy'] + \ + self._raw_results[key]['nuclear_repulsion_energy'] + self._energies.append(energy) + + result = BOPESSamplerResult(self._points, self._energies, self._raw_results) + + return result + + def run_points(self, points: List[float]) -> Dict[float, EigenstateResult]: + """Run the sampler at the given points. + + Args: + points: the points along the single degree of freedom to evaluate + + Returns: + The results for all points. + """ + raw_results = dict() # type: Dict[float, EigenstateResult] + if isinstance(self._gsc.solver, VQAlgorithm): + self._points_optparams = dict() + self._gsc.solver.initial_point = self._initial_point + + # Iterate over the points + for i, point in enumerate(points): + logger.info('Point %s of %s', i + 1, len(points)) + raw_result = self._run_single_point(point) # dict of results + raw_results[point] = raw_result + + return raw_results + + def _run_single_point(self, point: float) -> EigenstateResult: + """Run the sampler at the given single point + + Args: + point: The value of the degree of freedom to evaluate. + + Returns: + Results for a single point. + """ + + # update molecule geometry and thus resulting Hamiltonian based on specified point + self._driver.molecule.perturbations = [point] + + # find closest previously run point and take optimal parameters + if isinstance(self._gsc.solver, VQAlgorithm) and self._bootstrap: + prev_points = list(self._points_optparams.keys()) + prev_params = list(self._points_optparams.values()) + n_pp = len(prev_points) + + # set number of points to bootstrap + if self._extrapolator is None: + n_boot = len(prev_points) # bootstrap all points + else: + n_boot = self._num_bootstrap + + # Set initial params # if prev_points not empty + if prev_points: + if n_pp <= n_boot: + distances = np.array(point) - \ + np.array(prev_points).reshape(n_pp, -1) + # find min 'distance' from point to previous points + min_index = np.argmin(np.linalg.norm(distances, axis=1)) + # update initial point + self._gsc.solver.initial_point = prev_params[min_index] + else: # extrapolate using saved parameters + opt_params = self._points_optparams + param_sets = self._extrapolator.extrapolate(points=[point], + param_dict=opt_params) + # update initial point, note param_set is a list + # param set is a dictionary + self._gsc.solver.initial_point = param_sets.get(point) + + # the output is an instance of EigenstateResult + result = self._gsc.compute_groundstate(self._driver) + + # Save optimal point to bootstrap + if isinstance(self._gsc.solver, VQAlgorithm): + # at every point evaluation, the optimal params are updated + optimal_params = self._gsc.solver.optimal_params + self._points_optparams[point] = optimal_params + + return result + + def fit_to_surface(self, energy_surface: EnergySurfaceBase, **kwargs) -> None: + """Fit the sampled energy points to the energy surface. + + Args: + energy_surface: An energy surface object. + **kwargs: Arguments to pass through to the potential's ``fit_to_data`` function. + """ + energy_surface.fit_to_data(xdata=self._points, ydata=self._energies, **kwargs) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py new file mode 100644 index 0000000000..cb74705b2e --- /dev/null +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# 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. + +""" +Created on Mon Mar 30 10:30:21 2020 + +@author: dtrenev +""" + +import scipy.interpolate as interp +from scipy.optimize import minimize_scalar + +from .potential_base import EnergySurfaceBase + + +class EnergySurface1DSpline(EnergySurfaceBase): + """ + A simple cubic spline interpolation for the potential energy surface. + """ + + def __init__(self): + """ + Constructor. + Initializes the class with a molecule. + """ + self._eval = None + self.eval_d = None + self.min_x = None + self.min_val = None + + # Implementing the EnergySurfaceBase interface + + def eval(self, x): + """ + After fitting the data to the fit function, predict the energy + at a point x. + """ + assert self._eval is not None + result = self._eval(x) + ''' + # Here we could extrapolate if needed ... + # E.g: + result = np.where(x < 0.0, + self._eval(0.0)+np.exp(4*(0.0-x)), result) + result = np.where(x > 5, + self._eval(5) + 0*x, result) + ''' + return result + + def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None): + ## TODO: remove, no need for duplicate checking + # newx = np.unique(xdata) + # # new y is average of all repeated values + # newy = [np.average(ydata[np.where(xdata == val)[0]]) + # for val in np.unique(xdata)] + newx = xdata + newy = ydata + + tck = interp.splrep(newx, newy, k=3) + + self._eval = lambda x: interp.splev(x, tck) + self.eval_d = lambda x: interp.splev(x, tck, der=1) + + result = minimize_scalar(self._eval) + assert result.success + + self.min_x = result.x + self.min_val = result.fun + self.x_left = min(xdata) + self.x_right = max(xdata) + + def get_equilibrium_geometry(self, scaling=1.0): + """ + Returns the geometry for the minimal energy (scaled by 'scaling') + Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get + meters. + """ + assert self.min_x is not None + return self.min_x * scaling + + def get_minimal_energy(self, scaling=1.0): + """ + Returns the value of the minimal energy (scaled by 'scaling') + Default units (scaling=1.0) are J/mol. Scale appropriately for + Hartrees. + """ + assert self.min_val is not None + return self.min_val * scaling + + def get_trust_region(self): + """ + Returns the bounds of the region (in space) where the energy + surface implementation can be trusted. When doing spline + interpolation, for example, that would be the region where data + is interpolated (vs. extrapolated) from the arguments of + fit_to_data(). + """ + return (self.x_left, self.x_right) diff --git a/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py b/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py new file mode 100644 index 0000000000..93e12bad7a --- /dev/null +++ b/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py @@ -0,0 +1,453 @@ +# -*- coding: utf-8 -*- + +# (C) Copyright IBM 2018, 2020. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================= + +"""An implementation to extrapolate variational parameters.""" + +from abc import ABC, abstractmethod +from typing import Optional, List, Dict, Union + +import numpy as np +from sklearn import linear_model +from sklearn.decomposition import PCA, KernelPCA +from qiskit.aqua import AquaError + + +class Extrapolator(ABC): + """ + This class is based on performing extrapolation of parameters of a wavefunction for a + variational algorithm defined in the variational forms as part of the Qiskit Aqua module. + This concept is based on fitting a set of (point,parameter) data to some specified + function and predicting the optimal variational parameters for the next point. This + technique is aimed towards providing a better starting point for the variational algorithm, + in addition to bootstrapping techniques, ultimately reducing the total number of function + evaluations. + + Each instance of an Extrapolator requires a dictionary where each item consist of a point + (key) and a list of variational parameters (value) for that given point. In practice, a Sampler + Class can utilize the Extrapolator as a wrapper. The Extrapolator class then extracts optimal + variational parameters from the previous points for use in extrapolation. For instance, one can + utilize the Extrapolator to accelerate the computation of the Born-Oppenheimer Potential Energy + Surface (BOPES) for a given molecule. In this case, each point can represent the interatomic + distance and the list of parameters represent rotational parameters in a quantum circuit, + in the context of computing the bond dissociation profile for a diatomic molecule. + NOTE: However this is not a requirement - once an instance of the Extrapolator class is created, + extrapolation can proceed by specifying the point(s) of interest and the dictionary of + (point, parameter) pairs for a problem. + + There are two types of Extrapolators: external/wrapper and internal. + The external/wrapper extrapolator specifies the number of previous points or data window + within which to perform the extrapolation as well as the dimensionality/space to + perform the extrapolation. For instance, one can utilize the PCA Extrapolator as an external + extrapolator that sets the data window and transforms the variational parameters in PCA space + before the actual extrapolation is executed. The internal extrapolator can then proceed via + linear regression/spline fitting of variational parameters to predict a parameter set. + """ + + @abstractmethod + def extrapolate(self, points: List[float], + param_dict: Dict[float, List[float]]) -> Dict[float, List[float]]: + """ + Abstract method to extrapolate point(s) of interest. + + Args: + points: List of point(s) to be used for extrapolation. Can represent + some degree of freedom, ex, interatomic distance. + param_dict: Dictionary of variational parameters. Each key is the point + and the value is a list of the variational parameters. + + Returns: + Dictionary of variational parameters for extrapolated point(s). + """ + raise NotImplementedError() + + @classmethod + def factory(cls, mode: str, **kwargs) -> 'Extrapolator': + """ + Factory method for constructing extrapolators. + + Args: + mode: Extrapolator to instantiate. Can be one of: + - 'window' + - 'poly' + - 'diff_model' + - 'pca' + - 'l1' + kwargs: arguments to be passed to the constructor of an extrapolator + + Returns: + A newly created extrapolator instance. + + Raises: + AquaError: if specified mode is unknown. + """ + if mode == 'window': + return WindowExtrapolator(**kwargs) + elif mode == 'poly': + return PolynomialExtrapolator(**kwargs) + elif mode == 'diff_model': + return DifferentialExtrapolator(**kwargs) + elif mode == 'pca': + return PCAExtrapolator(**kwargs) + elif mode == 'l1': + return SieveExtrapolator(**kwargs) + else: + raise AquaError('No extrapolator called {}'.format(mode)) + + +class PolynomialExtrapolator(Extrapolator): + """ + An extrapolator based on fitting each parameter to a polynomial function of a user-specified + degree. + + WARNING: Should only be used with window. Using no window includes points after + the point being extrapolated in the data window. + """ + + def __init__(self, degree: int = 1) -> None: + """ + Constructor. + + Args: + degree: Degree of polynomial to use for fitting in extrapolation. + """ + + self._degree = degree + + def extrapolate(self, points: List[float], param_dict: Optional[Dict[float, List[float]]]) \ + -> Dict[float, List[float]]: + """ + Extrapolate at specified point of interest given a set of variational parameters. + Extrapolation is based on a polynomial function/spline fitting with a user-specified + degree. + + Args: + points: List of point(s) to be used for extrapolation. Can represent + some degree of freedom, ex, interatomic distance. + param_dict: Dictionary of variational parameters. Each key is the point + and the value is a list of the variational parameters. + + Returns: + Dictionary of variational parameters for extrapolated point(s). + """ + param_arr = np.transpose(list(param_dict.values())) + data_points = list(param_dict.keys()) + + ret_param_arr = [] + for params in param_arr: + # coefficients, _, _, _, _ = np.polyfit(data_points, params, deg=self._degree, full=True) + # todo: generates a warning in the tests: RankWarning: Polyfit may be poorly conditioned + coefficients = np.polyfit(data_points, params, deg=self._degree) + poly = np.poly1d(coefficients) + ret_param_arr += [poly(points)] + ret_param_arr = np.transpose(ret_param_arr).tolist() + ret_params = dict(zip(points, ret_param_arr)) + return ret_params + + +class DifferentialExtrapolator(Extrapolator): + """ + An extrapolator based on treating each param set as a point in space, and fitting a + Hamiltonian which evolves each point to the next. The user specifies the type of regression + model to perform fitting, and a degree which adds derivatives to the values in the point + vector; serving as features for the regression model. + WARNING: Should only be used with window. Using no window includes points after the + point being extrapolated in the data window. + """ + + def __init__(self, + degree: int = 1, + model: Optional[Union[linear_model.LinearRegression, linear_model.Ridge, + linear_model.RidgeCV, linear_model.SGDRegressor]] = None) \ + -> None: + """ + Constructor. + + Args: + model: Regression model (from sklearn) to be used for fitting + variational parameters. Currently supports the following models: + LinearRegression(), Ridge(), RidgeCV(), and SGDRegressor(). + + degree: Specifies (degree -1) derivatives to be added as + 'features' in regression model. + + """ + self._degree = degree + self._model = model or linear_model.LinearRegression() + + def extrapolate(self, points: List[float], param_dict: Optional[Dict[float, List[float]]]) \ + -> Dict[float, List[float]]: + """ + Extrapolate at specified point of interest given a set of variational parameters. + Each parameter list and list of numerical gradients is treated as a single point + in vector space. The regression model tries to fit a Hamiltonian that describes + the evolution from one parameter set (and its gradient features) at point r, + to another parameter set at point, r + epsilon. The regression model is then + used to predict the parameter set at the point of interest. Note that this + extrapolation technique does not explicitly use the spacing of the points + (step size) but rather infers it from the list of parameter values. + + Args: + points: List of point(s) to be used for extrapolation. Can represent + some degree of freedom, ex, interatomic distance. + param_dict: Dictionary of variational parameters. Each key is the point + and the value is a list of the variational parameters. + + Returns: + Dictionary of variational parameters for extrapolated point(s). + """ + response = list(param_dict.values())[1:] + features = [list(param_dict.values())] + for i in range(self._degree - 1): + grad = np.gradient(features[i], axis=0) + features.append(list(grad)) + features = np.concatenate(features, axis=1) + self._model.fit(features[:-1], response) + next_params = np.asarray(self._model.predict([features[-1]])[0].tolist()) + ret_params = {point: next_params for point in points} + return ret_params + + +class WindowExtrapolator(Extrapolator): + """ + An extrapolator which wraps another extrapolator, limiting the internal extrapolator's + ground truth parameter set to a fixed window size. + """ + + def __init__(self, + extrapolator: Union[PolynomialExtrapolator, + DifferentialExtrapolator] = None, + window: int = 2) -> None: + """ + Constructor. + + Args: + extrapolator: 'internal' extrapolator that performs extrapolation on + variational parameters based on data window + + window: Number of previous points to use for extrapolation. A value of zero + indicates that all previous points will be used for bootstrapping. + """ + self._extrapolator = extrapolator + self._window = window + + def extrapolate(self, points: List[float], param_dict: Optional[Dict[float, List[float]]]) \ + -> Dict[float, List[float]]: + """ + Extrapolate at specified point of interest given a set of variational parameters. + Based on the specified window, a subset of the data points will be used for + extrapolation. A default window of 2 points is used, while a value of zero indicates + that all previous points will be used for extrapolation. This method defines the + data window before performing the internal extrapolation. + + Args: + points: List of point(s) to be used for extrapolation. Can represent + some degree of freedom, ex, interatomic distance. + param_dict: Dictionary of variational parameters. Each key is the point + and the value is a list of the variational parameters. + + Returns: + Dictionary of variational parameters for extrapolated point(s). + """ + ret_params = {} + sorted_points = sorted(points) + reference_points = [pt for pt in sorted(param_dict.keys()) if pt < max(sorted_points)] + + for bottom_index, bottom in enumerate(reference_points): + if bottom_index < len(reference_points) - 1: + top = reference_points[bottom_index + 1] + else: + top = float('inf') + extrapolation_group = [pt for pt in sorted_points if bottom < pt <= top] + window_points = [pt for pt in reference_points if pt <= bottom] + if len(window_points) > self._window: + window_points = window_points[-self._window:] + window_param_dict = {pt: param_dict[pt] for pt in window_points} + if extrapolation_group: + ret_params.update(self._extrapolator.extrapolate(extrapolation_group, + param_dict=window_param_dict)) + return ret_params + + @property + def extrapolator(self) -> Extrapolator: + """Returns the internal extrapolator. + + Returns: + The internal extrapolator. + """ + return self._extrapolator + + @extrapolator.setter + def extrapolator(self, extrapolator: Union[PolynomialExtrapolator, + DifferentialExtrapolator]) -> None: + """Sets the internal extrapolator. + + Args: + extrapolator: The internal extrapolator to set. + """ + self._extrapolator = extrapolator + + @property + def window(self) -> int: + """Returns the size of the window. + + Returns: + The size of the window. + """ + return self._window + + @window.setter + def window(self, window: int) -> None: + """Set the size of the window + + Args: + window: the size of the window to set. + """ + self._window = window + + +class PCAExtrapolator(Extrapolator): + """ + A wrapper extrapolator which reduces the points' dimensionality with PCA, + performs extrapolation in the transformed pca space, and inverse transforms the + results before returning. + A user specifies the kernel within how the PCA transformation should be done. + """ + + def __init__(self, + extrapolator: Optional[Union[PolynomialExtrapolator, + DifferentialExtrapolator]] = None, + kernel: Optional[str] = None, + window: int = 2) -> None: + """ + Constructor. + + Args: + extrapolator: 'internal' extrapolator that performs extrapolation on + variational parameters based on data window. + kernel: Kernel (from sklearn) that specifies how dimensionality + reduction should be done for PCA. Default value is None, and switches + the extrapolation to standard PCA. + window: Number of previous points to use for extrapolation. + + Raises: + AquaError: if kernel is not defined in sklearn module. + """ + self._extrapolator = WindowExtrapolator(extrapolator=extrapolator, window=window) + self._kernel = kernel + if self._kernel is None: + self._pca_model = PCA() + elif self._kernel in ['linear', 'poly', 'rbf', 'sigmoid', 'cosine']: + self._pca_model = KernelPCA(kernel=self._kernel, fit_inverse_transform=True) + else: + raise AquaError('PCA kernel type {} not found'.format(self._kernel)) + + def extrapolate(self, points: List[float], param_dict: Optional[Dict[float, List[float]]]) \ + -> Dict[float, List[float]]: + """ + Extrapolate at specified point of interest given a set of variational parameters. + This method transforms the parameters in PCA space before performing the internal + extrapolation. The parameters are transformed back to regular space after extrapolation. + + Args: + points: List of point(s) to be used for extrapolation. Can represent + some degree of freedom, ex, interatomic distance. + param_dict: Dictionary of variational parameters. Each key is the point + and the value is a list of the variational parameters. + + Returns: + Dictionary of variational parameters for extrapolated point(s). + """ + # run pca fitting and extrapolate in pca space + self._pca_model.fit(list(param_dict.values())) + updated_params = {pt: self._pca_model.transform([param_dict[pt]])[0] + for pt in list(param_dict.keys())} + output_params = self._extrapolator.extrapolate(points, param_dict=updated_params) + + ret_params = {point: self._pca_model.inverse_transform(param) if not param else [] + for (point, param) in output_params.items()} + return ret_params + + +class SieveExtrapolator(Extrapolator): + """ + A wrapper extrapolator which clusters the parameter values - either before + extrapolation, after, or both - into two large and small clusters, and sets the + small clusters' parameters to zero. + """ + + def __init__(self, + extrapolator: Optional[Union[PolynomialExtrapolator, + DifferentialExtrapolator]] = None, + window: int = 2, + filter_before: bool = True, + filter_after: bool = True) -> None: + """ + Constructor. + + Args: + extrapolator: 'internal' extrapolator that performs extrapolation on + variational parameters based on data window. + window: Number of previous points to use for extrapolation. + filter_before: Keyword to perform clustering before extrapolation. + filter_after: Keyword to perform clustering after extrapolation. + + """ + self._extrapolator = WindowExtrapolator(extrapolator=extrapolator, window=window) + self._filter_before = filter_before + self._filter_after = filter_after + + def extrapolate(self, points: List[float], param_dict: Optional[Dict[float, List[float]]]) \ + -> Dict[float, List[float]]: + """ + Extrapolate at specified point of interest given a set of variational parameters. + Based on the specified window, a subset of the data points will be used for + extrapolation. A default window of 2 points is used, while a value of zero indicates + that all previous points will be used for extrapolation. This method finds a cutoff distance + based on the maximum average distance or 'gap' between the average values of the variational + parameters. This cutoff distance is used as a criteria to divide the parameters into two + clusters by setting all parameters that are below the cutoff distance to zero. + + Args: + points: List of point(s) to be used for extrapolation. Can represent + some degree of freedom, ex, interatomic distance. + param_dict: Dictionary of variational parameters. Each key is the point + and the value is a list of the variational parameters. + + Returns: + Dictionary of variational parameters for extrapolated point(s). + """ + # determine clustering cutoff + param_arr = np.transpose(list(param_dict.values())) + param_averages = np.array(sorted(np.average(np.log10(np.abs(param_arr)), axis=0))) + gaps = param_averages[1:] - param_averages[:-1] + max_gap = int(np.argmax(gaps)) + sieve_cutoff = 10 ** np.average([param_averages[max_gap], param_averages[max_gap + 1]]) + + if self._filter_before: + filtered_dict = {point: list(map(lambda x: x if np.abs(x) > sieve_cutoff else 0, param)) + for (point, param) in param_dict.items()} + output_params = self._extrapolator.extrapolate(points, param_dict=filtered_dict) + else: + output_params = self._extrapolator.extrapolate(points, param_dict=param_dict) + + if self._filter_after: + ret_params = {point: np.asarray(list(map(lambda x: + x if np.abs(x) > sieve_cutoff else 0, param))) + for (point, param) in output_params.items()} + else: + ret_params = np.asarray(output_params) + return ret_params diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py new file mode 100644 index 0000000000..0967f3cfcd --- /dev/null +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 1D Harmonic potential. It can be used to compute +thermodynamic properties of diatomic molecules (e.g. H2, HD, D2) via the +diatomic partition function and the thermodynamics module. +""" +import numpy as np +from scipy.optimize import curve_fit + +import qiskit.chemistry.constants as const +from .potential_base import PotentialBase + + +class HarmonicPotential(PotentialBase): + """ + Implements a 1D Harmonic potential. + Input units are Angstroms (distance between the two atoms), + and output units are Hartrees (molecular energy). + """ + # Works in Angstroms (input) and Hartrees (output) + + def __init__(self, molecule): + """ + Constructor. + Initializes the potential to the zero-function. + fit_to_data() should be used afterwards to fit the potential to + computed molecular energies. + + Args: + molecule: the underlying molecule. + + Raises: + ValueError: Only implemented for diatomic molecules + """ + super().__init__(molecule) + # Initialize with zero-potential. + # Later - fit energy values (fit_to_data) + self.k = 0.0 + self.m_shift = 0.0 + self.r_0 = 0.0 + self.d_e = None + self._mA = molecule.masses[0] + self._mB = molecule.masses[1] + + @staticmethod + def fit_function(x, k, r_0, m_shift): + """ + Functional form of the potential. + """ + # K (Hartree/(Ang^2)), r_0 (Angstrom), m_shift (Hartree) + return k / 2 * (x - r_0) ** 2 + m_shift + + def eval(self, x): + """ + Evaluates the potential at a given point. + """ + return self.fit_function(x, self.k, self.r_0, self.m_shift) + + def update_molecule(self, molecule): + """ + Updates the underlying molecule. + + Raises: + ValueError: Only implemented for diatomic molecules + """ + # Check the provided molecule + if len(molecule.masses) != 2: + raise ValueError( + 'Harmonic potential only works for diatomic molecules!') + self._mA = molecule.masses[0] + self._mB = molecule.masses[1] + + def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None, + preprocess_data=True): + """ + Fits a potential to computed molecular energies. + + Args: + xdata: interatomic distance points (Angstroms) + ydata: molecular energies (Hartrees) + initial_vals: Initial values for fit parameters. None for default. + Order of parameters is k, r_0 and m_shift + (see fit_function implementation) + bounds_list: Bounds for the fit parameters. None for default. + Order of parameters is k, r_0 and m_shift + (see fit_function implementation) + preprocess_data: Default True. Internally cleans the data so + that a fit is done only around the minimum. The wider range of + distances and energies provided is still taken into account in + estimating a dissociation energy for the molecule. + """ + # Fits the potential to given x/y data. + # If preProcessData is True (i.e. by default!) it only tries to fit + # for values around the x where the minimum y was obtained. + + # do the Harmonic potential fit here, the order of parameters is + # [k (Hartrees/(Ang**2)), r_0 (Ang), energy_shift (Hartrees)] + h_p0 = (initial_vals if initial_vals is not None + else np.array([0.2, 0.735, 1.5])) + h_bounds = (bounds_list if bounds_list is not None + else ([0, -1, -2], [2, 3.0, 2])) + + xdata_fit = xdata + ydata_fit = ydata + if preprocess_data: + xdata_fit, ydata_fit = HarmonicPotential.process_fit_data( + xdata, ydata) + + fit, _ = curve_fit(self.fit_function, xdata_fit, ydata_fit, + p0=h_p0, maxfev=100000, bounds=h_bounds) + + self.k = fit[0] + self.r_0 = fit[1] + self.m_shift = fit[2] + + # Crude approximation of dissociation energy (assuming a reasonable + # range of values were given) + self.d_e = max(ydata) - min(ydata) + + # return + + def get_equilibrium_geometry(self, scaling=1.0): + """ + Returns the interatomic distance corresponding to minimal energy. + Args: + scaling: Scaling to change units. (Default is 1.0 for Angstroms) + """ + # the returned value(s) is defined by the molecule's degrees of + # freedom? + + # Returns the distance for the minimal energy (scaled by 'scaling') + # Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get + # meters. + return self.r_0 * scaling + + def get_minimal_energy(self, scaling=1.0): + """ + Returns the smallest molecular energy for the current fit. + Args: + scaling: Scaling to change units. (Default is 1.0 for Hartrees) + """ + # Returns the distance for the minimal energy (scaled by 'scaling') + # Default units (scaling=1.0) are Hartrees. Scale appropriately for + # Joules (per molecule or mol). + return self.m_shift * scaling + + def dissociation_energy(self, scaling=1.0): + """ + Returns the estimated dissociation energy for the current fit. + Args: + scaling: Scaling to change units. (Default is 1.0 for Hartrees) + """ + # Hartree/(Ang**2) to Hartree!! 1/Ang**2 -> 1/m**2 + k = self.k * 1E20 + if self.d_e is not None: + k = self.d_e + + diss_nrg = k - self.vibrational_energy_level(0) + + # in Hartree + return diss_nrg * scaling + + def fundamental_frequency(self): + """ + Returns the fundamental frequency for the current fit (in s^-1). + """ + # Hartree(J)/(Ang**2), need Joules per molecule!! 1/Ang**2 -> 1/m**2 + k = self.k * const.HARTREE_TO_J * 1E20 + # r0 = self.r_0*1E-10 # angstrom, need meter + mr = (self._mA * self._mB) / (self._mA + self._mB) + + # omega_0 in units rad/s converted to 1/s by dividing by 2Pi + omega_0 = (np.sqrt(k / mr)) / (2 * np.pi) + + # fundamental frequency in s**-1 + return omega_0 + + def wave_number(self): + """ + Returns the wave number for the current fit (in cm^-1). + """ + return self.fundamental_frequency() / const.C_CM_PER_S + + def vibrational_energy_level(self, n): + """ + Returns the n-th vibrational energy level for the current fit + (in Hartrees). + """ + omega_0 = self.fundamental_frequency() + e_n = const.H_J_S * omega_0 * (n + 0.5) + + # energy level + return e_n * const.J_TO_HARTREE + + @classmethod + def process_fit_data(cls, xdata, ydata): + """ + Mostly for internal use. Preprocesses the data passed to fit_to_data() + so that only the points around the minimum are fit (which gives + more accurate vibrational modes). + """ + sort_ind = np.argsort(xdata) + ydata_s = ydata[sort_ind] + xdata_s = xdata[sort_ind] + min_y = min(ydata_s) + + # array of indices for X for which Y is min + x_min = np.where(ydata_s == min_y)[0] + + # array of indices where X is equal to the value for which Y + # is minimum + all_of_min = np.array([], dtype=int) + for i in x_min: + all_of_min = np.concatenate( + (all_of_min, np.where(xdata_s == xdata_s[i])[0])) + # array of indices where X is equal to the next smaller value + left_of_min = [] + if min(all_of_min) > 0: + left_of_min = np.where( + xdata_s == xdata_s[min(all_of_min) - 1])[0] + # array of indices where X is equal to the next bigger value + right_of_min = [] + if max(all_of_min) < (xdata_s.size - 1): + right_of_min = np.where( + xdata_s == xdata_s[max(all_of_min) + 1])[0] + # all those indices together are used for fitting a harmonic + # potential (around the min) + inds = np.concatenate((left_of_min, all_of_min, right_of_min)) + + return xdata_s[inds], ydata_s[inds] diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py new file mode 100644 index 0000000000..a2183c9a11 --- /dev/null +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 1D Morse potential. It can be used to compute +thermodynamic properties of diatomic molecules (e.g. H2, HD, D2) via the +diatomic partition function and the thermodynamics module. +""" +import numpy as np +from scipy.optimize import curve_fit + +from .potential_base import PotentialBase +import qiskit.chemistry.constants as const + + +class MorsePotential(PotentialBase): + """ + Implements a 1D Morse potential. + Input units are Angstroms (distance between the two atoms), + and output units are Hartrees (molecular energy). + """ + # Works in Angstroms and Hartrees + + def __init__(self, molecule): + """ + Constructor. + Initializes the potential to the zero-function. + fit_to_data() should be used afterwards to fit the potential to + computed molecular energies. + + Args: + molecule: the underlying molecule. + + Raises: + ValueError: Only implemented for diatomic molecules + """ + super().__init__(molecule) + # Initialize with zero-potential. + # Later - fit energy values (fit_to_data) + self.d_e = 0.0 + self.m_shift = 0.0 + self.alpha = 0.0 + self.r_0 = 0.0 + self._mA = molecule.masses[0] + self._mB = molecule.masses[1] + + @staticmethod + def fit_function(x, d_e, alpha, r_0, m_shift): + """ + Functional form of the potential. + """ + # d_e (Hartree), alpha (1/Angstrom), r_0 (Angstrom) + return d_e * (1 - np.exp(-alpha * (x - r_0))) ** 2 + m_shift + + def eval(self, x): + """ + Evaluates the potential at a given point. + """ + # Expects Angstroms returns Hartrees + return self.fit_function(x, self.d_e, + self.alpha, self.r_0, self.m_shift) + + def update_molecule(self, molecule): + """ + Updates the underlying molecule. + + Raises: + ValueError: Only implemented for diatomic molecules + """ + # Check the provided molecule + if len(molecule.masses) != 2: + raise ValueError( + 'Morse potential only works for diatomic molecules!') + self._mA = molecule.masses[0] + self._mB = molecule.masses[1] + + def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None): + """ + Fits a potential to computed molecular energies. + + Args: + xdata: interatomic distance points (Angstroms) + ydata: molecular energies (Hartrees) + initial_vals: Initial values for fit parameters. None for default. + Order of parameters is d_e, alpha, r_0 and m_shift + (see fit_function implementation) + bounds_list: Bounds for the fit parameters. None for default. + Order of parameters is d_e, alpha, r_0 and m_shift + (see fit_function implementation) + """ + + # do the Morse potential fit + # here, the order of parameters is + # [d_e (Hartree), alpha (1/ang), r_0 (ang), energy_shift (Hartree)] + m_p0 = (initial_vals if initial_vals is not None + else np.array([0.25, 2, 0.735, 1.5])) + m_bounds = (bounds_list if bounds_list is not None + else ([0, 0, 0.3, -5], + [2.5, np.inf, 1.0, 5])) + + fit, _ = curve_fit(self.fit_function, xdata, ydata, + p0=m_p0, maxfev=100000, bounds=m_bounds) + + self.d_e = fit[0] + self.alpha = fit[1] + self.r_0 = fit[2] + self.m_shift = fit[3] + + def get_equilibrium_geometry(self, scaling=1.0): + """ + Returns the interatomic distance corresponding to minimal energy. + Args: + scaling: Scaling to change units. (Default is 1.0 for Angstroms) + """ + # TODO: Should this be moved to Molecule, given that the meaning of + # the returned value(s) is defined by the molecule's degrees of + # freedom? + + # Returns the distance for the minimal energy (scaled by 'scaling') + # Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get + # meters. + return self.r_0 * scaling + + def get_minimal_energy(self, scaling=1.0): + """ + Returns the smallest molecular energy for the current fit. + Args: + scaling: Scaling to change units. (Default is 1.0 for Hartrees) + """ + # Returns the distance for the minimal energy (scaled by 'scaling'') + # Default units (scaling=1.0) are Hartrees. Scale appropriately for + # Joules (per mol). + return self.m_shift * scaling + + def dissociation_energy(self, scaling=1.0): + """ + Returns the calculated dissociation energy for the current fit. + Args: + scaling: Scaling to change units. (Default is 1.0 for Hartrees) + """ + # Returns the dissociation energy (scaled by 'scaling'). + # Default units (scaling=1.0) are Hartrees. Scale appropriately for + # Joules (per mol). + de = self.d_e + diss_nrg = de - self.vibrational_energy_level(0) + + return diss_nrg * scaling + + def fundamental_frequency(self): + """ + Returns the fundamental frequency for the current fit (in s^-1). + """ + de = self.d_e * const.HARTREE_TO_J # Hartree, need J/molecule + alp = self.alpha * 1E10 # 1/angstrom, need 1/meter + # r0 = self.r_0*1E-10 # angstrom, need meter + mr = (self._mA * self._mB) / (self._mA + self._mB) + + # omega_0 in units rad/s converted to 1/s by dividing by 2Pi + omega_0 = (np.sqrt((2 * de * alp ** 2) / mr)) / (2 * np.pi) + + # fundamental frequency in s**-1 + return omega_0 + + def wave_number(self): + """ + Returns the wave number for the current fit (in cm^-1). + """ + return self.fundamental_frequency() / const.C_CM_PER_S + + def vibrational_energy_level(self, n): + """ + Returns the n-th vibrational energy level for the current fit + (in Hartrees). + """ + de = self.d_e * const.HARTREE_TO_J # Hartree, need J/molecule + + omega_0 = self.fundamental_frequency() + e_n = const.H_J_S * omega_0 * (n + 0.5) - \ + ((const.H_J_S * omega_0 * (n + 0.5)) ** 2) / (4 * de) + + # energy level + return e_n * const.J_TO_HARTREE + + def get_maximum_trusted_level(self, mode=0): + # For the formula below, see + # "Partition Functions", by Popovas A., et.al + # Astronomy & Astrophysics, Vol. 595, November 2016 + # https://doi.org/10.1051/0004-6361/201527209 + return np.floor( + 2 * self.dissociation_energy(const.HARTREE_TO_J) / + (const.H_J_S * self.fundamental_frequency()) + ) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py new file mode 100644 index 0000000000..6547942766 --- /dev/null +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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 declares interfaces for implementing potential energy surface +and vibrational structure of a given molecule. +""" +from abc import ABC, abstractmethod + + +class EnergySurfaceBase(ABC): + """ Class to hold a potential energy surface """ + + def __init__(self, molecule): + pass + # self.update_molecule(molecule) + + # @abstractmethod + # def update_molecule(self, molecule): + # """ + # Wipe state if molecule changes, and check validity of molecule + # for potential. + # """ + + @abstractmethod + def eval(self, x): + """ + After fitting the data to the fit function, predict the energy + at a point x. + """ + raise NotImplementedError + + @abstractmethod + def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None): + """ Fit the surface to data - x are coordinates, y is energy.""" + raise NotImplementedError + + @abstractmethod + def get_equilibrium_geometry(self, scaling=1.0): + """ + Returns the geometry for the minimal energy (scaled by 'scaling') + Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get + meters. + """ + raise NotImplementedError + + @abstractmethod + def get_minimal_energy(self, scaling=1.0): + """ + Returns the value of the minimal energy (scaled by 'scaling') + Default units (scaling=1.0) are J/mol. Scale appropriately for + Hartrees. + """ + raise NotImplementedError + + @abstractmethod + def get_trust_region(self): + """ + Returns the bounds of the region (in space) where the energy + surface implementation can be trusted. When doing spline + interpolation, for example, that would be the region where data + is interpolated (vs. extrapolated) from the arguments of + fit_to_data(). + """ + raise NotImplementedError + + +class VibronicStructureBase(ABC): + """ + Class to hold a molecular vibronic structure providing access to + vibrational modes and energy levels + """ + + def __init__(self, molecule): + self.update_molecule(molecule) + + def update_molecule(self, molecule): + """ + Wipe state if molecule changes, and check validity of molecule + for potential. + """ + self.molecule = molecule + + @abstractmethod + def get_num_modes(self): + """ returns the number of vibrational modes for the molecule """ + raise NotImplementedError + + @abstractmethod + def vibrational_energy_level(self, n, mode=0): + """ returns the n-th vibrational energy level for a given mode """ + raise NotImplementedError + + def get_maximum_trusted_level(self, mode=0): + """ + Returns the maximum energy level for which the particular + implementation still provides a good approximation of reality. + Default value of 100. Redefined where needed (see e.g. Morse). + """ + return 100 + + ''' + # TODO: Do we need these? Does every vibrational mode have a fundamental + # frequency (e.g. when it is anharmonic)? + + @abstractmethod + def fundamental_frequency(self, mode=0): + """ returns the fundamental frequency for a given mode """ + raise NotImplementedError + + def wave_number(self, mode=0): + """ returns the wave number for a given mode """ + return self.fundamental_frequency(mode) / C_CM_PER_S + ''' + + +class PotentialBase(EnergySurfaceBase, VibronicStructureBase): + """ + Class to hold prescribed 1D potentials (e.g. Morse/Harmonic) + over a degree of freedom. + """ + + def get_num_modes(self): + """ This (1D) potential represents a single vibrational mode """ + return 1 + + def get_trust_region(self): + """ + The potential will usually be well-defined (even if not useful) for + arbitrary x so we return a fairly large interval here. + Redefine in derived classes if needed. + """ + return (-100, 100) + + @abstractmethod + def dissociation_energy(self, scaling=1.0): + """ returns the dissociation energy (scaled by 'scaling')""" + raise NotImplementedError + + +''' +class Potential1D(PotentialBase): + def __init__(self, molecule, energy_surface, vibronic_struct): + self.energy_surface = energy_surface + self.vibronic_structure = vibronic_struct + self.update_molecule(molecule) + + def update_molecule(self, molecule): + """ + Wipe state if molecule changes, and check validity of molecule + for potential. + """ + self.molecule = molecule + self.energy_surface.update_molecule(molecule) + self.vibronic_structure.update_molecule(molecule) + + def eval(self, x): + """ + After fitting the data to the fit function, predict the energy + at a point x. + """ + return self.energy_surface.eval(x) + + def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None): + """ Fit the surface to data - x are coordinates, y is energy.""" + return self.energy_surface.fit_to_data(xdata, ydata, initial_vals, + bounds_list) + + def get_equilibrium_geometry(self, scaling=1.0): + """ + Returns the geometry for the minimal energy (scaled by 'scaling') + Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get + meters. + """ + return self.energy_surface.get_equilibrium_geometry(scaling) + + def get_minimal_energy(self, scaling=1.0): + """ + Returns the value of the minimal energy (scaled by 'scaling') + Default units (scaling=1.0) are J/mol. Scale appropriately for + Hartrees. + """ + return self.energy_surface.get_minimal_energy(scaling) + + def get_num_modes(self): + """ returns the number of vibrational modes for the molecule """ + return self.vibronic_structure.get_num_modes() + + def fundamental_frequency(self, mode=0): + """ returns the fundamental frequency for a given mode """ + return self.vibronic_structure.fundamental_frequency(mode) + + def wave_number(self, mode=0): + """ returns the wave number for a given mode """ + return self.fundamental_frequency(mode) / C_CM_PER_S + + def vibrational_energy_level(self, n, mode=0): + """ returns the n-th vibrational energy level for a given mode """ + return self.vibronic_structure.vibrational_energy_level(n, mode) + + def get_maximum_trusted_level(self, mode = 0): + """ returns the dissociation energy (scaled by 'scaling')""" + return self.vibronic_structure.get_maximum_trusted_level(0) + + def dissociation_energy(self, scaling=1.0): + """ returns the dissociation energy (scaled by 'scaling')""" + return (self.eval(5) - self.get_minimal_energy() - self.vibrational_energy_level(0)) * scaling +''' From 41b1892d4926911a006aab26f88a5acaf924b6ae Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 14 Oct 2020 18:21:37 -0400 Subject: [PATCH 151/197] fix mypy --- .../ground_state_solvers/__init__.py | 2 -- .../ground_state_solvers/adapt_vqe.py | 3 ++- .../ground_state_eigensolver.py | 3 ++- .../ground_state_solver.py | 3 ++- .../vqe_uccsd_factory.py | 3 ++- qiskit/chemistry/results/__init__.py | 2 ++ .../results/vibronic_structure_result.py | 24 +++++++++++++++++++ 7 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 qiskit/chemistry/results/vibronic_structure_result.py diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py b/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py index 53a85df7e4..9217167746 100644 --- a/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py @@ -14,13 +14,11 @@ from .ground_state_solver import GroundStateSolver from .adapt_vqe import AdaptVQE -from .oovqe import OOVQE from .ground_state_eigensolver import GroundStateEigensolver from .minimum_eigensolver_factories import MinimumEigensolverFactory, VQEUCCSDFactory __all__ = ['GroundStateSolver', 'AdaptVQE', - 'OOVQE', '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 index 8dde351c28..41cf274627 100644 --- a/qiskit/chemistry/algorithms/ground_state_solvers/adapt_vqe.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/adapt_vqe.py @@ -22,6 +22,7 @@ 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 @@ -134,7 +135,7 @@ def solve(self, driver: BaseDriver, aux_operators: Optional[Union[List[FermionicOperator], List[BosonicOperator]]] = None) \ - -> Union[ElectronicStructureResult, 'VibronicStructureResult']: + -> Union[ElectronicStructureResult, VibronicStructureResult]: """Computes the ground state. Args: diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py index 516c5b3bc0..3b7d5dfcfa 100644 --- a/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py @@ -27,6 +27,7 @@ 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 @@ -64,7 +65,7 @@ def solve(self, driver: BaseDriver, aux_operators: Optional[Union[List[FermionicOperator], List[BosonicOperator]]] = None) \ - -> Union[ElectronicStructureResult, 'VibronicStructureResult']: + -> Union[ElectronicStructureResult, VibronicStructureResult]: """Compute Ground State properties. Args: diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py index 6206e01f87..bde680ac37 100644 --- a/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py @@ -26,6 +26,7 @@ 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 @@ -54,7 +55,7 @@ def solve(self, driver: BaseDriver, aux_operators: Optional[Union[List[FermionicOperator], List[BosonicOperator]]] = None) \ - -> Union[ElectronicStructureResult, 'VibronicStructureResult']: + -> Union[ElectronicStructureResult, VibronicStructureResult]: """Compute the ground state energy of the molecule that was supplied via the driver. Args: 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 index 58431a29e0..96cab2c7da 100644 --- 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 @@ -111,7 +111,8 @@ 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, transformation: FermionicTransformation) -> MinimumEigensolver: + 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``. diff --git a/qiskit/chemistry/results/__init__.py b/qiskit/chemistry/results/__init__.py index cf0197e58c..f7661d5b73 100644 --- a/qiskit/chemistry/results/__init__.py +++ b/qiskit/chemistry/results/__init__.py @@ -13,10 +13,12 @@ """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/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 From ab324a73359ef39861298d67b13f0920591676e0 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 00:30:13 +0200 Subject: [PATCH 152/197] test bopes and potentials --- .../ground_state_solvers/__init__.py | 2 - .../algorithms/pes_samplers/bopes_sampler.py | 10 +- .../pes_samplers/energy_surface_spline.py | 2 +- test/chemistry/test_bopes_sampler.py | 138 ++++++++++ test/chemistry/test_data_potentials.py | 36 +++ test/chemistry/test_data_potentials.py~ | 36 +++ test/chemistry/test_fd_vibronic.py | 64 +++++ ...est_fermionic_transformation_symmetries.py | 240 ++++++++++++++++++ test/chemistry/test_oovqe2.py~ | 149 +++++++++++ test/chemistry/test_partition_function.py | 76 ++++++ test/chemistry/test_potential.py | 162 ++++++++++++ test/chemistry/test_thermodynamics.py | 109 ++++++++ 12 files changed, 1016 insertions(+), 8 deletions(-) create mode 100644 test/chemistry/test_bopes_sampler.py create mode 100644 test/chemistry/test_data_potentials.py create mode 100644 test/chemistry/test_data_potentials.py~ create mode 100644 test/chemistry/test_fd_vibronic.py create mode 100644 test/chemistry/test_fermionic_transformation_symmetries.py create mode 100644 test/chemistry/test_oovqe2.py~ create mode 100644 test/chemistry/test_partition_function.py create mode 100644 test/chemistry/test_potential.py create mode 100644 test/chemistry/test_thermodynamics.py diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py b/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py index 53a85df7e4..9217167746 100644 --- a/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py +++ b/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py @@ -14,13 +14,11 @@ from .ground_state_solver import GroundStateSolver from .adapt_vqe import AdaptVQE -from .oovqe import OOVQE from .ground_state_eigensolver import GroundStateEigensolver from .minimum_eigensolver_factories import MinimumEigensolverFactory, VQEUCCSDFactory __all__ = ['GroundStateSolver', 'AdaptVQE', - 'OOVQE', 'GroundStateEigensolver', 'MinimumEigensolverFactory', 'VQEUCCSDFactory' diff --git a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py index 2e3566e712..daa7af7089 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py +++ b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py @@ -20,11 +20,11 @@ from qiskit.aqua import AquaError from qiskit.aqua.algorithms import VQAlgorithm from qiskit.chemistry.drivers import BaseDriver -from qiskit.chemistry.ground_state_calculation import GroundStateCalculation +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateSolver from qiskit.chemistry.results.bopes_sampler_result import BOPESSamplerResult from .energy_surface_spline import EnergySurfaceBase from .extrapolator import Extrapolator, WindowExtrapolator -from .results import EigenstateResult +from qiskit.chemistry.results import EigenstateResult logger = logging.getLogger(__name__) @@ -33,14 +33,14 @@ class BOPESSampler: """Class to evaluate the Born-Oppenheimer Potential Energy Surface (BOPES).""" def __init__(self, - gsc: GroundStateCalculation, + gsc: GroundStateSolver, tolerance: float = 1e-3, bootstrap: bool = True, num_bootstrap: Optional[int] = None, extrapolator: Optional[Extrapolator] = None) -> None: """ Args: - gsc: GroundStateCalculation + gsc: GroundStateSolver tolerance: Tolerance desired for minimum energy. bootstrap: Whether to warm-start the solve of variational minimum eigensolvers. num_bootstrap: Number of previous points for extrapolation @@ -187,7 +187,7 @@ def _run_single_point(self, point: float) -> EigenstateResult: self._gsc.solver.initial_point = param_sets.get(point) # the output is an instance of EigenstateResult - result = self._gsc.compute_groundstate(self._driver) + result = self._gsc.solve(self._driver) # Save optimal point to bootstrap if isinstance(self._gsc.solver, VQAlgorithm): diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index cb74705b2e..db0ef18a96 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -21,7 +21,7 @@ import scipy.interpolate as interp from scipy.optimize import minimize_scalar -from .potential_base import EnergySurfaceBase +from .potentials.potential_base import EnergySurfaceBase class EnergySurface1DSpline(EnergySurfaceBase): diff --git a/test/chemistry/test_bopes_sampler.py b/test/chemistry/test_bopes_sampler.py new file mode 100644 index 0000000000..84d1e26f84 --- /dev/null +++ b/test/chemistry/test_bopes_sampler.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- + +# 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. + +"""Tests of BOPES Sampler.""" + +import unittest +from functools import partial + +import numpy as np +from qiskit import BasicAer +from qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import VQE, NumPyMinimumEigensolver +from qiskit.aqua.components.optimizers import AQGD +from qiskit.aqua.operators import PauliExpectation +from qiskit.chemistry.algorithms.pes_samplers.bopes_sampler import BOPESSampler +from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.chemistry.drivers import Molecule, PySCFDriver +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver +from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential +from qiskit.chemistry.transformations import FermionicTransformation +from qiskit.circuit.library import RealAmplitudes + + +class TestBOPES(unittest.TestCase): + """Tests of BOPES Sampler.""" + + def test_h2_bopes_sampler(self): + """Test BOPES Sampler on H2""" + np.random.seed(100) + + # Molecule + dof = partial(Molecule.absolute_distance, atom_pair=(1, 0)) + m = Molecule(geometry=[['H', [0., 0., 1.]], + ['H', [0., 0.45, 1.]]], + degrees_of_freedom=[dof]) + + f_t = FermionicTransformation() + driver = PySCFDriver(molecule=m) + + qubitop, _ = f_t.transform(driver) + + # Quantum Instance: + shots = 1 + backend = 'statevector_simulator' + quantum_instance = QuantumInstance(BasicAer.get_backend(backend), shots=shots) + quantum_instance.run_config.seed_simulator = 50 + quantum_instance.compile_config['seed_transpiler'] = 50 + + # Variational form + i_state = HartreeFock(num_orbitals=f_t._molecule_info['num_orbitals'], + qubit_mapping=f_t._qubit_mapping, + two_qubit_reduction=f_t._two_qubit_reduction, + num_particles=f_t._molecule_info['num_particles'], + sq_list=f_t._molecule_info['z2_symmetries'].sq_list + ) + var_form = RealAmplitudes(qubitop.num_qubits, reps=1, entanglement='full', + initial_state=i_state, skip_unentangled_qubits=False) + + # Classical optimizer: + # Analytic Quantum Gradient Descent (AQGD) (with Epochs) + aqgd_max_iter = [10] + [1] * 100 + aqgd_eta = [1e0] + [1.0 / k for k in range(1, 101)] + aqgd_momentum = [0.5] + [0.5] * 100 + optimizer = AQGD(maxiter=aqgd_max_iter, + eta=aqgd_eta, + momentum=aqgd_momentum, + tol=1e-6, + averaging=4) + + # Min Eigensolver: VQE + solver = VQE(var_form=var_form, + optimizer=optimizer, + quantum_instance=quantum_instance, + expectation=PauliExpectation()) + + me_gsc = GroundStateEigensolver(f_t, solver) + + # BOPES sampler + sampler = BOPESSampler(gsc=me_gsc) + + # absolute internuclear distance in Angstrom + points = [0.7, 1.0, 1.3] + results = sampler.sample_surface(driver, points) + + points_run = results.points + energies = results.energies + + np.testing.assert_array_almost_equal(points_run, [0.7, 1.0, 1.3]) + np.testing.assert_array_almost_equal(energies, + [-1.13618945, -1.10115033, -1.03518627], decimal=3) + + def test_potential_interface(self): + """Tests potential interface.""" + np.random.seed(100) + + stretch = partial(Molecule.absolute_distance, atom_pair=(1, 0)) + # H-H molecule near equilibrium geometry + m = Molecule(geometry=[['H', [0., 0., 0.]], + ['H', [1., 0., 0.]], + ], + degrees_of_freedom=[stretch], + masses=[1.6735328E-27, 1.6735328E-27]) + + f_t = FermionicTransformation() + driver = PySCFDriver(molecule=m) + + f_t.transform(driver) + + solver = NumPyMinimumEigensolver() + + me_gsc = GroundStateEigensolver(f_t, solver) + # Run BOPESSampler with exact eigensolution + points = np.arange(0.45, 5.3, 0.3) + sampler = BOPESSampler(gsc=me_gsc) + + sampler.sample_surface(driver, points) + + # Testing Potential interface + pot = MorsePotential(m) + sampler.fit_to_surface(pot) + + np.testing.assert_array_almost_equal([pot.alpha, pot.r_0], [2.235, 0.720], decimal=3) + np.testing.assert_array_almost_equal([pot.d_e, pot.m_shift], [0.2107, -1.1419], decimal=3) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/chemistry/test_data_potentials.py b/test/chemistry/test_data_potentials.py new file mode 100644 index 0000000000..7e6b4387be --- /dev/null +++ b/test/chemistry/test_data_potentials.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import numpy as np + +from qiskit.chemistry.constants import HARTREE_TO_J_PER_MOL + +_xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, + 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, + 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, + 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, + 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, + 5.25]) + +_ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, + -2611323.75276296, -2502198.92978322, -2417457.48952287, + -2390778.71123391, -2379482.70907613, -2373850.72354504, + -2361426.93801724, -2369992.6305902, -2363833.07716161, + -2360577.93019891, -2356002.65576262, -2355574.41051646, + -2357254.94032554, -2351656.71871981, -2308055.75509618, + -2797576.98597419, -2715367.76135088, -2616523.58105343, + -2498053.2658529, -2424288.88205414, -2393385.83237565, + -2371800.12956182, -2353202.82294735, -2346873.32092711, + -2343485.8487826, -2342937.74947792, -2350276.02096954, + -2347674.75469199, -2346912.78218669, -2339886.28877723, + -2353456.10489755, -2359599.85281831, -2811321.68662548, + -2763866.98837641, -2613385.92519959, -2506804.00364042, + -2419329.49702063, -2393428.68052976, -2374166.67617163, + -2352961.35574553, -2344972.64297329, -2356294.5588125, + -2341396.63369969, -2337344.83138146, -2339793.71365995, + -2335667.95101689, -2327347.45385524, -2341367.28061372]) + +ydata_hartree = _ydata / HARTREE_TO_J_PER_MOL +ydata_j_per_mol = _ydata + +xdata_angstrom = _xdata diff --git a/test/chemistry/test_data_potentials.py~ b/test/chemistry/test_data_potentials.py~ new file mode 100644 index 0000000000..7e6b4387be --- /dev/null +++ b/test/chemistry/test_data_potentials.py~ @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import numpy as np + +from qiskit.chemistry.constants import HARTREE_TO_J_PER_MOL + +_xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, + 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, + 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, + 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, + 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, + 5.25]) + +_ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, + -2611323.75276296, -2502198.92978322, -2417457.48952287, + -2390778.71123391, -2379482.70907613, -2373850.72354504, + -2361426.93801724, -2369992.6305902, -2363833.07716161, + -2360577.93019891, -2356002.65576262, -2355574.41051646, + -2357254.94032554, -2351656.71871981, -2308055.75509618, + -2797576.98597419, -2715367.76135088, -2616523.58105343, + -2498053.2658529, -2424288.88205414, -2393385.83237565, + -2371800.12956182, -2353202.82294735, -2346873.32092711, + -2343485.8487826, -2342937.74947792, -2350276.02096954, + -2347674.75469199, -2346912.78218669, -2339886.28877723, + -2353456.10489755, -2359599.85281831, -2811321.68662548, + -2763866.98837641, -2613385.92519959, -2506804.00364042, + -2419329.49702063, -2393428.68052976, -2374166.67617163, + -2352961.35574553, -2344972.64297329, -2356294.5588125, + -2341396.63369969, -2337344.83138146, -2339793.71365995, + -2335667.95101689, -2327347.45385524, -2341367.28061372]) + +ydata_hartree = _ydata / HARTREE_TO_J_PER_MOL +ydata_j_per_mol = _ydata + +xdata_angstrom = _xdata diff --git a/test/chemistry/test_fd_vibronic.py b/test/chemistry/test_fd_vibronic.py new file mode 100644 index 0000000000..74bad5b36a --- /dev/null +++ b/test/chemistry/test_fd_vibronic.py @@ -0,0 +1,64 @@ +import unittest + +import numpy as np + +import chemistry.code.test.test_data as td + +from functools import partial + +from chemistry.code.morse_potential import MorsePotential +from chemistry.code.harmonic_potential import HarmonicPotential +from chemistry.code.molecule import Molecule +from chemistry.code.vibronic_structure_fd import VibronicStructure1DFD + +# TODO Fix this test + +class TestFDVibronic(unittest.TestCase): + def create_test_molecule(self): + stretch = partial(Molecule.absolute_stretching, + kwargs={'atom_pair': (1, 0)}) + m = Molecule(geometry=[['H', [0., 0., 0.]], ['D', [0., 0., 1.]]], + degrees_of_freedom=[stretch], + masses=[1.6735328E-27, 3.444946E-27], + spins=[1 / 2, 1]) + return m + + def test_with_morse(self): + m = self.create_test_molecule() + + M = MorsePotential(m) + + xdata = np.array(td.xdata_angstrom) + ydata = np.array(td.ydata_hartree) + + M.fit_to_data(xdata, ydata) + + VS = VibronicStructure1DFD(m, M) + + N = np.array(range(2,8)) + vib_levels = VS.vibrational_energy_level(N) + vib_levels_ref = M.vibrational_energy_level(N) + np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, + decimal=5) + + def test_with_harmonic(self): + m = self.create_test_molecule() + + H = HarmonicPotential(m) + + xdata = np.array(td.xdata_angstrom) + ydata = np.array(td.ydata_hartree) + + H.fit_to_data(xdata, ydata) + + VS = VibronicStructure1DFD(m, H) + + N = np.array(range(2,8)) + vib_levels = VS.vibrational_energy_level(N) + vib_levels_ref = H.vibrational_energy_level(N) + np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, + decimal=4) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_fermionic_transformation_symmetries.py b/test/chemistry/test_fermionic_transformation_symmetries.py new file mode 100644 index 0000000000..58b2d3c572 --- /dev/null +++ b/test/chemistry/test_fermionic_transformation_symmetries.py @@ -0,0 +1,240 @@ +# 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 Core Hamiltonian Symmetry Reduction """ + +import unittest +from test.chemistry import QiskitChemistryTestCase +import numpy as np + +from qiskit import BasicAer +from qiskit.aqua.algorithms import NumPyMinimumEigensolver, VQE +from qiskit.aqua.components.optimizers import SLSQP +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 import QiskitChemistryError +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.qubit_transformations import FermionicTransformation +from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation + + +class TestFermionicTransforationSymmetries(QiskitChemistryTestCase): + """ Core hamiltonian Driver symmetry 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_result(self, result, symm=True): + self.assertAlmostEqual(result.energy, -7.882324378883, places=3) + ref_dipole = (0.0, 0.0, -1.81741795) + if not symm: + np.testing.assert_almost_equal(result.dipole_moment, ref_dipole, decimal=2) + else: + self.assertIsNone(result.dipole_moment[0]) + self.assertIsNone(result.dipole_moment[1]) + self.assertAlmostEqual(result.dipole_moment[2], ref_dipole[2], places=2) + + def test_no_symmetry(self): + """ No symmetry reduction """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=None) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 12) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result, False) + + def test_auto_symmetry(self): + """ Auto symmetry reduction """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction='auto') + qubit_op, _ = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 8) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) + + def test_given_symmetry(self): + """ Supplied symmetry reduction """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 1, 1, 1]) + qubit_op, _ = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 8) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) + + def test_given_symmetry_fail_len(self): + """ Supplied symmetry reduction invalid len """ + with self.assertRaises(QiskitChemistryError): + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 1, 1]) + + _, _ = fermionic_transformation.transform(self.driver) + + def test_given_symmetry_fail_values(self): + """ Supplied symmetry reduction invalid values """ + with self.assertRaises(QiskitChemistryError): + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=None, + z2symmetry_reduction=[1, 0, 1, 1]) + + _, _ = fermionic_transformation.transform(self.driver) + + def test_auto_symmetry_freeze_core(self): + """ Auto symmetry reduction, with freeze core """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) + + def test_auto_freeze_core_parity(self): + """ Auto symmetry reduction, with freeze core and parity mapping """ + + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.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 """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) + + def test_auto_ph_freeze_core_parity_2(self): + """ Auto symmetry reduction, with freeze core, parity and two q reduction """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.PARTICLE_HOLE, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + solver = NumPyMinimumEigensolver() + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) + + def test_vqe_auto_symmetry_freeze_core(self): + """ Auto symmetry reduction, with freeze core using VQE """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=None, + z2symmetry_reduction='auto') + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self.assertEqual(qubit_op.num_qubits, 6) + num_orbitals = fermionic_transformation._molecule_info['num_orbitals'] + num_particles = fermionic_transformation._molecule_info['num_particles'] + qubit_mapping = 'jordan_wigner' + two_qubit_reduction = fermionic_transformation._two_qubit_reduction + z2_symmetries = qubit_op.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) + + solver = VQE(var_form=var_form, optimizer=SLSQP(maxiter=500), + quantum_instance=BasicAer.get_backend('statevector_simulator')) + gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) + result = gsc.compute_groundstate(self.driver) + self._validate_result(result) + self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_oovqe2.py~ b/test/chemistry/test_oovqe2.py~ new file mode 100644 index 0000000000..41934f2311 --- /dev/null +++ b/test/chemistry/test_oovqe2.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. + +""" Test of the OOVQE ground state calculations """ +import unittest +from test.chemistry import QiskitChemistryTestCase + +from qiskit.chemistry.drivers import HDF5Driver +from qiskit.providers.basicaer import BasicAer +from qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import VQE +from qiskit.chemistry.components.variational_forms import UCCSD +from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.aqua.components.optimizers import COBYLA +from qiskit.chemistry.algorithms.ground_state_solvers import VQEUCCSDFactory, OOVQE +from qiskit.chemistry.transformations import FermionicTransformation +from qiskit.chemistry.transformations.fermionic_transformation import QubitMappingType + + +class TestOOVQE(QiskitChemistryTestCase): + """ Test OOVQE Ground State Calculation. """ + + def setUp(self): + super().setUp() + + file1 = 'test_oovqe_h4.hdf5' + file2 = 'test_oovqe_lih.hdf5' + file3 = 'test_oovqe_h4_uhf.hdf5' + + self.driver1 = HDF5Driver(hdf5_input=self.get_resource_path(file1)) + self.driver2 = HDF5Driver(hdf5_input=self.get_resource_path(file2)) + self.driver3 = HDF5Driver(hdf5_input=self.get_resource_path(file3)) + + self.energy1_rotation = -3.0104 + self.energy1 = -2.77 # energy of the VQE with pUCCD ansatz and LBFGSB optimizer + self.energy2 = -7.70 + self.energy3 = -2.50 + self.initial_point1 = [0.039374, -0.47225463, -0.61891996, 0.02598386, 0.79045546, + -0.04134567, 0.04944946, -0.02971617, -0.00374005, 0.77542149] + + self.seed = 50 + + self.optimizer = COBYLA(maxiter=1) + self.transformation1 = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False) + self.transformation2 = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True) + + def make_solver(self): + """ Instantiates a solver for the test of OOVQE. """ + + quantum_instance = QuantumInstance(BasicAer.get_backend('statevector_simulator'), + shots=1, + seed_simulator=self.seed, + seed_transpiler=self.seed) + solver = VQEUCCSDFactory(quantum_instance) + + def get_custom_solver(self, transformation): + """Customize the solver.""" + + 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) + # only paired doubles excitations + 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, + excitation_type='d', + same_spin_doubles=False, + method_doubles='pucc') + vqe = VQE(var_form=var_form, quantum_instance=self._quantum_instance, + optimizer=COBYLA(maxiter=1)) + return vqe + + # pylint: disable=no-value-for-parameter + solver.get_solver = get_custom_solver.__get__(solver, VQEUCCSDFactory) + return solver + + def test_orbital_rotations(self): + """ Test that orbital rotations are performed correctly. """ + + solver = self.make_solver() + calc = OOVQE(self.transformation1, solver, iterative_oo=False, + initial_point=self.initial_point1) + calc._vqe.optimizer.set_options(maxiter=1) + algo_result = calc.solve(self.driver1) + self.assertAlmostEqual(algo_result.computed_electronic_energy, self.energy1_rotation, 4) + + # def test_oovqe(self): + # """ Test the simultaneous optimization of orbitals and ansatz parameters with OOVQE using + # BasicAer's statevector_simulator. """ + # + # solver = self.make_solver() + # calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=False, + # initial_point=self.initial_point1) + # calc._vqe.optimizer.set_options(maxiter=3, rhobeg=0.01) + # algo_result = calc.solve(self.driver1) + # self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1, 4) + # + # def test_iterative_oovqe(self): + # """ Test the iterative OOVQE using BasicAer's statevector_simulator. """ + # + # solver = self.make_solver() + # calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=True, + # initial_point=self.initial_point1, iterative_oo_iterations=2) + # calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) + # algo_result = calc.solve(self.driver1) + # self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1) + # + # def test_oovqe_with_frozen_core(self): + # """ Test the OOVQE with frozen core approximation. """ + # + # solver = self.make_solver() + # calc = OOVQE(self.transformation2, solver, self.driver2, iterative_oo=False) + # calc._vqe.optimizer.set_options(maxiter=2, rhobeg=1) + # algo_result = calc.solve(self.driver2) + # self.assertLessEqual(algo_result.computed_electronic_energy + + # self.transformation2._energy_shift + + # self.transformation2._nuclear_repulsion_energy, self.energy2) + # + # def test_oovqe_with_unrestricted_hf(self): + # """ Test the OOVQE with unrestricted HF method. """ + # + # solver = self.make_solver() + # calc = OOVQE(self.transformation1, solver, self.driver3, iterative_oo=False) + # calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) + # algo_result = calc.solve(self.driver3) + # self.assertLessEqual(algo_result.computed_electronic_energy, self.energy3) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_partition_function.py b/test/chemistry/test_partition_function.py new file mode 100644 index 0000000000..f3f3c08d73 --- /dev/null +++ b/test/chemistry/test_partition_function.py @@ -0,0 +1,76 @@ +import unittest + +import numpy as np + +import chemistry.code.test.test_data as td + +from functools import partial + +from chemistry.code.partition_function import DifferentiableFunction +from chemistry.code.partition_function import DiatomicPartitionFunction +from chemistry.code.molecule import Molecule +from chemistry.code.morse_potential import MorsePotential + +#TODO Fix this test + +class TestPartitionFunction(unittest.TestCase): + def create_test_molecule(self): + stretch = partial(Molecule.absolute_stretching, + kwargs={'atom_pair': (1, 0)}) + m = Molecule(geometry=[['H', [0., 0., 0.]], ['D', [0., 0., 1.]]], + degrees_of_freedom=[stretch], + masses=[1.6735328E-27, 3.444946E-27], + spins=[1 / 2, 1]) + return m + + def test_partition_function(self): + m = self.create_test_molecule() + + M = MorsePotential(m) + + xdata = np.array(td.xdata_angstrom) + ydata = np.array(td.ydata_hartree) + + M.fit_to_data(xdata, ydata) + + P = DiatomicPartitionFunction(m, M, M) + + pressure = 102523 + temps = np.array([10, 50, 100, 200]) + + trans = P.get_partition(part="trans", pressure=pressure) + with self.assertWarns(RuntimeWarning): + P.get_partition(part="trans", split='para', pressure=pressure) + log_trans = np.array( + [3.76841035, 7.79200513, 9.52487308, 11.25774103]) + np.testing.assert_array_almost_equal(log_trans, np.log(trans(temps))) + + vib = P.get_partition(part="vib", pressure=pressure) + log_vib = np.array( + [-269.92161476, -53.98432295, -26.99216148, -13.49608074]) + np.testing.assert_array_almost_equal(log_vib, np.log(vib(temps))) + + rot = P.get_partition(part="rot", pressure=pressure) + log_rot = np.array( + [5.63843874e-05, 2.98377556e-01, 7.93867745e-01, 1.39334663e+00]) + np.testing.assert_array_almost_equal(log_rot, np.log(rot(temps))) + + def test_differentiable_function(self): + def f(x, c): + return x**3 + np.exp(2*x) + np.log(x) + np.sin(x) + c + + def df(x, c): + return 3*x**2 + 2*np.exp(2*x) + 1/x + np.cos(x) + + F = DifferentiableFunction(f, argument_name='x') + F_analytic = DifferentiableFunction(f, derivative=df) + F_dc = DifferentiableFunction(f, argument_name='c') + + self.assertEqual(F(1, 2), f(1, 2)) + self.assertAlmostEqual(F.D(1, 2), df(1, 2), places=6) + self.assertEqual(F_analytic.D(1, 2), df(1, 2)) + self.assertAlmostEqual(F_dc.D(1, 2), 1.0) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_potential.py b/test/chemistry/test_potential.py new file mode 100644 index 0000000000..26f2b53afb --- /dev/null +++ b/test/chemistry/test_potential.py @@ -0,0 +1,162 @@ +import unittest + +import numpy as np + +import qiskit.chemistry.constants as const + +from functools import partial +import matplotlib.pyplot as plt + + +from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential +from qiskit.chemistry.algorithms.pes_samplers.potentials.harmonic_potential import HarmonicPotential +from qiskit.chemistry.drivers.molecule import Molecule + +from qiskit.chemistry.constants import HARTREE_TO_J_PER_MOL + +class TestPotential(unittest.TestCase): + + def create_test_molecule(self): + stretch = partial(Molecule.absolute_stretching, + kwargs={'atom_pair': (1, 0)}) + m = Molecule(geometry=[['H', [0., 0., 0.]], ['D', [0., 0., 1.]]], + degrees_of_freedom=[stretch], + masses=[1.6735328E-27, 3.444946E-27]) + return m + + def test_morse(self): + + self._xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, + 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, + 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, + 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, + 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, + 5.25]) + + self._ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, + -2611323.75276296, -2502198.92978322, -2417457.48952287, + -2390778.71123391, -2379482.70907613, -2373850.72354504, + -2361426.93801724, -2369992.6305902, -2363833.07716161, + -2360577.93019891, -2356002.65576262, -2355574.41051646, + -2357254.94032554, -2351656.71871981, -2308055.75509618, + -2797576.98597419, -2715367.76135088, -2616523.58105343, + -2498053.2658529, -2424288.88205414, -2393385.83237565, + -2371800.12956182, -2353202.82294735, -2346873.32092711, + -2343485.8487826, -2342937.74947792, -2350276.02096954, + -2347674.75469199, -2346912.78218669, -2339886.28877723, + -2353456.10489755, -2359599.85281831, -2811321.68662548, + -2763866.98837641, -2613385.92519959, -2506804.00364042, + -2419329.49702063, -2393428.68052976, -2374166.67617163, + -2352961.35574553, -2344972.64297329, -2356294.5588125, + -2341396.63369969, -2337344.83138146, -2339793.71365995, + -2335667.95101689, -2327347.45385524, -2341367.28061372]) + + self.ydata_hartree = self._ydata / HARTREE_TO_J_PER_MOL + self.ydata_j_per_mol = self._ydata + + self.xdata_angstrom = self._xdata + + m = self.create_test_molecule() + + M = MorsePotential(m) + + xdata = np.array(self.xdata_angstrom) + ydata = np.array(self.ydata_hartree) + + M.fit_to_data(xdata, ydata) + + #self.plot_potential(xdata, ydata, M) + + minimalEnergyDistance = M.get_equilibrium_geometry() + minimalEnergy = M.eval(minimalEnergyDistance) + waveNumber = M.wave_number() + + result = np.array([minimalEnergyDistance, minimalEnergy, waveNumber]) + benchmark = np.array([0.8106703001726382, + -1.062422610690636, 3800.7855102410026]) + np.testing.assert_array_almost_equal(result, benchmark) + + radia = np.array([0.5, 1, 1.5, 2]) + hartrees = np.array( + [-0.94045495, -1.04591482, -0.96876003, -0.92400906]) + np.testing.assert_array_almost_equal(hartrees, M.eval(radia)) + + vib_levels = [] + for N in range(2, 8): + vib_levels.append(M.vibrational_energy_level(N)) + vib_levels = np.array(vib_levels) + vib_levels_ref = np.array([0.04052116451981064, 0.05517676610999135, + 0.06894501671860434, 0.08182591634564956, + 0.09381946499112709, 0.10492566265503685]) + np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref) + + def test_harmonic(self): + + self._xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, + 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, + 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, + 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, + 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, + 5.25]) + + self._ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, + -2611323.75276296, -2502198.92978322, -2417457.48952287, + -2390778.71123391, -2379482.70907613, -2373850.72354504, + -2361426.93801724, -2369992.6305902, -2363833.07716161, + -2360577.93019891, -2356002.65576262, -2355574.41051646, + -2357254.94032554, -2351656.71871981, -2308055.75509618, + -2797576.98597419, -2715367.76135088, -2616523.58105343, + -2498053.2658529, -2424288.88205414, -2393385.83237565, + -2371800.12956182, -2353202.82294735, -2346873.32092711, + -2343485.8487826, -2342937.74947792, -2350276.02096954, + -2347674.75469199, -2346912.78218669, -2339886.28877723, + -2353456.10489755, -2359599.85281831, -2811321.68662548, + -2763866.98837641, -2613385.92519959, -2506804.00364042, + -2419329.49702063, -2393428.68052976, -2374166.67617163, + -2352961.35574553, -2344972.64297329, -2356294.5588125, + -2341396.63369969, -2337344.83138146, -2339793.71365995, + -2335667.95101689, -2327347.45385524, -2341367.28061372]) + + self.ydata_hartree = self._ydata / HARTREE_TO_J_PER_MOL + self.ydata_j_per_mol = self._ydata + + self.xdata_angstrom = self._xdata + + m = self.create_test_molecule() + + H = HarmonicPotential(m) + + xdata = np.array(self.xdata_angstrom) + ydata = np.array(self.ydata_hartree) + + H.fit_to_data(xdata, ydata) + + #self.plot_potential(xdata, ydata, H) + + minimalEnergyDistance = H.get_equilibrium_geometry() + minimalEnergy = H.eval(minimalEnergyDistance) + waveNumber = H.wave_number() + + result = np.array([minimalEnergyDistance, minimalEnergy, waveNumber]) + benchmark = np.array([0.8792058944654566, + -1.0678714520398802, 4670.969897517367]) + np.testing.assert_array_almost_equal(result, benchmark) + + radia = np.array([0.5, 1, 1.5, 2]) + hartrees = np.array( + [-0.92407434, -1.05328024, -0.68248613, 0.18830797]) + np.testing.assert_array_almost_equal(hartrees, + H.eval(radia)) + + vib_levels = [] + for N in range(2, 8): + vib_levels.append(H.vibrational_energy_level(N)) + vib_levels = np.array(vib_levels) + vib_levels_ref = np.array([0.053206266711245426, 0.07448877339574358, + 0.09577128008024177, 0.11705378676473993, + 0.13833629344923812, 0.15961880013373628]) + np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_thermodynamics.py b/test/chemistry/test_thermodynamics.py new file mode 100644 index 0000000000..0696a5e0b3 --- /dev/null +++ b/test/chemistry/test_thermodynamics.py @@ -0,0 +1,109 @@ +import unittest + +import numpy as np + +import chemistry.code.test.test_data as td + +from functools import partial + +from chemistry.code.partition_function import DiatomicPartitionFunction +from chemistry.code.molecule import Molecule +from chemistry.code.morse_potential import MorsePotential +import chemistry.code.thermodynamics as thermo + +# TODO Fix this test + +class TestThermodynamics(unittest.TestCase): + def create_test_molecule(self): + stretch = partial(Molecule.absolute_stretching, + kwargs={'atom_pair': (1, 0)}) + m = Molecule(geometry=[['H', [0., 0., 0.]], ['D', [0., 0., 1.]]], + degrees_of_freedom=[stretch], + masses=[1.6735328E-27, 3.444946E-27], + spins=[1 / 2, 1]) + return m + + def performTest(self, thermoFuncName, benchmark): + pf_callable = self.pf_callable + td_class = self.td_class + + temps = np.array([10, 50, 100, 200]) + + from_df_callable = getattr(thermo, thermoFuncName)(pf_callable, temps) + from_class = getattr(td_class, thermoFuncName)(temps) + + np.testing.assert_array_equal(from_class, from_df_callable) + np.testing.assert_array_almost_equal(from_class, benchmark) + + def test_thermo(self): + m = self.create_test_molecule() + + M = MorsePotential(m) + + xdata = np.array(td.xdata_angstrom) + ydata = np.array(td.ydata_hartree) + + M.fit_to_data(xdata, ydata) + + P = DiatomicPartitionFunction(m, M, M) + + pressure = 102523 + + self.pf_callable = P.get_default_callable(pressure=pressure) + self.td_class = thermo.Thermodynamics(P, pressure) + + benchmark = np.array([22129.20266631, 19079.17137585, + 13863.05240428, 1405.13250492]) + self.performTest('helmholtz_free_energy', benchmark) + + benchmark = np.array([22567.29835223, 23309.38074915, + 24363.27230243, 26445.90231315]) + self.performTest('thermodynamic_energy', benchmark) + + benchmark = np.array([43.80956859, 84.60418747, + 105.00219898, 125.20384904]) + self.performTest('entropy', benchmark) + + benchmark = np.array([22650.44297369, 23725.10385642, + 25194.71851698, 28108.79474224]) + self.performTest('enthalpy', benchmark) + + benchmark = np.array([22212.34728776, 19494.89448313, + 14694.49861883, 3068.02493402]) + self.performTest('gibbs_free_energy', benchmark) + + benchmark = np.array([12.52720256, 21.50674114, + 20.88104447, 20.80339556]) + self.performTest('constant_volume_heat_capacity', benchmark) + + benchmark = np.array([20.84166472, 29.8212033, + 29.19550663, 29.11785772]) + self.performTest('constant_pressure_heat_capacity', benchmark) + + def test_non_differentiable_callable(self): + m = self.create_test_molecule() + + M = MorsePotential(m) + + xdata = np.array(td.xdata_angstrom) + ydata = np.array(td.ydata_hartree) + + M.fit_to_data(xdata, ydata) + + P = DiatomicPartitionFunction(m, M, M) + + pressure = 102523 + temps = np.array([10, 50, 100, 200]) + vib = P.get_partition(part='vib', + pressure=pressure) + benchmark = np.array([22442.53040649, 22442.53040649, + 22442.53040649, 22442.53040672]) + diff = thermo.thermodynamic_energy(vib, temps) + n_diff = thermo.thermodynamic_energy(lambda T: vib(T), temps) + + np.testing.assert_array_almost_equal(diff, benchmark) + np.testing.assert_array_almost_equal(diff, n_diff, decimal=5) + + +if __name__ == '__main__': + unittest.main() From 25972e01dcaf59e90896b73eb50842a1b2b4a23c Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 14 Oct 2020 18:30:31 -0400 Subject: [PATCH 153/197] Update qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uccsd_factory.py Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- .../minimum_eigensolver_factories/vqe_uccsd_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 96cab2c7da..9284a09873 100644 --- 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 @@ -104,7 +104,7 @@ def expectation(self, expectation: ExpectationBase) -> None: @property def include_custom(self) -> bool: """Getter of the ``include_custom`` setting for the ``expectation`` setting.""" - return self._optimizer + return self._include_custom @include_custom.setter def include_custom(self, include_custom: bool) -> None: From 5e0ee8be122b76813f50ab90b2b54972d43ea2ae Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 00:31:27 +0200 Subject: [PATCH 154/197] bopes result and constants --- qiskit/chemistry/constants.py | 39 +++++++++++++ .../chemistry/results/bopes_sampler_result.py | 56 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 qiskit/chemistry/constants.py create mode 100644 qiskit/chemistry/results/bopes_sampler_result.py diff --git a/qiskit/chemistry/constants.py b/qiskit/chemistry/constants.py new file mode 100644 index 0000000000..08ed22bce8 --- /dev/null +++ b/qiskit/chemistry/constants.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2019, 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. +""" +Defines some constants used in chemical calculations. +""" + +# multiplicative conversions +N_A = 6.02214129E23 # particles per mol + +KCAL_PER_MOL_TO_J_PER_MOLECULE = 6.947695E-21 +HARTREE_TO_KCAL_PER_MOL = 627.509474 +HARTREE_TO_J_PER_MOL = 2625499.63922 +HARTREE_TO_KJ_PER_MOL = 2625.49963922 +HARTREE_TO_PER_CM = 219474.63 +J_PER_MOL_TO_PER_CM = 0.08359347178 +CAL_TO_J = 4.184 +HARTREE_TO_J = 4.3597443380807824e-18 # HARTREE_TO_J_PER_MOL / N_A +J_TO_HARTREE = 2.293712480489655e+17 # 1.0 / HARTREE_TO_J +M_TO_ANGSTROM = 1E10 +ANGSTROM_TO_M = 1E-10 + + +# physical constants +C_CM_PER_S = 2.9979245800E10 +C_M_PER_S = 2.9979245800E8 +HBAR_J_S = 1.054571800E-34 # note this is h/2Pi +H_J_S = 6.62607015E-34 +KB_J_PER_K = 1.3806488E-23 diff --git a/qiskit/chemistry/results/bopes_sampler_result.py b/qiskit/chemistry/results/bopes_sampler_result.py new file mode 100644 index 0000000000..2665cd4526 --- /dev/null +++ b/qiskit/chemistry/results/bopes_sampler_result.py @@ -0,0 +1,56 @@ +# 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. + +"""BOPES Sampler result""" + +import logging +from typing import List, Dict + +from qiskit.chemistry.results import EigenstateResult + +logger = logging.getLogger(__name__) + + +class BOPESSamplerResult: + """The BOPES Sampler result""" + + # def __init__(self, results, results_full): + # self._results = results + # self._results_full = results_full + def __init__(self, points: List[float], + energies: List[float], + raw_results: Dict[float, EigenstateResult]) -> None: + super().__init__() + self._points = points + self._energies = energies + self._raw_results = raw_results + + @property + def points(self) -> List[float]: + """ returns list of points.""" + # return self._results.get('point') + return self._points + + @property + def energies(self) -> List[float]: + """ returns list of energies.""" + # return self._results.get('energy') + return self._energies + + @property + def raw_results(self) -> Dict[float, EigenstateResult]: + """ returns all results for all points.""" + return self._raw_results + + def point_results(self, point: float) -> EigenstateResult: + """ returns all results for a specific point.""" + return self.raw_results[point] From a3ccb85a5db90dd222b14a5c9d9cbfe8634627c4 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 14 Oct 2020 18:32:16 -0400 Subject: [PATCH 155/197] fix spell --- .pylintdict | 1 + 1 file changed, 1 insertion(+) diff --git a/.pylintdict b/.pylintdict index e668b857c7..15db10878e 100644 --- a/.pylintdict +++ b/.pylintdict @@ -734,6 +734,7 @@ vecs Veis vertices vibrational +vibronic ville visualisation vqc From bec2fae5211db1ab402e7186f972eb1fbb778aa8 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 00:35:50 +0200 Subject: [PATCH 156/197] test_extrapolatrors added --- test/chemistry/test_extrapolators.py | 249 +++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 test/chemistry/test_extrapolators.py diff --git a/test/chemistry/test_extrapolators.py b/test/chemistry/test_extrapolators.py new file mode 100644 index 0000000000..d4408a9a90 --- /dev/null +++ b/test/chemistry/test_extrapolators.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018 IBM RESEARCH. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================= + +""" Unit test of Extrapolators based on a dictionary of point, parameter pairs.""" + +import unittest +from sklearn import linear_model +from qiskit.chemistry.algorithms.pes_samplers.extrapolator import Extrapolator, WindowExtrapolator, \ + PolynomialExtrapolator, DifferentialExtrapolator, PCAExtrapolator, SieveExtrapolator + +from qiskit.aqua import AquaError + +PARAM_DICT = { + 0.5: [0.07649726233077458, 1.2340960400591198e-07, 2.719308771599091e-08, + 0.008390437810203526, 0.0764741614750971, 1.9132000956096602e-07, + 2.9065333930913485e-07, 0.00833313170973392, -0.05158805745745537, + 5.8737804595134226e-08], + 0.6: [0.047537653925701376, -1.917048657008852e-07, -3.422658080883276e-08, + 0.004906396230989982, 0.0475356511244039, -1.4858989951517077e-07, + -4.554118927702692e-07, 0.004904674017550646, -0.026469990208471097, + -7.247195166355873e-08], + 0.7: [0.03613992230685178, 4.741673241363808e-07, 2.3773620079947958e-07, + 0.0019807112069739983, 0.03613265701497832, 6.110370616271814e-07, + 4.7624119913927746e-07, 0.0019793617137878546, -0.022690629295970738, + 1.4536323300165836e-07], + 0.8: [0.032353493452967126, -5.988966845753558e-07, 5.745328822729277e-08, + 0.00021194523430201692, 0.032355414067993846, -5.189590826114998e-07, + -3.67836416341141e-07, 0.0002135481005546501, -0.021642050861260625, + -1.7031569870078584e-08], + 0.9: [0.029140898456611407, -6.173482350529143e-07, 6.195627362572485e-08, + -0.0003490974905228344, 0.029145442226752687, -6.121480980106215e-07, + -7.301901035010772e-07, -0.0003490856477090481, -0.02152841574436387, + 1.7275126260813324e-07], + 1.0: [0.029247914836804657, 5.406923000869481e-08, -1.2835940341198057e-08, + -0.00014550115747168215, 0.02924483275168305, 3.332715515541604e-08, + -4.214594252866692e-08, -0.00014476700526947067, -0.022193103836975897, + 1.8066966314280466e-07], + 1.1: [0.03006212028509998, 8.719643359505152e-07, 2.2976675446724122e-07, + 0.00047712315923690516, 0.03006488639435572, 1.1166361133977898e-06, + 5.061212361236216e-07, 0.00047764287100316387, -0.023178549796925824, + -2.928595563199974e-07], + 1.2: [0.030974376843233304, -1.0856144455895877e-06, -3.476503050548108e-07, + 0.001136538429249089, 0.03097485850614847, -9.341556711096642e-07, + -1.1105021085720809e-07, 0.0011365812922166018, -0.024770225335378506, + 3.1946997465490094e-07], + 1.3: [0.031882221296263585, 1.786623717240475e-06, 5.966161740895298e-07, + 0.0019238138369525367, 0.03188025548265294, 2.001914958908424e-06, + -7.558698586542756e-08, 0.0019267033837603463, -0.026633630436000855, + -4.838673928102748e-07], + 1.4: [0.03363319046621523, -6.215327218045763e-06, -1.707461485292177e-06, + 0.0022111427295926026, 0.03363427344016048, -6.479433272163631e-06, + -8.620279811840461e-07, 0.0022079369298442677, -0.029254200628083923, + 2.03258913595112e-06], + 1.5: [0.03566191437849682, 1.3175681716659443e-05, 3.3463916528882136e-06, + 0.0030670576873521546, 0.03565986755932079, 1.3808936313520536e-05, + 2.1227354591337757e-06, 0.0030639663487480417, -0.03203113690256062, + -2.988438361808215e-06], + 1.6: [0.03853048160610931, -4.500510577352305e-05, -1.1042391095055013e-05, + 0.003589496950963951, 0.03852649109560952, -4.632560074669591e-05, + -6.9604927841086826e-06, 0.003591766338853773, -0.03617535567521557, + 1.03526517642164e-05], + 1.7: [0.04166595503111059, 0.00012474608362087326, 3.0811181106852395e-05, + 0.004449408009656353, 0.04167583498336048, 0.0001291807564363206, + 1.9103762924011895e-05, 0.004443558543591776, -0.0411176424372442, + -3.143959686889569e-05], + 1.8: [0.04630023768704881, -0.0003032527231323504, -7.224290210451026e-05, + 0.004988381942930891, 0.04629620402315099, -0.0003111138155773558, + -4.900370932911525e-05, 0.004995942389375613, -0.047398106863887825, + 7.734110549927737e-05], + 1.9: [0.05237961421167222, 0.0006396923182584415, 0.00014873747649097767, + 0.005855974769304974, 0.05234227038906301, 0.0006540391246003456, + 0.00010652381338578109, 0.005850757199904456, -0.055346836396118364, + -0.00018559571977688104] +} + + +class TestExtrapolators(unittest.TestCase): + """Test Extrapolators.""" + + def test_factory(self): + """ + Test factory method implementation to create instances of various Extrapolators. + """ + self.assertIsInstance(Extrapolator.factory(mode='window'), WindowExtrapolator) + self.assertIsInstance(Extrapolator.factory(mode='poly'), PolynomialExtrapolator) + self.assertIsInstance(Extrapolator.factory(mode='diff_model'), DifferentialExtrapolator) + self.assertIsInstance(Extrapolator.factory(mode='pca'), PCAExtrapolator) + self.assertIsInstance(Extrapolator.factory(mode='l1'), SieveExtrapolator) + self.assertRaises(AquaError, Extrapolator.factory, mode="unknown") + + def test_polynomial_extrapolator(self): + """ + Test extrapolation using a polynomial extrapolator with degree = 1 using all previous points + in the parameters for extrapolation. This test confirms that the extrapolation of the + parameters has a specified error relative to the actual parameter values. + NOTE: The polynomial fit may give a runtime warning if the data is poorly fitted. + This depends on degree and dataset and may need be tuned by the user to achieve + optimal results. This reasoning holds for any instance using an internal + polynomial extrapolator. + """ + points = 0.7 + params = PolynomialExtrapolator(degree=3).extrapolate(points=[points], + param_dict=PARAM_DICT) + sq_diff = [(actual - expected) ** 2 for actual, expected in + zip(params[points], PARAM_DICT[points])] + self.assertLess(sum(sq_diff), 1e-3) + + def test_poly_window_extrapolator(self): + """ + Test extrapolation using an WindowExtrapolator using a data window/lookback of 3 points + and an internal polynomial extrapolator with degree = 1. This test confirms that no + extrapolation is performed on points before the data window, i.e, the first two points, + and that the extrapolation of the parameters on the last three points has a error below + a threshold when compared to the actual parameter values. + """ + points_interspersed = [.3, .5, .7, .8, 1.5] + window_extrapolator = Extrapolator.factory("window", + extrapolator=PolynomialExtrapolator(degree=1), + window=3) + params = window_extrapolator.extrapolate(points=points_interspersed, param_dict=PARAM_DICT) + self.assertFalse(params.get(.3)) + self.assertFalse(params.get(.5)) + sq_diff_1 = [(actual - expected) ** 2 + for actual, expected in zip(params[.7], PARAM_DICT[.7])] + self.assertLess(sum(sq_diff_1), 1e-1) + sq_diff_2 = [(actual - expected) ** 2 + for actual, expected in zip(params[.8], PARAM_DICT[.8])] + self.assertLess(sum(sq_diff_2), 1e-2) + sq_diff_3 = [(actual - expected) ** 2 + for actual, expected in zip(params[1.5], PARAM_DICT[1.5])] + self.assertLess(sum(sq_diff_3), 1e-2) + + def test_differential_model_window_extrapolator(self): + """ + Test extrapolation using an WindowExtrapolator using a data window/lookback of 3 points + and an internal differential extrapolator with degree = 1 and the default linear regression + model from scikit-learn. This test confirms that no extrapolation is performed on points + before the data window, i.e, the first two points, and that the extrapolation of the + parameters on the last three points has some specified error relative to the actual values. + """ + points_interspersed = [.3, .5, .7, .8, 1.5] + window_extrapolator = WindowExtrapolator(extrapolator=DifferentialExtrapolator(degree=1), + window=3) + params = window_extrapolator.extrapolate(points=points_interspersed, param_dict=PARAM_DICT) + self.assertFalse(params.get(.3)) + self.assertFalse(params.get(.5)) + sq_diff_1 = [(actual - expected) ** 2 + for actual, expected in zip(params[.7], PARAM_DICT[.7])] + self.assertLess(sum(sq_diff_1), 1e-2) + sq_diff_2 = [(actual - expected) ** 2 + for actual, expected in zip(params[.8], PARAM_DICT[.8])] + self.assertLess(sum(sq_diff_2), 1e-3) + sq_diff_3 = [(actual - expected) ** 2 + for actual, expected in zip(params[1.5], PARAM_DICT[1.5])] + self.assertLess(sum(sq_diff_3), 1e-3) + + def test_differential_model_window_alternate_model_extrapolator(self): + """ + Test extrapolation using an WindowExtrapolator using a data window/lookback of 3 points + and an internal differential extrapolator with degree = 1 and the Ridge regression model + from scikit-learn. This test confirms that no extrapolation is performed on points before + the data window, i.e, the first two points, and that the extrapolation of the parameters on + the last three points has some specified error relative to the actual parameter values. + """ + points_interspersed = [.3, .5, .7, .8, 1.5] + model = linear_model.Ridge() + window_extrapolator = WindowExtrapolator(extrapolator=DifferentialExtrapolator(degree=1, + model=model), + window=3) + params = window_extrapolator.extrapolate(points=points_interspersed, param_dict=PARAM_DICT) + self.assertFalse(params.get(.3)) + self.assertFalse(params.get(.5)) + sq_diff_1 = [(actual - expected) ** 2 + for actual, expected in zip(params[.7], PARAM_DICT[.7])] + self.assertLess(sum(sq_diff_1), 1e-2) + sq_diff_2 = [(actual - expected) ** 2 + for actual, expected in zip(params[.8], PARAM_DICT[.8])] + self.assertLess(sum(sq_diff_2), 1e-3) + sq_diff_3 = [(actual - expected) ** 2 + for actual, expected in + zip(params[1.5], PARAM_DICT[1.5])] + self.assertLess(sum(sq_diff_3), 1e-3) + + def test_pca_polynomial_window_extrapolator(self): + """ + Test extrapolation using an PCAExtrapolator using a data window/lookback of 3 points + and an internal polynomial extrapolator with degree = 1 using regular PCA as default. + This test confirms that no extrapolation is performed on points before the + data window, i.e, the first two points, and that the extrapolation of the parameters on + last three points has a specified error relative to the actual parameter values. + """ + points_interspersed = [.3, .5, .7, .8, 1.5] + pca_poly_win_ext = PCAExtrapolator(extrapolator=PolynomialExtrapolator(degree=1), + window=3) + params = pca_poly_win_ext.extrapolate(points=points_interspersed, param_dict=PARAM_DICT) + self.assertFalse(params.get(.3)) + self.assertFalse(params.get(.5)) + sq_diff_1 = [(actual - expected) ** 2 + for actual, expected in zip(params[.7], PARAM_DICT[.7])] + self.assertLess(sum(sq_diff_1), 1e-2) + sq_diff_2 = [(actual - expected) ** 2 + for actual, expected in zip(params[.8], PARAM_DICT[.8])] + self.assertLess(sum(sq_diff_2), 1e-2) + sq_diff_3 = [(actual - expected) ** 2 + for actual, expected in zip(params[1.5], PARAM_DICT[1.5])] + self.assertLess(sum(sq_diff_3), 1e-2) + + def test_sieve_poly_window_extrapolator(self): + """ + Test extrapolation using an Sieve/Clustering Extrapolator using a data window/lookback of + 3 points and an internal polynomial extrapolator with degree = 1. + This test confirms that no extrapolation is performed on points before the + data window, i.e, the first two points, and that the extrapolation of the parameters on the + last three points has some specified error relative to the actual parameter values. + """ + points_interspersed = [.3, .5, .7, .8, 1.5] + sieve_win_extrapolator = SieveExtrapolator(extrapolator=PolynomialExtrapolator(degree=1), + window=3) + params = sieve_win_extrapolator.extrapolate(points=points_interspersed, + param_dict=PARAM_DICT) + self.assertFalse(params.get(.3)) + self.assertFalse(params.get(.5)) + sq_diff_1 = [(actual - expected) ** 2 + for actual, expected in zip(params[.7], PARAM_DICT[.7])] + self.assertLess(sum(sq_diff_1), 1e-1) + sq_diff_2 = [(actual - expected) ** 2 + for actual, expected in zip(params[.8], PARAM_DICT[.8])] + self.assertLess(sum(sq_diff_2), 1e-1) + sq_diff_3 = [(actual - expected) ** 2 + for actual, expected in zip(params[1.5], PARAM_DICT[1.5])] + self.assertLess(sum(sq_diff_3), 1e-1) + + +if __name__ == '__main__': + unittest.main() From 577eb1abea9dc8ae8cba3421b69c32da79d9b54f Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 00:11:55 +0100 Subject: [PATCH 157/197] linting --- test/chemistry/test_extrapolators.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/chemistry/test_extrapolators.py b/test/chemistry/test_extrapolators.py index d4408a9a90..3c9986415b 100644 --- a/test/chemistry/test_extrapolators.py +++ b/test/chemistry/test_extrapolators.py @@ -19,8 +19,9 @@ import unittest from sklearn import linear_model -from qiskit.chemistry.algorithms.pes_samplers.extrapolator import Extrapolator, WindowExtrapolator, \ - PolynomialExtrapolator, DifferentialExtrapolator, PCAExtrapolator, SieveExtrapolator +from qiskit.chemistry.algorithms.pes_samplers.extrapolator import Extrapolator, \ + WindowExtrapolator, PolynomialExtrapolator, DifferentialExtrapolator, PCAExtrapolator, \ + SieveExtrapolator from qiskit.aqua import AquaError From ff34e6da8a9154a1b9426afaad6e1928a3a3cc06 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 00:15:24 +0100 Subject: [PATCH 158/197] linting --- test/chemistry/test_data_potentials.py | 13 +++- test/chemistry/test_fd_vibronic.py | 41 ++++++----- ...est_fermionic_transformation_symmetries.py | 48 +++++++------ test/chemistry/test_partition_function.py | 34 +++++---- test/chemistry/test_potential.py | 72 ++++++++++--------- 5 files changed, 122 insertions(+), 86 deletions(-) diff --git a/test/chemistry/test_data_potentials.py b/test/chemistry/test_data_potentials.py index 7e6b4387be..4cdd5a224d 100644 --- a/test/chemistry/test_data_potentials.py +++ b/test/chemistry/test_data_potentials.py @@ -1,5 +1,14 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- +# 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. import numpy as np diff --git a/test/chemistry/test_fd_vibronic.py b/test/chemistry/test_fd_vibronic.py index 74bad5b36a..a3ec09cc9d 100644 --- a/test/chemistry/test_fd_vibronic.py +++ b/test/chemistry/test_fd_vibronic.py @@ -1,18 +1,27 @@ -import unittest - -import numpy as np - -import chemistry.code.test.test_data as td +# 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. +import unittest from functools import partial -from chemistry.code.morse_potential import MorsePotential -from chemistry.code.harmonic_potential import HarmonicPotential -from chemistry.code.molecule import Molecule +import chemistry.code.test.test_data as td +import numpy as np from chemistry.code.vibronic_structure_fd import VibronicStructure1DFD +from qiskit.chemistry.algorithms.pes_samplers.potentials.harmonic_potential import HarmonicPotential +from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential +from qiskit.chemistry.drivers import Molecule -# TODO Fix this test +# TODO Fix this test class TestFDVibronic(unittest.TestCase): def create_test_molecule(self): stretch = partial(Molecule.absolute_stretching, @@ -34,12 +43,11 @@ def test_with_morse(self): M.fit_to_data(xdata, ydata) VS = VibronicStructure1DFD(m, M) - - N = np.array(range(2,8)) + + N = np.array(range(2, 8)) vib_levels = VS.vibrational_energy_level(N) vib_levels_ref = M.vibrational_energy_level(N) - np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, - decimal=5) + np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, decimal=5) def test_with_harmonic(self): m = self.create_test_molecule() @@ -52,12 +60,11 @@ def test_with_harmonic(self): H.fit_to_data(xdata, ydata) VS = VibronicStructure1DFD(m, H) - - N = np.array(range(2,8)) + + N = np.array(range(2, 8)) vib_levels = VS.vibrational_energy_level(N) vib_levels_ref = H.vibrational_energy_level(N) - np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, - decimal=4) + np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, decimal=4) if __name__ == '__main__': diff --git a/test/chemistry/test_fermionic_transformation_symmetries.py b/test/chemistry/test_fermionic_transformation_symmetries.py index 58b2d3c572..0278bdbf9c 100644 --- a/test/chemistry/test_fermionic_transformation_symmetries.py +++ b/test/chemistry/test_fermionic_transformation_symmetries.py @@ -13,22 +13,26 @@ """ Test Core Hamiltonian Symmetry Reduction """ import unittest -from test.chemistry import QiskitChemistryTestCase + import numpy as np from qiskit import BasicAer from qiskit.aqua.algorithms import NumPyMinimumEigensolver, VQE from qiskit.aqua.components.optimizers import SLSQP +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver 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 import QiskitChemistryError from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.qubit_transformations import FermionicTransformation -from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.chemistry.transformations import FermionicTransformation +from test.chemistry import QiskitChemistryTestCase + + +# from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation -class TestFermionicTransforationSymmetries(QiskitChemistryTestCase): +class TestFermionicTransformationSymmetries(QiskitChemistryTestCase): """ Core hamiltonian Driver symmetry tests. """ def setUp(self): @@ -65,8 +69,8 @@ def test_no_symmetry(self): qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 12) solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) + result = gsc.solve(self.driver) self._validate_result(result, False) def test_auto_symmetry(self): @@ -81,8 +85,8 @@ def test_auto_symmetry(self): qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 8) solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) + result = gsc.solve(self.driver) self._validate_result(result) self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) @@ -98,8 +102,8 @@ def test_given_symmetry(self): qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 8) solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) + result = gsc.solve(self.driver) self._validate_result(result) self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) @@ -142,8 +146,8 @@ def test_auto_symmetry_freeze_core(self): qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) + result = gsc.solve(self.driver) self._validate_result(result) self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) @@ -161,8 +165,8 @@ def test_auto_freeze_core_parity(self): qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) + result = gsc.solve(self.driver) self._validate_result(result) self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, 1]) @@ -179,8 +183,8 @@ def test_auto_freeze_core_parity_2(self): qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) + result = gsc.solve(self.driver) self._validate_result(result) self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) @@ -197,8 +201,8 @@ def test_auto_ph_freeze_core_parity_2(self): qubit_op, _ = fermionic_transformation.transform(self.driver) self.assertEqual(qubit_op.num_qubits, 6) solver = NumPyMinimumEigensolver() - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) + result = gsc.solve(self.driver) self._validate_result(result) self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) @@ -230,8 +234,8 @@ def test_vqe_auto_symmetry_freeze_core(self): solver = VQE(var_form=var_form, optimizer=SLSQP(maxiter=500), quantum_instance=BasicAer.get_backend('statevector_simulator')) - gsc = MinimumEigensolverGroundStateCalculation(fermionic_transformation, solver) - result = gsc.compute_groundstate(self.driver) + gsc = GroundStateEigensolver(fermionic_transformation, solver) + result = gsc.solve(self.driver) self._validate_result(result) self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) diff --git a/test/chemistry/test_partition_function.py b/test/chemistry/test_partition_function.py index f3f3c08d73..93caa1c731 100644 --- a/test/chemistry/test_partition_function.py +++ b/test/chemistry/test_partition_function.py @@ -1,17 +1,27 @@ -import unittest - -import numpy as np - -import chemistry.code.test.test_data as td +# 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. +import unittest from functools import partial -from chemistry.code.partition_function import DifferentiableFunction -from chemistry.code.partition_function import DiatomicPartitionFunction +import chemistry.code.test.test_data as td +import numpy as np from chemistry.code.molecule import Molecule from chemistry.code.morse_potential import MorsePotential +from chemistry.code.partition_function import DiatomicPartitionFunction +from chemistry.code.partition_function import DifferentiableFunction + -#TODO Fix this test +# TODO Fix this test class TestPartitionFunction(unittest.TestCase): def create_test_molecule(self): @@ -42,12 +52,12 @@ def test_partition_function(self): with self.assertWarns(RuntimeWarning): P.get_partition(part="trans", split='para', pressure=pressure) log_trans = np.array( - [3.76841035, 7.79200513, 9.52487308, 11.25774103]) + [3.76841035, 7.79200513, 9.52487308, 11.25774103]) np.testing.assert_array_almost_equal(log_trans, np.log(trans(temps))) vib = P.get_partition(part="vib", pressure=pressure) log_vib = np.array( - [-269.92161476, -53.98432295, -26.99216148, -13.49608074]) + [-269.92161476, -53.98432295, -26.99216148, -13.49608074]) np.testing.assert_array_almost_equal(log_vib, np.log(vib(temps))) rot = P.get_partition(part="rot", pressure=pressure) @@ -57,10 +67,10 @@ def test_partition_function(self): def test_differentiable_function(self): def f(x, c): - return x**3 + np.exp(2*x) + np.log(x) + np.sin(x) + c + return x ** 3 + np.exp(2 * x) + np.log(x) + np.sin(x) + c def df(x, c): - return 3*x**2 + 2*np.exp(2*x) + 1/x + np.cos(x) + return 3 * x ** 2 + 2 * np.exp(2 * x) + 1 / x + np.cos(x) F = DifferentiableFunction(f, argument_name='x') F_analytic = DifferentiableFunction(f, derivative=df) diff --git a/test/chemistry/test_potential.py b/test/chemistry/test_potential.py index 26f2b53afb..7f9bdf0998 100644 --- a/test/chemistry/test_potential.py +++ b/test/chemistry/test_potential.py @@ -1,18 +1,24 @@ -import unittest - -import numpy as np - -import qiskit.chemistry.constants as const +# 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. +import unittest from functools import partial -import matplotlib.pyplot as plt - -from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential +import numpy as np from qiskit.chemistry.algorithms.pes_samplers.potentials.harmonic_potential import HarmonicPotential +from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential +from qiskit.chemistry.constants import HARTREE_TO_J_PER_MOL from qiskit.chemistry.drivers.molecule import Molecule -from qiskit.chemistry.constants import HARTREE_TO_J_PER_MOL class TestPotential(unittest.TestCase): @@ -27,35 +33,35 @@ def create_test_molecule(self): def test_morse(self): self._xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, - 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, - 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, - 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, - 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, - 5.25]) + 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, + 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, + 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, + 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, + 5.25]) self._ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, - -2611323.75276296, -2502198.92978322, -2417457.48952287, - -2390778.71123391, -2379482.70907613, -2373850.72354504, - -2361426.93801724, -2369992.6305902, -2363833.07716161, - -2360577.93019891, -2356002.65576262, -2355574.41051646, - -2357254.94032554, -2351656.71871981, -2308055.75509618, - -2797576.98597419, -2715367.76135088, -2616523.58105343, - -2498053.2658529, -2424288.88205414, -2393385.83237565, - -2371800.12956182, -2353202.82294735, -2346873.32092711, - -2343485.8487826, -2342937.74947792, -2350276.02096954, - -2347674.75469199, -2346912.78218669, -2339886.28877723, - -2353456.10489755, -2359599.85281831, -2811321.68662548, - -2763866.98837641, -2613385.92519959, -2506804.00364042, - -2419329.49702063, -2393428.68052976, -2374166.67617163, - -2352961.35574553, -2344972.64297329, -2356294.5588125, - -2341396.63369969, -2337344.83138146, -2339793.71365995, - -2335667.95101689, -2327347.45385524, -2341367.28061372]) + -2611323.75276296, -2502198.92978322, -2417457.48952287, + -2390778.71123391, -2379482.70907613, -2373850.72354504, + -2361426.93801724, -2369992.6305902, -2363833.07716161, + -2360577.93019891, -2356002.65576262, -2355574.41051646, + -2357254.94032554, -2351656.71871981, -2308055.75509618, + -2797576.98597419, -2715367.76135088, -2616523.58105343, + -2498053.2658529, -2424288.88205414, -2393385.83237565, + -2371800.12956182, -2353202.82294735, -2346873.32092711, + -2343485.8487826, -2342937.74947792, -2350276.02096954, + -2347674.75469199, -2346912.78218669, -2339886.28877723, + -2353456.10489755, -2359599.85281831, -2811321.68662548, + -2763866.98837641, -2613385.92519959, -2506804.00364042, + -2419329.49702063, -2393428.68052976, -2374166.67617163, + -2352961.35574553, -2344972.64297329, -2356294.5588125, + -2341396.63369969, -2337344.83138146, -2339793.71365995, + -2335667.95101689, -2327347.45385524, -2341367.28061372]) self.ydata_hartree = self._ydata / HARTREE_TO_J_PER_MOL self.ydata_j_per_mol = self._ydata self.xdata_angstrom = self._xdata - + m = self.create_test_molecule() M = MorsePotential(m) @@ -65,7 +71,7 @@ def test_morse(self): M.fit_to_data(xdata, ydata) - #self.plot_potential(xdata, ydata, M) + # self.plot_potential(xdata, ydata, M) minimalEnergyDistance = M.get_equilibrium_geometry() minimalEnergy = M.eval(minimalEnergyDistance) @@ -131,7 +137,7 @@ def test_harmonic(self): H.fit_to_data(xdata, ydata) - #self.plot_potential(xdata, ydata, H) + # self.plot_potential(xdata, ydata, H) minimalEnergyDistance = H.get_equilibrium_geometry() minimalEnergy = H.eval(minimalEnergyDistance) From 974e9437893122bda36c823713b746ab8421e4a7 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 00:45:10 +0100 Subject: [PATCH 159/197] linting --- .../algorithms/pes_samplers/extrapolator.py | 2 - test/chemistry/test_data_potentials.py | 10 +-- test/chemistry/test_fd_vibronic.py | 27 +++--- test/chemistry/test_potential.py | 45 +++++----- test/chemistry/test_thermodynamics.py | 89 ++++++++++--------- 5 files changed, 84 insertions(+), 89 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py b/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py index 93e12bad7a..daeb9dd3a2 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py +++ b/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py @@ -148,8 +148,6 @@ def extrapolate(self, points: List[float], param_dict: Optional[Dict[float, List ret_param_arr = [] for params in param_arr: - # coefficients, _, _, _, _ = np.polyfit(data_points, params, deg=self._degree, full=True) - # todo: generates a warning in the tests: RankWarning: Polyfit may be poorly conditioned coefficients = np.polyfit(data_points, params, deg=self._degree) poly = np.poly1d(coefficients) ret_param_arr += [poly(points)] diff --git a/test/chemistry/test_data_potentials.py b/test/chemistry/test_data_potentials.py index 4cdd5a224d..d1df8d8a2c 100644 --- a/test/chemistry/test_data_potentials.py +++ b/test/chemistry/test_data_potentials.py @@ -14,14 +14,14 @@ from qiskit.chemistry.constants import HARTREE_TO_J_PER_MOL -_xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, +X_DATA = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25]) -_ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, +Y_DATA = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, -2611323.75276296, -2502198.92978322, -2417457.48952287, -2390778.71123391, -2379482.70907613, -2373850.72354504, -2361426.93801724, -2369992.6305902, -2363833.07716161, @@ -39,7 +39,7 @@ -2341396.63369969, -2337344.83138146, -2339793.71365995, -2335667.95101689, -2327347.45385524, -2341367.28061372]) -ydata_hartree = _ydata / HARTREE_TO_J_PER_MOL -ydata_j_per_mol = _ydata +ydata_hartree = Y_DATA / HARTREE_TO_J_PER_MOL +ydata_j_per_mol = Y_DATA -xdata_angstrom = _xdata +xdata_angstrom = X_DATA diff --git a/test/chemistry/test_fd_vibronic.py b/test/chemistry/test_fd_vibronic.py index a3ec09cc9d..c243e97cf1 100644 --- a/test/chemistry/test_fd_vibronic.py +++ b/test/chemistry/test_fd_vibronic.py @@ -13,9 +13,10 @@ import unittest from functools import partial -import chemistry.code.test.test_data as td import numpy as np from chemistry.code.vibronic_structure_fd import VibronicStructure1DFD + +import test.chemistry.test_data_potentials as td from qiskit.chemistry.algorithms.pes_samplers.potentials.harmonic_potential import HarmonicPotential from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential from qiskit.chemistry.drivers import Molecule @@ -35,35 +36,35 @@ def create_test_molecule(self): def test_with_morse(self): m = self.create_test_molecule() - M = MorsePotential(m) + morse = MorsePotential(m) xdata = np.array(td.xdata_angstrom) ydata = np.array(td.ydata_hartree) - M.fit_to_data(xdata, ydata) + morse.fit_to_data(xdata, ydata) - VS = VibronicStructure1DFD(m, M) + vibronic_structure = VibronicStructure1DFD(m, morse) - N = np.array(range(2, 8)) - vib_levels = VS.vibrational_energy_level(N) - vib_levels_ref = M.vibrational_energy_level(N) + levels = np.array(range(2, 8)) + vib_levels = vibronic_structure.vibrational_energy_level(levels) + vib_levels_ref = morse.vibrational_energy_level(levels) np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, decimal=5) def test_with_harmonic(self): m = self.create_test_molecule() - H = HarmonicPotential(m) + harmonic = HarmonicPotential(m) xdata = np.array(td.xdata_angstrom) ydata = np.array(td.ydata_hartree) - H.fit_to_data(xdata, ydata) + harmonic.fit_to_data(xdata, ydata) - VS = VibronicStructure1DFD(m, H) + vibronic_structure = VibronicStructure1DFD(m, harmonic) - N = np.array(range(2, 8)) - vib_levels = VS.vibrational_energy_level(N) - vib_levels_ref = H.vibrational_energy_level(N) + levels = np.array(range(2, 8)) + vib_levels = vibronic_structure.vibrational_energy_level(levels) + vib_levels_ref = harmonic.vibrational_energy_level(levels) np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, decimal=4) diff --git a/test/chemistry/test_potential.py b/test/chemistry/test_potential.py index 7f9bdf0998..ab6497c36d 100644 --- a/test/chemistry/test_potential.py +++ b/test/chemistry/test_potential.py @@ -64,32 +64,30 @@ def test_morse(self): m = self.create_test_molecule() - M = MorsePotential(m) + morse = MorsePotential(m) xdata = np.array(self.xdata_angstrom) ydata = np.array(self.ydata_hartree) - M.fit_to_data(xdata, ydata) + morse.fit_to_data(xdata, ydata) # self.plot_potential(xdata, ydata, M) - minimalEnergyDistance = M.get_equilibrium_geometry() - minimalEnergy = M.eval(minimalEnergyDistance) - waveNumber = M.wave_number() + minimal_energy_distance = morse.get_equilibrium_geometry() + minimal_energy = morse.eval(minimal_energy_distance) + wave_number = morse.wave_number() - result = np.array([minimalEnergyDistance, minimalEnergy, waveNumber]) - benchmark = np.array([0.8106703001726382, - -1.062422610690636, 3800.7855102410026]) + result = np.array([minimal_energy_distance, minimal_energy, wave_number]) + benchmark = np.array([0.8106703001726382, -1.062422610690636, 3800.7855102410026]) np.testing.assert_array_almost_equal(result, benchmark) radia = np.array([0.5, 1, 1.5, 2]) - hartrees = np.array( - [-0.94045495, -1.04591482, -0.96876003, -0.92400906]) - np.testing.assert_array_almost_equal(hartrees, M.eval(radia)) + hartrees = np.array([-0.94045495, -1.04591482, -0.96876003, -0.92400906]) + np.testing.assert_array_almost_equal(hartrees, morse.eval(radia)) vib_levels = [] for N in range(2, 8): - vib_levels.append(M.vibrational_energy_level(N)) + vib_levels.append(morse.vibrational_energy_level(N)) vib_levels = np.array(vib_levels) vib_levels_ref = np.array([0.04052116451981064, 0.05517676610999135, 0.06894501671860434, 0.08182591634564956, @@ -130,33 +128,30 @@ def test_harmonic(self): m = self.create_test_molecule() - H = HarmonicPotential(m) + harmonic = HarmonicPotential(m) xdata = np.array(self.xdata_angstrom) ydata = np.array(self.ydata_hartree) - H.fit_to_data(xdata, ydata) + harmonic.fit_to_data(xdata, ydata) # self.plot_potential(xdata, ydata, H) - minimalEnergyDistance = H.get_equilibrium_geometry() - minimalEnergy = H.eval(minimalEnergyDistance) - waveNumber = H.wave_number() + minimal_energy_distance = harmonic.get_equilibrium_geometry() + minimal_energy = harmonic.eval(minimal_energy_distance) + wave_number = harmonic.wave_number() - result = np.array([minimalEnergyDistance, minimalEnergy, waveNumber]) - benchmark = np.array([0.8792058944654566, - -1.0678714520398802, 4670.969897517367]) + result = np.array([minimal_energy_distance, minimal_energy, wave_number]) + benchmark = np.array([0.8792058944654566, -1.0678714520398802, 4670.969897517367]) np.testing.assert_array_almost_equal(result, benchmark) radia = np.array([0.5, 1, 1.5, 2]) - hartrees = np.array( - [-0.92407434, -1.05328024, -0.68248613, 0.18830797]) - np.testing.assert_array_almost_equal(hartrees, - H.eval(radia)) + hartrees = np.array([-0.92407434, -1.05328024, -0.68248613, 0.18830797]) + np.testing.assert_array_almost_equal(hartrees, harmonic.eval(radia)) vib_levels = [] for N in range(2, 8): - vib_levels.append(H.vibrational_energy_level(N)) + vib_levels.append(harmonic.vibrational_energy_level(N)) vib_levels = np.array(vib_levels) vib_levels_ref = np.array([0.053206266711245426, 0.07448877339574358, 0.09577128008024177, 0.11705378676473993, diff --git a/test/chemistry/test_thermodynamics.py b/test/chemistry/test_thermodynamics.py index 0696a5e0b3..5f742a6807 100644 --- a/test/chemistry/test_thermodynamics.py +++ b/test/chemistry/test_thermodynamics.py @@ -1,15 +1,25 @@ -import unittest - -import numpy as np - -import chemistry.code.test.test_data as td +# 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. +import unittest from functools import partial -from chemistry.code.partition_function import DiatomicPartitionFunction +import chemistry.code.test.test_data as td +import chemistry.code.thermodynamics as thermo +import numpy as np from chemistry.code.molecule import Molecule from chemistry.code.morse_potential import MorsePotential -import chemistry.code.thermodynamics as thermo +from chemistry.code.partition_function import DiatomicPartitionFunction + # TODO Fix this test @@ -23,14 +33,14 @@ def create_test_molecule(self): spins=[1 / 2, 1]) return m - def performTest(self, thermoFuncName, benchmark): + def perform_test(self, thermo_func_name, benchmark): pf_callable = self.pf_callable td_class = self.td_class temps = np.array([10, 50, 100, 200]) - from_df_callable = getattr(thermo, thermoFuncName)(pf_callable, temps) - from_class = getattr(td_class, thermoFuncName)(temps) + from_df_callable = getattr(thermo, thermo_func_name)(pf_callable, temps) + from_class = getattr(td_class, thermo_func_name)(temps) np.testing.assert_array_equal(from_class, from_df_callable) np.testing.assert_array_almost_equal(from_class, benchmark) @@ -38,68 +48,59 @@ def performTest(self, thermoFuncName, benchmark): def test_thermo(self): m = self.create_test_molecule() - M = MorsePotential(m) + morse = MorsePotential(m) xdata = np.array(td.xdata_angstrom) ydata = np.array(td.ydata_hartree) - M.fit_to_data(xdata, ydata) + morse.fit_to_data(xdata, ydata) - P = DiatomicPartitionFunction(m, M, M) + partition_function = DiatomicPartitionFunction(m, morse, morse) pressure = 102523 - self.pf_callable = P.get_default_callable(pressure=pressure) - self.td_class = thermo.Thermodynamics(P, pressure) + self.pf_callable = partition_function.get_default_callable(pressure=pressure) + self.td_class = thermo.Thermodynamics(partition_function, pressure) - benchmark = np.array([22129.20266631, 19079.17137585, - 13863.05240428, 1405.13250492]) - self.performTest('helmholtz_free_energy', benchmark) + benchmark = np.array([22129.20266631, 19079.17137585, 13863.05240428, 1405.13250492]) + self.perform_test('helmholtz_free_energy', benchmark) - benchmark = np.array([22567.29835223, 23309.38074915, - 24363.27230243, 26445.90231315]) - self.performTest('thermodynamic_energy', benchmark) + benchmark = np.array([22567.29835223, 23309.38074915, 24363.27230243, 26445.90231315]) + self.perform_test('thermodynamic_energy', benchmark) - benchmark = np.array([43.80956859, 84.60418747, - 105.00219898, 125.20384904]) - self.performTest('entropy', benchmark) + benchmark = np.array([43.80956859, 84.60418747, 105.00219898, 125.20384904]) + self.perform_test('entropy', benchmark) - benchmark = np.array([22650.44297369, 23725.10385642, - 25194.71851698, 28108.79474224]) - self.performTest('enthalpy', benchmark) + benchmark = np.array([22650.44297369, 23725.10385642, 25194.71851698, 28108.79474224]) + self.perform_test('enthalpy', benchmark) - benchmark = np.array([22212.34728776, 19494.89448313, - 14694.49861883, 3068.02493402]) - self.performTest('gibbs_free_energy', benchmark) + benchmark = np.array([22212.34728776, 19494.89448313, 14694.49861883, 3068.02493402]) + self.perform_test('gibbs_free_energy', benchmark) - benchmark = np.array([12.52720256, 21.50674114, - 20.88104447, 20.80339556]) - self.performTest('constant_volume_heat_capacity', benchmark) + benchmark = np.array([12.52720256, 21.50674114, 20.88104447, 20.80339556]) + self.perform_test('constant_volume_heat_capacity', benchmark) - benchmark = np.array([20.84166472, 29.8212033, - 29.19550663, 29.11785772]) - self.performTest('constant_pressure_heat_capacity', benchmark) + benchmark = np.array([20.84166472, 29.8212033, 29.19550663, 29.11785772]) + self.perform_test('constant_pressure_heat_capacity', benchmark) def test_non_differentiable_callable(self): m = self.create_test_molecule() - M = MorsePotential(m) + morse = MorsePotential(m) xdata = np.array(td.xdata_angstrom) ydata = np.array(td.ydata_hartree) - M.fit_to_data(xdata, ydata) + morse.fit_to_data(xdata, ydata) - P = DiatomicPartitionFunction(m, M, M) + partition_function = DiatomicPartitionFunction(m, morse, morse) pressure = 102523 temps = np.array([10, 50, 100, 200]) - vib = P.get_partition(part='vib', - pressure=pressure) - benchmark = np.array([22442.53040649, 22442.53040649, - 22442.53040649, 22442.53040672]) + vib = partition_function.get_partition(part='vib', pressure=pressure) + benchmark = np.array([22442.53040649, 22442.53040649, 22442.53040649, 22442.53040672]) diff = thermo.thermodynamic_energy(vib, temps) - n_diff = thermo.thermodynamic_energy(lambda T: vib(T), temps) + n_diff = thermo.thermodynamic_energy(lambda t_value: vib(t_value), temps) np.testing.assert_array_almost_equal(diff, benchmark) np.testing.assert_array_almost_equal(diff, n_diff, decimal=5) From bd9282250a354113eaedf5b0185cdd098d1f9d7f Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 00:49:32 +0100 Subject: [PATCH 160/197] linting --- test/chemistry/test_partition_function.py | 45 +++++++++++------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/test/chemistry/test_partition_function.py b/test/chemistry/test_partition_function.py index 93caa1c731..a7f9f1eba6 100644 --- a/test/chemistry/test_partition_function.py +++ b/test/chemistry/test_partition_function.py @@ -13,15 +13,15 @@ import unittest from functools import partial -import chemistry.code.test.test_data as td import numpy as np -from chemistry.code.molecule import Molecule -from chemistry.code.morse_potential import MorsePotential from chemistry.code.partition_function import DiatomicPartitionFunction from chemistry.code.partition_function import DifferentiableFunction +from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential +# TODO Fix this test +from qiskit.chemistry.drivers import Molecule +import test.chemistry.test_data_potentials as td -# TODO Fix this test class TestPartitionFunction(unittest.TestCase): def create_test_molecule(self): @@ -36,33 +36,30 @@ def create_test_molecule(self): def test_partition_function(self): m = self.create_test_molecule() - M = MorsePotential(m) + morse = MorsePotential(m) xdata = np.array(td.xdata_angstrom) ydata = np.array(td.ydata_hartree) - M.fit_to_data(xdata, ydata) + morse.fit_to_data(xdata, ydata) - P = DiatomicPartitionFunction(m, M, M) + partition_function = DiatomicPartitionFunction(m, morse, morse) pressure = 102523 temps = np.array([10, 50, 100, 200]) - trans = P.get_partition(part="trans", pressure=pressure) + trans = partition_function.get_partition(part="trans", pressure=pressure) with self.assertWarns(RuntimeWarning): - P.get_partition(part="trans", split='para', pressure=pressure) - log_trans = np.array( - [3.76841035, 7.79200513, 9.52487308, 11.25774103]) + partition_function.get_partition(part="trans", split='para', pressure=pressure) + log_trans = np.array([3.76841035, 7.79200513, 9.52487308, 11.25774103]) np.testing.assert_array_almost_equal(log_trans, np.log(trans(temps))) - vib = P.get_partition(part="vib", pressure=pressure) - log_vib = np.array( - [-269.92161476, -53.98432295, -26.99216148, -13.49608074]) + vib = partition_function.get_partition(part="vib", pressure=pressure) + log_vib = np.array([-269.92161476, -53.98432295, -26.99216148, -13.49608074]) np.testing.assert_array_almost_equal(log_vib, np.log(vib(temps))) - rot = P.get_partition(part="rot", pressure=pressure) - log_rot = np.array( - [5.63843874e-05, 2.98377556e-01, 7.93867745e-01, 1.39334663e+00]) + rot = partition_function.get_partition(part="rot", pressure=pressure) + log_rot = np.array([5.63843874e-05, 2.98377556e-01, 7.93867745e-01, 1.39334663e+00]) np.testing.assert_array_almost_equal(log_rot, np.log(rot(temps))) def test_differentiable_function(self): @@ -72,14 +69,14 @@ def f(x, c): def df(x, c): return 3 * x ** 2 + 2 * np.exp(2 * x) + 1 / x + np.cos(x) - F = DifferentiableFunction(f, argument_name='x') - F_analytic = DifferentiableFunction(f, derivative=df) - F_dc = DifferentiableFunction(f, argument_name='c') + fun = DifferentiableFunction(f, argument_name='x') + fun_analytic = DifferentiableFunction(f, derivative=df) + fun_dc = DifferentiableFunction(f, argument_name='c') - self.assertEqual(F(1, 2), f(1, 2)) - self.assertAlmostEqual(F.D(1, 2), df(1, 2), places=6) - self.assertEqual(F_analytic.D(1, 2), df(1, 2)) - self.assertAlmostEqual(F_dc.D(1, 2), 1.0) + self.assertEqual(fun(1, 2), f(1, 2)) + self.assertAlmostEqual(fun.D(1, 2), df(1, 2), places=6) + self.assertEqual(fun_analytic.D(1, 2), df(1, 2)) + self.assertAlmostEqual(fun_dc.D(1, 2), 1.0) if __name__ == '__main__': From e90ecf4a87a0700dd9aa3b5ff5909dc08d9d491a Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 14 Oct 2020 20:54:34 -0400 Subject: [PATCH 161/197] fix copyright --- qiskit/chemistry/constants.py | 5 ++--- test/chemistry/test_bopes_sampler.py | 4 +--- test/chemistry/test_fd_vibronic.py | 9 ++------- .../test_fermionic_transformation_symmetries.py | 8 +------- test/chemistry/test_partition_function.py | 5 +---- test/chemistry/test_potential.py | 2 -- 6 files changed, 7 insertions(+), 26 deletions(-) diff --git a/qiskit/chemistry/constants.py b/qiskit/chemistry/constants.py index 08ed22bce8..b81dae0000 100644 --- a/qiskit/chemistry/constants.py +++ b/qiskit/chemistry/constants.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- - # This code is part of Qiskit. # -# (C) Copyright IBM 2019, 2020. +# (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 @@ -11,6 +9,7 @@ # 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. + """ Defines some constants used in chemical calculations. """ diff --git a/test/chemistry/test_bopes_sampler.py b/test/chemistry/test_bopes_sampler.py index 84d1e26f84..8a3de3596f 100644 --- a/test/chemistry/test_bopes_sampler.py +++ b/test/chemistry/test_bopes_sampler.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- - # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2020. +# (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 diff --git a/test/chemistry/test_fd_vibronic.py b/test/chemistry/test_fd_vibronic.py index 677e48ee99..04ec39b0a6 100644 --- a/test/chemistry/test_fd_vibronic.py +++ b/test/chemistry/test_fd_vibronic.py @@ -14,15 +14,10 @@ import unittest from functools import partial +import test.chemistry.test_data_potentials as td import numpy as np -from chemistry.code.vibronic_structure_fd import VibronicStructure1DFD -import chemistry.code.test.test_data as td -from chemistry.code.morse_potential import MorsePotential -from chemistry.code.harmonic_potential import HarmonicPotential -from chemistry.code.molecule import Molecule -from chemistry.code.vibronic_structure_fd import VibronicStructure1DFD -import test.chemistry.test_data_potentials as td +from chemistry.code.vibronic_structure_fd import VibronicStructure1DFD from qiskit.chemistry.algorithms.pes_samplers.potentials.harmonic_potential import HarmonicPotential from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential from qiskit.chemistry.drivers import Molecule diff --git a/test/chemistry/test_fermionic_transformation_symmetries.py b/test/chemistry/test_fermionic_transformation_symmetries.py index 6dea353b94..4981e57b16 100644 --- a/test/chemistry/test_fermionic_transformation_symmetries.py +++ b/test/chemistry/test_fermionic_transformation_symmetries.py @@ -13,25 +13,19 @@ """ Test Core Hamiltonian Symmetry Reduction """ import unittest - +from test.chemistry import QiskitChemistryTestCase import numpy as np from qiskit import BasicAer from qiskit.aqua.algorithms import NumPyMinimumEigensolver, VQE from qiskit.aqua.components.optimizers import SLSQP from qiskit.chemistry import QiskitChemistryError -from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver from qiskit.chemistry.components.initial_states import HartreeFock from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.core import TransformationType, QubitMappingType -from qiskit.chemistry.transformations import FermionicTransformation from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.transformations import FermionicTransformation -from test.chemistry import QiskitChemistryTestCase - - -# from qiskit.chemistry.ground_state_calculation import MinimumEigensolverGroundStateCalculation class TestFermionicTransformationSymmetries(QiskitChemistryTestCase): diff --git a/test/chemistry/test_partition_function.py b/test/chemistry/test_partition_function.py index abd4c4b371..57ac370471 100644 --- a/test/chemistry/test_partition_function.py +++ b/test/chemistry/test_partition_function.py @@ -14,17 +14,14 @@ import unittest from functools import partial +import test.chemistry.test_data_potentials as td import numpy as np -import chemistry.code.test.test_data as td - from chemistry.code.partition_function import DiatomicPartitionFunction from chemistry.code.partition_function import DifferentiableFunction from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential from qiskit.chemistry.drivers import Molecule -import test.chemistry.test_data_potentials as td - # TODO Fix this test diff --git a/test/chemistry/test_potential.py b/test/chemistry/test_potential.py index 1c6af9a62c..634a524f52 100644 --- a/test/chemistry/test_potential.py +++ b/test/chemistry/test_potential.py @@ -16,8 +16,6 @@ from functools import partial import numpy as np -import qiskit.chemistry.constants as const -from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential d from qiskit.chemistry.algorithms.pes_samplers.potentials.harmonic_potential import HarmonicPotential from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential from qiskit.chemistry.constants import HARTREE_TO_J_PER_MOL From 10c480d4b31b1b6a09eb53354c17fdaa89ea9fb6 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 14 Oct 2020 21:11:08 -0400 Subject: [PATCH 162/197] remove test oovqe --- test/chemistry/test_oovqe2.py~ | 149 --------------------------------- 1 file changed, 149 deletions(-) delete mode 100644 test/chemistry/test_oovqe2.py~ diff --git a/test/chemistry/test_oovqe2.py~ b/test/chemistry/test_oovqe2.py~ deleted file mode 100644 index 41934f2311..0000000000 --- a/test/chemistry/test_oovqe2.py~ +++ /dev/null @@ -1,149 +0,0 @@ -# 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 OOVQE ground state calculations """ -import unittest -from test.chemistry import QiskitChemistryTestCase - -from qiskit.chemistry.drivers import HDF5Driver -from qiskit.providers.basicaer import BasicAer -from qiskit.aqua import QuantumInstance -from qiskit.aqua.algorithms import VQE -from qiskit.chemistry.components.variational_forms import UCCSD -from qiskit.chemistry.components.initial_states import HartreeFock -from qiskit.aqua.components.optimizers import COBYLA -from qiskit.chemistry.algorithms.ground_state_solvers import VQEUCCSDFactory, OOVQE -from qiskit.chemistry.transformations import FermionicTransformation -from qiskit.chemistry.transformations.fermionic_transformation import QubitMappingType - - -class TestOOVQE(QiskitChemistryTestCase): - """ Test OOVQE Ground State Calculation. """ - - def setUp(self): - super().setUp() - - file1 = 'test_oovqe_h4.hdf5' - file2 = 'test_oovqe_lih.hdf5' - file3 = 'test_oovqe_h4_uhf.hdf5' - - self.driver1 = HDF5Driver(hdf5_input=self.get_resource_path(file1)) - self.driver2 = HDF5Driver(hdf5_input=self.get_resource_path(file2)) - self.driver3 = HDF5Driver(hdf5_input=self.get_resource_path(file3)) - - self.energy1_rotation = -3.0104 - self.energy1 = -2.77 # energy of the VQE with pUCCD ansatz and LBFGSB optimizer - self.energy2 = -7.70 - self.energy3 = -2.50 - self.initial_point1 = [0.039374, -0.47225463, -0.61891996, 0.02598386, 0.79045546, - -0.04134567, 0.04944946, -0.02971617, -0.00374005, 0.77542149] - - self.seed = 50 - - self.optimizer = COBYLA(maxiter=1) - self.transformation1 = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False) - self.transformation2 = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=True) - - def make_solver(self): - """ Instantiates a solver for the test of OOVQE. """ - - quantum_instance = QuantumInstance(BasicAer.get_backend('statevector_simulator'), - shots=1, - seed_simulator=self.seed, - seed_transpiler=self.seed) - solver = VQEUCCSDFactory(quantum_instance) - - def get_custom_solver(self, transformation): - """Customize the solver.""" - - 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) - # only paired doubles excitations - 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, - excitation_type='d', - same_spin_doubles=False, - method_doubles='pucc') - vqe = VQE(var_form=var_form, quantum_instance=self._quantum_instance, - optimizer=COBYLA(maxiter=1)) - return vqe - - # pylint: disable=no-value-for-parameter - solver.get_solver = get_custom_solver.__get__(solver, VQEUCCSDFactory) - return solver - - def test_orbital_rotations(self): - """ Test that orbital rotations are performed correctly. """ - - solver = self.make_solver() - calc = OOVQE(self.transformation1, solver, iterative_oo=False, - initial_point=self.initial_point1) - calc._vqe.optimizer.set_options(maxiter=1) - algo_result = calc.solve(self.driver1) - self.assertAlmostEqual(algo_result.computed_electronic_energy, self.energy1_rotation, 4) - - # def test_oovqe(self): - # """ Test the simultaneous optimization of orbitals and ansatz parameters with OOVQE using - # BasicAer's statevector_simulator. """ - # - # solver = self.make_solver() - # calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=False, - # initial_point=self.initial_point1) - # calc._vqe.optimizer.set_options(maxiter=3, rhobeg=0.01) - # algo_result = calc.solve(self.driver1) - # self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1, 4) - # - # def test_iterative_oovqe(self): - # """ Test the iterative OOVQE using BasicAer's statevector_simulator. """ - # - # solver = self.make_solver() - # calc = OOVQE(self.transformation1, solver, self.driver1, iterative_oo=True, - # initial_point=self.initial_point1, iterative_oo_iterations=2) - # calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) - # algo_result = calc.solve(self.driver1) - # self.assertLessEqual(algo_result.computed_electronic_energy, self.energy1) - # - # def test_oovqe_with_frozen_core(self): - # """ Test the OOVQE with frozen core approximation. """ - # - # solver = self.make_solver() - # calc = OOVQE(self.transformation2, solver, self.driver2, iterative_oo=False) - # calc._vqe.optimizer.set_options(maxiter=2, rhobeg=1) - # algo_result = calc.solve(self.driver2) - # self.assertLessEqual(algo_result.computed_electronic_energy + - # self.transformation2._energy_shift + - # self.transformation2._nuclear_repulsion_energy, self.energy2) - # - # def test_oovqe_with_unrestricted_hf(self): - # """ Test the OOVQE with unrestricted HF method. """ - # - # solver = self.make_solver() - # calc = OOVQE(self.transformation1, solver, self.driver3, iterative_oo=False) - # calc._vqe.optimizer.set_options(maxiter=2, rhobeg=0.01) - # algo_result = calc.solve(self.driver3) - # self.assertLessEqual(algo_result.computed_electronic_energy, self.energy3) - - -if __name__ == '__main__': - unittest.main() From 219e77045fdcc0fbc54f8013b79aa8ad517623bb Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 10:03:13 +0200 Subject: [PATCH 163/197] remove redundant tests --- test/chemistry/test_fd_vibronic.py | 78 -------------- test/chemistry/test_partition_function.py | 90 ---------------- test/chemistry/test_thermodynamics.py | 119 ---------------------- 3 files changed, 287 deletions(-) delete mode 100644 test/chemistry/test_fd_vibronic.py delete mode 100644 test/chemistry/test_partition_function.py delete mode 100644 test/chemistry/test_thermodynamics.py diff --git a/test/chemistry/test_fd_vibronic.py b/test/chemistry/test_fd_vibronic.py deleted file mode 100644 index 04ec39b0a6..0000000000 --- a/test/chemistry/test_fd_vibronic.py +++ /dev/null @@ -1,78 +0,0 @@ -# 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 fd vibronic """ - -import unittest -from functools import partial -import test.chemistry.test_data_potentials as td -import numpy as np - -from chemistry.code.vibronic_structure_fd import VibronicStructure1DFD -from qiskit.chemistry.algorithms.pes_samplers.potentials.harmonic_potential import HarmonicPotential -from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential -from qiskit.chemistry.drivers import Molecule - -# TODO Fix this test - - -class TestFDVibronic(unittest.TestCase): - """ Test fd vibronic """ - def create_test_molecule(self): - """ create test molecule """ - stretch = partial(Molecule.absolute_stretching, - kwargs={'atom_pair': (1, 0)}) - m = Molecule(geometry=[['H', [0., 0., 0.]], ['D', [0., 0., 1.]]], - degrees_of_freedom=[stretch], - masses=[1.6735328E-27, 3.444946E-27], - spins=[1 / 2, 1]) - return m - - def test_with_morse(self): - """ test with morse """ - m = self.create_test_molecule() - - morse = MorsePotential(m) - - xdata = np.array(td.xdata_angstrom) - ydata = np.array(td.ydata_hartree) - - morse.fit_to_data(xdata, ydata) - - vibronic_structure = VibronicStructure1DFD(m, morse) - - levels = np.array(range(2, 8)) - vib_levels = vibronic_structure.vibrational_energy_level(levels) - vib_levels_ref = morse.vibrational_energy_level(levels) - np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, decimal=5) - - def test_with_harmonic(self): - """ test with harmonic """ - m = self.create_test_molecule() - - harmonic = HarmonicPotential(m) - - xdata = np.array(td.xdata_angstrom) - ydata = np.array(td.ydata_hartree) - - harmonic.fit_to_data(xdata, ydata) - - vibronic_structure = VibronicStructure1DFD(m, harmonic) - - levels = np.array(range(2, 8)) - vib_levels = vibronic_structure.vibrational_energy_level(levels) - vib_levels_ref = harmonic.vibrational_energy_level(levels) - np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, decimal=4) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/chemistry/test_partition_function.py b/test/chemistry/test_partition_function.py deleted file mode 100644 index 57ac370471..0000000000 --- a/test/chemistry/test_partition_function.py +++ /dev/null @@ -1,90 +0,0 @@ -# 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 Partition Function """ - -import unittest -from functools import partial -import test.chemistry.test_data_potentials as td -import numpy as np - -from chemistry.code.partition_function import DiatomicPartitionFunction -from chemistry.code.partition_function import DifferentiableFunction -from qiskit.chemistry.algorithms.pes_samplers.potentials.morse_potential import MorsePotential -from qiskit.chemistry.drivers import Molecule - -# TODO Fix this test - - -class TestPartitionFunction(unittest.TestCase): - """ Test Partition Function """ - def create_test_molecule(self): - """ create test molecule """ - stretch = partial(Molecule.absolute_stretching, - kwargs={'atom_pair': (1, 0)}) - m = Molecule(geometry=[['H', [0., 0., 0.]], ['D', [0., 0., 1.]]], - degrees_of_freedom=[stretch], - masses=[1.6735328E-27, 3.444946E-27], - spins=[1 / 2, 1]) - return m - - def test_partition_function(self): - """ test partition function """ - m = self.create_test_molecule() - - morse = MorsePotential(m) - - xdata = np.array(td.xdata_angstrom) - ydata = np.array(td.ydata_hartree) - - morse.fit_to_data(xdata, ydata) - - partition_function = DiatomicPartitionFunction(m, morse, morse) - - pressure = 102523 - temps = np.array([10, 50, 100, 200]) - - trans = partition_function.get_partition(part="trans", pressure=pressure) - with self.assertWarns(RuntimeWarning): - P.get_partition(part="trans", split='para', pressure=pressure) - log_trans = np.array( - [3.76841035, 7.79200513, 9.52487308, 11.25774103]) - np.testing.assert_array_almost_equal(log_trans, np.log(trans(temps))) - - vib = P.get_partition(part="vib", pressure=pressure) - log_vib = np.array([-269.92161476, -53.98432295, -26.99216148, -13.49608074]) - - np.testing.assert_array_almost_equal(log_vib, np.log(vib(temps))) - - rot = partition_function.get_partition(part="rot", pressure=pressure) - log_rot = np.array([5.63843874e-05, 2.98377556e-01, 7.93867745e-01, 1.39334663e+00]) - np.testing.assert_array_almost_equal(log_rot, np.log(rot(temps))) - - def test_differentiable_function(self): - def f(x, c): - return x ** 3 + np.exp(2 * x) + np.log(x) + np.sin(x) + c - - def df(x, c): - return 3 * x ** 2 + 2 * np.exp(2 * x) + 1 / x + np.cos(x) - - fun = DifferentiableFunction(f, argument_name='x') - fun_analytic = DifferentiableFunction(f, derivative=df) - fun_dc = DifferentiableFunction(f, argument_name='c') - - self.assertEqual(fun(1, 2), f(1, 2)) - self.assertAlmostEqual(fun.D(1, 2), df(1, 2), places=6) - self.assertEqual(fun_analytic.D(1, 2), df(1, 2)) - self.assertAlmostEqual(fun_dc.D(1, 2), 1.0) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/chemistry/test_thermodynamics.py b/test/chemistry/test_thermodynamics.py deleted file mode 100644 index 812b3b38d1..0000000000 --- a/test/chemistry/test_thermodynamics.py +++ /dev/null @@ -1,119 +0,0 @@ -# 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 Thermodynamics """ - -import unittest -from functools import partial -import numpy as np - -import chemistry.code.test.test_data as td -from chemistry.code.partition_function import DiatomicPartitionFunction - -from chemistry.code.molecule import Molecule -from chemistry.code.morse_potential import MorsePotential -from chemistry.code.partition_function import DiatomicPartitionFunction - - -# TODO Fix this test - - -class TestThermodynamics(unittest.TestCase): - """ Test Thermodynamics """ - def create_test_molecule(self): - """ create test molecule""" - stretch = partial(Molecule.absolute_stretching, - kwargs={'atom_pair': (1, 0)}) - m = Molecule(geometry=[['H', [0., 0., 0.]], ['D', [0., 0., 1.]]], - degrees_of_freedom=[stretch], - masses=[1.6735328E-27, 3.444946E-27], - spins=[1 / 2, 1]) - return m - - def perform_test(self, thermo_func_name, benchmark): - """ perform test """ - pf_callable = self.pf_callable - td_class = self.td_class - - temps = np.array([10, 50, 100, 200]) - - from_df_callable = getattr(thermo, thermo_func_name)(pf_callable, temps) - from_class = getattr(td_class, thermo_func_name)(temps) - - np.testing.assert_array_equal(from_class, from_df_callable) - np.testing.assert_array_almost_equal(from_class, benchmark) - - def test_thermo(self): - """ test thermo""" - m = self.create_test_molecule() - - morse = MorsePotential(m) - - xdata = np.array(td.xdata_angstrom) - ydata = np.array(td.ydata_hartree) - - morse.fit_to_data(xdata, ydata) - - partition_function = DiatomicPartitionFunction(m, morse, morse) - - pressure = 102523 - - self.pf_callable = partition_function.get_default_callable(pressure=pressure) - self.td_class = thermo.Thermodynamics(partition_function, pressure) - - benchmark = np.array([22129.20266631, 19079.17137585, 13863.05240428, 1405.13250492]) - self.perform_test('helmholtz_free_energy', benchmark) - - benchmark = np.array([22567.29835223, 23309.38074915, 24363.27230243, 26445.90231315]) - self.perform_test('thermodynamic_energy', benchmark) - - benchmark = np.array([43.80956859, 84.60418747, 105.00219898, 125.20384904]) - self.perform_test('entropy', benchmark) - - benchmark = np.array([22650.44297369, 23725.10385642, 25194.71851698, 28108.79474224]) - self.perform_test('enthalpy', benchmark) - - benchmark = np.array([22212.34728776, 19494.89448313, 14694.49861883, 3068.02493402]) - self.perform_test('gibbs_free_energy', benchmark) - - benchmark = np.array([12.52720256, 21.50674114, 20.88104447, 20.80339556]) - self.perform_test('constant_volume_heat_capacity', benchmark) - - benchmark = np.array([20.84166472, 29.8212033, 29.19550663, 29.11785772]) - self.perform_test('constant_pressure_heat_capacity', benchmark) - - def test_non_differentiable_callable(self): - """ test non differentiable callable """ - m = self.create_test_molecule() - - morse = MorsePotential(m) - - xdata = np.array(td.xdata_angstrom) - ydata = np.array(td.ydata_hartree) - - morse.fit_to_data(xdata, ydata) - - partition_function = DiatomicPartitionFunction(m, morse, morse) - - pressure = 102523 - temps = np.array([10, 50, 100, 200]) - vib = partition_function.get_partition(part='vib', pressure=pressure) - benchmark = np.array([22442.53040649, 22442.53040649, 22442.53040649, 22442.53040672]) - diff = thermo.thermodynamic_energy(vib, temps) - n_diff = thermo.thermodynamic_energy(lambda t_value: vib(t_value), temps) - - np.testing.assert_array_almost_equal(diff, benchmark) - np.testing.assert_array_almost_equal(diff, n_diff, decimal=5) - - -if __name__ == '__main__': - unittest.main() From 03010650f0056f20b7e4aba450559249e16b4fa1 Mon Sep 17 00:00:00 2001 From: Stefan Woerner Date: Thu, 15 Oct 2020 11:08:11 +0200 Subject: [PATCH 164/197] BOPESSampler refactoring (renaming) --- .../algorithms/pes_samplers/bopes_sampler.py | 18 ++++-------------- .../pes_samplers/energy_surface_spline.py | 6 +++--- .../potentials/harmonic_potential.py | 6 +++--- .../pes_samplers/potentials/morse_potential.py | 8 ++++---- .../pes_samplers/potentials/potential_base.py | 8 ++++---- test/chemistry/test_bopes_sampler.py | 6 +++--- test/chemistry/test_potential.py | 5 +++-- 7 files changed, 24 insertions(+), 33 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py index fea01bdd9e..e36238158c 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py +++ b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py @@ -21,8 +21,7 @@ from qiskit.chemistry.drivers import BaseDriver from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateSolver from qiskit.chemistry.results.bopes_sampler_result import BOPESSamplerResult -from .energy_surface_spline import EnergySurfaceBase -from .extrapolator import Extrapolator, WindowExtrapolator +from qiskit.chemistry.algorithms.pes_samplers.extrapolator import Extrapolator, WindowExtrapolator from qiskit.chemistry.results import EigenstateResult logger = logging.getLogger(__name__) @@ -87,7 +86,7 @@ def __init__(self, # this will be used when NOT bootstrapping self._initial_point = self._gsc.solver.initial_point - def sample_surface(self, driver: BaseDriver, points: List[float]) -> BOPESSamplerResult: + def sample(self, driver: BaseDriver, points: List[float]) -> BOPESSamplerResult: """Run the sampler at the given points, potentially with repetitions. Args: @@ -108,7 +107,7 @@ def sample_surface(self, driver: BaseDriver, points: List[float]) -> BOPESSample raise AquaError('Please provide a molecule') # full dictionary of points - self._raw_results = self.run_points(points) + self._raw_results = self._run_points(points) # create results dictionary with (point, energy) self._points = list(self._raw_results.keys()) self._energies = [] @@ -121,7 +120,7 @@ def sample_surface(self, driver: BaseDriver, points: List[float]) -> BOPESSample return result - def run_points(self, points: List[float]) -> Dict[float, EigenstateResult]: + def _run_points(self, points: List[float]) -> Dict[float, EigenstateResult]: """Run the sampler at the given points. Args: @@ -195,12 +194,3 @@ def _run_single_point(self, point: float) -> EigenstateResult: self._points_optparams[point] = optimal_params return result - - def fit_to_surface(self, energy_surface: EnergySurfaceBase, **kwargs) -> None: - """Fit the sampled energy points to the energy surface. - - Args: - energy_surface: An energy surface object. - **kwargs: Arguments to pass through to the potential's ``fit_to_data`` function. - """ - energy_surface.fit_to_data(xdata=self._points, ydata=self._energies, **kwargs) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index 001a0bb174..b5f27b2cb3 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -19,7 +19,7 @@ import scipy.interpolate as interp from scipy.optimize import minimize_scalar -from .potentials.potential_base import EnergySurfaceBase +from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import EnergySurfaceBase class EnergySurface1DSpline(EnergySurfaceBase): @@ -56,7 +56,7 @@ def eval(self, x): ''' return result - def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None): + def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): # TODO: remove, no need for duplicate checking # newx = np.unique(xdata) # new y is average of all repeated values @@ -102,6 +102,6 @@ def get_trust_region(self): surface implementation can be trusted. When doing spline interpolation, for example, that would be the region where data is interpolated (vs. extrapolated) from the arguments of - fit_to_data(). + fit(). """ return (self.x_left, self.x_right) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 63b34f5437..dc49c8e076 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -19,7 +19,7 @@ from scipy.optimize import curve_fit import qiskit.chemistry.constants as const -from .potential_base import PotentialBase +from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import PotentialBase class HarmonicPotential(PotentialBase): @@ -81,8 +81,8 @@ def update_molecule(self, molecule): self._mA = molecule.masses[0] self._mB = molecule.masses[1] - def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None, - preprocess_data=True): + def fit(self, xdata, ydata, initial_vals=None, bounds_list=None, + preprocess_data=True): """ Fits a potential to computed molecular energies. diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index ada096f97b..f64285187e 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -18,7 +18,7 @@ import numpy as np from scipy.optimize import curve_fit -from .potential_base import PotentialBase +from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import PotentialBase import qiskit.chemistry.constants as const @@ -34,7 +34,7 @@ def __init__(self, molecule): """ Constructor. Initializes the potential to the zero-function. - fit_to_data() should be used afterwards to fit the potential to + fit() should be used afterwards to fit the potential to computed molecular energies. Args: @@ -45,7 +45,7 @@ def __init__(self, molecule): """ super().__init__(molecule) # Initialize with zero-potential. - # Later - fit energy values (fit_to_data) + # Later - fit energy values (fit) self.d_e = 0.0 self.m_shift = 0.0 self.alpha = 0.0 @@ -83,7 +83,7 @@ def update_molecule(self, molecule): self._mA = molecule.masses[0] self._mB = molecule.masses[1] - def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None): + def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): """ Fits a potential to computed molecular energies. diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index a8d1ca807d..aa29b2c344 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -40,7 +40,7 @@ def eval(self, x): raise NotImplementedError @abstractmethod - def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None): + def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): """ Fit the surface to data - x are coordinates, y is energy.""" raise NotImplementedError @@ -69,7 +69,7 @@ def get_trust_region(self): surface implementation can be trusted. When doing spline interpolation, for example, that would be the region where data is interpolated (vs. extrapolated) from the arguments of - fit_to_data(). + fit(). """ raise NotImplementedError @@ -170,9 +170,9 @@ def eval(self, x): """ return self.energy_surface.eval(x) - def fit_to_data(self, xdata, ydata, initial_vals=None, bounds_list=None): + def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): """ Fit the surface to data - x are coordinates, y is energy.""" - return self.energy_surface.fit_to_data(xdata, ydata, initial_vals, + return self.energy_surface.fit(xdata, ydata, initial_vals, bounds_list) def get_equilibrium_geometry(self, scaling=1.0): diff --git a/test/chemistry/test_bopes_sampler.py b/test/chemistry/test_bopes_sampler.py index 8a3de3596f..4f2469817a 100644 --- a/test/chemistry/test_bopes_sampler.py +++ b/test/chemistry/test_bopes_sampler.py @@ -89,7 +89,7 @@ def test_h2_bopes_sampler(self): # absolute internuclear distance in Angstrom points = [0.7, 1.0, 1.3] - results = sampler.sample_surface(driver, points) + results = sampler.sample(driver, points) points_run = results.points energies = results.energies @@ -122,11 +122,11 @@ def test_potential_interface(self): points = np.arange(0.45, 5.3, 0.3) sampler = BOPESSampler(gsc=me_gsc) - sampler.sample_surface(driver, points) + res = sampler.sample(driver, points) # Testing Potential interface pot = MorsePotential(m) - sampler.fit_to_surface(pot) + pot.fit(res.points, res.energies) np.testing.assert_array_almost_equal([pot.alpha, pot.r_0], [2.235, 0.720], decimal=3) np.testing.assert_array_almost_equal([pot.d_e, pot.m_shift], [0.2107, -1.1419], decimal=3) diff --git a/test/chemistry/test_potential.py b/test/chemistry/test_potential.py index 634a524f52..7fd18f8efe 100644 --- a/test/chemistry/test_potential.py +++ b/test/chemistry/test_potential.py @@ -24,6 +24,7 @@ class TestPotential(unittest.TestCase): """ Test Potential """ + def create_test_molecule(self): """ create test molecule """ stretch = partial(Molecule.absolute_stretching, @@ -72,7 +73,7 @@ def test_morse(self): xdata = np.array(self.xdata_angstrom) ydata = np.array(self.ydata_hartree) - morse.fit_to_data(xdata, ydata) + morse.fit(xdata, ydata) # self.plot_potential(xdata, ydata, M) @@ -136,7 +137,7 @@ def test_harmonic(self): xdata = np.array(self.xdata_angstrom) ydata = np.array(self.ydata_hartree) - harmonic.fit_to_data(xdata, ydata) + harmonic.fit(xdata, ydata) # self.plot_potential(xdata, ydata, H) From 102b68e5518812cc8944d064f3b4b53e125c4382 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 11:37:42 +0200 Subject: [PATCH 165/197] remove duplicate test --- ...est_fermionic_transformation_symmetries.py | 240 ------------------ 1 file changed, 240 deletions(-) delete mode 100644 test/chemistry/test_fermionic_transformation_symmetries.py diff --git a/test/chemistry/test_fermionic_transformation_symmetries.py b/test/chemistry/test_fermionic_transformation_symmetries.py deleted file mode 100644 index 4981e57b16..0000000000 --- a/test/chemistry/test_fermionic_transformation_symmetries.py +++ /dev/null @@ -1,240 +0,0 @@ -# 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 Core Hamiltonian Symmetry Reduction """ - -import unittest -from test.chemistry import QiskitChemistryTestCase -import numpy as np - -from qiskit import BasicAer -from qiskit.aqua.algorithms import NumPyMinimumEigensolver, 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.core import TransformationType, QubitMappingType -from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver -from qiskit.chemistry.drivers import PySCFDriver, UnitsType -from qiskit.chemistry.transformations import FermionicTransformation - - -class TestFermionicTransformationSymmetries(QiskitChemistryTestCase): - """ Core hamiltonian Driver symmetry 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_result(self, result, symm=True): - self.assertAlmostEqual(result.energy, -7.882324378883, places=3) - ref_dipole = (0.0, 0.0, -1.81741795) - if not symm: - np.testing.assert_almost_equal(result.dipole_moment, ref_dipole, decimal=2) - else: - self.assertIsNone(result.dipole_moment[0]) - self.assertIsNone(result.dipole_moment[1]) - self.assertAlmostEqual(result.dipole_moment[2], ref_dipole[2], places=2) - - def test_no_symmetry(self): - """ No symmetry reduction """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=None) - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 12) - solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.solve(self.driver) - self._validate_result(result, False) - - def test_auto_symmetry(self): - """ Auto symmetry reduction """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction='auto') - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 8) - solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.solve(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) - - def test_given_symmetry(self): - """ Supplied symmetry reduction """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 1, 1, 1]) - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 8) - solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.solve(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1, 1, 1]) - - def test_given_symmetry_fail_len(self): - """ Supplied symmetry reduction invalid len """ - with self.assertRaises(QiskitChemistryError): - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 1, 1]) - - _, _ = fermionic_transformation.transform(self.driver) - - def test_given_symmetry_fail_values(self): - """ Supplied symmetry reduction invalid values """ - with self.assertRaises(QiskitChemistryError): - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=None, - z2symmetry_reduction=[1, 0, 1, 1]) - - _, _ = fermionic_transformation.transform(self.driver) - - def test_auto_symmetry_freeze_core(self): - """ Auto symmetry reduction, with freeze core """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.solve(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) - - def test_auto_freeze_core_parity(self): - """ Auto symmetry reduction, with freeze core and parity mapping """ - - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.solve(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.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 """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.solve(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) - - def test_auto_ph_freeze_core_parity_2(self): - """ Auto symmetry reduction, with freeze core, parity and two q reduction """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.PARTICLE_HOLE, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.solve(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [1, 1]) - - def test_vqe_auto_symmetry_freeze_core(self): - """ Auto symmetry reduction, with freeze core using VQE """ - fermionic_transformation = \ - FermionicTransformation(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False, - freeze_core=True, - orbital_reduction=None, - z2symmetry_reduction='auto') - - qubit_op, _ = fermionic_transformation.transform(self.driver) - self.assertEqual(qubit_op.num_qubits, 6) - num_orbitals = fermionic_transformation._molecule_info['num_orbitals'] - num_particles = fermionic_transformation._molecule_info['num_particles'] - qubit_mapping = 'jordan_wigner' - two_qubit_reduction = fermionic_transformation._two_qubit_reduction - z2_symmetries = qubit_op.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) - - solver = VQE(var_form=var_form, optimizer=SLSQP(maxiter=500), - quantum_instance=BasicAer.get_backend('statevector_simulator')) - gsc = GroundStateEigensolver(fermionic_transformation, solver) - result = gsc.solve(self.driver) - self._validate_result(result) - self.assertEqual(qubit_op.z2_symmetries.tapering_values, [-1, 1, 1, -1]) - - -if __name__ == '__main__': - unittest.main() From 52ea961b7055e333f8e880789185f16ec9d43195 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 13:14:39 +0200 Subject: [PATCH 166/197] clean up --- .../pes_samplers/energy_surface_spline.py | 20 +--- .../potentials/harmonic_potential.py | 6 +- .../potentials/morse_potential.py | 8 +- .../pes_samplers/potentials/potential_base.py | 95 +------------------ 4 files changed, 8 insertions(+), 121 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index b5f27b2cb3..8e0d82d48e 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -11,9 +11,7 @@ # that they have been altered from the originals. """ -Created on Mon Mar 30 10:30:21 2020 - -@author: dtrenev +An spline interpolation method for data fitting """ import scipy.interpolate as interp @@ -30,7 +28,6 @@ class EnergySurface1DSpline(EnergySurfaceBase): def __init__(self): """ Constructor. - Initializes the class with a molecule. """ self._eval = None self.eval_d = None @@ -46,22 +43,11 @@ def eval(self, x): """ assert self._eval is not None result = self._eval(x) - ''' - # Here we could extrapolate if needed ... - # E.g: - result = np.where(x < 0.0, - self._eval(0.0)+np.exp(4*(0.0-x)), result) - result = np.where(x > 5, - self._eval(5) + 0*x, result) - ''' + return result def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): - # TODO: remove, no need for duplicate checking - # newx = np.unique(xdata) - # new y is average of all repeated values - # newy = [np.average(ydata[np.where(xdata == val)[0]]) - # for val in np.unique(xdata)] + newx = xdata newy = ydata diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index dc49c8e076..9a926e36b5 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -11,9 +11,7 @@ # that they have been altered from the originals. """ -This module implements a 1D Harmonic potential. It can be used to compute -thermodynamic properties of diatomic molecules (e.g. H2, HD, D2) via the -diatomic partition function and the thermodynamics module. +This module implements a 1D Harmonic potential. """ import numpy as np from scipy.optimize import curve_fit @@ -136,8 +134,6 @@ def get_equilibrium_geometry(self, scaling=1.0): Args: scaling: Scaling to change units. (Default is 1.0 for Angstroms) """ - # the returned value(s) is defined by the molecule's degrees of - # freedom? # Returns the distance for the minimal energy (scaled by 'scaling') # Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index f64285187e..fb716ecc04 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -11,9 +11,7 @@ # that they have been altered from the originals. """ -This module implements a 1D Morse potential. It can be used to compute -thermodynamic properties of diatomic molecules (e.g. H2, HD, D2) via the -diatomic partition function and the thermodynamics module. +This module implements a 1D Morse potential. """ import numpy as np from scipy.optimize import curve_fit @@ -30,7 +28,7 @@ class MorsePotential(PotentialBase): """ # Works in Angstroms and Hartrees - def __init__(self, molecule): + def __init__(self,molecule): """ Constructor. Initializes the potential to the zero-function. @@ -43,7 +41,7 @@ def __init__(self, molecule): Raises: ValueError: Only implemented for diatomic molecules """ - super().__init__(molecule) + super().__init__() # Initialize with zero-potential. # Later - fit energy values (fit) self.d_e = 0.0 diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index aa29b2c344..1b8339bcb6 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -20,16 +20,8 @@ class EnergySurfaceBase(ABC): """ Class to hold a potential energy surface """ - def __init__(self, molecule): + def __init__(self): pass - # self.update_molecule(molecule) - - # @abstractmethod - # def update_molecule(self, molecule): - # """ - # Wipe state if molecule changes, and check validity of molecule - # for potential. - # """ @abstractmethod def eval(self, x): @@ -108,20 +100,6 @@ def get_maximum_trusted_level(self, mode=0): """ return 100 - ''' - # TODO: Do we need these? Does every vibrational mode have a fundamental - # frequency (e.g. when it is anharmonic)? - - @abstractmethod - def fundamental_frequency(self, mode=0): - """ returns the fundamental frequency for a given mode """ - raise NotImplementedError - - def wave_number(self, mode=0): - """ returns the wave number for a given mode """ - return self.fundamental_frequency(mode) / C_CM_PER_S - ''' - class PotentialBase(EnergySurfaceBase, VibronicStructureBase): """ @@ -145,74 +123,3 @@ def get_trust_region(self): def dissociation_energy(self, scaling=1.0): """ returns the dissociation energy (scaled by 'scaling')""" raise NotImplementedError - - -''' -class Potential1D(PotentialBase): - def __init__(self, molecule, energy_surface, vibronic_struct): - self.energy_surface = energy_surface - self.vibronic_structure = vibronic_struct - self.update_molecule(molecule) - - def update_molecule(self, molecule): - """ - Wipe state if molecule changes, and check validity of molecule - for potential. - """ - self.molecule = molecule - self.energy_surface.update_molecule(molecule) - self.vibronic_structure.update_molecule(molecule) - - def eval(self, x): - """ - After fitting the data to the fit function, predict the energy - at a point x. - """ - return self.energy_surface.eval(x) - - def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): - """ Fit the surface to data - x are coordinates, y is energy.""" - return self.energy_surface.fit(xdata, ydata, initial_vals, - bounds_list) - - def get_equilibrium_geometry(self, scaling=1.0): - """ - Returns the geometry for the minimal energy (scaled by 'scaling') - Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get - meters. - """ - return self.energy_surface.get_equilibrium_geometry(scaling) - - def get_minimal_energy(self, scaling=1.0): - """ - Returns the value of the minimal energy (scaled by 'scaling') - Default units (scaling=1.0) are J/mol. Scale appropriately for - Hartrees. - """ - return self.energy_surface.get_minimal_energy(scaling) - - def get_num_modes(self): - """ returns the number of vibrational modes for the molecule """ - return self.vibronic_structure.get_num_modes() - - def fundamental_frequency(self, mode=0): - """ returns the fundamental frequency for a given mode """ - return self.vibronic_structure.fundamental_frequency(mode) - - def wave_number(self, mode=0): - """ returns the wave number for a given mode """ - return self.fundamental_frequency(mode) / C_CM_PER_S - - def vibrational_energy_level(self, n, mode=0): - """ returns the n-th vibrational energy level for a given mode """ - return self.vibronic_structure.vibrational_energy_level(n, mode) - - def get_maximum_trusted_level(self, mode = 0): - """ returns the dissociation energy (scaled by 'scaling')""" - return self.vibronic_structure.get_maximum_trusted_level(0) - - def dissociation_energy(self, scaling=1.0): - """ returns the dissociation energy (scaled by 'scaling')""" - return (self.eval(5) - self.get_minimal_energy() - - self.vibrational_energy_level(0)) * scaling -''' From 21abc5472ccb57f789640bb4ccb9a92cf5a04fde Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 13:41:38 +0200 Subject: [PATCH 167/197] fix potentials --- .../algorithms/pes_samplers/potentials/harmonic_potential.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 9a926e36b5..71fd35837c 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -41,7 +41,7 @@ def __init__(self, molecule): Raises: ValueError: Only implemented for diatomic molecules """ - super().__init__(molecule) + super().__init__() # Initialize with zero-potential. # Later - fit energy values (fit_to_data) self.k = 0.0 From 6775a143be0ad2bf54b506dddfb0640bfc25ce4f Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 13:47:38 +0200 Subject: [PATCH 168/197] added init --- .../algorithms/pes_samplers/__init__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 qiskit/chemistry/algorithms/pes_samplers/__init__.py diff --git a/qiskit/chemistry/algorithms/pes_samplers/__init__.py b/qiskit/chemistry/algorithms/pes_samplers/__init__.py new file mode 100644 index 0000000000..4b30577ea4 --- /dev/null +++ b/qiskit/chemistry/algorithms/pes_samplers/__init__.py @@ -0,0 +1,18 @@ +# 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. + +"""Potential energy surface samplers""" + +from .bopes_sampler import BOPESSampler + +__all__ = ['BOPESSampler' + ] From 7408d889bdc635a22a915d54a7c0b404913f7a8f Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 13:59:51 +0100 Subject: [PATCH 169/197] linting --- .../pes_samplers/energy_surface_spline.py | 3 + .../potentials/morse_potential.py | 2 +- test/chemistry/test_potential.py | 137 +++++++++--------- 3 files changed, 70 insertions(+), 72 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index 8e0d82d48e..98289aa48d 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -29,10 +29,13 @@ def __init__(self): """ Constructor. """ + super().__init__() self._eval = None self.eval_d = None self.min_x = None self.min_val = None + self.x_left = None + self.x_right = None # Implementing the EnergySurfaceBase interface diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index fb716ecc04..4d4e0a927e 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -28,7 +28,7 @@ class MorsePotential(PotentialBase): """ # Works in Angstroms and Hartrees - def __init__(self,molecule): + def __init__(self, molecule): """ Constructor. Initializes the potential to the zero-function. diff --git a/test/chemistry/test_potential.py b/test/chemistry/test_potential.py index 7fd18f8efe..eb1b15e590 100644 --- a/test/chemistry/test_potential.py +++ b/test/chemistry/test_potential.py @@ -25,7 +25,8 @@ class TestPotential(unittest.TestCase): """ Test Potential """ - def create_test_molecule(self): + @staticmethod + def create_test_molecule(): """ create test molecule """ stretch = partial(Molecule.absolute_stretching, kwargs={'atom_pair': (1, 0)}) @@ -36,47 +37,44 @@ def create_test_molecule(self): def test_morse(self): """ test morse """ - self._xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, - 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, - 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, - 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, - 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, - 5.25]) - - self._ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, - -2611323.75276296, -2502198.92978322, -2417457.48952287, - -2390778.71123391, -2379482.70907613, -2373850.72354504, - -2361426.93801724, -2369992.6305902, -2363833.07716161, - -2360577.93019891, -2356002.65576262, -2355574.41051646, - -2357254.94032554, -2351656.71871981, -2308055.75509618, - -2797576.98597419, -2715367.76135088, -2616523.58105343, - -2498053.2658529, -2424288.88205414, -2393385.83237565, - -2371800.12956182, -2353202.82294735, -2346873.32092711, - -2343485.8487826, -2342937.74947792, -2350276.02096954, - -2347674.75469199, -2346912.78218669, -2339886.28877723, - -2353456.10489755, -2359599.85281831, -2811321.68662548, - -2763866.98837641, -2613385.92519959, -2506804.00364042, - -2419329.49702063, -2393428.68052976, -2374166.67617163, - -2352961.35574553, -2344972.64297329, -2356294.5588125, - -2341396.63369969, -2337344.83138146, -2339793.71365995, - -2335667.95101689, -2327347.45385524, -2341367.28061372]) - - self.ydata_hartree = self._ydata / HARTREE_TO_J_PER_MOL - self.ydata_j_per_mol = self._ydata - - self.xdata_angstrom = self._xdata + xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, + 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, + 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, + 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, + 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, + 5.25]) + + ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, + -2611323.75276296, -2502198.92978322, -2417457.48952287, + -2390778.71123391, -2379482.70907613, -2373850.72354504, + -2361426.93801724, -2369992.6305902, -2363833.07716161, + -2360577.93019891, -2356002.65576262, -2355574.41051646, + -2357254.94032554, -2351656.71871981, -2308055.75509618, + -2797576.98597419, -2715367.76135088, -2616523.58105343, + -2498053.2658529, -2424288.88205414, -2393385.83237565, + -2371800.12956182, -2353202.82294735, -2346873.32092711, + -2343485.8487826, -2342937.74947792, -2350276.02096954, + -2347674.75469199, -2346912.78218669, -2339886.28877723, + -2353456.10489755, -2359599.85281831, -2811321.68662548, + -2763866.98837641, -2613385.92519959, -2506804.00364042, + -2419329.49702063, -2393428.68052976, -2374166.67617163, + -2352961.35574553, -2344972.64297329, -2356294.5588125, + -2341396.63369969, -2337344.83138146, -2339793.71365995, + -2335667.95101689, -2327347.45385524, -2341367.28061372]) + + ydata_hartree = ydata / HARTREE_TO_J_PER_MOL + + xdata_angstrom = xdata m = self.create_test_molecule() morse = MorsePotential(m) - xdata = np.array(self.xdata_angstrom) - ydata = np.array(self.ydata_hartree) + xdata = np.array(xdata_angstrom) + ydata = np.array(ydata_hartree) morse.fit(xdata, ydata) - # self.plot_potential(xdata, ydata, M) - minimal_energy_distance = morse.get_equilibrium_geometry() minimal_energy = morse.eval(minimal_energy_distance) wave_number = morse.wave_number() @@ -90,8 +88,8 @@ def test_morse(self): np.testing.assert_array_almost_equal(hartrees, morse.eval(radia)) vib_levels = [] - for N in range(2, 8): - vib_levels.append(morse.vibrational_energy_level(N)) + for level in range(2, 8): + vib_levels.append(morse.vibrational_energy_level(level)) vib_levels = np.array(vib_levels) vib_levels_ref = np.array([0.04052116451981064, 0.05517676610999135, 0.06894501671860434, 0.08182591634564956, @@ -100,47 +98,44 @@ def test_morse(self): def test_harmonic(self): """ test harmonic """ - self._xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, - 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, - 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, - 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, - 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, - 5.25]) - - self._ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, - -2611323.75276296, -2502198.92978322, -2417457.48952287, - -2390778.71123391, -2379482.70907613, -2373850.72354504, - -2361426.93801724, -2369992.6305902, -2363833.07716161, - -2360577.93019891, -2356002.65576262, -2355574.41051646, - -2357254.94032554, -2351656.71871981, -2308055.75509618, - -2797576.98597419, -2715367.76135088, -2616523.58105343, - -2498053.2658529, -2424288.88205414, -2393385.83237565, - -2371800.12956182, -2353202.82294735, -2346873.32092711, - -2343485.8487826, -2342937.74947792, -2350276.02096954, - -2347674.75469199, -2346912.78218669, -2339886.28877723, - -2353456.10489755, -2359599.85281831, -2811321.68662548, - -2763866.98837641, -2613385.92519959, -2506804.00364042, - -2419329.49702063, -2393428.68052976, -2374166.67617163, - -2352961.35574553, -2344972.64297329, -2356294.5588125, - -2341396.63369969, -2337344.83138146, -2339793.71365995, - -2335667.95101689, -2327347.45385524, -2341367.28061372]) - - self.ydata_hartree = self._ydata / HARTREE_TO_J_PER_MOL - self.ydata_j_per_mol = self._ydata - - self.xdata_angstrom = self._xdata + xdata = np.array([0.45, 0.75, 1.05, 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, + 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, + 1.35, 1.65, 1.95, 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, + 4.35, 4.65, 4.95, 5.25, 0.45, 0.75, 1.05, 1.35, 1.65, 1.95, + 2.25, 2.55, 2.85, 3.15, 3.45, 3.75, 4.05, 4.35, 4.65, 4.95, + 5.25]) + + ydata = np.array([-2254757.5348101, -2746067.46608231, -2664406.49829366, + -2611323.75276296, -2502198.92978322, -2417457.48952287, + -2390778.71123391, -2379482.70907613, -2373850.72354504, + -2361426.93801724, -2369992.6305902, -2363833.07716161, + -2360577.93019891, -2356002.65576262, -2355574.41051646, + -2357254.94032554, -2351656.71871981, -2308055.75509618, + -2797576.98597419, -2715367.76135088, -2616523.58105343, + -2498053.2658529, -2424288.88205414, -2393385.83237565, + -2371800.12956182, -2353202.82294735, -2346873.32092711, + -2343485.8487826, -2342937.74947792, -2350276.02096954, + -2347674.75469199, -2346912.78218669, -2339886.28877723, + -2353456.10489755, -2359599.85281831, -2811321.68662548, + -2763866.98837641, -2613385.92519959, -2506804.00364042, + -2419329.49702063, -2393428.68052976, -2374166.67617163, + -2352961.35574553, -2344972.64297329, -2356294.5588125, + -2341396.63369969, -2337344.83138146, -2339793.71365995, + -2335667.95101689, -2327347.45385524, -2341367.28061372]) + + ydata_hartree = ydata / HARTREE_TO_J_PER_MOL + + xdata_angstrom = xdata m = self.create_test_molecule() harmonic = HarmonicPotential(m) - xdata = np.array(self.xdata_angstrom) - ydata = np.array(self.ydata_hartree) + xdata = np.array(xdata_angstrom) + ydata = np.array(ydata_hartree) harmonic.fit(xdata, ydata) - # self.plot_potential(xdata, ydata, H) - minimal_energy_distance = harmonic.get_equilibrium_geometry() minimal_energy = harmonic.eval(minimal_energy_distance) wave_number = harmonic.wave_number() @@ -154,8 +149,8 @@ def test_harmonic(self): np.testing.assert_array_almost_equal(hartrees, harmonic.eval(radia)) vib_levels = [] - for N in range(2, 8): - vib_levels.append(harmonic.vibrational_energy_level(N)) + for level in range(2, 8): + vib_levels.append(harmonic.vibrational_energy_level(level)) vib_levels = np.array(vib_levels) vib_levels_ref = np.array([0.053206266711245426, 0.07448877339574358, 0.09577128008024177, 0.11705378676473993, From 2c03da2a6666b404a77bf9d97625ce9319ccf2b8 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 14:20:48 +0100 Subject: [PATCH 170/197] linting --- .pylintdict | 5 ++++- test/chemistry/test_data_potentials.py | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.pylintdict b/.pylintdict index f87ea57797..019c5fde75 100644 --- a/.pylintdict +++ b/.pylintdict @@ -41,6 +41,7 @@ backend backends baritompa barkoutsos +basedriver bb benchmarking bergholm @@ -203,7 +204,7 @@ exponentiated exponentiations expr extrapolator -Extrapolators +extrapolators factorizers factr fcidump @@ -251,6 +252,7 @@ grinko groundenergy groundstates grover +gsc gset gsls gto @@ -261,6 +263,7 @@ hamiltonian hamiltonians hardcoded hartree +hartrees hassidim hb hhl diff --git a/test/chemistry/test_data_potentials.py b/test/chemistry/test_data_potentials.py index 7a2a6ef945..bacd346593 100644 --- a/test/chemistry/test_data_potentials.py +++ b/test/chemistry/test_data_potentials.py @@ -41,7 +41,7 @@ -2341396.63369969, -2337344.83138146, -2339793.71365995, -2335667.95101689, -2327347.45385524, -2341367.28061372]) -ydata_hartree = Y_DATA / HARTREE_TO_J_PER_MOL -ydata_j_per_mol = Y_DATA +YDATA_HARTEE = Y_DATA / HARTREE_TO_J_PER_MOL +YDATA_J_PEL_MOL = Y_DATA -xdata_angstrom = X_DATA +XDATA_ANGSTROM = X_DATA From b9956c14c9f5b6b24b1c9a9897df4efb372de7f7 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Thu, 15 Oct 2020 09:48:40 -0400 Subject: [PATCH 171/197] fix spell --- .pylintdict | 1 + 1 file changed, 1 insertion(+) diff --git a/.pylintdict b/.pylintdict index 019c5fde75..6901d3182e 100644 --- a/.pylintdict +++ b/.pylintdict @@ -205,6 +205,7 @@ exponentiations expr extrapolator extrapolators +extrapolator's factorizers factr fcidump From 7a249f9446dc5c9e7a362e72509ada71797888de Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 15:06:39 +0100 Subject: [PATCH 172/197] removed unused code, more docstrings. --- qiskit/chemistry/results/bopes_sampler_result.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/results/bopes_sampler_result.py b/qiskit/chemistry/results/bopes_sampler_result.py index 2665cd4526..f6f617c170 100644 --- a/qiskit/chemistry/results/bopes_sampler_result.py +++ b/qiskit/chemistry/results/bopes_sampler_result.py @@ -23,12 +23,16 @@ class BOPESSamplerResult: """The BOPES Sampler result""" - # def __init__(self, results, results_full): - # self._results = results - # self._results_full = results_full def __init__(self, points: List[float], energies: List[float], raw_results: Dict[float, EigenstateResult]) -> None: + """ + Creates an new instance of the result. + Args: + points: List of points. + energies: List of energies. + raw_results: Raw results obtained from the solver. + """ super().__init__() self._points = points self._energies = energies @@ -37,13 +41,11 @@ def __init__(self, points: List[float], @property def points(self) -> List[float]: """ returns list of points.""" - # return self._results.get('point') return self._points @property def energies(self) -> List[float]: """ returns list of energies.""" - # return self._results.get('energy') return self._energies @property From 5ca21f86062e38df1ee302da13432cc5b53c125e Mon Sep 17 00:00:00 2001 From: Anton Dekusar <62334182+adekusar-drl@users.noreply.github.com> Date: Thu, 15 Oct 2020 16:12:44 +0100 Subject: [PATCH 173/197] Update qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- .../algorithms/pes_samplers/potentials/harmonic_potential.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 71fd35837c..a6bc10d581 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -30,7 +30,6 @@ class HarmonicPotential(PotentialBase): def __init__(self, molecule): """ - Constructor. Initializes the potential to the zero-function. fit_to_data() should be used afterwards to fit the potential to computed molecular energies. From c06d342f3f5c7e7dccc341c3410185e021e93c29 Mon Sep 17 00:00:00 2001 From: Anton Dekusar <62334182+adekusar-drl@users.noreply.github.com> Date: Thu, 15 Oct 2020 16:13:01 +0100 Subject: [PATCH 174/197] Update qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py index e36238158c..c9af5fcb32 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py +++ b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py @@ -40,7 +40,7 @@ def __init__(self, Args: gsc: GroundStateSolver tolerance: Tolerance desired for minimum energy. - bootstrap: Whether to warm-start the solve of variational minimum eigensolvers. + bootstrap: Whether to warm-start the solution of variational minimum eigensolvers. num_bootstrap: Number of previous points for extrapolation and bootstrapping. If None and a list of extrapolators is defined, the first two points will be used for bootstrapping. From 73ac78f22a049e5eb1acfe1bb479adb9ec49f496 Mon Sep 17 00:00:00 2001 From: Anton Dekusar <62334182+adekusar-drl@users.noreply.github.com> Date: Thu, 15 Oct 2020 16:13:41 +0100 Subject: [PATCH 175/197] Update qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- .../algorithms/pes_samplers/potentials/morse_potential.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 4d4e0a927e..19a08b0671 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -30,7 +30,6 @@ class MorsePotential(PotentialBase): def __init__(self, molecule): """ - Constructor. Initializes the potential to the zero-function. fit() should be used afterwards to fit the potential to computed molecular energies. From 91205d84e40cd52d858aa54b4dac2330dcbaf07a Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 16:23:08 +0100 Subject: [PATCH 176/197] converted to static --- qiskit/chemistry/algorithms/pes_samplers/extrapolator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py b/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py index 36273a73e9..cf77f9cd01 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py +++ b/qiskit/chemistry/algorithms/pes_samplers/extrapolator.py @@ -69,8 +69,8 @@ def extrapolate(self, points: List[float], """ raise NotImplementedError() - @classmethod - def factory(cls, mode: str, **kwargs) -> 'Extrapolator': + @staticmethod + def factory(mode: str, **kwargs) -> 'Extrapolator': """ Factory method for constructing extrapolators. From ebc73f414ae37b324f6c9eb95f74562042201a8d Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 16:40:41 +0100 Subject: [PATCH 177/197] code review fixes --- qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py | 6 +++--- .../pes_samplers/potentials/harmonic_potential.py | 1 - .../algorithms/pes_samplers/potentials/morse_potential.py | 1 - .../algorithms/pes_samplers/potentials/potential_base.py | 3 --- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py index c9af5fcb32..ce754c2d15 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py +++ b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py @@ -104,7 +104,7 @@ def sample(self, driver: BaseDriver, points: List[float]) -> BOPESSamplerResult: self._driver = driver if self._driver.molecule is None: - raise AquaError('Please provide a molecule') + raise AquaError('Driver MUST be configured with a Molecule.') # full dictionary of points self._raw_results = self._run_points(points) @@ -112,8 +112,8 @@ def sample(self, driver: BaseDriver, points: List[float]) -> BOPESSamplerResult: self._points = list(self._raw_results.keys()) self._energies = [] for key in self._raw_results: - energy = self._raw_results[key]['computed_electronic_energy'] + \ - self._raw_results[key]['nuclear_repulsion_energy'] + energy = self._raw_results[key].computed_electronic_energy + \ + self._raw_results[key].nuclear_repulsion_energy self._energies.append(energy) result = BOPESSamplerResult(self._points, self._energies, self._raw_results) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index a6bc10d581..43f0c8cbca 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -40,7 +40,6 @@ def __init__(self, molecule): Raises: ValueError: Only implemented for diatomic molecules """ - super().__init__() # Initialize with zero-potential. # Later - fit energy values (fit_to_data) self.k = 0.0 diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 19a08b0671..0c42a5c0e6 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -40,7 +40,6 @@ def __init__(self, molecule): Raises: ValueError: Only implemented for diatomic molecules """ - super().__init__() # Initialize with zero-potential. # Later - fit energy values (fit) self.d_e = 0.0 diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index 1b8339bcb6..d88a23db47 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -20,9 +20,6 @@ class EnergySurfaceBase(ABC): """ Class to hold a potential energy surface """ - def __init__(self): - pass - @abstractmethod def eval(self, x): """ From dfbbb3b9a720cf8690ee9911d7a8ef170f4134a0 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 16:43:10 +0100 Subject: [PATCH 178/197] gsc -> gss --- .../algorithms/pes_samplers/bopes_sampler.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py index ce754c2d15..1eb37255ef 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py +++ b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py @@ -31,14 +31,14 @@ class BOPESSampler: """Class to evaluate the Born-Oppenheimer Potential Energy Surface (BOPES).""" def __init__(self, - gsc: GroundStateSolver, + gss: GroundStateSolver, tolerance: float = 1e-3, bootstrap: bool = True, num_bootstrap: Optional[int] = None, extrapolator: Optional[Extrapolator] = None) -> None: """ Args: - gsc: GroundStateSolver + gss: GroundStateSolver tolerance: Tolerance desired for minimum energy. bootstrap: Whether to warm-start the solution of variational minimum eigensolvers. num_bootstrap: Number of previous points for extrapolation @@ -55,7 +55,7 @@ def __init__(self, ``WindowExtrapolator``. """ - self._gsc = gsc + self._gss = gss self._tolerance = tolerance self._bootstrap = bootstrap self._driver = None # type: Optional[BaseDriver] @@ -81,10 +81,10 @@ def __init__(self, raise AquaError( 'num_bootstrap must be None or an integer greater than or equal to 2') - if isinstance(self._gsc.solver, VQAlgorithm): + if isinstance(self._gss.solver, VQAlgorithm): # Save initial point passed to min_eigensolver; # this will be used when NOT bootstrapping - self._initial_point = self._gsc.solver.initial_point + self._initial_point = self._gss.solver.initial_point def sample(self, driver: BaseDriver, points: List[float]) -> BOPESSamplerResult: """Run the sampler at the given points, potentially with repetitions. @@ -130,9 +130,9 @@ def _run_points(self, points: List[float]) -> Dict[float, EigenstateResult]: The results for all points. """ raw_results = dict() # type: Dict[float, EigenstateResult] - if isinstance(self._gsc.solver, VQAlgorithm): + if isinstance(self._gss.solver, VQAlgorithm): self._points_optparams = dict() - self._gsc.solver.initial_point = self._initial_point + self._gss.solver.initial_point = self._initial_point # Iterate over the points for i, point in enumerate(points): @@ -156,7 +156,7 @@ def _run_single_point(self, point: float) -> EigenstateResult: self._driver.molecule.perturbations = [point] # find closest previously run point and take optimal parameters - if isinstance(self._gsc.solver, VQAlgorithm) and self._bootstrap: + if isinstance(self._gss.solver, VQAlgorithm) and self._bootstrap: prev_points = list(self._points_optparams.keys()) prev_params = list(self._points_optparams.values()) n_pp = len(prev_points) @@ -175,22 +175,22 @@ def _run_single_point(self, point: float) -> EigenstateResult: # find min 'distance' from point to previous points min_index = np.argmin(np.linalg.norm(distances, axis=1)) # update initial point - self._gsc.solver.initial_point = prev_params[min_index] + self._gss.solver.initial_point = prev_params[min_index] else: # extrapolate using saved parameters opt_params = self._points_optparams param_sets = self._extrapolator.extrapolate(points=[point], param_dict=opt_params) # update initial point, note param_set is a list # param set is a dictionary - self._gsc.solver.initial_point = param_sets.get(point) + self._gss.solver.initial_point = param_sets.get(point) # the output is an instance of EigenstateResult - result = self._gsc.solve(self._driver) + result = self._gss.solve(self._driver) # Save optimal point to bootstrap - if isinstance(self._gsc.solver, VQAlgorithm): + if isinstance(self._gss.solver, VQAlgorithm): # at every point evaluation, the optimal params are updated - optimal_params = self._gsc.solver.optimal_params + optimal_params = self._gss.solver.optimal_params self._points_optparams[point] = optimal_params return result From 4956f5f8df2568fef21175804a55258a294e3e5f Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 17:59:53 +0200 Subject: [PATCH 179/197] added dicstrings fixed missing args --- .../pes_samplers/energy_surface_spline.py | 44 ++++++++++--- .../pes_samplers/potentials/potential_base.py | 65 ++++++++++++++++--- 2 files changed, 91 insertions(+), 18 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index 8e0d82d48e..a3512ea275 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -11,7 +11,8 @@ # that they have been altered from the originals. """ -An spline interpolation method for data fitting +An spline interpolation method for data fitting. This allows for fiting +bopes sampler results or potential energy surfaces. """ import scipy.interpolate as interp @@ -26,9 +27,6 @@ class EnergySurface1DSpline(EnergySurfaceBase): """ def __init__(self): - """ - Constructor. - """ self._eval = None self.eval_d = None self.min_x = None @@ -36,18 +34,33 @@ def __init__(self): # Implementing the EnergySurfaceBase interface - def eval(self, x): + def eval(self, x: float)-> float: """ After fitting the data to the fit function, predict the energy at a point x. + + Args: + x: value to be evaluated + + Returns: + value of surface fit in point x """ + assert self._eval is not None result = self._eval(x) return result - def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): + def fit(self, xdata:List, ydata:List)-> None: + """ + Fits surface to data + Args: + xdata: x data to be fitted + ydata: y data to be fitted + Returns: + fitted surface + """ newx = xdata newy = ydata @@ -64,30 +77,43 @@ def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): self.x_left = min(xdata) self.x_right = max(xdata) - def get_equilibrium_geometry(self, scaling=1.0): + def get_equilibrium_geometry(self, scaling=1.0)-> float: """ Returns the geometry for the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get meters. + Args: + scaling: scaling factor + + Returns: + equilibrium geometry """ assert self.min_x is not None return self.min_x * scaling - def get_minimal_energy(self, scaling=1.0): + def get_minimal_energy(self, scaling=1.0)-> float: """ Returns the value of the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are J/mol. Scale appropriately for Hartrees. + Args: + scaling: scaling factor + + Returns: + minimum energy """ assert self.min_val is not None return self.min_val * scaling - def get_trust_region(self): + def get_trust_region(self)->tuple(float, float): """ Returns the bounds of the region (in space) where the energy surface implementation can be trusted. When doing spline interpolation, for example, that would be the region where data is interpolated (vs. extrapolated) from the arguments of fit(). + + Returns: + the trust region between bounds """ return (self.x_left, self.x_right) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index 1b8339bcb6..e84f5d4bbd 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -16,7 +16,6 @@ """ from abc import ABC, abstractmethod - class EnergySurfaceBase(ABC): """ Class to hold a potential energy surface """ @@ -24,16 +23,29 @@ def __init__(self): pass @abstractmethod - def eval(self, x): + def eval(self, x)-> float: """ After fitting the data to the fit function, predict the energy at a point x. + Args: + x: value to evaluate surface in + + Returns: + value of surface in point x """ raise NotImplementedError @abstractmethod - def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): - """ Fit the surface to data - x are coordinates, y is energy.""" + def fit(self, xdata, ydata, initial_vals=None, bounds_list=None)->None: + """ + Fits surface to data + Args: + xdata: x data to be fitted + ydata: y data to be fitted + + Returns: + fitted surface + """ raise NotImplementedError @abstractmethod @@ -42,6 +54,11 @@ def get_equilibrium_geometry(self, scaling=1.0): Returns the geometry for the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get meters. + Args: + scaling: scaling factor + + Returns: + equilibrium geometry """ raise NotImplementedError @@ -51,6 +68,11 @@ def get_minimal_energy(self, scaling=1.0): Returns the value of the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are J/mol. Scale appropriately for Hartrees. + Args: + scaling: scaling factor + + Returns: + minimum energy """ raise NotImplementedError @@ -62,6 +84,9 @@ def get_trust_region(self): interpolation, for example, that would be the region where data is interpolated (vs. extrapolated) from the arguments of fit(). + + Returns: + the trust region between bounds """ raise NotImplementedError @@ -79,24 +104,46 @@ def update_molecule(self, molecule): """ Wipe state if molecule changes, and check validity of molecule for potential. + Args: + molecule: chemistry molecule + + Returns: + molecule used """ self.molecule = molecule @abstractmethod - def get_num_modes(self): - """ returns the number of vibrational modes for the molecule """ + def get_num_modes(self)-> float: + """ + Returns the number of vibrational modes for the molecule + Returns: + the number of vibrational modes + """ raise NotImplementedError @abstractmethod - def vibrational_energy_level(self, n, mode=0): - """ returns the n-th vibrational energy level for a given mode """ + def vibrational_energy_level(self, n, mode=0) -> float: + """ + Returns the n-th vibrational energy level for a given mode + Args: + n: number of vibrational mode + mode: vibrational mode + + Returns: + n-th vibrational energy level for a given mode + """ raise NotImplementedError - def get_maximum_trusted_level(self, mode=0): + def get_maximum_trusted_level(self, mode=0)->float: """ Returns the maximum energy level for which the particular implementation still provides a good approximation of reality. Default value of 100. Redefined where needed (see e.g. Morse). + Args: + mode: vibronic mode + + Returns: + maximum_trusted_level setted """ return 100 From 4352045cc4674360cbae2e6b0ac9180d7a45c4d2 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 18:04:39 +0200 Subject: [PATCH 180/197] test fix --- test/chemistry/test_bopes_sampler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/chemistry/test_bopes_sampler.py b/test/chemistry/test_bopes_sampler.py index 4f2469817a..9517720ddf 100644 --- a/test/chemistry/test_bopes_sampler.py +++ b/test/chemistry/test_bopes_sampler.py @@ -82,10 +82,10 @@ def test_h2_bopes_sampler(self): quantum_instance=quantum_instance, expectation=PauliExpectation()) - me_gsc = GroundStateEigensolver(f_t, solver) + me_gss = GroundStateEigensolver(f_t, solver) # BOPES sampler - sampler = BOPESSampler(gsc=me_gsc) + sampler = BOPESSampler(gss=me_gss) # absolute internuclear distance in Angstrom points = [0.7, 1.0, 1.3] @@ -117,10 +117,10 @@ def test_potential_interface(self): solver = NumPyMinimumEigensolver() - me_gsc = GroundStateEigensolver(f_t, solver) + me_gss = GroundStateEigensolver(f_t, solver) # Run BOPESSampler with exact eigensolution points = np.arange(0.45, 5.3, 0.3) - sampler = BOPESSampler(gsc=me_gsc) + sampler = BOPESSampler(gss=me_gss) res = sampler.sample(driver, points) From 77e643eade443be7927de213fe851e99182e92f9 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 18:07:10 +0200 Subject: [PATCH 181/197] one more docstring --- .../algorithms/pes_samplers/energy_surface_spline.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index e92781da0c..c50630d8fc 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -27,6 +27,10 @@ class EnergySurface1DSpline(EnergySurfaceBase): """ def __init__(self): + """ + An spline interpolation method for data fitting. This allows for fiting + bopes sampler results or potential energy surfaces. + """ self._eval = None self.eval_d = None self.min_x = None From 2602521faff14523a8ca34e90198020cf954f95d Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 18:28:25 +0200 Subject: [PATCH 182/197] fix typehints --- .../pes_samplers/energy_surface_spline.py | 2 +- .../potentials/harmonic_potential.py | 59 +++++++++++---- .../potentials/morse_potential.py | 74 +++++++++++++++---- .../pes_samplers/potentials/potential_base.py | 14 ++-- 4 files changed, 114 insertions(+), 35 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index c50630d8fc..ef2cfd5877 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -26,7 +26,7 @@ class EnergySurface1DSpline(EnergySurfaceBase): A simple cubic spline interpolation for the potential energy surface. """ - def __init__(self): + def __init__(self)->None: """ An spline interpolation method for data fitting. This allows for fiting bopes sampler results or potential energy surfaces. diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 43f0c8cbca..4d9b8f0c48 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -28,7 +28,7 @@ class HarmonicPotential(PotentialBase): """ # Works in Angstroms (input) and Hartrees (output) - def __init__(self, molecule): + def __init__(self, molecule)->None: """ Initializes the potential to the zero-function. fit_to_data() should be used afterwards to fit the potential to @@ -50,20 +50,35 @@ def __init__(self, molecule): self._mB = molecule.masses[1] @staticmethod - def fit_function(x, k, r_0, m_shift): + def fit_function(x, k, r_0, m_shift)->float: """ Functional form of the potential. + + Args: + x: x parameter of harmonic potential functional form + k: k parameter of harmonic potential functional form + r_0: r_0 parameter of harmonic potential functional form + m_shift: m parameter of harmonic potential functional form + + Returns: + harmonic potential functional form """ # K (Hartree/(Ang^2)), r_0 (Angstrom), m_shift (Hartree) return k / 2 * (x - r_0) ** 2 + m_shift - def eval(self, x): + def eval(self, x)->float: """ - Evaluates the potential at a given point. + After fitting the data to the fit function, predict the energy + at a point x. + Args: + x: value to evaluate surface in + + Returns: + value of potential in point x """ return self.fit_function(x, self.k, self.r_0, self.m_shift) - def update_molecule(self, molecule): + def update_molecule(self, molecule)->Molecule: """ Updates the underlying molecule. @@ -78,7 +93,7 @@ def update_molecule(self, molecule): self._mB = molecule.masses[1] def fit(self, xdata, ydata, initial_vals=None, bounds_list=None, - preprocess_data=True): + preprocess_data=True)->None: """ Fits a potential to computed molecular energies. @@ -126,7 +141,7 @@ def fit(self, xdata, ydata, initial_vals=None, bounds_list=None, # return - def get_equilibrium_geometry(self, scaling=1.0): + def get_equilibrium_geometry(self, scaling=1.0)->float: """ Returns the interatomic distance corresponding to minimal energy. Args: @@ -138,7 +153,7 @@ def get_equilibrium_geometry(self, scaling=1.0): # meters. return self.r_0 * scaling - def get_minimal_energy(self, scaling=1.0): + def get_minimal_energy(self, scaling=1.0)->float: """ Returns the smallest molecular energy for the current fit. Args: @@ -149,11 +164,14 @@ def get_minimal_energy(self, scaling=1.0): # Joules (per molecule or mol). return self.m_shift * scaling - def dissociation_energy(self, scaling=1.0): + def dissociation_energy(self, scaling=1.0)->float: """ Returns the estimated dissociation energy for the current fit. Args: scaling: Scaling to change units. (Default is 1.0 for Hartrees) + + Returns: + estimated dissociation energy """ # Hartree/(Ang**2) to Hartree!! 1/Ang**2 -> 1/m**2 k = self.k * 1E20 @@ -165,9 +183,11 @@ def dissociation_energy(self, scaling=1.0): # in Hartree return diss_nrg * scaling - def fundamental_frequency(self): + def fundamental_frequency(self)->float: """ Returns the fundamental frequency for the current fit (in s^-1). + Returns: + fundamental frequency for the current fit """ # Hartree(J)/(Ang**2), need Joules per molecule!! 1/Ang**2 -> 1/m**2 k = self.k * const.HARTREE_TO_J * 1E20 @@ -180,16 +200,23 @@ def fundamental_frequency(self): # fundamental frequency in s**-1 return omega_0 - def wave_number(self): + def wave_number(self)->int: """ Returns the wave number for the current fit (in cm^-1). + Returns: + wave number for the current fit """ return self.fundamental_frequency() / const.C_CM_PER_S - def vibrational_energy_level(self, n): + def vibrational_energy_level(self, n)->float: """ Returns the n-th vibrational energy level for the current fit (in Hartrees). + Args: + n: vibrational mode + + Returns: + vibrational energy level for the current fit """ omega_0 = self.fundamental_frequency() e_n = const.H_J_S * omega_0 * (n + 0.5) @@ -198,11 +225,17 @@ def vibrational_energy_level(self, n): return e_n * const.J_TO_HARTREE @classmethod - def process_fit_data(cls, xdata, ydata): + def process_fit_data(cls, xdata, ydata)->tuple(list,list): """ Mostly for internal use. Preprocesses the data passed to fit_to_data() so that only the points around the minimum are fit (which gives more accurate vibrational modes). + Args: + xdata: xdata to be considered + ydata: ydata to be considered + + Returns: + the processed data that fit better to a harmonic potential """ sort_ind = np.argsort(xdata) ydata_s = ydata[sort_ind] diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 0c42a5c0e6..43d23c95d1 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -50,27 +50,49 @@ def __init__(self, molecule): self._mB = molecule.masses[1] @staticmethod - def fit_function(x, d_e, alpha, r_0, m_shift): + def fit_function(x:float, + d_e:float, + alpha:float, + r_0:float, + m_shift:float)->float: """ Functional form of the potential. + Args: + x: x parameter of morse potential + d_e: d_e parameter of morse potential + alpha: alpha parameter of morse potential + r_0: r_0 parameter of morse potential + m_shift: m_shift parameter of morse potential + + Returns: + potential functional form """ # d_e (Hartree), alpha (1/Angstrom), r_0 (Angstrom) return d_e * (1 - np.exp(-alpha * (x - r_0))) ** 2 + m_shift - def eval(self, x): + def eval(self, x)->float: """ - Evaluates the potential at a given point. + After fitting the data to the fit function, predict the energy + at a point x. + Args: + x: value to evaluate surface in + + Returns: + value of surface in point x """ # Expects Angstroms returns Hartrees return self.fit_function(x, self.d_e, self.alpha, self.r_0, self.m_shift) - def update_molecule(self, molecule): + def update_molecule(self, molecule)->None: """ Updates the underlying molecule. - + Args: + molecule: chemistry molecule Raises: ValueError: Only implemented for diatomic molecules + Returns: + updated molecule """ # Check the provided molecule if len(molecule.masses) != 2: @@ -79,7 +101,7 @@ def update_molecule(self, molecule): self._mA = molecule.masses[0] self._mB = molecule.masses[1] - def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): + def fit(self, xdata, ydata, initial_vals=None, bounds_list=None)-> None: """ Fits a potential to computed molecular energies. @@ -111,15 +133,15 @@ def fit(self, xdata, ydata, initial_vals=None, bounds_list=None): self.r_0 = fit[2] self.m_shift = fit[3] - def get_equilibrium_geometry(self, scaling=1.0): + def get_equilibrium_geometry(self, scaling=1.0)-> float: """ Returns the interatomic distance corresponding to minimal energy. Args: scaling: Scaling to change units. (Default is 1.0 for Angstroms) + + Returns: + interatomic distance corresponding to minimal energy """ - # TODO: Should this be moved to Molecule, given that the meaning of - # the returned value(s) is defined by the molecule's degrees of - # freedom? # Returns the distance for the minimal energy (scaled by 'scaling') # Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get @@ -131,6 +153,9 @@ def get_minimal_energy(self, scaling=1.0): Returns the smallest molecular energy for the current fit. Args: scaling: Scaling to change units. (Default is 1.0 for Hartrees) + + Returns: + smallest molecular energy for the current fit """ # Returns the distance for the minimal energy (scaled by 'scaling'') # Default units (scaling=1.0) are Hartrees. Scale appropriately for @@ -142,6 +167,9 @@ def dissociation_energy(self, scaling=1.0): Returns the calculated dissociation energy for the current fit. Args: scaling: Scaling to change units. (Default is 1.0 for Hartrees) + + Returns: + calculated dissociation energy for the current fit """ # Returns the dissociation energy (scaled by 'scaling'). # Default units (scaling=1.0) are Hartrees. Scale appropriately for @@ -151,9 +179,11 @@ def dissociation_energy(self, scaling=1.0): return diss_nrg * scaling - def fundamental_frequency(self): + def fundamental_frequency(self)->float: """ Returns the fundamental frequency for the current fit (in s^-1). + Returns: + fundamental frequency for the current fit """ de = self.d_e * const.HARTREE_TO_J # Hartree, need J/molecule alp = self.alpha * 1E10 # 1/angstrom, need 1/meter @@ -166,16 +196,23 @@ def fundamental_frequency(self): # fundamental frequency in s**-1 return omega_0 - def wave_number(self): + def wave_number(self)->float: """ Returns the wave number for the current fit (in cm^-1). + Returns: + wave number for the current fit """ return self.fundamental_frequency() / const.C_CM_PER_S - def vibrational_energy_level(self, n): + def vibrational_energy_level(self, n:int)->float """ Returns the n-th vibrational energy level for the current fit (in Hartrees). + Args: + n: vibrational mode + + Returns: + vibrational energy level for the current fit """ de = self.d_e * const.HARTREE_TO_J # Hartree, need J/molecule @@ -186,7 +223,16 @@ def vibrational_energy_level(self, n): # energy level return e_n * const.J_TO_HARTREE - def get_maximum_trusted_level(self, mode=0): + def get_maximum_trusted_level(self, mode=0)->float: + """ + Returns the maximum energy level for which the particular + implementation still provides a good approximation of reality. + Args: + mode: vibronic mode + + Returns: + maximum_trusted_level estimated + """ # For the formula below, see # "Partition Functions", by Popovas A., et.al # Astronomy & Astrophysics, Vol. 595, November 2016 diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index c09098199b..d89ebbfb6c 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -46,7 +46,7 @@ def fit(self, xdata, ydata, initial_vals=None, bounds_list=None)->None: raise NotImplementedError @abstractmethod - def get_equilibrium_geometry(self, scaling=1.0): + def get_equilibrium_geometry(self, scaling=1.0)->float: """ Returns the geometry for the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get @@ -60,7 +60,7 @@ def get_equilibrium_geometry(self, scaling=1.0): raise NotImplementedError @abstractmethod - def get_minimal_energy(self, scaling=1.0): + def get_minimal_energy(self, scaling=1.0)->float: """ Returns the value of the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are J/mol. Scale appropriately for @@ -74,7 +74,7 @@ def get_minimal_energy(self, scaling=1.0): raise NotImplementedError @abstractmethod - def get_trust_region(self): + def get_trust_region(self)->tuple(float,float): """ Returns the bounds of the region (in space) where the energy surface implementation can be trusted. When doing spline @@ -97,7 +97,7 @@ class VibronicStructureBase(ABC): def __init__(self, molecule): self.update_molecule(molecule) - def update_molecule(self, molecule): + def update_molecule(self, molecule)->Molecule: """ Wipe state if molecule changes, and check validity of molecule for potential. @@ -151,11 +151,11 @@ class PotentialBase(EnergySurfaceBase, VibronicStructureBase): over a degree of freedom. """ - def get_num_modes(self): + def get_num_modes(self)->int: """ This (1D) potential represents a single vibrational mode """ return 1 - def get_trust_region(self): + def get_trust_region(self)->tuple(float,float): """ The potential will usually be well-defined (even if not useful) for arbitrary x so we return a fairly large interval here. @@ -164,6 +164,6 @@ def get_trust_region(self): return (-100, 100) @abstractmethod - def dissociation_energy(self, scaling=1.0): + def dissociation_energy(self, scaling=1.0)->float: """ returns the dissociation energy (scaled by 'scaling')""" raise NotImplementedError From 32bfa286f2a507b61da3e255d63871168a51eaed Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 18:32:28 +0200 Subject: [PATCH 183/197] more fix typehints --- .../pes_samplers/potentials/harmonic_potential.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 4d9b8f0c48..9b72242162 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -82,6 +82,8 @@ def update_molecule(self, molecule)->Molecule: """ Updates the underlying molecule. + Args: + molecule: chemistry molecule Raises: ValueError: Only implemented for diatomic molecules """ @@ -146,6 +148,9 @@ def get_equilibrium_geometry(self, scaling=1.0)->float: Returns the interatomic distance corresponding to minimal energy. Args: scaling: Scaling to change units. (Default is 1.0 for Angstroms) + + Returns: + geometry corresponding to minimal energy """ # Returns the distance for the minimal energy (scaled by 'scaling') @@ -158,6 +163,9 @@ def get_minimal_energy(self, scaling=1.0)->float: Returns the smallest molecular energy for the current fit. Args: scaling: Scaling to change units. (Default is 1.0 for Hartrees) + + Returns: + smallest molecular energy for the current fit """ # Returns the distance for the minimal energy (scaled by 'scaling') # Default units (scaling=1.0) are Hartrees. Scale appropriately for From e6bd66d52d7f90815bc52dec6d9cfcc0a9f0fb1b Mon Sep 17 00:00:00 2001 From: Davindra Tulsi Date: Thu, 15 Oct 2020 13:01:50 -0400 Subject: [PATCH 184/197] fixed comments --- qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py index 1eb37255ef..9ce821b4f1 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py +++ b/qiskit/chemistry/algorithms/pes_samplers/bopes_sampler.py @@ -152,7 +152,7 @@ def _run_single_point(self, point: float) -> EigenstateResult: Results for a single point. """ - # update molecule geometry and thus resulting Hamiltonian based on specified point + # update molecule geometry and thus resulting Hamiltonian based on specified point self._driver.molecule.perturbations = [point] # find closest previously run point and take optimal parameters @@ -180,8 +180,7 @@ def _run_single_point(self, point: float) -> EigenstateResult: opt_params = self._points_optparams param_sets = self._extrapolator.extrapolate(points=[point], param_dict=opt_params) - # update initial point, note param_set is a list - # param set is a dictionary + # update initial point, note param_set is a dictionary self._gss.solver.initial_point = param_sets.get(point) # the output is an instance of EigenstateResult From f46f26f52328bcd8a12e9ceb522977b372d9710c Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 19:17:32 +0200 Subject: [PATCH 185/197] typehint fix --- .../chemistry/algorithms/pes_samplers/energy_surface_spline.py | 2 +- .../algorithms/pes_samplers/potentials/harmonic_potential.py | 2 +- .../algorithms/pes_samplers/potentials/potential_base.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index ef2cfd5877..ce1f3d861a 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -111,7 +111,7 @@ def get_minimal_energy(self, scaling=1.0)-> float: assert self.min_val is not None return self.min_val * scaling - def get_trust_region(self)->tuple(float, float): + def get_trust_region(self)->Tuple[float, float]: """ Returns the bounds of the region (in space) where the energy surface implementation can be trusted. When doing spline diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 9b72242162..21deeb9ea8 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -233,7 +233,7 @@ def vibrational_energy_level(self, n)->float: return e_n * const.J_TO_HARTREE @classmethod - def process_fit_data(cls, xdata, ydata)->tuple(list,list): + def process_fit_data(cls, xdata, ydata)->Tuple[list,list]: """ Mostly for internal use. Preprocesses the data passed to fit_to_data() so that only the points around the minimum are fit (which gives diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index d89ebbfb6c..e96bb2c517 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -155,7 +155,7 @@ def get_num_modes(self)->int: """ This (1D) potential represents a single vibrational mode """ return 1 - def get_trust_region(self)->tuple(float,float): + def get_trust_region(self)->Tuple[float,float]: """ The potential will usually be well-defined (even if not useful) for arbitrary x so we return a fairly large interval here. From 82163bfc6abd1202d2b40b460c6863f150cb7348 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 19:37:21 +0200 Subject: [PATCH 186/197] more typehint fix --- .../algorithms/pes_samplers/potentials/morse_potential.py | 2 +- .../algorithms/pes_samplers/potentials/potential_base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 43d23c95d1..a0432a6c04 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -204,7 +204,7 @@ def wave_number(self)->float: """ return self.fundamental_frequency() / const.C_CM_PER_S - def vibrational_energy_level(self, n:int)->float + def vibrational_energy_level(self, n:int)->float: """ Returns the n-th vibrational energy level for the current fit (in Hartrees). diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index e96bb2c517..e26d523024 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -74,7 +74,7 @@ def get_minimal_energy(self, scaling=1.0)->float: raise NotImplementedError @abstractmethod - def get_trust_region(self)->tuple(float,float): + def get_trust_region(self)->Tuple[float,float]: """ Returns the bounds of the region (in space) where the energy surface implementation can be trusted. When doing spline From 9c0165ca49a7383eb836d048494830f51260e4d3 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 19:42:21 +0200 Subject: [PATCH 187/197] more typehint fix 2 --- .../chemistry/algorithms/pes_samplers/energy_surface_spline.py | 2 +- .../algorithms/pes_samplers/potentials/harmonic_potential.py | 3 ++- .../algorithms/pes_samplers/potentials/morse_potential.py | 1 - .../algorithms/pes_samplers/potentials/potential_base.py | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index ce1f3d861a..7da03965fe 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -19,7 +19,7 @@ from scipy.optimize import minimize_scalar from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import EnergySurfaceBase - +from typing import Tuple class EnergySurface1DSpline(EnergySurfaceBase): """ diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 21deeb9ea8..69872c4533 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -18,7 +18,8 @@ import qiskit.chemistry.constants as const from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import PotentialBase - +from typing import Tuple +from qiskit.chemistry.drivers import Molecule class HarmonicPotential(PotentialBase): """ diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index a0432a6c04..8e45cc2c24 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -19,7 +19,6 @@ from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import PotentialBase import qiskit.chemistry.constants as const - class MorsePotential(PotentialBase): """ Implements a 1D Morse potential. diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index e26d523024..01ab4738ff 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -15,6 +15,8 @@ and vibrational structure of a given molecule. """ from abc import ABC, abstractmethod +from typing import Tuple +from qiskit.chemistry.drivers import Molecule class EnergySurfaceBase(ABC): """ Class to hold a potential energy surface """ From cb0dc6c5a04540c3d317d360c3566b726e8934ca Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 21:16:55 +0200 Subject: [PATCH 188/197] some lint --- .../chemistry/algorithms/pes_samplers/energy_surface_spline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index 7da03965fe..e8ae047a7a 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -19,7 +19,7 @@ from scipy.optimize import minimize_scalar from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import EnergySurfaceBase -from typing import Tuple +from typing import Tuple, List class EnergySurface1DSpline(EnergySurfaceBase): """ From 2c566deeef317e768b1ee1a240f3a6e42a93c6b2 Mon Sep 17 00:00:00 2001 From: Cryoris Date: Thu, 15 Oct 2020 21:27:30 +0200 Subject: [PATCH 189/197] fix first round of lints --- .../pes_samplers/energy_surface_spline.py | 50 +++++----- .../potentials/harmonic_potential.py | 84 ++++++++-------- .../potentials/morse_potential.py | 97 ++++++++++--------- .../pes_samplers/potentials/potential_base.py | 64 ++++++------ 4 files changed, 147 insertions(+), 148 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index 7da03965fe..4a1d7a3bc4 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -15,21 +15,20 @@ bopes sampler results or potential energy surfaces. """ +from typing import Tuple, List import scipy.interpolate as interp from scipy.optimize import minimize_scalar from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import EnergySurfaceBase -from typing import Tuple + class EnergySurface1DSpline(EnergySurfaceBase): - """ - A simple cubic spline interpolation for the potential energy surface. - """ + """A simple cubic spline interpolation for the potential energy surface.""" - def __init__(self)->None: - """ - An spline interpolation method for data fitting. This allows for fiting - bopes sampler results or potential energy surfaces. + def __init__(self) -> None: + """A spline interpolation method for data fitting. + + This allows for fitting BOPES sampler results or potential energy surfaces. """ self._eval = None self.eval_d = None @@ -38,18 +37,14 @@ def __init__(self)->None: self.x_left = None self.x_right = None - # Implementing the EnergySurfaceBase interface - - def eval(self, x: float)-> float: - """ - After fitting the data to the fit function, predict the energy - at a point x. + def eval(self, x: float) -> float: + """After fitting the data to the fit function, predict the energy at a point x. Args: - x: value to be evaluated + x: Value to be evaluated Returns: - value of surface fit in point x + Value of surface fit in point x. """ assert self._eval is not None @@ -57,15 +52,15 @@ def eval(self, x: float)-> float: return result - def fit(self, xdata:List, ydata:List)-> None: - """ - Fits surface to data + def fit(self, xdata: List[float], ydata: List[float], initial_vals=None, bounds_list=None + ) -> None: + """Fits surface to data. + Args: xdata: x data to be fitted ydata: y data to be fitted - - Returns: - fitted surface + initial_vals: TODO + bounds_list: TODO """ newx = xdata newy = ydata @@ -83,7 +78,7 @@ def fit(self, xdata:List, ydata:List)-> None: self.x_left = min(xdata) self.x_right = max(xdata) - def get_equilibrium_geometry(self, scaling=1.0)-> float: + def get_equilibrium_geometry(self, scaling: float = 1.0) -> float: """ Returns the geometry for the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get @@ -97,7 +92,7 @@ def get_equilibrium_geometry(self, scaling=1.0)-> float: assert self.min_x is not None return self.min_x * scaling - def get_minimal_energy(self, scaling=1.0)-> float: + def get_minimal_energy(self, scaling: float = 1.0) -> float: """ Returns the value of the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are J/mol. Scale appropriately for @@ -111,8 +106,9 @@ def get_minimal_energy(self, scaling=1.0)-> float: assert self.min_val is not None return self.min_val * scaling - def get_trust_region(self)->Tuple[float, float]: - """ + def get_trust_region(self) -> Tuple[float, float]: + """Get the trust region. + Returns the bounds of the region (in space) where the energy surface implementation can be trusted. When doing spline interpolation, for example, that would be the region where data @@ -120,6 +116,6 @@ def get_trust_region(self)->Tuple[float, float]: fit(). Returns: - the trust region between bounds + The trust region between bounds. """ return (self.x_left, self.x_right) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 69872c4533..e8ec941813 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -10,17 +10,17 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -This module implements a 1D Harmonic potential. -""" +"""This module implements a 1D Harmonic potential.""" + +from typing import Tuple, List import numpy as np from scipy.optimize import curve_fit import qiskit.chemistry.constants as const from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import PotentialBase -from typing import Tuple from qiskit.chemistry.drivers import Molecule + class HarmonicPotential(PotentialBase): """ Implements a 1D Harmonic potential. @@ -29,12 +29,8 @@ class HarmonicPotential(PotentialBase): """ # Works in Angstroms (input) and Hartrees (output) - def __init__(self, molecule)->None: + def __init__(self, molecule: Molecule) -> None: """ - Initializes the potential to the zero-function. - fit_to_data() should be used afterwards to fit the potential to - computed molecular energies. - Args: molecule: the underlying molecule. @@ -47,11 +43,11 @@ def __init__(self, molecule)->None: self.m_shift = 0.0 self.r_0 = 0.0 self.d_e = None - self._mA = molecule.masses[0] - self._mB = molecule.masses[1] + self._m_a = molecule.masses[0] + self._m_b = molecule.masses[1] @staticmethod - def fit_function(x, k, r_0, m_shift)->float: + def fit_function(x: float, k: float, r_0: float, m_shift: float) -> float: """ Functional form of the potential. @@ -67,10 +63,9 @@ def fit_function(x, k, r_0, m_shift)->float: # K (Hartree/(Ang^2)), r_0 (Angstrom), m_shift (Hartree) return k / 2 * (x - r_0) ** 2 + m_shift - def eval(self, x)->float: - """ - After fitting the data to the fit function, predict the energy - at a point x. + def eval(self, x: float) -> float: + """After fitting the data to the fit function, predict the energy at a point x. + Args: x: value to evaluate surface in @@ -79,26 +74,24 @@ def eval(self, x)->float: """ return self.fit_function(x, self.k, self.r_0, self.m_shift) - def update_molecule(self, molecule)->Molecule: - """ - Updates the underlying molecule. + def update_molecule(self, molecule: Molecule) -> Molecule: + """Updates the underlying molecule. Args: molecule: chemistry molecule + Raises: ValueError: Only implemented for diatomic molecules """ # Check the provided molecule if len(molecule.masses) != 2: - raise ValueError( - 'Harmonic potential only works for diatomic molecules!') - self._mA = molecule.masses[0] - self._mB = molecule.masses[1] + raise ValueError('Harmonic potential only works for diatomic molecules!') + self._m_a = molecule.masses[0] + self._m_b = molecule.masses[1] def fit(self, xdata, ydata, initial_vals=None, bounds_list=None, - preprocess_data=True)->None: - """ - Fits a potential to computed molecular energies. + preprocess_data=True) -> None: + """Fits a potential to computed molecular energies. Args: xdata: interatomic distance points (Angstroms) @@ -144,9 +137,9 @@ def fit(self, xdata, ydata, initial_vals=None, bounds_list=None, # return - def get_equilibrium_geometry(self, scaling=1.0)->float: - """ - Returns the interatomic distance corresponding to minimal energy. + def get_equilibrium_geometry(self, scaling: float = 1.0) -> float: + """Returns the interatomic distance corresponding to minimal energy. + Args: scaling: Scaling to change units. (Default is 1.0 for Angstroms) @@ -159,9 +152,9 @@ def get_equilibrium_geometry(self, scaling=1.0)->float: # meters. return self.r_0 * scaling - def get_minimal_energy(self, scaling=1.0)->float: - """ - Returns the smallest molecular energy for the current fit. + def get_minimal_energy(self, scaling: float = 1.0) -> float: + """Returns the smallest molecular energy for the current fit. + Args: scaling: Scaling to change units. (Default is 1.0 for Hartrees) @@ -173,9 +166,9 @@ def get_minimal_energy(self, scaling=1.0)->float: # Joules (per molecule or mol). return self.m_shift * scaling - def dissociation_energy(self, scaling=1.0)->float: - """ - Returns the estimated dissociation energy for the current fit. + def dissociation_energy(self, scaling: float = 1.0) -> float: + """Returns the estimated dissociation energy for the current fit. + Args: scaling: Scaling to change units. (Default is 1.0 for Hartrees) @@ -192,32 +185,32 @@ def dissociation_energy(self, scaling=1.0)->float: # in Hartree return diss_nrg * scaling - def fundamental_frequency(self)->float: - """ - Returns the fundamental frequency for the current fit (in s^-1). + def fundamental_frequency(self) -> float: + """Returns the fundamental frequency for the current fit (in s^-1). + Returns: fundamental frequency for the current fit """ # Hartree(J)/(Ang**2), need Joules per molecule!! 1/Ang**2 -> 1/m**2 k = self.k * const.HARTREE_TO_J * 1E20 # r0 = self.r_0*1E-10 # angstrom, need meter - mr = (self._mA * self._mB) / (self._mA + self._mB) + m_r = (self._m_a * self._m_b) / (self._m_a + self._m_b) # omega_0 in units rad/s converted to 1/s by dividing by 2Pi - omega_0 = (np.sqrt(k / mr)) / (2 * np.pi) + omega_0 = np.sqrt(k / m_r) / (2 * np.pi) # fundamental frequency in s**-1 return omega_0 - def wave_number(self)->int: - """ - Returns the wave number for the current fit (in cm^-1). + def wave_number(self) -> int: + """Returns the wave number for the current fit (in cm^-1). + Returns: wave number for the current fit """ return self.fundamental_frequency() / const.C_CM_PER_S - def vibrational_energy_level(self, n)->float: + def vibrational_energy_level(self, n: int) -> float: """ Returns the n-th vibrational energy level for the current fit (in Hartrees). @@ -234,11 +227,12 @@ def vibrational_energy_level(self, n)->float: return e_n * const.J_TO_HARTREE @classmethod - def process_fit_data(cls, xdata, ydata)->Tuple[list,list]: + def process_fit_data(cls, xdata: List[float], ydata: List[float]) -> Tuple[list, list]: """ Mostly for internal use. Preprocesses the data passed to fit_to_data() so that only the points around the minimum are fit (which gives more accurate vibrational modes). + Args: xdata: xdata to be considered ydata: ydata to be considered diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 8e45cc2c24..9ac37eef51 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -13,12 +13,16 @@ """ This module implements a 1D Morse potential. """ + +from typing import List import numpy as np from scipy.optimize import curve_fit from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import PotentialBase +from qiskit.chemistry.drivers import Molecule import qiskit.chemistry.constants as const + class MorsePotential(PotentialBase): """ Implements a 1D Morse potential. @@ -45,17 +49,17 @@ def __init__(self, molecule): self.m_shift = 0.0 self.alpha = 0.0 self.r_0 = 0.0 - self._mA = molecule.masses[0] - self._mB = molecule.masses[1] + self._m_a = molecule.masses[0] + self._m_b = molecule.masses[1] @staticmethod - def fit_function(x:float, - d_e:float, - alpha:float, - r_0:float, - m_shift:float)->float: - """ - Functional form of the potential. + def fit_function(x: float, + d_e: float, + alpha: float, + r_0: float, + m_shift: float) -> float: + """Functional form of the potential. + Args: x: x parameter of morse potential d_e: d_e parameter of morse potential @@ -69,10 +73,9 @@ def fit_function(x:float, # d_e (Hartree), alpha (1/Angstrom), r_0 (Angstrom) return d_e * (1 - np.exp(-alpha * (x - r_0))) ** 2 + m_shift - def eval(self, x)->float: - """ - After fitting the data to the fit function, predict the energy - at a point x. + def eval(self, x: float) -> float: + """After fitting the data to the fit function, predict the energy at a point x. + Args: x: value to evaluate surface in @@ -83,13 +86,15 @@ def eval(self, x)->float: return self.fit_function(x, self.d_e, self.alpha, self.r_0, self.m_shift) - def update_molecule(self, molecule)->None: - """ - Updates the underlying molecule. + def update_molecule(self, molecule: Molecule) -> None: + """Updates the underlying molecule. + Args: molecule: chemistry molecule + Raises: ValueError: Only implemented for diatomic molecules + Returns: updated molecule """ @@ -97,12 +102,12 @@ def update_molecule(self, molecule)->None: if len(molecule.masses) != 2: raise ValueError( 'Morse potential only works for diatomic molecules!') - self._mA = molecule.masses[0] - self._mB = molecule.masses[1] + self._m_a = molecule.masses[0] + self._m_b = molecule.masses[1] - def fit(self, xdata, ydata, initial_vals=None, bounds_list=None)-> None: - """ - Fits a potential to computed molecular energies. + def fit(self, xdata: List[float], ydata: List[float], initial_vals=None, bounds_list=None + ) -> None: + """Fits a potential to computed molecular energies. Args: xdata: interatomic distance points (Angstroms) @@ -132,9 +137,9 @@ def fit(self, xdata, ydata, initial_vals=None, bounds_list=None)-> None: self.r_0 = fit[2] self.m_shift = fit[3] - def get_equilibrium_geometry(self, scaling=1.0)-> float: - """ - Returns the interatomic distance corresponding to minimal energy. + def get_equilibrium_geometry(self, scaling: float = 1.0) -> float: + """Returns the interatomic distance corresponding to minimal energy. + Args: scaling: Scaling to change units. (Default is 1.0 for Angstroms) @@ -147,9 +152,9 @@ def get_equilibrium_geometry(self, scaling=1.0)-> float: # meters. return self.r_0 * scaling - def get_minimal_energy(self, scaling=1.0): - """ - Returns the smallest molecular energy for the current fit. + def get_minimal_energy(self, scaling: float = 1.0) -> float: + """Returns the smallest molecular energy for the current fit. + Args: scaling: Scaling to change units. (Default is 1.0 for Hartrees) @@ -161,9 +166,9 @@ def get_minimal_energy(self, scaling=1.0): # Joules (per mol). return self.m_shift * scaling - def dissociation_energy(self, scaling=1.0): - """ - Returns the calculated dissociation energy for the current fit. + def dissociation_energy(self, scaling: float = 1.0) -> float: + """Returns the calculated dissociation energy for the current fit. + Args: scaling: Scaling to change units. (Default is 1.0 for Hartrees) @@ -178,54 +183,54 @@ def dissociation_energy(self, scaling=1.0): return diss_nrg * scaling - def fundamental_frequency(self)->float: - """ - Returns the fundamental frequency for the current fit (in s^-1). + def fundamental_frequency(self) -> float: + """Returns the fundamental frequency for the current fit (in s^-1). + Returns: fundamental frequency for the current fit """ - de = self.d_e * const.HARTREE_TO_J # Hartree, need J/molecule + d_e = self.d_e * const.HARTREE_TO_J # Hartree, need J/molecule alp = self.alpha * 1E10 # 1/angstrom, need 1/meter # r0 = self.r_0*1E-10 # angstrom, need meter - mr = (self._mA * self._mB) / (self._mA + self._mB) + m_r = (self._m_a * self._m_b) / (self._m_a + self._m_b) # omega_0 in units rad/s converted to 1/s by dividing by 2Pi - omega_0 = (np.sqrt((2 * de * alp ** 2) / mr)) / (2 * np.pi) + omega_0 = np.sqrt((2 * d_e * alp ** 2) / m_r) / (2 * np.pi) # fundamental frequency in s**-1 return omega_0 - def wave_number(self)->float: - """ - Returns the wave number for the current fit (in cm^-1). + def wave_number(self) -> float: + """Returns the wave number for the current fit (in cm^-1). + Returns: wave number for the current fit """ return self.fundamental_frequency() / const.C_CM_PER_S - def vibrational_energy_level(self, n:int)->float: - """ - Returns the n-th vibrational energy level for the current fit - (in Hartrees). + def vibrational_energy_level(self, n: int) -> float: + """Returns the n-th vibrational energy level for the current fit (in Hartrees). + Args: n: vibrational mode Returns: vibrational energy level for the current fit """ - de = self.d_e * const.HARTREE_TO_J # Hartree, need J/molecule + d_e = self.d_e * const.HARTREE_TO_J # Hartree, need J/molecule omega_0 = self.fundamental_frequency() e_n = const.H_J_S * omega_0 * (n + 0.5) - \ - ((const.H_J_S * omega_0 * (n + 0.5)) ** 2) / (4 * de) + ((const.H_J_S * omega_0 * (n + 0.5)) ** 2) / (4 * d_e) # energy level return e_n * const.J_TO_HARTREE - def get_maximum_trusted_level(self, mode=0)->float: + def get_maximum_trusted_level(self, mode: int = 0) -> float: """ Returns the maximum energy level for which the particular implementation still provides a good approximation of reality. + Args: mode: vibronic mode diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index 01ab4738ff..73f944bdcf 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -14,18 +14,21 @@ This module declares interfaces for implementing potential energy surface and vibrational structure of a given molecule. """ + from abc import ABC, abstractmethod -from typing import Tuple +from typing import Tuple, List from qiskit.chemistry.drivers import Molecule + class EnergySurfaceBase(ABC): """ Class to hold a potential energy surface """ @abstractmethod - def eval(self, x)-> float: + def eval(self, x: float) -> float: """ After fitting the data to the fit function, predict the energy at a point x. + Args: x: value to evaluate surface in @@ -35,24 +38,24 @@ def eval(self, x)-> float: raise NotImplementedError @abstractmethod - def fit(self, xdata, ydata, initial_vals=None, bounds_list=None)->None: + def fit(self, xdata: List[float], ydata: List[float], initial_vals=None, bounds_list=None + ) -> None: """ Fits surface to data Args: xdata: x data to be fitted ydata: y data to be fitted - - Returns: - fitted surface """ raise NotImplementedError @abstractmethod - def get_equilibrium_geometry(self, scaling=1.0)->float: - """ + def get_equilibrium_geometry(self, scaling: float = 1.0) -> float: + """Get the equilibrium energy. + Returns the geometry for the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are Angstroms. Scale by 1E-10 to get meters. + Args: scaling: scaling factor @@ -62,11 +65,13 @@ def get_equilibrium_geometry(self, scaling=1.0)->float: raise NotImplementedError @abstractmethod - def get_minimal_energy(self, scaling=1.0)->float: - """ + def get_minimal_energy(self, scaling: float = 1.0) -> float: + """Get the minimal energy. + Returns the value of the minimal energy (scaled by 'scaling') Default units (scaling=1.0) are J/mol. Scale appropriately for Hartrees. + Args: scaling: scaling factor @@ -76,8 +81,9 @@ def get_minimal_energy(self, scaling=1.0)->float: raise NotImplementedError @abstractmethod - def get_trust_region(self)->Tuple[float,float]: - """ + def get_trust_region(self) -> Tuple[float, float]: + """Get the trust region. + Returns the bounds of the region (in space) where the energy surface implementation can be trusted. When doing spline interpolation, for example, that would be the region where data @@ -93,13 +99,13 @@ def get_trust_region(self)->Tuple[float,float]: class VibronicStructureBase(ABC): """ Class to hold a molecular vibronic structure providing access to - vibrational modes and energy levels + vibrational modes and energy levels. """ - def __init__(self, molecule): + def __init__(self, molecule: Molecule) -> None: self.update_molecule(molecule) - def update_molecule(self, molecule)->Molecule: + def update_molecule(self, molecule: Molecule) -> Molecule: """ Wipe state if molecule changes, and check validity of molecule for potential. @@ -112,18 +118,18 @@ def update_molecule(self, molecule)->Molecule: self.molecule = molecule @abstractmethod - def get_num_modes(self)-> float: - """ - Returns the number of vibrational modes for the molecule + def get_num_modes(self) -> float: + """Returns the number of vibrational modes for the molecule. + Returns: the number of vibrational modes """ raise NotImplementedError @abstractmethod - def vibrational_energy_level(self, n, mode=0) -> float: - """ - Returns the n-th vibrational energy level for a given mode + def vibrational_energy_level(self, n: int, mode: int = 0) -> float: + """Returns the n-th vibrational energy level for a given mode. + Args: n: number of vibrational mode mode: vibrational mode @@ -133,11 +139,12 @@ def vibrational_energy_level(self, n, mode=0) -> float: """ raise NotImplementedError - def get_maximum_trusted_level(self, mode=0)->float: + def get_maximum_trusted_level(self, mode: int = 0) -> float: # pylint: disable=unused-argument """ Returns the maximum energy level for which the particular implementation still provides a good approximation of reality. Default value of 100. Redefined where needed (see e.g. Morse). + Args: mode: vibronic mode @@ -148,16 +155,13 @@ def get_maximum_trusted_level(self, mode=0)->float: class PotentialBase(EnergySurfaceBase, VibronicStructureBase): - """ - Class to hold prescribed 1D potentials (e.g. Morse/Harmonic) - over a degree of freedom. - """ + """Class to hold prescribed 1D potentials (e.g. Morse/Harmonic) over a degree of freedom.""" - def get_num_modes(self)->int: + def get_num_modes(self) -> int: """ This (1D) potential represents a single vibrational mode """ return 1 - def get_trust_region(self)->Tuple[float,float]: + def get_trust_region(self) -> Tuple[float, float]: """ The potential will usually be well-defined (even if not useful) for arbitrary x so we return a fairly large interval here. @@ -166,6 +170,6 @@ def get_trust_region(self)->Tuple[float,float]: return (-100, 100) @abstractmethod - def dissociation_energy(self, scaling=1.0)->float: - """ returns the dissociation energy (scaled by 'scaling')""" + def dissociation_energy(self, scaling: float = 1.0) -> float: + """Returns the dissociation energy (scaled by 'scaling')""" raise NotImplementedError From ce81016347e2a958c09b0652728299a4cba67c0f Mon Sep 17 00:00:00 2001 From: Cryoris Date: Thu, 15 Oct 2020 21:31:18 +0200 Subject: [PATCH 190/197] fix mypy --- .../algorithms/pes_samplers/energy_surface_spline.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index 4a1d7a3bc4..1d1ad3e8fe 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -15,7 +15,7 @@ bopes sampler results or potential energy surfaces. """ -from typing import Tuple, List +from typing import Tuple, List, Optional, Callable, Any import scipy.interpolate as interp from scipy.optimize import minimize_scalar @@ -30,12 +30,12 @@ def __init__(self) -> None: This allows for fitting BOPES sampler results or potential energy surfaces. """ - self._eval = None - self.eval_d = None + self._eval = None # type: Optional[Callable[[Any], Any]] + self.eval_d = None # type: Optional[Callable[[Any], Any]] self.min_x = None self.min_val = None - self.x_left = None - self.x_right = None + self.x_left = None # type: Optional[float] + self.x_right = None # type: Optional[float] def eval(self, x: float) -> float: """After fitting the data to the fit function, predict the energy at a point x. From 66ffdd61dd39bca362b3b03b9e2bfececa11e37c Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 22:17:29 +0200 Subject: [PATCH 191/197] lint more --- .../chemistry/algorithms/pes_samplers/energy_surface_spline.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index e8ae047a7a..c79266d23e 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -17,7 +17,6 @@ import scipy.interpolate as interp from scipy.optimize import minimize_scalar - from qiskit.chemistry.algorithms.pes_samplers.potentials.potential_base import EnergySurfaceBase from typing import Tuple, List From b875b7ac531d6aeba0fc111d532695346eec500d Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 22:40:54 +0200 Subject: [PATCH 192/197] add check for masses --- .../algorithms/pes_samplers/energy_surface_spline.py | 3 +-- .../pes_samplers/potentials/morse_potential.py | 12 ++++++++++-- test/chemistry/test_bopes_sampler.py | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index a4a0906ce4..9670e97bbd 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -51,8 +51,7 @@ def eval(self, x: float) -> float: return result - def fit(self, xdata: List[float], ydata: List[float], initial_vals=None, bounds_list=None - ) -> None: + def fit(self, xdata: List[float], ydata: List[float]) -> None: """Fits surface to data. Args: diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 5fb2556a26..1ff0af88c6 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -49,8 +49,13 @@ def __init__(self, molecule): self.m_shift = 0.0 self.alpha = 0.0 self.r_0 = 0.0 - self._m_a = molecule.masses[0] - self._m_b = molecule.masses[1] + if molecule.masses is not None: + self._m_a = molecule.masses[0] + self._m_b = molecule.masses[1] + else: + raise ValueError( + 'Molecule massses need to be provided') + @staticmethod def fit_function(x: float, @@ -99,6 +104,9 @@ def update_molecule(self, molecule: Molecule) -> None: updated molecule """ # Check the provided molecule + if molecule.masses is None: + raise ValueError( + 'Molecule massses need to be provided') if len(molecule.masses) != 2: raise ValueError( 'Morse potential only works for diatomic molecules!') diff --git a/test/chemistry/test_bopes_sampler.py b/test/chemistry/test_bopes_sampler.py index 9517720ddf..dfec35f7c2 100644 --- a/test/chemistry/test_bopes_sampler.py +++ b/test/chemistry/test_bopes_sampler.py @@ -96,7 +96,7 @@ def test_h2_bopes_sampler(self): np.testing.assert_array_almost_equal(points_run, [0.7, 1.0, 1.3]) np.testing.assert_array_almost_equal(energies, - [-1.13618945, -1.10115033, -1.03518627], decimal=3) + [-1.13618945, -1.10115033, -1.03518627], decimal=2) def test_potential_interface(self): """Tests potential interface.""" From 1b065faebda7bfed7ef5f9eb9a277f0e3d3c044a Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 22:43:55 +0200 Subject: [PATCH 193/197] fix --- .../chemistry/algorithms/pes_samplers/energy_surface_spline.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index 9670e97bbd..67d30560e0 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -57,8 +57,6 @@ def fit(self, xdata: List[float], ydata: List[float]) -> None: Args: xdata: x data to be fitted ydata: y data to be fitted - initial_vals: TODO - bounds_list: TODO """ newx = xdata newy = ydata From f0356b403cc3967d300fd14c18518e75ee751ff2 Mon Sep 17 00:00:00 2001 From: Panagiotis Barkoutsos Date: Thu, 15 Oct 2020 22:51:34 +0200 Subject: [PATCH 194/197] fix docstring --- .../algorithms/pes_samplers/potentials/morse_potential.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 1ff0af88c6..54f72724f2 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -100,8 +100,6 @@ def update_molecule(self, molecule: Molecule) -> None: Raises: ValueError: Only implemented for diatomic molecules - Returns: - updated molecule """ # Check the provided molecule if molecule.masses is None: From ff131769bb831525f17a974a3c41898f1363eee8 Mon Sep 17 00:00:00 2001 From: Anton Dekusar Date: Thu, 15 Oct 2020 22:49:29 +0100 Subject: [PATCH 195/197] spelling, style --- .pylintdict | 3 +++ .../chemistry/algorithms/pes_samplers/energy_surface_spline.py | 2 +- .../algorithms/pes_samplers/potentials/morse_potential.py | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.pylintdict b/.pylintdict index 0d7f24ef9c..536c2aeac7 100644 --- a/.pylintdict +++ b/.pylintdict @@ -57,6 +57,7 @@ bohr bool boolean bools +bopes bosonic bpa brassard @@ -259,6 +260,7 @@ grover gsc gset gsls +gss gto gtol hadamard @@ -769,6 +771,7 @@ workq wrt wvf xatol +xdata xixj xopt xs diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py index 67d30560e0..6ae3109df2 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """ -An spline interpolation method for data fitting. This allows for fiting +An spline interpolation method for data fitting. This allows for fitting bopes sampler results or potential energy surfaces. """ diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 54f72724f2..5409434c3a 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -54,8 +54,7 @@ def __init__(self, molecule): self._m_b = molecule.masses[1] else: raise ValueError( - 'Molecule massses need to be provided') - + 'Molecule masses need to be provided') @staticmethod def fit_function(x: float, From 1f6770a52781647cecf216becf10462e736423af Mon Sep 17 00:00:00 2001 From: Stefan Woerner Date: Thu, 15 Oct 2020 23:53:42 +0200 Subject: [PATCH 196/197] update bopes sampler --- .../{ => potentials}/energy_surface_spline.py | 11 ++++++++++- .../potentials/harmonic_potential.py | 15 +++++---------- .../potentials/morse_potential.py | 14 ++++++++------ .../pes_samplers/potentials/potential_base.py | 19 +++++++++++++------ test/chemistry/test_potential.py | 7 ++++--- 5 files changed, 40 insertions(+), 26 deletions(-) rename qiskit/chemistry/algorithms/pes_samplers/{ => potentials}/energy_surface_spline.py (85%) diff --git a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/energy_surface_spline.py similarity index 85% rename from qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py rename to qiskit/chemistry/algorithms/pes_samplers/potentials/energy_surface_spline.py index 67d30560e0..46d6a8a5ad 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/energy_surface_spline.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/energy_surface_spline.py @@ -51,12 +51,21 @@ def eval(self, x: float) -> float: return result - def fit(self, xdata: List[float], ydata: List[float]) -> None: + def fit(self, xdata: List[float], ydata: List[float], + initial_vals: Optional[List[float]] = None, + bounds_list: Optional[Tuple[List[float], List[float]]] = None + ) -> None: """Fits surface to data. Args: xdata: x data to be fitted ydata: y data to be fitted + initial_vals: Initial values for fit parameters. None for default. + Order of parameters is d_e, alpha, r_0 and m_shift + (see fit_function implementation) + bounds_list: Bounds for the fit parameters. None for default. + Order of parameters is d_e, alpha, r_0 and m_shift + (see fit_function implementation) """ newx = xdata newy = ydata diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index e8ec941813..85ca3e8bdb 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -12,7 +12,7 @@ """This module implements a 1D Harmonic potential.""" -from typing import Tuple, List +from typing import Tuple, List, Optional import numpy as np from scipy.optimize import curve_fit @@ -84,13 +84,15 @@ def update_molecule(self, molecule: Molecule) -> Molecule: ValueError: Only implemented for diatomic molecules """ # Check the provided molecule + super().update_molecule(molecule) if len(molecule.masses) != 2: raise ValueError('Harmonic potential only works for diatomic molecules!') self._m_a = molecule.masses[0] self._m_b = molecule.masses[1] - def fit(self, xdata, ydata, initial_vals=None, bounds_list=None, - preprocess_data=True) -> None: + def fit(self, xdata: List[float], ydata: List[float], + initial_vals: Optional[List[float]] = None, + bounds_list: Optional[Tuple[List[float], List[float]]] = None) -> None: """Fits a potential to computed molecular energies. Args: @@ -102,10 +104,6 @@ def fit(self, xdata, ydata, initial_vals=None, bounds_list=None, bounds_list: Bounds for the fit parameters. None for default. Order of parameters is k, r_0 and m_shift (see fit_function implementation) - preprocess_data: Default True. Internally cleans the data so - that a fit is done only around the minimum. The wider range of - distances and energies provided is still taken into account in - estimating a dissociation energy for the molecule. """ # Fits the potential to given x/y data. # If preProcessData is True (i.e. by default!) it only tries to fit @@ -120,9 +118,6 @@ def fit(self, xdata, ydata, initial_vals=None, bounds_list=None, xdata_fit = xdata ydata_fit = ydata - if preprocess_data: - xdata_fit, ydata_fit = HarmonicPotential.process_fit_data( - xdata, ydata) fit, _ = curve_fit(self.fit_function, xdata_fit, ydata_fit, p0=h_p0, maxfev=100000, bounds=h_bounds) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py index 54f72724f2..f129d9e511 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/morse_potential.py @@ -14,7 +14,7 @@ This module implements a 1D Morse potential. """ -from typing import List +from typing import List, Optional, Tuple import numpy as np from scipy.optimize import curve_fit @@ -31,7 +31,7 @@ class MorsePotential(PotentialBase): """ # Works in Angstroms and Hartrees - def __init__(self, molecule): + def __init__(self, molecule: Molecule): """ Initializes the potential to the zero-function. fit() should be used afterwards to fit the potential to @@ -56,7 +56,6 @@ def __init__(self, molecule): raise ValueError( 'Molecule massses need to be provided') - @staticmethod def fit_function(x: float, d_e: float, @@ -102,6 +101,7 @@ def update_molecule(self, molecule: Molecule) -> None: """ # Check the provided molecule + super().update_molecule(molecule) if molecule.masses is None: raise ValueError( 'Molecule massses need to be provided') @@ -111,7 +111,9 @@ def update_molecule(self, molecule: Molecule) -> None: self._m_a = molecule.masses[0] self._m_b = molecule.masses[1] - def fit(self, xdata: List[float], ydata: List[float], initial_vals=None, bounds_list=None + def fit(self, xdata: List[float], ydata: List[float], + initial_vals: Optional[List[float]] = None, + bounds_list: Optional[Tuple[List[float], List[float]]] = None ) -> None: """Fits a potential to computed molecular energies. @@ -232,13 +234,13 @@ def vibrational_energy_level(self, n: int) -> float: # energy level return e_n * const.J_TO_HARTREE - def get_maximum_trusted_level(self, mode: int = 0) -> float: + def get_maximum_trusted_level(self, n: int = 0) -> float: """ Returns the maximum energy level for which the particular implementation still provides a good approximation of reality. Args: - mode: vibronic mode + n: vibronic mode Returns: maximum_trusted_level estimated diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py index 73f944bdcf..8c01a8bb83 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/potential_base.py @@ -16,7 +16,7 @@ """ from abc import ABC, abstractmethod -from typing import Tuple, List +from typing import Tuple, List, Optional from qiskit.chemistry.drivers import Molecule @@ -38,13 +38,21 @@ def eval(self, x: float) -> float: raise NotImplementedError @abstractmethod - def fit(self, xdata: List[float], ydata: List[float], initial_vals=None, bounds_list=None + def fit(self, xdata: List[float], ydata: List[float], + initial_vals: Optional[List[float]] = None, + bounds_list: Optional[Tuple[List[float], List[float]]] = None ) -> None: """ Fits surface to data Args: xdata: x data to be fitted ydata: y data to be fitted + initial_vals: Initial values for fit parameters. None for default. + Order of parameters is d_e, alpha, r_0 and m_shift + (see fit_function implementation) + bounds_list: Bounds for the fit parameters. None for default. + Order of parameters is d_e, alpha, r_0 and m_shift + (see fit_function implementation) """ raise NotImplementedError @@ -127,26 +135,25 @@ def get_num_modes(self) -> float: raise NotImplementedError @abstractmethod - def vibrational_energy_level(self, n: int, mode: int = 0) -> float: + def vibrational_energy_level(self, n: int) -> float: """Returns the n-th vibrational energy level for a given mode. Args: n: number of vibrational mode - mode: vibrational mode Returns: n-th vibrational energy level for a given mode """ raise NotImplementedError - def get_maximum_trusted_level(self, mode: int = 0) -> float: # pylint: disable=unused-argument + def get_maximum_trusted_level(self, n: int = 0) -> float: # pylint: disable=unused-argument """ Returns the maximum energy level for which the particular implementation still provides a good approximation of reality. Default value of 100. Redefined where needed (see e.g. Morse). Args: - mode: vibronic mode + n: vibronic mode Returns: maximum_trusted_level setted diff --git a/test/chemistry/test_potential.py b/test/chemistry/test_potential.py index eb1b15e590..69c36b1e43 100644 --- a/test/chemistry/test_potential.py +++ b/test/chemistry/test_potential.py @@ -81,11 +81,11 @@ def test_morse(self): result = np.array([minimal_energy_distance, minimal_energy, wave_number]) benchmark = np.array([0.8106703001726382, -1.062422610690636, 3800.7855102410026]) - np.testing.assert_array_almost_equal(result, benchmark) + np.testing.assert_array_almost_equal(result, benchmark, decimal=4) radia = np.array([0.5, 1, 1.5, 2]) hartrees = np.array([-0.94045495, -1.04591482, -0.96876003, -0.92400906]) - np.testing.assert_array_almost_equal(hartrees, morse.eval(radia)) + np.testing.assert_array_almost_equal(hartrees, morse.eval(radia), decimal=4) vib_levels = [] for level in range(2, 8): @@ -94,7 +94,7 @@ def test_morse(self): vib_levels_ref = np.array([0.04052116451981064, 0.05517676610999135, 0.06894501671860434, 0.08182591634564956, 0.09381946499112709, 0.10492566265503685]) - np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref) + np.testing.assert_array_almost_equal(vib_levels, vib_levels_ref, decimal=4) def test_harmonic(self): """ test harmonic """ @@ -134,6 +134,7 @@ def test_harmonic(self): xdata = np.array(xdata_angstrom) ydata = np.array(ydata_hartree) + xdata, ydata = HarmonicPotential.process_fit_data(xdata, ydata) harmonic.fit(xdata, ydata) minimal_energy_distance = harmonic.get_equilibrium_geometry() From 58f66068bb017d78f5f0bd62655a7cb742a206e2 Mon Sep 17 00:00:00 2001 From: Stefan Woerner Date: Fri, 16 Oct 2020 00:05:20 +0200 Subject: [PATCH 197/197] Update harmonic_potential.py --- .../pes_samplers/potentials/harmonic_potential.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py index 85ca3e8bdb..bc4291279f 100644 --- a/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py +++ b/qiskit/chemistry/algorithms/pes_samplers/potentials/harmonic_potential.py @@ -43,8 +43,12 @@ def __init__(self, molecule: Molecule) -> None: self.m_shift = 0.0 self.r_0 = 0.0 self.d_e = None - self._m_a = molecule.masses[0] - self._m_b = molecule.masses[1] + if molecule.masses is not None: + self._m_a = molecule.masses[0] + self._m_b = molecule.masses[1] + else: + raise ValueError( + 'Molecule masses need to be provided') @staticmethod def fit_function(x: float, k: float, r_0: float, m_shift: float) -> float: