From a5d9a04029044e18acbe358b1fcbf8283c92d98e Mon Sep 17 00:00:00 2001 From: Max Rossmannek Date: Thu, 7 Sep 2023 15:28:10 +0200 Subject: [PATCH] refactor: remove all deprecated code (#1248) * refactor: remove all deprecated code This commit removes all deprecated code. Even though this is done a bit earlier than originally announced under the Qiskit deprecation policy, we are taking this step now since this project has become part of a community effort. As a direct consequence, new codeowners will soon join the team behind Qiskit Nature. Rather than handing over a codebase that includes pieces of code which need attention in the near future (and even continued support until then) we are removing these parts early. As a consequence, if you still require parts of this code, we suggest you stick to Qiskit Nature 0.6 until you can transition based on the provided migration guides. This commit also removes the deprecation utilities which are superseded by the ones provided by Qiskit directly (see qiskit.utils.deprecation). * docs: remove doctest from migration guides Now that the old code covered by the migration guide is being removed, doctest can no longer execute this code. Thus, this commit replaces the doctest directives with simple code cells. * docs: add 0.7 prelude * docs: add spelling exceptions * docs: fix indent in release note * fix: remove final occurrence of opflow * lint: run black * fix: remove final occurrences of removed setting attributes * docs: removes a now obsolete release note * fix: remove left over setting --- .pylintdict | 2 + docs/howtos/adapt_vqe.rst | 11 - docs/migration/0.6_b_mes_factory.rst | 18 +- docs/migration/0.6_c_qubit_converter.rst | 34 +- qiskit_nature/deprecation.py | 556 ---------------- qiskit_nature/runtime/__init__.py | 38 -- qiskit_nature/runtime/vqe_client.py | 431 ------------- qiskit_nature/second_q/algorithms/__init__.py | 34 - .../excited_states_solvers/__init__.py | 3 - .../eigensolver_factories/__init__.py | 27 - .../eigensolver_factory.py | 36 -- .../numpy_eigensolver_factory.py | 120 ---- .../excited_states_eigensolver.py | 92 +-- .../excited_states_solver.py | 9 +- .../algorithms/excited_states_solvers/qeom.py | 336 ++-------- .../qeom_electronic_ops_builder.py | 45 +- .../qeom_vibrational_ops_builder.py | 41 +- .../ground_state_solvers/__init__.py | 10 - .../ground_state_eigensolver.py | 81 +-- .../ground_state_solver.py | 38 +- .../minimum_eigensolver_factories/__init__.py | 34 - .../minimum_eigensolver_factory.py | 70 --- .../numpy_minimum_eigensolver_factory.py | 101 --- .../vqe_ucc_factory.py | 190 ------ .../vqe_uvcc_factory.py | 182 ------ .../initial_points/mp2_initial_point.py | 7 +- .../circuit/library/ansatzes/puccd.py | 22 +- .../circuit/library/ansatzes/succd.py | 23 +- .../second_q/circuit/library/ansatzes/ucc.py | 52 +- .../circuit/library/ansatzes/uccsd.py | 23 +- .../second_q/circuit/library/ansatzes/uvcc.py | 52 +- .../circuit/library/ansatzes/uvccsd.py | 22 +- .../circuit/library/bogoliubov_transform.py | 24 +- .../fermionic_gaussian_state.py | 26 +- .../library/initial_states/hartree_fock.py | 99 +-- .../initial_states/slater_determinant.py | 24 +- .../circuit/library/initial_states/vscf.py | 86 +-- .../second_q/drivers/pyscfd/pyscfdriver.py | 51 +- .../second_q/formats/fcidump/fcidump.py | 139 +--- .../second_q/formats/fcidump/parser.py | 10 +- .../second_q/formats/qcschema/qc_schema.py | 208 ------ .../second_q/formats/qcschema_translator.py | 3 - .../hamiltonians/electronic_energy.py | 13 +- qiskit_nature/second_q/mappers/__init__.py | 11 - qiskit_nature/second_q/mappers/bksf.py | 6 +- .../second_q/mappers/bosonic_linear_mapper.py | 11 - .../second_q/mappers/bravyi_kitaev_mapper.py | 6 +- .../second_q/mappers/direct_mapper.py | 6 +- .../second_q/mappers/fermionic_mapper.py | 3 +- .../mappers/interleaved_qubit_mapper.py | 8 +- .../second_q/mappers/jordan_wigner_mapper.py | 6 +- .../second_q/mappers/linear_mapper.py | 28 +- .../second_q/mappers/logarithmic_mapper.py | 16 +- .../second_q/mappers/parity_mapper.py | 20 +- .../second_q/mappers/qubit_converter.py | 582 ----------------- .../second_q/mappers/qubit_mapper.py | 56 +- qiskit_nature/second_q/mappers/spin_mapper.py | 3 +- .../second_q/mappers/tapered_qubit_mapper.py | 46 +- .../second_q/mappers/vibrational_mapper.py | 3 +- .../second_q/operators/bosonic_op.py | 6 - .../operators/electronic_integrals.py | 7 +- .../second_q/operators/fermionic_op.py | 87 --- .../second_q/operators/polynomial_tensor.py | 41 +- qiskit_nature/second_q/operators/spin_op.py | 6 - .../second_q/operators/vibrational_op.py | 6 - .../second_q/problems/base_problem.py | 45 +- .../second_q/problems/eigenstate_result.py | 20 - .../problems/electronic_structure_problem.py | 81 +-- .../transformers/basis_transformer.py | 6 +- qiskit_nature/settings.py | 155 +---- .../notes/0.7-prelude-169d294985170fd5.yaml | 19 + ...removes-legacy-stack-0a98952b6c174aaa.yaml | 11 - test/__init__.py | 4 +- test/nature_test_case.py | 30 - test/runtime/__init__.py | 13 - test/runtime/fake_vqeruntime.py | 101 --- test/runtime/test_vqeprogram.py | 108 ---- .../eigensolver_factories/__init__.py | 11 - .../test_numpy_eigensolver_factory.py | 86 --- .../test_bosonic_esc_calculation.py | 73 ++- .../test_excited_states_solvers.py | 147 +---- ...test_excited_states_solvers_auxiliaries.py | 85 +-- .../test_qeom_electronic_ops.py | 39 +- .../test_qeom_vibrational_ops.py | 38 +- .../minimum_eigensolver_factories/__init__.py | 11 - .../test_vqe_ucc_factory.py | 122 ---- .../test_vqe_uvcc_factory.py | 119 ---- .../test_advanced_ucc_variants.py | 15 +- .../test_groundstate_eigensolver.py | 279 +++------ .../test_groundstate_eigensolver_mapper.py | 396 ------------ .../ground_state_solvers/test_swaprz.py | 3 +- .../initial_points/test_mp2_initial_point.py | 4 +- .../circuit/library/ansatzes/test_chc.py | 19 +- .../circuit/library/ansatzes/test_puccd.py | 59 +- .../circuit/library/ansatzes/test_succd.py | 154 ++--- .../circuit/library/ansatzes/test_ucc.py | 82 +-- .../circuit/library/ansatzes/test_uccsd.py | 98 +-- .../circuit/library/ansatzes/test_uvcc.py | 62 +- .../test_fermionic_gaussian_state.py | 41 +- .../initial_states/test_hartree_fock.py | 145 +---- .../test_hartree_fock_mapper.py | 134 ---- .../initial_states/test_slater_determinant.py | 36 +- .../library/initial_states/test_vscf.py | 15 +- .../library/test_bogoliubov_transform.py | 35 +- .../pyscfd/test_driver_methods_pyscf.py | 49 +- .../drivers/pyscfd/test_driver_pyscf.py | 76 +-- .../drivers/test_driver_methods_gsc.py | 7 +- test/second_q/formats/fcidump/test_fcidump.py | 31 +- .../formats/fcidump/test_fcidump_dumper.py | 64 +- .../formats/fcidump/test_methods_fcidump.py | 99 +-- test/second_q/formats/test_qcschema.py | 37 -- .../test_quadratic_hamiltonian.py | 47 +- .../resources/reference_direct_mapper.py | 5 +- test/second_q/mappers/test_bksf_mapper.py | 109 +--- .../mappers/test_bosonic_linear_mapper.py | 26 +- .../mappers/test_bravyi_kitaev_mapper.py | 114 +--- test/second_q/mappers/test_direct_mapper.py | 27 +- .../mappers/test_interleaved_qubit_mapper.py | 18 +- .../mappers/test_jordan_wigner_mapper.py | 140 ++--- test/second_q/mappers/test_linear_mapper.py | 14 +- .../mappers/test_logarithmic_mapper.py | 14 +- test/second_q/mappers/test_parity_mapper.py | 81 +-- test/second_q/mappers/test_qubit_converter.py | 571 ----------------- .../mappers/test_tapered_qubit_mapper.py | 165 +---- test/second_q/operators/test_fermionic_op.py | 85 --- .../operators/test_polynomial_tensor.py | 16 +- .../operators/test_symmetric_two_body.py | 492 ++++++--------- .../test_electronic_structure_problem.py | 32 - .../properties/test_electronic_density.py | 4 +- test/test_deprecation.py | 591 ------------------ test/test_end2end_with_vqe.py | 13 +- test/utils/test_linalg.py | 4 +- 132 files changed, 1181 insertions(+), 9038 deletions(-) delete mode 100644 qiskit_nature/deprecation.py delete mode 100644 qiskit_nature/runtime/__init__.py delete mode 100644 qiskit_nature/runtime/vqe_client.py delete mode 100644 qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/__init__.py delete mode 100644 qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/eigensolver_factory.py delete mode 100644 qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/numpy_eigensolver_factory.py delete mode 100644 qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py delete mode 100644 qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py delete mode 100644 qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py delete mode 100644 qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_ucc_factory.py delete mode 100644 qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uvcc_factory.py delete mode 100644 qiskit_nature/second_q/mappers/qubit_converter.py create mode 100644 releasenotes/notes/0.7-prelude-169d294985170fd5.yaml delete mode 100644 releasenotes/notes/removes-legacy-stack-0a98952b6c174aaa.yaml delete mode 100644 test/runtime/__init__.py delete mode 100644 test/runtime/fake_vqeruntime.py delete mode 100644 test/runtime/test_vqeprogram.py delete mode 100644 test/second_q/algorithms/excited_state_solvers/eigensolver_factories/__init__.py delete mode 100644 test/second_q/algorithms/excited_state_solvers/eigensolver_factories/test_numpy_eigensolver_factory.py delete mode 100644 test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py delete mode 100644 test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/test_vqe_ucc_factory.py delete mode 100644 test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/test_vqe_uvcc_factory.py delete mode 100644 test/second_q/algorithms/ground_state_solvers/test_groundstate_eigensolver_mapper.py delete mode 100644 test/second_q/circuit/library/initial_states/test_hartree_fock_mapper.py delete mode 100644 test/second_q/mappers/test_qubit_converter.py delete mode 100644 test/test_deprecation.py diff --git a/.pylintdict b/.pylintdict index 9a7217fc2..d5213ea68 100644 --- a/.pylintdict +++ b/.pylintdict @@ -86,6 +86,7 @@ cls cmap cnot codec +codeowners coeff coeffs colormap @@ -395,6 +396,7 @@ observables occupancies ok ollitrault +onboarding onee oneeints onsite diff --git a/docs/howtos/adapt_vqe.rst b/docs/howtos/adapt_vqe.rst index 46b24a1b5..19229662e 100644 --- a/docs/howtos/adapt_vqe.rst +++ b/docs/howtos/adapt_vqe.rst @@ -6,17 +6,6 @@ algorithm has been migrated to Qiskit Terra (released in v0.22). This tutorial outlines how the algorithm can be used. -0. We ensure the use of :class:`~qiskit.opflow.primitive_ops.PauliSumOp` (this is the default value - of this setting for now but we enforce it here to ensure stability of this guide as long as the - :class:`~qiskit.algorithms.minimum_eigensolvers.AdaptVQE` class is not yet guaranteed to handle - the :class:`~qiskit.quantum_info.SparsePauliOp` successor properly): - -.. testcode:: - - from qiskit_nature import settings - - settings.use_pauli_sum_op = True - 1. We obtain an :class:`~qiskit_nature.second_q.problems.ElectronicStructureProblem` which we want to solve: diff --git a/docs/migration/0.6_b_mes_factory.rst b/docs/migration/0.6_b_mes_factory.rst index 9a26670ab..2976fd038 100644 --- a/docs/migration/0.6_b_mes_factory.rst +++ b/docs/migration/0.6_b_mes_factory.rst @@ -36,7 +36,7 @@ For the following examples, we need a simple :class:`~qiskit_nature.second_q.problems.ElectronicStructureProblem` which we can obtain from a :class:`~qiskit_nature.second_q.drivers.PySCFDriver` like so: -.. testcode:: +.. code:: ipython3 from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import ParityMapper @@ -58,7 +58,7 @@ VQEUCCFactory The old way: -.. testcode:: +.. code:: ipython3 from qiskit.algorithms.optimizers import SLSQP from qiskit.primitives import Estimator @@ -72,13 +72,13 @@ The old way: result = solver.compute_minimum_eigenvalue(qubit_op, aux_ops) print(f"Eigenvalue = {result.eigenvalue: .6f}") -.. testoutput:: +.. parsed-literal:: Eigenvalue = -1.857275 And the corresponding new way: -.. testcode:: +.. code:: ipython3 from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import SLSQP @@ -107,7 +107,7 @@ And the corresponding new way: result = solver.compute_minimum_eigenvalue(qubit_op, aux_ops) print(f"Eigenvalue = {result.eigenvalue: .6f}") -.. testoutput:: +.. parsed-literal:: Eigenvalue = -1.857275 @@ -116,7 +116,7 @@ NumPyEigensolverFactory The old way: -.. testcode:: +.. code:: ipython3 from qiskit_nature.second_q.algorithms import NumPyEigensolverFactory @@ -132,7 +132,7 @@ The old way: for idx, eigenvalue in enumerate(result.eigenvalues): print(f"{idx}: {eigenvalue: .6f}") -.. testoutput:: +.. parsed-literal:: 0: -1.857275 1: -0.882722 @@ -140,7 +140,7 @@ The old way: And the corresponding new way: -.. testcode:: +.. code:: ipython3 from qiskit.algorithms.eigensolvers import NumPyEigensolver @@ -152,7 +152,7 @@ And the corresponding new way: for idx, eigenvalue in enumerate(result.eigenvalues): print(f"{idx}: {eigenvalue: .6f}") -.. testoutput:: +.. parsed-literal:: 0: -1.857275 1: -0.882722 diff --git a/docs/migration/0.6_c_qubit_converter.rst b/docs/migration/0.6_c_qubit_converter.rst index 4beaeb892..e12cf1b8f 100644 --- a/docs/migration/0.6_c_qubit_converter.rst +++ b/docs/migration/0.6_c_qubit_converter.rst @@ -17,7 +17,7 @@ Setup For the examples in this guide, we will always be using the following :class:`~qiskit_nature.second_q.operators.FermionicOp`: -.. testcode:: +.. code:: ipython3 from qiskit_nature.second_q.drivers import PySCFDriver @@ -29,7 +29,7 @@ For the examples in this guide, we will always be using the following for label, coeff in sorted(hamiltonian.items()): print(f"{coeff:+.8f} * '{label}'") -.. testoutput:: +.. parsed-literal:: +0.33785508 * '+_0 +_0 -_0 -_0' +0.09046560 * '+_0 +_0 -_1 -_1' @@ -80,7 +80,7 @@ now set the value of :attr:`~qiskit_nature.settings.QiskitNatureSettings.use_pau To ensure that we can consistently rely on using the :class:`~qiskit.quantum_info.SparsePauliOp` in the following parts of this guide, we are applying this setting here: -.. testcode:: +.. code:: ipython3 from qiskit_nature import settings @@ -102,7 +102,7 @@ In the simplest cases, all you did was pass a :class:`~qiskit_nature.second_q.ma object into the :class:`~qiskit_nature.second_q.mappers.QubitConverter`. For example, somewhat like this: -.. testcode:: +.. code:: ipython3 from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitConverter @@ -115,14 +115,14 @@ object from the example above into whichever place you were using it before. If you were working directly with some :class:`~qiskit_nature.second_q.operators.SparseLabelOp` like so: -.. testcode:: +.. code:: ipython3 qubit_op = converter.convert(hamiltonian) for pauli, coeff in sorted(qubit_op.label_iter()): print(f"{coeff.real:+.8f} * {pauli}") -.. testoutput:: +.. parsed-literal:: -0.81054798 * IIII +0.17218393 * IIIZ @@ -142,14 +142,14 @@ so: You should now directly use the ``mapper`` again, but its method is called ``.map``: -.. testcode:: +.. code:: ipython3 qubit_op = mapper.map(hamiltonian) for pauli, coeff in sorted(qubit_op.label_iter()): print(f"{coeff.real:+.8f} * {pauli}") -.. testoutput:: +.. parsed-literal:: -0.81054798 * IIII +0.17218393 * IIIZ @@ -185,7 +185,7 @@ able to use the ``two_qubit_reduction=True`` option of the to the :class:`~qiskit_nature.second_q.mappers.ParityMapper`, is now directly built into said mapper. So if you were doing something along these lines: -.. testcode:: +.. code:: ipython3 from qiskit_nature.second_q.mappers import ParityMapper @@ -196,7 +196,7 @@ mapper. So if you were doing something along these lines: for pauli, coeff in sorted(reduced_op.label_iter()): print(f"{coeff.real:+.8f} * {pauli}") -.. testoutput:: +.. parsed-literal:: -1.05237325 * II +0.39793742 * IZ @@ -206,7 +206,7 @@ mapper. So if you were doing something along these lines: The equivalent code now looks like the following: -.. testcode:: +.. code:: ipython3 mapper = ParityMapper(num_particles=problem.num_particles) @@ -215,7 +215,7 @@ The equivalent code now looks like the following: for pauli, coeff in sorted(reduced_op.label_iter()): print(f"{coeff.real:+.8f} * {pauli}") -.. testoutput:: +.. parsed-literal:: -1.05237325 * II +0.39793742 * IZ @@ -235,7 +235,7 @@ name: :class:`~qiskit.quantum_info.analysis.z2_symmetries.Z2Symmetries`), you sh In the past, you would have enabled this like so: -.. testcode:: +.. code:: ipython3 mapper = JordanWignerMapper() converter = QubitConverter(mapper, z2symmetry_reduction="auto") @@ -245,7 +245,7 @@ which would then later use sector of the Hilbert space in which the solution of your problem lies. This was only supported by the :class:`~qiskit_nature.second_q.problems.ElectronicStructureProblem`. Below is a quick example: -.. testcode:: +.. code:: ipython3 tapered_op = converter.convert( hamiltonian, @@ -256,7 +256,7 @@ the :class:`~qiskit_nature.second_q.problems.ElectronicStructureProblem`. Below for pauli, coeff in sorted(tapered_op.label_iter()): print(f"{coeff.real:+.8f} * {pauli}") -.. testoutput:: +.. parsed-literal:: -1.04109314 * I +0.18093120 * X @@ -266,7 +266,7 @@ Now, all you need to do is the use the :meth:`~qiskit_nature.second_q.problems.BaseProblem.get_tapered_mapper` method and provide the original mapper which you would like to wrap: -.. testcode:: +.. code:: ipython3 tapered_mapper = problem.get_tapered_mapper(mapper) @@ -275,7 +275,7 @@ original mapper which you would like to wrap: for pauli, coeff in sorted(tapered_op.label_iter()): print(f"{coeff.real:+.8f} * {pauli}") -.. testoutput:: +.. parsed-literal:: -1.04109314 * I +0.18093120 * X diff --git a/qiskit_nature/deprecation.py b/qiskit_nature/deprecation.py deleted file mode 100644 index 999f8e16d..000000000 --- a/qiskit_nature/deprecation.py +++ /dev/null @@ -1,556 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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. - -"""Contains the Deprecation message methods.""" - -from abc import abstractmethod -import warnings -import functools -import inspect -from typing import NamedTuple, Optional, Callable, Dict, Set, cast, Any, List, Type -from enum import Enum, EnumMeta - - -class NatureDeprecationWarning(DeprecationWarning): - """Deprecation Category for Qiskit Nature.""" - - pass - - -class DeprecatedEnum(Enum): - """ - Shows deprecate message whenever member is accessed - """ - - def __new__(cls, value, *args): - member = object.__new__(cls) - member._value_ = value - member._args = args - member._show_deprecate = member.deprecate - return member - - @abstractmethod - def deprecate(self): - """show deprecate message""" - pass - - -class DeprecatedEnumMeta(EnumMeta): - """ - Shows deprecate message whenever member is accessed - """ - - def __getattribute__(cls, name): - obj = super().__getattribute__(name) - if isinstance(obj, DeprecatedEnum) and obj._show_deprecate: - obj._show_deprecate() - return obj - - def __getitem__(cls, name): - member = super().__getitem__(name) - if member._show_deprecate: - member._show_deprecate() - return member - - # pylint: disable=redefined-builtin - def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1): - obj = super().__call__( - value, names, module=module, qualname=qualname, type=type, start=start - ) - if isinstance(obj, DeprecatedEnum) and obj._show_deprecate: - obj._show_deprecate() - return obj - - -class DeprecatedType(Enum): - """ " Deprecation Types""" - - PACKAGE = "package" - ENUM = "enum" - CLASS = "class" - METHOD = "method" - FUNCTION = "function" - ARGUMENT = "argument" - PROPERTY = "property" - - -class _DeprecatedTypeName(NamedTuple): - version: str - old_type: DeprecatedType - old_name: str - new_type: DeprecatedType - new_name: str - additional_msg: str - - -class _DeprecatedArgumentType(NamedTuple): - version: str - func_qualname: str - old_type: str - new_type: str - additional_msg: str - - -class _DeprecatedArgument(NamedTuple): - version: str - func_qualname: str - old_arg: str - new_arg: str - additional_msg: str - - -class _DeprecatedValue(NamedTuple): - version: str - func_qualname: str - argument: str - old_value: str - new_value: str - additional_msg: str - - -_DEPRECATED_OBJECTS: Set[NamedTuple] = set() - - -def warn_deprecated( - version: str, - old_type: DeprecatedType, - old_name: str, - new_type: Optional[DeprecatedType] = None, - new_name: Optional[str] = None, - additional_msg: Optional[str] = None, - stack_level: int = 2, - category: Type[Warning] = DeprecationWarning, -) -> None: - """Emits deprecation warning the first time only - Args: - version: Version to be used - old_type: Old type to be used - old_name: Old name to be used - new_type: New type to be used, if None, old_type is used instead. - new_name: New name to be used - additional_msg: any additional message - stack_level: stack level - category: warning category - """ - # skip if it was already added - obj = _DeprecatedTypeName(version, old_type, old_name, new_type, new_name, additional_msg) - if obj in _DEPRECATED_OBJECTS: - return - - _DEPRECATED_OBJECTS.add(cast(NamedTuple, obj)) - - msg = ( - f"The {old_name} {old_type.value} is deprecated as of version {version} " - "and will be removed no sooner than 3 months after the release" - ) - if new_name: - type_str = new_type.value if new_type is not None else old_type.value - msg += f". Instead use the {new_name} {type_str}" - if additional_msg: - msg += f" {additional_msg}" - msg += "." - - warnings.warn(msg, category=category, stacklevel=stack_level + 1) - - -def warn_deprecated_same_type_name( - version: str, - new_type: DeprecatedType, - new_name: str, - additional_msg: Optional[str] = None, - stack_level: int = 2, - category: Type[Warning] = DeprecationWarning, -) -> None: - """Emits deprecation warning the first time only - Used when the type and name remained the same. - Args: - version: Version to be used - new_type: new type to be used - new_name: new name to be used - additional_msg: any additional message - stack_level: stack level - category: warning category - """ - - warn_deprecated( - version, - old_type=new_type, - old_name=new_name, - new_type=new_type, - new_name=new_name, - additional_msg=additional_msg, - stack_level=stack_level + 1, - category=category, - ) - - -def warn_deprecated_type( - version: str, - argument_name: str, - old_type: str, - new_type: Optional[str] = None, - additional_msg: Optional[str] = None, - stack_level: int = 2, - category: Type[Warning] = DeprecationWarning, -) -> None: - """Emits deprecation warning the first time only - Used when only a specific supported type of an argument is to be deprecated. - - Args: - version: Version to be used - argument_name: The name of the argument whose type gets deprecated. - old_type: Old type to be used - new_type: New type to be used, if None, the type is deprecated without replacement. - additional_msg: any additional message - stack_level: stack level - category: warning category - """ - # skip if it was already added - obj = _DeprecatedArgumentType(version, argument_name, old_type, new_type, additional_msg) - if obj in _DEPRECATED_OBJECTS: - return - - _DEPRECATED_OBJECTS.add(cast(NamedTuple, obj)) - - msg = ( - f"The {old_type} type in the '{argument_name}' argument is deprecated as of version " - f"{version} and will be removed no sooner than 3 months after the release" - ) - if new_type: - msg += f". Instead use the {new_type} type" - if additional_msg: - msg += f" {additional_msg}" - msg += "." - - warnings.warn(msg, category=category, stacklevel=stack_level + 1) - - -def _rename_kwargs(version, qualname, func_name, kwargs, kwarg_map, additional_msg, stack_level): - for old_arg, new_arg in kwarg_map.items(): - if old_arg in kwargs: - if new_arg in kwargs: - raise TypeError(f"{func_name} received both {new_arg} and {old_arg} (deprecated).") - - # skip if it was already added - obj = _DeprecatedArgument(version, qualname, old_arg, new_arg, additional_msg) - if obj not in _DEPRECATED_OBJECTS: - _DEPRECATED_OBJECTS.add(obj) - - msg = ( - f"{func_name}: the {old_arg} {DeprecatedType.ARGUMENT.value} is deprecated " - f"as of version {version} and will be removed no sooner " - "than 3 months after the release" - ) - if new_arg: - msg += f". Instead use the {new_arg} {DeprecatedType.ARGUMENT.value}" - if additional_msg: - msg += f" {additional_msg}" - msg += "." - warnings.warn(msg, DeprecationWarning, stacklevel=stack_level) - - if new_arg: - kwargs[new_arg] = kwargs.pop(old_arg) - - -def deprecate_arguments( - version: str, - kwarg_map: Dict[str, str], - additional_msg: Optional[str] = None, - stack_level: int = 3, -) -> Callable: - """Decorator to alias deprecated argument names and warn upon use. - - When the new argument name is an empty string, this deprecates the argument without replacement. - - Args: - version: Version to be used - kwarg_map: Args dictionary with old, new arguments - additional_msg: any additional message - stack_level: stack level - - Returns: - The decorated function - """ - - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - if kwargs: - _rename_kwargs( - version, - func.__qualname__, - func.__name__, - kwargs, - kwarg_map, - additional_msg, - stack_level, - ) - return func(*args, **kwargs) - - return wrapper - - return decorator - - -def deprecate_positional_arguments( - version: str, - func_name: str, - old_function_arguments: List[str], - additional_msg: Optional[str] = None, - stack_level: int = 3, -) -> Callable: - """Decorator to convert positional arguments into keyword arguments and warn upon use. - If we had a function(a,b,c,d) and we now want the user to pass b and d as kwargs we - would do the following. - .. code-block:: python - def old_function(a,b,c,d): - return a+b+c+d - - @deprecate_positional_arguments("0.1","function", ["a","b","c","d"]) - def function(a, c, **kwargs): - returns a + c + sum(kwargs.values()) - - # The following two calls would be equivalent - function(1, 2, 3, 4) # Would raise deprecation warning for arguments b and d. - function(a=1, b=2, c=3, d=4) # Would not raise any warning. - function(1,3,b=2,d=4) #Would not raise any errors either. - - Args: - version: Version to be used - func_name: Name of the function where the deprecation takes place will be used to - write the deprecation message. - old_function_arguments: List of arguments of the function before the deprecation. - additional_msg: any additional message - stack_level: stack level - - Returns: - The decorated function - """ - - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - - new_function_arguments = inspect.getfullargspec(func)[0] - - for i, arg in enumerate(args): - kwargs[old_function_arguments[i]] = arg - if old_function_arguments[i] not in new_function_arguments: - msg = ( - f"{func_name}: {old_function_arguments[i]} is no longer a positional argument " - f"as of version {version} and will be removed no sooner " - "than 3 months after the release. Instead use it as a keyword argument" - ) - if additional_msg: - msg += f"{additional_msg}" + "." - - warnings.warn(msg, DeprecationWarning, stack_level) - return func(**kwargs) - - return wrapper - - return decorator - - -def _check_values(version, qualname, args, kwargs, kwarg_map, additional_msg, stack_level): - for arg, value_dict in kwarg_map.items(): - index = value_dict["index"] - values_map = value_dict["values"] - old_value = None - if args and 0 < index < len(args): - old_value = args[index] - if kwargs and arg in kwargs: - old_value = kwargs[arg] - - if old_value in values_map: - new_value = values_map[old_value] - - # skip if it was already added - obj = _DeprecatedValue(version, qualname, arg, old_value, new_value, additional_msg) - if obj in _DEPRECATED_OBJECTS: - continue - - _DEPRECATED_OBJECTS.add(obj) - - msg = ( - f'The {arg} {DeprecatedType.ARGUMENT.value} value "{old_value}" is deprecated ' - f"as of version {version} and will be removed no sooner " - "than 3 months after the release. Instead use the " - f'"{new_value}" value' - ) - if additional_msg: - msg += f" {additional_msg}" - msg += "." - warnings.warn(msg, DeprecationWarning, stacklevel=stack_level) - - -def deprecate_values( - version: str, - kwarg_map: Dict[str, Dict[Any, Any]], - additional_msg: Optional[str] = None, - stack_level: int = 3, -) -> Callable: - """Decorator to alias deprecated default values and warn upon use. - Args: - version: Version to be used - kwarg_map: Args dictionary with argument and map of old, new values - additional_msg: any additional message - stack_level: stack level - - Returns: - The decorated function - """ - - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - if args or kwargs: - parameter_names = list(inspect.signature(func).parameters.keys()) - new_kwarg_map = {} - for name, values_map in kwarg_map.items(): - new_kwarg_map[name] = { - "index": parameter_names.index(name), - "values": values_map, - } - _check_values( - version, - func.__qualname__, - args, - kwargs, - new_kwarg_map, - additional_msg, - stack_level, - ) - return func(*args, **kwargs) - - return wrapper - - return decorator - - -def _deprecate_object( - version: str, - old_type: DeprecatedType, - new_type: DeprecatedType, - new_name: str, - additional_msg: str, - stack_level: int, -) -> Callable: - """Decorator that prints deprecated message - Args: - version: Version to be used - old_type: New type to be used - new_type: New type to be used, if None, old_type is used instead. - new_name: New name to be used - additional_msg: any additional message - stack_level: stack level - - Returns: - The decorated method - """ - - def decorator(func): - msg = ( - f"The {func.__name__} {old_type.value} is deprecated as of version {version} " - "and will be removed no sooner than 3 months after the release" - ) - if new_name: - type_str = new_type.value if new_type is not None else old_type.value - msg += f". Instead use the {new_name} {type_str}" - if additional_msg: - msg += f" {additional_msg}" - msg += "." - - @functools.wraps(func) - def wrapper(*args, **kwargs): - # warn only once - if not wrapper._warned: - warnings.warn(msg, DeprecationWarning, stacklevel=stack_level) - wrapper._warned = True - return func(*args, **kwargs) - - wrapper._warned = False - return wrapper - - return decorator - - -def deprecate_method( - version: str, - new_type: Optional[DeprecatedType] = None, - new_name: Optional[str] = None, - additional_msg: Optional[str] = None, - stack_level: int = 2, -) -> Callable: - """Decorator that prints deprecated message for an instance method - Args: - version: Version to be used - new_type: New type to be used - new_name: New name to be used - additional_msg: any additional message - stack_level: stack level - - Returns: - The decorated method - """ - return _deprecate_object( - version, DeprecatedType.METHOD, new_type, new_name, additional_msg, stack_level - ) - - -def deprecate_property( - version: str, - new_type: Optional[DeprecatedType] = None, - new_name: Optional[str] = None, - additional_msg: Optional[str] = None, - stack_level: int = 2, -) -> Callable: - """Decorator that prints deprecated message for a property - - *** This decorator must be placed below the property decorator *** - - Args: - version: Version to be used - new_type: New type to be used - new_name: New name to be used - additional_msg: any additional message - stack_level: stack level - - Returns: - The decorated property - """ - return _deprecate_object( - version, DeprecatedType.PROPERTY, new_type, new_name, additional_msg, stack_level - ) - - -def deprecate_function( - version: str, - new_type: Optional[DeprecatedType] = None, - new_name: Optional[str] = None, - additional_msg: Optional[str] = None, - stack_level: int = 2, -) -> Callable: - """Decorator that prints deprecated message for a function - Args: - version: Version to be used - new_type: New type to be used - new_name: New name to be used - additional_msg: any additional message - stack_level: stack level - - Returns: - The decorated function - """ - return _deprecate_object( - version, DeprecatedType.FUNCTION, new_type, new_name, additional_msg, stack_level - ) diff --git a/qiskit_nature/runtime/__init__.py b/qiskit_nature/runtime/__init__.py deleted file mode 100644 index 419c44aec..000000000 --- a/qiskit_nature/runtime/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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. - -""" -DEPRECATED Qiskit Nature Runtime (:mod:`qiskit_nature.runtime`) -=============================================================== - -.. currentmodule:: qiskit_nature.runtime - -.. warning:: - This entire module is deprecated as of version 0.6.0 and will be removed no sooner than 3 months - after the release. Instead you should update your code to use the Qiskit Runtime Primitives. For - more details on how to migrate check out this guide, here: https://qisk.it/algo_migration#vqe ! - -Programs that embed Qiskit Runtime in the algorithmic interfaces and facilitate usage of -algorithms and scripts in the cloud. - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - VQEClient - VQERuntimeResult - -""" - -from .vqe_client import VQEClient, VQERuntimeResult - -__all__ = ["VQEClient", "VQERuntimeResult"] diff --git a/qiskit_nature/runtime/vqe_client.py b/qiskit_nature/runtime/vqe_client.py deleted file mode 100644 index da34f9eab..000000000 --- a/qiskit_nature/runtime/vqe_client.py +++ /dev/null @@ -1,431 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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 Qiskit Nature VQE Runtime Client.""" - - -from typing import Callable, Optional, Any, Dict, Union -import warnings -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.exceptions import QiskitError -from qiskit.providers import Provider -from qiskit.providers.backend import Backend, BackendV2 -from qiskit.algorithms import ( - MinimumEigensolver, - MinimumEigensolverResult, - VQEResult, - VariationalAlgorithm, -) -from qiskit.algorithms.optimizers import Optimizer, SPSA -from qiskit.opflow import OperatorBase, PauliSumOp -from qiskit.quantum_info import SparsePauliOp - -from qiskit_nature import ListOrDictType -from qiskit_nature.deprecation import warn_deprecated, DeprecatedType -from qiskit_nature.second_q.mappers.qubit_mapper import _ListOrDict - - -class VQEClient(VariationalAlgorithm, MinimumEigensolver): - """DEPRECATED The Qiskit Nature VQE Runtime Client. - - This class is a client to call the VQE program in Qiskit Runtime.""" - - def __init__( - self, - ansatz: QuantumCircuit, - optimizer: Optional[Union[Optimizer, Dict[str, Any]]] = None, - initial_point: Optional[np.ndarray] = None, - provider: Optional[Provider] = None, - backend: Optional[Backend] = None, - shots: int = 1024, - measurement_error_mitigation: bool = False, - callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, - store_intermediate: bool = False, - ) -> None: - """ - Args: - ansatz: A parameterized circuit used as Ansatz for the wave function. - optimizer: An optimizer or dictionary specifying a classical optimizer. - If a dictionary, only SPSA and QN-SPSA are supported. The dictionary must contain a - key ``name`` for the name of the optimizer and may contain additional keys for the - settings. E.g. ``{'name': 'SPSA', 'maxiter': 100}``. - Per default, SPSA is used. - backend: The backend to run the circuits on. - initial_point: An optional initial point (i.e. initial parameter values) - for the optimizer. If ``None`` a random vector is used. - provider: Provider that supports the runtime feature. - shots: The number of shots to be used - measurement_error_mitigation: Whether or not to use measurement error mitigation. - callback: a callback that can access the intermediate data during the optimization. - Four parameter values are passed to the callback as follows during each evaluation - by the optimizer for its current set of parameters as it works towards the minimum. - These are: the evaluation count, the optimizer parameters for the - ansatz, the evaluated mean and the evaluated standard deviation. - store_intermediate: Whether or not to store intermediate values of the optimization - steps. Per default False. - """ - warn_deprecated( - "0.6.0", - DeprecatedType.CLASS, - "VQEClient", - additional_msg=( - ". Instead you should use the new primitives based VQE with the Qiskit IBM Runtime " - "Estimator primitive. For more details on how to migrate check out this guide, " - "here: https://qisk.it/algo_migration#vqe" - ), - ) - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__init__() - if optimizer is None: - optimizer = SPSA(maxiter=300) - - # define program name - self._program_id = "vqe" - - # store settings - self._provider = None - self._ansatz = ansatz - self._optimizer = None - self._backend = backend - self._initial_point = initial_point - self._shots = shots - self._measurement_error_mitigation = measurement_error_mitigation - self._callback = callback - self._store_intermediate = store_intermediate - - # use setter to check for valid inputs - if provider is not None: - self.provider = provider - - self.optimizer = optimizer - - @property - def provider(self) -> Optional[Provider]: - """Return the provider.""" - return self._provider - - @provider.setter - def provider(self, provider: Provider) -> None: - """Set the provider. Must be a provider that supports the runtime feature.""" - try: - _ = hasattr(provider, "runtime") - except QiskitError: - # pylint: disable=raise-missing-from - raise ValueError(f"The provider {provider} does not provide a runtime environment.") - - self._provider = provider - - @property - def program_id(self) -> str: - """Return the program ID.""" - return self._program_id - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - @property - def ansatz(self) -> QuantumCircuit: - """Return the ansatz.""" - return self._ansatz - - @ansatz.setter - def ansatz(self, ansatz: QuantumCircuit) -> None: - """Set the ansatz.""" - self._ansatz = ansatz - - @property - def optimizer(self) -> Union[Optimizer, Dict[str, Any]]: - """Return the dictionary describing the optimizer.""" - return self._optimizer - - @optimizer.setter - def optimizer(self, optimizer: Union[Optimizer, Dict[str, Any]]) -> None: - """Set the optimizer.""" - if isinstance(optimizer, Optimizer): - self._optimizer = optimizer - else: - if "name" not in optimizer.keys(): - raise ValueError( - "The optimizer dictionary must contain a ``name`` key specifying the type " - "of the optimizer." - ) - - _validate_optimizer_settings(optimizer) - - self._optimizer = optimizer - - @property - def backend(self) -> Optional[Backend]: - """Returns the backend.""" - return self._backend - - @backend.setter - def backend(self, backend) -> None: - """Sets the backend.""" - self._backend = backend - - @property - def initial_point(self) -> Optional[np.ndarray]: - """Returns the initial point.""" - return self._initial_point - - @initial_point.setter - def initial_point(self, initial_point: Optional[np.ndarray]) -> None: - """Sets the initial point.""" - self._initial_point = initial_point - - @property - def shots(self) -> int: - """Return the number of shots.""" - return self._shots - - @shots.setter - def shots(self, shots: int) -> None: - """Set the number of shots.""" - self._shots = shots - - @property - def measurement_error_mitigation(self) -> bool: - """Returns whether or not to use measurement error mitigation. - - Readout error mitigation is done using a complete measurement fitter with the - ``self.shots`` number of shots and re-calibrations every 30 minutes. - """ - return self._measurement_error_mitigation - - @measurement_error_mitigation.setter - def measurement_error_mitigation(self, measurement_error_mitigation: bool) -> None: - """Whether or not to use readout error mitigation.""" - self._measurement_error_mitigation = measurement_error_mitigation - - @property - def store_intermediate(self) -> bool: - """Returns whether or not to store intermediate information of the optimization.""" - return self._store_intermediate - - @store_intermediate.setter - def store_intermediate(self, store: bool) -> None: - """Whether or not to store intermediate information of the optimization.""" - self._store_intermediate = store - - @property - def callback(self) -> Callable: - """Returns the callback.""" - return self._callback - - @callback.setter - def callback(self, callback: Callable) -> None: - """Set the callback.""" - self._callback = callback - - def _wrap_vqe_callback(self) -> Optional[Callable]: - """Wraps and returns the given callback to match the signature of the runtime callback.""" - - def wrapped_callback(*args) -> None: - _, data = args # first element is the job id - if isinstance(data, dict): - return # not expected params. skip - iteration_count = data[0] - params = data[1] - mean = data[2] - sigma = data[3] - self._callback(iteration_count, params, mean, sigma) - return - - # if callback is set, return wrapped callback, else return None - if self._callback: - return wrapped_callback - else: - return None - - def compute_minimum_eigenvalue( - self, operator: OperatorBase, aux_operators: Optional[ListOrDictType[OperatorBase]] = None - ) -> MinimumEigensolverResult: - """Calls the VQE Runtime to approximate the ground state of the given operator. - - Args: - operator: Qubit operator of the observable - aux_operators: Optional list of auxiliary operators or dictionary with - auxiliary operators as values and their names as keys to be evaluated with the - (approximate) eigenstate of the minimum eigenvalue main result and their expectation - values returned. For instance in chemistry these can be dipole operators, total - particle count operators so we can get values for these at the ground state. - - Returns: - MinimumEigensolverResult - - Raises: - ValueError: If the backend has not yet been set. - ValueError: If the provider has not yet been set. - RuntimeError: If the job execution failed. - - """ - if self.backend is None: - raise ValueError("The backend has not been set.") - - if self.provider is None: - raise ValueError("The provider has not been set.") - - # try to convert the operators to a PauliSumOp, if it isn't already one - operator = _convert_to_paulisumop(operator) - wrapped_type = type(aux_operators) - wrapped_aux_operators = { - str(aux_op_name_or_idx): _convert_to_paulisumop(aux_op) - for aux_op_name_or_idx, aux_op in _ListOrDict(aux_operators).items() - } - - # combine the settings with the given operator to runtime inputs - inputs = { - "operator": operator, - "aux_operators": wrapped_aux_operators, - "ansatz": self.ansatz, - "optimizer": self.optimizer, - "initial_point": self.initial_point, - "shots": self.shots, - "measurement_error_mitigation": self.measurement_error_mitigation, - "store_intermediate": self.store_intermediate, - } - - # define runtime options - options = { - "backend_name": self.backend.name - if isinstance(self.backend, BackendV2) - else self.backend.name() - } - - # send job to runtime and return result - job = self.provider.runtime.run( - program_id=self.program_id, - inputs=inputs, - options=options, - callback=self._wrap_vqe_callback(), - ) - # print job ID if something goes wrong - try: - result = job.result() - except Exception as exc: - raise RuntimeError(f"The job {job.job_id()} failed unexpectedly.") from exc - - # re-build result from serialized return value - vqe_result = VQERuntimeResult() - vqe_result.job_id = job.job_id() - vqe_result.cost_function_evals = result.get("cost_function_evals", None) - vqe_result.eigenstate = result.get("eigenstate", None) - vqe_result.eigenvalue = result.get("eigenvalue", None) - aux_op_eigenvalues = result.get("aux_operator_eigenvalues", None) - if aux_op_eigenvalues is not None: - if wrapped_type == list: - aux_op_eigenvalues = list( - aux_op_eigenvalues.get(str(key), None) for key in range(len(aux_op_eigenvalues)) - ) - elif wrapped_type == dict: - aux_op_eigenvalues = dict( - zip(wrapped_aux_operators.keys(), aux_op_eigenvalues.values()) - ) - if not aux_op_eigenvalues: # For consistency set to None for empty dict/list - aux_op_eigenvalues = None - vqe_result.aux_operator_eigenvalues = aux_op_eigenvalues - vqe_result.optimal_parameters = result.get("optimal_parameters", None) - vqe_result.optimal_point = result.get("optimal_point", None) - vqe_result.optimal_value = result.get("optimal_value", None) - vqe_result.optimizer_evals = result.get("optimizer_evals", None) - vqe_result.optimizer_time = result.get("optimizer_time", None) - vqe_result.optimizer_history = result.get("optimizer_history", None) - - return vqe_result - - -class VQERuntimeResult(VQEResult): - """DEPRECATED The VQEClient result object. - - This result objects contains the same as the VQEResult and additionally the history - of the optimizer, containing information such as the function and parameter values per step. - """ - - def __init__(self) -> None: - warn_deprecated("0.6.0", DeprecatedType.CLASS, "VQERuntimeResult") - super().__init__() - self._job_id = None # type: str - self._optimizer_history = None # type: Dict[str, Any] - - @property - def job_id(self) -> str: - """The job ID associated with the VQE runtime job.""" - return self._job_id - - @job_id.setter - def job_id(self, job_id: str) -> None: - """Set the job ID associated with the VQE runtime job.""" - self._job_id = job_id - - @property - def optimizer_history(self) -> Optional[Dict[str, Any]]: - """The optimizer history.""" - return self._optimizer_history - - @optimizer_history.setter - def optimizer_history(self, history: Dict[str, Any]) -> None: - """Set the optimizer history.""" - self._optimizer_history = history - - -def _validate_optimizer_settings(settings): - name = settings.get("name", None) - if name not in ["SPSA", "QN-SPSA"]: - raise NotImplementedError("Only SPSA and QN-SPSA are currently supported.") - - allowed_settings = [ - "name", - "maxiter", - "blocking", - "allowed_increase", - "trust_region", - "learning_rate", - "perturbation", - "resamplings", - "last_avg", - "second_order", - "hessian_delay", - "regularization", - "initial_hessian", - ] - - if name == "QN-SPSA": - allowed_settings.remove("trust_region") - allowed_settings.remove("second_order") - - unsupported_args = set(settings.keys()) - set(allowed_settings) - - if len(unsupported_args) > 0: - raise ValueError( - f"The following settings are unsupported for the {name} optimizer: " - f"{unsupported_args}" - ) - - -def _convert_to_paulisumop(operator): - """Attempt to convert the operator to a PauliSumOp.""" - if isinstance(operator, PauliSumOp): - return operator - - try: - primitive = SparsePauliOp(operator.primitive) - return PauliSumOp(primitive, operator.coeff) - except Exception as exc: - raise ValueError( - f"Invalid type of the operator {type(operator)} " - "must be PauliSumOp, or castable to one." - ) from exc diff --git a/qiskit_nature/second_q/algorithms/__init__.py b/qiskit_nature/second_q/algorithms/__init__.py index 978ec51e9..3f919b75c 100644 --- a/qiskit_nature/second_q/algorithms/__init__.py +++ b/qiskit_nature/second_q/algorithms/__init__.py @@ -56,16 +56,6 @@ EvaluationRule -The following factories are still available but have been **deprecated** in version 0.6.0 of Qiskit -Nature: - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - EigensolverFactory - NumPyEigensolverFactory - Ground State Solvers ++++++++++++++++++++ Algorithms that can find the minimum eigenvalue of an operator, e.g. ground state for chemistry. @@ -86,18 +76,6 @@ GroundStateEigensolver -The following factories are still available but have been **deprecated** in version 0.6.0 of Qiskit -Nature: - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - MinimumEigensolverFactory - NumPyMinimumEigensolverFactory - VQEUCCFactory - VQEUVCCFactory - Initial Points ++++++++++++++ When using variational algorithms such as the :class:`~qiskit.algorithms.minimum_eigensolvers.VQE` @@ -121,16 +99,10 @@ QEOM, QEOMResult, EvaluationRule, - EigensolverFactory, - NumPyEigensolverFactory, ) from .ground_state_solvers import ( GroundStateEigensolver, GroundStateSolver, - MinimumEigensolverFactory, - NumPyMinimumEigensolverFactory, - VQEUCCFactory, - VQEUVCCFactory, ) __all__ = [ @@ -139,12 +111,6 @@ "QEOM", "QEOMResult", "EvaluationRule", - "EigensolverFactory", - "NumPyEigensolverFactory", "GroundStateEigensolver", "GroundStateSolver", - "MinimumEigensolverFactory", - "NumPyMinimumEigensolverFactory", - "VQEUCCFactory", - "VQEUVCCFactory", ] diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/__init__.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/__init__.py index 83eedf1e1..8cc83d889 100644 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/__init__.py +++ b/qiskit_nature/second_q/algorithms/excited_states_solvers/__init__.py @@ -20,14 +20,11 @@ from .excited_states_solver import ExcitedStatesSolver from .qeom import QEOM, QEOMResult, EvaluationRule -from .eigensolver_factories import EigensolverFactory, NumPyEigensolverFactory from .excited_states_eigensolver import ExcitedStatesEigensolver __all__ = [ "ExcitedStatesSolver", "ExcitedStatesEigensolver", - "EigensolverFactory", - "NumPyEigensolverFactory", "QEOM", "QEOMResult", "EvaluationRule", diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/__init__.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/__init__.py deleted file mode 100644 index c8e8db163..000000000 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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. - -""" -Eigensolver Factories -(:mod:`qiskit_nature.second_q.algorithms.excited_states_solvers.eigensolver_factories`) -======================================================================================= - -DEPRECATED Factories that create an eigensolver based on a qubit transformation. - -.. currentmodule:: qiskit_nature.second_q.algorithms.excited_states_solvers.eigensolver_factories - -""" - -from .eigensolver_factory import EigensolverFactory -from .numpy_eigensolver_factory import NumPyEigensolverFactory - -__all__ = ["EigensolverFactory", "NumPyEigensolverFactory"] diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/eigensolver_factory.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/eigensolver_factory.py deleted file mode 100644 index d80b8ac47..000000000 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/eigensolver_factory.py +++ /dev/null @@ -1,36 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 eigensolver factory for excited states calculation algorithms.""" - -from abc import ABC, abstractmethod - -from qiskit.algorithms.eigensolvers import Eigensolver - -from qiskit_nature.second_q.problems.base_problem import BaseProblem - - -class EigensolverFactory(ABC): - """DEPRECATED A factory to construct an eigensolver based on a qubit operator transformation.""" - - @abstractmethod - def get_solver(self, problem: BaseProblem) -> Eigensolver: - """Returns an eigensolver, based on the qubit operator transformation. - - Args: - problem: A class encoding a problem to be solved. - - Returns: - An eigensolver suitable to compute the excited states of the molecule transformed - by ``transformation``. - """ - raise NotImplementedError diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/numpy_eigensolver_factory.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/numpy_eigensolver_factory.py deleted file mode 100644 index 49023ed6e..000000000 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/eigensolver_factories/numpy_eigensolver_factory.py +++ /dev/null @@ -1,120 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 eigensolver factory for ground+excited states calculation algorithms.""" - -from typing import Optional, Union, List, Callable - -import numpy as np -from qiskit.algorithms.eigensolvers import Eigensolver, NumPyEigensolver -from qiskit.utils.validation import validate_min - -from qiskit_nature.deprecation import DeprecatedType, warn_deprecated -from qiskit_nature.second_q.problems.base_problem import BaseProblem -from .eigensolver_factory import EigensolverFactory - - -class NumPyEigensolverFactory(EigensolverFactory): - """DEPRECATED A factory to construct a NumPyEigensolver. - - .. warning:: - - This class is deprecated! Please see :ref:`this guide ` for how to replace - your usage of it! - """ - - def __init__( - self, - filter_criterion: Callable[ - [Union[List, np.ndarray], float, Optional[List[float]]], bool - ] = None, - k: int = 100, - use_default_filter_criterion: bool = False, - ) -> None: - """ - Args: - filter_criterion: Callable that allows to filter eigenvalues/eigenstates. The minimum - eigensolver is only searching over feasible states and returns an eigenstate that - has the smallest eigenvalue among feasible states. The callable has the signature - ``filter(eigenstate, eigenvalue, aux_values)`` and must return a boolean to indicate - whether to consider this value or not. If there is no - feasible element, the result can even be empty. - use_default_filter_criterion: Whether to use default filter criteria or not. - k: How many eigenvalues are to be computed, has a min. value of 1. - use_default_filter_criterion: Whether to use the transformation's default filter - criterion if ``filter_criterion`` is ``None``. - """ - warn_deprecated( - "0.6.0", - DeprecatedType.CLASS, - "NumPyMinimumEigensolverFactory", - additional_msg=( - ". This class is deprecated without replacement. Instead, refer to this how-to " - "guide which explains the steps you need to take to replace the use of this class: " - "https://qiskit.org/documentation/nature/howtos/numpy_minimum_eigensolver.html" - ), - ) - self._filter_criterion = filter_criterion - self._k = k # pylint:disable=invalid-name - self._use_default_filter_criterion = use_default_filter_criterion - - @property - def filter_criterion( - self, - ) -> Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool]: - """Returns filter criterion.""" - return self._filter_criterion - - @filter_criterion.setter - def filter_criterion( - self, - value: Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool], - ) -> None: - """Sets filter criterion.""" - self._filter_criterion = value - - @property - def k(self) -> int: - """Returns k (number of eigenvalues requested).""" - return self._k - - @k.setter - def k(self, k: int) -> None: - """Sets k (number of eigenvalues requested).""" - validate_min("k", k, 1) - self._k = k - - @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, problem: BaseProblem) -> Eigensolver: - """Returns a ``NumPyEigensolver`` with the desired filter. - - Args: - problem: A class encoding a problem to be solved. - - Returns: - A ``NumPyEigensolver`` suitable to compute the ground state of the provided ``problem``. - """ - filter_criterion = self._filter_criterion - if not filter_criterion and self._use_default_filter_criterion: - filter_criterion = problem.get_default_filter_criterion() - - npe = NumPyEigensolver(filter_criterion=filter_criterion, k=self.k) - return npe diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/excited_states_eigensolver.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/excited_states_eigensolver.py index 1dc923b69..663428023 100644 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/excited_states_eigensolver.py +++ b/qiskit_nature/second_q/algorithms/excited_states_solvers/excited_states_eigensolver.py @@ -17,17 +17,14 @@ import logging from qiskit.algorithms.eigensolvers import Eigensolver -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from qiskit_nature.second_q.operators import SparseLabelOp from qiskit_nature.second_q.problems import BaseProblem from qiskit_nature.second_q.problems import EigenstateResult -from qiskit_nature.deprecation import deprecate_arguments, warn_deprecated_type from .excited_states_solver import ExcitedStatesSolver -from .eigensolver_factories import EigensolverFactory LOGGER = logging.getLogger(__name__) @@ -35,95 +32,47 @@ class ExcitedStatesEigensolver(ExcitedStatesSolver): """The calculation of excited states via an Eigensolver algorithm.""" - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, - qubit_mapper: QubitConverter | QubitMapper, - solver: Eigensolver | EigensolverFactory, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper, + solver: Eigensolver, ) -> None: # pylint: disable=unused-argument """ Args: - qubit_mapper: The ``QubitMapper`` or ``QubitConverter`` (use of the latter is - deprecated) to use for mapping. - solver: Minimum Eigensolver or MESFactory object. - qubit_converter: DEPRECATED The ``QubitConverter`` or ``QubitMapper`` to use for mapping - and symmetry reduction. + qubit_mapper: The ``QubitMapper`` to use for mapping. + solver: Minimum Eigensolver object. """ - if isinstance(solver, EigensolverFactory): - warn_deprecated_type( - "0.6.0", - "solver", - "EigensolverFactory", - ) self._qubit_mapper = qubit_mapper self._solver = solver @property - def solver(self) -> Eigensolver | EigensolverFactory: - """Returns the minimum eigensolver or factory.""" + def solver(self) -> Eigensolver: + """Returns the minimum eigensolver.""" return self._solver @solver.setter - def solver(self, solver: Eigensolver | EigensolverFactory) -> None: - """Sets the minimum eigensolver or factory.""" - if isinstance(solver, EigensolverFactory): - warn_deprecated_type( - "0.6.0", - "solver", - "EigensolverFactory", - ) + def solver(self, solver: Eigensolver) -> None: + """Sets the minimum eigensolver.""" self._solver = solver def get_qubit_operators( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | PauliSumOp | SparsePauliOp] | None = None, - ) -> tuple[PauliSumOp | SparsePauliOp, dict[str, PauliSumOp | SparsePauliOp] | None]: + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, + ) -> tuple[SparsePauliOp, dict[str, SparsePauliOp] | None]: # Note that ``aux_ops`` contains not only the transformed ``aux_operators`` passed by the # user but also additional ones from the transformation main_second_q_op, aux_second_q_ops = problem.second_q_ops() - num_particles = getattr(problem, "num_particles", None) - - if isinstance(self._qubit_mapper, QubitConverter): - main_operator = self._qubit_mapper.convert( - main_second_q_op, - num_particles=num_particles, - sector_locator=problem.symmetry_sector_locator, - ) - aux_ops = self._qubit_mapper.convert_match(aux_second_q_ops) - - else: - main_operator = self._qubit_mapper.map(main_second_q_op) - aux_ops = self._qubit_mapper.map(aux_second_q_ops) + main_operator = self._qubit_mapper.map(main_second_q_op) + aux_ops = self._qubit_mapper.map(aux_second_q_ops) if aux_operators is not None: for name_aux, aux_op in aux_operators.items(): - if isinstance(aux_op, PauliSumOp): - warn_deprecated_type( - "0.6.0", - argument_name="aux_operators", - old_type="PauliSumOp", - new_type="SparsePauliOp", - ) if isinstance(aux_op, SparseLabelOp): - if isinstance(self._qubit_mapper, QubitConverter): - converted_aux_op = self._qubit_mapper.convert_match( - aux_op, suppress_none=True - ) - else: - converted_aux_op = self._qubit_mapper.map(aux_op) + converted_aux_op = self._qubit_mapper.map(aux_op) else: converted_aux_op = aux_op @@ -147,15 +96,6 @@ def get_qubit_operators( name_aux, ) - if isinstance(self._solver, EigensolverFactory): - warn_deprecated_type( - "0.6.0", - "solver", - "EigensolverFactory", - ) - # this must be called after transformation.transform - self._solver = self._solver.get_solver(problem) - # if the eigensolver does not support auxiliary operators, reset them if not self._solver.supports_aux_operators(): aux_ops = None @@ -164,7 +104,7 @@ def get_qubit_operators( def solve( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | PauliSumOp | SparsePauliOp] | None = None, + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, ) -> EigenstateResult: """Compute Ground and Excited States properties. @@ -181,7 +121,7 @@ def solve( # by the user but also additional ones from the transformation main_operator, aux_ops = self.get_qubit_operators(problem, aux_operators) - raw_es_result = self._solver.compute_eigenvalues(main_operator, aux_ops) # type: ignore + raw_es_result = self._solver.compute_eigenvalues(main_operator, aux_ops) eigenstate_result = EigenstateResult.from_result(raw_es_result) result = problem.interpret(eigenstate_result) diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/excited_states_solver.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/excited_states_solver.py index 77c6b9718..69bb45cf2 100644 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/excited_states_solver.py +++ b/qiskit_nature/second_q/algorithms/excited_states_solvers/excited_states_solver.py @@ -16,7 +16,6 @@ from abc import ABC, abstractmethod -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.operators import SparseLabelOp @@ -31,7 +30,7 @@ class ExcitedStatesSolver(ABC): def solve( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | PauliSumOp | SparsePauliOp] | None = None, + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, ) -> EigenstateResult: r"""Compute the excited states energies of the molecule that was supplied via the driver. @@ -54,10 +53,10 @@ def solver(self): def get_qubit_operators( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | SparsePauliOp | PauliSumOp] | None = None, - ) -> tuple[PauliSumOp | SparseLabelOp, dict[str, PauliSumOp | SparseLabelOp] | None]: + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, + ) -> tuple[SparseLabelOp, dict[str, SparseLabelOp] | None]: """Gets the operator and auxiliary operators, and transforms the provided auxiliary operators - using a ``QubitConverter`` or ``QubitMapper``. + using a ``QubitMapper``. If the user-provided ``aux_operators`` contain a name which clashes with an internally constructed auxiliary operator, then the corresponding internal operator will be overridden by the user-provided operator. diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom.py index 3da5e3082..791d8f5c5 100644 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom.py +++ b/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom.py @@ -29,26 +29,17 @@ from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolver from qiskit.algorithms.observables_evaluator import estimate_observables from qiskit.circuit import QuantumCircuit -from qiskit.opflow import PauliSumOp from qiskit.tools import parallel_map from qiskit.tools.events import TextProgressBar from qiskit.utils import algorithm_globals -from qiskit.utils.deprecation import deprecate_function from qiskit.quantum_info import SparsePauliOp from qiskit.primitives import BaseEstimator from qiskit_nature.second_q.algorithms.ground_state_solvers import GroundStateSolver -from qiskit_nature.second_q.algorithms.ground_state_solvers.minimum_eigensolver_factories import ( - MinimumEigensolverFactory, -) from qiskit_nature.second_q.algorithms.excited_states_solvers.excited_states_solver import ( ExcitedStatesSolver, ) -from qiskit_nature.second_q.mappers import ( - QubitConverter, - QubitMapper, - TaperedQubitMapper, -) +from qiskit_nature.second_q.mappers import QubitMapper, TaperedQubitMapper from qiskit_nature.second_q.operators import SparseLabelOp from qiskit_nature.second_q.problems import ( BaseProblem, @@ -57,7 +48,6 @@ EigenstateResult, ElectronicStructureResult, ) -from qiskit_nature.deprecation import deprecate_property, warn_deprecated_type from .qeom_electronic_ops_builder import build_electronic_ops from .qeom_vibrational_ops_builder import build_vibrational_ops @@ -238,38 +228,28 @@ def __init__( self._problem_generated_aux_op_names: set[str] = set() @property - @deprecate_property("0.6.0", new_name="qubit_mapper") - def qubit_converter(self) -> QubitConverter | QubitMapper: - """DEPRECATED Returns the qubit_converter object defined in the ground state solver.""" - return self._gsc.qubit_mapper - - @property - def qubit_mapper(self) -> QubitConverter | QubitMapper: + def qubit_mapper(self) -> QubitMapper: """Returns the qubit_mapper object defined in the ground state solver.""" return self._gsc.qubit_mapper @property - def solver(self) -> MinimumEigensolver | MinimumEigensolverFactory: + def solver(self) -> MinimumEigensolver: """Returns the solver object defined in the ground state solver.""" return self._gsc.solver def _map_operators( self, operators: SparseLabelOp | ListOrDictType[SparseLabelOp] - ) -> PauliSumOp | ListOrDictType[PauliSumOp]: - if isinstance(self.qubit_mapper, QubitConverter): - mapped_ops = self.qubit_mapper.convert_match(operators) - elif isinstance(self.qubit_mapper, TaperedQubitMapper): + ) -> SparsePauliOp | ListOrDictType[SparsePauliOp]: + if isinstance(self.qubit_mapper, TaperedQubitMapper): mapped_ops = self.qubit_mapper.map_clifford(operators) else: mapped_ops = self.qubit_mapper.map(operators) return mapped_ops def _taper_operators( - self, operators: PauliSumOp | ListOrDictType[PauliSumOp] - ) -> PauliSumOp | ListOrDictType[PauliSumOp]: - if isinstance(self.qubit_mapper, QubitConverter): - tapered_ops = self.qubit_mapper.symmetry_reduce_clifford(operators) - elif isinstance(self.qubit_mapper, TaperedQubitMapper): + self, operators: SparsePauliOp | ListOrDictType[SparsePauliOp] + ) -> SparsePauliOp | ListOrDictType[SparsePauliOp]: + if isinstance(self.qubit_mapper, TaperedQubitMapper): tapered_ops = self.qubit_mapper.taper_clifford(operators, suppress_none=True) else: tapered_ops = operators @@ -278,8 +258,8 @@ def _taper_operators( def get_qubit_operators( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | SparsePauliOp | PauliSumOp] | None = None, - ) -> tuple[SparsePauliOp | PauliSumOp, dict[str, SparsePauliOp | PauliSumOp] | None]: + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, + ) -> tuple[SparsePauliOp, dict[str, SparsePauliOp] | None]: """ Gets the operator and auxiliary operators, and transforms the provided auxiliary operators. If the user-provided ``aux_operators`` contain a name which clashes with an internally @@ -299,13 +279,9 @@ def get_qubit_operators( main_operator, aux_second_q_operators = problem.second_q_ops() self._problem_generated_aux_op_names = set(aux_second_q_operators.keys()) - num_particles = getattr(problem, "num_particles", None) # 1. Convert the main operator (hamiltonian) to a Qubit Operator and apply two qubit reduction - if isinstance(self.qubit_mapper, QubitConverter): - self.qubit_mapper.force_match(num_particles=num_particles) - main_op = self.qubit_mapper.convert_only(main_operator, num_particles=num_particles) - elif isinstance(self.qubit_mapper, TaperedQubitMapper): + if isinstance(self.qubit_mapper, TaperedQubitMapper): main_op = self.qubit_mapper.map_clifford(main_operator) else: main_op = self.qubit_mapper.map(main_operator) @@ -319,13 +295,6 @@ def get_qubit_operators( if aux_operators is not None: for name, op in aux_operators.items(): - if isinstance(op, PauliSumOp): - warn_deprecated_type( - "0.6.0", - argument_name="aux_operators", - old_type="PauliSumOp", - new_type="SparsePauliOp", - ) if isinstance(op, (SparseLabelOp)): converted_aux_op = self._map_operators(op) else: @@ -342,30 +311,15 @@ def get_qubit_operators( # The custom op overrides the default op if the key is already taken. aux_ops[name] = converted_aux_op - if isinstance(self.qubit_mapper, QubitConverter): - # 4. Find the z2symmetries, set them in the QubitConverter, and apply the first step of - # the tapering. - _, z2symmetries = self.qubit_mapper.find_taper_op( - main_op, problem.symmetry_sector_locator - ) - self.qubit_mapper.force_match(z2symmetries=z2symmetries) - untap_main_op = self.qubit_mapper.convert_clifford(main_op) - untap_aux_ops = self.qubit_mapper.convert_clifford(aux_ops) - else: - untap_main_op = main_op - untap_aux_ops = aux_ops - - # 5. If a MinimumEigensolverFactory was provided, then an additional call to get_solver() is - # required. - if isinstance(self.solver, MinimumEigensolverFactory): - self._gsc._solver = self.solver.get_solver(problem, self.qubit_mapper) # type: ignore + untap_main_op = main_op + untap_aux_ops = aux_ops return untap_main_op, untap_aux_ops def solve( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | SparsePauliOp | PauliSumOp] | None = None, + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, ) -> EigenstateResult: """Run the excited-states calculation. @@ -383,36 +337,25 @@ def solve( instance which holds additional information specific to the qEOM problem. """ - # 1. Prepare all operators and set the particle number in the qubit converter + # 1. Prepare all operators and set the particle number in the qubit mapper ( - untap_main_op_sumop, # Hamiltonian - untap_aux_ops_sumop, # Auxiliary observables + untap_main_op, # Hamiltonian + untap_aux_ops, # Auxiliary observables ) = self.get_qubit_operators(problem, aux_operators) - untap_main_op = untap_main_op_sumop - if isinstance(untap_main_op, PauliSumOp): - untap_main_op = untap_main_op.primitive - - untap_aux_ops = None - if untap_aux_ops_sumop is not None: - untap_aux_ops = { - key: op.primitive if isinstance(op, PauliSumOp) else op - for key, op in untap_aux_ops_sumop.items() - } - # before we taper our operators we filter the ones which come from the problem internally as # to not trigger a bunch of warnings being raised about overwritten auxiliary operators filtered_aux_ops = { k: v - for k, v in untap_aux_ops_sumop.items() + for k, v in untap_aux_ops.items() if k not in self._problem_generated_aux_op_names and k not in aux_operators.keys() } # 2. Run ground state calculation with fully tapered custom auxiliary operators # Note that the solve() method includes the `second_q' auxiliary operators - tap_aux_operators_sumop = self._taper_operators(filtered_aux_ops) + tap_aux_operators = self._taper_operators(filtered_aux_ops) - groundstate_result = self._gsc.solve(problem, tap_aux_operators_sumop) + groundstate_result = self._gsc.solve(problem, tap_aux_operators) ground_state = groundstate_result.eigenstates[0] # 3. Prepare the expansion operators for the excited state calculation @@ -464,7 +407,7 @@ def solve( def _build_hopping_ops( self, problem: BaseProblem ) -> tuple[ - dict[str, SparsePauliOp | PauliSumOp], + dict[str, SparsePauliOp], dict[str, list[bool]], dict[str, tuple[tuple[int, ...], tuple[int, ...]]], ]: @@ -529,7 +472,7 @@ def _build_one_sector(available_hopping_ops): to_be_computed_list.append((m_u, n_u, left_op_1, right_op_1, right_op_2)) if ( - isinstance(self.qubit_mapper, (QubitConverter, TaperedQubitMapper)) + isinstance(self.qubit_mapper, TaperedQubitMapper) and not self.qubit_mapper.z2symmetries.is_empty() ): @@ -724,17 +667,7 @@ def _prepare_expansion_basis( hopping_operators, type_of_commutativities, excitation_indices = data size = int(len(list(excitation_indices.keys())) // 2) - if isinstance(self.qubit_mapper, QubitConverter): - untap_hopping_ops = self.qubit_mapper.convert_clifford(hopping_operators) - else: - untap_hopping_ops = hopping_operators - - untap_hopping_ops_sparse = { - key: op.primitive if isinstance(op, PauliSumOp) else op - for key, op in untap_hopping_ops.items() - } - - return untap_hopping_ops_sparse, type_of_commutativities, size + return hopping_operators, type_of_commutativities, size def _build_qeom_pseudoeigenvalue_problem( self, @@ -764,17 +697,13 @@ def _build_qeom_pseudoeigenvalue_problem( expansion_basis_data, ) - untap_eom_matrix_ops_sumop = { - key: PauliSumOp(op) for key, op in untap_eom_matrix_ops.items() - } - - tap_eom_matrix_ops_sumop = self._taper_operators(untap_eom_matrix_ops_sumop) + tap_eom_matrix_ops = self._taper_operators(untap_eom_matrix_ops) # 2. Evaluate all EOM operators on the ground state measurement_results = estimate_observables( self._estimator, reference_state[0], - tap_eom_matrix_ops_sumop, + tap_eom_matrix_ops, reference_state[1], ) @@ -850,12 +779,10 @@ def _build_excitation_operators( """ untap_hopping_ops, _, size = expansion_basis_data - untap_hopping_ops_sumop = {key: PauliSumOp(op) for key, op in untap_hopping_ops.items()} - - tap_hopping_ops_sumop = self._taper_operators(untap_hopping_ops_sumop) + tap_hopping_ops = self._taper_operators(untap_hopping_ops) additionnal_measurements = estimate_observables( - self._estimator, reference_state[0], tap_hopping_ops_sumop, reference_state[1] + self._estimator, reference_state[0], tap_hopping_ops, reference_state[1] ) num_qubits = list(untap_hopping_ops.values())[0].num_qubits @@ -996,13 +923,11 @@ def _evaluate_observables_excited_states( untap_aux_ops, excitations_ops_reduced, size ) - op_aux_op_dict_sumop = {key: PauliSumOp(op) for key, op in op_aux_op_dict.items()} - # 3. Measure observables - tap_op_aux_op_dict_sumop = self._taper_operators(op_aux_op_dict_sumop) + tap_op_aux_op_dict = self._taper_operators(op_aux_op_dict) aux_measurements = estimate_observables( - self._estimator, reference_state[0], tap_op_aux_op_dict_sumop, reference_state[1] + self._estimator, reference_state[0], tap_op_aux_op_dict, reference_state[1] ) # 4. Format aux_operators_eigenvalues @@ -1104,15 +1029,6 @@ def __init__(self) -> None: self.h_matrix_std: np.ndarray = np.zeros((2, 2)) self.s_matrix_std: np.ndarray = np.zeros((2, 2)) - self._m_matrix: np.ndarray | None = None - self._v_matrix: np.ndarray | None = None - self._q_matrix: np.ndarray | None = None - self._w_matrix: np.ndarray | None = None - self._m_matrix_std: float = 0.0 - self._v_matrix_std: float = 0.0 - self._q_matrix_std: float = 0.0 - self._w_matrix_std: float = 0.0 - self.transition_amplitudes: list[ ListOrDictType[tuple[complex, dict[str, Any]]] ] | None = None @@ -1121,201 +1037,55 @@ def __init__(self) -> None: @property def m_matrix(self) -> np.ndarray | None: """returns the M matrix""" - if self.h_matrix is not None and self._m_matrix is None: - return self.h_matrix[: len(self.h_matrix) // 2, : len(self.h_matrix) // 2] - else: - return self._m_matrix - - @m_matrix.setter - @deprecate_function( - "You should now set the H matrix from which the M matrix will be extracted. " - "This setter will be deprecated in a future release and subsequently " - "removed after that.", - category=PendingDeprecationWarning, - ) - def m_matrix(self, value: np.ndarray) -> None: - """sets the M matrix""" - self._m_matrix = value - logger.warning( - "This setter for the M matrix will not update the H matrix to match. " - "Using this setter will make this result object bypass the H matrix " - "values for M." - ) + if self.h_matrix is None: + return None + return self.h_matrix[: len(self.h_matrix) // 2, : len(self.h_matrix) // 2] @property def v_matrix(self) -> np.ndarray | None: """returns the V matrix""" - if self.s_matrix is not None and self._v_matrix is None: - return self.s_matrix[: len(self.s_matrix) // 2, : len(self.s_matrix) // 2] - else: - return self._v_matrix - - @v_matrix.setter - @deprecate_function( - "You should now set the S matrix from which the V matrix will be extracted. " - "This setter will be deprecated in a future release and subsequently " - "removed after that.", - category=PendingDeprecationWarning, - ) - def v_matrix(self, value: np.ndarray) -> None: - """sets the V matrix""" - self._v_matrix = value - logger.warning( - "This setter for the V matrix will not update the S matrix to match. " - "Using this setter will make this result object bypass the S matrix " - "values for V." - ) + if self.s_matrix is None: + return None + return self.s_matrix[: len(self.s_matrix) // 2, : len(self.s_matrix) // 2] @property def q_matrix(self) -> np.ndarray | None: """returns the Q matrix""" - q_mat: np.ndarray | None = None - if self.h_matrix is not None: - q_mat = self.h_matrix[len(self.h_matrix) // 2 :, : len(self.h_matrix) // 2] - if self._q_matrix is not None: - q_mat = self._q_matrix - return q_mat - - @q_matrix.setter - @deprecate_function( - "You should now set the H matrix from which the Q matrix will be extracted. " - "This setter will be deprecated in a future release and subsequently " - "removed after that.", - category=PendingDeprecationWarning, - ) - def q_matrix(self, value: np.ndarray) -> None: - """sets the Q matrix""" - self._q_matrix = value - logger.warning( - "This setter for the Q matrix will not update the H matrix to match. " - "Using this setter will make this result object bypass the H matrix " - "values for Q." - ) + if self.h_matrix is None: + return None + return self.h_matrix[len(self.h_matrix) // 2 :, : len(self.h_matrix) // 2] @property def w_matrix(self) -> np.ndarray | None: """returns the W matrix""" - if self.s_matrix is not None and self._w_matrix is None: - return self.s_matrix[len(self.s_matrix) // 2 :, : len(self.s_matrix) // 2] - else: - return self._w_matrix - - @w_matrix.setter - @deprecate_function( - "You should now set the S matrix from which the W matrix will be extracted. " - "This setter will be deprecated in a future release and subsequently " - "removed after that.", - category=PendingDeprecationWarning, - ) - def w_matrix(self, value: np.ndarray) -> None: - """sets the W matrix""" - self._w_matrix = value - logger.warning( - "This setter for the W matrix will not update the S matrix to match. " - "Using this setter will make this result object bypass the S matrix " - "values for W." - ) + if self.s_matrix is None: + return None + return self.s_matrix[len(self.s_matrix) // 2 :, : len(self.s_matrix) // 2] @property def m_matrix_std(self) -> float: """returns the M matrix standard deviation""" - if not np.isclose(self.h_matrix_std[0, 0], 0.0) and np.isclose(self._m_matrix_std, 0.0): - return self.h_matrix_std[0, 0] - else: - return self._m_matrix_std - - @m_matrix_std.setter - @deprecate_function( - "You should now set the H matrix standard deviation from which the M matrix " - "standard deviation will be extracted. " - "This setter will be deprecated in a future release and subsequently " - "removed after that.", - category=PendingDeprecationWarning, - ) - def m_matrix_std(self, value: float) -> None: - """sets the M matrix standard deviation""" - self._m_matrix_std = value - logger.warning( - "This setter for the M standard deviation matrix will not " - "update the H standard deviation matrix to match. " - "Using this setter will make this result object bypass the H " - "standard deviation matrix values for M." - ) + if np.isclose(self.h_matrix_std[0, 0], 0.0): + return 0.0 + return self.h_matrix_std[0, 0] @property def v_matrix_std(self) -> float: """returns the V matrix standard deviation""" - if not np.isclose(self.s_matrix_std[0, 0], 0.0) and np.isclose(self._v_matrix_std, 0.0): - return self.s_matrix_std[0, 0] - else: - return self._v_matrix_std - - @v_matrix_std.setter - @deprecate_function( - "You should now set the S matrix standard deviation from which the V matrix " - "standard deviation will be extracted. " - "This setter will be deprecated in a future release and subsequently " - "removed after that.", - category=PendingDeprecationWarning, - ) - def v_matrix_std(self, value: float) -> None: - """sets the V matrix standard deviation""" - self._v_matrix_std = value - logger.warning( - "This setter for the V standard deviation matrix will not " - "update the S standard deviation matrix to match. " - "Using this setter will make this result object bypass the S " - "standard deviation matrix values for V." - ) + if np.isclose(self.s_matrix_std[0, 0], 0.0): + return 0.0 + return self.s_matrix_std[0, 0] @property def q_matrix_std(self) -> float: """returns the Q matrix standard deviation""" - if not np.isclose(self.h_matrix_std[0, 1], 0.0) and np.isclose(self._q_matrix_std, 0.0): - return self.h_matrix_std[0, 0] - else: - return self._q_matrix_std - - @q_matrix_std.setter - @deprecate_function( - "You should now set the H matrix standard deviation from which the Q matrix " - "standard deviation will be extracted. " - "This setter will be deprecated in a future release and subsequently " - "removed after that.", - category=PendingDeprecationWarning, - ) - def q_matrix_std(self, value: float) -> None: - """sets the Q matrix standard deviation""" - self._q_matrix_std = value - logger.warning( - "This setter for the Q standard deviation matrix will not " - "update the H standard deviation matrix to match. " - "Using this setter will make this result object bypass the H " - "standard deviation matrix values for Q." - ) + if np.isclose(self.h_matrix_std[0, 1], 0.0): + return 0.0 + return self.h_matrix_std[0, 0] @property def w_matrix_std(self) -> float: """returns the W matrix standard deviation""" - if not np.isclose(self.s_matrix_std[0, 1], 0.0) and np.isclose(self._w_matrix_std, 0.0): - return self.s_matrix_std[0, 0] - else: - return self._w_matrix_std - - @w_matrix_std.setter - @deprecate_function( - "You should now set the S matrix standard deviation from which the W matrix " - "standard deviation will be extracted. " - "This setter will be deprecated in a future release and subsequently " - "removed after that.", - category=PendingDeprecationWarning, - ) - def w_matrix_std(self, value: float) -> None: - """sets the W matrix standard deviation""" - self._w_matrix_std = value - logger.warning( - "This setter for the W standard deviation matrix will not " - "update the S standard deviation matrix to match. " - "Using this setter will make this result object bypass the S " - "standard deviation matrix values for W." - ) + if np.isclose(self.s_matrix_std[0, 1], 0.0): + return 0.0 + return self.s_matrix_std[0, 0] diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom_electronic_ops_builder.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom_electronic_ops_builder.py index 9e9a2a92b..1a39183f8 100644 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom_electronic_ops_builder.py +++ b/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom_electronic_ops_builder.py @@ -16,7 +16,6 @@ from typing import Callable -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit.tools import parallel_map from qiskit.utils import algorithm_globals @@ -24,18 +23,9 @@ from qiskit_nature import QiskitNatureError from qiskit_nature.second_q.circuit.library import UCC from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper, TaperedQubitMapper -from qiskit_nature.deprecation import deprecate_arguments - - -@deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), -) +from qiskit_nature.second_q.mappers import QubitMapper, TaperedQubitMapper + + def build_electronic_ops( num_spatial_orbitals: int, num_particles: tuple[int, int], @@ -46,11 +36,9 @@ def build_electronic_ops( [int, tuple[int, int]], list[tuple[tuple[int, ...], tuple[int, ...]]], ], - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper, ) -> tuple[ - dict[str, PauliSumOp | SparsePauliOp], + dict[str, SparsePauliOp], dict[str, list[bool]], dict[str, tuple[tuple[int, ...], tuple[int, ...]]], ]: @@ -67,12 +55,7 @@ def build_electronic_ops( - and finally a callable which can be used to specify a custom list of excitations. For more details on how to write such a function refer to the default method, :meth:`generate_fermionic_excitations`. - qubit_mapper: The ``QubitMapper`` or ``QubitConverter`` (use of the latter is deprecated) to - use for mapping. - qubit_converter: DEPRECATED The ``QubitConverter`` or ``QubitMapper`` to use for mapping and - symmetry reduction. The Z2 symmetries stored in this instance are the basis for the - commutativity information returned by this method. These symmetries are set to ``None`` - when a ``QubitMapper`` is used. + qubit_mapper: The ``QubitMapper`` to use for mapping. Returns: A tuple containing the hopping operators, the types of commutativities and the excitation @@ -86,7 +69,7 @@ def build_electronic_ops( size = len(excitations_list) # build all hopping operators - hopping_operators: dict[str, PauliSumOp | SparsePauliOp] = {} + hopping_operators: dict[str, SparsePauliOp] = {} type_of_commutativities: dict[str, list[bool]] = {} excitation_indices: dict[str, tuple[tuple[int, ...], tuple[int, ...]]] = {} to_be_executed_list = [] @@ -116,8 +99,8 @@ def build_electronic_ops( def _build_single_hopping_operator( excitation: tuple[tuple[int, ...], tuple[int, ...]], num_spatial_orbitals: int, - qubit_mapper: QubitConverter | QubitMapper, -) -> tuple[PauliSumOp | SparsePauliOp, list[bool]]: + qubit_mapper: QubitMapper, +) -> tuple[SparsePauliOp, list[bool]]: label = [] for occ in excitation[0]: label.append(f"+_{occ}") @@ -125,10 +108,7 @@ def _build_single_hopping_operator( label.append(f"-_{unocc}") fer_op = FermionicOp({" ".join(label): 1.0}, num_spin_orbitals=2 * num_spatial_orbitals) - if isinstance(qubit_mapper, QubitConverter): - qubit_op = qubit_mapper.convert_only(fer_op, num_particles=qubit_mapper.num_particles) - symmetries_for_commutativity = qubit_mapper.z2symmetries.symmetries - elif isinstance(qubit_mapper, TaperedQubitMapper): + if isinstance(qubit_mapper, TaperedQubitMapper): qubit_op = qubit_mapper.map_clifford(fer_op) # Because the clifford conversion was already done, the commutativity information are based # on the single qubit pauli objects. @@ -141,10 +121,7 @@ def _build_single_hopping_operator( if not len(symmetries_for_commutativity) == 0: for symmetry in symmetries_for_commutativity: symmetry_op = SparsePauliOp.from_list([(symmetry.to_label(), 1.0)]) - if isinstance(qubit_op, PauliSumOp): - paulis = qubit_op.primitive.paulis - else: - paulis = qubit_op.paulis + paulis = qubit_op.paulis len_paulis = len(paulis) commuting = len(paulis.commutes_with_all(symmetry_op.paulis)) == len_paulis anticommuting = len(paulis.anticommutes_with_all(symmetry_op.paulis)) == len_paulis diff --git a/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom_vibrational_ops_builder.py b/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom_vibrational_ops_builder.py index 50050ac19..a9af7ed5e 100644 --- a/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom_vibrational_ops_builder.py +++ b/qiskit_nature/second_q/algorithms/excited_states_solvers/qeom_vibrational_ops_builder.py @@ -16,25 +16,15 @@ from typing import Callable -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit.tools import parallel_map from qiskit.utils import algorithm_globals from qiskit_nature.second_q.circuit.library import UVCC from qiskit_nature.second_q.operators import VibrationalOp -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper, TaperedQubitMapper -from qiskit_nature.deprecation import deprecate_arguments - - -@deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), -) +from qiskit_nature.second_q.mappers import QubitMapper, TaperedQubitMapper + + def build_vibrational_ops( num_modals: list[int], excitations: str @@ -44,11 +34,9 @@ def build_vibrational_ops( [int, tuple[int, int]], list[tuple[tuple[int, ...], tuple[int, ...]]], ], - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper, ) -> tuple[ - dict[str, PauliSumOp | SparsePauliOp], + dict[str, SparsePauliOp], dict[str, list[bool]], dict[str, tuple[tuple[int, ...], tuple[int, ...]]], ]: @@ -63,11 +51,8 @@ def build_vibrational_ops( - and finally a callable which can be used to specify a custom list of excitations. For more details on how to write such a function refer to the default method, :meth:`generate_vibrational_excitations`. - qubit_mapper: The ``QubitMapper`` or ``QubitConverter`` (use of the latter is deprecated) to - use for mapping. - qubit_converter: DEPRECATED The ``QubitConverter`` or ``QubitMapper`` to use for mapping and - symmetry reduction. Note that the ``QubitConverter`` will use its stored Z2 symmetries - as basis for the commutativity information returned by this method. + qubit_mapper: The ``QubitMapper`` to use for mapping. + Returns: Dict of hopping operators, dict of commutativity types and dict of excitation indices. """ @@ -76,7 +61,7 @@ def build_vibrational_ops( excitations_list = ansatz._get_excitation_list() size = len(excitations_list) - hopping_operators: dict[str, PauliSumOp | SparsePauliOp] = {} + hopping_operators: dict[str, SparsePauliOp] = {} excitation_indices: dict[str, tuple[tuple[int, ...], tuple[int, ...]]] = {} to_be_executed_list = [] for idx in range(size): @@ -106,8 +91,8 @@ def build_vibrational_ops( def _build_single_hopping_operator( excitation: tuple[tuple[int, ...], tuple[int, ...]], num_modals: list[int], - qubit_mapper: QubitConverter | QubitMapper, -) -> PauliSumOp: + qubit_mapper: QubitMapper, +) -> SparsePauliOp: label = [] for occ in excitation[0]: label.append(f"+_{VibrationalOp.build_dual_index(num_modals, occ)}") @@ -116,10 +101,8 @@ def _build_single_hopping_operator( vibrational_op = VibrationalOp({" ".join(label): 1}, num_modals) - qubit_op: PauliSumOp - if isinstance(qubit_mapper, QubitConverter): - qubit_op = qubit_mapper.convert_match(vibrational_op) - elif isinstance(qubit_mapper, TaperedQubitMapper): + qubit_op: SparsePauliOp + if isinstance(qubit_mapper, TaperedQubitMapper): qubit_op = qubit_mapper.map_clifford(vibrational_op) else: qubit_op = qubit_mapper.map(vibrational_op) diff --git a/qiskit_nature/second_q/algorithms/ground_state_solvers/__init__.py b/qiskit_nature/second_q/algorithms/ground_state_solvers/__init__.py index c8da44e3e..11f1e7ffc 100644 --- a/qiskit_nature/second_q/algorithms/ground_state_solvers/__init__.py +++ b/qiskit_nature/second_q/algorithms/ground_state_solvers/__init__.py @@ -20,18 +20,8 @@ from .ground_state_solver import GroundStateSolver from .ground_state_eigensolver import GroundStateEigensolver -from .minimum_eigensolver_factories import ( - MinimumEigensolverFactory, - NumPyMinimumEigensolverFactory, - VQEUCCFactory, - VQEUVCCFactory, -) __all__ = [ "GroundStateSolver", "GroundStateEigensolver", - "MinimumEigensolverFactory", - "NumPyMinimumEigensolverFactory", - "VQEUCCFactory", - "VQEUVCCFactory", ] diff --git a/qiskit_nature/second_q/algorithms/ground_state_solvers/ground_state_eigensolver.py b/qiskit_nature/second_q/algorithms/ground_state_solvers/ground_state_eigensolver.py index 5ead56c77..1936e813a 100644 --- a/qiskit_nature/second_q/algorithms/ground_state_solvers/ground_state_eigensolver.py +++ b/qiskit_nature/second_q/algorithms/ground_state_solvers/ground_state_eigensolver.py @@ -17,17 +17,14 @@ import logging from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolver -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.operators import SparseLabelOp -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from qiskit_nature.second_q.problems import BaseProblem from qiskit_nature.second_q.problems import EigenstateResult -from qiskit_nature.deprecation import deprecate_arguments, warn_deprecated_type from .ground_state_solver import GroundStateSolver -from .minimum_eigensolver_factories import MinimumEigensolverFactory LOGGER = logging.getLogger(__name__) @@ -35,43 +32,23 @@ class GroundStateEigensolver(GroundStateSolver): """Ground state computation using a minimum eigensolver.""" - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, - qubit_mapper: QubitConverter | QubitMapper, - solver: MinimumEigensolver | MinimumEigensolverFactory, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper, + solver: MinimumEigensolver, ) -> None: # pylint: disable=unused-argument """ Args: - qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` - or :class:`~qiskit_nature.second_q.mappers.QubitConverter` (use of the latter is - deprecated) instance that converts a second quantized operator to qubit operators. - qubit_converter: DEPRECATED The :class:`~qiskit_nature.second_q.mappers.QubitConverter` - or :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance that converts a - second quantized operator to qubit operators and applies subsequent qubit reduction. - solver: Minimum Eigensolver or MESFactory object, e.g. the VQEUCCSDFactory. + qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance that + converts a second quantized operator to qubit operators. + solver: Minimum Eigensolver object. """ super().__init__(qubit_mapper) - if isinstance(solver, MinimumEigensolverFactory): - warn_deprecated_type( - "0.6.0", - "solver", - "MinimumEigensolverFactory", - ) self._solver = solver @property - def solver(self) -> MinimumEigensolver | MinimumEigensolverFactory: + def solver(self) -> MinimumEigensolver: return self._solver def supports_aux_operators(self): @@ -80,7 +57,7 @@ def supports_aux_operators(self): def solve( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | SparsePauliOp | PauliSumOp] | None = None, + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, ) -> EigenstateResult: """Compute Ground State properties. @@ -93,9 +70,7 @@ def solve( :meth:`~.BaseProblem.interpret`. """ main_operator, aux_ops = self.get_qubit_operators(problem, aux_operators) - raw_mes_result = self.solver.compute_minimum_eigenvalue( # type: ignore - main_operator, aux_ops - ) + raw_mes_result = self.solver.compute_minimum_eigenvalue(main_operator, aux_ops) eigenstate_result = EigenstateResult.from_result(raw_mes_result) result = problem.interpret(eigenstate_result) @@ -104,39 +79,19 @@ def solve( def get_qubit_operators( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | SparsePauliOp | PauliSumOp] | None = None, - ) -> tuple[SparsePauliOp | PauliSumOp, dict[str, SparsePauliOp | PauliSumOp] | None]: + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, + ) -> tuple[SparsePauliOp, dict[str, SparsePauliOp] | None]: # Note that ``aux_ops`` contains not only the transformed ``aux_operators`` passed by the # user but also additional ones from the transformation main_second_q_op, aux_second_q_ops = problem.second_q_ops() - num_particles = getattr(problem, "num_particles", None) - - if isinstance(self._qubit_mapper, QubitConverter): - main_operator = self._qubit_mapper.convert( - main_second_q_op, - num_particles=num_particles, - sector_locator=problem.symmetry_sector_locator, - ) - aux_ops = self._qubit_mapper.convert_match(aux_second_q_ops) - else: - main_operator = self._qubit_mapper.map(main_second_q_op) - aux_ops = self._qubit_mapper.map(aux_second_q_ops) + main_operator = self._qubit_mapper.map(main_second_q_op) + aux_ops = self._qubit_mapper.map(aux_second_q_ops) if aux_operators is not None: for name_aux, aux_op in aux_operators.items(): - if isinstance(aux_op, PauliSumOp): - warn_deprecated_type( - "0.6.0", - argument_name="aux_operators", - old_type="PauliSumOp", - new_type="SparsePauliOp", - ) if isinstance(aux_op, SparseLabelOp): - if isinstance(self._qubit_mapper, QubitConverter): - converted_aux_op = self._qubit_mapper.convert_match(aux_op) - else: - converted_aux_op = self._qubit_mapper.map(aux_op) + converted_aux_op = self._qubit_mapper.map(aux_op) else: converted_aux_op = aux_op @@ -160,14 +115,6 @@ def get_qubit_operators( name_aux, ) - if isinstance(self.solver, MinimumEigensolverFactory): - warn_deprecated_type( - "0.6.0", - "solver", - "MinimumEigensolverFactory", - ) - # this must be called after transformation.transform - self._solver = self.solver.get_solver(problem, self._qubit_mapper) # if the eigensolver does not support auxiliary operators, reset them if not self.solver.supports_aux_operators(): aux_ops = None diff --git a/qiskit_nature/second_q/algorithms/ground_state_solvers/ground_state_solver.py b/qiskit_nature/second_q/algorithms/ground_state_solvers/ground_state_solver.py index 14b199119..7b126238e 100644 --- a/qiskit_nature/second_q/algorithms/ground_state_solvers/ground_state_solver.py +++ b/qiskit_nature/second_q/algorithms/ground_state_solvers/ground_state_solver.py @@ -16,42 +16,26 @@ from abc import ABC, abstractmethod -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.operators import SparseLabelOp -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from qiskit_nature.second_q.problems import BaseProblem from qiskit_nature.second_q.problems import EigenstateResult -from qiskit_nature.deprecation import deprecate_arguments, deprecate_property class GroundStateSolver(ABC): """The ground state calculation interface.""" - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper, ) -> None: # pylint: disable=unused-argument """ Args: - qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` - or :class:`~qiskit_nature.second_q.mappers.QubitConverter` (use of the latter is - deprecated) instance that converts a second quantized operator to qubit operators. - qubit_converter: DEPRECATED The :class:`~qiskit_nature.second_q.mappers.QubitConverter` - or :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance that converts a - second quantized operator to qubit operators and applies subsequent qubit reduction. + qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance that + converts a second quantized operator to qubit operators. """ self._qubit_mapper = qubit_mapper @@ -59,7 +43,7 @@ def __init__( def solve( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | SparsePauliOp | PauliSumOp] | None = None, + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, ) -> EigenstateResult: """Compute the ground state energy of the molecule that was supplied via the driver. @@ -77,10 +61,10 @@ def solve( def get_qubit_operators( self, problem: BaseProblem, - aux_operators: dict[str, SparseLabelOp | SparsePauliOp | PauliSumOp] | None = None, - ) -> tuple[SparsePauliOp | PauliSumOp, dict[str, SparsePauliOp | PauliSumOp] | None]: + aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, + ) -> tuple[SparsePauliOp, dict[str, SparsePauliOp] | None]: """Gets the operator and auxiliary operators, and transforms the provided auxiliary operators - using a ``QubitConverter`` or ``QubitMapper``. + using a ``QubitMapper``. If the user-provided ``aux_operators`` contain a name which clashes with an internally constructed auxiliary operator, then the corresponding internal operator will be overridden by the user-provided operator. @@ -99,12 +83,6 @@ def supports_aux_operators(self) -> bool: """Returns whether the eigensolver supports auxiliary operators.""" raise NotImplementedError - @property - @deprecate_property("0.6.0", new_name="qubit_mapper") - def qubit_converter(self): - """DEPRECATED Returns the qubit converter.""" - return self._qubit_mapper - @property def qubit_mapper(self): """Returns the qubit mapper.""" diff --git a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py b/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py deleted file mode 100644 index b6902b65b..000000000 --- a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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. - -""" -Minimum Eigensolver Factories -(:mod:`qiskit_nature.second_q.algorithms.ground_state_solvers.minimum_eigensolver_factories`) -============================================================================================= - -DEPRECATED Factories that create a minimum eigensolver based on a qubit transformation. - -.. currentmodule:: qiskit_nature.second_q.algorithms.ground_state_solvers.minimum_eigensolver_factories - -""" - -from .minimum_eigensolver_factory import MinimumEigensolverFactory -from .numpy_minimum_eigensolver_factory import NumPyMinimumEigensolverFactory -from .vqe_ucc_factory import VQEUCCFactory -from .vqe_uvcc_factory import VQEUVCCFactory - -__all__ = [ - "MinimumEigensolverFactory", - "NumPyMinimumEigensolverFactory", - "VQEUCCFactory", - "VQEUVCCFactory", -] diff --git a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py b/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py deleted file mode 100644 index 1658d3c37..000000000 --- a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py +++ /dev/null @@ -1,70 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 __future__ import annotations - -from abc import ABC, abstractmethod - -from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolver - -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper -from qiskit_nature.second_q.problems import BaseProblem -from qiskit_nature.deprecation import deprecate_arguments - - -class MinimumEigensolverFactory(ABC): - """DEPRECATED A factory to construct a minimum eigensolver based on a qubit operator - transformation. - """ - - @abstractmethod - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) - def get_solver( - self, - problem: BaseProblem, - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, - ) -> MinimumEigensolver: - """Returns a minimum eigensolver, based on the qubit operator transformation. - - Args: - problem: A class encoding a problem to be solved. - qubit_mapper: A class that converts second quantized operator to qubit operator. - Providing a ``QubitConverter`` instance here is deprecated. - qubit_converter: DEPRECATED A class that converts second quantized operator to qubit - operator according to a mapper it is initialized with. - - Returns: - A minimum eigensolver suitable to compute the ground state of the molecule. - """ - raise NotImplementedError - - @abstractmethod - def supports_aux_operators(self) -> bool: - """Returns whether the eigensolver generated by this factory supports auxiliary operators.""" - raise NotImplementedError - - @property - @abstractmethod - def minimum_eigensolver(self) -> MinimumEigensolver: - """Returns the solver instance.""" - raise NotImplementedError diff --git a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py b/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py deleted file mode 100644 index efed6ab2a..000000000 --- a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py +++ /dev/null @@ -1,101 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 __future__ import annotations - -from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolver, NumPyMinimumEigensolver - -from qiskit_nature.deprecation import DeprecatedType, warn_deprecated -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper -from qiskit_nature.second_q.problems import BaseProblem -from qiskit_nature.deprecation import deprecate_arguments - -from .minimum_eigensolver_factory import MinimumEigensolverFactory - - -class NumPyMinimumEigensolverFactory(MinimumEigensolverFactory): - """DEPRECATED A factory to construct a NumPyMinimumEigensolver. - - .. warning:: - - This class is deprecated! Please see :ref:`this guide ` for how to replace - your usage of it! - """ - - def __init__( - self, - use_default_filter_criterion: bool = False, - **kwargs, - ) -> None: - """ - Args: - use_default_filter_criterion: Whether to use the transformation's default filter - criterion. - kwargs: Keyword arguments passed to NumpyMinimumEigensolver to construct - the internal ``MinimumEigensolver``. - """ - warn_deprecated( - "0.6.0", - DeprecatedType.CLASS, - "NumPyMinimumEigensolverFactory", - additional_msg=( - ". This class is deprecated without replacement. Instead, refer to this how-to " - "guide which explains the steps you need to take to replace the use of this class: " - "https://qiskit.org/documentation/nature/howtos/numpy_minimum_eigensolver.html" - ), - ) - self._use_default_filter_criterion = use_default_filter_criterion - self._minimum_eigensolver = NumPyMinimumEigensolver(**kwargs) - - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) - def get_solver( - self, - problem: BaseProblem, - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, - ) -> MinimumEigensolver: - # pylint: disable=unused-argument - """Returns a NumPyMinimumEigensolver which possibly uses the default filter criterion - provided by the ``problem``. - - Args: - problem: A class encoding a problem to be solved. - qubit_mapper: A class that converts second quantized operator to qubit operator. - Providing a ``QubitConverter`` instance here is deprecated. - qubit_converter: DEPRECATED A class that converts second quantized operator to qubit - operator according to a mapper it is initialized with. - Returns: - A NumPyMinimumEigensolver suitable to compute the ground state of the molecule. - """ - - if not self.minimum_eigensolver.filter_criterion and self._use_default_filter_criterion: - self._minimum_eigensolver.filter_criterion = problem.get_default_filter_criterion() - - return self._minimum_eigensolver - - def supports_aux_operators(self): - return NumPyMinimumEigensolver.supports_aux_operators() - - @property - def minimum_eigensolver(self) -> NumPyMinimumEigensolver: - """Returns the solver instance.""" - return self._minimum_eigensolver diff --git a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_ucc_factory.py b/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_ucc_factory.py deleted file mode 100644 index 1cdc9f529..000000000 --- a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_ucc_factory.py +++ /dev/null @@ -1,190 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 __future__ import annotations - -import logging -import numpy as np -from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolver, VQE -from qiskit.algorithms.optimizers import Minimizer, Optimizer -from qiskit.circuit import QuantumCircuit -from qiskit.primitives import BaseEstimator - -from qiskit_nature.deprecation import DeprecatedType, warn_deprecated -from qiskit_nature.second_q.circuit.library import HartreeFock, UCC -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper -from qiskit_nature.second_q.problems import ( - ElectronicStructureProblem, -) -from qiskit_nature.deprecation import deprecate_arguments - -from ...initial_points import InitialPoint, HFInitialPoint -from .minimum_eigensolver_factory import MinimumEigensolverFactory - -logger = logging.getLogger(__name__) - - -class VQEUCCFactory(MinimumEigensolverFactory): - """DEPRECATED Factory to construct a :class:`~qiskit.algorithms.minimum_eigensolvers.VQE` - minimum eigensolver with :class:`~.UCC` ansatz wavefunction. - - .. warning:: - - This class is deprecated! Please see :ref:`this guide ` for how to replace - your usage of it! - """ - - def __init__( - self, - estimator: BaseEstimator, - ansatz: UCC, - optimizer: Optimizer | Minimizer, - *, - initial_point: np.ndarray | InitialPoint | None = None, - initial_state: QuantumCircuit | None = None, - **kwargs, - ) -> None: - """ - Args: - estimator: The ``BaseEstimator`` class to use for the internal - :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. - ansatz: The ``UCC`` ansatz. Its attributes ``qubit_mapper``, ``num_particles``, - ``num_spatial_orbitals``, and ``initial_point`` will be completed at runtime based on - the problem being solved. - optimizer: The ``Optimizer`` or ``Minimizer`` to use for the internal - :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. - initial_point: An optional initial point (i.e., initial parameter values for the VQE - optimizer). If ``None`` then VQE will use an all-zero initial point of the - appropriate length computed using - :class:`~qiskit_nature.second_q.algorithms.initial_points.\ - hf_initial_point.HFInitialPoint`. - This then defaults to the Hartree-Fock (HF) state when the HF circuit is prepended - to the ansatz circuit. If another ``InitialPoint`` instance, this is used to - compute an initial point for the VQE ansatz parameters. If a user-provided NumPy - array, this is used directly. - initial_state: Allows specification of a custom ``QuantumCircuit`` to be used as the - initial state of the ansatz. If this is never set by the user, the factory will - default to the :class:`~.HartreeFock` state. - kwargs: Remaining keyword arguments are passed to the - :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. - """ - warn_deprecated( - "0.6.0", - DeprecatedType.CLASS, - "VQEUCCFactory", - additional_msg=( - ". This class is deprecated without replacement. Instead, refer to this how-to " - "guide which explains the steps you need to take to replace the use of this class: " - "https://qiskit.org/documentation/nature/howtos/vqe_ucc.html" - ), - ) - self._initial_state = initial_state - self._initial_point = initial_point if initial_point is not None else HFInitialPoint() - - self._vqe = VQE(estimator, ansatz, optimizer, **kwargs) - - @property - def ansatz(self) -> UCC: - """Gets the user provided ansatz of future VQEs produced by the factory.""" - return self.minimum_eigensolver.ansatz - - @ansatz.setter - def ansatz(self, ansatz: UCC) -> None: - """Sets the ansatz of future VQEs produced by the factory.""" - self.minimum_eigensolver.ansatz = ansatz - - @property - def initial_point(self) -> np.ndarray | InitialPoint | None: - """Gets the initial point of future VQEs produced by the factory.""" - return self._initial_point - - @initial_point.setter - def initial_point(self, initial_point: np.ndarray | InitialPoint | None) -> None: - """Sets the initial point of future VQEs produced by the factory.""" - self._initial_point = initial_point - - @property - def initial_state(self) -> QuantumCircuit | None: - """ - Getter of the initial state. - If value is ``None`` it will default to using the :class:`~.HartreeFock`. - """ - return self._initial_state - - @initial_state.setter - def initial_state(self, initial_state: QuantumCircuit | None) -> None: - """ - Setter of the initial state. - If ``None`` is passed, this factory will default to using the :class:`~.HartreeFock`. - """ - self._initial_state = initial_state - - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) - def get_solver( - self, - problem: ElectronicStructureProblem, - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, - ) -> MinimumEigensolver: - # pylint: disable=unused-argument - """Returns a VQE with a UCC wavefunction ansatz, based on ``qubit_mapper``. - - Args: - problem: A class encoding a problem to be solved. - qubit_mapper: A class that converts second quantized operator to qubit operator. - Providing a ``QubitConverter`` instance here is deprecated. - qubit_converter: DEPRECATED A class that converts second quantized operator to qubit - operator according to a mapper it is initialized with. - - Returns: - A VQE suitable to compute the ground state of the molecule. - """ - driver_result = problem - num_spatial_orbitals = problem.num_spatial_orbitals - num_particles = problem.num_alpha, problem.num_beta - - initial_state = self.initial_state - if initial_state is None: - initial_state = HartreeFock(num_spatial_orbitals, num_particles, qubit_mapper) - - self.ansatz.qubit_mapper = qubit_mapper - self.ansatz.num_particles = num_particles - self.ansatz.num_spatial_orbitals = num_spatial_orbitals - self.ansatz.initial_state = initial_state - - if isinstance(self.initial_point, InitialPoint): - self.initial_point.ansatz = self.ansatz - self.initial_point.problem = driver_result - initial_point = self.initial_point.to_numpy_array() - else: - initial_point = self.initial_point - - self.minimum_eigensolver.initial_point = initial_point - return self.minimum_eigensolver - - def supports_aux_operators(self): - return VQE.supports_aux_operators() - - @property - def minimum_eigensolver(self) -> VQE: - """Returns the solver instance.""" - return self._vqe diff --git a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uvcc_factory.py b/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uvcc_factory.py deleted file mode 100644 index aef44d880..000000000 --- a/qiskit_nature/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uvcc_factory.py +++ /dev/null @@ -1,182 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 __future__ import annotations - -import logging -import numpy as np - -from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolver, VQE -from qiskit.algorithms.optimizers import Minimizer, Optimizer -from qiskit.circuit import QuantumCircuit -from qiskit.primitives import BaseEstimator - -from qiskit_nature.deprecation import DeprecatedType, warn_deprecated -from qiskit_nature.second_q.circuit.library import UVCC, VSCF -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper -from qiskit_nature.second_q.problems import ( - VibrationalStructureProblem, -) -from qiskit_nature.deprecation import deprecate_arguments - -from .minimum_eigensolver_factory import MinimumEigensolverFactory -from ...initial_points import InitialPoint, VSCFInitialPoint - -logger = logging.getLogger(__name__) - - -class VQEUVCCFactory(MinimumEigensolverFactory): - """DEPRECATED Factory to construct a :class:`~qiskit.algorithms.minimum_eigensolvers.VQE` - minimum eigensolver with :class:`~.UVCC` ansatz wavefunction. - - .. warning:: - - This class is deprecated! Please see :ref:`this guide ` for how to replace - your usage of it! - """ - - def __init__( - self, - estimator: BaseEstimator, - ansatz: UVCC, - optimizer: Optimizer | Minimizer, - *, - initial_point: np.ndarray | InitialPoint | None = None, - initial_state: QuantumCircuit | None = None, - **kwargs, - ) -> None: - """ - Args: - estimator: The ``BaseEstimator`` class to use for the internal - :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. - ansatz: The ``UVCC`` ansatz. Its attributes ``qubit_mapper``, ``num_modals``, and - ``initial_point`` will be completed at runtime based on the problem being solved. - optimizer: The ``Optimizer`` or ``Minimizer`` to use for the internal - :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. - initial_point: An optional initial point (i.e., initial parameter values for the VQE - optimizer). If ``None`` then VQE will use an all-zero initial point of the - appropriate length computed using - :class:`~initial_points.vscf_initial_point.VSCFInitialPoint`. - This then defaults to the VSCF state when the VSCF circuit is prepended - to the ansatz circuit. If another ``InitialPoint`` instance, this is used to - compute an initial point for the VQE ansatz parameters. If a user-provided NumPy - array, this is used directly. - initial_state: Allows specification of a custom ``QuantumCircuit`` to be used as the - initial state of the ansatz. If this is never set by the user, the factory will - default to the :class:`~.VSCF` state. - kwargs: Remaining keyword arguments are passed to the - :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. - """ - warn_deprecated( - "0.6.0", - DeprecatedType.CLASS, - "VQEUVCCFactory", - additional_msg=( - ". This class is deprecated without replacement. Instead, refer to this how-to " - "guide which explains the steps you need to take to replace the use of this class: " - "https://qiskit.org/documentation/nature/howtos/vqe_uvcc.html" - ), - ) - self._initial_state = initial_state - self._initial_point = initial_point if initial_point is not None else VSCFInitialPoint() - - self._vqe = VQE(estimator, ansatz, optimizer, **kwargs) - - @property - def ansatz(self) -> UVCC: - """Gets the user provided ansatz of future VQEs produced by the factory.""" - return self.minimum_eigensolver.ansatz - - @ansatz.setter - def ansatz(self, ansatz: UVCC) -> None: - """Sets the ansatz of future VQEs produced by the factory.""" - self.minimum_eigensolver.ansatz = ansatz - - @property - def initial_state(self) -> QuantumCircuit | None: - """Getter of the initial state.""" - return self._initial_state - - @initial_state.setter - def initial_state(self, initial_state: QuantumCircuit | None) -> None: - """ - Setter of the initial state. - If ``None`` is passed, this factory will default to using the :class:`~.VSCF`. - """ - self._initial_state = initial_state - - @property - def initial_point(self) -> np.ndarray | InitialPoint | None: - """ - Gets the initial point of future VQEs produced by the factory. - """ - return self._initial_point - - @initial_point.setter - def initial_point(self, initial_point: np.ndarray | InitialPoint | None) -> None: - """Sets the initial point of future VQEs produced by the factory.""" - self._initial_point = initial_point - - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) - def get_solver( - self, - problem: VibrationalStructureProblem, - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, - ) -> MinimumEigensolver: - # pylint: disable=unused-argument - """Returns a VQE with a :class:`~.UVCC` wavefunction ansatz, based on ``qubit_mapper``. - - Args: - problem: A class encoding a problem to be solved. - qubit_mapper: A class that converts second quantized operator to qubit operator. - Providing a ``QubitConverter`` instance here is deprecated. - qubit_converter: DEPRECATED A class that converts second quantized operator to qubit - operator according to a mapper it is initialized with. - - Returns: - A VQE suitable to compute the ground state of the molecule. - """ - initial_state = self.initial_state - if initial_state is None: - initial_state = VSCF(problem.num_modals) - - self.ansatz.qubit_mapper = qubit_mapper - self.ansatz.num_modals = problem.num_modals - self.ansatz.initial_state = initial_state - - if isinstance(self.initial_point, InitialPoint): - self.initial_point.ansatz = self.ansatz - initial_point = self.initial_point.to_numpy_array() - else: - initial_point = self.initial_point - - self.minimum_eigensolver.initial_point = initial_point - return self.minimum_eigensolver - - def supports_aux_operators(self): - return VQE.supports_aux_operators() - - @property - def minimum_eigensolver(self) -> VQE: - """Returns the solver instance.""" - return self._vqe diff --git a/qiskit_nature/second_q/algorithms/initial_points/mp2_initial_point.py b/qiskit_nature/second_q/algorithms/initial_points/mp2_initial_point.py index cab7a9020..a77cab2ae 100644 --- a/qiskit_nature/second_q/algorithms/initial_points/mp2_initial_point.py +++ b/qiskit_nature/second_q/algorithms/initial_points/mp2_initial_point.py @@ -19,7 +19,7 @@ from qiskit_nature.exceptions import QiskitNatureError from qiskit_nature.second_q.circuit.library import UCC from qiskit_nature.second_q.operators import ElectronicIntegrals -from qiskit_nature.second_q.operators.tensor import ARRAY_TYPE, Tensor +from qiskit_nature.second_q.operators.tensor import Tensor from qiskit_nature.second_q.operators.symmetric_two_body import SymmetricTwoBodyIntegrals, unfold from qiskit_nature.second_q.problems import BaseProblem, ElectronicStructureProblem from qiskit_nature.utils import get_einsum @@ -28,7 +28,7 @@ def _compute_mp2( - num_occ: int, integral_matrix: ARRAY_TYPE | Tensor, orbital_energies: np.ndarray + num_occ: int, integral_matrix: Tensor, orbital_energies: np.ndarray ) -> tuple[np.ndarray, float]: """Compute the T2 amplitudes and MP2 energy correction. @@ -45,9 +45,6 @@ def _compute_mp2( """ if isinstance(integral_matrix, SymmetricTwoBodyIntegrals): integral_matrix = unfold(integral_matrix) - elif not isinstance(integral_matrix, Tensor): - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - integral_matrix = Tensor(integral_matrix) # We use NumPy broadcasting to compute the matrix of occupied - virtual energy deltas with # shape (num_occ, num_vir), such that diff --git a/qiskit_nature/second_q/circuit/library/ansatzes/puccd.py b/qiskit_nature/second_q/circuit/library/ansatzes/puccd.py index 3e8d088c0..d7489f1d2 100644 --- a/qiskit_nature/second_q/circuit/library/ansatzes/puccd.py +++ b/qiskit_nature/second_q/circuit/library/ansatzes/puccd.py @@ -19,8 +19,7 @@ from qiskit.circuit import QuantumCircuit from qiskit_nature import QiskitNatureError -from qiskit_nature.deprecation import deprecate_arguments -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from .ucc import UCC from .utils.fermionic_excitation_generator import ( @@ -48,26 +47,17 @@ class PUCCD(UCC): """ - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, num_spatial_orbitals: int | None = None, num_particles: tuple[int, int] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, reps: int = 1, initial_state: QuantumCircuit | None = None, include_singles: tuple[bool, bool] = (False, False), generalized: bool = False, include_imaginary: bool = False, - qubit_converter: QubitConverter | QubitMapper | None = None, ) -> None: # pylint: disable=unused-argument """ @@ -75,9 +65,8 @@ def __init__( Args: num_spatial_orbitals: The number of spatial orbitals. num_particles: The tuple of the number of alpha- and beta-spin particles. - qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` or - :class:`~qiskit_nature.second_q.mappers.QubitConverter` instance (use of the latter - is deprecated) which takes care of mapping to a qubit operator. + qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` which takes care + of mapping to a qubit operator. reps: The number of times to repeat the evolved operators. initial_state: A ``QuantumCircuit`` object to prepend to the circuit. include_singles: enables the inclusion of single excitations per spin species. @@ -87,9 +76,6 @@ def __init__( particles. include_imaginary: Boolean flag which when set to ``True`` expands the ansatz to include imaginary parts using twice the number of free parameters. - qubit_converter: DEPRECATED The :class:`~qiskit_nature.second_q.mappers.QubitConverter` - or :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance which takes care of - mapping to a qubit operator. Raises: QiskitNatureError: if the number of alpha and beta electrons is not equal. diff --git a/qiskit_nature/second_q/circuit/library/ansatzes/succd.py b/qiskit_nature/second_q/circuit/library/ansatzes/succd.py index 1c142702d..6726bcfdb 100644 --- a/qiskit_nature/second_q/circuit/library/ansatzes/succd.py +++ b/qiskit_nature/second_q/circuit/library/ansatzes/succd.py @@ -23,8 +23,7 @@ from qiskit.circuit import QuantumCircuit from qiskit_nature import QiskitNatureError -from qiskit_nature.deprecation import deprecate_arguments -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from qiskit_nature.second_q.operators import FermionicOp @@ -54,35 +53,25 @@ class SUCCD(UCC): """ - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, num_spatial_orbitals: int | None = None, num_particles: tuple[int, int] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, reps: int = 1, initial_state: QuantumCircuit | None = None, include_singles: tuple[bool, bool] = (False, False), generalized: bool = False, mirror: bool = False, - qubit_converter: QubitConverter | QubitMapper | None = None, ) -> None: # pylint: disable=unused-argument """ Args: num_spatial_orbitals: The number of spatial orbitals. num_particles: The tuple of the number of alpha- and beta-spin particles. - qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` or - :class:`~qiskit_nature.second_q.mappers.QubitConverter` instance (use of the latter - is deprecated) which takes care of mapping to a qubit operator. + qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` which takes care + of mapping to a qubit operator. reps: The number of times to repeat the evolved operators. initial_state: A ``QuantumCircuit`` object to prepend to the circuit. include_singles: enables the inclusion of single excitations per spin species. @@ -95,9 +84,7 @@ def __init__( parameters. This results in mirrored excitations having identical parameter values. Enabling this parameter will result in the SUCCD ansatz referred to as "q-UCCSD0-full" in reference [1]. - qubit_converter: DEPRECATED The :class:`~qiskit_nature.second_q.mappers.QubitConverter` - or :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance which takes care of - mapping to a qubit operator. + Raises: QiskitNatureError: if the number of alpha and beta electrons is not equal. """ diff --git a/qiskit_nature/second_q/circuit/library/ansatzes/ucc.py b/qiskit_nature/second_q/circuit/library/ansatzes/ucc.py index ac8c90bfc..44e3e4d2f 100644 --- a/qiskit_nature/second_q/circuit/library/ansatzes/ucc.py +++ b/qiskit_nature/second_q/circuit/library/ansatzes/ucc.py @@ -24,8 +24,7 @@ from qiskit.circuit.library import EvolvedOperatorAnsatz from qiskit_nature import QiskitNatureError -from qiskit_nature.deprecation import deprecate_arguments, deprecate_property, warn_deprecated_type -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper, TaperedQubitMapper +from qiskit_nature.second_q.mappers import QubitMapper, TaperedQubitMapper from qiskit_nature.second_q.operators import FermionicOp, SparseLabelOp from .utils.fermionic_excitation_generator import generate_fermionic_excitations @@ -124,14 +123,6 @@ def custom_excitation_list(num_spatial_orbitals: int, "q": 4, } - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, num_spatial_orbitals: int | None = None, @@ -144,7 +135,7 @@ def __init__( list[tuple[tuple[int, ...], tuple[int, ...]]], ] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, alpha_spin: bool = True, beta_spin: bool = True, @@ -154,7 +145,6 @@ def __init__( include_imaginary: bool = False, reps: int = 1, initial_state: QuantumCircuit | None = None, - qubit_converter: QubitConverter | QubitMapper | None = None, ) -> None: # pylint: disable=unused-argument """ @@ -177,9 +167,8 @@ def __init__( to write such a callable refer to the default method :meth:`~qiskit_nature.second_q.circuit.library.ansatzes.utils.\ generate_fermionic_excitations`. - qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` or - :class:`~qiskit_nature.second_q.mappers.QubitConverter` instance (use of the latter - is deprecated) which takes care of mapping to a qubit operator. + qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` which takes care + of mapping to a qubit operator. alpha_spin: Boolean flag whether to include alpha-spin excitations. beta_spin: Boolean flag whether to include beta-spin excitations. max_spin_excitation: The largest number of excitations within a spin. E.g. you can set @@ -203,9 +192,6 @@ def __init__( likely you will also want to use a :class:`~qiskit_nature.second_q.algorithms.initial_points.HFInitialPoint` that has been configured using the corresponding ansatz parameters. - qubit_converter: DEPRECATED The :class:`~qiskit_nature.second_q.mappers.QubitConverter` - or :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance which takes care of - mapping to a qubit operator. """ self._qubit_mapper = qubit_mapper self._num_particles = num_particles @@ -236,31 +222,13 @@ def __init__( _ = self.operators @property - @deprecate_property("0.6.0", new_name="qubit_mapper") - def qubit_converter(self) -> QubitConverter | QubitMapper | None: - """DEPRECATED The qubit operator converter.""" - return self._qubit_mapper - - @qubit_converter.setter - def qubit_converter(self, conv: QubitConverter | QubitMapper | None) -> None: - """Sets the qubit operator converter.""" - self.qubit_mapper = conv - - @property - def qubit_mapper(self) -> QubitConverter | QubitMapper | None: + def qubit_mapper(self) -> QubitMapper | None: """The qubit operator mapper.""" return self._qubit_mapper @qubit_mapper.setter - def qubit_mapper(self, mapper: QubitConverter | QubitMapper | None) -> None: + def qubit_mapper(self, mapper: QubitMapper | None) -> None: """Sets the qubit operator mapper.""" - if isinstance(mapper, QubitConverter): - warn_deprecated_type( - "0.6.0", - argument_name="mapper", - old_type="QubitConverter", - new_type="QubitMapper", - ) self._operators = None self._invalidate() self._qubit_mapper = mapper @@ -338,16 +306,14 @@ def operators(self): # pylint: disable=invalid-overridden-method excitation_ops = self.excitation_ops() logger.debug("Converting second-quantized into qubit operators...") - # Convert operators according to saved state in converter from the conversion of the + # Convert operators according to saved state in mapper from the conversion of the # main operator since these need to be compatible. If Z2 Symmetry tapering was done # it may be that one or more excitation operators do not commute with the symmetry. - # The converted operators are maintained at the same index by the converter + # The converted operators are maintained at the same index by the mapper # inserting ``None`` as the result if an operator did not commute. To ensure that # the ``excitation_list`` is transformed identically to the operators, we retain # ``None`` for non-commuting operators in order to manually remove them in unison. - if isinstance(self.qubit_mapper, QubitConverter): - operators = self.qubit_mapper.convert_match(excitation_ops, suppress_none=False) - elif isinstance(self.qubit_mapper, TaperedQubitMapper): + if isinstance(self.qubit_mapper, TaperedQubitMapper): operators = self.qubit_mapper.map_clifford(excitation_ops) operators = self.qubit_mapper.taper_clifford(operators, suppress_none=False) else: diff --git a/qiskit_nature/second_q/circuit/library/ansatzes/uccsd.py b/qiskit_nature/second_q/circuit/library/ansatzes/uccsd.py index ebed41baf..a50214fc2 100644 --- a/qiskit_nature/second_q/circuit/library/ansatzes/uccsd.py +++ b/qiskit_nature/second_q/circuit/library/ansatzes/uccsd.py @@ -16,8 +16,7 @@ from __future__ import annotations from qiskit.circuit import QuantumCircuit -from qiskit_nature.deprecation import deprecate_arguments -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from .ucc import UCC @@ -27,35 +26,25 @@ class UCCSD(UCC): This is a convenience subclass of the UCC ansatz. For more information refer to :class:`UCC`. """ - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, num_spatial_orbitals: int | None = None, num_particles: tuple[int, int] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, reps: int = 1, initial_state: QuantumCircuit | None = None, generalized: bool = False, preserve_spin: bool = True, include_imaginary: bool = False, - qubit_converter: QubitConverter | QubitMapper | None = None, ) -> None: # pylint: disable=unused-argument """ Args: num_spatial_orbitals: The number of spatial orbitals. num_particles: The tuple of the number of alpha- and beta-spin particles. - qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` or - :class:`~qiskit_nature.second_q.mappers.QubitConverter` instance (use of the latter - is deprecated) which takes care of mapping to a qubit operator. + qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` which takes care + of mapping to a qubit operator. reps: The number of times to repeat the evolved operators. initial_state: A ``QuantumCircuit`` object to prepend to the circuit. generalized: Boolean flag whether or not to use generalized excitations, which ignore @@ -65,10 +54,6 @@ def __init__( preserve_spin: Boolean flag whether or not to preserve the particle spins. include_imaginary: Boolean flag which when set to ``True`` expands the ansatz to include imaginary parts using twice the number of free parameters. - qubit_converter: DEPRECATED The :class:`~qiskit_nature.second_q.mappers.QubitConverter` - or :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance which takes care of - mapping to a qubit operator. - """ super().__init__( num_spatial_orbitals=num_spatial_orbitals, diff --git a/qiskit_nature/second_q/circuit/library/ansatzes/uvcc.py b/qiskit_nature/second_q/circuit/library/ansatzes/uvcc.py index 5eff70ed1..fe933673d 100644 --- a/qiskit_nature/second_q/circuit/library/ansatzes/uvcc.py +++ b/qiskit_nature/second_q/circuit/library/ansatzes/uvcc.py @@ -22,8 +22,7 @@ from qiskit.circuit.library import EvolvedOperatorAnsatz from qiskit_nature import QiskitNatureError -from qiskit_nature.deprecation import deprecate_arguments, deprecate_property, warn_deprecated_type -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper, TaperedQubitMapper +from qiskit_nature.second_q.mappers import QubitMapper, TaperedQubitMapper from qiskit_nature.second_q.operators import SparseLabelOp, VibrationalOp from .utils.vibration_excitation_generator import generate_vibration_excitations @@ -60,14 +59,6 @@ class UVCC(EvolvedOperatorAnsatz): "q": 4, } - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, num_modals: list[int] | None = None, @@ -79,11 +70,10 @@ def __init__( list[tuple[tuple[int, ...], tuple[int, ...]]], ] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, reps: int = 1, initial_state: QuantumCircuit | None = None, - qubit_converter: QubitConverter | QubitMapper | None = None, ) -> None: # pylint: disable=unused-argument """ @@ -105,9 +95,8 @@ def __init__( ``list[tuple[tuple[int, ...], tuple[int, ...]]]``. For more information on how to write such a callable refer to the default method :meth:`~qiskit_nature.\ second_q.circuit.library.ansatzes.utils.generate_vibration_excitations`. - qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` or - :class:`~qiskit_nature.second_q.mappers.QubitConverter` instance (use of the latter - is deprecated) which takes care of mapping to a qubit operator. + qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` which takes care + of mapping to a qubit operator. reps: The number of repetitions of basic module. initial_state: A ``QuantumCircuit`` object to prepend to the circuit. Note that this setting does *not* influence the ``excitations``. When relying on the default @@ -118,9 +107,6 @@ def __init__( also want to use a :class:`~qiskit_nature.second_q.algorithms.initial_points.VSCFInitialPoint` that has been configured using the corresponding ansatz parameters. - qubit_converter: DEPRECATED The :class:`~qiskit_nature.second_q.mappers.QubitConverter` - or :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance which takes care of - mapping to a qubit operator. """ self._qubit_mapper = qubit_mapper self._num_modals = num_modals @@ -144,31 +130,13 @@ def __init__( _ = self.operators @property - @deprecate_property("0.6.0", new_name="qubit_mapper") - def qubit_converter(self) -> QubitConverter | QubitMapper | None: - """DEPRECATED The qubit operator converter.""" - return self._qubit_mapper - - @qubit_converter.setter - def qubit_converter(self, conv: QubitConverter | QubitMapper) -> None: - """Sets the qubit operator converter.""" - self.qubit_mapper = conv - - @property - def qubit_mapper(self) -> QubitConverter | QubitMapper | None: + def qubit_mapper(self) -> QubitMapper | None: """The qubit operator mapper.""" return self._qubit_mapper @qubit_mapper.setter - def qubit_mapper(self, mapper: QubitConverter | QubitMapper) -> None: + def qubit_mapper(self, mapper: QubitMapper) -> None: """Sets the qubit operator mapper.""" - if isinstance(mapper, QubitConverter): - warn_deprecated_type( - "0.6.0", - argument_name="mapper", - old_type="QubitConverter", - new_type="QubitMapper", - ) self._operators = None self._invalidate() self._qubit_mapper = mapper @@ -229,16 +197,14 @@ def operators(self): # pylint: disable=invalid-overridden-method excitation_ops = self.excitation_ops() logger.debug("Converting second-quantized into qubit operators...") - # Convert operators according to saved state in converter from the conversion of the + # Convert operators according to saved state in mapper from the conversion of the # main operator since these need to be compatible. If Z2 Symmetry tapering was done # it may be that one or more excitation operators do not commute with the symmetry. - # The converted operators are maintained at the same index by the converter + # The converted operators are maintained at the same index by the mapper # inserting ``None`` as the result if an operator did not commute. To ensure that # the ``excitation_list`` is transformed identically to the operators, we retain # ``None`` for non-commuting operators in order to manually remove them in unison. - if isinstance(self.qubit_mapper, QubitConverter): - operators = self.qubit_mapper.convert_match(excitation_ops, suppress_none=False) - elif isinstance(self.qubit_mapper, TaperedQubitMapper): + if isinstance(self.qubit_mapper, TaperedQubitMapper): operators = self.qubit_mapper.map_clifford(excitation_ops) operators = self.qubit_mapper.taper_clifford(operators, suppress_none=False) else: diff --git a/qiskit_nature/second_q/circuit/library/ansatzes/uvccsd.py b/qiskit_nature/second_q/circuit/library/ansatzes/uvccsd.py index 5e062cffe..bdaa4fbf4 100644 --- a/qiskit_nature/second_q/circuit/library/ansatzes/uvccsd.py +++ b/qiskit_nature/second_q/circuit/library/ansatzes/uvccsd.py @@ -16,8 +16,7 @@ from __future__ import annotations from qiskit.circuit import QuantumCircuit -from qiskit_nature.deprecation import deprecate_arguments -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from .uvcc import UVCC @@ -27,36 +26,23 @@ class UVCCSD(UVCC): This is a convenience subclass of the UVCC ansatz. For more information refer to :class:`UVCC`. """ - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, num_modals: list[int] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, reps: int = 1, initial_state: QuantumCircuit | None = None, - qubit_converter: QubitConverter | QubitMapper | None = None, ) -> None: # pylint: disable=unused-argument """ Args: num_modals: A list defining the number of modals per mode. E.g. for a 3-mode system with 4 modals per mode ``num_modals = [4, 4, 4]``. - qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` or - :class:`~qiskit_nature.second_q.mappers.QubitConverter` instance (use of the latter - is deprecated) which takes care of mapping to a qubit operator. + qubit_mapper: The :class:`~qiskit_nature.second_q.mappers.QubitMapper` which takes care + of mapping to a qubit operator. reps: The number of times to repeat the evolved operators. initial_state: A ``QuantumCircuit`` object to prepend to the circuit. - qubit_converter: DEPRECATED The :class:`~qiskit_nature.second_q.mappers.QubitConverter` - or :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance which takes care of - mapping to a qubit operator. """ super().__init__( num_modals=num_modals, diff --git a/qiskit_nature/second_q/circuit/library/bogoliubov_transform.py b/qiskit_nature/second_q/circuit/library/bogoliubov_transform.py index 35d239243..1077cae97 100644 --- a/qiskit_nature/second_q/circuit/library/bogoliubov_transform.py +++ b/qiskit_nature/second_q/circuit/library/bogoliubov_transform.py @@ -20,9 +20,8 @@ from qiskit import QuantumCircuit, QuantumRegister from qiskit.circuit import Gate, Qubit from qiskit.circuit.library import RZGate, XXPlusYYGate -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from qiskit_nature.second_q.mappers import JordanWignerMapper -from qiskit_nature.deprecation import deprecate_arguments from qiskit_nature.utils import apply_matrix_to_slices, givens_matrix from qiskit_nature.utils.linalg import fermionic_gaussian_decomposition_jw @@ -130,20 +129,11 @@ class BogoliubovTransform(QuantumCircuit): .. _arXiv:1603.08788: https://arxiv.org/abs/1603.08788 """ - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, transformation_matrix: np.ndarray, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, - qubit_converter: QubitConverter | QubitMapper | None = None, validate: bool = True, rtol: float = 1e-5, atol: float = 1e-8, @@ -155,11 +145,8 @@ def __init__( transformation_matrix: The matrix :math:`W` that specifies the coefficients of the new creation operators in terms of the original creation operators. Should be either :math:`N \times N` or :math:`N \times 2N`. - qubit_mapper: The ``QubitMapper`` or ``QubitConverter`` (use of the latter is - deprecated). The default behavior is to create one using the call + qubit_mapper: The ``QubitMapper``. The default behavior is to create one using the call ``JordanWignerMapper()``. - qubit_converter: DEPRECATED The ``QubitConverter`` or ``QubitMapper``. The default - behavior is to create one using the call ``JordanWignerMapper()``. validate: Whether to validate the inputs. rtol: Relative numerical tolerance for input validation. atol: Absolute numerical tolerance for input validation. @@ -187,10 +174,7 @@ def __init__( register = QuantumRegister(n) super().__init__(register, **circuit_kwargs) - if ( - isinstance(qubit_mapper, QubitConverter) - and isinstance(qubit_mapper.mapper, JordanWignerMapper) - ) or (isinstance(qubit_mapper, JordanWignerMapper)): + if isinstance(qubit_mapper, JordanWignerMapper): operations = _bogoliubov_transform_jw(register, transformation_matrix) for gate, qubits in operations: self.append(gate, qubits) diff --git a/qiskit_nature/second_q/circuit/library/initial_states/fermionic_gaussian_state.py b/qiskit_nature/second_q/circuit/library/initial_states/fermionic_gaussian_state.py index 9c19c4b04..d09345e1a 100644 --- a/qiskit_nature/second_q/circuit/library/initial_states/fermionic_gaussian_state.py +++ b/qiskit_nature/second_q/circuit/library/initial_states/fermionic_gaussian_state.py @@ -18,8 +18,7 @@ import numpy as np from qiskit import QuantumCircuit, QuantumRegister -from qiskit_nature.deprecation import deprecate_arguments -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper, JordanWignerMapper +from qiskit_nature.second_q.mappers import QubitMapper, JordanWignerMapper from .utils.givens_rotations import _prepare_fermionic_gaussian_state_jw @@ -114,21 +113,12 @@ class FermionicGaussianState(QuantumCircuit): .. _arXiv:1711.05395: https://arxiv.org/abs/1711.05395 """ - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, transformation_matrix: np.ndarray, occupied_orbitals: Sequence[int] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, - qubit_converter: QubitConverter | QubitMapper | None = None, validate: bool = True, rtol: float = 1e-5, atol: float = 1e-8, @@ -144,11 +134,8 @@ def __init__( of the operators :math:`\{b^\dagger_j\}` from the main body of the docstring of this function. The default behavior is to use the empty set of orbitals, which corresponds to a state with zero pseudo-particles. - qubit_mapper: The ``QubitMapper`` or ``QubitConverter`` (use of the latter is - deprecated). The default behavior is to create one using the call + qubit_mapper: The ``QubitMapper``. The default behavior is to create one using the call ``JordanWignerMapper()``. - qubit_converter: DEPRECATED The ``QubitConverter`` or ``QubitMapper``. The default - behavior is to create one using the call ``JordanWignerMapper()``. validate: Whether to validate the inputs. rtol: Relative numerical tolerance for input validation. atol: Absolute numerical tolerance for input validation. @@ -172,16 +159,13 @@ def __init__( occupied_orbitals = [] if qubit_mapper is None: - qubit_mapper = QubitConverter(JordanWignerMapper()) + qubit_mapper = JordanWignerMapper() n, _ = transformation_matrix.shape register = QuantumRegister(n) super().__init__(register, **circuit_kwargs) - if ( - isinstance(qubit_mapper, QubitConverter) - and isinstance(qubit_mapper.mapper, JordanWignerMapper) - ) or (isinstance(qubit_mapper, JordanWignerMapper)): + if isinstance(qubit_mapper, JordanWignerMapper): operations = _prepare_fermionic_gaussian_state_jw( register, transformation_matrix, occupied_orbitals ) diff --git a/qiskit_nature/second_q/circuit/library/initial_states/hartree_fock.py b/qiskit_nature/second_q/circuit/library/initial_states/hartree_fock.py index 2d6b0c70c..bd29faabc 100644 --- a/qiskit_nature/second_q/circuit/library/initial_states/hartree_fock.py +++ b/qiskit_nature/second_q/circuit/library/initial_states/hartree_fock.py @@ -13,42 +13,28 @@ """Hartree-Fock initial state.""" from __future__ import annotations -import warnings import numpy as np from qiskit import QuantumRegister from qiskit.circuit.library import BlueprintCircuit -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit.utils.validation import validate_min from qiskit_nature.second_q.mappers import ( BravyiKitaevSuperFastMapper, - QubitConverter, QubitMapper, TaperedQubitMapper, ) from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature.deprecation import deprecate_arguments, deprecate_property, warn_deprecated_type class HartreeFock(BlueprintCircuit): """A Hartree-Fock initial state.""" - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, num_spatial_orbitals: int | None = None, num_particles: tuple[int, int] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, ) -> None: # pylint: disable=unused-argument """ @@ -56,11 +42,7 @@ def __init__( num_spatial_orbitals: The number of spatial orbitals. num_particles: The number of particles as a tuple storing the number of alpha and beta-spin electrons in the first and second number, respectively. - qubit_mapper: A :class:`~qiskit_nature.second_q.mappers.QubitMapper` or a - :class:`~qiskit_nature.second_q.mappers.QubitConverter` instance (use of the latter - is deprecated). - qubit_converter: DEPRECATED A :class:`~qiskit_nature.second_q.mappers.QubitConverter` or - a :class:`~qiskit_nature.second_q.mappers.QubitMapper` instance. + qubit_mapper: A :class:`~qiskit_nature.second_q.mappers.QubitMapper`. Raises: NotImplementedError: If ``qubit_mapper`` is (or uses) a @@ -77,31 +59,13 @@ def __init__( self._reset_register() @property - @deprecate_property("0.6.0", new_name="qubit_mapper") - def qubit_converter(self) -> QubitConverter | QubitMapper | None: - """DEPRECATED The qubit converter.""" - return self._qubit_mapper - - @qubit_converter.setter - def qubit_converter(self, conv: QubitConverter | QubitMapper | None) -> None: - """Sets the qubit converter.""" - self.qubit_mapper = conv - - @property - def qubit_mapper(self) -> QubitConverter | QubitMapper | None: + def qubit_mapper(self) -> QubitMapper | None: """The qubit mapper.""" return self._qubit_mapper @qubit_mapper.setter - def qubit_mapper(self, mapper: QubitConverter | QubitMapper | None) -> None: + def qubit_mapper(self, mapper: QubitMapper | None) -> None: """Sets the qubit mapper.""" - if isinstance(mapper, QubitConverter): - warn_deprecated_type( - "0.6.0", - argument_name="mapper", - old_type="QubitConverter", - new_type="QubitMapper", - ) self._invalidate() self._qubit_mapper = mapper self._reset_register() @@ -187,9 +151,7 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: raise ValueError("The qubit mapper cannot be `None`.") return False - if isinstance(self.qubit_mapper, QubitConverter): - mapper = self.qubit_mapper.mapper - elif isinstance(self.qubit_mapper, TaperedQubitMapper): + if isinstance(self.qubit_mapper, TaperedQubitMapper): # we also include the TaperedQubitMapper here, purely for the check done below mapper = self.qubit_mapper.mapper else: @@ -212,14 +174,11 @@ def _reset_register(self): self._bitstr = None if self._check_configuration(raise_on_failure=False): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - self._bitstr = hartree_fock_bitstring_mapped( - self.num_spatial_orbitals, - self.num_particles, - self.qubit_mapper, - match_convert=True, - ) + self._bitstr = hartree_fock_bitstring_mapped( + self.num_spatial_orbitals, + self.num_particles, + self.qubit_mapper, + ) self.qregs = [QuantumRegister(len(self._bitstr), name="q")] def _build(self) -> None: @@ -244,29 +203,10 @@ def _build(self) -> None: self.x(i) -@deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), -) -@deprecate_arguments( - "0.6.0", - {"match_convert": ""}, - additional_msg=( - ". This argument is no longer necessary when working directly with QubitMapper objects " - "outside of a QubitConverter because TaperedQubitMapper instances are now built differently" - ), -) def hartree_fock_bitstring_mapped( num_spatial_orbitals: int, num_particles: tuple[int, int], - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, - match_convert: bool = True, + qubit_mapper: QubitMapper, ) -> list[bool]: # pylint: disable=unused-argument """Compute the bitstring representing the mapped Hartree-Fock state for the specified system. @@ -275,12 +215,7 @@ def hartree_fock_bitstring_mapped( num_spatial_orbitals: The number of spatial orbitals, has a min. value of 1. num_particles: The number of particles as a tuple (alpha, beta) containing the number of alpha- and beta-spin electrons, respectively. - qubit_mapper: A QubitMapper or QubitConverter instance (use of the latter is deprecated). - qubit_converter: DEPRECATED A QubitConverter or QubitMapper instance. - match_convert: Whether to use `convert_match` method of the qubit converter (default), - or just do mapping and possibly two qubit reduction but no tapering. The latter - is an advanced usage - e.g. if we are trying to auto-select the tapering sector - then we would not want any match conversion done on a converter that was set to taper. + qubit_mapper: A QubitMapper. Returns: The bitstring representing the mapped state of the Hartree-Fock state as array of bools. @@ -296,21 +231,13 @@ def hartree_fock_bitstring_mapped( # map the `FermionicOp` to a qubit operator qubit_op: SparsePauliOp - if isinstance(qubit_mapper, QubitConverter): - if match_convert: - qubit_op = qubit_mapper.convert_match(bitstr_op, check_commutes=False) - else: - qubit_op = qubit_mapper.convert_only(bitstr_op, num_particles) - elif isinstance(qubit_mapper, TaperedQubitMapper): + if isinstance(qubit_mapper, TaperedQubitMapper): # To avoid checking commutativity, we call the two methods separately. qubit_op = qubit_mapper.map_clifford(bitstr_op) qubit_op = qubit_mapper.taper_clifford(qubit_op, check_commutes=False) else: qubit_op = qubit_mapper.map(bitstr_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive - # We check the mapped operator `x` part of the paulis because we want to have particles # i.e. True, where the initial state introduced a creation (`+`) operator. bits = [] diff --git a/qiskit_nature/second_q/circuit/library/initial_states/slater_determinant.py b/qiskit_nature/second_q/circuit/library/initial_states/slater_determinant.py index 0e499f582..545867a4d 100644 --- a/qiskit_nature/second_q/circuit/library/initial_states/slater_determinant.py +++ b/qiskit_nature/second_q/circuit/library/initial_states/slater_determinant.py @@ -16,9 +16,8 @@ import numpy as np from qiskit import QuantumCircuit, QuantumRegister -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from qiskit_nature.second_q.mappers import JordanWignerMapper -from qiskit_nature.deprecation import deprecate_arguments from .utils.givens_rotations import _prepare_slater_determinant_jw @@ -72,20 +71,11 @@ class SlaterDeterminant(QuantumCircuit): .. _arXiv:1711.05395: https://arxiv.org/abs/1711.05395 """ - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, transformation_matrix: np.ndarray, - qubit_mapper: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, *, - qubit_converter: QubitConverter | QubitMapper | None = None, validate: bool = True, rtol: float = 1e-5, atol: float = 1e-8, @@ -97,11 +87,8 @@ def __init__( transformation_matrix: The matrix :math:`Q` that specifies the coefficients of the new creation operators in terms of the original creation operators. The rows of the matrix must be orthonormal. - qubit_mapper: The ``QubitMapper`` or ``QubitConverter`` (use of the latter is - deprecated). The default behavior is to create one using the call + qubit_mapper: The ``QubitMapper``. The default behavior is to create one using the call ``JordanWignerMapper()``. - qubit_converter: DEPRECATED The ``QubitConverter`` or ``QubitMapper``. The default - behavior is to create one using the call ``JordanWignerMapper()``. validate: Whether to validate the inputs. rtol: Relative numerical tolerance for input validation. atol: Absolute numerical tolerance for input validation. @@ -123,10 +110,7 @@ def __init__( register = QuantumRegister(n) super().__init__(register, **circuit_kwargs) - if ( - isinstance(qubit_mapper, QubitConverter) - and isinstance(qubit_mapper.mapper, JordanWignerMapper) - ) or (isinstance(qubit_mapper, JordanWignerMapper)): + if isinstance(qubit_mapper, JordanWignerMapper): operations = _prepare_slater_determinant_jw(register, transformation_matrix) for gate, qubits in operations: self.append(gate, qubits) diff --git a/qiskit_nature/second_q/circuit/library/initial_states/vscf.py b/qiskit_nature/second_q/circuit/library/initial_states/vscf.py index 044457dd2..b0c3fd72a 100644 --- a/qiskit_nature/second_q/circuit/library/initial_states/vscf.py +++ b/qiskit_nature/second_q/circuit/library/initial_states/vscf.py @@ -20,12 +20,10 @@ from qiskit import QuantumRegister from qiskit.circuit.library import BlueprintCircuit -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.mappers import DirectMapper -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper, TaperedQubitMapper +from qiskit_nature.second_q.mappers import QubitMapper, TaperedQubitMapper from qiskit_nature.second_q.operators import VibrationalOp -from qiskit_nature.deprecation import deprecate_arguments, deprecate_property, warn_deprecated_type logger = logging.getLogger(__name__) @@ -41,35 +39,20 @@ class VSCF(BlueprintCircuit): [1] Ollitrault Pauline J., Chemical science 11 (2020): 6842-6855. """ - @deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), - ) def __init__( self, num_modals: list[int] | None = None, - qubit_mapper: QubitConverter | QubitMapper | None = None, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper | None = None, ) -> None: # pylint: disable=unused-argument """ Args: num_modals: Is a list defining the number of modals per mode. E.g. for a 3 modes system with 4 modals per mode num_modals = [4,4,4] - qubit_mapper: a QubitMapper or QubitConverter instance (use of the latter is - deprecated). This argument is currently being ignored because only a single use-case - is supported at the time of release: that of the :class:`.DirectMapper`. However, - for future-compatibility of this functions signature, the argument has already been - inserted. - qubit_converter: DEPRECATED a QubitConverter or QubitMapper instance. This argument is - currently being ignored because only a single use-case is supported at the time of - release: that of the :class:`.DirectMapper`. However, for future-compatibility of - this functions signature, the argument has already been inserted. + qubit_mapper: a QubitMapper. This argument is currently being ignored because only a + single use-case is supported at the time of release: that of the + :class:`.DirectMapper`. However, for future-compatibility of this functions + signature, the argument has already been inserted. """ super().__init__() self._num_modals = num_modals @@ -79,45 +62,24 @@ def __init__( self.qubit_mapper = DirectMapper() if qubit_mapper is None else qubit_mapper @property - @deprecate_property("0.6.0", new_name="qubit_mapper") - def qubit_converter(self) -> QubitConverter | QubitMapper | None: - """DEPRECATED The qubit converter.""" - return self._qubit_mapper - - @qubit_converter.setter - def qubit_converter(self, conv: QubitConverter | QubitMapper | None) -> None: - """Sets the qubit converter.""" - self.qubit_mapper = conv - - @property - def qubit_mapper(self) -> QubitConverter | QubitMapper | None: + def qubit_mapper(self) -> QubitMapper | None: """The qubit mapper.""" return self._qubit_mapper @qubit_mapper.setter - def qubit_mapper(self, mapper: QubitConverter | QubitMapper | None) -> None: + def qubit_mapper(self, mapper: QubitMapper | None) -> None: """Sets the qubit mapper.""" - if isinstance(mapper, QubitConverter): - warn_deprecated_type( - "0.6.0", - argument_name="mapper", - old_type="QubitConverter", - new_type="QubitMapper", - ) self._invalidate() - if isinstance(mapper, QubitConverter): - mapper = mapper.mapper - elif isinstance(mapper, TaperedQubitMapper): + if isinstance(mapper, TaperedQubitMapper): # we also include the TaperedQubitMapper here, purely for the check done below mapper = mapper.mapper if not isinstance(mapper, DirectMapper): logger.warning( - "The only supported `QubitConverter` or `QubitMapper` for this application are those " - "based on the `DirectMapper`. " - "However you specified %s as an input, which will be ignored until more " - "variants will be supported.", + "The only supported `QubitMapper` for this application are those based on the " + "`DirectMapper`. However you specified %s as an input, which will be ignored until " + "more variants will be supported.", type(mapper), ) mapper = DirectMapper() @@ -197,29 +159,18 @@ def _build(self) -> None: self.x(i) -@deprecate_arguments( - "0.6.0", - {"qubit_converter": "qubit_mapper"}, - additional_msg=( - ". Additionally, the QubitConverter type in the qubit_mapper argument is deprecated " - "and support for it will be removed together with the qubit_converter argument." - ), -) def vscf_bitstring_mapped( num_modals: list[int], - qubit_mapper: QubitConverter | QubitMapper, - *, - qubit_converter: QubitConverter | QubitMapper | None = None, + qubit_mapper: QubitMapper, ) -> list[bool]: # pylint: disable=unused-argument """Compute the bitstring representing the mapped VSCF initial state - based on the given the number of modals per mode and qubit converter. + based on the given the number of modals per mode and qubit mapper. Args: num_modals: A list defining the number of modals per mode. E.g. for a 3 modes system with 4 modals per mode num_modals = [4,4,4]. - qubit_mapper: A QubitMapper or QubitConverter instance (use of the latter is deprecated). - qubit_converter: DEPRECATED A QubitConverter or QubitMapper instance. + qubit_mapper: A QubitMapper. Returns: The bitstring representing the mapped state of the VSCF initial state as array of bools. @@ -241,18 +192,13 @@ def vscf_bitstring_mapped( ) # map the `VibrationalOp` to a qubit operator qubit_op: SparsePauliOp - if isinstance(qubit_mapper, QubitConverter): - qubit_op = qubit_mapper.convert_match(bitstr_op, check_commutes=False) - elif isinstance(qubit_mapper, TaperedQubitMapper): + if isinstance(qubit_mapper, TaperedQubitMapper): # To avoid checking commutativity, we call the two methods separately. qubit_op = qubit_mapper.map_clifford(bitstr_op) qubit_op = qubit_mapper.taper_clifford(qubit_op, check_commutes=False) else: qubit_op = qubit_mapper.map(bitstr_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive - # We check the mapped operator `x` part of the paulis because we want to have particles # i.e. True, where the initial state introduced a creation (`+`) operator. bits = [] diff --git a/qiskit_nature/second_q/drivers/pyscfd/pyscfdriver.py b/qiskit_nature/second_q/drivers/pyscfd/pyscfdriver.py index 59a51f7b1..7e02e35aa 100644 --- a/qiskit_nature/second_q/drivers/pyscfd/pyscfdriver.py +++ b/qiskit_nature/second_q/drivers/pyscfd/pyscfdriver.py @@ -32,7 +32,6 @@ from qiskit_nature.second_q.formats.qcschema_translator import qcschema_to_problem from qiskit_nature.second_q.operators.symmetric_two_body import fold from qiskit_nature.second_q.problems import ElectronicBasis, ElectronicStructureProblem -from qiskit_nature.settings import settings import qiskit_nature.optionals as _optionals from qiskit_nature.utils import get_einsum @@ -537,49 +536,17 @@ def to_qcschema(self, *, include_dipole: bool = True) -> QCSchema: if data.mo_coeff_b is not None: data.hij_mo_b = np.dot(np.dot(data.mo_coeff_b.T, data.hij), data.mo_coeff_b) - einsum_ao_to_mo = "pqrs,pi,qj,rk,sl->ijkl" - if settings.use_symmetry_reduced_integrals: - data.eri = self._mol.intor("int2e", aosym=8) - data.eri_mo = fold(ao2mo.full(self._mol, data.mo_coeff, aosym=4)) - if data.mo_coeff_b is not None: - data.eri_mo_bb = fold(ao2mo.full(self._mol, data.mo_coeff_b, aosym=4)) - data.eri_mo_ba = fold( - ao2mo.general( - self._mol, - [data.mo_coeff_b, data.mo_coeff_b, data.mo_coeff, data.mo_coeff], - aosym=4, - ) + data.eri = self._mol.intor("int2e", aosym=8) + data.eri_mo = fold(ao2mo.full(self._mol, data.mo_coeff, aosym=4)) + if data.mo_coeff_b is not None: + data.eri_mo_bb = fold(ao2mo.full(self._mol, data.mo_coeff_b, aosym=4)) + data.eri_mo_ba = fold( + ao2mo.general( + self._mol, + [data.mo_coeff_b, data.mo_coeff_b, data.mo_coeff, data.mo_coeff], + aosym=4, ) - else: - data.eri = self._mol.intor("int2e", aosym=1) - data.eri_mo = einsum_func( - einsum_ao_to_mo, - data.eri, - data.mo_coeff, - data.mo_coeff, - data.mo_coeff, - data.mo_coeff, - optimize=settings.optimize_einsum, ) - if data.mo_coeff_b is not None: - data.eri_mo_ba = einsum_func( - einsum_ao_to_mo, - data.eri, - data.mo_coeff_b, - data.mo_coeff_b, - data.mo_coeff, - data.mo_coeff, - optimize=settings.optimize_einsum, - ) - data.eri_mo_bb = einsum_func( - einsum_ao_to_mo, - data.eri, - data.mo_coeff_b, - data.mo_coeff_b, - data.mo_coeff_b, - data.mo_coeff_b, - optimize=settings.optimize_einsum, - ) data.e_nuc = gto.mole.energy_nuc(self._mol) data.e_ref = self._calc.e_tot diff --git a/qiskit_nature/second_q/formats/fcidump/fcidump.py b/qiskit_nature/second_q/formats/fcidump/fcidump.py index 4ec283e2e..d35bcd92f 100644 --- a/qiskit_nature/second_q/formats/fcidump/fcidump.py +++ b/qiskit_nature/second_q/formats/fcidump/fcidump.py @@ -14,7 +14,6 @@ from __future__ import annotations -import warnings from dataclasses import dataclass from pathlib import Path from typing import Sequence @@ -23,11 +22,6 @@ from qiskit_nature import QiskitNatureError from qiskit_nature.second_q.operators.symmetric_two_body import SymmetricTwoBodyIntegrals -from qiskit_nature.second_q.operators.tensor_ordering import ( - IndexType, - find_index_order, - to_chemist_ordering, -) from .dumper import _dump_1e_ints, _dump_2e_ints, _write_to_outfile @@ -50,13 +44,13 @@ class FCIDump: """The number of electrons.""" hij: np.ndarray """The alpha 1-electron integrals.""" - hijkl: np.ndarray | SymmetricTwoBodyIntegrals + hijkl: SymmetricTwoBodyIntegrals """The alpha/alpha 2-electron integrals ordered in chemist' notation.""" hij_b: np.ndarray | None = None """The beta 1-electron integrals.""" - hijkl_bb: np.ndarray | SymmetricTwoBodyIntegrals | None = None + hijkl_bb: SymmetricTwoBodyIntegrals | None = None """The beta/beta 2-electron integrals ordered in chemist' notation.""" - hijkl_ba: np.ndarray | SymmetricTwoBodyIntegrals | None = None + hijkl_ba: SymmetricTwoBodyIntegrals | None = None """The beta/alpha 2-electron integrals ordered in chemist' notation.""" constant_energy: float | None = None """The constant energy comprising (for example) the nuclear repulsion energy and inactive @@ -73,126 +67,6 @@ def num_orbitals(self) -> int: """The number of orbitals.""" return self.hij.shape[0] - @property - def _hijkl(self) -> np.ndarray | SymmetricTwoBodyIntegrals: - if not isinstance(self._chemist_hijkl, SymmetricTwoBodyIntegrals): - warnings.warn( - DeprecationWarning( - "The `np.ndarray` return type of the `FCIDump.hijkl` property is deprecated as " - "of version 0.6.0 and support for it will be removed no sooner than 3 months " - "after the release. Instead, the new return type will be a " - "`qiskit_nature.second_q.operators.symmetric_two_body.SymmetricTwoBodyIntegrals`" - " (which can be used in place of `np.ndarray` objects). You may switch to the " - "new return type immediately by setting " - "`qiskit_nature.settings.use_symmetry_reduced_integrals` to `True`." - ), - stacklevel=3, - ) - return self._chemist_hijkl - - @_hijkl.setter - def _hijkl(self, hijkl: np.ndarray | SymmetricTwoBodyIntegrals) -> None: - self._original_index_order = find_index_order(hijkl) - if self._original_index_order != IndexType.CHEMIST: - warnings.warn( - DeprecationWarning( - "Setting `FCIDump.hijkl` to non-chemistry ordered integrals is deprecated as of " - "version 0.6.0 and support for it will be removed no sooner than 3 months after " - "the release. Instead, ensure that your integrals are already ordered in " - "chemist' notation for example by using the " - "`qiskit_nature.second_q.operators.tensor_ordering.to_chemist_ordering` utility." - ), - stacklevel=3, - ) - if isinstance(hijkl, SymmetricTwoBodyIntegrals): - self._chemist_hijkl = hijkl - else: - self._chemist_hijkl = to_chemist_ordering(hijkl) # type: ignore[assignment] - - @property - def _hijkl_bb(self) -> np.ndarray | SymmetricTwoBodyIntegrals | None: - if self._chemist_hijkl_bb is not None and not isinstance( - self._chemist_hijkl_bb, SymmetricTwoBodyIntegrals - ): - warnings.warn( - DeprecationWarning( - "The `np.ndarray` return type of the `FCIDump.hijkl_bb` property is deprecated as " - "of version 0.6.0 and support for it will be removed no sooner than 3 months " - "after the release. Instead, the new return type will be a " - "`qiskit_nature.second_q.operators.symmetric_two_body.SymmetricTwoBodyIntegrals`" - " (which can be used in place of `np.ndarray` objects). You may switch to the " - "new return type immediately by setting " - "`qiskit_nature.settings.use_symmetry_reduced_integrals` to `True`." - ), - stacklevel=3, - ) - return self._chemist_hijkl_bb - - @_hijkl_bb.setter - def _hijkl_bb(self, hijkl_bb: np.ndarray | SymmetricTwoBodyIntegrals | None) -> None: - if hijkl_bb is None: - self._chemist_hijkl_bb = None - return - if isinstance(hijkl_bb, SymmetricTwoBodyIntegrals): - self._chemist_hijkl_bb = hijkl_bb - else: - if find_index_order(hijkl_bb) != IndexType.CHEMIST: - warnings.warn( - DeprecationWarning( - "Setting `FCIDump.hijkl_bb` to non-chemistry ordered integrals is deprecated " - "as of version 0.6.0 and support for it will be removed no sooner than 3 " - "months after the release. Instead, ensure that your integrals are already " - "ordered in chemist' notation for example by using the " - "`qiskit_nature.second_q.operators.tensor_ordering.to_chemist_ordering` " - "utility." - ), - stacklevel=3, - ) - self._chemist_hijkl_bb = to_chemist_ordering(hijkl_bb) # type: ignore[assignment] - - @property - def _hijkl_ba(self) -> np.ndarray | SymmetricTwoBodyIntegrals | None: - if self._chemist_hijkl_ba is not None and not isinstance( - self._chemist_hijkl_ba, SymmetricTwoBodyIntegrals - ): - warnings.warn( - DeprecationWarning( - "The `np.ndarray` return type of the `FCIDump.hijkl_ba` property is deprecated as " - "of version 0.6.0 and support for it will be removed no sooner than 3 months " - "after the release. Instead, the new return type will be a " - "`qiskit_nature.second_q.operators.symmetric_two_body.SymmetricTwoBodyIntegrals`" - " (which can be used in place of `np.ndarray` objects). You may switch to the " - "new return type immediately by setting " - "`qiskit_nature.settings.use_symmetry_reduced_integrals` to `True`." - ), - stacklevel=3, - ) - return self._chemist_hijkl_ba - - @_hijkl_ba.setter - def _hijkl_ba(self, hijkl_ba: np.ndarray | SymmetricTwoBodyIntegrals | None) -> None: - if hijkl_ba is None: - self._chemist_hijkl_ba = None - return - if isinstance(hijkl_ba, SymmetricTwoBodyIntegrals): - self._chemist_hijkl_ba = hijkl_ba - else: - if self._original_index_order != IndexType.CHEMIST: - warnings.warn( - DeprecationWarning( - "Setting `FCIDump.hijkl_ba` to non-chemistry ordered integrals is deprecated " - "as of version 0.6.0 and support for it will be removed no sooner than 3 " - "months after the release. Instead, ensure that your integrals are already " - "ordered in chemist' notation for example by using the " - "`qiskit_nature.second_q.operators.tensor_ordering.to_chemist_ordering` " - "utility." - ), - stacklevel=3, - ) - self._chemist_hijkl_ba = to_chemist_ordering( # type: ignore[assignment] - hijkl_ba, index_order=self._original_index_order - ) - @classmethod def from_file(cls, fcidump: str | Path) -> FCIDump: """Constructs an FCIDump object from a file. @@ -258,10 +132,3 @@ def to_file(self, fcidump: str | Path) -> None: # append inactive energy if einact is not None: _write_to_outfile(outfile, einact, (0, 0, 0, 0)) - - -# inject the property getter/setter methods for the dataclass attributes -# See also: https://stackoverflow.com/a/61480946 -FCIDump.hijkl = FCIDump._hijkl # type: ignore[assignment] -FCIDump.hijkl_bb = FCIDump._hijkl_bb # type: ignore[assignment] -FCIDump.hijkl_ba = FCIDump._hijkl_ba # type: ignore[assignment] diff --git a/qiskit_nature/second_q/formats/fcidump/parser.py b/qiskit_nature/second_q/formats/fcidump/parser.py index 75e5cb8e7..079b55899 100644 --- a/qiskit_nature/second_q/formats/fcidump/parser.py +++ b/qiskit_nature/second_q/formats/fcidump/parser.py @@ -20,8 +20,7 @@ import numpy as np from qiskit_nature import QiskitNatureError -from qiskit_nature.second_q.operators.symmetric_two_body import unfold, S4Integrals, S8Integrals -from qiskit_nature.settings import settings +from qiskit_nature.second_q.operators.symmetric_two_body import S4Integrals, S8Integrals from .fcidump import FCIDump @@ -174,13 +173,6 @@ def _parse(fcidump: Path) -> FCIDump: if _uhf: hij_b[tril_indices] = hij_b.T[tril_indices] - if not settings.use_symmetry_reduced_integrals: - s8_hijkl = unfold(s8_hijkl).to_dense().array # type: ignore[assignment] - if s8_hijkl_bb is not None: - s8_hijkl_bb = unfold(s8_hijkl_bb).to_dense().array # type: ignore[assignment] - if s4_hijkl_ba is not None: - s4_hijkl_ba = unfold(s4_hijkl_ba).to_dense().array # type: ignore[assignment] - return FCIDump( num_electrons=output.get("NELEC"), hij=hij, diff --git a/qiskit_nature/second_q/formats/qcschema/qc_schema.py b/qiskit_nature/second_q/formats/qcschema/qc_schema.py index 165891a0d..b814db769 100644 --- a/qiskit_nature/second_q/formats/qcschema/qc_schema.py +++ b/qiskit_nature/second_q/formats/qcschema/qc_schema.py @@ -15,7 +15,6 @@ from __future__ import annotations from dataclasses import dataclass -from pathlib import Path from typing import Any, Sequence, cast # Sphinx is somehow unable to resolve this forward reference which gets inherited from QCSchemaInput @@ -24,9 +23,6 @@ import h5py -import numpy as np - -from qiskit_nature.deprecation import deprecate_function from qiskit_nature.version import __version__ from .qc_error import QCError @@ -133,207 +129,3 @@ def _from_hdf5_group(cls, h5py_group: h5py.Group) -> QCSchemaInput: data["keywords"] = dict(h5py_group["keywords"].attrs.items()) return cls(**data) - - @classmethod - @deprecate_function("0.7.0") - def from_legacy_hdf5(cls, legacy_hdf5: str | Path) -> QCSchema: - # pylint: disable=line-too-long - """Constructs a schema object from a legacy HDF5 object. - - .. warning:: - This method will **not** produce a fully valid QCSchema object! - It will contain all the data needed by Qiskit Nature to continue with any computation, - but the file might not be usable as input to other codes that take a QCSchema as an - input, because some of the required data is simply not available in the legacy HDF5 - files. - - In particular, the following attributes are known to be invalid: - - - :attr:`.QCSchemaInput.schema_version` - - :attr:`.QCModel.method` - - :attr:`.QCModel.basis` - - :attr:`.QCWavefunction.basis` - - Args: - legacy_hdf5: the path to the legacy HDF5 file. - - Raises: - ValueError: if the provided legacy HDF5 file contains anything other than a - :class:`~qiskit_nature.properties.second_quantization.electronic.ElectronicStructureDriverResult` - - Returns: - An instance of the schema object. - """ - # values which are already provided here, ensure that all required init arguments exist, - # once it comes to the construction of the QCSchema object - topology_data: dict[str, Any] = { - "symbols": [], - "geometry": [], - } - properties_data: dict[str, Any] = {} - provenance_data: dict[str, Any] = { - "creator": "Qiskit Nature", - "version": __version__, - "routine": f"{cls.__module__}.{cls.__name__}.from_legacy_hdf5", - } - wavefunction_data: dict[str, Any] = {} - return_result: float | None = None - - root_name = "ElectronicStructureDriverResult" - - with h5py.File(legacy_hdf5, "r") as file: - if root_name not in file.keys(): - raise ValueError( - "A QCSchema can only be constructed from a legacy " - f"ElectronicStructureDriverResult, not from a {set(file.keys()).pop()}.\n" - "You may want to look for other formats available in " - "qiskit_nature.second_q.formats to see if one of those matches your desired " - "object type." - ) - - driver_metadata = file[root_name].get("DriverMetadata", None) - if driver_metadata is not None: - provenance_data["creator"] = driver_metadata.attrs["program"] - provenance_data["version"] = driver_metadata.attrs["version"] - provenance_data["routine"] = driver_metadata.attrs["config"] - - molecule = file[root_name].get("Molecule", None) - if molecule is not None: - symbols: list[str] = [] - geometry: list[float] = [] - - for atom in molecule["geometry"].values(): - symbols.append(atom.attrs["symbol"]) - geometry.extend(atom[...].tolist()) - - properties_data["calcinfo_natom"] = len(symbols) - topology_data["symbols"] = symbols - topology_data["geometry"] = geometry - - if "multiplicity" in molecule.attrs.keys(): - topology_data["molecular_multiplicity"] = int(molecule.attrs["multiplicity"]) - if "charge" in molecule.attrs.keys(): - topology_data["molecular_charge"] = int(molecule.attrs["charge"]) - if "masses" in molecule.keys(): - topology_data["masses"] = list(molecule["masses"]) - - basis_transform = file[root_name].get("ElectronicBasisTransform", None) - if basis_transform is not None: - if ( - basis_transform.attrs["initial_basis"] == "AO" - and basis_transform.attrs["final_basis"] == "MO" - ): - orbitals_a = np.asarray(basis_transform["Alpha coefficients"][...]) - nao, nmo = orbitals_a.shape - properties_data["calcinfo_nbasis"] = nao - properties_data["calcinfo_nmo"] = nmo - - wavefunction_data["orbitals_a"] = "scf_orbitals_a" - wavefunction_data["scf_orbitals_a"] = orbitals_a.ravel().tolist() - if "Beta coefficients" in basis_transform.keys(): - wavefunction_data["orbitals_b"] = "scf_orbitals_b" - wavefunction_data["scf_orbitals_b"] = ( - np.asarray(basis_transform["Beta coefficients"][...]).ravel().tolist() - ) - - particle_number = file[root_name].get("ParticleNumber", None) - if particle_number is not None: - if properties_data.get("calcinfo_nmo", None) is None: - # we might have gotten this number from the basis transform already - properties_data["calcinfo_nmo"] = ( - int(particle_number.attrs["num_spin_orbitals"]) // 2 - ) - properties_data["calcinfo_nalpha"] = int(particle_number.attrs["num_alpha"]) - properties_data["calcinfo_nbeta"] = int(particle_number.attrs["num_beta"]) - wavefunction_data["occupations_a"] = "scf_occupations_a" - wavefunction_data["occupations_b"] = "scf_occupations_b" - wavefunction_data["scf_occupations_a"] = list( - particle_number["occupation_alpha"][...] - ) - wavefunction_data["scf_occupations_b"] = list( - particle_number["occupation_beta"][...] - ) - - electronic_energy = file[root_name].get("ElectronicEnergy", None) - if electronic_energy is not None: - if "reference_energy" in electronic_energy.attrs.keys(): - return_result = float(electronic_energy.attrs["reference_energy"]) - properties_data["return_energy"] = return_result - if "nuclear_repulsion_energy" in electronic_energy.attrs.keys(): - properties_data["nuclear_repulsion_energy"] = float( - electronic_energy.attrs["nuclear_repulsion_energy"] - ) - - orbital_energies = electronic_energy.attrs.get("orbital_energies", None) - if orbital_energies is not None: - orbital_energies = np.asarray(orbital_energies) - - wavefunction_data["eigenvalues_a"] = "scf_eigenvalues_a" - - if len(orbital_energies.shape) == 2: - wavefunction_data["eigenvalues_b"] = "scf_eigenvalues_b" - wavefunction_data["scf_eigenvalues_a"] = ( - orbital_energies[0].ravel().tolist() - ) - wavefunction_data["scf_eigenvalues_b"] = ( - orbital_energies[1].ravel().tolist() - ) - else: - wavefunction_data["scf_eigenvalues_a"] = orbital_energies.ravel().tolist() - - electronic_integrals = electronic_energy["electronic_integrals"] - - def _extract_electronic_integral( - basis: str, - nbody: str, - spin: str, - qcschema_key: str, - ): - basis_integrals = electronic_integrals.get(basis, None) - if basis_integrals is None: - return - nbody_integrals = basis_integrals.get(f"{nbody}BodyElectronicIntegrals", None) - if nbody_integrals is None: - return - spin_integrals = nbody_integrals.get(spin, None) - if spin_integrals is None: - return - wavefunction_data[qcschema_key] = f"scf_{qcschema_key}" - wavefunction_data[f"scf_{qcschema_key}"] = ( - np.asarray(spin_integrals).ravel().tolist() - ) - - _extract_electronic_integral("AO", "One", "Alpha", "fock_a") - _extract_electronic_integral("AO", "One", "Beta", "fock_b") - _extract_electronic_integral("AO", "Two", "Alpha-Alpha", "eri") - - _extract_electronic_integral("MO", "One", "Alpha", "fock_mo_a") - _extract_electronic_integral("MO", "One", "Beta", "fock_mo_b") - _extract_electronic_integral("MO", "Two", "Alpha-Alpha", "eri_mo_aa") - _extract_electronic_integral("MO", "Two", "Beta-Alpha", "eri_mo_ba") - _extract_electronic_integral("MO", "Two", "Beta-Beta", "eri_mo_bb") - _extract_electronic_integral("MO", "Two", "Alpha-Beta", "eri_mo_ab") - - return cls( - schema_name="qcschema", - schema_version=-1, # this is meant to indicate, that this is not a fully valid file - molecule=QCTopology( - schema_name="qcschema_molecule", - schema_version=2, - **topology_data, - ), - driver="energy", - model=QCModel( - method="?", - basis="?", - ), - keywords={}, - provenance=QCProvenance(**provenance_data), - return_result=return_result, - success=True, - properties=QCProperties(**properties_data), - wavefunction=QCWavefunction( - basis="?", # this is technically invalid, but the data simply does not exist - **wavefunction_data, - ), - ) diff --git a/qiskit_nature/second_q/formats/qcschema_translator.py b/qiskit_nature/second_q/formats/qcschema_translator.py index 1ff5dc325..e5312c3ef 100644 --- a/qiskit_nature/second_q/formats/qcschema_translator.py +++ b/qiskit_nature/second_q/formats/qcschema_translator.py @@ -34,7 +34,6 @@ ParticleNumber, ) from qiskit_nature.second_q.transformers import BasisTransformer -from qiskit_nature.settings import settings from .molecule_info import MoleculeInfo from .qcschema import QCSchema @@ -144,8 +143,6 @@ def _reshape_4(arr, dim): return S4Integrals(np.asarray(arr).reshape((npair,) * 2)) if len(arr) == dim**4: - if not settings.use_symmetry_reduced_integrals: - return np.asarray(arr).reshape((dim,) * 4) return S1Integrals(np.asarray(arr).reshape((dim,) * 4)) return arr diff --git a/qiskit_nature/second_q/hamiltonians/electronic_energy.py b/qiskit_nature/second_q/hamiltonians/electronic_energy.py index 90b371238..7d92ded7d 100644 --- a/qiskit_nature/second_q/hamiltonians/electronic_energy.py +++ b/qiskit_nature/second_q/hamiltonians/electronic_energy.py @@ -20,12 +20,7 @@ import numpy as np import qiskit_nature # pylint: disable=unused-import -from qiskit_nature.second_q.operators import ( - ElectronicIntegrals, - FermionicOp, - PolynomialTensor, - Tensor, -) +from qiskit_nature.second_q.operators import ElectronicIntegrals, FermionicOp, PolynomialTensor from .hamiltonian import Hamiltonian @@ -229,9 +224,6 @@ def coulomb(self, density: ElectronicIntegrals) -> ElectronicIntegrals: :attr:`.ElectronicEnergy.electronic_integrals`. """ two_body_aa = self.electronic_integrals.alpha.get("++--", None) - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - if not isinstance(two_body_aa, Tensor): - two_body_aa = Tensor(two_body_aa) einsum = f"{''.join(two_body_aa._reverse_label_template('pqrs'))},ps->qr" coulomb = ElectronicIntegrals.einsum( @@ -272,9 +264,6 @@ def exchange(self, density: ElectronicIntegrals) -> ElectronicIntegrals: :attr:`.ElectronicEnergy.electronic_integrals`. """ two_body_aa = self.electronic_integrals.alpha.get("++--", None) - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - if not isinstance(two_body_aa, Tensor): - two_body_aa = Tensor(two_body_aa) einsum = f"{''.join(two_body_aa._reverse_label_template('pqrs'))},qs->pr" exchange = ElectronicIntegrals.einsum( diff --git a/qiskit_nature/second_q/mappers/__init__.py b/qiskit_nature/second_q/mappers/__init__.py index 27b1aefe0..9d98b9515 100644 --- a/qiskit_nature/second_q/mappers/__init__.py +++ b/qiskit_nature/second_q/mappers/__init__.py @@ -90,15 +90,6 @@ TaperedQubitMapper -Qubit Converter -+++++++++++++++ - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - QubitConverter - """ from .bksf import BravyiKitaevSuperFastMapper @@ -110,7 +101,6 @@ from .logarithmic_mapper import LogarithmicMapper from .direct_mapper import DirectMapper from .qubit_mapper import QubitMapper -from .qubit_converter import QubitConverter from .interleaved_qubit_mapper import InterleavedQubitMapper from .tapered_qubit_mapper import TaperedQubitMapper @@ -123,7 +113,6 @@ "LinearMapper", "BosonicLinearMapper", "LogarithmicMapper", - "QubitConverter", "QubitMapper", "InterleavedQubitMapper", "TaperedQubitMapper", diff --git a/qiskit_nature/second_q/mappers/bksf.py b/qiskit_nature/second_q/mappers/bksf.py index 279ca5da9..943d09f44 100644 --- a/qiskit_nature/second_q/mappers/bksf.py +++ b/qiskit_nature/second_q/mappers/bksf.py @@ -17,11 +17,9 @@ from enum import Enum import numpy as np -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit.quantum_info.operators import Pauli -from qiskit_nature import settings from qiskit_nature.second_q.operators import FermionicOp from .fermionic_mapper import FermionicMapper @@ -39,7 +37,7 @@ class BravyiKitaevSuperFastMapper(FermionicMapper): def _map_single( self, second_q_op: FermionicOp, *, register_length: int | None = None - ) -> SparsePauliOp | PauliSumOp: + ) -> SparsePauliOp: if register_length is None: register_length = second_q_op.register_length @@ -56,7 +54,7 @@ def _map_single( coeffs = sparse_pauli.coeffs[indices] sorted_sparse_pauli = SparsePauliOp(table, coeffs) - return PauliSumOp(sorted_sparse_pauli) if settings.use_pauli_sum_op else sorted_sparse_pauli + return sorted_sparse_pauli class TermType(Enum): diff --git a/qiskit_nature/second_q/mappers/bosonic_linear_mapper.py b/qiskit_nature/second_q/mappers/bosonic_linear_mapper.py index 60148e3f8..e366968c7 100644 --- a/qiskit_nature/second_q/mappers/bosonic_linear_mapper.py +++ b/qiskit_nature/second_q/mappers/bosonic_linear_mapper.py @@ -20,7 +20,6 @@ import numpy as np from qiskit.quantum_info import Pauli, SparsePauliOp -from qiskit_nature import settings from qiskit_nature.second_q.operators import BosonicOp from .bosonic_mapper import BosonicMapper @@ -96,17 +95,7 @@ def _map_single( Returns: The qubit operator corresponding to the problem-Hamiltonian in the qubit space. - - Raises: - NotImplementedError: when `qiskit_nature.settings.use_pauli_sum_op` is set to `True`. - This value is deprecated and, thus, not supported by this new implementation. - Set it to `False` in order to use this mapper. """ - if settings.use_pauli_sum_op: - raise NotImplementedError( - "This class does not support the deprecated `settings.use_pauli_sum_op` value." - "Please set `settings.use_pauli_sum_op = False` in order to use this mapper." - ) if register_length is None: register_length = second_q_op.num_modes diff --git a/qiskit_nature/second_q/mappers/bravyi_kitaev_mapper.py b/qiskit_nature/second_q/mappers/bravyi_kitaev_mapper.py index 6564cec6e..957f579f4 100644 --- a/qiskit_nature/second_q/mappers/bravyi_kitaev_mapper.py +++ b/qiskit_nature/second_q/mappers/bravyi_kitaev_mapper.py @@ -20,7 +20,6 @@ from qiskit.quantum_info.operators import Pauli -from qiskit_nature.deprecation import deprecate_arguments from .fermionic_mapper import FermionicMapper @@ -28,11 +27,8 @@ class BravyiKitaevMapper(FermionicMapper): """The Bravyi-Kitaev fermion-to-qubit mapping.""" @classmethod - @deprecate_arguments("0.6.0", {"nmodes": "register_length"}) @lru_cache(maxsize=32) - def pauli_table( - cls, register_length: int, *, nmodes: int | None = None - ) -> list[tuple[Pauli, Pauli]]: + def pauli_table(cls, register_length: int) -> list[tuple[Pauli, Pauli]]: # pylint: disable=unused-argument def parity_set(j, n): """ diff --git a/qiskit_nature/second_q/mappers/direct_mapper.py b/qiskit_nature/second_q/mappers/direct_mapper.py index 22caeb9cf..1902ea135 100644 --- a/qiskit_nature/second_q/mappers/direct_mapper.py +++ b/qiskit_nature/second_q/mappers/direct_mapper.py @@ -20,7 +20,6 @@ from qiskit.quantum_info.operators import Pauli -from qiskit_nature.deprecation import deprecate_arguments from .vibrational_mapper import VibrationalMapper @@ -32,11 +31,8 @@ class DirectMapper(VibrationalMapper): """ @classmethod - @deprecate_arguments("0.6.0", {"nmodes": "register_length"}) @lru_cache(maxsize=32) - def pauli_table( - cls, register_length: int, *, nmodes: int | None = None - ) -> list[tuple[Pauli, Pauli]]: + def pauli_table(cls, register_length: int) -> list[tuple[Pauli, Pauli]]: # pylint: disable=unused-argument pauli_table = [] diff --git a/qiskit_nature/second_q/mappers/fermionic_mapper.py b/qiskit_nature/second_q/mappers/fermionic_mapper.py index 8b4e41fee..7e0f8df30 100644 --- a/qiskit_nature/second_q/mappers/fermionic_mapper.py +++ b/qiskit_nature/second_q/mappers/fermionic_mapper.py @@ -14,7 +14,6 @@ from __future__ import annotations -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.operators import FermionicOp @@ -30,5 +29,5 @@ def map( second_q_ops: FermionicOp | ListOrDictType[FermionicOp], *, register_length: int | None = None, - ) -> SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp]: + ) -> SparsePauliOp | ListOrDictType[SparsePauliOp]: return super().map(second_q_ops, register_length=register_length) diff --git a/qiskit_nature/second_q/mappers/interleaved_qubit_mapper.py b/qiskit_nature/second_q/mappers/interleaved_qubit_mapper.py index 5ede19346..2a4dae044 100644 --- a/qiskit_nature/second_q/mappers/interleaved_qubit_mapper.py +++ b/qiskit_nature/second_q/mappers/interleaved_qubit_mapper.py @@ -14,10 +14,8 @@ from __future__ import annotations -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp -from qiskit_nature import settings from qiskit_nature.second_q.operators import FermionicOp from .fermionic_mapper import FermionicMapper @@ -104,7 +102,7 @@ def __init__(self, mapper: FermionicMapper): def _map_single( self, second_q_op: FermionicOp, *, register_length: int | None = None - ) -> SparsePauliOp | PauliSumOp: + ) -> SparsePauliOp: if register_length is None: register_length = second_q_op.register_length @@ -115,7 +113,5 @@ def _map_single( interleaved_op = self.mapper._map_single( interleaved_sec_op, register_length=register_length ) - if isinstance(interleaved_op, PauliSumOp): - interleaved_op = interleaved_op.primitive - return PauliSumOp(interleaved_op) if settings.use_pauli_sum_op else interleaved_op + return interleaved_op diff --git a/qiskit_nature/second_q/mappers/jordan_wigner_mapper.py b/qiskit_nature/second_q/mappers/jordan_wigner_mapper.py index f7e861f32..569d35f58 100644 --- a/qiskit_nature/second_q/mappers/jordan_wigner_mapper.py +++ b/qiskit_nature/second_q/mappers/jordan_wigner_mapper.py @@ -20,7 +20,6 @@ from qiskit.quantum_info.operators import Pauli -from qiskit_nature.deprecation import deprecate_arguments from .fermionic_mapper import FermionicMapper @@ -28,11 +27,8 @@ class JordanWignerMapper(FermionicMapper): """The Jordan-Wigner fermion-to-qubit mapping.""" @classmethod - @deprecate_arguments("0.6.0", {"nmodes": "register_length"}) @lru_cache(maxsize=32) - def pauli_table( - cls, register_length: int, *, nmodes: int | None = None - ) -> list[tuple[Pauli, Pauli]]: + def pauli_table(cls, register_length: int) -> list[tuple[Pauli, Pauli]]: # pylint: disable=unused-argument pauli_table = [] diff --git a/qiskit_nature/second_q/mappers/linear_mapper.py b/qiskit_nature/second_q/mappers/linear_mapper.py index 1e28e2244..c4466db0d 100644 --- a/qiskit_nature/second_q/mappers/linear_mapper.py +++ b/qiskit_nature/second_q/mappers/linear_mapper.py @@ -21,10 +21,8 @@ import numpy as np -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import Pauli, SparsePauliOp -from qiskit_nature import settings from qiskit_nature.second_q.operators import SpinOp from .spin_mapper import SpinMapper @@ -34,11 +32,11 @@ class LinearMapper(SpinMapper): def _map_single( self, second_q_op: SpinOp, *, register_length: int | None = None - ) -> SparsePauliOp | PauliSumOp: + ) -> SparsePauliOp: if register_length is None: register_length = second_q_op.register_length - qubit_ops_list: list[PauliSumOp] = [] + qubit_ops_list: list[SparsePauliOp] = [] # get linear encoding of the general spin matrices spinx, spiny, spinz, identity = self._linear_encoding(second_q_op.spin) @@ -58,9 +56,9 @@ def _map_single( qubit_ops_list.append(coeff * reduce(operator.xor, reversed(operatorlist))) qubit_op = reduce(operator.add, qubit_ops_list) - return qubit_op if settings.use_pauli_sum_op else qubit_op.primitive.simplify() + return qubit_op.simplify() - def _linear_encoding(self, spin: Fraction | float) -> list[PauliSumOp]: + def _linear_encoding(self, spin: Fraction | float) -> list[SparsePauliOp]: """ Generates a 'linear_encoding' of the spin S operators 'X', 'Y', 'Z' and 'identity' to qubit operators (linear combinations of pauli strings). @@ -93,20 +91,16 @@ def pauli_z(i): x_summands = [] for i, coeff in enumerate(np.diag(SpinOp.x(spin).to_matrix(), 1)): x_summands.append( - PauliSumOp( - coeff / 2.0 * SparsePauliOp(pauli_x(i).dot(pauli_x(i + 1))) - + coeff / 2.0 * SparsePauliOp(pauli_y(i).dot(pauli_y(i + 1))) - ) + coeff / 2.0 * SparsePauliOp(pauli_x(i).dot(pauli_x(i + 1))) + + coeff / 2.0 * SparsePauliOp(pauli_y(i).dot(pauli_y(i + 1))) ) # 2. build the non-diagonal Y operator y_summands = [] for i, coeff in enumerate(np.diag(SpinOp.y(spin).to_matrix(), 1)): y_summands.append( - PauliSumOp( - -1j * coeff / 2.0 * SparsePauliOp(pauli_x(i).dot(pauli_y(i + 1))) - + 1j * coeff / 2.0 * SparsePauliOp(pauli_y(i).dot(pauli_x(i + 1))) - ) + -1j * coeff / 2.0 * SparsePauliOp(pauli_x(i).dot(pauli_y(i + 1))) + + 1j * coeff / 2.0 * SparsePauliOp(pauli_y(i).dot(pauli_x(i + 1))) ) # 3. build the diagonal Z @@ -114,9 +108,7 @@ def pauli_z(i): for i, coeff in enumerate(np.diag(SpinOp.z(spin).to_matrix())): # get the first upper diagonal of coeff. z_summands.append( - PauliSumOp( - coeff / 2.0 * SparsePauliOp(pauli_z(i)) + coeff / 2.0 * SparsePauliOp(pauli_id) - ) + coeff / 2.0 * SparsePauliOp(pauli_z(i)) + coeff / 2.0 * SparsePauliOp(pauli_id) ) # return the lookup table for the transformed XYZI operators @@ -124,6 +116,6 @@ def pauli_z(i): reduce(operator.add, x_summands), reduce(operator.add, y_summands), reduce(operator.add, z_summands), - PauliSumOp(1.0 * SparsePauliOp(pauli_id)), + SparsePauliOp(pauli_id), ] return spin_op_encoding diff --git a/qiskit_nature/second_q/mappers/logarithmic_mapper.py b/qiskit_nature/second_q/mappers/logarithmic_mapper.py index 44929420b..78c33ee68 100644 --- a/qiskit_nature/second_q/mappers/logarithmic_mapper.py +++ b/qiskit_nature/second_q/mappers/logarithmic_mapper.py @@ -21,11 +21,9 @@ import numpy as np -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit.quantum_info.operators import Operator -from qiskit_nature import settings from qiskit_nature.second_q.operators import SpinOp from .spin_mapper import SpinMapper @@ -75,7 +73,7 @@ def __init__(self, *, padding: float = 1, embed_upper: bool = True) -> None: def _map_single( self, second_q_op: SpinOp, *, register_length: int | None = None - ) -> SparsePauliOp | PauliSumOp: + ) -> SparsePauliOp: """Map spins to qubits using the Logarithmic encoding. Args: @@ -87,7 +85,7 @@ def _map_single( if register_length is None: register_length = second_q_op.register_length - qubit_ops_list: list[PauliSumOp] = [] + qubit_ops_list: list[SparsePauliOp] = [] # get logarithmic encoding of the general spin matrices. spinx, spiny, spinz, identity = self._logarithmic_encoding(second_q_op.spin) @@ -108,20 +106,20 @@ def _map_single( qubit_op = reduce(operator.add, qubit_ops_list) - return qubit_op if settings.use_pauli_sum_op else qubit_op.primitive + return qubit_op def _logarithmic_encoding( self, spin: Fraction | int - ) -> tuple[PauliSumOp, PauliSumOp, PauliSumOp, PauliSumOp]: + ) -> tuple[SparsePauliOp, SparsePauliOp, SparsePauliOp, SparsePauliOp]: """The logarithmic encoding. Args: spin: Positive half-integer (integer or half-odd-integer) that represents spin. Returns: - A tuple containing four PauliSumOp. + A tuple containing four SparsePauliOp. """ - spin_op_encoding: list[PauliSumOp] = [] + spin_op_encoding: list[SparsePauliOp] = [] dspin = int(2 * spin + 1) num_qubits = int(np.ceil(np.log2(dspin))) @@ -143,7 +141,7 @@ def _logarithmic_encoding( for op in embedded_operators: op = SparsePauliOp.from_operator(op) op.chop() - spin_op_encoding.append(PauliSumOp(1.0 * op)) + spin_op_encoding.append(op) return tuple(spin_op_encoding) # type: ignore diff --git a/qiskit_nature/second_q/mappers/parity_mapper.py b/qiskit_nature/second_q/mappers/parity_mapper.py index 4c1906899..78d7912ba 100644 --- a/qiskit_nature/second_q/mappers/parity_mapper.py +++ b/qiskit_nature/second_q/mappers/parity_mapper.py @@ -20,12 +20,9 @@ import numpy as np -from qiskit.opflow import PauliSumOp from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries from qiskit.quantum_info.operators import Pauli, PauliList, SparsePauliOp -from qiskit_nature import settings -from qiskit_nature.deprecation import deprecate_arguments, deprecate_property from qiskit_nature.second_q.operators import FermionicOp from .fermionic_mapper import FermionicMapper @@ -58,11 +55,6 @@ def __init__(self, num_particles: tuple[int, int] | None = None): self._tapering_values: list | None = None self.num_particles = num_particles - @property - @deprecate_property("0.6.0") - def allows_two_qubit_reduction(self) -> bool: - return True - @property def num_particles(self) -> tuple[int, int] | None: """Get number of particles.""" @@ -81,11 +73,8 @@ def num_particles(self, value: tuple[int, int] | None) -> None: self._tapering_values = [par_2, par_1] @classmethod - @deprecate_arguments("0.6.0", {"nmodes": "register_length"}) @lru_cache(maxsize=32) - def pauli_table( - cls, register_length: int, *, nmodes: int | None = None - ) -> list[tuple[Pauli, Pauli]]: + def pauli_table(cls, register_length: int) -> list[tuple[Pauli, Pauli]]: # pylint: disable=unused-argument pauli_table = [] @@ -139,12 +128,9 @@ def _two_qubit_reduce(self, operator: SparsePauliOp) -> SparsePauliOp: def _map_single( self, second_q_op: FermionicOp, *, register_length: int | None = None - ) -> SparsePauliOp | PauliSumOp: + ) -> SparsePauliOp: mapped_op = ParityMapper.mode_based_mapping(second_q_op, register_length=register_length) - if isinstance(mapped_op, PauliSumOp): - mapped_op = mapped_op.primitive - reduced_op = mapped_op if self.num_particles is not None: if mapped_op.num_qubits > 2: @@ -156,4 +142,4 @@ def _map_single( mapped_op.num_qubits, ) - return PauliSumOp(reduced_op) if settings.use_pauli_sum_op else reduced_op + return reduced_op diff --git a/qiskit_nature/second_q/mappers/qubit_converter.py b/qiskit_nature/second_q/mappers/qubit_converter.py deleted file mode 100644 index bdfefdd81..000000000 --- a/qiskit_nature/second_q/mappers/qubit_converter.py +++ /dev/null @@ -1,582 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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 converter from Second-Quantized to Qubit Operators.""" - -from __future__ import annotations - -import copy -import logging -from typing import Callable - -import numpy as np - -from qiskit.algorithms.list_or_dict import ListOrDict as ListOrDictType -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info import SparsePauliOp -from qiskit.opflow.primitive_ops import Z2Symmetries - -from qiskit_nature import QiskitNatureError, settings -from qiskit_nature.deprecation import warn_deprecated, DeprecatedType -from qiskit_nature.second_q.operators import SparseLabelOp - -from .qubit_mapper import QubitMapper, _ListOrDict -from .parity_mapper import ParityMapper -from .tapered_qubit_mapper import TaperedQubitMapper - -logger = logging.getLogger(__name__) - - -class QubitConverter: - """DEPRECATED A converter from Second-Quantized to Qubit Operators. - - This converter can be configured with a mapper instance which will later be used - when 2nd quantized operators are requested to be converted (mapped) to qubit operators. - - For a Fermionic system, when its a electronic problem, there are certain mappers, such as - the :class:`~qiskit_nature.second_q.mappers.ParityMapper` that introduces known - symmetries, by virtue of the mapping, that can be exploited to reduce the size of the - problem, i.e the qubit operator, by two qubits. The two qubit reduction setting indicates - to do this where possible - i.e. mapper supports it and the number of particles in the - Fermionic system is provided for the conversion. (The number of particles is used to - determine the symmetry.) - - Also this converter supports Z2 Symmetry reduction to reduce a problem (operator) size - based on mathematical symmetries that can be detected in the operator. A knowledgeable user - can define which sector the problem solution lies in. This sector information can also - be passed in on :meth:`convert` which will override this value should both be given. - """ - - def __init__( - self, - mapper: QubitMapper, - *, - two_qubit_reduction: bool = False, - z2symmetry_reduction: str | list[int] | None = None, - sort_operators: bool = False, - ): - """ - - Args: - mapper: A mapper instance used to convert second quantized to qubit operators - two_qubit_reduction: Whether to carry out two qubit reduction when possible - z2symmetry_reduction: Indicates whether a z2 symmetry reduction should be applied to - resulting qubit operators that are computed. For each symmetry detected the operator - will be split into two where each requires one qubit less for computation. So for - example 3 symmetries will split 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 the sector is known - from other experiments with the z2symmetry logic, 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. - sort_operators: Whether or not the second-quantized operators should be sorted before - mapping them to the qubit space. Enable this if you encounter non-reproducible - results which can occur when operator terms are not consistently ordered. - This is disabled by default, because in practice the Pauli-terms will be grouped - later on anyways. - - Raises: - ValueError: If the mapper is a ``TaperedQubitMapper``. - """ - warn_deprecated( - "0.6.0", - DeprecatedType.CLASS, - "QubitConverter", - additional_msg=( - ". Instead you should directly use the QubitMapper instance which you used to pass " - "into the QubitConverter as the first argument. Refer to the documentation of the " - "qiskit_nature.second_q.mappers module for more information" - ), - ) - if isinstance(mapper, TaperedQubitMapper): - raise ValueError( - "The TaperedQubitMapper is not supported by the QubitConverter. " - "If you want to use tapering please either use the tapering built " - "directly into the QubitConverter (see its documentation) " - "or use the TaperedQubitMapper standalone (recommended)." - ) - self._mapper: QubitMapper = mapper - - self._two_qubit_reduction: bool = two_qubit_reduction - self._z2symmetry_reduction: str | list[int] | None = None - # We use the setter for the additional validation - self.z2symmetry_reduction = z2symmetry_reduction - self._z2symmetries: Z2Symmetries = self._no_symmetries - - self._sort_operators: bool = sort_operators - - @property - def _no_symmetries(self) -> Z2Symmetries: - return Z2Symmetries([], [], [], None) - - @property - def mapper(self) -> QubitMapper: - """Get mapper""" - return self._mapper - - @mapper.setter - def mapper(self, value: QubitMapper) -> None: - """Set mapper""" - self._mapper = value - self._z2symmetries = None # Reset as symmetries my change due to mapper change - - @property - def two_qubit_reduction(self) -> bool: - """Get two_qubit_reduction""" - return self._two_qubit_reduction - - @two_qubit_reduction.setter - def two_qubit_reduction(self, value: bool) -> None: - """Set two_qubit_reduction""" - self._two_qubit_reduction = value - self._z2symmetries = None # Reset as symmetries my change due to this reduction - - @property - def z2symmetry_reduction(self) -> str | list[int] | None: - """Get z2symmetry_reduction""" - return self._z2symmetry_reduction - - @z2symmetry_reduction.setter - def z2symmetry_reduction(self, z2symmetry_reduction: str | list[int] | None) -> None: - """Set z2symmetry_reduction""" - if z2symmetry_reduction is not None: - if isinstance(z2symmetry_reduction, str): - if z2symmetry_reduction != "auto": - raise ValueError( - "The only string-like option for z2symmetry_reduction is " - f"'auto', not {z2symmetry_reduction}" - ) - elif not np.all(np.isin(z2symmetry_reduction, [-1, 1])): - raise ValueError( - "z2symmetry_reduction tapering values list must " - f"contain -1's and/or 1's only but was {z2symmetry_reduction}" - ) - - self._z2symmetry_reduction = z2symmetry_reduction - - @property - def num_particles(self) -> tuple[int, int] | None: - """Get the number of particles as supplied to :meth:`convert`. - - This can also be set, for advanced usage, using :meth:`force_match` - """ - if isinstance(self._mapper, ParityMapper): - return self._mapper.num_particles - else: - return None - - @property - def z2symmetries(self) -> Z2Symmetries: - """Get Z2Symmetries as detected from conversion via :meth:`convert`. - - This may indicate no symmetries, i.e. be empty, if none were detected. - - This can also be set, for advanced usage, using :meth:`force_match` - """ - return copy.deepcopy(self._z2symmetries) - - def _check_reset_mapper(self) -> None: - """Resets the ``ParityMapper`` if the attribute :attr:`two_qubit_reduction` is set to False. This - makes the behavior of the QubitConverter compatible with the new ParityMapper class which only - has one attribute :attr:`num_particles`. This must be called right before any mapping method of - the mappers. - """ - if not self.two_qubit_reduction and isinstance(self._mapper, ParityMapper): - self._mapper.num_particles = None - - def convert( - self, - second_q_op: SparseLabelOp, - num_particles: tuple[int, int] | None = None, - sector_locator: Callable[[Z2Symmetries, "QubitConverter"], list[int] | None] | None = None, - ) -> SparsePauliOp | PauliSumOp: - """ - Map the given second quantized operator to a qubit operators. Also it will - carry out z2 symmetry reduction on the qubit operators if z2symmetry_reduction has - been specified whether via the constructor or indirectly via the sector locator which - is passed the detected symmetries to inform the determination. - - Args: - second_q_op: A second quantized operator. - num_particles: Needed for two qubit reduction to determine correct sector. If - not supplied, even if two_qubit_reduction is possible, it will not be done. - sector_locator: An optional callback, that given the detected Z2Symmetries, and also - the instance of the converter, can determine the correct sector of the tapered - operators so the correct one can be returned, which contains the problem solution, - out of the set that are the result of tapering. - - Returns: - A qubit operator. - """ - if isinstance(self._mapper, ParityMapper): - self._mapper.num_particles = num_particles - - self._check_reset_mapper() - - reduced_op = self._mapper.map(second_q_op) - tapered_op, z2symmetries = self.find_taper_op(reduced_op, sector_locator) - - self._z2symmetries = z2symmetries - - return tapered_op - - def convert_only( - self, - second_q_op: SparseLabelOp, - num_particles: tuple[int, int] | None = None, - ) -> SparsePauliOp | PauliSumOp: - """ - Map the given second quantized operator to a qubit operators using the mapper - and possibly two qubit reduction. No tapering is done, nor is any conversion state saved, - as is done in :meth:`convert` where a later :meth:`convert_match` will convert - further operators in an identical manner. - - Args: - second_q_op: A second quantized operator. - num_particles: Needed for two qubit reduction to determine correct sector. If - not supplied, even if two_qubit_reduction is possible, it will not be done. - - Returns: - A qubit operator. - """ - if num_particles is not None and isinstance(self._mapper, ParityMapper): - self._mapper.num_particles = num_particles - - self._check_reset_mapper() - - reduced_op = self._mapper.map(second_q_op) - - return reduced_op - - def force_match( - self, - *, - num_particles: tuple[int, int] | None = None, - z2symmetries: Z2Symmetries | None = None, - ) -> None: - """This is for advanced use where :meth:`convert` may not have been called or the - converter should be used to taper to some external characteristics to be matched - when using :meth:`convert_match`. Parameters passed here, when not None, - will override any values stored from :meth:`convert`. - - Args: - num_particles: The number or particles pertaining to two qubit reduction - z2symmetries: Z2Symmetry information for tapering - - Raises: - ValueError: If format of Z2Symmetry tapering values is invalid - """ - if num_particles is not None and isinstance(self._mapper, ParityMapper): - self._mapper.num_particles = num_particles - - if z2symmetries is not None: - if not z2symmetries.is_empty(): - if len(z2symmetries.tapering_values) != len(z2symmetries.sq_list): - raise ValueError( - f"Z2Symmetries tapering value length was " - f"{len(z2symmetries.tapering_values)} but expected " - f"{len(z2symmetries.sq_list)}." - ) - if not np.all(np.isin(z2symmetries.tapering_values, [-1, 1])): - raise ValueError( - f"Z2Symmetries values list must contain only " - f"-1's and/or 1's but was {z2symmetries.tapering_values}." - ) - - self._z2symmetries = z2symmetries - - def convert_match( - self, - second_q_ops: SparseLabelOp | ListOrDictType[SparseLabelOp], - *, - suppress_none: bool = False, - check_commutes: bool = True, - ) -> SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp]: - """Convert further operators to match that done in :meth:`convert`, or as set by - :meth:`force_match`. - - Args: - second_q_ops: A second quantized operator or list thereof to be converted - suppress_none: If None should be placed in the output list where an operator - did not commute with symmetry, to maintain order, or whether that should - be suppressed where the output list length may then be smaller than the input - check_commutes: If True (default) an operator must commute with the - symmetry to be tapered otherwise None is returned for that operator. When - False the operator is tapered with no check so due consideration needs to - be given in this case to how such operator(s) are eventually used. - - Returns: - A qubit operator or list thereof of the same length as the second_q_ops list. All - operators in the second_q_ops list must commute with the symmetry detected when - :meth:`convert` was called. If it does not then the position in the output list - will be set to `None` to preserve the order, unless suppress_none is set; or None may - be directly returned in the case when a single operator is provided (that cannot be - suppressed as it's a single value) - """ - - self._check_reset_mapper() - - # To allow a single operator to be converted, but use the same logic that does the - # actual conversions, we make a single entry list of it here and unwrap to return. - wrapped_type = type(second_q_ops) - - if isinstance(second_q_ops, SparseLabelOp): - second_q_ops = [second_q_ops] - suppress_none = False # When only a single op we will return None back - - wrapped_second_q_ops: _ListOrDict[SparseLabelOp] = _ListOrDict(second_q_ops) - - reduced_ops: _ListOrDict[PauliSumOp] = _ListOrDict() - for name, second_q_op in iter(wrapped_second_q_ops): - reduced_op = self._mapper.map(second_q_op) - # NOTE: we ensure all operators are PauliSumOp here, because this is required by the - # opflow-based Z2Symmetries class still used by the QubitConverter - reduced_ops[name] = ( - PauliSumOp(reduced_op) if not isinstance(reduced_op, PauliSumOp) else reduced_op - ) - - tapered_ops: _ListOrDict[PauliSumOp] = self._symmetry_reduce(reduced_ops, check_commutes) - - # NOTE: _ListOrDict.unwrap takes care of the conversion to/from PauliSumOp based on - # settings.use_pauli_sum_op - returned_ops: PauliSumOp | ListOrDictType[PauliSumOp] = tapered_ops.unwrap( - wrapped_type, suppress_none=suppress_none - ) - - return returned_ops - - def find_taper_op( - self, - qubit_op: SparsePauliOp | PauliSumOp, - sector_locator: Callable[[Z2Symmetries, "QubitConverter"], list[int] | None] | None = None, - ) -> tuple[SparsePauliOp | PauliSumOp, Z2Symmetries]: - r""" - Find the $Z_2$-symmetries associated with the qubit operator and taper it accordingly. - - Args: - qubit_op: Qubit main operator - often the hamiltonian - from which symmetries - will be identified. - sector_locator: Method associated to the problem of interest which identifies the - symmetry sector of the solution. Defaults to None. - - Raises: - QiskitNatureError: The user-specified or identified symmetry sector is not compatible with - the symmetries found for this problem. - QiskitNatureError: The main operator does not commute with its expected symmetries. - - Returns: - Tuple of the form (tapered qubit operator, identified $Z_2$-symmetry object) - """ - if not isinstance(qubit_op, PauliSumOp): - # NOTE: we ensure the operator is a PauliSumOp here, because this is required by the - # opflow-based Z2Symmetries class still used by the QubitConverter - qubit_op = PauliSumOp(qubit_op) - - # Return operator unchanged and empty symmetries if we do not taper - tapered_qubit_op = qubit_op - z2_symmetries = self._no_symmetries - - # If we were given a sector, or one might be located, we first need to find any symmetries - if self.z2symmetry_reduction is not None: - z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) - if z2_symmetries.is_empty(): - logger.debug("No Z2 symmetries found") - else: - # As we have symmetries, if we have a sector locator, if that provides one back - # it will override any value defined on constructor - if sector_locator is not None and self.z2symmetry_reduction == "auto": - z2symmetry_reduction = sector_locator(z2_symmetries, self) - if z2symmetry_reduction is not None: - self.z2symmetry_reduction = z2symmetry_reduction # Overrides any value - - # We may end up that neither were we given a sector nor that the locator - # returned one. Since though we may have found valid symmetries above we should - # simply just forget about them so as not to return something we are not using. - if self.z2symmetry_reduction is None: - z2_symmetries = self._no_symmetries - - # So now if we have a sector and have symmetries we found we can attempt to taper - if ( - self.z2symmetry_reduction is not None - and self.z2symmetry_reduction != "auto" - and not z2_symmetries.is_empty() - ): - # check sector definition fits to symmetries found - if len(self._z2symmetry_reduction) != len(z2_symmetries.symmetries): - raise QiskitNatureError( - "z2symmetry_reduction tapering values list has " - f"invalid length {len(self._z2symmetry_reduction)} " - f"should be {len(z2_symmetries.symmetries)}" - ) - # Check all operators commute with main operator's symmetry - logger.debug("Sanity check that operator commutes with the symmetry") - symmetry_ops = [] - for symmetry in z2_symmetries.symmetries: - symmetry_ops.append(PauliSumOp.from_list([(symmetry.to_label(), 1.0)])) - commutes = QubitConverter._check_commutes(symmetry_ops, qubit_op) - if not commutes: - raise QiskitNatureError( - "Z2 symmetry failure. The operator must commute " - "with symmetries found from it!" - ) - - z2_symmetries.tapering_values = self._z2symmetry_reduction - tapered_qubit_op = z2_symmetries.taper(qubit_op) if commutes else None - - if ( - tapered_qubit_op is not None - and not isinstance(tapered_qubit_op, SparsePauliOp) - and not settings.use_pauli_sum_op - ): - tapered_qubit_op = tapered_qubit_op.primitive - - return tapered_qubit_op, z2_symmetries - - def _symmetry_reduce( - self, - qubit_ops: _ListOrDict[PauliSumOp], - check_commutes: bool, - ) -> _ListOrDict[PauliSumOp]: - - if self._z2symmetries is None or self._z2symmetries.is_empty(): - tapered_qubit_ops = qubit_ops - else: - if check_commutes: - logger.debug("Checking operators commute with symmetry:") - symmetry_ops = [] - for symmetry in self._z2symmetries.symmetries: - symmetry_ops.append(PauliSumOp.from_list([(symmetry.to_label(), 1.0)])) - commuted = {} - for name, qubit_op in iter(qubit_ops): - commutes = QubitConverter._check_commutes(symmetry_ops, qubit_op) - commuted[name] = commutes - logger.debug("Qubit operator '%s' commuted with symmetry: %s", name, commutes) - - # Tapering values were set from prior convert so we go ahead and taper operators - tapered_qubit_ops = _ListOrDict() - for name, commutes in commuted.items(): - if commutes: - tapered_qubit_ops[name] = self._z2symmetries.taper(qubit_ops[name]) - else: - logger.debug("Tapering operators whether they commute with symmetry or not:") - tapered_qubit_ops = _ListOrDict() - for name, qubit_op in iter(qubit_ops): - tapered_qubit_ops[name] = self._z2symmetries.taper(qubit_ops[name]) - - return tapered_qubit_ops - - def symmetry_reduce_clifford( - self, - converted_ops: ListOrDictType[SparsePauliOp | PauliSumOp], - *, - check_commutes: bool = True, - ) -> ListOrDictType[SparsePauliOp | PauliSumOp]: - """ - Applies the tapering to a list of operators previously converted with the Clifford - transformation from the current symmetry. - - Args: - converted_ops: Operators to taper. - check_commutes: If True (default) an operator must commute with the - symmetry to be tapered otherwise None is returned for that operator. When - False the operator is tapered with no check so due consideration needs to - be given in this case to how such operator(s) are eventually used. - - Returns: - Tapered operators. - """ - if converted_ops is None or self._z2symmetries is None or self._z2symmetries.is_empty(): - return_ops = converted_ops - else: - wrapped_converted_ops, wrapped_type = _ListOrDict.wrap(converted_ops) - - pauli_sum_ops: ListOrDictType[PauliSumOp] = _ListOrDict() - for name, qubit_op in iter(wrapped_converted_ops): - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - pauli_sum_ops[name] = qubit_op - - if check_commutes: - logger.debug("Checking operators commute with symmetry:") - symmetry_ops = [] - for sq_pauli in self._z2symmetries._sq_paulis: - symmetry_ops.append(PauliSumOp.from_list([(sq_pauli.to_label(), 1.0)])) - commuted = {} - for name, qubit_op in iter(pauli_sum_ops): - commutes = QubitConverter._check_commutes(symmetry_ops, qubit_op) - commuted[name] = commutes - logger.debug("Qubit operator '%s' commuted with symmetry: %s", name, commutes) - - # Tapering values were set from prior convert, so we go ahead and taper operators - tapered_qubit_ops: _ListOrDict[PauliSumOp] = _ListOrDict() - for name, commutes in commuted.items(): - if commutes: - tapered_qubit_ops[name] = self._z2symmetries.taper_clifford( - pauli_sum_ops[name] - ) - else: - logger.debug("Tapering operators whether they commute with symmetry or not:") - tapered_qubit_ops = _ListOrDict() - for name, qubit_op in iter(pauli_sum_ops): - tapered_qubit_ops[name] = self._z2symmetries.taper_clifford(pauli_sum_ops[name]) - - # NOTE: _ListOrDict.unwrap takes care of the conversion to/from PauliSumOp based on - # settings.use_pauli_sum_op - return_ops = tapered_qubit_ops.unwrap(wrapped_type) - - return return_ops - - def convert_clifford( - self, - qubit_ops: SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp], - ) -> SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp]: - """ - Applies the Clifford transformation from the current symmetry to all operators. - - Args: - qubit_ops: Operators to convert. - - Returns: - Converted operators - """ - if qubit_ops is None or self._z2symmetries is None or self._z2symmetries.is_empty(): - converted_ops = qubit_ops - else: - wrapped_second_q_ops, wrapped_type = _ListOrDict.wrap(qubit_ops) - - pauli_sum_ops: ListOrDictType[PauliSumOp] = _ListOrDict() - for name, qubit_op in iter(wrapped_second_q_ops): - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - pauli_sum_ops[name] = qubit_op - - converted_ops = _ListOrDict() - for name, second_q_op in iter(pauli_sum_ops): - converted_ops[name] = self._z2symmetries.convert_clifford(second_q_op) - - # NOTE: _ListOrDict.unwrap takes care of the conversion to/from PauliSumOp based on - # settings.use_pauli_sum_op - converted_ops = converted_ops.unwrap(wrapped_type) - - return converted_ops - - @staticmethod - def _check_commutes(cliffords: list[PauliSumOp], qubit_op: PauliSumOp) -> bool: - commutes = [] - for clifford in cliffords: - commuting_rows = qubit_op.primitive.paulis.commutes_with_all(clifford.primitive.paulis) - commutes.append(len(commuting_rows) == qubit_op.primitive.size) - does_commute = bool(np.all(commutes)) - logger.debug(" '%s' commutes: %s, %s", id(qubit_op), does_commute, commutes) - - return does_commute diff --git a/qiskit_nature/second_q/mappers/qubit_mapper.py b/qiskit_nature/second_q/mappers/qubit_mapper.py index 4b782f9fe..f2150ba96 100644 --- a/qiskit_nature/second_q/mappers/qubit_mapper.py +++ b/qiskit_nature/second_q/mappers/qubit_mapper.py @@ -19,12 +19,10 @@ from typing import TypeVar, Dict, Iterable, Generic, Generator import numpy as np -from qiskit.opflow import PauliSumOp from qiskit.quantum_info.operators import Pauli, SparsePauliOp from qiskit.algorithms.list_or_dict import ListOrDict as ListOrDictType -from qiskit_nature import QiskitNatureError, settings -from qiskit_nature.deprecation import deprecate_arguments, deprecate_property +from qiskit_nature import QiskitNatureError from qiskit_nature.second_q.operators import SparseLabelOp # pylint: disable=invalid-name @@ -100,16 +98,9 @@ def unwrap(self, wrapped_type: type, *, suppress_none: bool = True) -> dict | It Content of the current class instance as a list, a dictionary or a single element. """ - def _qubit_op_type_wrapper(qubit_op: SparsePauliOp | PauliSumOp | None): + def _qubit_op_type_wrapper(qubit_op: SparsePauliOp | None): if qubit_op is None: return None - current_type = type(qubit_op) - if settings.use_pauli_sum_op: - if issubclass(current_type, PauliSumOp): - return qubit_op - return PauliSumOp(qubit_op) - if issubclass(current_type, PauliSumOp): - return qubit_op.primitive return qubit_op if wrapped_type == list: @@ -128,27 +119,14 @@ def _qubit_op_type_wrapper(qubit_op: SparsePauliOp | PauliSumOp | None): class QubitMapper(ABC): """The interface for implementing methods which map from a ``SparseLabelOp`` to a - qubit operator in the form of a ``PauliSumOp`` or ``SparsePauliOp`` (depending on - :attr:`~qiskit_nature.settings.use_pauli_sum_op`). + qubit operator in the form of a ``SparsePauliOp``. """ - @property - @deprecate_property("0.6.0") - def allows_two_qubit_reduction(self) -> bool: - """ - DEPRECATED: Getter for symmetry information for two qubit reduction - - Returns: If mapping generates the known symmetry that allows two qubit reduction. - - """ - return False - def _map_single( self, second_q_op: SparseLabelOp, *, register_length: int | None = None - ) -> SparsePauliOp | PauliSumOp: + ) -> SparsePauliOp: """Maps a :class:`~qiskit_nature.second_q.operators.SparseLabelOp` - to a ``PauliSumOp`` or ``SparsePauliOp`` (depending on - :attr:`~qiskit_nature.settings.use_pauli_sum_op`). + to a ``SparsePauliOp``. Args: second_q_op: the ``SparseLabelOp`` to be mapped. @@ -166,7 +144,7 @@ def map( second_q_ops: SparseLabelOp | ListOrDictType[SparseLabelOp], *, register_length: int | None = None, - ) -> SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp]: + ) -> SparsePauliOp | ListOrDictType[SparsePauliOp]: """Maps a second quantized operator or a list, dict of second quantized operators based on the current mapper. @@ -177,8 +155,7 @@ def map( ``register_length`` is considered a lower bound in a ``SparseLabelOp``. Returns: - A qubit operator in the form of a ``PauliSumOp`` or ``SparsePauliOp`` (depending on - :attr:`~qiskit_nature.settings.use_pauli_sum_op`), or list (resp. dict) thereof if a + A qubit operator in the form of a ``SparsePauliOp``, or list (resp. dict) thereof if a list (resp. dict) of second quantized operators was supplied. """ wrapped_second_q_ops, wrapped_type = _ListOrDict.wrap(second_q_ops) @@ -193,18 +170,14 @@ def map( return returned_ops @classmethod - @deprecate_arguments("0.6.0", {"nmodes": "register_length"}) @lru_cache(maxsize=32) - def pauli_table( - cls, register_length: int, *, nmodes: int | None = None - ) -> list[tuple[Pauli, Pauli]]: + def pauli_table(cls, register_length: int) -> list[tuple[Pauli, Pauli]]: """Generates a Pauli-lookup table mapping from modes to pauli pairs. The generated table is processed by :meth:`.QubitMapper.sparse_pauli_operators`. Args: register_length: the register length for which to generate the table. - nmodes: (DEPRECATED) The old name for ``register_length``. Returns: A list of tuples in which the first and second Pauli operator the real and imaginary @@ -212,10 +185,9 @@ def pauli_table( """ @classmethod - @deprecate_arguments("0.6.0", {"nmodes": "register_length"}) @lru_cache(maxsize=32) def sparse_pauli_operators( - cls, register_length: int, *, nmodes: int | None = None + cls, register_length: int ) -> tuple[list[SparsePauliOp], list[SparsePauliOp]]: # pylint: disable=unused-argument """Generates the cached :class:`.SparsePauliOp` terms. @@ -225,7 +197,6 @@ def sparse_pauli_operators( Args: register_length: the register length for which to generate the operators. - nmodes: (DEPRECATED) The old name for ``register_length``. Returns: Two lists stored in a tuple, consisting of the creation and annihilation operators, @@ -249,21 +220,16 @@ def sparse_pauli_operators( return (times_creation_op, times_annihilation_op) @classmethod - @deprecate_arguments("0.6.0", {"nmodes": "register_length"}) def mode_based_mapping( cls, second_q_op: SparseLabelOp, - nmodes: int | None = None, - *, register_length: int | None = None, - ) -> SparsePauliOp | PauliSumOp: + ) -> SparsePauliOp: # pylint: disable=unused-argument """Utility method to map a ``SparseLabelOp`` to a qubit operator using a pauli table. Args: second_q_op: the `SparseLabelOp` to be mapped. - nmodes: (DEPRECATED) the number of modes for which to generate the operators. This - argument is ignored in favor of :attr:`.SparseLabelOp.register_length`. register_length: when provided, this will be used to overwrite the ``register_length`` attribute of the operator being mapped. This is possible because the ``register_length`` is considered a lower bound. @@ -306,4 +272,4 @@ def mode_based_mapping( ret_op_list.append(ret_op) sparse_op = SparsePauliOp.sum(ret_op_list).simplify() - return PauliSumOp(sparse_op) if settings.use_pauli_sum_op else sparse_op + return sparse_op diff --git a/qiskit_nature/second_q/mappers/spin_mapper.py b/qiskit_nature/second_q/mappers/spin_mapper.py index 58ffa8150..f03994d74 100644 --- a/qiskit_nature/second_q/mappers/spin_mapper.py +++ b/qiskit_nature/second_q/mappers/spin_mapper.py @@ -14,7 +14,6 @@ from __future__ import annotations -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.operators import SpinOp @@ -30,5 +29,5 @@ def map( second_q_ops: SpinOp | ListOrDictType[SpinOp], *, register_length: int | None = None, - ) -> SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp]: + ) -> SparsePauliOp | ListOrDictType[SparsePauliOp]: return super().map(second_q_ops, register_length=register_length) diff --git a/qiskit_nature/second_q/mappers/tapered_qubit_mapper.py b/qiskit_nature/second_q/mappers/tapered_qubit_mapper.py index 69b2a7e45..b6b89496c 100644 --- a/qiskit_nature/second_q/mappers/tapered_qubit_mapper.py +++ b/qiskit_nature/second_q/mappers/tapered_qubit_mapper.py @@ -18,11 +18,9 @@ from typing import cast from qiskit.algorithms.list_or_dict import ListOrDict as ListOrDictType -from qiskit.opflow import PauliSumOp from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries from qiskit.quantum_info.operators import SparsePauliOp -from qiskit_nature import settings from qiskit_nature.second_q.operators import SparseLabelOp from .qubit_mapper import QubitMapper, _ListOrDict @@ -71,8 +69,6 @@ def _map_clifford_single( self, second_q_op: SparseLabelOp, *, register_length: int | None = None ) -> SparsePauliOp: mapped_op = self.mapper.map(second_q_op, register_length=register_length) - if isinstance(mapped_op, PauliSumOp): - mapped_op = mapped_op.primitive converted_op = self.z2symmetries.convert_clifford(mapped_op) return converted_op @@ -87,17 +83,17 @@ def _taper_clifford_single(self, converted_op: SparsePauliOp) -> SparsePauliOp: def _map_single( self, second_q_op: SparseLabelOp, *, register_length: int | None = None - ) -> SparsePauliOp | PauliSumOp: + ) -> SparsePauliOp: converted_op = self._map_clifford_single(second_q_op, register_length=register_length) tapered_op = self._taper_clifford_single(converted_op) - return PauliSumOp(tapered_op) if settings.use_pauli_sum_op else tapered_op + return tapered_op def map_clifford( self, second_q_ops: SparseLabelOp | ListOrDictType[SparseLabelOp], *, register_length: int | None = None, - ) -> SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp]: + ) -> SparsePauliOp | ListOrDictType[SparsePauliOp]: """Maps a second quantized operator or a list, dict of second quantized operators based on the internal mapper. Then, composes all mapped pauli operators with the clifford operations defined by the internal ``Z2Symmetries`` to prepare for the symmetry reduction. @@ -110,8 +106,7 @@ def map_clifford( ``register_length`` is considered a lower bound in a ``SparseLabelOp``. Returns: - A qubit operator in the form of a ``PauliSumOp`` or ``SparsePauliOp`` (depending on - :attr:`~qiskit_nature.settings.use_pauli_sum_op`), or list (resp. dict) thereof if a + A qubit operator in the form of a ``SparsePauliOp``, or list (resp. dict) thereof if a list (resp. dict) of second quantized operators was supplied. """ wrapped_second_q_ops, wrapped_type = _ListOrDict.wrap(second_q_ops) @@ -122,22 +117,21 @@ def map_clifford( second_q_op, register_length=register_length ) - # NOTE: _ListOrDict.unwrap takes care of the conversion to/from PauliSumOp based on - # settings.use_pauli_sum_op returned_ops = qubit_ops.unwrap(wrapped_type) return returned_ops def taper_clifford( self, - pauli_ops: SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp], + pauli_ops: SparsePauliOp | ListOrDictType[SparsePauliOp], *, check_commutes: bool = True, suppress_none: bool = True, - ) -> SparsePauliOp | PauliSumOp | None | ListOrDictType[SparsePauliOp | PauliSumOp | None]: - """Applies the symmetry reduction on a ``PauliSumOp`` or a list (resp. dict). This method implies - that the second quantized operators were already mapped to Pauli operators and composed with the - clifford operations defined in the symmetry, for example using the ``map_clifford`` method. + ) -> SparsePauliOp | None | ListOrDictType[SparsePauliOp | None]: + """Applies the symmetry reduction on a ``SparsePauliOp`` or a list (resp. dict). This method + implies that the second quantized operators were already mapped to Pauli operators and + composed with the clifford operations defined in the symmetry, for example using the + ``map_clifford`` method. Args: pauli_ops: A pauli operator already evolved with the symmetry clifford operations. @@ -148,30 +142,25 @@ def taper_clifford( be suppressed where the output list length may then be smaller than the input. Returns: - A qubit operator in the form of a ``PauliSumOp`` or ``SparsePauliOp`` (depending on - :attr:`~qiskit_nature.settings.use_pauli_sum_op`), or list (resp. dict) thereof if a + A qubit operator in the form of a ``SparsePauliOp``, or list (resp. dict) thereof if a list (resp. dict) of second quantized operators was supplied. """ wrapped_pauli_ops, wrapped_type = _ListOrDict.wrap(pauli_ops) - qubit_ops: _ListOrDict[SparsePauliOp | PauliSumOp] + qubit_ops: _ListOrDict[SparsePauliOp] if self.z2symmetries.is_empty(): qubit_ops = wrapped_pauli_ops else: qubit_ops = _ListOrDict() for name, pauli_op in iter(wrapped_pauli_ops): - if isinstance(pauli_op, PauliSumOp): - pauli_op = pauli_op.primitive if check_commutes and not self._check_commutes(pauli_op): qubit_ops[name] = None else: qubit_ops[name] = self._taper_clifford_single(pauli_op) - # NOTE: _ListOrDict.unwrap takes care of the conversion to/from PauliSumOp based on - # settings.use_pauli_sum_op - returned_ops: SparsePauliOp | PauliSumOp | ListOrDictType[ - SparsePauliOp | PauliSumOp - ] = qubit_ops.unwrap(wrapped_type, suppress_none=suppress_none) + returned_ops: SparsePauliOp | ListOrDictType[SparsePauliOp] = qubit_ops.unwrap( + wrapped_type, suppress_none=suppress_none + ) return returned_ops @@ -180,7 +169,7 @@ def map( second_q_ops: SparseLabelOp | ListOrDictType[SparseLabelOp], *, register_length: int | None = None, - ) -> SparsePauliOp | PauliSumOp | None | ListOrDictType[SparsePauliOp | PauliSumOp | None]: + ) -> SparsePauliOp | None | ListOrDictType[SparsePauliOp | None]: """Maps a second quantized operator or a list, dict of second quantized operators based on the current mapper. @@ -191,8 +180,7 @@ def map( ``register_length`` is considered a lower bound in a ``SparseLabelOp``. Returns: - A qubit operator in the form of a ``PauliSumOp`` or ``SparsePauliOp`` (depending on - :attr:`~qiskit_nature.settings.use_pauli_sum_op`), or list (resp. dict) thereof if a + A qubit operator in the form of a ``SparsePauliOp``, or list (resp. dict) thereof if a list (resp. dict) of second quantized operators was supplied. """ # NOTE: we do not rely on the `_map_single` method here because we want to ensure that diff --git a/qiskit_nature/second_q/mappers/vibrational_mapper.py b/qiskit_nature/second_q/mappers/vibrational_mapper.py index 3da7e21bd..ba9744b10 100644 --- a/qiskit_nature/second_q/mappers/vibrational_mapper.py +++ b/qiskit_nature/second_q/mappers/vibrational_mapper.py @@ -14,7 +14,6 @@ from __future__ import annotations -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.operators import VibrationalOp @@ -30,5 +29,5 @@ def map( second_q_ops: VibrationalOp | ListOrDictType[VibrationalOp], *, register_length: int | None = None, - ) -> SparsePauliOp | PauliSumOp | ListOrDictType[SparsePauliOp | PauliSumOp]: + ) -> SparsePauliOp | ListOrDictType[SparsePauliOp]: return super().map(second_q_ops, register_length=register_length) diff --git a/qiskit_nature/second_q/operators/bosonic_op.py b/qiskit_nature/second_q/operators/bosonic_op.py index e73035dcd..60b0ef21c 100644 --- a/qiskit_nature/second_q/operators/bosonic_op.py +++ b/qiskit_nature/second_q/operators/bosonic_op.py @@ -25,7 +25,6 @@ from .polynomial_tensor import PolynomialTensor from .sparse_label_op import _TCoeff, SparseLabelOp, _to_number -from .tensor import Tensor class BosonicOp(SparseLabelOp): @@ -249,11 +248,6 @@ def from_polynomial_tensor(cls, tensor: PolynomialTensor) -> BosonicOp: mat = tensor[key] - if not isinstance(mat, Tensor): - # TODO: this case is to be removed once qiskit_nature.settings.tensor_unwrapping is - # deprecated and the PolynomialTensor item is guaranteed to be of type Tensor - mat = Tensor(mat) - label_template = mat.label_template.format(*key) for value, index in mat.coord_iter(): diff --git a/qiskit_nature/second_q/operators/electronic_integrals.py b/qiskit_nature/second_q/operators/electronic_integrals.py index 5d43de7db..a9d1151bb 100644 --- a/qiskit_nature/second_q/operators/electronic_integrals.py +++ b/qiskit_nature/second_q/operators/electronic_integrals.py @@ -265,12 +265,7 @@ def alpha_beta(self) -> PolynomialTensor: # we delegate the conjugation to the objects themselves return PolynomialTensor({"++--": two_body_ba.conjugate()}, validate=False) - alpha_beta: Tensor | np.ndarray | SparseArray - if not isinstance(two_body_ba, Tensor): - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - alpha_beta = Tensor(np.moveaxis(Tensor(two_body_ba), (0, 1), (2, 3))) - else: - alpha_beta = np.moveaxis(two_body_ba, (0, 1), (2, 3)) + alpha_beta = cast(Tensor, np.moveaxis(two_body_ba, (0, 1), (2, 3))) return PolynomialTensor({"++--": alpha_beta}, validate=False) @property diff --git a/qiskit_nature/second_q/operators/fermionic_op.py b/qiskit_nature/second_q/operators/fermionic_op.py index f5254a8cd..d2e508a5a 100644 --- a/qiskit_nature/second_q/operators/fermionic_op.py +++ b/qiskit_nature/second_q/operators/fermionic_op.py @@ -20,15 +20,12 @@ from typing import Iterator, Sequence import numpy as np -from scipy.sparse import csc_matrix -from qiskit_nature.deprecation import deprecate_method from qiskit_nature.exceptions import QiskitNatureError from ._bits_container import _BitsContainer from .polynomial_tensor import PolynomialTensor from .sparse_label_op import _TCoeff, SparseLabelOp, _to_number -from .tensor import Tensor class FermionicOp(SparseLabelOp): @@ -253,11 +250,6 @@ def from_polynomial_tensor(cls, tensor: PolynomialTensor) -> FermionicOp: mat = tensor[key] - if not isinstance(mat, Tensor): - # TODO: this case is to be removed once qiskit_nature.settings.tensor_unwrapping is - # deprecated and the PolynomialTensor item is guaranteed to be of type Tensor - mat = Tensor(mat) - label_template = mat.label_template.format(*key) for value, index in mat.coord_iter(): @@ -348,85 +340,6 @@ def _tensor(cls, a: FermionicOp, b: FermionicOp, *, offset: bool = True) -> Ferm new_op.num_spin_orbitals = a.num_spin_orbitals + b.num_spin_orbitals return new_op - # pylint: disable=bad-docstring-quotes - @deprecate_method( - "0.6.0", - additional_msg=( - ". This method has no direct replacement. Instead, use the " - "`qiskit_nature.second_q.mappers.JordanWignerMapper` to create a qubit operator and " - "subsequently use its `to_matrix()` method. Be advised, that the basis state ordering " - "of that output will differ due to the bitstring endianness. For more information " - "refer to https://github.com/Qiskit/qiskit-nature/issues/875." - ), - ) - def to_matrix(self, sparse: bool | None = True) -> csc_matrix | np.ndarray: - """DEPRECATED Convert to a matrix representation over the full fermionic Fock space in the - occupation number basis. - - The basis states are ordered in increasing bitstring order as 0000, 0001, ..., 1111. - - Args: - sparse: If true, the matrix is returned as a sparse matrix, otherwise it is returned as - a dense numpy array. - - Returns: - The matrix of the operator in the Fock basis - - Raises: - ValueError: Operator contains parameters. - """ - if self.is_parameterized(): - raise ValueError("to_matrix is not supported for operators containing parameters.") - - csc_data, csc_col, csc_row = [], [], [] - - dimension = 1 << self.num_spin_orbitals - - # loop over all columns of the matrix - for col_idx in range(dimension): - initial_occupations = [occ == "1" for occ in f"{col_idx:0{self.num_spin_orbitals}b}"] - # loop over the terms in the operator data - for terms, prefactor in self.simplify().terms(): - # check if op string is the identity - if not terms: - csc_data.append(prefactor) - csc_row.append(col_idx) - csc_col.append(col_idx) - else: - occupations = initial_occupations.copy() - sign = 1 - mapped_to_zero = False - - # apply terms sequentially to the current basis state - for char, index in reversed(terms): - index = int(index) - occ = occupations[index] - if (char == "+") == occ: - # Applying the creation operator on an occupied state maps to zero. So - # does applying the annihilation operator on an unoccupied state. - mapped_to_zero = True - break - sign *= (-1) ** sum(occupations[:index]) - occupations[index] = not occ - - # add data point to matrix in the correct row - if not mapped_to_zero: - row_idx = sum(int(occ) << idx for idx, occ in enumerate(occupations[::-1])) - csc_data.append(sign * prefactor) - csc_row.append(row_idx) - csc_col.append(col_idx) - - sparse_mat = csc_matrix( - (csc_data, (csc_row, csc_col)), - shape=(dimension, dimension), - dtype=complex, - ) - - if sparse: - return sparse_mat - else: - return sparse_mat.toarray() - def transpose(self) -> FermionicOp: data = {} diff --git a/qiskit_nature/second_q/operators/polynomial_tensor.py b/qiskit_nature/second_q/operators/polynomial_tensor.py index 9db6c4070..eb4dac77c 100644 --- a/qiskit_nature/second_q/operators/polynomial_tensor.py +++ b/qiskit_nature/second_q/operators/polynomial_tensor.py @@ -17,7 +17,7 @@ from collections.abc import Callable, Mapping from itertools import product from numbers import Number -from typing import Iterator, Sequence, Type, Union, cast +from typing import Iterator, Sequence, Type, cast import numpy as np @@ -182,9 +182,7 @@ def __init__( Args: data: mapping of string-based operator keys to coefficient tensor values. If the values are not already of type :class:`~qiskit_nature.second_q.operators.Tensor`, they will - automatically be wrapped into one. Upon retrieval via item access (``__getitem__``) - automatically wrapped objects will be unwrapped again depending on the value of - :attr:`~qiskit_nature.settings.tensor_unwrapping`. + automatically be wrapped into one. validate: when set to False the ``data`` will not be validated. Disable this setting with care! @@ -200,9 +198,6 @@ def __init__( for key, value in data.items(): if not isinstance(value, Tensor): value = Tensor(value) - # NOTE: the following monkey patch attribute is used only for the deprecation period - # during which this Tensor class is being introduced into the stack - value._monkey_patched_unwrap_toggle = True if validate and len(value.shape) != len(key): raise ValueError( @@ -238,8 +233,7 @@ def register_length(self) -> int | None: for key in self._data: if key == "": continue - # TODO: remove unnecessary cast once settings.tensor_unwrapping is removed - return cast(Union[np.ndarray, SparseArray, Tensor], self[key]).shape[0] + return self[key].shape[0] return None def __repr__(self) -> str: @@ -268,30 +262,22 @@ def is_empty(self) -> bool: @_optionals.HAS_SPARSE.require_in_call def is_sparse(self) -> bool: """Returns whether all matrices in this tensor are sparse.""" - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - return all(Tensor(self[key]).is_sparse() for key in self if key != "") + return all(self[key].is_sparse() for key in self if key != "") def is_dense(self) -> bool: """Returns whether all matrices in this tensor are dense.""" - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - return all(Tensor(self[key]).is_dense() for key in self if key != "") + return all(self[key].is_dense() for key in self if key != "") - def __getitem__(self, __k: str) -> np.ndarray | SparseArray | Number | Tensor: + def __getitem__(self, __k: str) -> Tensor: """Gets the value from the ``PolynomialTensor``. Args: __k: operator key string in the ``PolynomialTensor``. Returns: - Value corresponding to the operator key ``__k``. If - :attr:`~qiskit_nature.settings.tensor_unwrapping` is ``False``, the returned is - guaranteed to be of type :class:`~qiskit_nature.second_q.operators.Tensor`. + Value corresponding to the operator key ``__k``. """ item = self._data.__getitem__(__k) - - if settings.tensor_unwrapping and hasattr(item, "_monkey_patched_unwrap_toggle"): - return item.array - return item def __len__(self) -> int: @@ -486,11 +472,6 @@ def compose( atensor = a[akey] btensor = b[bkey] - # TODO: remove these once settings.tensor_unwrapping is removed - if not isinstance(atensor, Tensor): - atensor = Tensor(atensor) - if not isinstance(btensor, Tensor): - btensor = Tensor(btensor) outer = atensor.compose(btensor, qargs=qargs, front=True) @@ -551,11 +532,6 @@ def _tensor(cls, a: PolynomialTensor, b: PolynomialTensor) -> PolynomialTensor: atensor = a[akey] btensor = b[bkey] - # TODO: remove these once settings.tensor_unwrapping is removed - if not isinstance(atensor, Tensor): - atensor = Tensor(atensor) - if not isinstance(btensor, Tensor): - btensor = Tensor(btensor) einsum = atensor.tensor(btensor) @@ -848,12 +824,9 @@ def einsum( for einsum, terms in einsum_map.items(): *inputs, output = terms try: - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed ops = [] for idx, term in enumerate(inputs): op = operand_list[idx]._data[term] - if not isinstance(op, Tensor): - op = Tensor(op) ops.append(op) result = einsum_func(einsum, *ops, optimize=settings.optimize_einsum) except KeyError: diff --git a/qiskit_nature/second_q/operators/spin_op.py b/qiskit_nature/second_q/operators/spin_op.py index 8df839a7f..b7f429aac 100644 --- a/qiskit_nature/second_q/operators/spin_op.py +++ b/qiskit_nature/second_q/operators/spin_op.py @@ -33,7 +33,6 @@ from .polynomial_tensor import PolynomialTensor from .sparse_label_op import _TCoeff, SparseLabelOp, _to_number -from .tensor import Tensor class SpinOp(SparseLabelOp): @@ -309,11 +308,6 @@ def from_polynomial_tensor(cls, tensor: PolynomialTensor) -> SpinOp: mat = tensor[key] - if not isinstance(mat, Tensor): - # TODO: this case is to be removed once qiskit_nature.settings.tensor_unwrapping is - # deprecated and the PolynomialTensor item is guaranteed to be of type Tensor - mat = Tensor(mat) - label_template = mat.label_template.format(*key) for value, index in mat.coord_iter(): diff --git a/qiskit_nature/second_q/operators/vibrational_op.py b/qiskit_nature/second_q/operators/vibrational_op.py index f9f9ee528..23b11c4ec 100644 --- a/qiskit_nature/second_q/operators/vibrational_op.py +++ b/qiskit_nature/second_q/operators/vibrational_op.py @@ -29,7 +29,6 @@ from ._bits_container import _BitsContainer from .polynomial_tensor import PolynomialTensor from .sparse_label_op import _TCoeff, SparseLabelOp, _to_number -from .tensor import Tensor logger = logging.getLogger(__name__) @@ -306,11 +305,6 @@ def _reshape_index(index): mat = tensor[key] - if not isinstance(mat, Tensor): - # TODO: this case is to be removed once qiskit_nature.settings.tensor_unwrapping is - # deprecated and the PolynomialTensor item is guaranteed to be of type Tensor - mat = Tensor(mat) - label_template = mat.label_template.format(*key.replace("_", "")) for value, index in mat.coord_iter(): diff --git a/qiskit_nature/second_q/problems/base_problem.py b/qiskit_nature/second_q/problems/base_problem.py index 0598c1bb1..a9355a726 100644 --- a/qiskit_nature/second_q/problems/base_problem.py +++ b/qiskit_nature/second_q/problems/base_problem.py @@ -19,12 +19,9 @@ import numpy as np from qiskit.algorithms.eigensolvers import EigensolverResult from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolverResult -from qiskit.opflow import PauliSumOp -from qiskit.opflow.primitive_ops import Z2Symmetries as OpflowZ2Symmetries from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries -from qiskit_nature.deprecation import deprecate_function -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper, TaperedQubitMapper +from qiskit_nature.second_q.mappers import QubitMapper, TaperedQubitMapper from qiskit_nature.second_q.operators import SparseLabelOp from qiskit_nature.second_q.hamiltonians import Hamiltonian @@ -77,39 +74,10 @@ def second_q_ops(self) -> tuple[SparseLabelOp, dict[str, SparseLabelOp]]: return main_op, aux_ops - # pylint: disable=bad-docstring-quotes - @deprecate_function( - "0.6.0", - additional_msg=( - ". This function is deprecated because it will be removed from the public API. It is " - "no longer necessary to be used when working directly with QubitMapper objects outside " - "a QubitConverter because a TaperedQubitMapper can now be obtained using the new " - "get_tapered_mapper function provided by the problem classes" - ), - ) - def symmetry_sector_locator( - self, - z2_symmetries: OpflowZ2Symmetries | Z2Symmetries, - converter: QubitConverter | QubitMapper, - ) -> list[int] | None: - # pylint: disable=unused-argument - """Given the detected Z2Symmetries, it can determine the correct sector of the tapered - operators so the correct one can be returned - - Args: - z2_symmetries: the z2 symmetries object. - converter: the ``QubitConverter`` or ``QubitMapper`` instance used for the operator - conversion that symmetries are to be determined for. - - Returns: - the sector of the tapered operators with the problem solution - """ - return None - def _symmetry_sector_locator( self, - z2_symmetries: OpflowZ2Symmetries | Z2Symmetries, - mapper: QubitConverter | QubitMapper, + z2_symmetries: Z2Symmetries, + mapper: QubitMapper, ) -> list[int] | None: # pylint: disable=unused-argument """Given the detected Z2Symmetries, it can determine the correct sector of the tapered @@ -117,9 +85,8 @@ def _symmetry_sector_locator( Args: z2_symmetries: the z2 symmetries object. - mapper: the ``QubitMapper`` or ``QubitConverter`` instance (use of the latter is - deprecated) used for the operator conversion that symmetries are to be determined - for. + mapper: the ``QubitMapper`` used for the operator conversion that symmetries are to be + determined for. Returns: the sector of the tapered operators with the problem solution @@ -150,8 +117,6 @@ def get_tapered_mapper(self, mapper: QubitMapper) -> TaperedQubitMapper: qubit_op, _ = self.second_q_ops() mapped_op = mapper.map(qubit_op) - if isinstance(mapped_op, PauliSumOp): - mapped_op = mapped_op.primitive z2_symmetries = Z2Symmetries.find_z2_symmetries(mapped_op) # pylint: disable=assignment-from-none # Known issue for abstract class methods https://github.com/PyCQA/pylint/issues/2559 diff --git a/qiskit_nature/second_q/problems/eigenstate_result.py b/qiskit_nature/second_q/problems/eigenstate_result.py index 9d3f7b018..b157ca9d0 100644 --- a/qiskit_nature/second_q/problems/eigenstate_result.py +++ b/qiskit_nature/second_q/problems/eigenstate_result.py @@ -25,8 +25,6 @@ from qiskit.circuit import QuantumCircuit from qiskit.quantum_info import Statevector -from qiskit_nature.runtime.vqe_client import VQERuntimeResult - def _statevector_to_circuit(state: Statevector) -> QuantumCircuit: circ = QuantumCircuit(state.num_qubits) @@ -95,8 +93,6 @@ def from_result( return EigenstateResult.from_eigensolver_result(raw_result) if isinstance(raw_result, MinimumEigensolverResult): return EigenstateResult.from_minimum_eigensolver_result(raw_result) - if isinstance(raw_result, VQERuntimeResult): - return EigenstateResult._from_vqe_runtime_result(raw_result) raise TypeError( f"Cannot construct an EigenstateResult from a result of type, {type(raw_result)}." ) @@ -163,22 +159,6 @@ def from_minimum_eigensolver_result( return result - @classmethod - def _from_vqe_runtime_result(cls, raw_result: VQERuntimeResult) -> EigenstateResult: - result = EigenstateResult() - result.raw_result = raw_result - result.eigenvalues = np.asarray([raw_result.eigenvalue]) - - if hasattr(raw_result, "optimal_circuit") and hasattr(raw_result, "optimal_point"): - result.eigenstates = [(raw_result.optimal_circuit, raw_result.optimal_point)] - - if raw_result.aux_operator_eigenvalues is not None: - result.aux_operators_evaluated = [ - cls._unwrap_aux_op_values(raw_result.aux_operator_eigenvalues) - ] - - return result - @staticmethod def _unwrap_aux_op_values( aux_operators_evaluated: ListOrDict[tuple[complex, dict[str, Any]]] diff --git a/qiskit_nature/second_q/problems/electronic_structure_problem.py b/qiskit_nature/second_q/problems/electronic_structure_problem.py index 63c547bf2..e29bae96d 100644 --- a/qiskit_nature/second_q/problems/electronic_structure_problem.py +++ b/qiskit_nature/second_q/problems/electronic_structure_problem.py @@ -14,7 +14,6 @@ from __future__ import annotations -import warnings from functools import partial from typing import cast, Callable, List, Optional, Union, TYPE_CHECKING @@ -22,15 +21,13 @@ from qiskit.algorithms.eigensolvers import EigensolverResult from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolverResult -from qiskit.opflow.primitive_ops import Z2Symmetries as OpflowZ2Symmetries from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries -from qiskit_nature.deprecation import deprecate_function, warn_deprecated_type from qiskit_nature.exceptions import QiskitNatureError from qiskit_nature.second_q.circuit.library.initial_states.hartree_fock import ( hartree_fock_bitstring_mapped, ) -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper +from qiskit_nature.second_q.mappers import QubitMapper from qiskit_nature.second_q.hamiltonians import ElectronicEnergy from qiskit_nature.second_q.properties import Interpretable @@ -275,75 +272,18 @@ def filter_criterion(self, eigenstate, eigenvalue, aux_values): return partial(filter_criterion, self) - # pylint: disable=bad-docstring-quotes - @deprecate_function( - "0.6.0", - additional_msg=( - ". This function is deprecated because it has been removed from the public API. It is " - "no longer necessary to be used when working directly with QubitMapper objects outside " - "a QubitConverter because a TaperedQubitMapper can now be obtained using the new " - "get_tapered_mapper function provided by the problem classes." - ), - ) - def symmetry_sector_locator( - self, - z2_symmetries: OpflowZ2Symmetries | Z2Symmetries, - converter: QubitConverter | QubitMapper, - ) -> Optional[List[int]]: - """Given the detected Z2Symmetries this determines the correct sector of the tapered - operator that contains the ground state we need and returns that information. - - Args: - z2_symmetries: the z2 symmetries object. - converter: the ``QubitConverter`` or ``QubitMapper`` instance used for the operator - conversion that symmetries are to be determined for. - - Raises: - QiskitNatureError: if the :attr:`num_particles` attribute is ``None``. - - Returns: - The sector of the tapered operators with the problem solution. - """ - if self.num_particles is None: - raise QiskitNatureError( - "Determining the correct symmetry sector for Z2 symmetry reduction requires the " - "number of particles to be set on the problem instance. Please set " - "ElectronicStructureProblem.num_particles or disable the use of Z2Symmetries to " - "fix this." - ) - - num_particles = self.num_particles - if not isinstance(num_particles, tuple): - num_particles = (self.num_alpha, self.num_beta) - - # We need the HF bitstring mapped to the qubit space but without any tapering done - # by the converter (just qubit mapping and any two qubit reduction) since we are - # going to determine the tapering sector - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - hf_bitstr = hartree_fock_bitstring_mapped( - num_spatial_orbitals=self.num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=converter, - match_convert=False, - ) - sector = ElectronicStructureProblem._pick_sector(z2_symmetries, hf_bitstr) - - return sector - def _symmetry_sector_locator( self, - z2_symmetries: OpflowZ2Symmetries | Z2Symmetries, - mapper: QubitConverter | QubitMapper, + z2_symmetries: Z2Symmetries, + mapper: QubitMapper, ) -> Optional[List[int]]: """Given the detected Z2Symmetries this determines the correct sector of the tapered operator that contains the ground state we need and returns that information. Args: z2_symmetries: the z2 symmetries object. - mapper: the ``QubitMapper`` or ``QubitConverter`` instance (use of the latter is - deprecated) used for the operator conversion that symmetries are to be determined - for. + mapper: the ``QubitMapper`` used for the operator conversion that symmetries are to be + determined for. Raises: QiskitNatureError: if the :attr:`num_particles` attribute is ``None``. @@ -358,13 +298,6 @@ def _symmetry_sector_locator( "ElectronicStructureProblem.num_particles or disable the use of Z2Symmetries to " "fix this." ) - if isinstance(mapper, QubitConverter): - warn_deprecated_type( - "0.6.0", - argument_name="mapper", - old_type="QubitConverter", - new_type="QubitMapper", - ) num_particles = self.num_particles if not isinstance(num_particles, tuple): @@ -380,9 +313,7 @@ def _symmetry_sector_locator( return sector @staticmethod - def _pick_sector( - z2_symmetries: OpflowZ2Symmetries | Z2Symmetries, hf_str: List[bool] - ) -> List[int]: + def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: List[bool]) -> List[int]: # Finding all the symmetries using the find_Z2_symmetries: taper_coeff: List[int] = [] for sym in z2_symmetries.symmetries: diff --git a/qiskit_nature/second_q/transformers/basis_transformer.py b/qiskit_nature/second_q/transformers/basis_transformer.py index 452921ee4..2ab335be0 100644 --- a/qiskit_nature/second_q/transformers/basis_transformer.py +++ b/qiskit_nature/second_q/transformers/basis_transformer.py @@ -21,7 +21,7 @@ from qiskit_nature.exceptions import QiskitNatureError from qiskit_nature.second_q.hamiltonians import ElectronicEnergy, Hamiltonian -from qiskit_nature.second_q.operators import ElectronicIntegrals, PolynomialTensor, Tensor +from qiskit_nature.second_q.operators import ElectronicIntegrals, PolynomialTensor from qiskit_nature.second_q.problems import BaseProblem, ElectronicBasis, ElectronicStructureProblem from qiskit_nature.second_q.properties import ( AngularMomentum, @@ -183,10 +183,6 @@ def transform_electronic_integrals(self, integrals: ElectronicIntegrals) -> Elec two_body_aa = integrals.alpha.get("++--", None) if two_body_aa is not None: - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - if not isinstance(two_body_aa, Tensor): - two_body_aa = Tensor(two_body_aa) - prsq = "".join(two_body_aa._reverse_label_template(prsq)) iklj = "".join(two_body_aa._reverse_label_template(iklj)) diff --git a/qiskit_nature/settings.py b/qiskit_nature/settings.py index e0cd633f9..bf0691a3f 100644 --- a/qiskit_nature/settings.py +++ b/qiskit_nature/settings.py @@ -14,67 +14,20 @@ from __future__ import annotations -import warnings - - -class ListAuxOpsDeprecationWarning(DeprecationWarning): - """Deprecation Category for List-based aux. operators.""" - - pass - - -class PauliSumOpDeprecationWarning(DeprecationWarning): - """Deprecation Category for PauliSumOp.""" - - pass - class QiskitNatureSettings: """Global settings for Qiskit Nature.""" def __init__(self) -> None: self._optimize_einsum = True - self._deprecation_shown: set[str] = set() - self._tensor_unwrapping = True - self._use_pauli_sum_op: bool = True - self._use_symmetry_reduced_integrals: bool = False - - @property - def use_pauli_sum_op(self) -> bool: - """Return whether ``PauliSumOp`` or ``SparsePauliOp`` should be returned on methods.""" - if self._use_pauli_sum_op and "PauliSumOp" not in self._deprecation_shown: - warnings.filterwarnings("default", category=PauliSumOpDeprecationWarning) - warnings.warn( - PauliSumOpDeprecationWarning( - "PauliSumOp is deprecated as of version 0.6.0 and support for " - "them will be removed no sooner than 3 months after the release. Instead, use " - "SparsePauliOp. You can switch to SparsePauliOp " - "immediately, by setting `qiskit_nature.settings.use_pauli_sum_op` to `False`." - ), - stacklevel=3, - ) - warnings.filterwarnings("ignore", category=PauliSumOpDeprecationWarning) - self._deprecation_shown.add("PauliSumOp") - - return self._use_pauli_sum_op - @use_pauli_sum_op.setter - def use_pauli_sum_op(self, pauli_sum_op: bool) -> None: - """Set whether ``PauliSumOp`` or ``SparsePauliOp`` should be returned on methods.""" - if pauli_sum_op and "PauliSumOp" not in self._deprecation_shown: - warnings.filterwarnings("default", category=PauliSumOpDeprecationWarning) - warnings.warn( - PauliSumOpDeprecationWarning( - "PauliSumOp is deprecated as of version 0.6.0 and support for " - "them will be removed no sooner than 3 months after the release. Instead, use " - "SparsePauliOp. You can switch to SparsePauliOp " - "immediately, by setting `qiskit_nature.settings.use_pauli_sum_op` to `False`." - ), - stacklevel=3, - ) - warnings.filterwarnings("ignore", category=PauliSumOpDeprecationWarning) - self._deprecation_shown.add("PauliSumOp") - self._use_pauli_sum_op = pauli_sum_op + # The set below can be used to handle deprecation warnings for various settings. + # It exists to keep track of which deprecation warnings were already shown in order to avoid + # spamming the user with the same warning over and over. + # To use it, simply add a unique string to this set after having raised some warning and + # only raise the warning in the first place, if this unique string is not already part of + # this set. + self._deprecation_shown: set[str] = set() @property def optimize_einsum(self) -> bool: @@ -94,99 +47,5 @@ def optimize_einsum(self, optimize_einsum: bool) -> None: """ self._optimize_einsum = optimize_einsum - @property - def use_symmetry_reduced_integrals(self) -> bool: - """Whether or not to use symmetry-reduced integrals whenever possible. - - This setting affects whether the drivers and formats should attempt to use the utilities - provided by the :mod:`~qiskit_nature.second_q.operators.symmetric_two_body` module. - Setting this to ``True`` will very likely result in lower memory consumptions at runtime. - """ - if ( - not self._use_symmetry_reduced_integrals - and "SymmetricTwoBodyIntegrals" not in self._deprecation_shown - ): - warnings.warn( - DeprecationWarning( - "As of version 0.6.0 the current default-value `False` of " - "`qiskit_nature.settings.use_symmetry_reduced_integrals` is deprecated. " - "No sooner than 3 months after this release, this default value will be " - "switched to `True`. You can change the value of this setting yourself already." - ), - stacklevel=3, - ) - self._deprecation_shown.add("SymmetricTwoBodyIntegrals") - - return self._use_symmetry_reduced_integrals - - @use_symmetry_reduced_integrals.setter - def use_symmetry_reduced_integrals(self, use_symmetry_reduced_integrals: bool) -> None: - if ( - not use_symmetry_reduced_integrals - and "SymmetricTwoBodyIntegrals" not in self._deprecation_shown - ): - warnings.warn( - DeprecationWarning( - "As of version 0.6.0 the current default-value `False` of " - "`qiskit_nature.settings.use_symmetry_reduced_integrals` is deprecated. " - "No sooner than 3 months after this release, this default value will be " - "switched to `True`. You can change the value of this setting yourself already." - ), - stacklevel=3, - ) - self._deprecation_shown.add("SymmetricTwoBodyIntegrals") - - self._use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - - @property - def tensor_unwrapping(self) -> bool: - """Returns whether tensors inside the :class:`~.PolynomialTensor` should be unwrapped. - - More specifically, if this setting is disabled, the tensor objects stored in a - :class:`~qiskit_nature.second_q.operators.PolynomialTensor` will be of type - :class:`~qiskit_nature.second_q.operators.Tensor` when accessed via ``__getitem__``. - Otherwise, they will appear as the nested array object which may be of type - ``numpy.ndarray``, ``sparse.SparseArray`` or a plain ``Number``. - """ - if self._tensor_unwrapping and "Tensor" not in self._deprecation_shown: - warnings.warn( - DeprecationWarning( - "As of version 0.6.0 the return of unwrapped tensors in the " - "`PolynomialTensor.__getitem__` method is deprecated. No sooner than 3 months " - "after this release, arrays will always be returned as `Tensor` objects. You " - "can switch to the new objects immediately, by setting " - "`qiskit_nature.settings.tensor_unwrapping` to `False`." - ), - stacklevel=3, - ) - self._deprecation_shown.add("Tensor") - - return self._tensor_unwrapping - - @tensor_unwrapping.setter - def tensor_unwrapping(self, tensor_unwrapping: bool) -> None: - """Returns whether tensors inside the :class:`~.PolynomialTensor` should be unwrapped. - - More specifically, if this setting is disabled, the tensor objects stored in a - :class:`~qiskit_nature.second_q.operators.PolynomialTensor` will be of type - :class:`~qiskit_nature.second_q.operators.Tensor` when accessed via ``__getitem__``. - Otherwise, they will appear as the nested array object which may be of type - ``numpy.ndarray``, ``sparse.SparseArray`` or a plain ``Number``. - """ - if tensor_unwrapping and "Tensor" not in self._deprecation_shown: - warnings.warn( - DeprecationWarning( - "As of version 0.6.0 the return of unwrapped tensors in the " - "`PolynomialTensor.__getitem__` method is deprecated. No sooner than 3 months " - "after this release, arrays will always be returned as `Tensor` objects. You " - "can switch to the new objects immediately, by setting " - "`qiskit_nature.settings.tensor_unwrapping` to `False`." - ), - stacklevel=3, - ) - self._deprecation_shown.add("Tensor") - - self._tensor_unwrapping = tensor_unwrapping - settings = QiskitNatureSettings() diff --git a/releasenotes/notes/0.7-prelude-169d294985170fd5.yaml b/releasenotes/notes/0.7-prelude-169d294985170fd5.yaml new file mode 100644 index 000000000..61c04ce35 --- /dev/null +++ b/releasenotes/notes/0.7-prelude-169d294985170fd5.yaml @@ -0,0 +1,19 @@ +--- +prelude: > + Qiskit Nature has been migrated from the `Qiskit Github organization `_ to + the `qiskit-community Github organization `_. + This reflects the fact, that Qiskit Nature is `not` part of Qiskit and is `not` maintained by IBM Quantum. + Rather, it is a community-driven project which builds upon Qiskit. + + To reflect this significant change and because we are onboarding additional codeowners and maintainers, + with this version (0.7) we have decided to *remove all deprecated* code, regardless of time of its deprecation. + This ensures that the new members of the development team do not have a large bulk of legacy code to maintain, + which they do not know a lot about and for which they were not involved in the discussions that led to its + deprecation. + + This can mean one of two things for you as the end-user: + + #. Nothing, if you already migrated your code and no longer rely on any deprecated features. + + #. Otherwise, you need to migrate your code immediately. If you cannot do that, or want to continue using some + of the features that were removed, you should pin your version of Qiskit Nature to 0.6 diff --git a/releasenotes/notes/removes-legacy-stack-0a98952b6c174aaa.yaml b/releasenotes/notes/removes-legacy-stack-0a98952b6c174aaa.yaml deleted file mode 100644 index d4fdfa919..000000000 --- a/releasenotes/notes/removes-legacy-stack-0a98952b6c174aaa.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -upgrade: - - | - Removes the entire legacy code stack which was deprecated in version 0.5. - Follow the `migration guides `_ - to upgrade your code if you have not done so already. - - | - Removes the deprecated and now unused ``qiskit_nature.settings.dict_aux_operators`` setting. -deprecations: - - | - Deprecates the :meth:`.QCSchema.from_legacy_hdf5` method without a replacement. diff --git a/test/__init__.py b/test/__init__.py index c4003cec0..3a9c8d396 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -12,6 +12,6 @@ """ Qiskit Nature test packages """ -from .nature_test_case import QiskitNatureTestCase, QiskitNatureDeprecatedTestCase +from .nature_test_case import QiskitNatureTestCase -__all__ = ["QiskitNatureTestCase", "QiskitNatureDeprecatedTestCase"] +__all__ = ["QiskitNatureTestCase"] diff --git a/test/nature_test_case.py b/test/nature_test_case.py index a193759ed..7c70b3672 100644 --- a/test/nature_test_case.py +++ b/test/nature_test_case.py @@ -22,16 +22,8 @@ import time import math from qiskit.quantum_info import SparsePauliOp -from qiskit_nature.deprecation import NatureDeprecationWarning -# disable unit tests NatureDeprecationWarning warnings on imports -warnings.filterwarnings("ignore", category=NatureDeprecationWarning) - - -# disable deprecation warnings that can cause log output overflow # pylint: disable=unused-argument - - def _noop(*args, **kargs): pass @@ -48,8 +40,6 @@ class QiskitNatureTestCase(unittest.TestCase, ABC): def setUp(self) -> None: warnings.filterwarnings("default", category=DeprecationWarning) - # disable unit tests NatureDeprecationWarning warnings previously reset - warnings.filterwarnings("ignore", category=NatureDeprecationWarning) warnings.filterwarnings("ignore", category=DeprecationWarning, module="pyscf") warnings.filterwarnings(action="ignore", category=DeprecationWarning, module=".*drivers*") warnings.filterwarnings( @@ -154,23 +144,3 @@ def assertNotEqualSparsePauliOp( if len(message) > 0: msg = f"{msg} : {message}" raise AssertionError(msg) - - -class QiskitNatureDeprecatedTestCase(QiskitNatureTestCase): - """Nature Deprecated Test Case. - Used for tests that need to suppress deprecation messages. - """ - - def setUp(self) -> None: - super().setUp() - # disable deprecation warnings - warnings.filterwarnings("ignore", category=PendingDeprecationWarning) - warnings.filterwarnings("ignore", category=DeprecationWarning) - warnings.filterwarnings("ignore", category=NatureDeprecationWarning) - - def tearDown(self) -> None: - # enable deprecation warnings - warnings.filterwarnings("default", category=PendingDeprecationWarning) - warnings.filterwarnings("default", category=DeprecationWarning) - warnings.filterwarnings("default", category=NatureDeprecationWarning) - super().tearDown() diff --git a/test/runtime/__init__.py b/test/runtime/__init__.py deleted file mode 100644 index a160dc1b7..000000000 --- a/test/runtime/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2018, 2023. -# -# 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. - -"""Runtime test packages.""" diff --git a/test/runtime/fake_vqeruntime.py b/test/runtime/fake_vqeruntime.py deleted file mode 100644 index 4045e8453..000000000 --- a/test/runtime/fake_vqeruntime.py +++ /dev/null @@ -1,101 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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. - -"""Fake runtime provider and VQE runtime.""" - -from typing import Dict, Any -import numpy as np -from qiskit.algorithms.optimizers import Optimizer -from qiskit.circuit import QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.providers import Provider -from qiskit_nature.runtime import VQERuntimeResult - - -class FakeVQEJob: - """A fake job for unit tests.""" - - def result(self) -> Dict[str, Any]: - """Return a VQE result.""" - result = VQERuntimeResult() - - serialized_result = { - "optimizer_evals": result.optimizer_evals, - "optimizer_time": result.optimizer_time, - "optimal_value": result.optimal_value, - "optimal_point": result.optimal_point, - "optimal_parameters": result.optimal_parameters, - "cost_function_evals": result.cost_function_evals, - "eigenstate": result.eigenstate, - "eigenvalue": result.eigenvalue, - "aux_operator_eigenvalues": result.aux_operator_eigenvalues, - "optimizer_history": result.optimizer_history, - } - return serialized_result - - def job_id(self) -> str: - """Return a fake job ID.""" - return "c2985khdm6upobbnmll0" - - -class FakeVQERuntime: - """A fake VQE runtime for unit tests.""" - - def run(self, program_id, inputs, options, callback=None): - """Run the fake program. Checks the input types.""" - - if program_id != "vqe": - raise ValueError("program_id is not vqe.") - - allowed_inputs = { - "operator": PauliSumOp, - "aux_operators": (list, dict, type(None)), - "ansatz": QuantumCircuit, - "initial_point": (np.ndarray, str), - "optimizer": (Optimizer, dict), - "shots": int, - "measurement_error_mitigation": bool, - "store_intermediate": bool, - } - for arg, value in inputs.items(): - if not isinstance(value, allowed_inputs[arg]): - raise ValueError(f"{arg} does not have the right type: {allowed_inputs[arg]}") - - allowed_options = {"backend_name": str} - for arg, value in options.items(): - if not isinstance(value, allowed_options[arg]): - raise ValueError(f"{arg} does not have the right type: {allowed_inputs[arg]}") - - if callback is not None: - try: - fake_job_id = "c2985khdm6upobbnmll0" - fake_data = [3, np.arange(10), 1.3] - _ = callback(fake_job_id, fake_data) - except Exception as exc: - raise ValueError("Callback failed") from exc - - return FakeVQEJob() - - -class FakeRuntimeProvider(Provider): - """A fake runtime provider for unit tests.""" - - def has_service(self, service): - """Check if a service is available.""" - if service == "runtime": - return True - return False - - @property - def runtime(self) -> FakeVQERuntime: - """Return the runtime.""" - return FakeVQERuntime() diff --git a/test/runtime/test_vqeprogram.py b/test/runtime/test_vqeprogram.py deleted file mode 100644 index 2c7b74baf..000000000 --- a/test/runtime/test_vqeprogram.py +++ /dev/null @@ -1,108 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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 the VQE program.""" - -from test import QiskitNatureDeprecatedTestCase -from test.runtime.fake_vqeruntime import FakeRuntimeProvider - -import unittest -import warnings -from ddt import ddt, data -import numpy as np -from qiskit.providers.basicaer import QasmSimulatorPy -from qiskit.providers.fake_provider import FakeArmonk, FakeArmonkV2 -from qiskit.algorithms import VQEResult -from qiskit.algorithms.optimizers import SPSA -from qiskit.circuit.library import RealAmplitudes -from qiskit.opflow import I, Z - -from qiskit_nature.second_q.algorithms.ground_state_solvers import GroundStateEigensolver -from qiskit_nature.second_q.mappers import JordanWignerMapper -from qiskit_nature.runtime import VQEClient, VQERuntimeResult - - -@ddt -class TestVQEClient(QiskitNatureDeprecatedTestCase): - """Test the VQE program.""" - - def get_standard_program(self): - """Get a standard VQEClient and operator to find the ground state of.""" - circuit = RealAmplitudes(3) - operator = Z ^ I ^ Z - initial_point = np.random.random(circuit.num_parameters) - backend = QasmSimulatorPy() - - provider = FakeRuntimeProvider() - vqe_cls = VQEClient - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - vqe = vqe_cls( - ansatz=circuit, - optimizer=SPSA(), - initial_point=initial_point, - backend=backend, - provider=provider, - ) - - return vqe, operator - - @data({"name": "SPSA", "maxiter": 100}, SPSA(maxiter=100)) - def test_standard_case(self, optimizer): - """Test a standard use case.""" - vqe, operator = self.get_standard_program() - vqe.optimizer = optimizer - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - result = vqe.compute_minimum_eigenvalue(operator) - - self.assertIsInstance(result, VQEResult) - self.assertIsInstance(result, VQERuntimeResult) - - def test_supports_aux_ops(self): - """Test the VQEClient says it supports aux operators.""" - vqe, _ = self.get_standard_program() - self.assertTrue(vqe.supports_aux_operators) - - def test_gss_supports_aux_ops(self): - """Test the VQEClient yields a GroundStateEigensolver that supports aux operators.""" - vqe, _ = self.get_standard_program() - qubit_mapper = JordanWignerMapper() - gss = GroundStateEigensolver(qubit_mapper, vqe) - self.assertTrue(gss.supports_aux_operators) - - @data(FakeArmonk, FakeArmonkV2) - def test_v1_and_v2_compatibility(self, backend_cls): - """Test the VQE client is compatible both with V1 and V2 backends. - - Regression test of github.com/Qiskit/qiskit-nature/issues/1100. - """ - provider = FakeRuntimeProvider() - backend = backend_cls() - - circuit = RealAmplitudes(1, reps=0) - operator = Z - initial_point = np.array([0]) - optimizer = SPSA(maxiter=1, perturbation=0.1, learning_rate=0.1) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - vqe = VQEClient(circuit, optimizer, initial_point, provider, backend) - result = vqe.compute_minimum_eigenvalue(operator) - - self.assertIsInstance(result, VQERuntimeResult) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/second_q/algorithms/excited_state_solvers/eigensolver_factories/__init__.py b/test/second_q/algorithms/excited_state_solvers/eigensolver_factories/__init__.py deleted file mode 100644 index def83287c..000000000 --- a/test/second_q/algorithms/excited_state_solvers/eigensolver_factories/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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. diff --git a/test/second_q/algorithms/excited_state_solvers/eigensolver_factories/test_numpy_eigensolver_factory.py b/test/second_q/algorithms/excited_state_solvers/eigensolver_factories/test_numpy_eigensolver_factory.py deleted file mode 100644 index dd25f451a..000000000 --- a/test/second_q/algorithms/excited_state_solvers/eigensolver_factories/test_numpy_eigensolver_factory.py +++ /dev/null @@ -1,86 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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 NumPyMinimumEigensolver Factory """ -import unittest -import warnings -from test import QiskitNatureTestCase -import numpy as np - -from qiskit.algorithms.eigensolvers import NumPyEigensolver -from qiskit_nature.units import DistanceUnit -from qiskit_nature.second_q.algorithms import NumPyEigensolverFactory -from qiskit_nature.second_q.drivers import PySCFDriver -import qiskit_nature.optionals as _optionals - - -class TestNumPyEigensolverFactory(QiskitNatureTestCase): - """Test NumPyMinimumEigensovler Factory""" - - # NOTE: The actual usage of this class is mostly tested in combination with the ground-state - # eigensolvers (one module above). - - @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") - def setUp(self): - super().setUp() - - self.driver = PySCFDriver( - atom="H .0 .0 .0; H .0 .0 0.75", - unit=DistanceUnit.ANGSTROM, - charge=0, - spin=0, - basis="sto3g", - ) - - self.electronic_structure_problem = self.driver.run() - - # pylint: disable=unused-argument - def filter_criterion(eigenstate, eigenvalue, aux_values): - return np.isclose(aux_values["ParticleNumber"][0], 2.0) - - self.k = 99 - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - self._numpy_eigensolver_factory = NumPyEigensolverFactory( - filter_criterion=filter_criterion, k=self.k - ) - - def test_setters_getters(self): - """Test Getter/Setter""" - - # filter_criterion - self.assertIsNotNone(self._numpy_eigensolver_factory.filter_criterion) - - # pylint: disable=unused-argument - def filter_criterion(eigenstate, eigenvalue, aux_values): - return np.isclose(aux_values["ParticleNumber"][0], 3.0) - - self._numpy_eigensolver_factory.filter_criterion = filter_criterion - self.assertEqual(self._numpy_eigensolver_factory.filter_criterion, filter_criterion) - - # k - self.assertEqual(self._numpy_eigensolver_factory.k, self.k) - self._numpy_eigensolver_factory.k = 100 - self.assertEqual(self._numpy_eigensolver_factory.k, 100) - - # use_default_filter_criterion - self.assertFalse(self._numpy_eigensolver_factory.use_default_filter_criterion) - self._numpy_eigensolver_factory.use_default_filter_criterion = True - self.assertTrue(self._numpy_eigensolver_factory.use_default_filter_criterion) - # get_solver - solver = self._numpy_eigensolver_factory.get_solver(self.electronic_structure_problem) - self.assertIsInstance(solver, NumPyEigensolver) - self.assertEqual(solver.k, 100) - self.assertEqual(solver.filter_criterion, filter_criterion) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/second_q/algorithms/excited_state_solvers/test_bosonic_esc_calculation.py b/test/second_q/algorithms/excited_state_solvers/test_bosonic_esc_calculation.py index 2969c85ba..ff21e32f0 100644 --- a/test/second_q/algorithms/excited_state_solvers/test_bosonic_esc_calculation.py +++ b/test/second_q/algorithms/excited_state_solvers/test_bosonic_esc_calculation.py @@ -15,28 +15,22 @@ import contextlib import io import unittest -import warnings from test import QiskitNatureTestCase import numpy as np +from qiskit.algorithms.eigensolvers import NumPyEigensolver +from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver, VQE from qiskit.algorithms.optimizers import COBYLA from qiskit.primitives import Estimator from qiskit.utils import algorithm_globals -from qiskit_nature.second_q.algorithms import ( - GroundStateEigensolver, - NumPyMinimumEigensolverFactory, - VQEUVCCFactory, - QEOM, - ExcitedStatesEigensolver, - NumPyEigensolverFactory, -) -from qiskit_nature.second_q.circuit.library import UVCCSD +from qiskit_nature.second_q.algorithms import GroundStateEigensolver, QEOM, ExcitedStatesEigensolver +from qiskit_nature.second_q.circuit.library import VSCF, UVCCSD from qiskit_nature.second_q.formats.watson import WatsonHamiltonian from qiskit_nature.second_q.formats.watson_translator import watson_to_problem -from qiskit_nature.second_q.mappers import DirectMapper, QubitConverter +from qiskit_nature.second_q.mappers import DirectMapper from qiskit_nature.second_q.problems import HarmonicBasis import qiskit_nature.optionals as _optionals @@ -55,7 +49,7 @@ def setUp(self): 5819.76975784, ] - self.qubit_converter = QubitConverter(DirectMapper()) + self.qubit_mapper = DirectMapper() import sparse as sp # pylint: disable=import-error @@ -108,46 +102,49 @@ def _assert_energies(self, computed, references, *, places=4): def test_numpy_mes(self): """Test with NumPyMinimumEigensolver""" estimator = Estimator() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory(use_default_filter_criterion=True) - gsc = GroundStateEigensolver(self.qubit_converter, solver) + solver = NumPyMinimumEigensolver() + solver.filter_criterion = self.vibrational_problem.get_default_filter_criterion() + gsc = GroundStateEigensolver(self.qubit_mapper, solver) esc = QEOM(gsc, estimator, "sd") results = esc.solve(self.vibrational_problem) self._assert_energies(results.computed_vibrational_energies, self.reference_energies) - def test_numpy_factory(self): + def test_numpy(self): """Test with NumPyEigensolver""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyEigensolverFactory(use_default_filter_criterion=True) - esc = ExcitedStatesEigensolver(self.qubit_converter, solver) + solver = NumPyEigensolver(k=4) + solver.filter_criterion = self.vibrational_problem.get_default_filter_criterion() + esc = ExcitedStatesEigensolver(self.qubit_mapper, solver) results = esc.solve(self.vibrational_problem) self._assert_energies(results.computed_vibrational_energies, self.reference_energies) - def test_vqe_uvccsd_factory(self): + def test_vqe_uvccsd(self): """Test with VQE plus UVCCSD""" optimizer = COBYLA(maxiter=5000) estimator = Estimator() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUVCCFactory(estimator, UVCCSD(), optimizer) - gsc = GroundStateEigensolver(self.qubit_converter, solver) + initial_state = VSCF(self.vibrational_problem.num_modals, self.qubit_mapper) + ansatz = UVCCSD( + self.vibrational_problem.num_modals, self.qubit_mapper, initial_state=initial_state + ) + solver = VQE(estimator, ansatz, optimizer) + solver.initial_point = [0] * ansatz.num_parameters + gsc = GroundStateEigensolver(self.qubit_mapper, solver) esc = QEOM(gsc, estimator, "sd") results = esc.solve(self.vibrational_problem) self._assert_energies( results.computed_vibrational_energies, self.reference_energies, places=0 ) - def test_vqe_uvcc_factory_with_user_initial_point(self): - """Test VQEUVCCFactory when using it with a user defined initial point.""" + def test_vqe_uvcc_with_user_initial_point(self): + """Test when using a user defined initial point.""" initial_point = np.asarray([-7.35250290e-05, -9.73079292e-02, -5.43346282e-05]) estimator = Estimator() optimizer = COBYLA(maxiter=1) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUVCCFactory(estimator, UVCCSD(), optimizer, initial_point=initial_point) - gsc = GroundStateEigensolver(self.qubit_converter, solver) + initial_state = VSCF(self.vibrational_problem.num_modals, self.qubit_mapper) + ansatz = UVCCSD( + self.vibrational_problem.num_modals, self.qubit_mapper, initial_state=initial_state + ) + solver = VQE(estimator, ansatz, optimizer, initial_point=initial_point) + gsc = GroundStateEigensolver(self.qubit_mapper, solver) esc = QEOM(gsc, estimator, "sd") results = esc.solve(self.vibrational_problem) np.testing.assert_array_almost_equal( @@ -162,11 +159,13 @@ def cb_callback(nfev, parameters, energy, stddev): estimator = Estimator() optimizer = COBYLA(maxiter=5000) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUVCCFactory(estimator, UVCCSD(), optimizer, callback=cb_callback) - gsc = GroundStateEigensolver(self.qubit_converter, solver) + initial_state = VSCF(self.vibrational_problem.num_modals, self.qubit_mapper) + ansatz = UVCCSD( + self.vibrational_problem.num_modals, self.qubit_mapper, initial_state=initial_state + ) + solver = VQE(estimator, ansatz, optimizer, callback=cb_callback) + solver.initial_point = [0] * ansatz.num_parameters + gsc = GroundStateEigensolver(self.qubit_mapper, solver) esc = QEOM(gsc, estimator, "sd") with contextlib.redirect_stdout(io.StringIO()) as out: results = esc.solve(self.vibrational_problem) diff --git a/test/second_q/algorithms/excited_state_solvers/test_excited_states_solvers.py b/test/second_q/algorithms/excited_state_solvers/test_excited_states_solvers.py index 35af5cd8e..2f7883af0 100644 --- a/test/second_q/algorithms/excited_state_solvers/test_excited_states_solvers.py +++ b/test/second_q/algorithms/excited_state_solvers/test_excited_states_solvers.py @@ -15,38 +15,29 @@ from __future__ import annotations import unittest -import warnings from test import QiskitNatureTestCase from ddt import ddt, named_data import numpy as np from qiskit.algorithms.eigensolvers import NumPyEigensolver -from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver +from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver, VQE from qiskit.algorithms.optimizers import SLSQP from qiskit.primitives import Estimator from qiskit.utils import algorithm_globals from qiskit_nature.units import DistanceUnit -from qiskit_nature.second_q.circuit.library import UCCSD +from qiskit_nature.second_q.circuit.library import HartreeFock, UCCSD from qiskit_nature.second_q.transformers import ActiveSpaceTransformer from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import ( - BravyiKitaevMapper, JordanWignerMapper, ParityMapper, QubitMapper, TaperedQubitMapper, ) -from qiskit_nature.second_q.mappers import QubitConverter -from qiskit_nature.second_q.algorithms import ( - GroundStateEigensolver, - VQEUCCFactory, - NumPyEigensolverFactory, - ExcitedStatesEigensolver, - QEOM, -) +from qiskit_nature.second_q.algorithms import GroundStateEigensolver, ExcitedStatesEigensolver, QEOM import qiskit_nature.optionals as _optionals @@ -73,11 +64,10 @@ def setUp(self): -1.8427016 + 1.5969296, ] self.mapper = JordanWignerMapper() - self.qubit_converter = QubitConverter(self.mapper) self.electronic_structure_problem = self.driver.run() self.num_particles = self.electronic_structure_problem.num_particles - solver = NumPyEigensolver() + solver = NumPyEigensolver(k=10) self.ref = solver def _assert_energies(self, computed, references, *, places=4): @@ -91,11 +81,20 @@ def _assert_energies(self, computed, references, *, places=4): with self.subTest(f"{i}. excited state"): self.assertAlmostEqual(computed[i], references[i], places=places) - def _compute_and_assert_qeom_energies(self, mapper: QubitConverter | QubitMapper): + def _compute_and_assert_qeom_energies(self, mapper: QubitMapper): estimator = Estimator() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(estimator, UCCSD(), SLSQP()) + ansatz = UCCSD( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + mapper, + initial_state=HartreeFock( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + mapper, + ), + ) + solver = VQE(estimator, ansatz, SLSQP()) + solver.initial_point = [0] * ansatz.num_parameters gsc = GroundStateEigensolver(mapper, solver) esc = QEOM(gsc, estimator, "sd") results = esc.solve(self.electronic_structure_problem) @@ -104,35 +103,18 @@ def _compute_and_assert_qeom_energies(self, mapper: QubitConverter | QubitMapper def test_numpy_mes(self): """Test NumPyMinimumEigenSolver with QEOM""" solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(self.qubit_converter, solver) + gsc = GroundStateEigensolver(self.mapper, solver) esc = QEOM(gsc, Estimator(), "sd") results = esc.solve(self.electronic_structure_problem) self._assert_energies(results.computed_energies, self.reference_energies) - @named_data( - ["JWM", QubitConverter(JordanWignerMapper())], - ["JWM_Z2", QubitConverter(JordanWignerMapper(), z2symmetry_reduction="auto")], - ["PM", QubitConverter(ParityMapper())], - ["PM_TQR", QubitConverter(ParityMapper(), two_qubit_reduction=True)], - ["PM_Z2", QubitConverter(ParityMapper(), z2symmetry_reduction="auto")], - [ - "PM_TQR_Z2", - QubitConverter(ParityMapper(), two_qubit_reduction=True, z2symmetry_reduction="auto"), - ], - ["BKM", QubitConverter(BravyiKitaevMapper())], - ["BKM_Z2", QubitConverter(BravyiKitaevMapper(), z2symmetry_reduction="auto")], - ) - def test_solve_with_vqe_mes(self, converter: QubitConverter): - """Test QEOM with VQEUCCFactory and various QubitConverter""" - self._compute_and_assert_qeom_energies(converter) - @named_data( ["JWM", JordanWignerMapper()], ["PM", ParityMapper()], ["PM_TQR", ParityMapper(num_particles=(1, 1))], ) def test_solve_with_vqe_mes_mapper(self, mapper: QubitMapper): - """Test QEOM with VQEUCCFactory and various QubitMapper""" + """Test QEOM with VQE + UCCSD and various QubitMapper""" self._compute_and_assert_qeom_energies(mapper) @named_data( @@ -144,23 +126,22 @@ def test_solve_with_vqe_mes_mapper(self, mapper: QubitMapper): ["PM_TQR_Z2", lambda n, esp: esp.get_tapered_mapper(ParityMapper(n))], ) def test_solve_with_vqe_mes_taperedmapper(self, tapered_mapper_creator): - """Test QEOM with VQEUCCFactory and various QubitMapper""" + """Test QEOM with VQE + UCCSD and various QubitMapper""" tapered_mapper = tapered_mapper_creator( self.num_particles, self.electronic_structure_problem ) self._compute_and_assert_qeom_energies(tapered_mapper) - def test_numpy_factory(self): - """Test NumPyEigenSolverFactory with ExcitedStatesEigensolver""" + def test_numpy(self): + """Test NumPy with ExcitedStatesEigensolver""" # pylint: disable=unused-argument def filter_criterion(eigenstate, eigenvalue, aux_values): return np.isclose(aux_values["ParticleNumber"][0], 2.0) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyEigensolverFactory(filter_criterion=filter_criterion) - esc = ExcitedStatesEigensolver(self.qubit_converter, solver) + solver = NumPyEigensolver(k=10) + solver.filter_criterion = filter_criterion + esc = ExcitedStatesEigensolver(self.mapper, solver) results = esc.solve(self.electronic_structure_problem) # filter duplicates from list @@ -172,8 +153,7 @@ def filter_criterion(eigenstate, eigenvalue, aux_values): self._assert_energies(computed_energies, self.reference_energies) def test_custom_filter_criterion(self): - """Test NumPyEigenSolverFactory with ExcitedStatesEigensolver + Custom filter criterion - for doublet states""" + """Test NumPy with ExcitedStatesEigensolver + Custom filter criterion for doublet states""" driver = PySCFDriver( atom="Be .0 .0 .0; H .0 .0 0.75", @@ -186,10 +166,10 @@ def test_custom_filter_criterion(self): transformer = ActiveSpaceTransformer((1, 2), 4) # We define an ActiveSpaceTransformer to reduce the duration of this test example. - converter = QubitConverter(JordanWignerMapper(), z2symmetry_reduction="auto") - esp = transformer.transform(driver.run()) + mapper = esp.get_tapered_mapper(JordanWignerMapper()) + expected_spin = 0.75 # Doublet states expected_num_electrons = 3 # 1 alpha electron + 2 beta electrons @@ -202,10 +182,9 @@ def custom_filter_criterion(eigenstate, eigenvalue, aux_values): num_particles_aux, expected_num_electrons ) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyEigensolverFactory(filter_criterion=custom_filter_criterion) - esc = ExcitedStatesEigensolver(converter, solver) + solver = NumPyEigensolver(k=100) + solver.filter_criterion = custom_filter_criterion + esc = ExcitedStatesEigensolver(mapper, solver) results = esc.solve(esp) # filter duplicates from list @@ -225,70 +204,6 @@ def custom_filter_criterion(eigenstate, eigenvalue, aux_values): self._assert_energies(computed_energies, ref_energies, places=3) - @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") - def test_solver_compatibility_with_mappers(self): - """Test that solvers can use both QubitConverter and QubitMapper""" - - # pylint: disable=unused-argument - def filter_criterion(eigenstate, eigenvalue, aux_values): - return np.isclose(aux_values["ParticleNumber"][0], 2.0) - - with self.subTest("Excited states solver with qubit converter"): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyEigensolverFactory(filter_criterion=filter_criterion) - esc_converter = ExcitedStatesEigensolver(self.qubit_converter, solver) - results_converter = esc_converter.solve(self.electronic_structure_problem) - computed_energies_converter = [results_converter.computed_energies[0]] - # filter duplicates from list - for comp_energy in results_converter.computed_energies[1:]: - if not np.isclose(comp_energy, computed_energies_converter[-1]): - computed_energies_converter.append(comp_energy) - self._assert_energies(computed_energies_converter, self.reference_energies) - - with self.subTest("Excited states solver with qubit mapper"): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyEigensolverFactory(filter_criterion=filter_criterion) - esc_mapper = ExcitedStatesEigensolver(self.mapper, solver) - results_mapper = esc_mapper.solve(self.electronic_structure_problem) - # filter duplicates from list - computed_energies_mapper = [results_mapper.computed_energies[0]] - for comp_energy in results_mapper.computed_energies[1:]: - if not np.isclose(comp_energy, computed_energies_mapper[-1]): - computed_energies_mapper.append(comp_energy) - self._assert_energies(computed_energies_mapper, self.reference_energies) - - with self.subTest("QEOM with qubit converter"): - estimator = Estimator() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(estimator, UCCSD(), SLSQP()) - gsc_converter = GroundStateEigensolver(self.qubit_converter, solver) - esc_converter = QEOM(gsc_converter, estimator, "sd") - results_converter = esc_converter.solve(self.electronic_structure_problem) - # filter duplicates from list - computed_energies_converter = [results_converter.computed_energies[0]] - for comp_energy in results_converter.computed_energies[1:]: - if not np.isclose(comp_energy, computed_energies_converter[-1]): - computed_energies_converter.append(comp_energy) - self._assert_energies(computed_energies_converter, self.reference_energies) - - with self.subTest("QEOM with qubit mapper"): - estimator = Estimator() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(estimator, UCCSD(), SLSQP()) - gsc_mapper = GroundStateEigensolver(self.mapper, solver) - esc_mapper = QEOM(gsc_mapper, estimator, "sd") - results_mapper = esc_mapper.solve(self.electronic_structure_problem) - # filter duplicates from list - computed_energies_mapper = [results_mapper.computed_energies[0]] - for comp_energy in results_mapper.computed_energies[1:]: - if not np.isclose(comp_energy, computed_energies_mapper[-1]): - computed_energies_mapper.append(comp_energy) - self._assert_energies(computed_energies_mapper, self.reference_energies) - if __name__ == "__main__": unittest.main() diff --git a/test/second_q/algorithms/excited_state_solvers/test_excited_states_solvers_auxiliaries.py b/test/second_q/algorithms/excited_state_solvers/test_excited_states_solvers_auxiliaries.py index 1348eb87b..ba9b8686f 100644 --- a/test/second_q/algorithms/excited_state_solvers/test_excited_states_solvers_auxiliaries.py +++ b/test/second_q/algorithms/excited_state_solvers/test_excited_states_solvers_auxiliaries.py @@ -15,31 +15,26 @@ from __future__ import annotations import unittest -import warnings from test import QiskitNatureTestCase from ddt import ddt, named_data import numpy as np +from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import SLSQP from qiskit.primitives import Estimator from qiskit.utils import algorithm_globals from qiskit_nature.units import DistanceUnit -from qiskit_nature.second_q.circuit.library import UCCSD +from qiskit_nature.second_q.circuit.library import HartreeFock, UCCSD from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import ( - BravyiKitaevMapper, JordanWignerMapper, ParityMapper, ) -from qiskit_nature.second_q.mappers import QubitConverter, QubitMapper, TaperedQubitMapper +from qiskit_nature.second_q.mappers import QubitMapper, TaperedQubitMapper from qiskit_nature.second_q.operators.fermionic_op import FermionicOp -from qiskit_nature.second_q.algorithms import ( - GroundStateEigensolver, - VQEUCCFactory, - QEOM, -) +from qiskit_nature.second_q.algorithms import GroundStateEigensolver, QEOM from qiskit_nature.second_q.algorithms.excited_states_solvers.qeom import EvaluationRule import qiskit_nature.optionals as _optionals from .resources.expected_transition_amplitudes import reference_trans_amps @@ -130,13 +125,22 @@ def _assert_transition_amplitudes(self, computed, references, *, places=4): trans_amp_expected = np.abs(references[key][opkey][0]) self.assertAlmostEqual(trans_amp, trans_amp_expected, places=places) - def _compute_and_assert_qeom_aux_eigenvalues(self, mapper: QubitConverter | QubitMapper): + def _compute_and_assert_qeom_aux_eigenvalues(self, mapper: QubitMapper): hamiltonian_op, _ = self.electronic_structure_problem.second_q_ops() aux_ops = {"hamiltonian": hamiltonian_op} estimator = Estimator() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(estimator, UCCSD(), SLSQP()) + ansatz = UCCSD( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + mapper, + initial_state=HartreeFock( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + mapper, + ), + ) + solver = VQE(estimator, ansatz, SLSQP()) + solver.initial_point = [0] * ansatz.num_parameters gsc = GroundStateEigensolver(mapper, solver) esc = QEOM(gsc, estimator, "sd", aux_eval_rules=EvaluationRule.DIAG) results = esc.solve(self.electronic_structure_problem, aux_operators=aux_ops) @@ -148,16 +152,25 @@ def _compute_and_assert_qeom_aux_eigenvalues(self, mapper: QubitConverter | Qubi self._assert_energies(results.computed_energies, self.reference_energies) self._assert_energies(energies_recalculated, self.reference_energies) - def _compute_and_assert_qeom_trans_amp(self, mapper: QubitConverter | QubitMapper): + def _compute_and_assert_qeom_trans_amp(self, mapper: QubitMapper): aux_eval_rules = { "hamiltonian_derivative": [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] } aux_ops = {"hamiltonian_derivative": self._hamiltonian_derivative()} estimator = Estimator() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(estimator, UCCSD(), SLSQP()) + ansatz = UCCSD( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + mapper, + initial_state=HartreeFock( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + mapper, + ), + ) + solver = VQE(estimator, ansatz, SLSQP()) + solver.initial_point = [0] * ansatz.num_parameters gsc = GroundStateEigensolver(mapper, solver) esc = QEOM(gsc, estimator, excitations="sd", aux_eval_rules=aux_eval_rules) results = esc.solve(self.electronic_structure_problem, aux_operators=aux_ops) @@ -173,7 +186,7 @@ def _compute_and_assert_qeom_trans_amp(self, mapper: QubitConverter | QubitMappe ["PM", ParityMapper()], ["PM_TQR", ParityMapper(num_particles=(1, 1))], ) - def test_aux_ops_qeom_mapper(self, mapper: QubitMapper): + def test_aux_ops_qeom(self, mapper: QubitMapper): """Test QEOM evaluation of excited state properties""" self._compute_and_assert_qeom_aux_eigenvalues(mapper) @@ -192,29 +205,12 @@ def test_aux_ops_qeom_taperedmapper(self, tapered_mapper_creator): ) self._compute_and_assert_qeom_aux_eigenvalues(tapered_mapper) - @named_data( - ["JWM", QubitConverter(JordanWignerMapper())], - ["JWM_Z2", QubitConverter(JordanWignerMapper(), z2symmetry_reduction="auto")], - ["PM", QubitConverter(ParityMapper())], - ["PM_TQR", QubitConverter(ParityMapper(), two_qubit_reduction=True)], - ["PM_Z2", QubitConverter(ParityMapper(), z2symmetry_reduction="auto")], - [ - "PM_TQR_Z2", - QubitConverter(ParityMapper(), two_qubit_reduction=True, z2symmetry_reduction="auto"), - ], - ["BKM", QubitConverter(BravyiKitaevMapper())], - ["BKM_Z2", QubitConverter(BravyiKitaevMapper(), z2symmetry_reduction="auto")], - ) - def test_aux_ops_qeom(self, converter: QubitConverter): - """Test QEOM evaluation of excited state properties""" - self._compute_and_assert_qeom_aux_eigenvalues(converter) - @named_data( ["JWM", JordanWignerMapper()], ["PM", ParityMapper()], ["PM_TQR", ParityMapper(num_particles=(1, 1))], ) - def test_trans_amps_qeom_mapper(self, mapper: QubitMapper): + def test_trans_amps_qeom(self, mapper: QubitMapper): """Test QEOM evaluation of transition amplitudes""" self._compute_and_assert_qeom_trans_amp(mapper) @@ -233,23 +229,6 @@ def test_trans_amps_qeom_taperedmapper(self, tapered_mapper_creator): ) self._compute_and_assert_qeom_trans_amp(tapered_mapper) - @named_data( - ["JWM", QubitConverter(JordanWignerMapper())], - ["JWM_Z2", QubitConverter(JordanWignerMapper(), z2symmetry_reduction="auto")], - ["PM", QubitConverter(ParityMapper())], - ["PM_TQR", QubitConverter(ParityMapper(), two_qubit_reduction=True)], - ["PM_Z2", QubitConverter(ParityMapper(), z2symmetry_reduction="auto")], - [ - "PM_TQR_Z2", - QubitConverter(ParityMapper(), two_qubit_reduction=True, z2symmetry_reduction="auto"), - ], - ["BKM", QubitConverter(BravyiKitaevMapper())], - ["BKM_Z2", QubitConverter(BravyiKitaevMapper(), z2symmetry_reduction="auto")], - ) - def test_trans_amps_qeom(self, converter: QubitConverter): - """Test QEOM evaluation of transition amplitudes""" - self._compute_and_assert_qeom_trans_amp(converter) - if __name__ == "__main__": unittest.main() diff --git a/test/second_q/algorithms/excited_state_solvers/test_qeom_electronic_ops.py b/test/second_q/algorithms/excited_state_solvers/test_qeom_electronic_ops.py index 05a962b07..9b98b31f5 100644 --- a/test/second_q/algorithms/excited_state_solvers/test_qeom_electronic_ops.py +++ b/test/second_q/algorithms/excited_state_solvers/test_qeom_electronic_ops.py @@ -15,10 +15,9 @@ from test import QiskitNatureTestCase from qiskit.utils import algorithm_globals -from qiskit.opflow import PauliSumOp from qiskit_nature.units import DistanceUnit -from qiskit_nature.second_q.mappers import QubitConverter, JordanWignerMapper, TaperedQubitMapper +from qiskit_nature.second_q.mappers import JordanWignerMapper, TaperedQubitMapper from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.algorithms.excited_states_solvers.qeom_electronic_ops_builder import ( build_electronic_ops, @@ -48,42 +47,10 @@ def setUp(self): self.mapper = JordanWignerMapper() self.tapered_mapper = TaperedQubitMapper(JordanWignerMapper()) - self.qubit_converter = QubitConverter(self.mapper) self.electronic_structure_problem = self.driver.run() self.electronic_structure_problem.second_q_ops() def test_build_hopping_operators(self): - """Tests that the correct hopping operator is built.""" - - hopping_operators, commutativities, indices = build_electronic_ops( - self.electronic_structure_problem.num_spatial_orbitals, - self.electronic_structure_problem.num_particles, - "sd", - self.qubit_converter, - ) - - with self.subTest("hopping operators"): - self.assertEqual(hopping_operators.keys(), expected_hopping_operators_electronic.keys()) - for key, exp_key in zip( - hopping_operators.keys(), expected_hopping_operators_electronic.keys() - ): - self.assertEqual(key, exp_key) - val = hopping_operators[key] - if isinstance(val, PauliSumOp): - val = val.primitive - exp_val = expected_hopping_operators_electronic[exp_key] - if not val.equiv(exp_val): - print(val) - print(exp_val) - self.assertTrue(val.equiv(exp_val), msg=(val, exp_val)) - - with self.subTest("commutativities"): - self.assertEqual(commutativities, expected_commutativies_electronic) - - with self.subTest("excitation indices"): - self.assertEqual(indices, expected_indices_electronic) - - def test_build_hopping_operators_mapper(self): """Tests that the correct hopping operator is built with a qubit mapper.""" hopping_operators, commutativities, indices = build_electronic_ops( @@ -100,8 +67,6 @@ def test_build_hopping_operators_mapper(self): ): self.assertEqual(key, exp_key) val = hopping_operators[key] - if isinstance(val, PauliSumOp): - val = val.primitive exp_val = expected_hopping_operators_electronic[exp_key] if not val.equiv(exp_val): print(val) @@ -131,8 +96,6 @@ def test_build_hopping_operators_taperedmapper(self): ): self.assertEqual(key, exp_key) val = hopping_operators[key] - if isinstance(val, PauliSumOp): - val = val.primitive exp_val = expected_hopping_operators_electronic[exp_key] if not val.equiv(exp_val): print(val) diff --git a/test/second_q/algorithms/excited_state_solvers/test_qeom_vibrational_ops.py b/test/second_q/algorithms/excited_state_solvers/test_qeom_vibrational_ops.py index e1748ddcb..2ae80f163 100644 --- a/test/second_q/algorithms/excited_state_solvers/test_qeom_vibrational_ops.py +++ b/test/second_q/algorithms/excited_state_solvers/test_qeom_vibrational_ops.py @@ -17,14 +17,13 @@ import unittest from qiskit.utils import algorithm_globals -from qiskit.opflow import PauliSumOp from qiskit_nature.second_q.algorithms.excited_states_solvers.qeom_vibrational_ops_builder import ( build_vibrational_ops, ) from qiskit_nature.second_q.formats.watson import WatsonHamiltonian from qiskit_nature.second_q.formats.watson_translator import watson_to_problem -from qiskit_nature.second_q.mappers import DirectMapper, QubitConverter, TaperedQubitMapper +from qiskit_nature.second_q.mappers import DirectMapper, TaperedQubitMapper from qiskit_nature.second_q.problems import HarmonicBasis import qiskit_nature.optionals as _optionals @@ -45,7 +44,6 @@ def setUp(self): self.mapper = DirectMapper() self.tapered_mapper = TaperedQubitMapper(self.mapper) - self.qubit_converter = QubitConverter(self.mapper) import sparse as sp # pylint: disable=import-error @@ -84,36 +82,6 @@ def setUp(self): self.basis = HarmonicBasis([2, 2]) self.vibrational_problem = watson_to_problem(watson, self.basis) - def test_build_hopping_operators(self): - """Tests that the correct hopping operator is built.""" - - hopping_operators, commutativities, indices = build_vibrational_ops( - self.basis.num_modals, "sd", self.qubit_converter - ) - - with self.subTest("hopping operators"): - self.assertEqual( - hopping_operators.keys(), expected_hopping_operators_vibrational.keys() - ) - for key, exp_key in zip( - hopping_operators.keys(), expected_hopping_operators_vibrational.keys() - ): - self.assertEqual(key, exp_key) - val = hopping_operators[key] - if isinstance(val, PauliSumOp): - val = val.primitive - exp_val = expected_hopping_operators_vibrational[exp_key] - if not val.equiv(exp_val): - print(val) - print(exp_val) - self.assertTrue(val.equiv(exp_val), msg=(val, exp_val)) - - with self.subTest("commutativities"): - self.assertEqual(commutativities, expected_commutativies_vibrational) - - with self.subTest("excitation indices"): - self.assertEqual(indices, expected_indices_vibrational) - def test_build_hopping_operators_mapper(self): """Tests that the correct hopping operator is built with a qubit mapper.""" @@ -130,8 +98,6 @@ def test_build_hopping_operators_mapper(self): ): self.assertEqual(key, exp_key) val = hopping_operators[key] - if isinstance(val, PauliSumOp): - val = val.primitive exp_val = expected_hopping_operators_vibrational[exp_key] if not val.equiv(exp_val): print(val) @@ -160,8 +126,6 @@ def test_build_hopping_operators_taperedmapper(self): ): self.assertEqual(key, exp_key) val = hopping_operators[key] - if isinstance(val, PauliSumOp): - val = val.primitive exp_val = expected_hopping_operators_vibrational[exp_key] if not val.equiv(exp_val): print(val) diff --git a/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py b/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py deleted file mode 100644 index def83287c..000000000 --- a/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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. diff --git a/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/test_vqe_ucc_factory.py b/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/test_vqe_ucc_factory.py deleted file mode 100644 index adad9759d..000000000 --- a/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/test_vqe_ucc_factory.py +++ /dev/null @@ -1,122 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 VQE UCC MinimumEigensolver Factory """ - -import unittest -import warnings - -from test import QiskitNatureTestCase - -from qiskit.algorithms.optimizers import SLSQP -from qiskit.primitives import Estimator - -from qiskit_nature.second_q.circuit.library import HartreeFock, UCC, UCCSD - -from qiskit_nature.second_q.mappers import QubitConverter -from qiskit_nature.second_q.mappers import JordanWignerMapper -from qiskit_nature.second_q.algorithms import VQEUCCFactory -from qiskit_nature.second_q.algorithms.initial_points import HFInitialPoint - - -class TestVQEUCCFactory(QiskitNatureTestCase): - """Test VQE UCC MinimumEigensolver Factory""" - - # NOTE: The actual usage of this class is mostly tested in combination with the ground-state - # eigensolvers (one module above). - - def setUp(self): - super().setUp() - self.mapper = QubitConverter(JordanWignerMapper()) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - self._vqe_ucc_factory = VQEUCCFactory(Estimator(), UCCSD(), SLSQP()) - - def auxiliary_tester(self, title: str, prop: str, cases: tuple): - """ - Tests the setter and getter of a given property. - - Args: - title: A string that will be the name of the subTest - prop: A string making reference to the getter/setter to be tested - cases: A tuple containing 2 possible instances for that property. - The first instance needs to be the same used in the constructor. - """ - - with self.subTest(title): - # Check initialization - self.assertEqual(getattr(self._vqe_ucc_factory, prop), cases[0]) - self.assertEqual(getattr(self._vqe_ucc_factory.minimum_eigensolver, prop), cases[0]) - # Check factory setter - setattr(self._vqe_ucc_factory, prop, cases[1]) - self.assertEqual(getattr(self._vqe_ucc_factory, prop), cases[1]) - self.assertEqual(getattr(self._vqe_ucc_factory.minimum_eigensolver, prop), cases[1]) - # Check vqe setter - setattr(self._vqe_ucc_factory.minimum_eigensolver, prop, cases[0]) - self.assertEqual(getattr(self._vqe_ucc_factory, prop), cases[0]) - self.assertEqual(getattr(self._vqe_ucc_factory.minimum_eigensolver, prop), cases[0]) - - def auxiliary_tester_isinstance(self, title: str, prop: str, cases: tuple): - """ - Tests a getter and setter of a given property. - Only checks if the type of the property is correct. - - Args: - title: A string that will be the name of the subTest - prop: A string making reference to the getter/setter to be tested - cases: A tuple containing 2 possible types (or classes) for that property. - The first class (or type) needs to be the same used in the constructor. - """ - - with self.subTest(title): - # Check initialization - self.assertTrue(isinstance(getattr(self._vqe_ucc_factory, prop), cases[0])) - self.assertTrue( - isinstance(getattr(self._vqe_ucc_factory.minimum_eigensolver, prop), cases[0]) - ) - # Check factory setter - setattr(self._vqe_ucc_factory, prop, cases[1]()) - self.assertTrue(isinstance(getattr(self._vqe_ucc_factory, prop), cases[1])) - self.assertTrue( - isinstance(getattr(self._vqe_ucc_factory.minimum_eigensolver, prop), cases[1]) - ) - # Check vqe setter - setattr(self._vqe_ucc_factory.minimum_eigensolver, prop, cases[0]()) - self.assertTrue(isinstance(getattr(self._vqe_ucc_factory, prop), cases[0])) - self.assertTrue( - isinstance(getattr(self._vqe_ucc_factory.minimum_eigensolver, prop), cases[0]) - ) - - def test_setters_getters(self): - """Test Getter/Setter. These tests are using the getter/setter from the""" - - with self.subTest("Initial Point"): - self.assertTrue(isinstance(self._vqe_ucc_factory.initial_point, HFInitialPoint)) - initial_point = [1, 2, 3] - self._vqe_ucc_factory.initial_point = initial_point - self.assertEqual(self._vqe_ucc_factory.initial_point, initial_point) - - with self.subTest("Ansatz"): - self.assertTrue(isinstance(self._vqe_ucc_factory.ansatz, UCCSD)) - self._vqe_ucc_factory.ansatz = UCC() - self.assertTrue(isinstance(self._vqe_ucc_factory.ansatz, UCC)) - self.assertFalse(isinstance(self._vqe_ucc_factory.ansatz, UCCSD)) - - with self.subTest("Initial State"): - self.assertEqual(self._vqe_ucc_factory.initial_state, None) - initial_state = HartreeFock(4, (1, 1), self.mapper) - self._vqe_ucc_factory.initial_state = initial_state - self.assertEqual(self._vqe_ucc_factory.initial_state, initial_state) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/test_vqe_uvcc_factory.py b/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/test_vqe_uvcc_factory.py deleted file mode 100644 index 2a2c333b0..000000000 --- a/test/second_q/algorithms/ground_state_solvers/minimum_eigensolver_factories/test_vqe_uvcc_factory.py +++ /dev/null @@ -1,119 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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 VQE UVCC MinimumEigensolver Factory """ - -import unittest -import warnings - -from test import QiskitNatureTestCase - -from qiskit.algorithms.optimizers import SLSQP -from qiskit.primitives import Estimator - -from qiskit_nature.second_q.circuit.library import HartreeFock, UVCC, UVCCSD -from qiskit_nature.second_q.mappers import QubitConverter -from qiskit_nature.second_q.mappers import JordanWignerMapper -from qiskit_nature.second_q.algorithms import VQEUVCCFactory -from qiskit_nature.second_q.algorithms.initial_points import VSCFInitialPoint - - -class TestVQEUVCCFactory(QiskitNatureTestCase): - """Test VQE UVCC MinimumEigensolver Factory""" - - # NOTE: The actual usage of this class is mostly tested in combination with the ground-state - # eigensolvers (one module above). - - def setUp(self): - super().setUp() - self.mapper = QubitConverter(JordanWignerMapper()) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - self._vqe_uvcc_factory = VQEUVCCFactory(Estimator(), UVCCSD(), SLSQP()) - - def auxiliary_tester(self, title: str, prop: str, cases: tuple): - """ - Tests the setter and getter of a given property. - - Args: - title: A string that will be the name of the subTest - prop: A string making reference to the getter/setter to be tested - cases: A tuple containing 2 possible instances for that property. - The first instance needs to be the same used in the constructor. - """ - - with self.subTest(title): - # Check initialization - self.assertEqual(getattr(self._vqe_uvcc_factory, prop), cases[0]) - self.assertEqual(getattr(self._vqe_uvcc_factory.minimum_eigensolver, prop), cases[0]) - # Check factory setter - setattr(self._vqe_uvcc_factory, prop, cases[1]) - self.assertEqual(getattr(self._vqe_uvcc_factory, prop), cases[1]) - self.assertEqual(getattr(self._vqe_uvcc_factory.minimum_eigensolver, prop), cases[1]) - # Check vqe setter - setattr(self._vqe_uvcc_factory.minimum_eigensolver, prop, cases[0]) - self.assertEqual(getattr(self._vqe_uvcc_factory, prop), cases[0]) - self.assertEqual(getattr(self._vqe_uvcc_factory.minimum_eigensolver, prop), cases[0]) - - def auxiliary_tester_isinstance(self, title: str, prop: str, cases: tuple): - """ - Tests a getter and setter of a given property. - Only checks if the type of the property is correct. - - Args: - title: A string that will be the name of the subTest - prop: A string making reference to the getter/setter to be tested - cases: A tuple containing 2 possible types (or classes) for that property. - The first class (or type) needs to be the same used in the constructor. - """ - - with self.subTest(title): - # Check initialization - self.assertTrue(isinstance(getattr(self._vqe_uvcc_factory, prop), cases[0])) - self.assertTrue( - isinstance(getattr(self._vqe_uvcc_factory.minimum_eigensolver, prop), cases[0]) - ) - # Check factory setter - setattr(self._vqe_uvcc_factory, prop, cases[1]()) - self.assertTrue(isinstance(getattr(self._vqe_uvcc_factory, prop), cases[1])) - self.assertTrue( - isinstance(getattr(self._vqe_uvcc_factory.minimum_eigensolver, prop), cases[1]) - ) - # Check vqe setter - setattr(self._vqe_uvcc_factory.minimum_eigensolver, prop, cases[0]()) - self.assertTrue(isinstance(getattr(self._vqe_uvcc_factory, prop), cases[0])) - self.assertTrue( - isinstance(getattr(self._vqe_uvcc_factory.minimum_eigensolver, prop), cases[0]) - ) - - def test_setters_getters(self): - """Test Getter/Setter""" - with self.subTest("Initial Point"): - self.assertTrue(isinstance(self._vqe_uvcc_factory.initial_point, VSCFInitialPoint)) - initial_point = [1, 2, 3] - self._vqe_uvcc_factory.initial_point = initial_point - self.assertEqual(self._vqe_uvcc_factory.initial_point, initial_point) - - with self.subTest("Ansatz"): - self.assertTrue(isinstance(self._vqe_uvcc_factory.ansatz, UVCCSD)) - self._vqe_uvcc_factory.ansatz = UVCC() - self.assertTrue(isinstance(self._vqe_uvcc_factory.ansatz, UVCC)) - self.assertFalse(isinstance(self._vqe_uvcc_factory.ansatz, UVCCSD)) - - with self.subTest("Initial State"): - self.assertEqual(self._vqe_uvcc_factory.initial_state, None) - initial_state = HartreeFock(4, (1, 1), self.mapper) - self._vqe_uvcc_factory.initial_state = initial_state - self.assertEqual(self._vqe_uvcc_factory.initial_state, initial_state) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/second_q/algorithms/ground_state_solvers/test_advanced_ucc_variants.py b/test/second_q/algorithms/ground_state_solvers/test_advanced_ucc_variants.py index 7b2a4c432..40428a731 100644 --- a/test/second_q/algorithms/ground_state_solvers/test_advanced_ucc_variants.py +++ b/test/second_q/algorithms/ground_state_solvers/test_advanced_ucc_variants.py @@ -26,7 +26,6 @@ from qiskit_nature.second_q.circuit.library import HartreeFock, SUCCD, PUCCD from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import ParityMapper -from qiskit_nature.second_q.mappers import QubitConverter from qiskit_nature.second_q.transformers import FreezeCoreTransformer import qiskit_nature.optionals as _optionals @@ -42,21 +41,15 @@ def setUp(self): algorithm_globals.random_seed = 42 self.driver = PySCFDriver(atom="H 0 0 0.735; H 0 0 0", basis="631g") - self.mapper = QubitConverter(ParityMapper(), two_qubit_reduction=True) - self.electronic_structure_problem = FreezeCoreTransformer().transform(self.driver.run()) + self.mapper = self.electronic_structure_problem.get_tapered_mapper( + ParityMapper(num_particles=self.electronic_structure_problem.num_particles) + ) + self.num_spatial_orbitals = 4 self.num_particles = (1, 1) - # because we create the initial state and ansatzes early, we need to ensure the qubit - # mapper already ran such that convert_match works as expected - main_op, _ = self.electronic_structure_problem.second_q_ops() - _ = self.mapper.convert( - main_op, - self.num_particles, - ) - self.reference_energy_pUCCD = -1.1434447924298028 self.reference_energy_UCCD0 = -1.1476045878481704 self.reference_energy_UCCD0full = -1.1515491334334347 diff --git a/test/second_q/algorithms/ground_state_solvers/test_groundstate_eigensolver.py b/test/second_q/algorithms/ground_state_solvers/test_groundstate_eigensolver.py index e62be3b49..abca34ee6 100644 --- a/test/second_q/algorithms/ground_state_solvers/test_groundstate_eigensolver.py +++ b/test/second_q/algorithms/ground_state_solvers/test_groundstate_eigensolver.py @@ -16,36 +16,31 @@ import copy import io import unittest -import warnings from test import QiskitNatureTestCase import numpy as np -from qiskit.algorithms.minimum_eigensolvers import VQE +from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver, VQE from qiskit.algorithms.optimizers import SLSQP, SPSA from qiskit.primitives import Estimator from qiskit.test import slow_test from qiskit.utils import algorithm_globals import qiskit_nature.optionals as _optionals -from qiskit_nature.second_q.algorithms import ( - GroundStateEigensolver, - VQEUCCFactory, - NumPyMinimumEigensolverFactory, -) +from qiskit_nature.second_q.algorithms import GroundStateEigensolver from qiskit_nature.second_q.circuit.library import HartreeFock, UCC, UCCSD from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import JordanWignerMapper, ParityMapper -from qiskit_nature.second_q.mappers import QubitConverter +from qiskit_nature.second_q.mappers import TaperedQubitMapper from qiskit_nature.second_q.hamiltonians import ElectronicEnergy from qiskit_nature.second_q.transformers import FreezeCoreTransformer from qiskit_nature.second_q.algorithms.initial_points import MP2InitialPoint @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") -class TestGroundStateEigensolver(QiskitNatureTestCase): - """Test GroundStateEigensolver""" +class TestGroundStateEigensolverMapper(QiskitNatureTestCase): + """Test GroundStateEigensolver with Mapper""" def setUp(self): super().setUp() @@ -56,7 +51,7 @@ def setUp(self): self.reference_energy = -1.1373060356951838 self.mapper = JordanWignerMapper() - self.qubit_converter = QubitConverter(self.mapper) + self.tapered_mapper = TaperedQubitMapper(self.mapper) self.electronic_structure_problem = self.driver.run() self.num_spatial_orbitals = 2 @@ -65,37 +60,54 @@ def setUp(self): def test_npme(self): """Test NumPyMinimumEigensolver""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - calc = GroundStateEigensolver(self.qubit_converter, solver) + solver = NumPyMinimumEigensolver() + calc = GroundStateEigensolver(self.tapered_mapper, solver) res = calc.solve(self.electronic_structure_problem) self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) def test_npme_with_default_filter(self): """Test NumPyMinimumEigensolver with default filter""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory(use_default_filter_criterion=True) - calc = GroundStateEigensolver(self.qubit_converter, solver) + solver = NumPyMinimumEigensolver() + solver.filter_criterion = self.electronic_structure_problem.get_default_filter_criterion() + calc = GroundStateEigensolver(self.tapered_mapper, solver) res = calc.solve(self.electronic_structure_problem) self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) def test_vqe_uccsd(self): """Test VQE UCCSD case""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCC(excitations="d"), SLSQP()) - calc = GroundStateEigensolver(self.qubit_converter, solver) + ansatz = UCC( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + "d", + self.mapper, + initial_state=HartreeFock( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + self.mapper, + ), + ) + solver = VQE(Estimator(), ansatz, SLSQP()) + solver.initial_point = [0] * ansatz.num_parameters + calc = GroundStateEigensolver(self.mapper, solver) res = calc.solve(self.electronic_structure_problem) self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - def test_vqe_uccsd_mapper(self): - """Test VQE UCCSD case with QubitMapper""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCC(excitations="d"), SLSQP()) - calc = GroundStateEigensolver(self.mapper, solver) + def test_vqe_uccsd_taper(self): + """Test VQE UCCSD case with TaperedQubitMapper""" + ansatz = UCC( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + "d", + self.mapper, + initial_state=HartreeFock( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + self.mapper, + ), + ) + solver = VQE(Estimator(), ansatz, SLSQP()) + solver.initial_point = [0] * ansatz.num_parameters + calc = GroundStateEigensolver(self.tapered_mapper, solver) res = calc.solve(self.electronic_structure_problem) self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) @@ -106,10 +118,20 @@ def callback(nfev, parameters, energy, stddev): # pylint: disable=unused-argument print(f"iterations {nfev}: energy: {energy}") - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP(), callback=callback) - calc = GroundStateEigensolver(self.qubit_converter, solver) + ansatz = UCC( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + "d", + self.mapper, + initial_state=HartreeFock( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + self.mapper, + ), + ) + solver = VQE(Estimator(), ansatz, SLSQP(), callback=callback) + solver.initial_point = [0] * ansatz.num_parameters + calc = GroundStateEigensolver(self.tapered_mapper, solver) with contextlib.redirect_stdout(io.StringIO()) as out: res = calc.solve(self.electronic_structure_problem) self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) @@ -117,22 +139,11 @@ def callback(nfev, parameters, energy, stddev): if line.strip(): self.assertTrue(line.startswith(f"iterations {idx+1}: energy: ")) - def test_vqe_ucc_custom(self): - """Test custom ansatz in Factory use case""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP()) - calc = GroundStateEigensolver(self.qubit_converter, solver) - res = calc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - def test_aux_ops_reusability(self): """Test that the auxiliary operators can be reused""" # Regression test against #1475 - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - calc = GroundStateEigensolver(self.qubit_converter, solver) + solver = NumPyMinimumEigensolver() + calc = GroundStateEigensolver(self.tapered_mapper, solver) modes = 4 h_1 = np.eye(modes, dtype=complex) @@ -148,24 +159,33 @@ def test_aux_ops_reusability(self): def _setup_evaluation_operators(self): # first we run a ground state calculation - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP()) - calc = GroundStateEigensolver(self.qubit_converter, solver) + ansatz = UCCSD( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + self.mapper, + initial_state=HartreeFock( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + self.mapper, + ), + ) + solver = VQE(Estimator(), ansatz, SLSQP()) + solver.initial_point = [0] * ansatz.num_parameters + calc = GroundStateEigensolver(self.tapered_mapper, solver) res = calc.solve(self.electronic_structure_problem) # now we decide that we want to evaluate another operator # for testing simplicity, we just use some pre-constructed auxiliary operators _, second_q_ops = self.electronic_structure_problem.second_q_ops() - aux_ops_dict = self.qubit_converter.convert_match(second_q_ops) + aux_ops_dict = self.tapered_mapper.map(second_q_ops) return calc, res, aux_ops_dict - def _prepare_uccsd_hf(self, qubit_converter): - initial_state = HartreeFock(self.num_spatial_orbitals, self.num_particles, qubit_converter) + def _prepare_uccsd_hf(self, tapered_mapper): + initial_state = HartreeFock(self.num_spatial_orbitals, self.num_particles, tapered_mapper) ansatz = UCCSD( self.num_spatial_orbitals, self.num_particles, - qubit_converter, + tapered_mapper, initial_state=initial_state, ) @@ -173,7 +193,7 @@ def _prepare_uccsd_hf(self, qubit_converter): def test_uccsd_hf(self): """uccsd hf test""" - ansatz = self._prepare_uccsd_hf(self.qubit_converter) + ansatz = self._prepare_uccsd_hf(self.tapered_mapper) optimizer = SLSQP(maxiter=100) solver = VQE( @@ -183,7 +203,7 @@ def test_uccsd_hf(self): initial_point=[0.0] * ansatz.num_parameters, ) - gsc = GroundStateEigensolver(self.qubit_converter, solver) + gsc = GroundStateEigensolver(self.tapered_mapper, solver) result = gsc.solve(self.electronic_structure_problem) @@ -192,8 +212,8 @@ def test_uccsd_hf(self): @slow_test def test_uccsd_hf_qasm(self): """uccsd hf test with qasm simulator.""" - qubit_converter = QubitConverter(ParityMapper()) - ansatz = self._prepare_uccsd_hf(qubit_converter) + tapered_mapper = TaperedQubitMapper(ParityMapper()) + ansatz = self._prepare_uccsd_hf(tapered_mapper) optimizer = SPSA(maxiter=200, last_avg=5) solver = VQE( @@ -203,7 +223,7 @@ def test_uccsd_hf_qasm(self): initial_point=[0.0] * ansatz.num_parameters, ) - gsc = GroundStateEigensolver(qubit_converter, solver) + gsc = GroundStateEigensolver(tapered_mapper, solver) result = gsc.solve(self.electronic_structure_problem) self.assertAlmostEqual(result.total_energies[0], -1.138, places=2) @@ -218,16 +238,11 @@ def test_freeze_core_z2_symmetry_compatibility(self): atom="LI 0 0 0; H 0 0 1.6", ) problem = FreezeCoreTransformer().transform(driver.run()) - qubit_converter = QubitConverter( - ParityMapper(), - two_qubit_reduction=True, - z2symmetry_reduction="auto", - ) + num_particles = problem.num_particles + tapered_mapper = problem.get_tapered_mapper(ParityMapper(num_particles)) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - gsc = GroundStateEigensolver(qubit_converter, solver) + solver = NumPyMinimumEigensolver() + gsc = GroundStateEigensolver(tapered_mapper, solver) result = gsc.solve(problem) self.assertAlmostEqual(result.total_energies[0], -7.882, places=2) @@ -237,19 +252,15 @@ def test_total_dipole(self): An issue with calculating the dipole moment that had division None/float. """ - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - calc = GroundStateEigensolver(self.qubit_converter, solver) + solver = NumPyMinimumEigensolver() + calc = GroundStateEigensolver(self.tapered_mapper, solver) res = calc.solve(self.electronic_structure_problem) self.assertAlmostEqual(res.total_dipole_moment_in_debye[0], 0.0, places=1) def test_print_result(self): """Regression test against #198 and general issues with printing results.""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - calc = GroundStateEigensolver(self.qubit_converter, solver) + solver = NumPyMinimumEigensolver() + calc = GroundStateEigensolver(self.tapered_mapper, solver) res = calc.solve(self.electronic_structure_problem) res.formatting_precision = 6 with contextlib.redirect_stdout(io.StringIO()) as out: @@ -280,114 +291,28 @@ def test_print_result(self): for truth, expected in zip(out.getvalue().split("\n"), expected.split("\n")): assert truth.strip().startswith(expected.strip()) - def test_default_initial_point(self): - """Test when using the default initial point.""" - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP()) - calc = GroundStateEigensolver(self.qubit_converter, solver) - res = calc.solve(self.electronic_structure_problem) - # pylint: disable=no-member - np.testing.assert_array_equal(solver.initial_point.to_numpy_array(), [0.0, 0.0, 0.0]) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_default_initial_point_with_imaginary_ucc(self): - """Test when using the default initial point and the imaginary parts of the UCC ansatz.""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - ansatz = UCCSD( - self.num_spatial_orbitals, - self.num_particles, - self.qubit_converter, - include_imaginary=True, - ) - - solver = VQEUCCFactory(Estimator(), ansatz, SLSQP()) - - calc = GroundStateEigensolver(self.qubit_converter, solver) - res = calc.solve(self.electronic_structure_problem) - - # pylint: disable=no-member - np.testing.assert_array_equal( - solver.initial_point.to_numpy_array(), [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - ) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_ucc_factory_with_user_initial_point(self): - """Test VQEUCCFactory when using it with a user defined initial point.""" - - initial_point = np.asarray([1.28074029e-19, 5.92226076e-08, 1.11762559e-01]) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory( - Estimator(), UCCSD(), SLSQP(maxiter=1), initial_point=initial_point - ) - calc = GroundStateEigensolver(self.qubit_converter, solver) - res = calc.solve(self.electronic_structure_problem) - np.testing.assert_array_almost_equal(res.raw_result.optimal_point, initial_point) - - def test_vqe_ucc_factory_with_mp2(self): + def test_vqe_ucc_with_mp2(self): """Test when using MP2InitialPoint to generate the initial point.""" - - initial_point = MP2InitialPoint() - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP(), initial_point=initial_point) - calc = GroundStateEigensolver(self.qubit_converter, solver) - res = calc.solve(self.electronic_structure_problem) - # pylint: disable=no-member - np.testing.assert_array_almost_equal( - solver.initial_point.to_numpy_array(), self.mp2_initial_point - ) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_ucc_factory_with_reps(self): - """Test when using the default initial point with repeated evolved operators.""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - ansatz = UCCSD( - qubit_converter=self.qubit_converter, - num_particles=self.num_particles, - num_spatial_orbitals=self.num_spatial_orbitals, - reps=2, - ) - - solver = VQEUCCFactory(Estimator(), ansatz, SLSQP()) - - calc = GroundStateEigensolver(self.qubit_converter, solver) - res = calc.solve(self.electronic_structure_problem) - # pylint: disable=no-member - np.testing.assert_array_almost_equal( - solver.initial_point.to_numpy_array(), np.zeros(6, dtype=float) + ansatz = UCCSD( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + self.mapper, + initial_state=HartreeFock( + self.electronic_structure_problem.num_spatial_orbitals, + self.electronic_structure_problem.num_particles, + self.mapper, + ), ) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_ucc_factory_with_mp2_with_reps(self): - """Test when using MP2InitialPoint to generate the initial point with repeated evolved - operators. - """ + solver = VQE(Estimator(), ansatz, SLSQP()) initial_point = MP2InitialPoint() + initial_point.ansatz = ansatz + initial_point.problem = self.electronic_structure_problem - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - ansatz = UCCSD( - qubit_converter=self.qubit_converter, - num_particles=self.num_particles, - num_spatial_orbitals=self.num_spatial_orbitals, - reps=2, - ) + solver.initial_point = initial_point.to_numpy_array() - solver = VQEUCCFactory(Estimator(), ansatz, SLSQP(), initial_point=initial_point) - - calc = GroundStateEigensolver(self.qubit_converter, solver) + calc = GroundStateEigensolver(self.tapered_mapper, solver) res = calc.solve(self.electronic_structure_problem) - # pylint: disable=no-member - np.testing.assert_array_almost_equal( - solver.initial_point.to_numpy_array(), np.tile(self.mp2_initial_point, 2) - ) self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) diff --git a/test/second_q/algorithms/ground_state_solvers/test_groundstate_eigensolver_mapper.py b/test/second_q/algorithms/ground_state_solvers/test_groundstate_eigensolver_mapper.py deleted file mode 100644 index 4dfa85d5c..000000000 --- a/test/second_q/algorithms/ground_state_solvers/test_groundstate_eigensolver_mapper.py +++ /dev/null @@ -1,396 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 GroundStateEigensolver """ - -import contextlib -import copy -import io -import unittest -import warnings - -from test import QiskitNatureTestCase - -import numpy as np - -from qiskit.algorithms.minimum_eigensolvers import VQE -from qiskit.algorithms.optimizers import SLSQP, SPSA -from qiskit.primitives import Estimator -from qiskit.test import slow_test -from qiskit.utils import algorithm_globals - -import qiskit_nature.optionals as _optionals -from qiskit_nature.second_q.algorithms import ( - GroundStateEigensolver, - VQEUCCFactory, - NumPyMinimumEigensolverFactory, -) -from qiskit_nature.second_q.circuit.library import HartreeFock, UCC, UCCSD -from qiskit_nature.second_q.drivers import PySCFDriver -from qiskit_nature.second_q.mappers import JordanWignerMapper, ParityMapper -from qiskit_nature.second_q.mappers import TaperedQubitMapper -from qiskit_nature.second_q.hamiltonians import ElectronicEnergy -from qiskit_nature.second_q.transformers import FreezeCoreTransformer -from qiskit_nature.second_q.algorithms.initial_points import MP2InitialPoint - - -@unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") -class TestGroundStateEigensolverMapper(QiskitNatureTestCase): - """Test GroundStateEigensolver with Mapper""" - - def setUp(self): - super().setUp() - self.driver = PySCFDriver() - self.seed = 56 - algorithm_globals.random_seed = self.seed - - self.reference_energy = -1.1373060356951838 - - self.mapper = JordanWignerMapper() - self.tapered_mapper = TaperedQubitMapper(self.mapper) - self.electronic_structure_problem = self.driver.run() - - self.num_spatial_orbitals = 2 - self.num_particles = (1, 1) - self.mp2_initial_point = [0.0, 0.0, -0.07197145] - - def test_npme(self): - """Test NumPyMinimumEigensolver""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_npme_with_default_filter(self): - """Test NumPyMinimumEigensolver with default filter""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory(use_default_filter_criterion=True) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_uccsd(self): - """Test VQE UCCSD case""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCC(excitations="d"), SLSQP()) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_uccsd_mapper(self): - """Test VQE UCCSD case with QubitMapper""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCC(excitations="d"), SLSQP()) - calc = GroundStateEigensolver(self.mapper, solver) - res = calc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_uccsd_with_callback(self): - """Test VQE UCCSD with callback.""" - - def callback(nfev, parameters, energy, stddev): - # pylint: disable=unused-argument - print(f"iterations {nfev}: energy: {energy}") - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP(), callback=callback) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - with contextlib.redirect_stdout(io.StringIO()) as out: - res = calc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - for idx, line in enumerate(out.getvalue().split("\n")): - if line.strip(): - self.assertTrue(line.startswith(f"iterations {idx+1}: energy: ")) - - def test_vqe_ucc_custom(self): - """Test custom ansatz in Factory use case""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP()) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_aux_ops_reusability(self): - """Test that the auxiliary operators can be reused""" - # Regression test against #1475 - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - calc = GroundStateEigensolver(self.tapered_mapper, solver) - - modes = 4 - h_1 = np.eye(modes, dtype=complex) - h_2 = np.zeros((modes, modes, modes, modes)) - aux_ops = [ElectronicEnergy.from_raw_integrals(h_1, h_2).second_q_op()] - aux_ops_copy = copy.deepcopy(aux_ops) - - _ = calc.solve(self.electronic_structure_problem) - - assert all( - frozenset(a.items()) == frozenset(b.items()) for a, b in zip(aux_ops, aux_ops_copy) - ) - - def _setup_evaluation_operators(self): - # first we run a ground state calculation - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP()) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - - # now we decide that we want to evaluate another operator - # for testing simplicity, we just use some pre-constructed auxiliary operators - _, second_q_ops = self.electronic_structure_problem.second_q_ops() - aux_ops_dict = self.tapered_mapper.map(second_q_ops) - return calc, res, aux_ops_dict - - def _prepare_uccsd_hf(self, tapered_mapper): - initial_state = HartreeFock(self.num_spatial_orbitals, self.num_particles, tapered_mapper) - ansatz = UCCSD( - self.num_spatial_orbitals, - self.num_particles, - tapered_mapper, - initial_state=initial_state, - ) - - return ansatz - - def test_uccsd_hf(self): - """uccsd hf test""" - ansatz = self._prepare_uccsd_hf(self.tapered_mapper) - - optimizer = SLSQP(maxiter=100) - solver = VQE( - ansatz=ansatz, - optimizer=optimizer, - estimator=Estimator(), - initial_point=[0.0] * ansatz.num_parameters, - ) - - gsc = GroundStateEigensolver(self.tapered_mapper, solver) - - result = gsc.solve(self.electronic_structure_problem) - - self.assertAlmostEqual(result.total_energies[0], self.reference_energy, places=6) - - @slow_test - def test_uccsd_hf_qasm(self): - """uccsd hf test with qasm simulator.""" - tapered_mapper = TaperedQubitMapper(ParityMapper()) - ansatz = self._prepare_uccsd_hf(tapered_mapper) - - optimizer = SPSA(maxiter=200, last_avg=5) - solver = VQE( - ansatz=ansatz, - optimizer=optimizer, - estimator=Estimator(), - initial_point=[0.0] * ansatz.num_parameters, - ) - - gsc = GroundStateEigensolver(tapered_mapper, solver) - - result = gsc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(result.total_energies[0], -1.138, places=2) - - def test_freeze_core_z2_symmetry_compatibility(self): - """Regression test against #192. - - An issue arose when the FreezeCoreTransformer was combined with the automatic Z2Symmetry - reduction. This regression test ensures that this behavior remains fixed. - """ - driver = PySCFDriver( - atom="LI 0 0 0; H 0 0 1.6", - ) - problem = FreezeCoreTransformer().transform(driver.run()) - num_particles = problem.num_particles - tapered_mapper = problem.get_tapered_mapper(ParityMapper(num_particles)) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - gsc = GroundStateEigensolver(tapered_mapper, solver) - - result = gsc.solve(problem) - self.assertAlmostEqual(result.total_energies[0], -7.882, places=2) - - def test_total_dipole(self): - """Regression test against #198. - - An issue with calculating the dipole moment that had division None/float. - """ - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - self.assertAlmostEqual(res.total_dipole_moment_in_debye[0], 0.0, places=1) - - def test_print_result(self): - """Regression test against #198 and general issues with printing results.""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = NumPyMinimumEigensolverFactory() - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - res.formatting_precision = 6 - with contextlib.redirect_stdout(io.StringIO()) as out: - print(res) - # do NOT change the below! Lines have been truncated as to not force exact numerical matches - expected = """\ - === GROUND STATE ENERGY === - - * Electronic ground state energy (Hartree): -1.857275 - - computed part: -1.857275 - ~ Nuclear repulsion energy (Hartree): 0.719969 - > Total ground state energy (Hartree): -1.137306 - - === MEASURED OBSERVABLES === - - 0: # Particles: 2.000 S: 0.000 S^2: 0.000 M: 0.000 - - === DIPOLE MOMENTS === - - ~ Nuclear dipole moment (a.u.): [0.0 0.0 1.388949] - - 0: - * Electronic dipole moment (a.u.): [0.0 0.0 1.388949] - - computed part: [0.0 0.0 1.388949] - > Dipole moment (a.u.): [0.0 0.0 0.0] Total: 0.0 - (debye): [0.0 0.0 0.0] Total: 0.0 - """ - for truth, expected in zip(out.getvalue().split("\n"), expected.split("\n")): - assert truth.strip().startswith(expected.strip()) - - def test_default_initial_point(self): - """Test when using the default initial point.""" - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP()) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - - np.testing.assert_array_equal( - solver.initial_point.to_numpy_array(), [0.0, 0.0, 0.0] # pylint: disable=no-member - ) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_default_initial_point_with_imaginary_ucc(self): - """Test when using the default initial point and the imaginary parts of the UCC ansatz.""" - ansatz = UCCSD( - self.num_spatial_orbitals, - self.num_particles, - self.tapered_mapper, - include_imaginary=True, - ) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), ansatz, SLSQP()) - - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - - # pylint: disable=no-member - np.testing.assert_array_equal( - solver.initial_point.to_numpy_array(), [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - ) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_ucc_factory_with_user_initial_point(self): - """Test VQEUCCFactory when using it with a user defined initial point.""" - - initial_point = np.asarray([1.28074029e-19, 5.92226076e-08, 1.11762559e-01]) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory( - Estimator(), UCCSD(), SLSQP(maxiter=1), initial_point=initial_point - ) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - np.testing.assert_array_almost_equal(res.raw_result.optimal_point, initial_point) - - def test_vqe_ucc_factory_with_mp2(self): - """Test when using MP2InitialPoint to generate the initial point.""" - - initial_point = MP2InitialPoint() - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), UCCSD(), SLSQP(), initial_point=initial_point) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - - np.testing.assert_array_almost_equal( - solver.initial_point.to_numpy_array(), # pylint: disable=no-member - self.mp2_initial_point, - ) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_ucc_factory_with_reps(self): - """Test when using the default initial point with repeated evolved operators.""" - ansatz = UCCSD( - qubit_mapper=self.tapered_mapper, - num_particles=self.num_particles, - num_spatial_orbitals=self.num_spatial_orbitals, - reps=2, - ) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), ansatz, SLSQP()) - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - - np.testing.assert_array_almost_equal( - solver.initial_point.to_numpy_array(), # pylint: disable=no-member - np.zeros(6, dtype=float), - ) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - def test_vqe_ucc_factory_with_mp2_with_reps(self): - """Test when using MP2InitialPoint to generate the initial point with repeated evolved - operators. - """ - - initial_point = MP2InitialPoint() - - ansatz = UCCSD( - qubit_mapper=self.tapered_mapper, - num_particles=self.num_particles, - num_spatial_orbitals=self.num_spatial_orbitals, - reps=2, - ) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - solver = VQEUCCFactory(Estimator(), ansatz, SLSQP(), initial_point=initial_point) - - calc = GroundStateEigensolver(self.tapered_mapper, solver) - res = calc.solve(self.electronic_structure_problem) - - np.testing.assert_array_almost_equal( - solver.initial_point.to_numpy_array(), # pylint: disable=no-member - np.tile(self.mp2_initial_point, 2), - ) - self.assertAlmostEqual(res.total_energies[0], self.reference_energy, places=6) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/second_q/algorithms/ground_state_solvers/test_swaprz.py b/test/second_q/algorithms/ground_state_solvers/test_swaprz.py index dbe86f7a7..50069e141 100644 --- a/test/second_q/algorithms/ground_state_solvers/test_swaprz.py +++ b/test/second_q/algorithms/ground_state_solvers/test_swaprz.py @@ -26,7 +26,6 @@ from qiskit_nature.second_q.circuit.library import HartreeFock from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import ParityMapper -from qiskit_nature.second_q.mappers import QubitConverter class TestExcitationPreserving(QiskitNatureTestCase): @@ -50,7 +49,7 @@ def test_excitation_preserving(self): driver = PySCFDriver() - mapper = QubitConverter(ParityMapper()) + mapper = ParityMapper() problem = driver.run() diff --git a/test/second_q/algorithms/initial_points/test_mp2_initial_point.py b/test/second_q/algorithms/initial_points/test_mp2_initial_point.py index 7298da124..5ca0415e6 100644 --- a/test/second_q/algorithms/initial_points/test_mp2_initial_point.py +++ b/test/second_q/algorithms/initial_points/test_mp2_initial_point.py @@ -28,7 +28,7 @@ from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.problems import ElectronicStructureProblem from qiskit_nature.second_q.hamiltonians import ElectronicEnergy -from qiskit_nature.second_q.mappers import QubitConverter, JordanWignerMapper +from qiskit_nature.second_q.mappers import JordanWignerMapper from qiskit_nature.second_q.algorithms.initial_points import MP2InitialPoint @@ -72,7 +72,7 @@ def test_mp2_initial_point_with_real_molecules( num_particles = (problem.num_alpha, problem.num_beta) num_spatial_orbitals = problem.num_spatial_orbitals - mapper = QubitConverter(mapper=JordanWignerMapper()) + mapper = JordanWignerMapper() initial_state = HartreeFock( num_spatial_orbitals=num_spatial_orbitals, diff --git a/test/second_q/circuit/library/ansatzes/test_chc.py b/test/second_q/circuit/library/ansatzes/test_chc.py index 32ec922e9..ae0d78848 100644 --- a/test/second_q/circuit/library/ansatzes/test_chc.py +++ b/test/second_q/circuit/library/ansatzes/test_chc.py @@ -26,7 +26,6 @@ generate_vibration_excitations, ) from qiskit_nature.second_q.mappers import DirectMapper -from qiskit_nature.second_q.mappers import QubitConverter from qiskit_nature.second_q.operators import VibrationalOp @@ -87,19 +86,11 @@ def test_chc_vscf(self): algo = VQE(Estimator(), chc_ansatz, optimizer) mapper = DirectMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - qubit_op = converter.convert_match(vibr_op) - vqe_result = algo.compute_minimum_eigenvalue(qubit_op) - energy = vqe_result.optimal_value - self.assertAlmostEqual(energy, self.reference_energy, places=4) - - with self.subTest("Qubit Mapper object"): - qubit_op = mapper.map(vibr_op) - vqe_result = algo.compute_minimum_eigenvalue(qubit_op) - energy = vqe_result.optimal_value - self.assertAlmostEqual(energy, self.reference_energy, places=4) + + qubit_op = mapper.map(vibr_op) + vqe_result = algo.compute_minimum_eigenvalue(qubit_op) + energy = vqe_result.optimal_value + self.assertAlmostEqual(energy, self.reference_energy, places=4) if __name__ == "__main__": diff --git a/test/second_q/circuit/library/ansatzes/test_puccd.py b/test/second_q/circuit/library/ansatzes/test_puccd.py index 6051b95d2..5adde1879 100644 --- a/test/second_q/circuit/library/ansatzes/test_puccd.py +++ b/test/second_q/circuit/library/ansatzes/test_puccd.py @@ -23,7 +23,6 @@ from qiskit_nature.second_q.circuit.library import PUCCD from qiskit_nature.second_q.mappers import JordanWignerMapper from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature.second_q.mappers import QubitConverter @ddt @@ -50,7 +49,7 @@ class TestPUCC(QiskitNatureTestCase): ) def test_puccd_ansatz(self, num_spatial_orbitals, num_particles, expect): """Tests the PUCCD Ansatz.""" - mapper = QubitConverter(JordanWignerMapper()) + mapper = JordanWignerMapper() ansatz = PUCCD( qubit_mapper=mapper, @@ -95,7 +94,7 @@ def test_puccd_ansatz_with_singles( self, num_spatial_orbitals, num_particles, include_singles, expect ): """Tests the PUCCD Ansatz with included single excitations.""" - mapper = QubitConverter(JordanWignerMapper()) + mapper = JordanWignerMapper() ansatz = PUCCD( qubit_mapper=mapper, @@ -135,25 +134,14 @@ def test_raise_non_singlet(self): def test_puccd_ansatz_generalized(self, num_spatial_orbitals, num_particles, expect): """Tests the generalized PUCCD Ansatz.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = PUCCD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - generalized=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - - with self.subTest("Qubit Mapper object"): - ansatz = PUCCD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - generalized=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + + ansatz = PUCCD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + generalized=True, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -181,25 +169,14 @@ def test_puccd_ansatz_generalized(self, num_spatial_orbitals, num_particles, exp def test_puccd_ansatz_include_imaginary(self, num_spatial_orbitals, num_particles, expect): """Tests the PUCCD Ansatz with imaginary terms included.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = PUCCD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - include_imaginary=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - - with self.subTest("Qubit Mapper object"): - ansatz = PUCCD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - include_imaginary=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + + ansatz = PUCCD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + include_imaginary=True, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) if __name__ == "__main__": diff --git a/test/second_q/circuit/library/ansatzes/test_succd.py b/test/second_q/circuit/library/ansatzes/test_succd.py index c44db4169..a9b082f42 100644 --- a/test/second_q/circuit/library/ansatzes/test_succd.py +++ b/test/second_q/circuit/library/ansatzes/test_succd.py @@ -23,7 +23,6 @@ from qiskit_nature.second_q.circuit.library import SUCCD from qiskit_nature.second_q.mappers import JordanWignerMapper from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature.second_q.mappers import QubitConverter @ddt @@ -57,23 +56,13 @@ class TestSUCCD(QiskitNatureTestCase): def test_succd_ansatz(self, num_spatial_orbitals, num_particles, expect): """Tests the SUCCD Ansatz.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - with self.subTest("Qubit Converter object"): - ansatz = SUCCD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - - with self.subTest("Qubit Mapper object"): - ansatz = SUCCD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = SUCCD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -111,25 +100,14 @@ def test_succd_ansatz_with_singles( ): """Tests the SUCCD Ansatz with included single excitations.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = SUCCD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - include_singles=include_singles, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - with self.subTest("Qubit Mapper object"): - ansatz = SUCCD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - include_singles=include_singles, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = SUCCD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + include_singles=include_singles, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) def test_raise_non_singlet(self): """Test an error is raised when the number of alpha and beta electrons differ.""" @@ -166,25 +144,14 @@ def test_raise_non_singlet(self): def test_succd_ansatz_generalized(self, num_spatial_orbitals, num_particles, expect): """Tests the generalized SUCCD Ansatz.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = SUCCD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - generalized=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - with self.subTest("Qubit Mapper object"): - ansatz = SUCCD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - generalized=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = SUCCD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + generalized=True, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -209,25 +176,14 @@ def test_succd_ansatz_generalized(self, num_spatial_orbitals, num_particles, exp def test_succ_mirror(self, num_spatial_orbitals, num_particles, expect): """Tests the `mirror` option of the SUCCD Ansatz.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - with self.subTest("Qubit Converter object"): - ansatz = SUCCD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - mirror=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - - with self.subTest("Qubit Mapper object"): - ansatz = SUCCD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - mirror=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = SUCCD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + mirror=True, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -299,27 +255,15 @@ def test_succ_mirror_with_singles( ): """Tests the succ_mirror Ansatz with included single excitations.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = SUCCD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - include_singles=include_singles, - mirror=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - with self.subTest("Qubit Mapper object"): - ansatz = SUCCD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - include_singles=include_singles, - mirror=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = SUCCD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + include_singles=include_singles, + mirror=True, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -363,27 +307,15 @@ def test_succ_mirror_with_singles( def test_succ_mirror_ansatz_generalized(self, num_spatial_orbitals, num_particles, expect): """Tests the generalized succ_mirror Ansatz.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = SUCCD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - generalized=True, - mirror=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - with self.subTest("Qubit Mapper object"): - ansatz = SUCCD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - generalized=True, - mirror=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = SUCCD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + generalized=True, + mirror=True, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) if __name__ == "__main__": diff --git a/test/second_q/circuit/library/ansatzes/test_ucc.py b/test/second_q/circuit/library/ansatzes/test_ucc.py index 514c910fb..9e6cb6b5b 100644 --- a/test/second_q/circuit/library/ansatzes/test_ucc.py +++ b/test/second_q/circuit/library/ansatzes/test_ucc.py @@ -15,14 +15,12 @@ from test import QiskitNatureTestCase import unittest -import warnings from ddt import data, ddt, unpack from qiskit import transpile from qiskit_nature import QiskitNatureError from qiskit_nature.second_q.circuit.library import UCC -from qiskit_nature.second_q.mappers import QubitConverter from qiskit_nature.second_q.mappers import JordanWignerMapper, ParityMapper from qiskit_nature.second_q.operators import FermionicOp @@ -122,25 +120,14 @@ class TestUCC(QiskitNatureTestCase): def test_ucc_ansatz(self, excitations, num_spatial_orbitals, num_particles, expect): """Tests the UCC Ansatz.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - with self.subTest("Qubit Converter object"): - ansatz = UCC( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - excitations=excitations, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - - with self.subTest("Qubit Mapper object"): - ansatz = UCC( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - excitations=excitations, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = UCC( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + excitations=excitations, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -170,7 +157,7 @@ def test_ucc_ansatz(self, excitations, num_spatial_orbitals, num_particles, expe ) def test_custom_excitations(self, num_spatial_orbitals, num_particles, excitations): """Tests if an error is raised when the excitations have a wrong format""" - mapper = QubitConverter(JordanWignerMapper()) + mapper = JordanWignerMapper() # pylint: disable=unused-argument def custom_excitations(num_spatial_orbitals, num_particles): @@ -192,29 +179,16 @@ def test_transpile_no_parameters(self): num_particles = (2, 2) mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - with self.subTest("Qubit Converter object"): - ansatz = UCC( - num_spatial_orbitals=num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=converter, - excitations="s", - ) + ansatz = UCC( + num_spatial_orbitals=num_spatial_orbitals, + num_particles=num_particles, + qubit_mapper=mapper, + excitations="s", + ) - ansatz = transpile(ansatz, optimization_level=3) - self.assertEqual(ansatz.num_qubits, 8) - - with self.subTest("Qubit Mapper object"): - ansatz = UCC( - num_spatial_orbitals=num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=mapper, - excitations="s", - ) - - ansatz = transpile(ansatz, optimization_level=3) - self.assertEqual(ansatz.num_qubits, 8) + ansatz = transpile(ansatz, optimization_level=3) + self.assertEqual(ansatz.num_qubits, 8) def test_build_ucc(self): """Test building UCC""" @@ -226,10 +200,6 @@ def test_build_ucc(self): self.assertIsNone(ucc.excitations) self.assertIsNone(ucc.qubit_mapper) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - self.assertIsNone(ucc.qubit_converter) - self.assertIsNone(ucc.operators) self.assertIsNone(ucc.excitation_list) self.assertEqual(ucc.num_qubits, 0) @@ -257,17 +227,6 @@ def test_build_ucc(self): with self.assertRaises(ValueError): _ = ucc.data - with self.subTest("Set qubit converter to complete build"): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - converter = QubitConverter(JordanWignerMapper()) - ucc.qubit_converter = converter - self.assertEqual(ucc.qubit_converter, converter) - self.assertIsNotNone(ucc.operators) - self.assertEqual(len(ucc.operators), 3) - self.assertEqual(ucc.num_qubits, 4) - self.assertIsNotNone(ucc.data) - with self.subTest("Set qubit mapper to complete build"): mapper = JordanWignerMapper() ucc.qubit_mapper = mapper @@ -310,14 +269,7 @@ def test_build_ucc(self): self.assertIsNotNone(ucc.operators) self.assertEqual(len(ucc.operators), 4) - with self.subTest("Change qubit converter"): - ucc.qubit_mapper = QubitConverter(ParityMapper(), two_qubit_reduction=True) - # Has not been used to convert so we need to force it to do two qubit reduction - ucc.qubit_mapper.force_match(num_particles=ucc.num_particles) - self.assertIsNotNone(ucc.operators) - self.assertEqual(ucc.num_qubits, 4) - - with self.subTest("Change qubit converter to qubit mapper"): + with self.subTest("Change qubit mapper"): mapper = ParityMapper() ucc.qubit_mapper = mapper self.assertIsNotNone(ucc.operators) diff --git a/test/second_q/circuit/library/ansatzes/test_uccsd.py b/test/second_q/circuit/library/ansatzes/test_uccsd.py index c68bea297..61f5ad125 100644 --- a/test/second_q/circuit/library/ansatzes/test_uccsd.py +++ b/test/second_q/circuit/library/ansatzes/test_uccsd.py @@ -22,7 +22,6 @@ from qiskit_nature.second_q.circuit.library import UCCSD from qiskit_nature.second_q.mappers import JordanWignerMapper from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature.second_q.mappers import QubitConverter @ddt @@ -102,23 +101,13 @@ class TestUCCSD(QiskitNatureTestCase): def test_uccsd_ansatz(self, num_spatial_orbitals, num_particles, expect): """Tests the UCCSD Ansatz.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - with self.subTest("Qubit Converter object"): - ansatz = UCCSD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - - with self.subTest("Qubit Mapper object"): - ansatz = UCCSD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = UCCSD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -147,25 +136,14 @@ def test_uccsd_ansatz(self, num_spatial_orbitals, num_particles, expect): def test_uccsd_ansatz_generalized(self, num_spatial_orbitals, num_particles, expect): """Tests the generalized UCCSD Ansatz.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = UCCSD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - generalized=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - with self.subTest("Qubit Mapper object"): - ansatz = UCCSD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - generalized=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = UCCSD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + generalized=True, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -204,25 +182,14 @@ def test_uccsd_ansatz_generalized(self, num_spatial_orbitals, num_particles, exp def test_uccsd_ansatz_preserve_spin(self, num_spatial_orbitals, num_particles, expect): """Tests UCCSD Ansatz with spin flips.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - with self.subTest("Qubit Converter object"): - ansatz = UCCSD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - preserve_spin=False, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - - with self.subTest("Qubit Mapper object"): - ansatz = UCCSD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - preserve_spin=False, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = UCCSD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + preserve_spin=False, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) @unpack @data( @@ -242,25 +209,14 @@ def test_uccsd_ansatz_preserve_spin(self, num_spatial_orbitals, num_particles, e def test_uccsd_ansatz_include_imaginary(self, num_spatial_orbitals, num_particles, expect): """Tests UCCSD Ansatz with imaginary terms included.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = UCCSD( - qubit_mapper=converter, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - include_imaginary=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) - with self.subTest("Qubit Mapper object"): - ansatz = UCCSD( - qubit_mapper=mapper, - num_particles=num_particles, - num_spatial_orbitals=num_spatial_orbitals, - include_imaginary=True, - ) - assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) + ansatz = UCCSD( + qubit_mapper=mapper, + num_particles=num_particles, + num_spatial_orbitals=num_spatial_orbitals, + include_imaginary=True, + ) + assert_ucc_like_ansatz(self, ansatz, num_spatial_orbitals, expect) if __name__ == "__main__": diff --git a/test/second_q/circuit/library/ansatzes/test_uvcc.py b/test/second_q/circuit/library/ansatzes/test_uvcc.py index 28c5b6585..40bfa3d4a 100644 --- a/test/second_q/circuit/library/ansatzes/test_uvcc.py +++ b/test/second_q/circuit/library/ansatzes/test_uvcc.py @@ -27,7 +27,6 @@ from qiskit_nature.second_q.circuit.library import UVCC, VSCF from qiskit_nature.second_q.mappers import DirectMapper from qiskit_nature.second_q.operators import VibrationalOp -from qiskit_nature.second_q.mappers import QubitConverter def assert_ucc_like_ansatz(test_case, ansatz, num_modals, expected_ops): @@ -61,31 +60,15 @@ class TestUVCC(QiskitNatureTestCase): def test_ucc_ansatz(self, excitations, num_modals, expect): """Tests the UVCC Ansatz.""" mapper = DirectMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = UVCC(qubit_mapper=converter, num_modals=num_modals, excitations=excitations) - assert_ucc_like_ansatz(self, ansatz, num_modals, expect) - - with self.subTest("Qubit Mapper object"): - ansatz = UVCC(qubit_mapper=mapper, num_modals=num_modals, excitations=excitations) - assert_ucc_like_ansatz(self, ansatz, num_modals, expect) + ansatz = UVCC(qubit_mapper=mapper, num_modals=num_modals, excitations=excitations) + assert_ucc_like_ansatz(self, ansatz, num_modals, expect) def test_transpile_no_parameters(self): """Test transpilation without parameters""" - mapper = DirectMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - ansatz = UVCC(qubit_mapper=converter, num_modals=[2], excitations="s") - ansatz = transpile(ansatz, optimization_level=3) - self.assertEqual(ansatz.num_qubits, 2) - - with self.subTest("Qubit Mapper object"): - ansatz = UVCC(qubit_mapper=mapper, num_modals=[2], excitations="s") - ansatz = transpile(ansatz, optimization_level=3) - self.assertEqual(ansatz.num_qubits, 2) + ansatz = UVCC(qubit_mapper=mapper, num_modals=[2], excitations="s") + ansatz = transpile(ansatz, optimization_level=3) + self.assertEqual(ansatz.num_qubits, 2) class TestUVCCVSCF(QiskitNatureTestCase): @@ -131,25 +114,14 @@ def test_uvcc_vscf(self): vibr_op = VibrationalOp(vibrational_op_labels, num_modals) init_state = VSCF(num_modals) mapper = DirectMapper() - converter = QubitConverter(mapper) - - with self.subTest("Qubit Converter object"): - qubit_op = converter.convert_match(vibr_op) - uvcc_ansatz = UVCC(num_modals, "sd", converter, initial_state=init_state) - optimizer = COBYLA(maxiter=1000) - algo = VQE(Estimator(), uvcc_ansatz, optimizer) - vqe_result = algo.compute_minimum_eigenvalue(qubit_op) - energy = vqe_result.optimal_value - self.assertAlmostEqual(energy, self.reference_energy, places=4) - - with self.subTest("Qubit Mapper object"): - qubit_op = mapper.map(vibr_op) - uvcc_ansatz = UVCC(num_modals, "sd", mapper, initial_state=init_state) - optimizer = COBYLA(maxiter=1000) - algo = VQE(Estimator(), uvcc_ansatz, optimizer) - vqe_result = algo.compute_minimum_eigenvalue(qubit_op) - energy = vqe_result.optimal_value - self.assertAlmostEqual(energy, self.reference_energy, places=4) + + qubit_op = mapper.map(vibr_op) + uvcc_ansatz = UVCC(num_modals, "sd", mapper, initial_state=init_state) + optimizer = COBYLA(maxiter=1000) + algo = VQE(Estimator(), uvcc_ansatz, optimizer) + vqe_result = algo.compute_minimum_eigenvalue(qubit_op) + energy = vqe_result.optimal_value + self.assertAlmostEqual(energy, self.reference_energy, places=4) def test_build_uvcc(self): """Test building UVCC""" @@ -179,10 +151,10 @@ def test_build_uvcc(self): with self.assertRaises(ValueError): _ = uvcc.data - with self.subTest("Set qubit converter to complete build"): - converter = QubitConverter(DirectMapper()) - uvcc.qubit_mapper = converter - self.assertEqual(uvcc.qubit_mapper, converter) + with self.subTest("Set qubit mapper to complete build"): + mapper = DirectMapper() + uvcc.qubit_mapper = mapper + self.assertEqual(uvcc.qubit_mapper, mapper) self.assertIsNotNone(uvcc.operators) self.assertEqual(len(uvcc.operators), 3) self.assertEqual(uvcc.num_qubits, 4) diff --git a/test/second_q/circuit/library/initial_states/test_fermionic_gaussian_state.py b/test/second_q/circuit/library/initial_states/test_fermionic_gaussian_state.py index 56eb6b501..28dd6cf59 100644 --- a/test/second_q/circuit/library/initial_states/test_fermionic_gaussian_state.py +++ b/test/second_q/circuit/library/initial_states/test_fermionic_gaussian_state.py @@ -18,7 +18,7 @@ from qiskit.quantum_info import Statevector from qiskit_nature.second_q.circuit.library import FermionicGaussianState -from qiskit_nature.second_q.mappers import BravyiKitaevMapper, JordanWignerMapper, QubitConverter +from qiskit_nature.second_q.mappers import BravyiKitaevMapper, JordanWignerMapper from qiskit_nature.testing import random_quadratic_hamiltonian @@ -29,7 +29,6 @@ def test_fermionic_gaussian_state(self): """Test preparing fermionic Gaussian states.""" n_orbitals = 5 mapper = JordanWignerMapper() - converter = QubitConverter(mapper) quad_ham = random_quadratic_hamiltonian(n_orbitals, seed=5957) ( @@ -48,27 +47,15 @@ def test_fermionic_gaussian_state(self): range(n_orbitals), ] - with self.subTest("Qubit Converter object"): - qubit_op = converter.convert(fermionic_op) - matrix = qubit_op.to_matrix() - for occupied_orbitals in occupied_orbitals_lists: - circuit = FermionicGaussianState( - transformation_matrix, occupied_orbitals, qubit_mapper=converter - ) - final_state = np.array(Statevector(circuit)) - eig = np.sum(orbital_energies[occupied_orbitals]) + transformed_constant - np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-7) - - with self.subTest("Qubit Mapper object"): - qubit_op = mapper.map(fermionic_op) - matrix = qubit_op.to_matrix() - for occupied_orbitals in occupied_orbitals_lists: - circuit = FermionicGaussianState( - transformation_matrix, occupied_orbitals, qubit_mapper=mapper - ) - final_state = np.array(Statevector(circuit)) - eig = np.sum(orbital_energies[occupied_orbitals]) + transformed_constant - np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-7) + qubit_op = mapper.map(fermionic_op) + matrix = qubit_op.to_matrix() + for occupied_orbitals in occupied_orbitals_lists: + circuit = FermionicGaussianState( + transformation_matrix, occupied_orbitals, qubit_mapper=mapper + ) + final_state = np.array(Statevector(circuit)) + eig = np.sum(orbital_energies[occupied_orbitals]) + transformed_constant + np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-7) def test_no_side_effects(self): """Test that the routines don't mutate the input array.""" @@ -97,14 +84,6 @@ def test_circuit_kwargs(self): def test_unsupported_mapper(self): """Test passing unsupported mapper fails gracefully.""" - with self.assertRaisesRegex(NotImplementedError, "supported"): - _ = FermionicGaussianState( - np.block([np.eye(2), np.zeros((2, 2))]), - qubit_mapper=QubitConverter(BravyiKitaevMapper()), - ) - - def test_unsupported_mapper_no_converter(self): - """Test passing unsupported mapper fails gracefully when bypassing the qubit converter.""" with self.assertRaisesRegex(NotImplementedError, "supported"): _ = FermionicGaussianState( np.block([np.eye(2), np.zeros((2, 2))]), diff --git a/test/second_q/circuit/library/initial_states/test_hartree_fock.py b/test/second_q/circuit/library/initial_states/test_hartree_fock.py index fec39ff11..0c79f7002 100644 --- a/test/second_q/circuit/library/initial_states/test_hartree_fock.py +++ b/test/second_q/circuit/library/initial_states/test_hartree_fock.py @@ -13,16 +13,13 @@ """Test Hartree Fock initial state circuit.""" import unittest -import warnings from test import QiskitNatureTestCase -import numpy as np from qiskit import QuantumCircuit -from qiskit.opflow.primitive_ops.tapered_pauli_sum_op import Z2Symmetries +from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries from qiskit.quantum_info.operators.symplectic import Pauli from qiskit_nature.second_q.circuit.library import HartreeFock from qiskit_nature.second_q.circuit.library.initial_states.hartree_fock import ( - hartree_fock_bitstring, hartree_fock_bitstring_mapped, ) from qiskit_nature.second_q.mappers import ( @@ -31,32 +28,16 @@ ParityMapper, BravyiKitaevSuperFastMapper, ) -from qiskit_nature.second_q.mappers import QubitConverter +from qiskit_nature.second_q.mappers import TaperedQubitMapper class TestHartreeFock(QiskitNatureTestCase): """Initial State HartreeFock tests""" - def test_bitstring(self): - """Simple test for the bitstring function.""" - bitstr = hartree_fock_bitstring(2, (1, 1)) - self.assertTrue(all(bitstr == np.array([True, False, True, False]))) - - def test_bitstring_invalid_input(self): - """Test passing invalid input raises.""" - - with self.subTest("too many particles"): - with self.assertRaises(ValueError): - _ = hartree_fock_bitstring(2, (3, 3)) - - with self.subTest("too few orbitals"): - with self.assertRaises(ValueError): - _ = hartree_fock_bitstring(-1, (2, 2)) - - def test_raises_on_unsupported_mapper(self): + def test_raises_on_unsupported_tapered_mapper(self): """Test if an error is raised for an unsupported mapper.""" with self.assertRaises(NotImplementedError): - mapper = QubitConverter(BravyiKitaevSuperFastMapper()) + mapper = TaperedQubitMapper(BravyiKitaevSuperFastMapper()) state = HartreeFock(num_spatial_orbitals=2, num_particles=(1, 1), qubit_mapper=mapper) state.draw() @@ -69,7 +50,7 @@ def test_raises_on_unsupported_mapper_no_mapper(self): def test_qubits_4_jw_h2(self): """qubits 4 jw h2 test""" - state = HartreeFock(2, (1, 1), QubitConverter(JordanWignerMapper())) + state = HartreeFock(2, (1, 1), TaperedQubitMapper(JordanWignerMapper())) ref = QuantumCircuit(4) ref.x([0, 2]) self.assertEqual(state, ref) @@ -79,21 +60,21 @@ def test_qubits_4_jw_h2_lazy_attribute_setting(self): state = HartreeFock() state.num_spatial_orbitals = 2 state.num_particles = (1, 1) - state.qubit_mapper = QubitConverter(JordanWignerMapper()) + state.qubit_mapper = TaperedQubitMapper(JordanWignerMapper()) ref = QuantumCircuit(4) ref.x([0, 2]) self.assertEqual(state, ref) def test_qubits_4_py_h2(self): """qubits 4 py h2 test""" - state = HartreeFock(2, (1, 1), QubitConverter(ParityMapper())) + state = HartreeFock(2, (1, 1), TaperedQubitMapper(ParityMapper())) ref = QuantumCircuit(4) ref.x([0, 1]) self.assertEqual(state, ref) def test_qubits_4_bk_h2(self): """qubits 4 bk h2 test""" - state = HartreeFock(2, (1, 1), QubitConverter(BravyiKitaevMapper())) + state = HartreeFock(2, (1, 1), TaperedQubitMapper(BravyiKitaevMapper())) ref = QuantumCircuit(4) ref.x([0, 1, 2]) self.assertEqual(state, ref) @@ -101,8 +82,7 @@ def test_qubits_4_bk_h2(self): def test_qubits_2_py_h2(self): """qubits 2 py h2 test""" num_particles = (1, 1) - mapper = QubitConverter(ParityMapper(), two_qubit_reduction=True) - mapper.force_match(num_particles=num_particles) + mapper = TaperedQubitMapper(ParityMapper(num_particles)) state = HartreeFock(2, num_particles, mapper) ref = QuantumCircuit(2) ref.x(0) @@ -111,14 +91,15 @@ def test_qubits_2_py_h2(self): def test_qubits_6_py_lih(self): """qubits 6 py lih test""" num_particles = (1, 1) - mapper = QubitConverter(ParityMapper(), two_qubit_reduction=True) z2symmetries = Z2Symmetries( symmetries=[Pauli("ZIZIZIZI"), Pauli("ZZIIZZII")], sq_paulis=[Pauli("IIIIIIXI"), Pauli("IIIIIXII")], sq_list=[2, 3], tapering_values=[1, 1], ) - mapper.force_match(num_particles=num_particles, z2symmetries=z2symmetries) + mapper = TaperedQubitMapper( + ParityMapper(num_particles=num_particles), z2symmetries=z2symmetries + ) state = HartreeFock(5, num_particles, mapper) ref = QuantumCircuit(6) ref.x([0, 1]) @@ -126,107 +107,27 @@ def test_qubits_6_py_lih(self): def test_hf_bitstring_mapped(self): """Mapped bitstring test for water""" - # Original driver config when creating operator that resulted in symmetries coded - # below. The sector [1, -1] is the correct ground sector. - # PySCFDriver( - # atom="O 0.0000 0.0000 0.1173; H 0.0000 0.07572 -0.4692;H 0.0000 -0.07572 -0.4692", - # unit=DistanceUnit.ANGSTROM, - # charge=0, - # spin=0, - # basis='sto-3g', - # hf_method=HFMethodType.RHF) + num_spatial_orbitals = 7 num_particles = (5, 5) - mapper = QubitConverter(ParityMapper(), two_qubit_reduction=True) z2symmetries = Z2Symmetries( symmetries=[Pauli("IZZIIIIZZIII"), Pauli("ZZIZIIZZIZII")], sq_paulis=[Pauli("IIIIIIIIXIII"), Pauli("IIIIIIIIIXII")], sq_list=[3, 2], tapering_values=[1, -1], ) - with self.subTest("Matched bitsring creation"): - mapper.force_match(num_particles=num_particles, z2symmetries=z2symmetries) - bitstr = hartree_fock_bitstring_mapped( - num_spatial_orbitals=num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=mapper, - ) - ref_matched = [True, False, True, True, False, True, False, True, False, False] - self.assertListEqual(bitstr, ref_matched) - with self.subTest("Bitsring creation with no tapering"): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - bitstr = hartree_fock_bitstring_mapped( - num_spatial_orbitals=num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=mapper, - match_convert=False, - ) - ref_notaper = [ - True, - False, - True, - False, - True, - True, - False, - True, - False, - True, - False, - False, - ] - self.assertListEqual(bitstr, ref_notaper) - - def test_hf_bitstring_mapped_with_qubitmapper(self): - """Mapped bitstring test for water with qubit mapper""" + mapper = TaperedQubitMapper( + ParityMapper(num_particles=num_particles), z2symmetries=z2symmetries + ) - num_spatial_orbitals = 7 - num_particles = (5, 5) + bitstr = hartree_fock_bitstring_mapped( + num_spatial_orbitals=num_spatial_orbitals, + num_particles=num_particles, + qubit_mapper=mapper, + ) - ref_notaper_no_red = [ - True, - False, - True, - False, - True, - True, - True, - False, - True, - False, - True, - False, - False, - False, - ] - - with self.subTest("Qubit Converter object"): - mapper = QubitConverter(ParityMapper()) - bitstr = hartree_fock_bitstring_mapped( - num_spatial_orbitals=num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=mapper, - ) - self.assertListEqual(bitstr, ref_notaper_no_red) - - with self.subTest("Qubit Mapper object"): - mapper = ParityMapper() - bitstr = hartree_fock_bitstring_mapped( - num_spatial_orbitals=num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=mapper, - ) - self.assertListEqual(bitstr, ref_notaper_no_red) - - with self.subTest("ParityMapper with builtin two-qubit reduction"): - mapper = ParityMapper(num_particles=num_particles) - bitstr = hartree_fock_bitstring_mapped( - num_spatial_orbitals=num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=mapper, - ) - self.assertListEqual(bitstr, ref_notaper_no_red[:5] + ref_notaper_no_red[6:-1]) + ref_matched = [True, False, True, True, False, True, False, True, False, False] + self.assertListEqual(bitstr, ref_matched) if __name__ == "__main__": diff --git a/test/second_q/circuit/library/initial_states/test_hartree_fock_mapper.py b/test/second_q/circuit/library/initial_states/test_hartree_fock_mapper.py deleted file mode 100644 index 0c79f7002..000000000 --- a/test/second_q/circuit/library/initial_states/test_hartree_fock_mapper.py +++ /dev/null @@ -1,134 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2020, 2023. -# -# 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 Hartree Fock initial state circuit.""" - -import unittest -from test import QiskitNatureTestCase - -from qiskit import QuantumCircuit -from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries -from qiskit.quantum_info.operators.symplectic import Pauli -from qiskit_nature.second_q.circuit.library import HartreeFock -from qiskit_nature.second_q.circuit.library.initial_states.hartree_fock import ( - hartree_fock_bitstring_mapped, -) -from qiskit_nature.second_q.mappers import ( - BravyiKitaevMapper, - JordanWignerMapper, - ParityMapper, - BravyiKitaevSuperFastMapper, -) -from qiskit_nature.second_q.mappers import TaperedQubitMapper - - -class TestHartreeFock(QiskitNatureTestCase): - """Initial State HartreeFock tests""" - - def test_raises_on_unsupported_tapered_mapper(self): - """Test if an error is raised for an unsupported mapper.""" - with self.assertRaises(NotImplementedError): - mapper = TaperedQubitMapper(BravyiKitaevSuperFastMapper()) - state = HartreeFock(num_spatial_orbitals=2, num_particles=(1, 1), qubit_mapper=mapper) - state.draw() - - def test_raises_on_unsupported_mapper_no_mapper(self): - """Test if an error is raised for an unsupported mapper.""" - with self.assertRaises(NotImplementedError): - mapper = BravyiKitaevSuperFastMapper() - state = HartreeFock(num_spatial_orbitals=2, num_particles=(1, 1), qubit_mapper=mapper) - state.draw() - - def test_qubits_4_jw_h2(self): - """qubits 4 jw h2 test""" - state = HartreeFock(2, (1, 1), TaperedQubitMapper(JordanWignerMapper())) - ref = QuantumCircuit(4) - ref.x([0, 2]) - self.assertEqual(state, ref) - - def test_qubits_4_jw_h2_lazy_attribute_setting(self): - """qubits 4 jw h2 with lazy attribute setting test""" - state = HartreeFock() - state.num_spatial_orbitals = 2 - state.num_particles = (1, 1) - state.qubit_mapper = TaperedQubitMapper(JordanWignerMapper()) - ref = QuantumCircuit(4) - ref.x([0, 2]) - self.assertEqual(state, ref) - - def test_qubits_4_py_h2(self): - """qubits 4 py h2 test""" - state = HartreeFock(2, (1, 1), TaperedQubitMapper(ParityMapper())) - ref = QuantumCircuit(4) - ref.x([0, 1]) - self.assertEqual(state, ref) - - def test_qubits_4_bk_h2(self): - """qubits 4 bk h2 test""" - state = HartreeFock(2, (1, 1), TaperedQubitMapper(BravyiKitaevMapper())) - ref = QuantumCircuit(4) - ref.x([0, 1, 2]) - self.assertEqual(state, ref) - - def test_qubits_2_py_h2(self): - """qubits 2 py h2 test""" - num_particles = (1, 1) - mapper = TaperedQubitMapper(ParityMapper(num_particles)) - state = HartreeFock(2, num_particles, mapper) - ref = QuantumCircuit(2) - ref.x(0) - self.assertEqual(state, ref) - - def test_qubits_6_py_lih(self): - """qubits 6 py lih test""" - num_particles = (1, 1) - z2symmetries = Z2Symmetries( - symmetries=[Pauli("ZIZIZIZI"), Pauli("ZZIIZZII")], - sq_paulis=[Pauli("IIIIIIXI"), Pauli("IIIIIXII")], - sq_list=[2, 3], - tapering_values=[1, 1], - ) - mapper = TaperedQubitMapper( - ParityMapper(num_particles=num_particles), z2symmetries=z2symmetries - ) - state = HartreeFock(5, num_particles, mapper) - ref = QuantumCircuit(6) - ref.x([0, 1]) - self.assertEqual(state, ref) - - def test_hf_bitstring_mapped(self): - """Mapped bitstring test for water""" - - num_spatial_orbitals = 7 - num_particles = (5, 5) - z2symmetries = Z2Symmetries( - symmetries=[Pauli("IZZIIIIZZIII"), Pauli("ZZIZIIZZIZII")], - sq_paulis=[Pauli("IIIIIIIIXIII"), Pauli("IIIIIIIIIXII")], - sq_list=[3, 2], - tapering_values=[1, -1], - ) - mapper = TaperedQubitMapper( - ParityMapper(num_particles=num_particles), z2symmetries=z2symmetries - ) - - bitstr = hartree_fock_bitstring_mapped( - num_spatial_orbitals=num_spatial_orbitals, - num_particles=num_particles, - qubit_mapper=mapper, - ) - - ref_matched = [True, False, True, True, False, True, False, True, False, False] - self.assertListEqual(bitstr, ref_matched) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/second_q/circuit/library/initial_states/test_slater_determinant.py b/test/second_q/circuit/library/initial_states/test_slater_determinant.py index 52d7b7142..4da1bf4de 100644 --- a/test/second_q/circuit/library/initial_states/test_slater_determinant.py +++ b/test/second_q/circuit/library/initial_states/test_slater_determinant.py @@ -18,7 +18,7 @@ from qiskit.quantum_info import Statevector from qiskit_nature.second_q.circuit.library import SlaterDeterminant -from qiskit_nature.second_q.mappers import BravyiKitaevMapper, JordanWignerMapper, QubitConverter +from qiskit_nature.second_q.mappers import BravyiKitaevMapper, JordanWignerMapper from qiskit_nature.testing import random_quadratic_hamiltonian @@ -29,7 +29,6 @@ def test_slater_determinant(self): """Test preparing Slater determinants.""" n_orbitals = 5 mapper = JordanWignerMapper() - converter = QubitConverter(mapper) quad_ham = random_quadratic_hamiltonian(n_orbitals, num_conserving=True, seed=8839) ( transformation_matrix, @@ -38,27 +37,13 @@ def test_slater_determinant(self): ) = quad_ham.diagonalizing_bogoliubov_transform() fermionic_op = quad_ham.second_q_op() - with self.subTest("Qubit Converter object"): - qubit_op = converter.convert(fermionic_op) - matrix = qubit_op.to_matrix() - for n_particles in range(n_orbitals + 1): - circuit = SlaterDeterminant( - transformation_matrix[:n_particles], qubit_mapper=converter - ) - final_state = np.array(Statevector(circuit)) - eig = np.sum(orbital_energies[:n_particles]) + transformed_constant - np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-7) - - with self.subTest("Qubit Mapper object"): - qubit_op = mapper.map(fermionic_op) - matrix = qubit_op.to_matrix() - for n_particles in range(n_orbitals + 1): - circuit = SlaterDeterminant( - transformation_matrix[:n_particles], qubit_mapper=mapper - ) - final_state = np.array(Statevector(circuit)) - eig = np.sum(orbital_energies[:n_particles]) + transformed_constant - np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-7) + qubit_op = mapper.map(fermionic_op) + matrix = qubit_op.to_matrix() + for n_particles in range(n_orbitals + 1): + circuit = SlaterDeterminant(transformation_matrix[:n_particles], qubit_mapper=mapper) + final_state = np.array(Statevector(circuit)) + eig = np.sum(orbital_energies[:n_particles]) + transformed_constant + np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-7) def test_no_side_effects(self): """Test that the routines don't mutate the input array.""" @@ -86,10 +71,5 @@ def test_circuit_kwargs(self): def test_unsupported_mapper(self): """Test passing unsupported mapper fails gracefully.""" - with self.assertRaisesRegex(NotImplementedError, "supported"): - _ = SlaterDeterminant(np.eye(2), qubit_mapper=QubitConverter(BravyiKitaevMapper())) - - def test_unsupported_mapper_no_converter(self): - """Test passing unsupported mapper fails gracefully when bypassing the qubit converter.""" with self.assertRaisesRegex(NotImplementedError, "supported"): _ = SlaterDeterminant(np.eye(2), qubit_mapper=BravyiKitaevMapper()) diff --git a/test/second_q/circuit/library/initial_states/test_vscf.py b/test/second_q/circuit/library/initial_states/test_vscf.py index 4d513ed0a..753b67415 100644 --- a/test/second_q/circuit/library/initial_states/test_vscf.py +++ b/test/second_q/circuit/library/initial_states/test_vscf.py @@ -19,7 +19,7 @@ from qiskit import QuantumCircuit from qiskit_nature.second_q.circuit.library import VSCF from qiskit_nature.second_q.circuit.library.initial_states.vscf import vscf_bitstring -from qiskit_nature.second_q.mappers import DirectMapper, TaperedQubitMapper, QubitConverter +from qiskit_nature.second_q.mappers import DirectMapper, TaperedQubitMapper class TestVSCF(QiskitNatureTestCase): @@ -52,19 +52,6 @@ def test_qubits_6_lazy_attribute_setting(self): """Test 2 modes 2 modal for the first mode and 4 modals for the second with lazy attribute setting.""" num_modals = [2, 4] - mapper = QubitConverter(DirectMapper()) - vscf = VSCF() - vscf.num_modals = num_modals - vscf.qubit_mapper = mapper - ref = QuantumCircuit(6) - ref.x([0, 2]) - - self.assertEqual(ref, vscf) - - def test_qubits_6_lazy_attribute_setting_no_converter(self): - """Test 2 modes 2 modal for the first mode and 4 modals for the second - with lazy attribute setting when bypassing the Qubit converter.""" - num_modals = [2, 4] mapper = DirectMapper() vscf = VSCF() vscf.num_modals = num_modals diff --git a/test/second_q/circuit/library/test_bogoliubov_transform.py b/test/second_q/circuit/library/test_bogoliubov_transform.py index 19a0a3cca..cbeb85115 100644 --- a/test/second_q/circuit/library/test_bogoliubov_transform.py +++ b/test/second_q/circuit/library/test_bogoliubov_transform.py @@ -21,7 +21,7 @@ from qiskit_nature.second_q.circuit.library import BogoliubovTransform from qiskit_nature.second_q.hamiltonians.quadratic_hamiltonian import QuadraticHamiltonian -from qiskit_nature.second_q.mappers import BravyiKitaevMapper, JordanWignerMapper, QubitConverter +from qiskit_nature.second_q.mappers import BravyiKitaevMapper, JordanWignerMapper from qiskit_nature.testing import random_quadratic_hamiltonian @@ -41,7 +41,6 @@ class TestBogoliubovTransform(QiskitNatureTestCase): def test_bogoliubov_transform(self, n_orbitals, num_conserving): """Test Bogoliubov transform.""" mapper = JordanWignerMapper() - converter = QubitConverter(mapper) hamiltonian = random_quadratic_hamiltonian( n_orbitals, num_conserving=num_conserving, seed=5740 ) @@ -51,25 +50,14 @@ def test_bogoliubov_transform(self, n_orbitals, num_conserving): transformed_constant, ) = hamiltonian.diagonalizing_bogoliubov_transform() - with self.subTest("Qubit Converter object"): - matrix = converter.convert_only(hamiltonian.second_q_op()).to_matrix() - bog_circuit = BogoliubovTransform(transformation_matrix, qubit_mapper=converter) - for initial_state in range(2**n_orbitals): - state = Statevector.from_int(initial_state, dims=2**n_orbitals) - final_state = np.array(state.evolve(bog_circuit)) - occupied_orbitals = [i for i in range(n_orbitals) if initial_state >> i & 1] - eig = np.sum(orbital_energies[occupied_orbitals]) + transformed_constant - np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-8) - - with self.subTest("Qubit Mapper object"): - matrix = mapper.map(hamiltonian.second_q_op()).to_matrix() - bog_circuit = BogoliubovTransform(transformation_matrix, qubit_mapper=mapper) - for initial_state in range(2**n_orbitals): - state = Statevector.from_int(initial_state, dims=2**n_orbitals) - final_state = np.array(state.evolve(bog_circuit)) - occupied_orbitals = [i for i in range(n_orbitals) if initial_state >> i & 1] - eig = np.sum(orbital_energies[occupied_orbitals]) + transformed_constant - np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-8) + matrix = mapper.map(hamiltonian.second_q_op()).to_matrix() + bog_circuit = BogoliubovTransform(transformation_matrix, qubit_mapper=mapper) + for initial_state in range(2**n_orbitals): + state = Statevector.from_int(initial_state, dims=2**n_orbitals) + final_state = np.array(state.evolve(bog_circuit)) + occupied_orbitals = [i for i in range(n_orbitals) if initial_state >> i & 1] + eig = np.sum(orbital_energies[occupied_orbitals]) + transformed_constant + np.testing.assert_allclose(matrix @ final_state, eig * final_state, atol=1e-8) @data(4, 5) def test_bogoliubov_transform_compose_num_conserving(self, n_orbitals): @@ -152,10 +140,5 @@ def test_circuit_kwargs(self): def test_unsupported_mapper(self): """Test passing unsupported mapper fails gracefully.""" - with self.assertRaisesRegex(NotImplementedError, "supported"): - _ = BogoliubovTransform(np.eye(2), qubit_mapper=QubitConverter(BravyiKitaevMapper())) - - def test_unsupported_mapper_no_converter(self): - """Test passing unsupported mapper fails gracefully when bypassing the qubit converter.""" with self.assertRaisesRegex(NotImplementedError, "supported"): _ = BogoliubovTransform(np.eye(2), qubit_mapper=BravyiKitaevMapper()) diff --git a/test/second_q/drivers/pyscfd/test_driver_methods_pyscf.py b/test/second_q/drivers/pyscfd/test_driver_methods_pyscf.py index 750862931..adc3d451c 100644 --- a/test/second_q/drivers/pyscfd/test_driver_methods_pyscf.py +++ b/test/second_q/drivers/pyscfd/test_driver_methods_pyscf.py @@ -15,12 +15,11 @@ import unittest from test.second_q.drivers.test_driver_methods_gsc import TestDriverMethods +from qiskit.test import slow_test from qiskit_nature.units import DistanceUnit from qiskit_nature.second_q.drivers import PySCFDriver, MethodType from qiskit_nature.second_q.mappers import BravyiKitaevMapper, ParityMapper -from qiskit_nature.second_q.mappers import QubitConverter from qiskit_nature.second_q.transformers import FreezeCoreTransformer -from qiskit_nature.settings import settings import qiskit_nature.optionals as _optionals @@ -83,7 +82,7 @@ def test_lih_rhf_parity(self): ) result = self._run_driver( driver, - converter=QubitConverter(ParityMapper()), + mapper=ParityMapper(), transformers=[FreezeCoreTransformer()], ) self._assert_energy_and_dipole(result, "lih") @@ -100,7 +99,9 @@ def test_lih_rhf_parity_2q(self): ) result = self._run_driver( driver, - converter=QubitConverter(ParityMapper(), two_qubit_reduction=True), + # NOTE: the particle number provided below already needs to take the core orbital + # freezing into account + mapper=ParityMapper(num_particles=(1, 1)), transformers=[FreezeCoreTransformer()], ) self._assert_energy_and_dipole(result, "lih") @@ -117,11 +118,12 @@ def test_lih_rhf_bk(self): ) result = self._run_driver( driver, - converter=QubitConverter(BravyiKitaevMapper()), + mapper=BravyiKitaevMapper(), transformers=[FreezeCoreTransformer()], ) self._assert_energy_and_dipole(result, "lih") + @slow_test def test_oh_rohf(self): """oh rohf test""" driver = PySCFDriver( @@ -135,6 +137,7 @@ def test_oh_rohf(self): result = self._run_driver(driver) self._assert_energy_and_dipole(result, "oh") + @slow_test def test_oh_uhf(self): """oh uhf test""" driver = PySCFDriver( @@ -148,6 +151,7 @@ def test_oh_uhf(self): result = self._run_driver(driver) self._assert_energy_and_dipole(result, "oh") + @slow_test def test_oh_rohf_parity(self): """oh rohf parity test""" driver = PySCFDriver( @@ -158,9 +162,10 @@ def test_oh_rohf_parity(self): basis="sto-3g", method=MethodType.ROHF, ) - result = self._run_driver(driver, converter=QubitConverter(ParityMapper())) + result = self._run_driver(driver, mapper=ParityMapper()) self._assert_energy_and_dipole(result, "oh") + @slow_test def test_oh_rohf_parity_2q(self): """oh rohf parity 2q test""" driver = PySCFDriver( @@ -171,11 +176,10 @@ def test_oh_rohf_parity_2q(self): basis="sto-3g", method=MethodType.ROHF, ) - result = self._run_driver( - driver, converter=QubitConverter(ParityMapper(), two_qubit_reduction=True) - ) + result = self._run_driver(driver, mapper=ParityMapper(num_particles=(5, 4))) self._assert_energy_and_dipole(result, "oh") + @slow_test def test_oh_uhf_parity(self): """oh uhf parity test""" driver = PySCFDriver( @@ -186,9 +190,10 @@ def test_oh_uhf_parity(self): basis="sto-3g", method=MethodType.UHF, ) - result = self._run_driver(driver, converter=QubitConverter(ParityMapper())) + result = self._run_driver(driver, mapper=ParityMapper()) self._assert_energy_and_dipole(result, "oh") + @slow_test def test_oh_uhf_parity_2q(self): """oh uhf parity 2q test""" driver = PySCFDriver( @@ -199,11 +204,10 @@ def test_oh_uhf_parity_2q(self): basis="sto-3g", method=MethodType.UHF, ) - result = self._run_driver( - driver, converter=QubitConverter(ParityMapper(), two_qubit_reduction=True) - ) + result = self._run_driver(driver, mapper=ParityMapper(num_particles=(5, 4))) self._assert_energy_and_dipole(result, "oh") + @slow_test def test_oh_rohf_bk(self): """oh rohf bk test""" driver = PySCFDriver( @@ -214,9 +218,10 @@ def test_oh_rohf_bk(self): basis="sto-3g", method=MethodType.ROHF, ) - result = self._run_driver(driver, converter=QubitConverter(BravyiKitaevMapper())) + result = self._run_driver(driver, mapper=BravyiKitaevMapper()) self._assert_energy_and_dipole(result, "oh") + @slow_test def test_oh_uhf_bk(self): """oh uhf bk test""" driver = PySCFDriver( @@ -227,23 +232,9 @@ def test_oh_uhf_bk(self): basis="sto-3g", method=MethodType.UHF, ) - result = self._run_driver(driver, converter=QubitConverter(BravyiKitaevMapper())) + result = self._run_driver(driver, mapper=BravyiKitaevMapper()) self._assert_energy_and_dipole(result, "oh") -class TestDriverMethodsPySCFSymmetric(TestDriverMethodsPySCF): - """Driver Methods PySCF tests with symmetry-reduced integrals enabled""" - - @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") - def setUp(self): - super().setUp() - self._prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = True - - def tearDown(self): - super().tearDown() - settings.use_symmetry_reduced_integrals = self._prev_setting - - if __name__ == "__main__": unittest.main() diff --git a/test/second_q/drivers/pyscfd/test_driver_pyscf.py b/test/second_q/drivers/pyscfd/test_driver_pyscf.py index 7e37019c8..ee1e6e9db 100644 --- a/test/second_q/drivers/pyscfd/test_driver_pyscf.py +++ b/test/second_q/drivers/pyscfd/test_driver_pyscf.py @@ -16,16 +16,12 @@ from test import QiskitNatureTestCase from test.second_q.drivers.test_driver import TestDriver -from ddt import ddt, data - from qiskit_nature.units import DistanceUnit from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature import QiskitNatureError -from qiskit_nature.settings import settings import qiskit_nature.optionals as _optionals -@ddt class TestDriverPySCF(QiskitNatureTestCase, TestDriver): """PYSCF Driver tests.""" @@ -41,70 +37,38 @@ def setUp(self): ) self.driver_result = driver.run() - @data(True, False) - def test_h3(self, use_symmetry_reduced_integrals: bool): + def test_h3(self): """Test for H3 chain, see also https://github.com/Qiskit/qiskit-aqua/issues/1148.""" - prev_settings = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - atom = "H 0 0 0; H 0 0 1; H 0 0 2" - driver = PySCFDriver( - atom=atom, unit=DistanceUnit.ANGSTROM, charge=0, spin=1, basis="sto3g" - ) - driver_result = driver.run() - self.assertAlmostEqual(driver_result.reference_energy, -1.523996200246108, places=5) - finally: - settings.use_symmetry_reduced_integrals = prev_settings + atom = "H 0 0 0; H 0 0 1; H 0 0 2" + driver = PySCFDriver(atom=atom, unit=DistanceUnit.ANGSTROM, charge=0, spin=1, basis="sto3g") + driver_result = driver.run() + self.assertAlmostEqual(driver_result.reference_energy, -1.523996200246108, places=5) - @data(True, False) - def test_h4(self, use_symmetry_reduced_integrals: bool): + def test_h4(self): """Test for H4 chain""" - prev_settings = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - atom = "H 0 0 0; H 0 0 1; H 0 0 2; H 0 0 3" - driver = PySCFDriver( - atom=atom, unit=DistanceUnit.ANGSTROM, charge=0, spin=0, basis="sto3g" - ) - driver_result = driver.run() - self.assertAlmostEqual(driver_result.reference_energy, -2.09854593699776, places=5) - finally: - settings.use_symmetry_reduced_integrals = prev_settings + atom = "H 0 0 0; H 0 0 1; H 0 0 2; H 0 0 3" + driver = PySCFDriver(atom=atom, unit=DistanceUnit.ANGSTROM, charge=0, spin=0, basis="sto3g") + driver_result = driver.run() + self.assertAlmostEqual(driver_result.reference_energy, -2.09854593699776, places=5) def test_invalid_atom_type(self): """Atom is string with ; separator or list of string""" with self.assertRaises(QiskitNatureError): PySCFDriver(atom=("H", 0, 0, 0)) - @data(True, False) - def test_list_atom(self, use_symmetry_reduced_integrals: bool): + def test_list_atom(self): """Check input with list of strings""" - prev_settings = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - atom = ["H 0 0 0", "H 0 0 1"] - driver = PySCFDriver( - atom=atom, unit=DistanceUnit.ANGSTROM, charge=0, spin=0, basis="sto3g" - ) - driver_result = driver.run() - self.assertAlmostEqual(driver_result.reference_energy, -1.0661086493179366, places=5) - finally: - settings.use_symmetry_reduced_integrals = prev_settings + atom = ["H 0 0 0", "H 0 0 1"] + driver = PySCFDriver(atom=atom, unit=DistanceUnit.ANGSTROM, charge=0, spin=0, basis="sto3g") + driver_result = driver.run() + self.assertAlmostEqual(driver_result.reference_energy, -1.0661086493179366, places=5) - @data(True, False) - def test_zmatrix(self, use_symmetry_reduced_integrals: bool): + def test_zmatrix(self): """Check z-matrix input""" - prev_settings = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - atom = "H; H 1 1.0" - driver = PySCFDriver( - atom=atom, unit=DistanceUnit.ANGSTROM, charge=0, spin=0, basis="sto3g" - ) - driver_result = driver.run() - self.assertAlmostEqual(driver_result.reference_energy, -1.0661086493179366, places=5) - finally: - settings.use_symmetry_reduced_integrals = prev_settings + atom = "H; H 1 1.0" + driver = PySCFDriver(atom=atom, unit=DistanceUnit.ANGSTROM, charge=0, spin=0, basis="sto3g") + driver_result = driver.run() + self.assertAlmostEqual(driver_result.reference_energy, -1.0661086493179366, places=5) if __name__ == "__main__": diff --git a/test/second_q/drivers/test_driver_methods_gsc.py b/test/second_q/drivers/test_driver_methods_gsc.py index 1e43db460..ecce0e77c 100644 --- a/test/second_q/drivers/test_driver_methods_gsc.py +++ b/test/second_q/drivers/test_driver_methods_gsc.py @@ -20,8 +20,7 @@ from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver from qiskit_nature.second_q.algorithms import GroundStateEigensolver from qiskit_nature.second_q.drivers import ElectronicStructureDriver -from qiskit_nature.second_q.mappers import JordanWignerMapper -from qiskit_nature.second_q.mappers import QubitConverter +from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitMapper from qiskit_nature.second_q.problems import BaseProblem from qiskit_nature.second_q.transformers import BaseTransformer @@ -39,7 +38,7 @@ def setUp(self): @staticmethod def _run_driver( driver: ElectronicStructureDriver, - converter: QubitConverter = QubitConverter(JordanWignerMapper()), + mapper: QubitMapper = JordanWignerMapper(), transformers: Optional[List[BaseTransformer]] = None, ): @@ -51,7 +50,7 @@ def _run_driver( solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(converter, solver) + gsc = GroundStateEigensolver(mapper, solver) result = gsc.solve(problem) return result diff --git a/test/second_q/formats/fcidump/test_fcidump.py b/test/second_q/formats/fcidump/test_fcidump.py index 238a7f290..6760ef871 100644 --- a/test/second_q/formats/fcidump/test_fcidump.py +++ b/test/second_q/formats/fcidump/test_fcidump.py @@ -14,7 +14,6 @@ import builtins import unittest -import warnings from abc import ABC, abstractmethod from test import QiskitNatureTestCase from test.second_q.utils import get_expected_two_body_ints @@ -157,12 +156,10 @@ def setUp(self): ) self.mo_eri_ba = None self.mo_eri_bb = None - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - fcidump = FCIDump.from_file( - self.get_resource_path("test_fcidump_h2.fcidump", "second_q/formats/fcidump") - ) - self.problem = fcidump_to_problem(fcidump) + fcidump = FCIDump.from_file( + self.get_resource_path("test_fcidump_h2.fcidump", "second_q/formats/fcidump") + ) + self.problem = fcidump_to_problem(fcidump) class TestFCIDumpLiH(QiskitNatureTestCase, BaseTestFCIDump): @@ -180,12 +177,10 @@ def setUp(self): self.mo_eri = loaded["mo_eri"] self.mo_eri_ba = None self.mo_eri_bb = None - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - fcidump = FCIDump.from_file( - self.get_resource_path("test_fcidump_lih.fcidump", "second_q/formats/fcidump") - ) - self.problem = fcidump_to_problem(fcidump) + fcidump = FCIDump.from_file( + self.get_resource_path("test_fcidump_lih.fcidump", "second_q/formats/fcidump") + ) + self.problem = fcidump_to_problem(fcidump) class TestFCIDumpOH(QiskitNatureTestCase, BaseTestFCIDump): @@ -203,12 +198,10 @@ def setUp(self): self.mo_eri = loaded["mo_eri"] self.mo_eri_ba = loaded["mo_eri_ba"] self.mo_eri_bb = loaded["mo_eri_bb"] - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - fcidump = FCIDump.from_file( - self.get_resource_path("test_fcidump_oh.fcidump", "second_q/formats/fcidump") - ) - self.problem = fcidump_to_problem(fcidump) + fcidump = FCIDump.from_file( + self.get_resource_path("test_fcidump_oh.fcidump", "second_q/formats/fcidump") + ) + self.problem = fcidump_to_problem(fcidump) if __name__ == "__main__": diff --git a/test/second_q/formats/fcidump/test_fcidump_dumper.py b/test/second_q/formats/fcidump/test_fcidump_dumper.py index a392643d4..510cac723 100644 --- a/test/second_q/formats/fcidump/test_fcidump_dumper.py +++ b/test/second_q/formats/fcidump/test_fcidump_dumper.py @@ -15,21 +15,18 @@ import tempfile import builtins import unittest -import warnings from abc import ABC, abstractmethod from test import QiskitNatureTestCase from pathlib import Path import numpy as np -from ddt import ddt, data - from qiskit_nature import QiskitNatureError from qiskit_nature.second_q.formats.fcidump import FCIDump +from qiskit_nature.second_q.operators.symmetric_two_body import S1Integrals from qiskit_nature.second_q.operators.tensor_ordering import to_chemist_ordering, find_index_order from qiskit_nature.second_q.problems import ElectronicStructureProblem from qiskit_nature.units import DistanceUnit from qiskit_nature.second_q.drivers import PySCFDriver -from qiskit_nature.settings import settings import qiskit_nature.optionals as _optionals @@ -161,49 +158,38 @@ def dump(problem: ElectronicStructureProblem, outpath: Path) -> None: hijkl = electronic_integrals.alpha.get("++--", None) hijkl_ba = electronic_integrals.beta_alpha.get("++--", None) hijkl_bb = electronic_integrals.beta.get("++--", None) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - fcidump = FCIDump( - num_electrons=problem.num_alpha + problem.num_beta, - hij=electronic_integrals.alpha.get("+-", None), - hij_b=electronic_integrals.beta.get("+-", None), - hijkl=to_chemist_ordering(hijkl) if hijkl is not None else None, - hijkl_ba=to_chemist_ordering(hijkl_ba, index_order=find_index_order(hijkl)) - if hijkl_ba is not None - else None, - hijkl_bb=to_chemist_ordering(hijkl_bb) if hijkl_bb is not None else None, - multiplicity=problem.molecule.multiplicity, - constant_energy=electronic_energy.nuclear_repulsion_energy, - ) - fcidump.to_file(outpath) + fcidump = FCIDump( + num_electrons=problem.num_alpha + problem.num_beta, + hij=electronic_integrals.alpha.get("+-", None), + hij_b=electronic_integrals.beta.get("+-", None), + hijkl=S1Integrals(to_chemist_ordering(hijkl)) if hijkl is not None else None, + hijkl_ba=S1Integrals(to_chemist_ordering(hijkl_ba, index_order=find_index_order(hijkl))) + if hijkl_ba is not None + else None, + hijkl_bb=S1Integrals(to_chemist_ordering(hijkl_bb)) if hijkl_bb is not None else None, + multiplicity=problem.molecule.multiplicity, + constant_energy=electronic_energy.nuclear_repulsion_energy, + ) + fcidump.to_file(outpath) -@ddt class TestFCIDumpDumpOH(QiskitNatureTestCase): """RHF FCIDump tests.""" - @data(True, False) - def test_dump_unrestricted_spin(self, use_symmetry_reduced_integrals: bool): + def test_dump_unrestricted_spin(self): """Tests dumping unrestricted spin data. PySCF cannot handle this themselves, so we need to rely on a custom FCIDUMP file, load it, dump it again, and make sure it did not change.""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - path = self.get_resource_path("test_fcidump_oh.fcidump", "second_q/formats/fcidump") - fcidump = FCIDump.from_file(path) - with tempfile.TemporaryDirectory() as dump_dir: - dump_file = Path(dump_dir) / "fcidump" - fcidump.to_file(dump_file) - - with open(path, "r", encoding="utf-8") as reference: - with open(dump_file, "r", encoding="utf-8") as result: - for ref, res in zip(reference.readlines(), result.readlines()): - self.assertEqual(ref.strip(), res.strip()) - finally: - settings.use_symmetry_reduced_integrals = prev_setting + path = self.get_resource_path("test_fcidump_oh.fcidump", "second_q/formats/fcidump") + fcidump = FCIDump.from_file(path) + with tempfile.TemporaryDirectory() as dump_dir: + dump_file = Path(dump_dir) / "fcidump" + fcidump.to_file(dump_file) + + with open(path, "r", encoding="utf-8") as reference: + with open(dump_file, "r", encoding="utf-8") as result: + for ref, res in zip(reference.readlines(), result.readlines()): + self.assertEqual(ref.strip(), res.strip()) if __name__ == "__main__": diff --git a/test/second_q/formats/fcidump/test_methods_fcidump.py b/test/second_q/formats/fcidump/test_methods_fcidump.py index 094590566..fee45c65e 100644 --- a/test/second_q/formats/fcidump/test_methods_fcidump.py +++ b/test/second_q/formats/fcidump/test_methods_fcidump.py @@ -13,32 +13,27 @@ """ Test Methods FCIDump """ import unittest -import warnings from typing import List, Optional from test.second_q.drivers.test_driver_methods_gsc import TestDriverMethods -from ddt import ddt, data - from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver from qiskit_nature.second_q.algorithms import GroundStateEigensolver from qiskit_nature.second_q.formats.fcidump_translator import fcidump_to_problem from qiskit_nature.second_q.formats.fcidump import FCIDump from qiskit_nature.second_q.transformers import BaseTransformer, ActiveSpaceTransformer from qiskit_nature.second_q.problems import BaseProblem -from qiskit_nature.second_q.mappers import QubitConverter, JordanWignerMapper +from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitMapper from qiskit_nature.second_q.problems import EigenstateResult -from qiskit_nature.settings import settings -@ddt class TestMethodsFCIDump(TestDriverMethods): """Methods FCIDump tests""" @staticmethod def _run_fcidump( fcidump: FCIDump, - converter: QubitConverter = QubitConverter(JordanWignerMapper()), + mapper: QubitMapper = JordanWignerMapper(), transformers: Optional[List[BaseTransformer]] = None, ) -> EigenstateResult: problem: BaseProblem = fcidump_to_problem(fcidump) @@ -49,76 +44,42 @@ def _run_fcidump( solver = NumPyMinimumEigensolver() - gsc = GroundStateEigensolver(converter, solver) + gsc = GroundStateEigensolver(mapper, solver) result = gsc.solve(problem) return result - @data(True, False) - def test_lih(self, use_symmetry_reduced_integrals: bool): + def test_lih(self): """LiH test""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - fcidump = FCIDump.from_file( - self.get_resource_path("test_fcidump_lih.fcidump", "second_q/formats/fcidump") - ) - result = self._run_fcidump(fcidump) - self._assert_energy(result, "lih") - finally: - settings.use_symmetry_reduced_integrals = prev_setting - - @data(True, False) - def test_oh(self, use_symmetry_reduced_integrals: bool): + fcidump = FCIDump.from_file( + self.get_resource_path("test_fcidump_lih.fcidump", "second_q/formats/fcidump") + ) + result = self._run_fcidump(fcidump) + self._assert_energy(result, "lih") + + def test_oh(self): """OH test""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - fcidump = FCIDump.from_file( - self.get_resource_path("test_fcidump_oh.fcidump", "second_q/formats/fcidump") - ) - result = self._run_fcidump(fcidump) - self._assert_energy(result, "oh") - finally: - settings.use_symmetry_reduced_integrals = prev_setting - - @data(True, False) - def test_lih_with_active_space(self, use_symmetry_reduced_integrals: bool): + fcidump = FCIDump.from_file( + self.get_resource_path("test_fcidump_oh.fcidump", "second_q/formats/fcidump") + ) + result = self._run_fcidump(fcidump) + self._assert_energy(result, "oh") + + def test_lih_with_active_space(self): """LiH with active space test""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - fcidump = FCIDump.from_file( - self.get_resource_path("test_fcidump_lih.fcidump", "second_q/formats/fcidump"), - ) - result = self._run_fcidump(fcidump, transformers=[ActiveSpaceTransformer(4, 6)]) - self._assert_energy(result, "lih") - finally: - settings.use_symmetry_reduced_integrals = prev_setting - - @data(True, False) - def test_oh_with_active_space(self, use_symmetry_reduced_integrals: bool): + fcidump = FCIDump.from_file( + self.get_resource_path("test_fcidump_lih.fcidump", "second_q/formats/fcidump"), + ) + result = self._run_fcidump(fcidump, transformers=[ActiveSpaceTransformer(4, 6)]) + self._assert_energy(result, "lih") + + def test_oh_with_active_space(self): """OH with active space test""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = use_symmetry_reduced_integrals - try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - fcidump = FCIDump.from_file( - self.get_resource_path("test_fcidump_oh.fcidump", "second_q/formats/fcidump"), - ) - result = self._run_fcidump( - fcidump, transformers=[ActiveSpaceTransformer((5, 4), 6)] - ) - self._assert_energy(result, "oh") - finally: - settings.use_symmetry_reduced_integrals = prev_setting + fcidump = FCIDump.from_file( + self.get_resource_path("test_fcidump_oh.fcidump", "second_q/formats/fcidump"), + ) + result = self._run_fcidump(fcidump, transformers=[ActiveSpaceTransformer((5, 4), 6)]) + self._assert_energy(result, "oh") if __name__ == "__main__": diff --git a/test/second_q/formats/test_qcschema.py b/test/second_q/formats/test_qcschema.py index ddc7b8826..77b19ae36 100644 --- a/test/second_q/formats/test_qcschema.py +++ b/test/second_q/formats/test_qcschema.py @@ -13,7 +13,6 @@ """Test the QCSchema implementation.""" import unittest -import warnings from pathlib import Path from tempfile import TemporaryDirectory @@ -88,41 +87,5 @@ def test_to_hdf5(self): self.assertEqual(qcs, EXPECTED_WATER_OUTPUT_V3) -class TestQCSchemaLegacy(QiskitNatureTestCase): - """Tests the QCSchema.from_legacy_hdf5 method.""" - - def test_legacy_from_hdf5(self): - """Tests the legacy_from_hdf5 method.""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - - qcschema = QCSchema.from_legacy_hdf5( - self.get_resource_path( - "legacy_electronic_structure_driver_result.hdf5", - "second_q/formats/qcschema", - ) - ) - - expected = QCSchema.from_json( - self.get_resource_path( - "legacy_electronic_structure_driver_result.json", - "second_q/formats/qcschema", - ) - ) - - self.assertEqual(qcschema, expected) - - @unittest.skip("Deprecated and no more HDF5 files available to test against.") - def test_legacy_from_hdf5_error(self): - """Tests the legacy_from_hdf5 method error.""" - with self.assertRaises(ValueError): - _ = QCSchema.from_legacy_hdf5( - self.get_resource_path( - "vibrational_structure_driver_result.hdf5", - "properties/second_quantization/vibrational/resources", - ) - ) - - if __name__ == "__main__": unittest.main() diff --git a/test/second_q/hamiltonians/test_quadratic_hamiltonian.py b/test/second_q/hamiltonians/test_quadratic_hamiltonian.py index d8a0bd0de..fd2d51648 100644 --- a/test/second_q/hamiltonians/test_quadratic_hamiltonian.py +++ b/test/second_q/hamiltonians/test_quadratic_hamiltonian.py @@ -17,12 +17,10 @@ import numpy as np from ddt import data, ddt from qiskit.quantum_info import random_hermitian -from qiskit.opflow import PauliSumOp from qiskit_nature.second_q.hamiltonians import QuadraticHamiltonian -from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitConverter +from qiskit_nature.second_q.mappers import JordanWignerMapper from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature.settings import settings from qiskit_nature.testing import random_antisymmetric_matrix @@ -106,9 +104,7 @@ def test_diagonalizing_bogoliubov_transform(self): ) # confirm eigenvalues match with Jordan-Wigner transformed Hamiltonian - qubit_op = QubitConverter(mapper=JordanWignerMapper()).convert(quad_ham.second_q_op()) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive + qubit_op = JordanWignerMapper().map(quad_ham.second_q_op()) hamiltonian_jw = qubit_op.to_matrix() eigs, _ = np.linalg.eigh(hamiltonian_jw) expected_eigs = np.array( @@ -246,31 +242,16 @@ def test_multiplication(self): ) self.assertEqual(quad_ham_scaled, expected) - @data(True, False) - def test_repr(self, tensor_unwrapping): + def test_repr(self): """Test repr""" - aux = settings.tensor_unwrapping - try: - settings.tensor_unwrapping = tensor_unwrapping - quad_ham = QuadraticHamiltonian( - np.array([[1, 2j], [-2j, 3]]), np.array([[0, 4.0], [-4.0, 0]]), 5.0 - ) - - if not tensor_unwrapping: - self.assertEqual( - repr(quad_ham), - "QuadraticHamiltonian(" - "hermitian_part=array([[ 1.+0.j, 0.+2.j],\n [-0.-2.j, 3.+0.j]]), " - "antisymmetric_part=array([[ 0., 4.],\n [-4., 0.]]), " - "constant=array(5.), num_modes=2)", - ) - else: - self.assertEqual( - repr(quad_ham), - "QuadraticHamiltonian(" - "hermitian_part=array([[ 1.+0.j, 0.+2.j],\n [-0.-2.j, 3.+0.j]]), " - "antisymmetric_part=array([[ 0., 4.],\n [-4., 0.]]), " - "constant=5.0, num_modes=2)", - ) - finally: - settings.tensor_unwrapping = aux + quad_ham = QuadraticHamiltonian( + np.array([[1, 2j], [-2j, 3]]), np.array([[0, 4.0], [-4.0, 0]]), 5.0 + ) + + self.assertEqual( + repr(quad_ham), + "QuadraticHamiltonian(" + "hermitian_part=array([[ 1.+0.j, 0.+2.j],\n [-0.-2.j, 3.+0.j]]), " + "antisymmetric_part=array([[ 0., 4.],\n [-4., 0.]]), " + "constant=array(5.), num_modes=2)", + ) diff --git a/test/second_q/mappers/resources/reference_direct_mapper.py b/test/second_q/mappers/resources/reference_direct_mapper.py index 400611f19..34d350166 100644 --- a/test/second_q/mappers/resources/reference_direct_mapper.py +++ b/test/second_q/mappers/resources/reference_direct_mapper.py @@ -10,9 +10,8 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""The reference PauliSumOp and SparsePauliOp for the DirectMapper.""" +"""The reference SparsePauliOp for the DirectMapper.""" -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp _list_2_q_op = [ @@ -311,7 +310,5 @@ ("IIIIIIIYYYIY", -31.184025000000013), ] -_num_modals_2_q_op = PauliSumOp.from_list(_list_2_q_op) -_num_modals_3_q_op = PauliSumOp.from_list(_list_3_q_op) _sparse_num_modals_2_q_op = SparsePauliOp.from_list(_list_2_q_op) _sparse_num_modals_3_q_op = SparsePauliOp.from_list(_list_3_q_op) diff --git a/test/second_q/mappers/test_bksf_mapper.py b/test/second_q/mappers/test_bksf_mapper.py index d8a0f163c..71aff36c4 100644 --- a/test/second_q/mappers/test_bksf_mapper.py +++ b/test/second_q/mappers/test_bksf_mapper.py @@ -21,7 +21,6 @@ import numpy as np from qiskit.quantum_info import SparsePauliOp, PauliList -from qiskit.opflow import PauliSumOp from qiskit_nature.second_q.operators import FermionicOp from qiskit_nature.second_q.mappers import BravyiKitaevSuperFastMapper @@ -30,7 +29,6 @@ _edge_operator_bi, _bksf_edge_list_fermionic_op, ) -from qiskit_nature import settings def _sort_simplify(sparse_pauli): @@ -160,105 +158,42 @@ def test_h2(self): ] ) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = BravyiKitaevSuperFastMapper().map(h2_fop) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive + pauli_sparse_op = BravyiKitaevSuperFastMapper().map(h2_fop) - op1 = _sort_simplify(expected_pauli_op) - op2 = _sort_simplify(qubit_op) + op1 = _sort_simplify(expected_pauli_op) + op2 = _sort_simplify(pauli_sparse_op) - with self.subTest("Map H2 frome sto3g basis, number of terms"): - self.assertEqual(len(op1), len(op2)) + with self.subTest("Map H2 frome sto3g basis, number of terms"): + self.assertEqual(len(op1), len(op2)) - with self.subTest("Map H2 frome sto3g basis result"): - self.assertEqual(op1, op2) - - settings.use_pauli_sum_op = False - pauli_sparse_op = BravyiKitaevSuperFastMapper().map(h2_fop) - - op1 = _sort_simplify(expected_pauli_op) - op2 = _sort_simplify(pauli_sparse_op) - - with self.subTest("Map H2 frome sto3g basis, number of terms"): - self.assertEqual(len(op1), len(op2)) - - with self.subTest("Map H2 frome sto3g basis result"): - self.assertEqualSparsePauliOp(op1, op2) - finally: - settings.use_pauli_sum_op = aux + with self.subTest("Map H2 frome sto3g basis result"): + self.assertEqualSparsePauliOp(op1, op2) with self.subTest("Sparse FermionicOp input"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - h2_fop_sparse = h2_fop.normal_order() - pauli_sum_op_from_sparse = BravyiKitaevSuperFastMapper().map(h2_fop_sparse) - op2_from_sparse = _sort_simplify(pauli_sum_op_from_sparse.primitive) - self.assertEqual(op1, op2_from_sparse) - - settings.use_pauli_sum_op = False - pauli_sum_op_from_sparse = BravyiKitaevSuperFastMapper().map(h2_fop_sparse) - op2_from_sparse = _sort_simplify(pauli_sum_op_from_sparse) - self.assertEqualSparsePauliOp(op1, op2_from_sparse) - finally: - settings.use_pauli_sum_op = aux + h2_fop_sparse = h2_fop.normal_order() + pauli_sum_op_from_sparse = BravyiKitaevSuperFastMapper().map(h2_fop_sparse) + op2_from_sparse = _sort_simplify(pauli_sum_op_from_sparse) + self.assertEqualSparsePauliOp(op1, op2_from_sparse) with self.subTest("Test accepting identity with zero coefficient"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - h2_fop_zero_term = h2_fop + FermionicOp({"": 0.0}, num_spin_orbitals=4) - pauli_sum_op_extra = BravyiKitaevSuperFastMapper().map(h2_fop_zero_term) - op3 = _sort_simplify(pauli_sum_op_extra.primitive) - self.assertEqual(op1, op3) - - settings.use_pauli_sum_op = False - pauli_sum_op_extra = BravyiKitaevSuperFastMapper().map(h2_fop_zero_term) - op3 = _sort_simplify(pauli_sum_op_extra) - self.assertEqualSparsePauliOp(op1, op3) - finally: - settings.use_pauli_sum_op = aux + h2_fop_zero_term = h2_fop + FermionicOp({"": 0.0}, num_spin_orbitals=4) + pauli_sum_op_extra = BravyiKitaevSuperFastMapper().map(h2_fop_zero_term) + op3 = _sort_simplify(pauli_sum_op_extra) + self.assertEqualSparsePauliOp(op1, op3) with self.subTest("Test zero FermiOp"): fermi_op = FermionicOp.zero() fermi_op.num_spin_orbitals = 4 - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - pauli_op = BravyiKitaevSuperFastMapper().map(fermi_op).primitive - x = np.array([], dtype="bool") - z = np.array([], dtype="bool") - expected_pauli_op = SparsePauliOp(PauliList.from_symplectic(x, z), coeffs=[0.0]) - self.assertEqual(pauli_op, expected_pauli_op) - - settings.use_pauli_sum_op = False - pauli_op = BravyiKitaevSuperFastMapper().map(fermi_op) - x = np.array([], dtype="bool") - z = np.array([], dtype="bool") - expected_pauli_op = SparsePauliOp(PauliList.from_symplectic(x, z), coeffs=[0.0]) - self.assertEqualSparsePauliOp(pauli_op, expected_pauli_op) - finally: - settings.use_pauli_sum_op = aux + pauli_op = BravyiKitaevSuperFastMapper().map(fermi_op) + x = np.array([], dtype="bool") + z = np.array([], dtype="bool") + expected_pauli_op = SparsePauliOp(PauliList.from_symplectic(x, z), coeffs=[0.0]) + self.assertEqualSparsePauliOp(pauli_op, expected_pauli_op) def test_LiH(self): """Test LiH molecule""" - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = BravyiKitaevSuperFastMapper().map(FERMIONIC_HAMILTONIAN) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive - self.assertEqual(qubit_op, QUBIT_HAMILTONIAN) - aux = settings.use_pauli_sum_op - - settings.use_pauli_sum_op = False - pauli_sparse_op = BravyiKitaevSuperFastMapper().map(FERMIONIC_HAMILTONIAN) - self.assertEqualSparsePauliOp(pauli_sparse_op, QUBIT_HAMILTONIAN) - finally: - settings.use_pauli_sum_op = aux + pauli_sparse_op = BravyiKitaevSuperFastMapper().map(FERMIONIC_HAMILTONIAN) + self.assertEqualSparsePauliOp(pauli_sparse_op, QUBIT_HAMILTONIAN) def test_mapping_overwrite_reg_len(self): """Test overwriting the register length.""" diff --git a/test/second_q/mappers/test_bosonic_linear_mapper.py b/test/second_q/mappers/test_bosonic_linear_mapper.py index cc176e4bb..ad46ee800 100644 --- a/test/second_q/mappers/test_bosonic_linear_mapper.py +++ b/test/second_q/mappers/test_bosonic_linear_mapper.py @@ -22,7 +22,6 @@ from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.operators import BosonicOp from qiskit_nature.second_q.mappers import BosonicLinearMapper -from qiskit_nature import settings @ddt @@ -147,13 +146,8 @@ class TestBosonicLinearMapper(QiskitNatureTestCase): def test_mapping_max_occupation_1(self, bos_op, ref_qubit_op): """Test mapping to qubit operator""" mapper = BosonicLinearMapper(max_occupation=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = False - qubit_op = mapper.map(bos_op) - self.assertEqualSparsePauliOp(qubit_op, ref_qubit_op) - finally: - settings.use_pauli_sum_op = aux + qubit_op = mapper.map(bos_op) + self.assertEqualSparsePauliOp(qubit_op, ref_qubit_op) @data( (bos_op1, ref_qubit_op1_tr2), @@ -170,20 +164,8 @@ def test_mapping_max_occupation_1(self, bos_op, ref_qubit_op): def test_mapping_max_occupation_2(self, bos_op, ref_qubit_op): """Test mapping to qubit operator""" mapper = BosonicLinearMapper(max_occupation=2) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = False - qubit_op = mapper.map(bos_op) - self.assertEqualSparsePauliOp(qubit_op, ref_qubit_op) - finally: - settings.use_pauli_sum_op = aux - - def test_error_pauli_sum_op(self): - """Test that if the user sets use_pauli_sum_op to true, then we return an error""" - mapper = BosonicLinearMapper(max_occupation=1) - settings.use_pauli_sum_op = True - with self.assertRaises(NotImplementedError): - mapper.map(BosonicOp({"+_0": 1})) + qubit_op = mapper.map(bos_op) + self.assertEqualSparsePauliOp(qubit_op, ref_qubit_op) if __name__ == "__main__": diff --git a/test/second_q/mappers/test_bravyi_kitaev_mapper.py b/test/second_q/mappers/test_bravyi_kitaev_mapper.py index 2358e19da..3db12262e 100644 --- a/test/second_q/mappers/test_bravyi_kitaev_mapper.py +++ b/test/second_q/mappers/test_bravyi_kitaev_mapper.py @@ -15,35 +15,35 @@ import unittest from test import QiskitNatureTestCase -from qiskit.opflow import I, PauliSumOp, X, Z from qiskit.quantum_info import SparsePauliOp import qiskit_nature.optionals as _optionals from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import BravyiKitaevMapper from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature import settings class TestBravyiKitaevMapper(QiskitNatureTestCase): """Test Bravyi-Kitaev Mapper""" - REF_H2 = ( - -0.81054798160031430 * (I ^ I ^ I ^ I) - + 0.17218393211855787 * (I ^ Z ^ I ^ I) - + 0.12091263243164174 * (I ^ I ^ Z ^ I) - + 0.12091263243164174 * (Z ^ I ^ Z ^ I) - - 0.22575349071287365 * (Z ^ Z ^ Z ^ I) - + 0.17218393211855818 * (I ^ I ^ I ^ Z) - + 0.16892753854646372 * (I ^ Z ^ I ^ Z) - + 0.17464343053355980 * (Z ^ Z ^ I ^ Z) - - 0.22575349071287362 * (I ^ I ^ Z ^ Z) - + 0.16614543242281926 * (I ^ Z ^ Z ^ Z) - + 0.16614543242281926 * (Z ^ Z ^ Z ^ Z) - + 0.04523279999117751 * (I ^ X ^ I ^ X) - + 0.04523279999117751 * (Z ^ X ^ I ^ X) - - 0.04523279999117751 * (I ^ X ^ Z ^ X) - - 0.04523279999117751 * (Z ^ X ^ Z ^ X) + REF_H2 = SparsePauliOp.from_list( + [ + ("IIII", -0.81054798160031430), + ("IZII", +0.17218393211855787), + ("IIZI", +0.12091263243164174), + ("ZIZI", +0.12091263243164174), + ("ZZZI", -0.22575349071287365), + ("IIIZ", +0.17218393211855818), + ("IZIZ", +0.16892753854646372), + ("ZZIZ", +0.17464343053355980), + ("IIZZ", -0.22575349071287362), + ("IZZZ", +0.16614543242281926), + ("ZZZZ", +0.16614543242281926), + ("IXIX", +0.04523279999117751), + ("ZXIX", +0.04523279999117751), + ("IXZX", -0.04523279999117751), + ("ZXZX", -0.04523279999117751), + ] ) @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") @@ -53,89 +53,35 @@ def test_mapping(self): driver_result = driver.run() fermionic_op, _ = driver_result.second_q_ops() mapper = BravyiKitaevMapper() - - # Note: The PauliSumOp equals, as used in the test below, use the equals of the - # SparsePauliOp which in turn uses np.allclose() to determine equality of - # coeffs. So the reference operator above will be matched on that basis so - # we don't need to worry about tiny precision changes for any reason. - - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = mapper.map(fermionic_op) - self.assertEqual(qubit_op, TestBravyiKitaevMapper.REF_H2) - settings.use_pauli_sum_op = False - qubit_op = mapper.map(fermionic_op) - self.assertEqualSparsePauliOp(qubit_op, TestBravyiKitaevMapper.REF_H2.primitive) - finally: - settings.use_pauli_sum_op = aux + qubit_op = mapper.map(fermionic_op) + self.assertTrue(qubit_op.equiv(TestBravyiKitaevMapper.REF_H2)) def test_mapping_for_single_op(self): """Test for single register operator.""" with self.subTest("test +"): op = FermionicOp({"+_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("X", 0.5), ("Y", -0.5j)]) - self.assertEqual(BravyiKitaevMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("X", 0.5), ("Y", -0.5j)]) - self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("X", 0.5), ("Y", -0.5j)]) + self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) with self.subTest("test -"): op = FermionicOp({"-_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("X", 0.5), ("Y", 0.5j)]) - self.assertEqual(BravyiKitaevMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("X", 0.5), ("Y", 0.5j)]) - self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("X", 0.5), ("Y", 0.5j)]) + self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) with self.subTest("test N"): op = FermionicOp({"+_0 -_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 0.5), ("Z", -0.5)]) - self.assertEqual(BravyiKitaevMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 0.5), ("Z", -0.5)]) - self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 0.5), ("Z", -0.5)]) + self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) with self.subTest("test E"): op = FermionicOp({"-_0 +_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 0.5), ("Z", 0.5)]) - self.assertEqual(BravyiKitaevMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 0.5), ("Z", 0.5)]) - self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 0.5), ("Z", 0.5)]) + self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) with self.subTest("test I"): op = FermionicOp({"": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 1)]) - self.assertEqual(BravyiKitaevMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 1)]) - self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 1)]) + self.assertEqualSparsePauliOp(BravyiKitaevMapper().map(op), expected) def test_mapping_overwrite_reg_len(self): """Test overwriting the register length.""" diff --git a/test/second_q/mappers/test_direct_mapper.py b/test/second_q/mappers/test_direct_mapper.py index b070b7f2e..056e20def 100644 --- a/test/second_q/mappers/test_direct_mapper.py +++ b/test/second_q/mappers/test_direct_mapper.py @@ -16,12 +16,9 @@ from test import QiskitNatureTestCase from test.second_q.mappers.resources.reference_direct_mapper import ( - _num_modals_2_q_op, - _num_modals_3_q_op, _sparse_num_modals_2_q_op, _sparse_num_modals_3_q_op, ) -from qiskit_nature import settings from qiskit_nature.second_q.drivers import GaussianForcesDriver from qiskit_nature.second_q.mappers import DirectMapper from qiskit_nature.second_q.operators import VibrationalOp @@ -54,16 +51,8 @@ def test_mapping(self): vibration_op = vibration_energy.second_q_op() mapper = DirectMapper() - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = mapper.map(vibration_op) - self.assertEqual(qubit_op, _num_modals_2_q_op) - settings.use_pauli_sum_op = False - qubit_op = mapper.map(vibration_op) - self.assertEqualSparsePauliOp(qubit_op, _sparse_num_modals_2_q_op) - finally: - settings.use_pauli_sum_op = aux + qubit_op = mapper.map(vibration_op) + self.assertEqualSparsePauliOp(qubit_op, _sparse_num_modals_2_q_op) @unittest.skipIf(not _optionals.HAS_SPARSE, "Sparse not available.") def test_larger_tutorial_qubit_op(self): @@ -76,16 +65,8 @@ def test_larger_tutorial_qubit_op(self): vibration_op = vibration_energy.second_q_op() mapper = DirectMapper() - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = mapper.map(vibration_op) - self.assertEqual(qubit_op, _num_modals_3_q_op) - settings.use_pauli_sum_op = False - qubit_op = mapper.map(vibration_op) - self.assertEqualSparsePauliOp(qubit_op, _sparse_num_modals_3_q_op) - finally: - settings.use_pauli_sum_op = aux + qubit_op = mapper.map(vibration_op) + self.assertEqualSparsePauliOp(qubit_op, _sparse_num_modals_3_q_op) def test_mapping_overwrite_reg_len(self): """Test overwriting the register length.""" diff --git a/test/second_q/mappers/test_interleaved_qubit_mapper.py b/test/second_q/mappers/test_interleaved_qubit_mapper.py index f858cfe5b..e2c25e64c 100644 --- a/test/second_q/mappers/test_interleaved_qubit_mapper.py +++ b/test/second_q/mappers/test_interleaved_qubit_mapper.py @@ -15,37 +15,23 @@ import unittest from test import QiskitNatureTestCase -from ddt import data, ddt - -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.mappers import InterleavedQubitMapper, JordanWignerMapper from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature.settings import settings -@ddt class TestInterleavedQubitMapper(QiskitNatureTestCase): """Test InterleavedQubitMapper""" - def tearDown(self) -> None: - super().tearDown() - settings.use_pauli_sum_op = True - - @data(True, False) - def test_mapping(self, use_pauli_sum_op: bool) -> None: + def test_mapping(self) -> None: """Test the actual mapping procedure.""" - settings.use_pauli_sum_op = use_pauli_sum_op - interleaved_mapper = InterleavedQubitMapper(JordanWignerMapper()) with self.subTest("1-body excitation"): ferm_op = FermionicOp({"+_0 -_1": 1}, num_spin_orbitals=4) qubit_op = interleaved_mapper.map(ferm_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertEqual( qubit_op, @@ -62,8 +48,6 @@ def test_mapping(self, use_pauli_sum_op: bool) -> None: ) qubit_op = interleaved_mapper.map(ferm_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertEqual( qubit_op, diff --git a/test/second_q/mappers/test_jordan_wigner_mapper.py b/test/second_q/mappers/test_jordan_wigner_mapper.py index 7bb5cb28e..b7b2fbd9a 100644 --- a/test/second_q/mappers/test_jordan_wigner_mapper.py +++ b/test/second_q/mappers/test_jordan_wigner_mapper.py @@ -16,35 +16,35 @@ from test import QiskitNatureTestCase from qiskit.circuit import Parameter -from qiskit.opflow import I, PauliSumOp, X, Y, Z from qiskit.quantum_info import SparsePauliOp import qiskit_nature.optionals as _optionals from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import JordanWignerMapper from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature import settings class TestJordanWignerMapper(QiskitNatureTestCase): """Test Jordan Wigner Mapper""" - REF_H2 = ( - -0.81054798160031430 * (I ^ I ^ I ^ I) - - 0.22575349071287365 * (Z ^ I ^ I ^ I) - + 0.17218393211855787 * (I ^ Z ^ I ^ I) - + 0.12091263243164174 * (Z ^ Z ^ I ^ I) - - 0.22575349071287362 * (I ^ I ^ Z ^ I) - + 0.17464343053355980 * (Z ^ I ^ Z ^ I) - + 0.16614543242281926 * (I ^ Z ^ Z ^ I) - + 0.17218393211855818 * (I ^ I ^ I ^ Z) - + 0.16614543242281926 * (Z ^ I ^ I ^ Z) - + 0.16892753854646372 * (I ^ Z ^ I ^ Z) - + 0.12091263243164174 * (I ^ I ^ Z ^ Z) - + 0.04523279999117751 * (X ^ X ^ X ^ X) - + 0.04523279999117751 * (Y ^ Y ^ X ^ X) - + 0.04523279999117751 * (X ^ X ^ Y ^ Y) - + 0.04523279999117751 * (Y ^ Y ^ Y ^ Y) + REF_H2 = SparsePauliOp.from_list( + [ + ("IIII", -0.81054798160031430), + ("ZIII", -0.22575349071287365), + ("IZII", +0.17218393211855787), + ("ZZII", +0.12091263243164174), + ("IIZI", -0.22575349071287362), + ("ZIZI", +0.17464343053355980), + ("IZZI", +0.16614543242281926), + ("IIIZ", +0.17218393211855818), + ("ZIIZ", +0.16614543242281926), + ("IZIZ", +0.16892753854646372), + ("IIZZ", +0.12091263243164174), + ("XXXX", +0.04523279999117751), + ("YYXX", +0.04523279999117751), + ("XXYY", +0.04523279999117751), + ("YYYY", +0.04523279999117751), + ] ) @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") @@ -54,97 +54,41 @@ def test_mapping(self): driver_result = driver.run() fermionic_op, _ = driver_result.second_q_ops() mapper = JordanWignerMapper() - - # Note: The PauliSumOp equals, as used in the test below, use the equals of the - # SparsePauliOp which in turn uses np.allclose() to determine equality of - # coeffs. So the reference operator above will be matched on that basis so - # we don't need to worry about tiny precision changes for any reason. - - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = mapper.map(fermionic_op) - self.assertEqual(qubit_op, TestJordanWignerMapper.REF_H2) - settings.use_pauli_sum_op = False - qubit_op = mapper.map(fermionic_op) - self.assertEqualSparsePauliOp(qubit_op, TestJordanWignerMapper.REF_H2.primitive) - finally: - settings.use_pauli_sum_op = aux + qubit_op = mapper.map(fermionic_op) + self.assertTrue(qubit_op.equiv(TestJordanWignerMapper.REF_H2)) def test_mapping_for_single_op(self): """Test for single register operator.""" with self.subTest("test +"): op = FermionicOp({"+_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("X", 0.5), ("Y", -0.5j)]) - self.assertEqual(JordanWignerMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("X", 0.5), ("Y", -0.5j)]) - self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("X", 0.5), ("Y", -0.5j)]) + self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) with self.subTest("test -"): op = FermionicOp({"-_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("X", 0.5), ("Y", 0.5j)]) - self.assertEqual(JordanWignerMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("X", 0.5), ("Y", 0.5j)]) - self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("X", 0.5), ("Y", 0.5j)]) + self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) with self.subTest("test N"): op = FermionicOp({"+_0 -_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 0.5), ("Z", -0.5)]) - self.assertEqual(JordanWignerMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 0.5), ("Z", -0.5)]) - self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 0.5), ("Z", -0.5)]) + self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) with self.subTest("test E"): op = FermionicOp({"-_0 +_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 0.5), ("Z", 0.5)]) - self.assertEqual(JordanWignerMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 0.5), ("Z", 0.5)]) - self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 0.5), ("Z", 0.5)]) + self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) with self.subTest("test I"): op = FermionicOp({"": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 1)]) - self.assertEqual(JordanWignerMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 1)]) - self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 1)]) + self.assertEqualSparsePauliOp(JordanWignerMapper().map(op), expected) with self.subTest("test parameters"): a = Parameter("a") op = FermionicOp({"+_0": a}) expected = SparsePauliOp.from_list([("X", 0.5 * a), ("Y", -0.5j * a)], dtype=object) qubit_op = JordanWignerMapper().map(op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertEqual(qubit_op, expected) def test_mapping_for_list_ops(self): @@ -157,18 +101,16 @@ def test_mapping_for_list_ops(self): FermionicOp({"": 1}, num_spin_orbitals=1), ] expected = [ - PauliSumOp.from_list([("X", 0.5), ("Y", -0.5j)]), - PauliSumOp.from_list([("X", 0.5), ("Y", 0.5j)]), - PauliSumOp.from_list([("I", 0.5), ("Z", -0.5)]), - PauliSumOp.from_list([("I", 0.5), ("Z", 0.5)]), - PauliSumOp.from_list([("I", 1)]), + SparsePauliOp.from_list([("X", 0.5), ("Y", -0.5j)]), + SparsePauliOp.from_list([("X", 0.5), ("Y", 0.5j)]), + SparsePauliOp.from_list([("I", 0.5), ("Z", -0.5)]), + SparsePauliOp.from_list([("I", 0.5), ("Z", 0.5)]), + SparsePauliOp.from_list([("I", 1)]), ] mapped_ops = JordanWignerMapper().map(ops) self.assertEqual(len(mapped_ops), len(expected)) for mapped_op, expected_op in zip(mapped_ops, expected): - if not isinstance(mapped_op, PauliSumOp): - mapped_op = PauliSumOp(mapped_op) self.assertEqual(mapped_op, expected_op) def test_mapping_for_dict_ops(self): @@ -181,18 +123,16 @@ def test_mapping_for_dict_ops(self): "I": FermionicOp({"": 1}, num_spin_orbitals=1), } expected = { - "+": PauliSumOp.from_list([("X", 0.5), ("Y", -0.5j)]), - "-": PauliSumOp.from_list([("X", 0.5), ("Y", 0.5j)]), - "N": PauliSumOp.from_list([("I", 0.5), ("Z", -0.5)]), - "E": PauliSumOp.from_list([("I", 0.5), ("Z", 0.5)]), - "I": PauliSumOp.from_list([("I", 1)]), + "+": SparsePauliOp.from_list([("X", 0.5), ("Y", -0.5j)]), + "-": SparsePauliOp.from_list([("X", 0.5), ("Y", 0.5j)]), + "N": SparsePauliOp.from_list([("I", 0.5), ("Z", -0.5)]), + "E": SparsePauliOp.from_list([("I", 0.5), ("Z", 0.5)]), + "I": SparsePauliOp.from_list([("I", 1)]), } mapped_ops = JordanWignerMapper().map(ops) self.assertEqual(len(mapped_ops), len(expected)) for k in mapped_ops.keys(): - if not isinstance(mapped_ops[k], PauliSumOp): - mapped_ops[k] = PauliSumOp(mapped_ops[k]) self.assertEqual(mapped_ops[k], expected[k]) def test_mapping_overwrite_reg_len(self): diff --git a/test/second_q/mappers/test_linear_mapper.py b/test/second_q/mappers/test_linear_mapper.py index 7239898a5..139d17877 100644 --- a/test/second_q/mappers/test_linear_mapper.py +++ b/test/second_q/mappers/test_linear_mapper.py @@ -18,11 +18,9 @@ from ddt import ddt, data, unpack -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp from qiskit_nature.second_q.operators import SpinOp from qiskit_nature.second_q.mappers import LinearMapper -from qiskit_nature import settings @ddt @@ -81,16 +79,8 @@ class TestLinearMapper(QiskitNatureTestCase): def test_mapping(self, spin_op, ref_qubit_op): """Test mapping to qubit operator""" mapper = LinearMapper() - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = mapper.map(spin_op) - self.assertEqual(qubit_op, PauliSumOp(ref_qubit_op)) - settings.use_pauli_sum_op = False - qubit_op = mapper.map(spin_op) - self.assertEqualSparsePauliOp(qubit_op, ref_qubit_op) - finally: - settings.use_pauli_sum_op = aux + qubit_op = mapper.map(spin_op) + self.assertEqualSparsePauliOp(qubit_op, ref_qubit_op) def test_mapping_overwrite_reg_len(self): """Test overwriting the register length.""" diff --git a/test/second_q/mappers/test_logarithmic_mapper.py b/test/second_q/mappers/test_logarithmic_mapper.py index 4d4fd2582..0b08ae58f 100644 --- a/test/second_q/mappers/test_logarithmic_mapper.py +++ b/test/second_q/mappers/test_logarithmic_mapper.py @@ -18,11 +18,9 @@ from ddt import ddt, data, unpack -from qiskit.opflow import PauliSumOp from qiskit.quantum_info.operators import SparsePauliOp from qiskit_nature.second_q.operators import SpinOp from qiskit_nature.second_q.mappers import LogarithmicMapper -from qiskit_nature import settings @ddt @@ -65,16 +63,8 @@ class TestLogarithmicMapper(QiskitNatureTestCase): def test_mapping(self, spin_op, ref_qubit_op, padding=1, embed_upper=True): """Test mapping to qubit operator""" mapper = LogarithmicMapper(padding=padding, embed_upper=embed_upper) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = mapper.map(spin_op) - self.assertEqual(qubit_op, PauliSumOp(ref_qubit_op)) - settings.use_pauli_sum_op = False - qubit_op = mapper.map(spin_op) - self.assertEqualSparsePauliOp(qubit_op, ref_qubit_op) - finally: - settings.use_pauli_sum_op = aux + qubit_op = mapper.map(spin_op) + self.assertEqualSparsePauliOp(qubit_op, ref_qubit_op) def test_mapping_overwrite_reg_len(self): """Test overwriting the register length.""" diff --git a/test/second_q/mappers/test_parity_mapper.py b/test/second_q/mappers/test_parity_mapper.py index 3eda6ca7f..ae029c826 100644 --- a/test/second_q/mappers/test_parity_mapper.py +++ b/test/second_q/mappers/test_parity_mapper.py @@ -15,20 +15,18 @@ import unittest from test import QiskitNatureTestCase -from qiskit.opflow import PauliSumOp from qiskit.quantum_info import SparsePauliOp import qiskit_nature.optionals as _optionals from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.mappers import ParityMapper from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature import settings class TestParityMapper(QiskitNatureTestCase): """Test Parity Mapper""" - REF_H2 = PauliSumOp.from_list( + REF_H2 = SparsePauliOp.from_list( [ ("IIII", -0.81054798160031430), ("ZZII", -0.22575349071287365), @@ -50,7 +48,7 @@ class TestParityMapper(QiskitNatureTestCase): tapering_values_expected = [-1, 1] - REF_H2_reduced = PauliSumOp.from_list( + REF_H2_reduced = SparsePauliOp.from_list( [ ("II", -1.05237325), ("IZ", 0.39793742), @@ -68,15 +66,7 @@ def test_mapping_without_two_qubit_reduction(self): fermionic_op, _ = driver_result.second_q_ops() mapper = ParityMapper() qubit_op = mapper.map(fermionic_op) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - - # Note: The PauliSumOp equals, as used in the test below, use the equals of the - # SparsePauliOp which in turn uses np.allclose() to determine equality of - # coeffs. So the reference operator above will be matched on that basis so - # we don't need to worry about tiny precision changes for any reason. - - self.assertEqual(qubit_op, TestParityMapper.REF_H2) + self.assertTrue(qubit_op.equiv(TestParityMapper.REF_H2)) @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") def test_mapping_with_two_qubit_reduction(self): @@ -86,81 +76,40 @@ def test_mapping_with_two_qubit_reduction(self): fermionic_op, _ = driver_result.second_q_ops() mapper = ParityMapper(num_particles=(1, 1)) qubit_op = mapper.map(fermionic_op) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestParityMapper.REF_H2_reduced) + self.assertTrue(qubit_op.equiv(TestParityMapper.REF_H2_reduced)) self.assertEqual(mapper._tapering_values, TestParityMapper.tapering_values_expected) # Test change num particles on the fly mapper.num_particles = None qubit_op_reduction = mapper.map(fermionic_op) - if not isinstance(qubit_op_reduction, PauliSumOp): - qubit_op_reduction = PauliSumOp(qubit_op_reduction) - self.assertEqual(qubit_op_reduction, TestParityMapper.REF_H2) + self.assertTrue(qubit_op_reduction.equiv(TestParityMapper.REF_H2)) def test_mapping_for_single_op(self): """Test for single register operator.""" with self.subTest("test +"): op = FermionicOp({"+_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("X", 0.5), ("Y", -0.5j)]) - self.assertEqual(ParityMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("X", 0.5), ("Y", -0.5j)]) - self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("X", 0.5), ("Y", -0.5j)]) + self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) with self.subTest("test -"): op = FermionicOp({"-_0": 1}, num_spin_orbitals=1) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("X", 0.5), ("Y", 0.5j)]) - self.assertEqual(ParityMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("X", 0.5), ("Y", 0.5j)]) - self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("X", 0.5), ("Y", 0.5j)]) + self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) with self.subTest("test N"): op = FermionicOp({"+_0 -_0": 1}, num_spin_orbitals=1) - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 0.5), ("Z", -0.5)]) - self.assertEqual(ParityMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 0.5), ("Z", -0.5)]) - self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 0.5), ("Z", -0.5)]) + self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) with self.subTest("test E"): op = FermionicOp({"-_0 +_0": 1}, num_spin_orbitals=1) - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 0.5), ("Z", 0.5)]) - self.assertEqual(ParityMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 0.5), ("Z", 0.5)]) - self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 0.5), ("Z", 0.5)]) + self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) with self.subTest("test I"): op = FermionicOp({"": 1}, num_spin_orbitals=1) - try: - settings.use_pauli_sum_op = True - expected = PauliSumOp.from_list([("I", 1)]) - self.assertEqual(ParityMapper().map(op), expected) - settings.use_pauli_sum_op = False - expected = SparsePauliOp.from_list([("I", 1)]) - self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) - finally: - settings.use_pauli_sum_op = aux + expected = SparsePauliOp.from_list([("I", 1)]) + self.assertEqualSparsePauliOp(ParityMapper().map(op), expected) def test_mapping_overwrite_reg_len(self): """Test overwriting the register length.""" diff --git a/test/second_q/mappers/test_qubit_converter.py b/test/second_q/mappers/test_qubit_converter.py deleted file mode 100644 index d2cf12220..000000000 --- a/test/second_q/mappers/test_qubit_converter.py +++ /dev/null @@ -1,571 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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 Qubit Converter """ - -import contextlib -import io -import unittest -import warnings -from test import QiskitNatureTestCase -from typing import List, Optional - -from qiskit.opflow import I, PauliSumOp, X, Y, Z, Z2Symmetries -from qiskit.quantum_info import SparsePauliOp - -import qiskit_nature.optionals as _optionals -from qiskit_nature import QiskitNatureError -from qiskit_nature.second_q.drivers import PySCFDriver -from qiskit_nature.second_q.operators import FermionicOp -from qiskit_nature.second_q.mappers import ( - JordanWignerMapper, - ParityMapper, - QubitConverter, -) -from qiskit_nature.settings import settings - - -@unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") -class TestQubitConverter(QiskitNatureTestCase): - """Test Qubit Converter""" - - REF_H2_JW = ( - -0.81054798160031430 * (I ^ I ^ I ^ I) - - 0.22575349071287365 * (Z ^ I ^ I ^ I) - + 0.17218393211855787 * (I ^ Z ^ I ^ I) - + 0.12091263243164174 * (Z ^ Z ^ I ^ I) - - 0.22575349071287362 * (I ^ I ^ Z ^ I) - + 0.17464343053355980 * (Z ^ I ^ Z ^ I) - + 0.16614543242281926 * (I ^ Z ^ Z ^ I) - + 0.17218393211855818 * (I ^ I ^ I ^ Z) - + 0.16614543242281926 * (Z ^ I ^ I ^ Z) - + 0.16892753854646372 * (I ^ Z ^ I ^ Z) - + 0.12091263243164174 * (I ^ I ^ Z ^ Z) - + 0.04523279999117751 * (X ^ X ^ X ^ X) - + 0.04523279999117751 * (Y ^ Y ^ X ^ X) - + 0.04523279999117751 * (X ^ X ^ Y ^ Y) - + 0.04523279999117751 * (Y ^ Y ^ Y ^ Y) - ) - - REF_H2_PARITY = ( - -0.81054798160031430 * (I ^ I ^ I ^ I) - - 0.22575349071287365 * (Z ^ Z ^ I ^ I) - + 0.12091263243164174 * (I ^ I ^ Z ^ I) - + 0.12091263243164174 * (Z ^ I ^ Z ^ I) - + 0.17218393211855787 * (I ^ Z ^ Z ^ I) - + 0.17218393211855818 * (I ^ I ^ I ^ Z) - + 0.16614543242281926 * (I ^ Z ^ I ^ Z) - + 0.16614543242281926 * (Z ^ Z ^ I ^ Z) - - 0.22575349071287362 * (I ^ I ^ Z ^ Z) - + 0.16892753854646372 * (I ^ Z ^ Z ^ Z) - + 0.17464343053355980 * (Z ^ Z ^ Z ^ Z) - + 0.04523279999117751 * (I ^ X ^ I ^ X) - + 0.04523279999117751 * (Z ^ X ^ I ^ X) - - 0.04523279999117751 * (I ^ X ^ Z ^ X) - - 0.04523279999117751 * (Z ^ X ^ Z ^ X) - ) - - REF_H2_PARITY_2Q_REDUCED = ( - -1.05237324646359750 * (I ^ I) - - 0.39793742283143140 * (Z ^ I) - + 0.39793742283143163 * (I ^ Z) - - 0.01128010423438501 * (Z ^ Z) - + 0.18093119996471000 * (X ^ X) - ) - - REF_H2_JW_TAPERED = -1.04109314222921270 * I - 0.79587484566286240 * Z + 0.18093119996470988 * X - - REF_H2_PARITY_TAPERED = ( - -1.04109314222921250 * I - 0.79587484566286300 * Z - 0.18093119996470994 * X - ) - - def setUp(self): - super().setUp() - driver = PySCFDriver() - self.driver_result = driver.run() - self.num_particles = self.driver_result.num_particles # (1, 1) - self.h2_op, _ = self.driver_result.second_q_ops() - self.mapper = ParityMapper() - self.qubit_conv = QubitConverter(self.mapper) - - def test_mapping_basic(self): - """Test mapping to qubit operator""" - mapper = JordanWignerMapper() - qubit_conv = QubitConverter(mapper) - - # Note: The PauliSumOp equals, as used in the test below, use the equals of the - # SparsePauliOp which in turn uses np.allclose() to determine equality of - # coeffs. So the reference operator above will be matched on that basis so - # we don't need to worry about tiny precision changes for any reason. - - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert(self.h2_op) - self.assertIsInstance(qubit_op, PauliSumOp) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_JW) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert(self.h2_op) - self.assertIsInstance(qubit_op, SparsePauliOp) - self.assertEqualSparsePauliOp(qubit_op, TestQubitConverter.REF_H2_JW.primitive) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("Re-use test"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert(self.h2_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_JW) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert(self.h2_op) - self.assertEqualSparsePauliOp(qubit_op, TestQubitConverter.REF_H2_JW.primitive) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("convert_match()"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_JW) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqualSparsePauliOp(qubit_op, TestQubitConverter.REF_H2_JW.primitive) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("Re-use with different mapper"): - qubit_conv.mapper = ParityMapper() - qubit_conv.two_qubit_reduction = True - qubit_op = qubit_conv.convert(self.h2_op) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - - with self.subTest("Force match set num particles"): - qubit_conv.force_match(num_particles=self.num_particles) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqualSparsePauliOp( - qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED.primitive - ) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("Convert with number of particles"): - qubit_conv.force_match(num_particles=None) - qubit_op = qubit_conv.convert(self.h2_op, num_particles=self.num_particles) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED) - - def test_two_qubit_reduction(self): - """Test mapping to qubit operator with two qubit reduction""" - mapper = ParityMapper() - qubit_conv = QubitConverter(mapper, two_qubit_reduction=True) - - with self.subTest("Two qubit reduction produces list as no particle number is given"): - qubit_op = qubit_conv.convert(self.h2_op) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - self.assertIsNone(qubit_conv.num_particles) - - with self.subTest("Two qubit reduction, num particles given"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert(self.h2_op, self.num_particles) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert(self.h2_op, self.num_particles) - self.assertEqualSparsePauliOp( - qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED.primitive - ) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("convert_match()"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqualSparsePauliOp( - qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED.primitive - ) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("State is reset (Num particles lost)"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert(self.h2_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - self.assertIsNone(qubit_conv.num_particles) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert(self.h2_op) - self.assertEqualSparsePauliOp(qubit_op, TestQubitConverter.REF_H2_PARITY.primitive) - self.assertIsNone(qubit_conv.num_particles) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("Num particles given again"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert(self.h2_op, self.num_particles) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert(self.h2_op, self.num_particles) - self.assertEqualSparsePauliOp( - qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED.primitive - ) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("Set two qubit reduction to False"): - qubit_conv.two_qubit_reduction = False - self.assertFalse(qubit_conv.two_qubit_reduction) - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert(self.h2_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert(self.h2_op) - self.assertEqualSparsePauliOp(qubit_op, TestQubitConverter.REF_H2_PARITY.primitive) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("Set two qubit reduction to False, set particle number in convert"): - qubit_conv.two_qubit_reduction = False - self.assertFalse(qubit_conv.two_qubit_reduction) - qubit_op = qubit_conv.convert(self.h2_op, num_particles=self.num_particles) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - - with self.subTest("Set two qubit reduction to False, set particle numbers in the mapper"): - qubit_conv.two_qubit_reduction = False - self.assertFalse(qubit_conv.two_qubit_reduction) - qubit_conv.force_match(num_particles=self.num_particles) - qubit_op = qubit_conv.convert(self.h2_op) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - - # Regression test against https://github.com/Qiskit/qiskit-nature/issues/271 - with self.subTest("Two qubit reduction skipped when operator too small"): - qubit_conv.two_qubit_reduction = True - small_op = FermionicOp({"+_0 -_0": 1.0, "-_1 +_1": 1.0}, num_spin_orbitals=2) - expected_op = 1.0 * (I ^ I) - 0.5 * (I ^ Z) + 0.5 * (Z ^ Z) - with contextlib.redirect_stderr(io.StringIO()) as out: - qubit_op = qubit_conv.convert(small_op, num_particles=self.num_particles) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, expected_op) - self.assertTrue( - out.getvalue() - .strip() - .startswith( - "The original qubit operator only contains 2 qubits! " - "Skipping the requested two-qubit reduction!" - ) - ) - - def test_paritymapper_two_qubit_reduction(self): - """Test mapping to qubit operator with two qubit reduction from the parity Mapper.""" - - with self.subTest("No particle number in the mapper and the convert method"): - mapper = ParityMapper() - qubit_conv = QubitConverter(mapper, two_qubit_reduction=False) - qubit_op = qubit_conv.convert(self.h2_op) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - self.assertIsNone(qubit_conv.num_particles) - - with self.subTest("Set particle number in the mapper only"): - mapper = ParityMapper(num_particles=(1, 1)) - qubit_conv = QubitConverter(mapper, two_qubit_reduction=False) - qubit_op = qubit_conv.convert(self.h2_op) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - self.assertIsNone(qubit_conv.num_particles) - - with self.subTest("Two qubit reduction is False and num particles given"): - mapper = ParityMapper(num_particles=(1, 1)) - qubit_conv = QubitConverter(mapper, two_qubit_reduction=False) - qubit_op = qubit_conv.convert(self.h2_op, num_particles=(1, 1)) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY) - self.assertIsNone(mapper.num_particles) - - with self.subTest("Set particle number in the converter only"): - mapper = ParityMapper() - qubit_conv = QubitConverter(mapper, two_qubit_reduction=True) - qubit_op = qubit_conv.convert(self.h2_op, num_particles=(1, 1)) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_2Q_REDUCED) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - - # Regression test against https://github.com/Qiskit/qiskit-nature/issues/271 - with self.subTest("Two qubit reduction skipped when operator too small"): - mapper = ParityMapper(num_particles=self.num_particles) - qubit_conv = QubitConverter(mapper, two_qubit_reduction=True) - small_op = FermionicOp({"+_0 -_0": 1.0, "-_1 +_1": 1.0}, num_spin_orbitals=2) - expected_op = 1.0 * (I ^ I) - 0.5 * (I ^ Z) + 0.5 * (Z ^ Z) - with contextlib.redirect_stderr(io.StringIO()) as out: - qubit_op = qubit_conv.convert(small_op, num_particles=self.num_particles) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, expected_op) - self.assertTrue( - out.getvalue() - .strip() - .startswith( - "The original qubit operator only contains 2 qubits! " - "Skipping the requested two-qubit reduction!" - ) - ) - - def test_z2_symmetry(self): - """Test mapping to qubit operator with z2 symmetry tapering""" - z2_sector = [-1, 1, -1] - - def cb_finder( - z2_symmetries: Z2Symmetries, converter: QubitConverter - ) -> Optional[List[int]]: - return z2_sector if not z2_symmetries.is_empty() else None - - def cb_find_none( - _z2_symmetries: Z2Symmetries, converter: QubitConverter - ) -> Optional[List[int]]: - return None - - mapper = JordanWignerMapper() - qubit_conv = QubitConverter(mapper, z2symmetry_reduction="auto") - - with self.subTest("Locator returns None, should be untapered operator"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert(self.h2_op, sector_locator=cb_find_none) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_JW) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert(self.h2_op, sector_locator=cb_find_none) - self.assertEqualSparsePauliOp(qubit_op, TestQubitConverter.REF_H2_JW.primitive) - finally: - settings.use_pauli_sum_op = aux - - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert(self.h2_op, sector_locator=cb_finder) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_JW_TAPERED) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert(self.h2_op, sector_locator=cb_finder) - self.assertEqualSparsePauliOp(qubit_op, TestQubitConverter.REF_H2_JW_TAPERED.primitive) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("convert_match()"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_JW_TAPERED) - self.assertIsNone(qubit_conv.num_particles) - self.assertListEqual(qubit_conv.z2symmetries.tapering_values, z2_sector) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqualSparsePauliOp( - qubit_op, TestQubitConverter.REF_H2_JW_TAPERED.primitive - ) - self.assertIsNone(qubit_conv.num_particles) - self.assertListEqual(qubit_conv.z2symmetries.tapering_values, z2_sector) - finally: - settings.use_pauli_sum_op = aux - - def test_two_qubit_reduction_and_z2_symmetry(self): - """Test mapping to qubit operator with z2 symmetry tapering and two qubit reduction""" - z2_sector = [-1] - - def cb_finder( - z2_symmetries: Z2Symmetries, converter: QubitConverter - ) -> Optional[List[int]]: - return z2_sector if not z2_symmetries.is_empty() else None - - mapper = ParityMapper() - qubit_conv = QubitConverter(mapper, two_qubit_reduction=True, z2symmetry_reduction="auto") - qubit_op = qubit_conv.convert(self.h2_op, self.num_particles, sector_locator=cb_finder) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_TAPERED) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - self.assertListEqual(qubit_conv.z2symmetries.tapering_values, z2_sector) - - with self.subTest("convert_match()"): - aux = settings.use_pauli_sum_op - try: - qubit_op = qubit_conv.convert_match(self.h2_op) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_TAPERED) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - self.assertListEqual(qubit_conv.z2symmetries.tapering_values, z2_sector) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert( - self.h2_op, self.num_particles, sector_locator=cb_finder - ) - self.assertEqualSparsePauliOp( - qubit_op, TestQubitConverter.REF_H2_PARITY_TAPERED.primitive - ) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - self.assertListEqual(qubit_conv.z2symmetries.tapering_values, z2_sector) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("convert_match()"): - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_TAPERED) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - self.assertListEqual(qubit_conv.z2symmetries.tapering_values, z2_sector) - settings.use_pauli_sum_op = False - qubit_op = qubit_conv.convert_match(self.h2_op) - self.assertEqualSparsePauliOp( - qubit_op, TestQubitConverter.REF_H2_PARITY_TAPERED.primitive - ) - self.assertEqual(qubit_conv.num_particles, self.num_particles) - self.assertListEqual(qubit_conv.z2symmetries.tapering_values, z2_sector) - finally: - settings.use_pauli_sum_op = aux - - with self.subTest("Change setting"): - qubit_conv.z2symmetry_reduction = [1] - qubit_op = qubit_conv.convert(self.h2_op, self.num_particles) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertNotEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_TAPERED) - qubit_conv.z2symmetry_reduction = [-1] - qubit_op = qubit_conv.convert(self.h2_op, self.num_particles) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_TAPERED) - - with self.subTest("Specify sector upfront"): - qubit_conv = QubitConverter( - mapper, two_qubit_reduction=True, z2symmetry_reduction=z2_sector - ) - qubit_op = qubit_conv.convert(self.h2_op, self.num_particles) - if not isinstance(qubit_op, PauliSumOp): - qubit_op = PauliSumOp(qubit_op) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_PARITY_TAPERED) - - with self.subTest("Specify sector upfront, but invalid content"): - with self.assertRaises(ValueError): - _ = QubitConverter(mapper, two_qubit_reduction=True, z2symmetry_reduction=[5]) - - with self.subTest("Specify sector upfront, but invalid length"): - qubit_conv = QubitConverter( - mapper, two_qubit_reduction=True, z2symmetry_reduction=[-1, 1] - ) - with self.assertRaises(QiskitNatureError): - _ = qubit_conv.convert(self.h2_op, self.num_particles) - - def test_molecular_problem_sector_locator_z2_symmetry(self): - """Test mapping to qubit operator with z2 symmetry tapering and two qubit reduction""" - - driver = PySCFDriver() - problem = driver.run() - - mapper = JordanWignerMapper() - qubit_conv = QubitConverter(mapper, two_qubit_reduction=True, z2symmetry_reduction="auto") - main_op, _ = problem.second_q_ops() - aux = settings.use_pauli_sum_op - try: - settings.use_pauli_sum_op = True - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - qubit_op = qubit_conv.convert( - main_op, - self.num_particles, - sector_locator=problem.symmetry_sector_locator, - ) - self.assertEqual(qubit_op, TestQubitConverter.REF_H2_JW_TAPERED) - settings.use_pauli_sum_op = False - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - qubit_op = qubit_conv.convert( - main_op, - self.num_particles, - sector_locator=problem.symmetry_sector_locator, - ) - self.assertEqualSparsePauliOp(qubit_op, TestQubitConverter.REF_H2_JW_TAPERED.primitive) - finally: - settings.use_pauli_sum_op = aux - - def test_compatibiliy_with_mappers(self): - """Test that Qubit converter and mappers produces the same results.""" - - with self.subTest("JordanWigner Mapper"): - mapper = JordanWignerMapper() - qubit_conv = QubitConverter(mapper) - qubit_op_converter = mapper.map(self.h2_op) - qubit_op_mapper = qubit_conv.convert(self.h2_op) - self.assertEqual(qubit_op_converter, qubit_op_mapper) - - with self.subTest("Parity Mapper"): - mapper = ParityMapper() - qubit_conv = QubitConverter(mapper) - qubit_op_converter = mapper.map(self.h2_op) - qubit_op_mapper = qubit_conv.convert(self.h2_op) - self.assertEqual(qubit_op_converter, qubit_op_mapper) - - with self.subTest("Parity Mapper and two qubit reduction"): - mapper = ParityMapper(num_particles=(1, 1)) - qubit_conv = QubitConverter(mapper, two_qubit_reduction=True) - qubit_op_converter = mapper.map(self.h2_op) - qubit_op_mapper = qubit_conv.convert_match(self.h2_op) - self.assertEqual(qubit_op_converter, qubit_op_mapper) - - def test_error_with_tapered_qubit_mapper(self): - """Test that the qubit converter cannot be used with a Tapered Qubit Mapper""" - - mapper = JordanWignerMapper() - tq_mapper = self.driver_result.get_tapered_mapper(mapper) - with self.assertRaises(ValueError): - _ = QubitConverter(tq_mapper) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/second_q/mappers/test_tapered_qubit_mapper.py b/test/second_q/mappers/test_tapered_qubit_mapper.py index 511b1df1e..aec78660e 100644 --- a/test/second_q/mappers/test_tapered_qubit_mapper.py +++ b/test/second_q/mappers/test_tapered_qubit_mapper.py @@ -13,24 +13,18 @@ """Tests for the TaperedQubitMapper.""" import unittest -import warnings from test import QiskitNatureTestCase -from ddt import data, ddt - from qiskit.quantum_info import Pauli, SparsePauliOp from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries -from qiskit.opflow import PauliSumOp import qiskit_nature.optionals as _optionals from qiskit_nature.second_q.drivers import PySCFDriver -from qiskit_nature.second_q.mappers import JordanWignerMapper, ParityMapper, QubitConverter +from qiskit_nature.second_q.mappers import JordanWignerMapper, ParityMapper from qiskit_nature.second_q.mappers.tapered_qubit_mapper import TaperedQubitMapper -from qiskit_nature.settings import settings @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") -@ddt class TestTaperedQubitMapper(QiskitNatureTestCase): """Test Tapered Qubit Mapper""" @@ -141,31 +135,13 @@ def setUp(self): self.jw_mapper = JordanWignerMapper() self.pt_mapper = ParityMapper() - def tearDown(self) -> None: - super().tearDown() - settings.use_pauli_sum_op = True - - @data(True, False) - def test_z2_symmetry(self, use_pauli_sum_op: bool): + def test_z2_symmetry(self): """Test mapping to qubit operator with z2 symmetry tapering""" - settings.use_pauli_sum_op = use_pauli_sum_op mapper = JordanWignerMapper() - with self.subTest("QubitConverter"): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - sector_locator = self.driver_result.symmetry_sector_locator - qubit_conv = QubitConverter(mapper, z2symmetry_reduction="auto") - qubit_op = qubit_conv.convert(self.h2_op, sector_locator=sector_locator) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive - self.assertEqual(qubit_op, TestTaperedQubitMapper.REF_H2_JW_TAPERED) - with self.subTest("TaperedQubitMapper"): tapered_qubit_mapper = self.driver_result.get_tapered_mapper(mapper) qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertEqual(qubit_op, TestTaperedQubitMapper.REF_H2_JW_TAPERED) with self.subTest("From Z2Symmetry object"): @@ -177,16 +153,12 @@ def test_z2_symmetry(self, use_pauli_sum_op: bool): ) tapered_qubit_mapper = TaperedQubitMapper(mapper, z2symmetries=z2_sym) qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertEqual(qubit_op, TestTaperedQubitMapper.REF_H2_JW_TAPERED) with self.subTest("From empty Z2Symmetry object"): z2_sym = Z2Symmetries([], [], [], None) tapered_qubit_mapper = TaperedQubitMapper(mapper, z2symmetries=z2_sym) qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertTrue(qubit_op.equiv(TestTaperedQubitMapper.REF_H2_JW)) with self.subTest("From Z2Symmetry object no tapering values"): @@ -197,59 +169,33 @@ def test_z2_symmetry(self, use_pauli_sum_op: bool): ) tapered_qubit_mapper = TaperedQubitMapper(mapper, z2symmetries=z2_sym) qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertTrue(qubit_op.equiv(TestTaperedQubitMapper.REF_H2_JW_CLIF)) tapered_qubit_mapper.z2symmetries.tapering_values = [-1, 1, -1] qubit_op = tapered_qubit_mapper.taper_clifford(qubit_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertTrue(qubit_op.equiv(TestTaperedQubitMapper.REF_H2_JW_TAPERED)) with self.subTest("From Z2Symmetry object automatic but no sector locator"): qubit_op = mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive z2_sym = Z2Symmetries.find_z2_symmetries(qubit_op) tapered_qubit_mapper = TaperedQubitMapper(mapper, z2_sym) qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertTrue(qubit_op.equiv(TestTaperedQubitMapper.REF_H2_JW_CLIF)) - @data(True, False) - def test_z2_symmetry_two_qubit_reduction(self, use_pauli_sum_op: bool): + def test_z2_symmetry_two_qubit_reduction(self): """Test mapping to qubit operator with z2 symmetry tapering and two qubit reduction""" - settings.use_pauli_sum_op = use_pauli_sum_op - with self.subTest("No 2-qubit reduction in the ParityMapper"): mapper = ParityMapper(num_particles=None) tapered_qubit_mapper = self.driver_result.get_tapered_mapper(mapper) qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive - self.assertEqual(qubit_op, TestTaperedQubitMapper.REF_H2_PT_TAPERED) - - with self.subTest("With 2-qubit reduction in the ParityMapper"): - mapper = ParityMapper(num_particles=(1, 1)) - tapered_qubit_mapper = self.driver_result.get_tapered_mapper(mapper) - qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertEqual(qubit_op, TestTaperedQubitMapper.REF_H2_PT_TAPERED) - @data(True, False) - def test_empty_z2_symmetry_two_qubit_reduction(self, use_pauli_sum_op: bool): + def test_empty_z2_symmetry_two_qubit_reduction(self): """Test mapping to qubit operator with empty z2 symmetry tapering and two qubit reduction""" - settings.use_pauli_sum_op = use_pauli_sum_op - with self.subTest("No 2-qubit reduction in the ParityMapper"): mapper = ParityMapper(num_particles=None) z2_sym = Z2Symmetries([], [], [], None) tapered_qubit_mapper = TaperedQubitMapper(mapper, z2symmetries=z2_sym) qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertTrue(qubit_op.equiv(TestTaperedQubitMapper.REF_H2_PT)) with self.subTest("With 2-qubit reduction in the ParityMapper"): @@ -257,52 +203,35 @@ def test_empty_z2_symmetry_two_qubit_reduction(self, use_pauli_sum_op: bool): z2_sym = Z2Symmetries([], [], [], None) tapered_qubit_mapper = TaperedQubitMapper(mapper, z2symmetries=z2_sym) qubit_op = tapered_qubit_mapper.map(self.h2_op) - if isinstance(qubit_op, PauliSumOp): - qubit_op = qubit_op.primitive self.assertTrue(qubit_op.equiv(TestTaperedQubitMapper.REF_H2_PT_2Q_REDUCED)) - @data(True, False) - def test_map_clifford(self, use_pauli_sum_op: bool): + def test_map_clifford(self): """Test the first exposed step of the mapping. Mapping to Pauli operators and composing with symmetry cliffords""" - settings.use_pauli_sum_op = use_pauli_sum_op - with self.subTest("Single operator JW"): jw_tqm = TaperedQubitMapper(self.jw_mapper) jw_op_h2 = jw_tqm.map_clifford(self.h2_op) - if isinstance(jw_op_h2, PauliSumOp): - jw_op_h2 = jw_op_h2.primitive self.assertTrue(jw_op_h2.equiv(TestTaperedQubitMapper.REF_H2_JW)) jw_tqm = self.driver_result.get_tapered_mapper(self.jw_mapper) jw_op_h2 = jw_tqm.map_clifford(self.h2_op) - if isinstance(jw_op_h2, PauliSumOp): - jw_op_h2 = jw_op_h2.primitive self.assertTrue(jw_op_h2.equiv(TestTaperedQubitMapper.REF_H2_JW_CLIF)) # Compose with symmetry cliffords if z2 not empty even if Num_particles is empty jw_tqm.z2symmetries.tapering_values = None jw_op_h2 = jw_tqm.map_clifford(self.h2_op) - if isinstance(jw_op_h2, PauliSumOp): - jw_op_h2 = jw_op_h2.primitive self.assertTrue(jw_op_h2.equiv(TestTaperedQubitMapper.REF_H2_JW_CLIF)) with self.subTest("Single operator PT"): pt_tqm = TaperedQubitMapper(self.pt_mapper) pt_op_h2 = pt_tqm.map_clifford(self.h2_op) - if isinstance(pt_op_h2, PauliSumOp): - pt_op_h2 = pt_op_h2.primitive self.assertTrue(pt_op_h2.equiv(TestTaperedQubitMapper.REF_H2_PT)) pt_tqm = self.driver_result.get_tapered_mapper(self.pt_mapper) pt_op_h2 = pt_tqm.map_clifford(self.h2_op) - if isinstance(pt_op_h2, PauliSumOp): - pt_op_h2 = pt_op_h2.primitive self.assertTrue(pt_op_h2.equiv(TestTaperedQubitMapper.REF_H2_PT_CLIF)) # Compose with symmetry cliffords if z2 not empty even with empty tapering values pt_tqm.z2symmetries.tapering_values = None pt_op_h2 = pt_tqm.map_clifford(self.h2_op) - if isinstance(pt_op_h2, PauliSumOp): - pt_op_h2 = pt_op_h2.primitive self.assertTrue(pt_op_h2.equiv(TestTaperedQubitMapper.REF_H2_PT_CLIF)) with self.subTest("Dictionary / List of operators and JW Mapper"): @@ -314,11 +243,7 @@ def test_map_clifford(self, use_pauli_sum_op: bool): self.assertEqual(len(jw_op_h2_list), 1) self.assertEqual(len(jw_op_h2_dict), 1) sparse_jw_op_h2_list = jw_op_h2_list[0] - if isinstance(sparse_jw_op_h2_list, PauliSumOp): - sparse_jw_op_h2_list = sparse_jw_op_h2_list.primitive sparse_jw_op_h2_dict = jw_op_h2_dict["h2"] - if isinstance(sparse_jw_op_h2_dict, PauliSumOp): - sparse_jw_op_h2_dict = sparse_jw_op_h2_dict.primitive self.assertTrue(sparse_jw_op_h2_list.equiv(TestTaperedQubitMapper.REF_H2_JW)) self.assertTrue(sparse_jw_op_h2_dict.equiv(TestTaperedQubitMapper.REF_H2_JW)) @@ -330,11 +255,7 @@ def test_map_clifford(self, use_pauli_sum_op: bool): self.assertEqual(len(jw_op_h2_list), 1) self.assertEqual(len(jw_op_h2_dict), 1) sparse_jw_op_h2_list = jw_op_h2_list[0] - if isinstance(sparse_jw_op_h2_list, PauliSumOp): - sparse_jw_op_h2_list = sparse_jw_op_h2_list.primitive sparse_jw_op_h2_dict = jw_op_h2_dict["h2"] - if isinstance(sparse_jw_op_h2_dict, PauliSumOp): - sparse_jw_op_h2_dict = sparse_jw_op_h2_dict.primitive self.assertTrue(sparse_jw_op_h2_list.equiv(TestTaperedQubitMapper.REF_H2_JW_CLIF)) self.assertTrue(sparse_jw_op_h2_dict.equiv(TestTaperedQubitMapper.REF_H2_JW_CLIF)) @@ -347,11 +268,7 @@ def test_map_clifford(self, use_pauli_sum_op: bool): self.assertEqual(len(pt_op_h2_list), 1) self.assertEqual(len(pt_op_h2_dict), 1) sparse_pt_op_h2_list = pt_op_h2_list[0] - if isinstance(sparse_pt_op_h2_list, PauliSumOp): - sparse_pt_op_h2_list = sparse_pt_op_h2_list.primitive sparse_pt_op_h2_dict = pt_op_h2_dict["h2"] - if isinstance(sparse_pt_op_h2_dict, PauliSumOp): - sparse_pt_op_h2_dict = sparse_pt_op_h2_dict.primitive self.assertTrue(sparse_pt_op_h2_list.equiv(TestTaperedQubitMapper.REF_H2_PT)) self.assertTrue(sparse_pt_op_h2_dict.equiv(TestTaperedQubitMapper.REF_H2_PT)) @@ -363,58 +280,35 @@ def test_map_clifford(self, use_pauli_sum_op: bool): self.assertEqual(len(pt_op_h2_list), 1) self.assertEqual(len(pt_op_h2_dict), 1) sparse_pt_op_h2_list = pt_op_h2_list[0] - if isinstance(sparse_pt_op_h2_list, PauliSumOp): - sparse_pt_op_h2_list = sparse_pt_op_h2_list.primitive sparse_pt_op_h2_dict = pt_op_h2_dict["h2"] - if isinstance(sparse_pt_op_h2_dict, PauliSumOp): - sparse_pt_op_h2_dict = sparse_pt_op_h2_dict.primitive self.assertTrue(sparse_pt_op_h2_list.equiv(TestTaperedQubitMapper.REF_H2_PT_CLIF)) self.assertTrue(sparse_pt_op_h2_dict.equiv(TestTaperedQubitMapper.REF_H2_PT_CLIF)) - @data(True, False) - def test_taper_clifford(self, use_pauli_sum_op: bool): + def test_taper_clifford(self): """Test the second exposed step of the mapping. Applying the symmetry reduction""" - settings.use_pauli_sum_op = use_pauli_sum_op - with self.subTest("Single operator JW"): jw_tqm = TaperedQubitMapper(self.jw_mapper) jw_op_h2 = jw_tqm.map_clifford(self.h2_op) - if isinstance(jw_op_h2, PauliSumOp): - jw_op_h2 = jw_op_h2.primitive jw_op_h2_tap = jw_tqm.taper_clifford(jw_op_h2) - if isinstance(jw_op_h2_tap, PauliSumOp): - jw_op_h2_tap = jw_op_h2_tap.primitive self.assertTrue(jw_op_h2.equiv(TestTaperedQubitMapper.REF_H2_JW)) self.assertTrue(jw_op_h2_tap.equiv(TestTaperedQubitMapper.REF_H2_JW)) jw_tqm = self.driver_result.get_tapered_mapper(self.jw_mapper) jw_op_h2 = jw_tqm.map_clifford(self.h2_op) - if isinstance(jw_op_h2, PauliSumOp): - jw_op_h2 = jw_op_h2.primitive jw_op_h2_tap = jw_tqm.taper_clifford(jw_op_h2) - if isinstance(jw_op_h2_tap, PauliSumOp): - jw_op_h2_tap = jw_op_h2_tap.primitive self.assertTrue(jw_op_h2.equiv(TestTaperedQubitMapper.REF_H2_JW_CLIF)) self.assertTrue(jw_op_h2_tap.equiv(TestTaperedQubitMapper.REF_H2_JW_TAPERED)) with self.subTest("Single operator PT"): pt_tqm = TaperedQubitMapper(self.pt_mapper) pt_op_h2 = pt_tqm.map_clifford(self.h2_op) - if isinstance(pt_op_h2, PauliSumOp): - pt_op_h2 = pt_op_h2.primitive pt_op_h2_tap = pt_tqm.taper_clifford(pt_op_h2) - if isinstance(pt_op_h2_tap, PauliSumOp): - pt_op_h2_tap = pt_op_h2_tap.primitive self.assertTrue(pt_op_h2.equiv(TestTaperedQubitMapper.REF_H2_PT)) self.assertTrue(pt_op_h2_tap.equiv(TestTaperedQubitMapper.REF_H2_PT)) pt_tqm = self.driver_result.get_tapered_mapper(self.pt_mapper) pt_op_h2 = pt_tqm.map_clifford(self.h2_op) - if isinstance(pt_op_h2, PauliSumOp): - pt_op_h2 = pt_op_h2.primitive pt_op_h2_tap = pt_tqm.taper_clifford(pt_op_h2) - if isinstance(pt_op_h2_tap, PauliSumOp): - pt_op_h2_tap = pt_op_h2_tap.primitive self.assertTrue(pt_op_h2.equiv(TestTaperedQubitMapper.REF_H2_PT_CLIF)) self.assertTrue(pt_op_h2_tap.equiv(TestTaperedQubitMapper.REF_H2_PT_TAPERED)) @@ -432,17 +326,9 @@ def test_taper_clifford(self, use_pauli_sum_op: bool): self.assertEqual(len(jw_op_h2_tap_list), 1) self.assertEqual(len(jw_op_h2_tap_dict), 1) sparse_jw_op_h2_list = jw_op_h2_list[0] - if isinstance(sparse_jw_op_h2_list, PauliSumOp): - sparse_jw_op_h2_list = sparse_jw_op_h2_list.primitive sparse_jw_op_h2_tap_list = jw_op_h2_tap_list[0] - if isinstance(sparse_jw_op_h2_tap_list, PauliSumOp): - sparse_jw_op_h2_tap_list = sparse_jw_op_h2_tap_list.primitive sparse_jw_op_h2_dict = jw_op_h2_dict["h2"] - if isinstance(sparse_jw_op_h2_dict, PauliSumOp): - sparse_jw_op_h2_dict = sparse_jw_op_h2_dict.primitive sparse_jw_op_h2_tap_dict = jw_op_h2_tap_dict["h2"] - if isinstance(sparse_jw_op_h2_tap_dict, PauliSumOp): - sparse_jw_op_h2_tap_dict = sparse_jw_op_h2_tap_dict.primitive self.assertTrue(sparse_jw_op_h2_list.equiv(TestTaperedQubitMapper.REF_H2_JW)) self.assertTrue(sparse_jw_op_h2_tap_list.equiv(TestTaperedQubitMapper.REF_H2_JW)) self.assertTrue(sparse_jw_op_h2_dict.equiv(TestTaperedQubitMapper.REF_H2_JW)) @@ -461,17 +347,9 @@ def test_taper_clifford(self, use_pauli_sum_op: bool): self.assertEqual(len(jw_op_h2_tap_list), 1) self.assertEqual(len(jw_op_h2_tap_dict), 1) sparse_jw_op_h2_list = jw_op_h2_list[0] - if isinstance(sparse_jw_op_h2_list, PauliSumOp): - sparse_jw_op_h2_list = sparse_jw_op_h2_list.primitive sparse_jw_op_h2_tap_list = jw_op_h2_tap_list[0] - if isinstance(sparse_jw_op_h2_tap_list, PauliSumOp): - sparse_jw_op_h2_tap_list = sparse_jw_op_h2_tap_list.primitive sparse_jw_op_h2_dict = jw_op_h2_dict["h2"] - if isinstance(sparse_jw_op_h2_dict, PauliSumOp): - sparse_jw_op_h2_dict = sparse_jw_op_h2_dict.primitive sparse_jw_op_h2_tap_dict = jw_op_h2_tap_dict["h2"] - if isinstance(sparse_jw_op_h2_tap_dict, PauliSumOp): - sparse_jw_op_h2_tap_dict = sparse_jw_op_h2_tap_dict.primitive self.assertTrue(sparse_jw_op_h2_list.equiv(TestTaperedQubitMapper.REF_H2_JW_CLIF)) self.assertTrue( sparse_jw_op_h2_tap_list.equiv(TestTaperedQubitMapper.REF_H2_JW_TAPERED) @@ -495,17 +373,9 @@ def test_taper_clifford(self, use_pauli_sum_op: bool): self.assertEqual(len(pt_op_h2_tap_list), 1) self.assertEqual(len(pt_op_h2_tap_dict), 1) sparse_pt_op_h2_list = pt_op_h2_list[0] - if isinstance(sparse_pt_op_h2_list, PauliSumOp): - sparse_pt_op_h2_list = sparse_pt_op_h2_list.primitive sparse_pt_op_h2_tap_list = pt_op_h2_tap_list[0] - if isinstance(sparse_pt_op_h2_tap_list, PauliSumOp): - sparse_pt_op_h2_tap_list = sparse_pt_op_h2_tap_list.primitive sparse_pt_op_h2_dict = pt_op_h2_dict["h2"] - if isinstance(sparse_pt_op_h2_dict, PauliSumOp): - sparse_pt_op_h2_dict = sparse_pt_op_h2_dict.primitive sparse_pt_op_h2_tap_dict = pt_op_h2_tap_dict["h2"] - if isinstance(sparse_pt_op_h2_tap_dict, PauliSumOp): - sparse_pt_op_h2_tap_dict = sparse_pt_op_h2_tap_dict.primitive self.assertTrue(sparse_pt_op_h2_list.equiv(TestTaperedQubitMapper.REF_H2_PT)) self.assertTrue(sparse_pt_op_h2_tap_list.equiv(TestTaperedQubitMapper.REF_H2_PT)) self.assertTrue(sparse_pt_op_h2_dict.equiv(TestTaperedQubitMapper.REF_H2_PT)) @@ -524,17 +394,9 @@ def test_taper_clifford(self, use_pauli_sum_op: bool): self.assertEqual(len(pt_op_h2_tap_list), 1) self.assertEqual(len(pt_op_h2_tap_dict), 1) sparse_pt_op_h2_list = pt_op_h2_list[0] - if isinstance(sparse_pt_op_h2_list, PauliSumOp): - sparse_pt_op_h2_list = sparse_pt_op_h2_list.primitive sparse_pt_op_h2_tap_list = pt_op_h2_tap_list[0] - if isinstance(sparse_pt_op_h2_tap_list, PauliSumOp): - sparse_pt_op_h2_tap_list = sparse_pt_op_h2_tap_list.primitive sparse_pt_op_h2_dict = pt_op_h2_dict["h2"] - if isinstance(sparse_pt_op_h2_dict, PauliSumOp): - sparse_pt_op_h2_dict = sparse_pt_op_h2_dict.primitive sparse_pt_op_h2_tap_dict = pt_op_h2_tap_dict["h2"] - if isinstance(sparse_pt_op_h2_tap_dict, PauliSumOp): - sparse_pt_op_h2_tap_dict = sparse_pt_op_h2_tap_dict.primitive self.assertTrue(sparse_pt_op_h2_list.equiv(TestTaperedQubitMapper.REF_H2_PT_CLIF)) self.assertTrue( sparse_pt_op_h2_tap_list.equiv(TestTaperedQubitMapper.REF_H2_PT_TAPERED) @@ -548,15 +410,13 @@ def test_taper_clifford(self, use_pauli_sum_op: bool): jw_tqm = self.driver_result.get_tapered_mapper(self.jw_mapper) ops = [ - PauliSumOp(TestTaperedQubitMapper.REF_H2_JW_CLIF), - PauliSumOp.from_list([("IXYZ", 1.0)]), + TestTaperedQubitMapper.REF_H2_JW_CLIF, + SparsePauliOp.from_list([("IXYZ", 1.0)]), ] jw_op_h2_tap_list = jw_tqm.taper_clifford(ops, suppress_none=False) self.assertTrue(isinstance(jw_op_h2_tap_list, list)) self.assertEqual(len(jw_op_h2_tap_list), 2) sparse_jw_op_h2_tap_list = jw_op_h2_tap_list[0] - if isinstance(sparse_jw_op_h2_tap_list, PauliSumOp): - sparse_jw_op_h2_tap_list = sparse_jw_op_h2_tap_list.primitive self.assertTrue( sparse_jw_op_h2_tap_list.equiv(TestTaperedQubitMapper.REF_H2_JW_TAPERED) ) @@ -568,8 +428,6 @@ def test_taper_clifford(self, use_pauli_sum_op: bool): self.assertTrue(isinstance(jw_op_h2_tap_list, list)) self.assertEqual(len(jw_op_h2_tap_list), 1) sparse_jw_op_h2_tap_list = jw_op_h2_tap_list[0] - if isinstance(sparse_jw_op_h2_tap_list, PauliSumOp): - sparse_jw_op_h2_tap_list = sparse_jw_op_h2_tap_list.primitive self.assertTrue( sparse_jw_op_h2_tap_list.equiv(TestTaperedQubitMapper.REF_H2_JW_TAPERED) ) @@ -578,15 +436,10 @@ def test_taper_clifford(self, use_pauli_sum_op: bool): self.assertTrue(isinstance(jw_op_h2_tap_list, list)) self.assertEqual(len(jw_op_h2_tap_list), 2) sparse_jw_op_h2_tap_list = jw_op_h2_tap_list[0] - if isinstance(sparse_jw_op_h2_tap_list, PauliSumOp): - sparse_jw_op_h2_tap_list = sparse_jw_op_h2_tap_list.primitive self.assertTrue( sparse_jw_op_h2_tap_list.equiv(TestTaperedQubitMapper.REF_H2_JW_TAPERED) ) - if isinstance(jw_op_h2_tap_list[1], PauliSumOp): - self.assertTrue(jw_op_h2_tap_list[1] == PauliSumOp.from_list([("I", 1.0)])) - else: - self.assertTrue(jw_op_h2_tap_list[1] == SparsePauliOp.from_list([("I", 1.0)])) + self.assertTrue(jw_op_h2_tap_list[1] == SparsePauliOp.from_list([("I", 1.0)])) if __name__ == "__main__": diff --git a/test/second_q/operators/test_fermionic_op.py b/test/second_q/operators/test_fermionic_op.py index e89f76025..2b67a6de0 100644 --- a/test/second_q/operators/test_fermionic_op.py +++ b/test/second_q/operators/test_fermionic_op.py @@ -13,14 +13,11 @@ """Test for FermionicOp""" import unittest -import warnings from test import QiskitNatureTestCase import numpy as np from ddt import data, ddt, unpack from qiskit.circuit import Parameter -from scipy.sparse import csc_matrix -from scipy.sparse.linalg import eigs from qiskit_nature.exceptions import QiskitNatureError from qiskit_nature.second_q.operators import FermionicOp, PolynomialTensor @@ -310,76 +307,6 @@ def test_equiv(self): FermionicOp.atol = prev_atol FermionicOp.rtol = prev_rtol - def test_to_matrix(self): - """Test to_matrix""" - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - - with self.subTest("identity operator matrix"): - op = FermionicOp.one() - op.num_spin_orbitals = 2 - mat = op.to_matrix(sparse=False) - targ = np.eye(4) - self.assertTrue(np.allclose(mat, targ)) - - with self.subTest("number operator matrix"): - mat = FermionicOp({"+_1 -_1": 1}, num_spin_orbitals=2).to_matrix(sparse=False) - targ = np.array([[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]) - self.assertTrue(np.allclose(mat, targ)) - - with self.subTest("emptiness operator matrix"): - mat = FermionicOp({"-_1 +_1": 1}, num_spin_orbitals=2).to_matrix(sparse=False) - targ = np.array([[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0]]) - self.assertTrue(np.allclose(mat, targ)) - - with self.subTest("raising operator matrix"): - mat = FermionicOp({"+_1": 1}, num_spin_orbitals=2).to_matrix(sparse=False) - targ = np.array([[0, 0, 0, 0], [1, 0, 0, 0], [0, 0, 0, 0], [0, 0, -1, 0]]) - self.assertTrue(np.allclose(mat, targ)) - - with self.subTest("lowering operator matrix"): - mat = FermionicOp({"-_1": 1}, num_spin_orbitals=2).to_matrix(sparse=False) - targ = np.array([[0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, -1], [0, 0, 0, 0]]) - self.assertTrue(np.allclose(mat, targ)) - - with self.subTest("nontrivial sparse matrix"): - mat = FermionicOp( - {"-_0 +_0 +_1 -_1 +_3": 3j, "-_0 +_1 -_1 +_2 -_3": -2}, num_spin_orbitals=4 - ).to_matrix() - targ = csc_matrix(([-3j, 3j, -2], ([5, 7, 6], [4, 6, 13])), shape=(16, 16)) - self.assertTrue((mat != targ).nnz == 0) - - with self.subTest("Test Hydrogen spectrum"): - h2_labels = { - "+_0 -_1 +_2 -_3": 0.18093120148374142, - "+_0 -_1 -_2 +_3": -0.18093120148374134, - "-_0 +_1 +_2 -_3": -0.18093120148374134, - "-_0 +_1 -_2 +_3": 0.18093120148374128, - "+_3 -_3": -0.4718960038869427, - "+_2 -_2": -1.2563391028292563, - "+_2 -_2 +_3 -_3": 0.48365053378098793, - "+_1 -_1": -0.4718960038869427, - "+_1 -_1 +_3 -_3": 0.6985737398458793, - "+_1 -_1 +_2 -_2": 0.6645817352647293, - "+_0 -_0": -1.2563391028292563, - "+_0 -_0 +_3 -_3": 0.6645817352647293, - "+_0 -_0 +_2 -_2": 0.6757101625347564, - "+_0 -_0 +_1 -_1": 0.48365053378098793, - } - h2_matrix = FermionicOp(h2_labels, num_spin_orbitals=4).to_matrix() - evals, evecs = eigs(h2_matrix) - self.assertTrue(np.isclose(np.min(evals), -1.8572750)) - # make sure the ground state has support only in the 2-particle subspace - groundstate = evecs[:, np.argmin(evals)] - for idx in np.where(~np.isclose(groundstate, 0))[0]: - binary = f"{idx:0{4}b}" - self.assertEqual(binary.count("1"), 2) - - with self.subTest("parameters"): - fer_op = FermionicOp({"+_0": self.a}) - with self.assertRaisesRegex(ValueError, "parameter"): - _ = fer_op.to_matrix() - def test_normal_order(self): """test normal_order method""" with self.subTest("Test for creation operator"): @@ -621,18 +548,6 @@ def test_no_num_spin_orbitals(self): self.assertEqual(op1, op3) self.assertTrue(op1.equiv(1.000001 * op3)) - with self.subTest("to_matrix"): - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - - ref = np.array([[0, 0], [0, 1]]) - np.testing.assert_array_almost_equal(op1.to_matrix(False), ref) - op1.num_spin_orbitals = 2 - np.testing.assert_array_almost_equal(op1.to_matrix(False), np.kron(ref, np.eye(2))) - - ref = np.array([[1]]) - np.testing.assert_array_almost_equal(op0.to_matrix(False), ref) - def test_terms(self): """Test terms generator.""" op = FermionicOp( diff --git a/test/second_q/operators/test_polynomial_tensor.py b/test/second_q/operators/test_polynomial_tensor.py index 36f53df02..8d42cd3d6 100644 --- a/test/second_q/operators/test_polynomial_tensor.py +++ b/test/second_q/operators/test_polynomial_tensor.py @@ -22,7 +22,7 @@ from ddt import ddt, idata import qiskit_nature.optionals as _optionals -from qiskit_nature.second_q.operators import PolynomialTensor, Tensor +from qiskit_nature.second_q.operators import PolynomialTensor @ddt @@ -295,10 +295,9 @@ def test_add(self): expected["++--"][0, 0, 0, 1] += 1 expected["++--"][1, 0, 2, 1] += 2 self.assertEqual(result, PolynomialTensor(expected)) - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - self.assertIsInstance(Tensor(result["+"]).array, np.ndarray) - self.assertIsInstance(Tensor(result["+-"]).array, np.ndarray) - self.assertIsInstance(Tensor(result["++--"]).array, np.ndarray) + self.assertIsInstance(result["+"].array, np.ndarray) + self.assertIsInstance(result["+-"].array, np.ndarray) + self.assertIsInstance(result["++--"].array, np.ndarray) with self.subTest("sparse + sparse"): result = PolynomialTensor(self.sparse_1) + PolynomialTensor(self.sparse_2) @@ -309,10 +308,9 @@ def test_add(self): "++--": sp.as_coo({(0, 0, 0, 1): 1, (0, 1, 0, 1): 1}, shape=(4, 4, 4, 4)), } self.assertEqual(result, PolynomialTensor(expected)) - # TODO: remove extra-wrapping of Tensor once settings.tensor_unwrapping is removed - self.assertIsInstance(Tensor(result["+"]).array, sp.COO) - self.assertIsInstance(Tensor(result["+-"]).array, sp.COO) - self.assertIsInstance(Tensor(result["++--"]).array, sp.COO) + self.assertIsInstance(result["+"].array, sp.COO) + self.assertIsInstance(result["+-"].array, sp.COO) + self.assertIsInstance(result["++--"].array, sp.COO) with self.assertRaisesRegex( TypeError, "Incorrect argument type: other should be PolynomialTensor" diff --git a/test/second_q/operators/test_symmetric_two_body.py b/test/second_q/operators/test_symmetric_two_body.py index 45fccdc9c..f66e0ffd8 100644 --- a/test/second_q/operators/test_symmetric_two_body.py +++ b/test/second_q/operators/test_symmetric_two_body.py @@ -22,7 +22,6 @@ from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver import qiskit_nature.optionals as _optionals -from qiskit_nature.settings import settings from qiskit_nature.second_q.algorithms import GroundStateEigensolver from qiskit_nature.second_q.drivers import MethodType, PySCFDriver from qiskit_nature.second_q.formats.qcschema_translator import ( @@ -45,13 +44,8 @@ fold_s1_to_s8, fold_s4_to_s8, ) -from qiskit_nature.second_q.operators.tensor_ordering import ( - IndexType, - to_physicist_ordering, - to_chemist_ordering, -) +from qiskit_nature.second_q.operators.tensor_ordering import to_physicist_ordering from qiskit_nature.second_q.problems import ElectronicBasis, ElectronicStructureProblem -from qiskit_nature.second_q.transformers import ActiveSpaceTransformer @ddt @@ -197,331 +191,199 @@ def test_s8_fermionic_op_sparse(self): def test_s4_integration(self): """Test integration of the S4Integrals.""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = False - try: - # pylint: disable=import-error - from pyscf import ao2mo - - algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) - driver = PySCFDriver() - problem = driver.run() - expected = algo.solve(problem).computed_energies[0] - - s4_hamil = ElectronicEnergy( - ElectronicIntegrals( - PolynomialTensor( - { - "+-": np.dot( - np.dot(driver._calc.mo_coeff.T, driver._calc.get_hcore()), - driver._calc.mo_coeff, - ), - "++--": S4Integrals( - ao2mo.full(driver._mol, driver._calc.mo_coeff, aosym=4) - ), - }, - validate=False, - ) + # pylint: disable=import-error + from pyscf import ao2mo + + algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) + driver = PySCFDriver() + problem = driver.run() + expected = algo.solve(problem).computed_energies[0] + + s4_hamil = ElectronicEnergy( + ElectronicIntegrals( + PolynomialTensor( + { + "+-": np.dot( + np.dot(driver._calc.mo_coeff.T, driver._calc.get_hcore()), + driver._calc.mo_coeff, + ), + "++--": S4Integrals( + ao2mo.full(driver._mol, driver._calc.mo_coeff, aosym=4) + ), + }, + validate=False, ) ) - s4_problem = ElectronicStructureProblem(s4_hamil) - result = algo.solve(s4_problem) - with self.subTest("computed energy"): - self.assertAlmostEqual(expected, result.computed_energies[0]) - with self.subTest("generated FermionicOp"): - self.assertTrue( - problem.hamiltonian.second_q_op().equiv(s4_problem.hamiltonian.second_q_op()) - ) - finally: - settings.use_symmetry_reduced_integrals = prev_setting + ) + s4_problem = ElectronicStructureProblem(s4_hamil) + result = algo.solve(s4_problem) + with self.subTest("computed energy"): + self.assertAlmostEqual(expected, result.computed_energies[0]) + with self.subTest("generated FermionicOp"): + self.assertTrue( + problem.hamiltonian.second_q_op().equiv(s4_problem.hamiltonian.second_q_op()) + ) def test_s4_integration_uhf(self): """Test integration of the S4Integrals with UHF.""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = False - try: - # pylint: disable=import-error - from pyscf import ao2mo - - algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) - driver = PySCFDriver(method=MethodType.UHF) - problem = driver.run() - expected = algo.solve(problem).computed_energies[0] - - s4_hamil = ElectronicEnergy( - ElectronicIntegrals( - PolynomialTensor( - { - "+-": np.dot( - np.dot(driver._calc.mo_coeff[0].T, driver._calc.get_hcore()), - driver._calc.mo_coeff[0], - ), - "++--": S4Integrals( - ao2mo.full(driver._mol, driver._calc.mo_coeff[0], aosym=4) - ), - }, - validate=False, - ), - PolynomialTensor( - { - "+-": np.dot( - np.dot(driver._calc.mo_coeff[1].T, driver._calc.get_hcore()), - driver._calc.mo_coeff[1], - ), - "++--": S4Integrals( - ao2mo.full(driver._mol, driver._calc.mo_coeff[1], aosym=4) - ), - }, - validate=False, - ), - PolynomialTensor( - { - "++--": S4Integrals( - ao2mo.general( - driver._mol, - [ - driver._calc.mo_coeff[1], - driver._calc.mo_coeff[1], - driver._calc.mo_coeff[0], - driver._calc.mo_coeff[0], - ], - aosym=4, - ) - ), - }, - validate=False, - ), - ) + # pylint: disable=import-error + from pyscf import ao2mo + + algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) + driver = PySCFDriver(method=MethodType.UHF) + problem = driver.run() + expected = algo.solve(problem).computed_energies[0] + + s4_hamil = ElectronicEnergy( + ElectronicIntegrals( + PolynomialTensor( + { + "+-": np.dot( + np.dot(driver._calc.mo_coeff[0].T, driver._calc.get_hcore()), + driver._calc.mo_coeff[0], + ), + "++--": S4Integrals( + ao2mo.full(driver._mol, driver._calc.mo_coeff[0], aosym=4) + ), + }, + validate=False, + ), + PolynomialTensor( + { + "+-": np.dot( + np.dot(driver._calc.mo_coeff[1].T, driver._calc.get_hcore()), + driver._calc.mo_coeff[1], + ), + "++--": S4Integrals( + ao2mo.full(driver._mol, driver._calc.mo_coeff[1], aosym=4) + ), + }, + validate=False, + ), + PolynomialTensor( + { + "++--": S4Integrals( + ao2mo.general( + driver._mol, + [ + driver._calc.mo_coeff[1], + driver._calc.mo_coeff[1], + driver._calc.mo_coeff[0], + driver._calc.mo_coeff[0], + ], + aosym=4, + ) + ), + }, + validate=False, + ), + ) + ) + s4_problem = ElectronicStructureProblem(s4_hamil) + result = algo.solve(s4_problem) + with self.subTest("computed energy"): + self.assertAlmostEqual(expected, result.computed_energies[0]) + with self.subTest("generated FermionicOp"): + self.assertTrue( + problem.hamiltonian.second_q_op().equiv(s4_problem.hamiltonian.second_q_op()) ) - s4_problem = ElectronicStructureProblem(s4_hamil) - result = algo.solve(s4_problem) - with self.subTest("computed energy"): - self.assertAlmostEqual(expected, result.computed_energies[0]) - with self.subTest("generated FermionicOp"): - self.assertTrue( - problem.hamiltonian.second_q_op().equiv(s4_problem.hamiltonian.second_q_op()) - ) - finally: - settings.use_symmetry_reduced_integrals = prev_setting def test_s8_integration(self): """Test integration of the S8Integrals.""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = False - try: - algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) - driver = PySCFDriver() - driver.run_pyscf() - qcschema = driver.to_qcschema() - trafo = get_ao_to_mo_from_qcschema(qcschema) - problem = qcschema_to_problem(qcschema, basis=ElectronicBasis.MO) - expected = algo.solve(problem).computed_energies[0] - - s8_hamil = ElectronicEnergy( - ElectronicIntegrals( - PolynomialTensor( - { - "+-": driver._calc.get_hcore(), - "++--": S8Integrals(driver._mol.intor("int2e", aosym=8)), - }, - validate=False, - ) + algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) + driver = PySCFDriver() + driver.run_pyscf() + qcschema = driver.to_qcschema() + trafo = get_ao_to_mo_from_qcschema(qcschema) + problem = qcschema_to_problem(qcschema, basis=ElectronicBasis.MO) + expected = algo.solve(problem).computed_energies[0] + + s8_hamil = ElectronicEnergy( + ElectronicIntegrals( + PolynomialTensor( + { + "+-": driver._calc.get_hcore(), + "++--": S8Integrals(driver._mol.intor("int2e", aosym=8)), + }, + validate=False, ) ) - ao_problem = ElectronicStructureProblem(s8_hamil) - ao_problem.basis = ElectronicBasis.AO - s8_mo_problem = trafo.transform(ao_problem) - result = algo.solve(s8_mo_problem) - with self.subTest("computed energy"): - self.assertAlmostEqual(expected, result.computed_energies[0]) - with self.subTest("generated FermionicOp"): - self.assertTrue( - problem.hamiltonian.second_q_op().equiv(s8_mo_problem.hamiltonian.second_q_op()) - ) - finally: - settings.use_symmetry_reduced_integrals = prev_setting + ) + ao_problem = ElectronicStructureProblem(s8_hamil) + ao_problem.basis = ElectronicBasis.AO + s8_mo_problem = trafo.transform(ao_problem) + result = algo.solve(s8_mo_problem) + with self.subTest("computed energy"): + self.assertAlmostEqual(expected, result.computed_energies[0]) + with self.subTest("generated FermionicOp"): + self.assertTrue( + problem.hamiltonian.second_q_op().equiv(s8_mo_problem.hamiltonian.second_q_op()) + ) def test_s8_integration_uhf(self): """Test integration of the S8Integrals with UHF.""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = False - try: - # pylint: disable=import-error - from pyscf import ao2mo - - algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) - driver = PySCFDriver(method=MethodType.UHF) - problem = driver.run() - expected = algo.solve(problem).computed_energies[0] - - s8_hamil = ElectronicEnergy( - ElectronicIntegrals( - PolynomialTensor( - { - "+-": np.dot( - np.dot(driver._calc.mo_coeff[0].T, driver._calc.get_hcore()), - driver._calc.mo_coeff[0], - ), - "++--": fold_s4_to_s8( - ao2mo.full(driver._mol, driver._calc.mo_coeff[0], aosym=4) - ), - }, - validate=False, - ), - PolynomialTensor( - { - "+-": np.dot( - np.dot(driver._calc.mo_coeff[1].T, driver._calc.get_hcore()), - driver._calc.mo_coeff[1], - ), - "++--": fold_s4_to_s8( - ao2mo.full(driver._mol, driver._calc.mo_coeff[1], aosym=4) - ), - }, - validate=False, - ), - PolynomialTensor( - { - "++--": fold_s4_to_s8( - ao2mo.general( - driver._mol, - [ - driver._calc.mo_coeff[1], - driver._calc.mo_coeff[1], - driver._calc.mo_coeff[0], - driver._calc.mo_coeff[0], - ], - aosym=4, - ) - ), - }, - validate=False, - ), - ) - ) - s8_problem = ElectronicStructureProblem(s8_hamil) - result = algo.solve(s8_problem) - with self.subTest("computed energy"): - self.assertAlmostEqual(expected, result.computed_energies[0]) - with self.subTest("generated FermionicOp"): - self.assertTrue( - problem.hamiltonian.second_q_op().equiv(s8_problem.hamiltonian.second_q_op()) - ) - finally: - settings.use_symmetry_reduced_integrals = prev_setting - - def test_active(self): - """Test integration with ActiveSpaceTransformer.""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = False - try: - algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) - driver = PySCFDriver(atom="Li 0 0 0; H 0 0 1.1") - problem = driver.run() - trafo = ActiveSpaceTransformer(2, 2) - as_problem = trafo.transform(problem) - expected = algo.solve(as_problem).computed_energies[0] - - red_hamil = ElectronicEnergy( - ElectronicIntegrals( - PolynomialTensor( - { - "+-": problem.hamiltonian.electronic_integrals.alpha["+-"], - "++--": fold_s1_to_s8( - to_chemist_ordering( - problem.hamiltonian.electronic_integrals.alpha["++--"] - ), - ), - }, - validate=False, - ) - ) + # pylint: disable=import-error + from pyscf import ao2mo + + algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) + driver = PySCFDriver(method=MethodType.UHF) + problem = driver.run() + expected = algo.solve(problem).computed_energies[0] + + s8_hamil = ElectronicEnergy( + ElectronicIntegrals( + PolynomialTensor( + { + "+-": np.dot( + np.dot(driver._calc.mo_coeff[0].T, driver._calc.get_hcore()), + driver._calc.mo_coeff[0], + ), + "++--": fold_s4_to_s8( + ao2mo.full(driver._mol, driver._calc.mo_coeff[0], aosym=4) + ), + }, + validate=False, + ), + PolynomialTensor( + { + "+-": np.dot( + np.dot(driver._calc.mo_coeff[1].T, driver._calc.get_hcore()), + driver._calc.mo_coeff[1], + ), + "++--": fold_s4_to_s8( + ao2mo.full(driver._mol, driver._calc.mo_coeff[1], aosym=4) + ), + }, + validate=False, + ), + PolynomialTensor( + { + "++--": fold_s4_to_s8( + ao2mo.general( + driver._mol, + [ + driver._calc.mo_coeff[1], + driver._calc.mo_coeff[1], + driver._calc.mo_coeff[0], + driver._calc.mo_coeff[0], + ], + aosym=4, + ) + ), + }, + validate=False, + ), ) - - red_problem = ElectronicStructureProblem(red_hamil) - red_problem.basis = ElectronicBasis.MO - red_problem.num_particles = problem.num_particles - trafo = ActiveSpaceTransformer(2, 2) - red_as_problem = trafo.transform(red_problem) - as_result = algo.solve(red_as_problem) - with self.subTest("computed energy"): - self.assertAlmostEqual(expected, as_result.computed_energies[0]) - with self.subTest("generated FermionicOp"): - self.assertTrue( - as_problem.hamiltonian.second_q_op().equiv( - red_as_problem.hamiltonian.second_q_op() - ) - ) - finally: - settings.use_symmetry_reduced_integrals = prev_setting - - def test_active_uhf(self): - """Test integration with ActiveSpaceTransformer with UHF.""" - prev_setting = settings.use_symmetry_reduced_integrals - settings.use_symmetry_reduced_integrals = False - try: - algo = GroundStateEigensolver(JordanWignerMapper(), NumPyMinimumEigensolver()) - driver = PySCFDriver(atom="Li 0 0 0; H 0 0 1.1", method=MethodType.UHF) - problem = driver.run() - trafo = ActiveSpaceTransformer(2, 2) - as_problem = trafo.transform(problem) - expected = algo.solve(as_problem).computed_energies[0] - - red_hamil = ElectronicEnergy( - ElectronicIntegrals( - PolynomialTensor( - { - "+-": problem.hamiltonian.electronic_integrals.alpha["+-"], - "++--": fold_s1_to_s8( - to_chemist_ordering( - problem.hamiltonian.electronic_integrals.alpha["++--"] - ), - ), - }, - validate=False, - ), - PolynomialTensor( - { - "+-": problem.hamiltonian.electronic_integrals.beta["+-"], - "++--": fold_s1_to_s8( - to_chemist_ordering( - problem.hamiltonian.electronic_integrals.beta["++--"] - ), - ), - }, - validate=False, - ), - PolynomialTensor( - { - "++--": S1Integrals( - to_chemist_ordering( - problem.hamiltonian.electronic_integrals.beta_alpha["++--"], - index_order=IndexType.PHYSICIST, - ), - ), - }, - validate=False, - ), - ) + ) + s8_problem = ElectronicStructureProblem(s8_hamil) + result = algo.solve(s8_problem) + with self.subTest("computed energy"): + self.assertAlmostEqual(expected, result.computed_energies[0]) + with self.subTest("generated FermionicOp"): + self.assertTrue( + problem.hamiltonian.second_q_op().equiv(s8_problem.hamiltonian.second_q_op()) ) - red_problem = ElectronicStructureProblem(red_hamil) - red_problem.basis = ElectronicBasis.MO - red_problem.num_particles = problem.num_particles - trafo = ActiveSpaceTransformer(2, 2) - red_as_problem = trafo.transform(red_problem) - as_result = algo.solve(red_as_problem) - with self.subTest("computed energy"): - self.assertAlmostEqual(expected, as_result.computed_energies[0]) - with self.subTest("generated FermionicOp"): - self.assertTrue( - as_problem.hamiltonian.second_q_op().equiv( - red_as_problem.hamiltonian.second_q_op() - ) - ) - finally: - settings.use_symmetry_reduced_integrals = prev_setting - if __name__ == "__main__": unittest.main() diff --git a/test/second_q/problems/test_electronic_structure_problem.py b/test/second_q/problems/test_electronic_structure_problem.py index 490503c40..b1959e456 100644 --- a/test/second_q/problems/test_electronic_structure_problem.py +++ b/test/second_q/problems/test_electronic_structure_problem.py @@ -12,21 +12,16 @@ """Tests Electronic Structure Problem.""" import unittest -import warnings from test import QiskitNatureTestCase import json import numpy as np from qiskit.algorithms.minimum_eigensolvers import MinimumEigensolverResult -from qiskit.opflow import PauliSumOp -from qiskit.opflow.primitive_ops import Z2Symmetries -from qiskit.quantum_info.analysis.z2_symmetries import Z2Symmetries as Z2SparseSymmetries import qiskit_nature.optionals as _optionals from qiskit_nature.second_q.drivers import PySCFDriver from qiskit_nature.second_q.hamiltonians import ElectronicEnergy -from qiskit_nature.second_q.mappers import JordanWignerMapper from qiskit_nature.second_q.operators import SparseLabelOp from qiskit_nature.second_q.problems import ElectronicStructureProblem from qiskit_nature.second_q.properties import AngularMomentum, Magnetization, ParticleNumber @@ -132,33 +127,6 @@ def test_second_q_ops_with_active_space(self): for s, t in zip(sorted(expected.items()), sorted(electr_sec_quant_op.items())) ) - @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") - def test_symmetry_sector_locator(self): - """Tests that the symmetry sector locator gives the right sector.""" - driver = PySCFDriver() - electronic_structure_problem = driver.run() - hamiltonian, _ = electronic_structure_problem.second_q_ops() - mapper = JordanWignerMapper() - mapped_op = mapper.map(hamiltonian) - expected_sector = [-1, 1, -1] - - with self.subTest("Opflow Z2Symmetries"): - if isinstance(mapped_op, PauliSumOp): - mapped_op = mapped_op.primitive - z2sym = Z2SparseSymmetries.find_z2_symmetries(mapped_op) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - sector = electronic_structure_problem.symmetry_sector_locator(z2sym, mapper) - self.assertEqual(sector, expected_sector) - with self.subTest("Opflow Z2Symmetries"): - if not isinstance(mapped_op, PauliSumOp): - mapped_op = PauliSumOp(mapped_op) - z2sym = Z2Symmetries.find_Z2_symmetries(mapped_op) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=DeprecationWarning) - sector = electronic_structure_problem.symmetry_sector_locator(z2sym, mapper) - self.assertEqual(sector, expected_sector) - if __name__ == "__main__": unittest.main() diff --git a/test/second_q/properties/test_electronic_density.py b/test/second_q/properties/test_electronic_density.py index f74e2b037..b23880bce 100644 --- a/test/second_q/properties/test_electronic_density.py +++ b/test/second_q/properties/test_electronic_density.py @@ -27,7 +27,7 @@ from qiskit_nature.second_q.algorithms import GroundStateEigensolver from qiskit_nature.second_q.drivers import PySCFDriver, MethodType -from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitConverter +from qiskit_nature.second_q.mappers import JordanWignerMapper from qiskit_nature.second_q.operators import ElectronicIntegrals from qiskit_nature.second_q.operators.tensor_ordering import _chem_to_phys from qiskit_nature.second_q.problems import ElectronicStructureResult @@ -276,7 +276,7 @@ def test_evaluated_densities(self, method): problem.properties.electronic_density = electronic_density algo = GroundStateEigensolver( - QubitConverter(JordanWignerMapper()), + JordanWignerMapper(), NumPyMinimumEigensolver(), ) diff --git a/test/test_deprecation.py b/test/test_deprecation.py deleted file mode 100644 index 6cc912db8..000000000 --- a/test/test_deprecation.py +++ /dev/null @@ -1,591 +0,0 @@ -# This code is part of a Qiskit project. -# -# (C) Copyright IBM 2021, 2023. -# -# 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 The deprecation methods -""" - -from __future__ import annotations - -import unittest -import sys -import inspect -import warnings -from test import QiskitNatureTestCase -from ddt import data, ddt -from qiskit_nature.deprecation import ( - DeprecatedEnum, - DeprecatedEnumMeta, - DeprecatedType, - warn_deprecated, - warn_deprecated_same_type_name, - warn_deprecated_type, - deprecate_function, - deprecate_property, - deprecate_method, - deprecate_arguments, - deprecate_values, - deprecate_positional_arguments, -) - -# pylint: disable=bad-docstring-quotes - - -class EnumTest(DeprecatedEnum, metaclass=DeprecatedEnumMeta): - """Enumeration Test""" - - ONE = "one" - TWO = "two" - - def deprecate(self): - """show deprecate message""" - warn_deprecated( - "0.2.0", DeprecatedType.ENUM, self.__class__.__name__, new_name="NewEnum", stack_level=3 - ) - - -@deprecate_function("0.1.1", DeprecatedType.CLASS, "some_class", "and more information") -def func1(arg1: int) -> int: - """function 1""" - return arg1 - - -@deprecate_function("0.2.0", new_name="some_function2") -def func2(arg2: int) -> int: - """function 2""" - return arg2 - - -@deprecate_arguments("0.1.2", {"old_arg": "new_arg"}) -def func3(new_arg: int | None = None, old_arg: int | None = None) -> tuple[int, int]: - """function 3""" - return new_arg, old_arg - - -class DeprecatedClass1: - """Deprecated Test class 1""" - - def __init__(self): - warn_deprecated( - "0.3.0", DeprecatedType.CLASS, "DeprecatedClass1", DeprecatedType.CLASS, "NewClass" - ) - self.value = 10 - - -class DeprecatedClass2: - """Deprecated Test class 2""" - - def __init__(self): - warn_deprecated_same_type_name( - "0.3.0", DeprecatedType.CLASS, "DeprecatedClass2", "from package test2" - ) - self.value = 10 - - -class DeprecatedClass3: - """Deprecated Test class 3""" - - @deprecate_values("0.2.0", {"loss": {"l1": "absolute_error", "l2": "squared_error"}}) - def __init__(self, arg1: int, loss: str = "squared_error") -> None: - self.value = arg1 - self.loss = loss - - @deprecate_values("0.2.0", {"loss": {"l1": "absolute_error", "l2": "squared_error"}}) - def method1(self, arg1: int, loss: str = "squared_error") -> None: - """method 1""" - self.value = arg1 - self.loss = loss - - -class DeprecatedClass4: - """Deprecated Test class 4""" - - def __init__( - self, - ) -> None: - self.status = 2 - - @deprecate_values("0.2.0", {"status": {3: 2, 4: 5}}) - def method1(self, status: int = 2) -> None: - """method 1""" - self.status = status - - -class DeprecatedClass5: - """Deprecated Test class 5""" - - def __init__(self, value: int | str): - if isinstance(value, str): - warn_deprecated_type("0.6.0", "value", "str", "int") - self.value = int(value) - else: - self.value = value - - -@deprecate_positional_arguments( - version="0.1", - func_name="function_positional", - old_function_arguments=["a", "b", "c", "d"], - stack_level=2, -) -def function_positional( - a, - c, - **kwargs, -) -> int: - """Deprecated function positional""" - - return a + c + sum(kwargs.values()) - - -class TestClass: - """Test class with deprecation""" - - def __init__(self): - self._value = 20 - - # Bug in mypy, if property decorator is used with another one - # https://github.com/python/mypy/issues/1362 - - @property - @deprecate_property("0.1.0") - def property1(self) -> int: - """property1 get""" - return self._value - - @property1.setter - @deprecate_property( - "0.1.0", new_name="new_property", additional_msg="and some additional information" - ) - def property1(self, value: int): - """property 1 set""" - self._value = value - - @deprecate_method("0.1.0", new_name="some_method1", additional_msg="and additional information") - def method1(self, arg: int) -> int: - """method 1""" - return arg - - @deprecate_method("0.2.0", new_name="some_method2") - def method2(self, arg: int) -> int: - """method 2""" - return arg - - @deprecate_arguments("0.1.2", {"old_arg": "new_arg"}) - def method3(self, new_arg: int | None = None, old_arg: int | None = None) -> tuple[int, int]: - """method3""" - return new_arg, old_arg - - -@ddt -class TestDeprecation(QiskitNatureTestCase): - """Test deprecation methods""" - - def setUp(self) -> None: - super().setUp() - self._source = inspect.getsource(sys.modules[self.__module__]).splitlines() - - def _get_line_from_str(self, text: str) -> int: - for idx, line in enumerate(self._source): - if text in line: - return idx + 1 - return -1 - - def test_enum_deprecation(self): - """test enumeration deprecation""" - - msg_ref = ( - "The EnumTest enum is deprecated as of version 0.2.0 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the NewEnum enum." - ) - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - _ = EnumTest.ONE - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("EnumTest.ONE"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - _ = EnumTest.TWO - self.assertListEqual(c_m, []) - - @data( - ( - "func1", - "The func1 function is deprecated as of version 0.1.1 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the some_class class and more information.", - ), - ( - "func2", - "The func2 function is deprecated as of version 0.2.0 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the some_function2 function.", - ), - ) - def test_function_deprecation(self, config): - """test function deprecation""" - - function_name, msg_ref = config - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(2, globals()[function_name](2)) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("globals()[function_name](2)"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - globals()[function_name](None) - self.assertListEqual(c_m, []) - - def test_class_deprecation1(self): - """test class deprecation 1""" - - msg_ref = ( - "The DeprecatedClass1 class is deprecated as of version 0.3.0 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the NewClass class." - ) - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj = DeprecatedClass1() - self.assertEqual(obj.value, 10) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("DeprecatedClass1()"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj = DeprecatedClass1() - self.assertEqual(obj.value, 10) - self.assertListEqual(c_m, []) - - def test_class_deprecation2(self): - """test class deprecation 2""" - - msg_ref = ( - "The DeprecatedClass2 class is deprecated as of version 0.3.0 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the DeprecatedClass2 class from package test2." - ) - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj = DeprecatedClass2() - self.assertEqual(obj.value, 10) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("DeprecatedClass2()"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj = DeprecatedClass2() - self.assertEqual(obj.value, 10) - self.assertListEqual(c_m, []) - - @data( - ( - "l1", - 'The loss argument value "l1" is deprecated as of version 0.2.0 ' - "and will be removed no sooner than 3 months after the release. " - 'Instead use the "absolute_error" value.', - ), - ( - "l2", - 'The loss argument value "l2" is deprecated as of version 0.2.0 ' - "and will be removed no sooner than 3 months after the release. " - 'Instead use the "squared_error" value.', - ), - ) - def test_string_values_deprecation(self, config): - """test string values deprecation""" - - loss, msg_ref = config - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj = DeprecatedClass3(10, loss=loss) - self.assertEqual(obj.value, 10) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual( - self._get_line_from_str("DeprecatedClass3(10, loss=loss)"), c_m[0].lineno - ) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj = DeprecatedClass3(10, loss=loss) - self.assertEqual(obj.value, 10) - self.assertListEqual(c_m, []) - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj = DeprecatedClass3(5, loss=loss) - obj.method1(10, loss) - self.assertEqual(obj.value, 10) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("obj.method1(10, loss)"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj = DeprecatedClass3(5, loss=loss) - obj.method1(10, loss) - self.assertEqual(obj.value, 10) - self.assertListEqual(c_m, []) - - @data( - ( - 3, - 'The status argument value "3" is deprecated as of version 0.2.0 ' - "and will be removed no sooner than 3 months after the release. " - 'Instead use the "2" value.', - ), - ( - 4, - 'The status argument value "4" is deprecated as of version 0.2.0 ' - "and will be removed no sooner than 3 months after the release. " - 'Instead use the "5" value.', - ), - ) - def test_int_values_deprecation(self, config): - """test int values deprecation""" - - status, msg_ref = config - obj = DeprecatedClass4() - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj.method1(status) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("obj.method1(status)"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj.method1(status) - self.assertEqual(obj.status, status) - self.assertListEqual(c_m, []) - - @data( - ( - "method1", - "The method1 method is deprecated as of version 0.1.0 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the some_method1 method and additional information.", - ), - ( - "method2", - "The method2 method is deprecated as of version 0.2.0 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the some_method2 method.", - ), - ) - def test_method_deprecation(self, config): - """test method deprecation""" - - method_name, msg_ref = config - method = getattr(TestClass(), method_name) - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(3, method(3)) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("method(3)"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(4, method(4)) - self.assertListEqual(c_m, []) - - def test_function_arguments_deprecation(self): - """test function arguments deprecation""" - - msg_ref = ( - "func3: the old_arg argument is deprecated as of version 0.1.2 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the new_arg argument." - ) - # both arguments at the same time should raise exception - with self.assertRaises(TypeError): - func3(new_arg="2222", old_arg="hello") - - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(("hello", None), func3(old_arg="hello")) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str('func3(old_arg="hello")'), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(("hello", None), func3(old_arg="hello")) - self.assertListEqual(c_m, []) - - def test_method_arguments_deprecation(self): - """test method arguments deprecation""" - - obj = TestClass() - - msg_ref = ( - "method3: the old_arg argument is deprecated as of version 0.1.2 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the new_arg argument." - ) - # both arguments at the same time should raise exception - with self.assertRaises(TypeError): - obj.method3(new_arg="2222", old_arg="hello") - - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(("hello", None), obj.method3(old_arg="hello")) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str('obj.method3(old_arg="hello")'), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(("hello", None), obj.method3(old_arg="hello")) - self.assertListEqual(c_m, []) - - def test_method_argument_type_deprecation(self): - """test method argument type deprecation""" - - msg_ref = ( - "The str type in the 'value' argument is deprecated as of version 0.6.0 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the int type." - ) - - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - _ = DeprecatedClass5("10") - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str('DeprecatedClass5("10")'), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - _ = DeprecatedClass5("10") - self.assertListEqual(c_m, []) - - def test_property_deprecation(self): - """test property deprecation""" - - obj = TestClass() - - msg_ref = ( - "The property1 property is deprecated as of version 0.1.0 " - "and will be removed no sooner than 3 months after the release." - ) - # property get - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(20, obj.property1) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("obj.property1"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - self.assertEqual(20, obj.property1) - self.assertListEqual(c_m, []) - - msg_ref = ( - "The property1 property is deprecated as of version 0.1.0 " - "and will be removed no sooner than 3 months after the release. " - "Instead use the new_property property and some additional information." - ) - # property set - # emit deprecation the first time it is used - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj.property1 = 0 - self.assertEqual(0, obj.property1) - msg = str(c_m[0].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[0].filename, c_m[0].filename) - self.assertEqual(self._get_line_from_str("obj.property1 = 0"), c_m[0].lineno) - - # trying again should not emit deprecation - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - obj.property1 = 0 - self.assertEqual(0, obj.property1) - self.assertListEqual(c_m, []) - - def test_function_positional(self): - """test function positional deprecation""" - - # pylint: disable=too-many-function-args - with warnings.catch_warnings(record=True) as c_m: - warnings.simplefilter("always") - value = function_positional(1, 2, 3, 4) - self.assertEqual(10, value) - for idx, name in enumerate(["b", "d"]): - msg_ref = ( - f"function_positional: {name} is no longer a positional argument " - "as of version 0.1 and will be removed no sooner " - "than 3 months after the release. Instead use it as a keyword argument" - ) - msg = str(c_m[idx].message) - self.assertEqual(msg, msg_ref) - self.assertTrue("test_deprecation.py" in c_m[idx].filename, c_m[idx].filename) - self.assertEqual( - self._get_line_from_str("value = function_positional(1, 2, 3, 4)"), - c_m[0].lineno, - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test_end2end_with_vqe.py b/test/test_end2end_with_vqe.py index b50f7d99e..6e7524573 100644 --- a/test/test_end2end_with_vqe.py +++ b/test/test_end2end_with_vqe.py @@ -14,7 +14,7 @@ import unittest -from test import QiskitNatureDeprecatedTestCase +from test import QiskitNatureTestCase from qiskit.algorithms.minimum_eigensolvers import VQE from qiskit.algorithms.optimizers import COBYLA @@ -23,11 +23,11 @@ from qiskit.utils import algorithm_globals import qiskit_nature.optionals as _optionals from qiskit_nature.second_q.drivers import PySCFDriver -from qiskit_nature.second_q.mappers import ParityMapper, QubitConverter +from qiskit_nature.second_q.mappers import ParityMapper @unittest.skipIf(not _optionals.HAS_PYSCF, "pyscf not available.") -class TestEnd2End(QiskitNatureDeprecatedTestCase): +class TestEnd2End(QiskitNatureTestCase): """End2End VQE tests.""" def setUp(self): @@ -37,10 +37,9 @@ def setUp(self): driver = PySCFDriver() problem = driver.run() main_op, aux_ops = problem.second_q_ops() - converter = QubitConverter(mapper=ParityMapper(), two_qubit_reduction=True) - num_particles = problem.num_particles - self.qubit_op = converter.convert(main_op, num_particles) - self.aux_ops = converter.convert_match(aux_ops) + mapper = ParityMapper(num_particles=problem.num_particles) + self.qubit_op = mapper.map(main_op) + self.aux_ops = mapper.map(aux_ops) self.reference_energy = -1.857275027031588 def test_end2end_h2(self): diff --git a/test/utils/test_linalg.py b/test/utils/test_linalg.py index 386a3f584..275be3aef 100644 --- a/test/utils/test_linalg.py +++ b/test/utils/test_linalg.py @@ -21,7 +21,7 @@ import qiskit_nature.optionals as _optionals from qiskit_nature.second_q.drivers import PySCFDriver -from qiskit_nature.second_q.operators.tensor_ordering import to_chemist_ordering +from qiskit_nature.second_q.operators.symmetric_two_body import unfold from qiskit_nature.testing import random_two_body_tensor_real from qiskit_nature.utils import double_factorized, givens_matrix, modified_cholesky @@ -81,7 +81,7 @@ def test_double_factorized_error_threshold_max_vecs(self): driver = PySCFDriver(atom="Li 0 0 0; H 0 0 1.6") driver_result = driver.run() electronic_energy = driver_result.hamiltonian - two_body_tensor = to_chemist_ordering(electronic_energy.electronic_integrals.alpha["++--"]) + two_body_tensor = unfold(electronic_energy.electronic_integrals.alpha["++--"]) with self.subTest("max rank"): max_vecs = 20