Skip to content

Commit

Permalink
refactor: Towards a non-driver future
Browse files Browse the repository at this point in the history
As outlined in qiskit-community#701, Qiskit Nature is working towards a non-driver
focused future. This means, rather than the `BaseProblem` interface
taking in a `BaseDriver` and an optional list of `BaseTransformer`
instances, the problem itself should be a fully standalone description
of the user's problem of interest.
In other words, `BaseProblem` should be the produced result of a
`BaseDriver` and, by extension, `BaseTransformer` instances should
transform problem instances.

This commit works towards such a future by refactoring the code:
- `BaseProblem` is the returned object of `BaseDriver.run`
- `BaseTransformer.transform` has `BaseProblem` as in- and output
- `BaseProblem` takes over the role of `GroupedProperty`
  - in doing so, `Hamiltonian` objects are more clearly separated from
    `Property` objects (further refactoring to come)
  • Loading branch information
mrossinek committed Jul 21, 2022
1 parent b0be0fb commit 271305e
Show file tree
Hide file tree
Showing 74 changed files with 682 additions and 891 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ the ground-state (minimum) energy of a molecule.
from qiskit_nature.settings import settings
from qiskit_nature.second_q.drivers import UnitsType
from qiskit_nature.second_q.drivers import PySCFDriver
from qiskit_nature.second_q.problems import ElectronicStructureProblem

settings.dict_aux_operators = True

Expand All @@ -78,13 +77,12 @@ settings.dict_aux_operators = True
driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.735',
unit=UnitsType.ANGSTROM,
basis='sto3g')
problem = ElectronicStructureProblem(driver)
problem = driver.run()

# generate the second-quantized operators
second_q_ops = problem.second_q_ops()
main_op = second_q_ops['ElectronicEnergy']
main_op, aux_ops = problem.second_q_ops()

particle_number = problem.grouped_property_transformed.get_property("ParticleNumber")
particle_number = problem.properties["ParticleNumber"]

num_particles = (particle_number.num_alpha, particle_number.num_beta)
num_spin_orbitals = particle_number.num_spin_orbitals
Expand Down
7 changes: 3 additions & 4 deletions docs/tutorials/01_electronic_structure.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@
"metadata": {},
"outputs": [],
"source": [
"from qiskit_nature.second_q.problems import ElectronicStructureProblem\n",
"from qiskit_nature.second_q.mappers import QubitConverter\n",
"from qiskit_nature.second_q.mappers import JordanWignerMapper, ParityMapper"
]
Expand Down Expand Up @@ -188,9 +187,9 @@
}
],
"source": [
"es_problem = ElectronicStructureProblem(driver)\n",
"second_q_op = es_problem.second_q_ops()\n",
"print(second_q_op[0])"
"es_problem = driver.run()\n",
"main_op, second_q_ops = es_problem.second_q_ops()\n",
"print(main_op)"
]
},
{
Expand Down
19 changes: 11 additions & 8 deletions docs/tutorials/02_vibrational_structure.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,13 @@
"metadata": {},
"outputs": [],
"source": [
"from qiskit_nature.second_q.problems import VibrationalStructureProblem\n",
"from qiskit_nature.second_q.mappers import QubitConverter\n",
"from qiskit_nature.second_q.mappers import DirectMapper\n",
"\n",
"vibrational_problem = VibrationalStructureProblem(driver, num_modals=2, truncation_order=2)\n",
"second_q_ops = vibrational_problem.second_q_ops()"
"vibrational_problem = driver.run()\n",
"vibrational_problem.num_modals = 2\n",
"vibrational_problem.truncation_order = 2\n",
"main_op, second_q_ops = vibrational_problem.second_q_ops()"
]
},
{
Expand Down Expand Up @@ -270,7 +271,7 @@
}
],
"source": [
"print(second_q_ops[0])"
"print(main_op)"
]
},
{
Expand Down Expand Up @@ -342,7 +343,7 @@
],
"source": [
"qubit_converter = QubitConverter(mapper=DirectMapper())\n",
"qubit_op = qubit_converter.convert(second_q_ops[0])\n",
"qubit_op = qubit_converter.convert(main_op)\n",
"\n",
"print(qubit_op)"
]
Expand Down Expand Up @@ -610,12 +611,14 @@
}
],
"source": [
"vibrational_problem = VibrationalStructureProblem(driver, num_modals=3, truncation_order=2)\n",
"second_q_ops = vibrational_problem.second_q_ops()\n",
"vibrational_problem = driver.run()\n",
"vibrational_problem.num_modals = 3\n",
"vibrational_problem.truncation_order = 2\n",
"main_op, second_q_ops = vibrational_problem.second_q_ops()\n",
"\n",
"qubit_converter = QubitConverter(mapper=DirectMapper())\n",
"\n",
"qubit_op = qubit_converter.convert(second_q_ops[0])\n",
"qubit_op = qubit_converter.convert(main_op)\n",
"\n",
"print(qubit_op)"
]
Expand Down
8 changes: 4 additions & 4 deletions docs/tutorials/03_ground_state_solvers.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
" ElectronicStructureDriverType,\n",
" ElectronicStructureMoleculeDriver,\n",
")\n",
"from qiskit_nature.second_q.problems import ElectronicStructureProblem\n",
"from qiskit_nature.second_q.mappers import QubitConverter\n",
"from qiskit_nature.second_q.mappers import JordanWignerMapper\n",
"\n",
Expand All @@ -43,7 +42,7 @@
" molecule, basis=\"sto3g\", driver_type=ElectronicStructureDriverType.PYSCF\n",
")\n",
"\n",
"es_problem = ElectronicStructureProblem(driver)\n",
"es_problem = driver.run()\n",
"qubit_converter = QubitConverter(JordanWignerMapper())"
]
},
Expand Down Expand Up @@ -276,12 +275,13 @@
"source": [
"from qiskit_nature.second_q.drivers import GaussianForcesDriver\n",
"from qiskit_nature.second_q.algorithms import NumPyMinimumEigensolverFactory\n",
"from qiskit_nature.second_q.problems import VibrationalStructureProblem\n",
"from qiskit_nature.second_q.mappers import DirectMapper\n",
"\n",
"driver = GaussianForcesDriver(logfile=\"aux_files/CO2_freq_B3LYP_631g.log\")\n",
"\n",
"vib_problem = VibrationalStructureProblem(driver, num_modals=2, truncation_order=2)\n",
"vib_problem = driver.run()\n",
"vib_problem.num_modals = 2\n",
"vib_problem.truncation_order = 2\n",
"\n",
"qubit_covnerter = QubitConverter(DirectMapper())\n",
"\n",
Expand Down
3 changes: 1 addition & 2 deletions docs/tutorials/04_excited_states_solvers.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
" ElectronicStructureDriverType,\n",
" ElectronicStructureMoleculeDriver,\n",
")\n",
"from qiskit_nature.second_q.problems import ElectronicStructureProblem\n",
"from qiskit_nature.second_q.mappers import QubitConverter\n",
"from qiskit_nature.second_q.mappers import JordanWignerMapper\n",
"\n",
Expand All @@ -58,7 +57,7 @@
" molecule, basis=\"sto3g\", driver_type=ElectronicStructureDriverType.PYSCF\n",
")\n",
"\n",
"es_problem = ElectronicStructureProblem(driver)\n",
"es_problem = driver.run()\n",
"qubit_converter = QubitConverter(JordanWignerMapper())"
]
},
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/07_leveraging_qiskit_runtime.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
" ElectronicStructureDriverType,\n",
" ElectronicStructureMoleculeDriver,\n",
")\n",
"from qiskit_nature.second_q.problems import ElectronicStructureProblem\n",
"from qiskit_nature.second_q.mappers import QubitConverter\n",
"from qiskit_nature.second_q.mappers import ParityMapper\n",
"from qiskit_nature.second_q.properties import ParticleNumber\n",
Expand Down Expand Up @@ -88,7 +87,8 @@
")\n",
"\n",
"# define electronic structure problem\n",
"problem = ElectronicStructureProblem(driver, transformers=[active_space_trafo])\n",
"problem = driver.run()\n",
"problem = active_space_trafo.transform(problem)",
"\n",
"# construct qubit converter (parity mapping + 2-qubit reduction)\n",
"qubit_converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)"
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/10_lattice_models.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,7 @@
" onsite_interaction=u,\n",
")\n",
"\n",
"lmp = LatticeModelProblem(lattice_model=fhm)"
"lmp = LatticeModelProblem(fhm)"
]
},
{
Expand Down Expand Up @@ -1021,4 +1021,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,7 @@ def get_qubit_operators(
"""Gets the operator and auxiliary operators, and transforms the provided auxiliary operators"""
# Note that ``aux_ops`` contains not only the transformed ``aux_operators`` passed by the
# user but also additional ones from the transformation
second_q_ops = problem.second_q_ops()
aux_second_q_ops: ListOrDictType[SecondQuantizedOp]
if isinstance(second_q_ops, list):
main_second_q_op = second_q_ops[0]
aux_second_q_ops = second_q_ops[1:]
elif isinstance(second_q_ops, dict):
name = problem.main_property_name
main_second_q_op = second_q_ops.pop(name, None)
if main_second_q_op is None:
raise ValueError(
f"The main `SecondQuantizedOp` associated with the {name} property cannot be "
"`None`."
)
aux_second_q_ops = second_q_ops
main_second_q_op, aux_second_q_ops = problem.second_q_ops()

main_operator = self._qubit_converter.convert(
main_second_q_op,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,7 @@ def solve(
groundstate_result = self._gsc.solve(problem, aux_operators)

# 2. Prepare the excitation operators
second_q_ops = problem.second_q_ops()
if isinstance(second_q_ops, list):
main_second_q_op = second_q_ops[0]
elif isinstance(second_q_ops, dict):
main_second_q_op = second_q_ops.pop(problem.main_property_name)
main_second_q_op, _ = problem.second_q_ops()

self._untapered_qubit_op_main = self._gsc.qubit_converter.convert_only(
main_second_q_op, problem.num_particles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,21 +211,7 @@ def solve(
information about the AdaptVQE algorithm like the number of iterations, finishing
criterion, and the final maximum gradient.
"""
second_q_ops = problem.second_q_ops()

aux_second_q_ops: ListOrDictType[SecondQuantizedOp]
if isinstance(second_q_ops, list):
main_second_q_op = second_q_ops[0]
aux_second_q_ops = second_q_ops[1:]
elif isinstance(second_q_ops, dict):
name = problem.main_property_name
main_second_q_op = second_q_ops.pop(name, None)
if main_second_q_op is None:
raise ValueError(
f"The main `SecondQuantizedOp` associated with the {name} property cannot be "
"`None`."
)
aux_second_q_ops = second_q_ops
main_second_q_op, aux_second_q_ops = problem.second_q_ops()

self._main_operator = self._qubit_converter.convert(
main_second_q_op,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,8 @@ def get_qubit_operators(
"""Gets the operator and auxiliary operators, and transforms the provided auxiliary operators"""
# Note that ``aux_ops`` contains not only the transformed ``aux_operators`` passed by the
# user but also additional ones from the transformation
second_q_ops = problem.second_q_ops()
aux_second_q_ops: ListOrDictType[SecondQuantizedOp]
if isinstance(second_q_ops, list):
main_second_q_op = second_q_ops[0]
aux_second_q_ops = second_q_ops[1:]
elif isinstance(second_q_ops, dict):
name = problem.main_property_name
main_second_q_op = second_q_ops.pop(name, None)
if main_second_q_op is None:
raise ValueError(
f"The main `SecondQuantizedOp` associated with the {name} property cannot be "
"`None`."
)
aux_second_q_ops = second_q_ops
main_second_q_op, aux_second_q_ops = problem.second_q_ops()

main_operator = self._qubit_converter.convert(
main_second_q_op,
num_particles=problem.num_particles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,7 @@ def get_solver( # type: ignore[override]
Returns:
A VQE suitable to compute the ground state of the molecule.
"""
driver_result = problem.grouped_property_transformed
particle_number = cast(ParticleNumber, driver_result.get_property(ParticleNumber))
particle_number = cast(ParticleNumber, problem.properties["ParticleNumber"])
num_spin_orbitals = particle_number.num_spin_orbitals
num_particles = particle_number.num_alpha, particle_number.num_beta

Expand All @@ -296,7 +295,7 @@ def get_solver( # type: ignore[override]

if isinstance(self.initial_point, InitialPoint):
self.initial_point.ansatz = ansatz
self.initial_point.grouped_property = driver_result
self.initial_point.grouped_property = problem
initial_point = self.initial_point.to_numpy_array()
else:
initial_point = self.initial_point
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""The minimum eigensolver factory for ground state calculation algorithms."""

import logging
from typing import Optional, Union, Callable, cast
from typing import Optional, Union, Callable
import numpy as np

from qiskit.algorithms import MinimumEigensolver, VQE
Expand All @@ -27,9 +27,6 @@
from qiskit_nature.second_q.problems import (
VibrationalStructureProblem,
)
from qiskit_nature.second_q.properties import (
VibrationalStructureDriverResult,
)
from qiskit_nature.deprecation import deprecate_property, deprecate_positional_arguments

from .minimum_eigensolver_factory import MinimumEigensolverFactory
Expand Down Expand Up @@ -265,7 +262,7 @@ def get_solver( # type: ignore[override]
A VQE suitable to compute the ground state of the molecule.
"""

basis = cast(VibrationalStructureDriverResult, problem.grouped_property_transformed).basis
basis = problem.basis
num_modals = basis.num_modals_per_mode
num_modes = len(num_modals)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@

from qiskit_nature.second_q.circuit.library import UCC
from qiskit_nature.second_q.properties import ElectronicEnergy
from qiskit_nature.second_q.properties.second_quantized_property import (
GroupedSecondQuantizedProperty,
)
from qiskit_nature.second_q.problems import BaseProblem
from qiskit_nature.exceptions import QiskitNatureError

from .initial_point import InitialPoint
Expand Down Expand Up @@ -50,7 +48,7 @@ def __init__(self) -> None:
self._parameters: np.ndarray | None = None

@property
def grouped_property(self) -> GroupedSecondQuantizedProperty | None:
def grouped_property(self) -> BaseProblem | None:
"""The grouped property.
The grouped property is not required to compute the HF initial point. If it is provided we
Expand All @@ -59,9 +57,9 @@ def grouped_property(self) -> GroupedSecondQuantizedProperty | None:
return self._grouped_property

@grouped_property.setter
def grouped_property(self, grouped_property: GroupedSecondQuantizedProperty) -> None:
electronic_energy: ElectronicEnergy | None = grouped_property.get_property(ElectronicEnergy)
if electronic_energy is None:
def grouped_property(self, grouped_property: BaseProblem) -> None:
electronic_energy = grouped_property.hamiltonian
if electronic_energy is None or not isinstance(electronic_energy, ElectronicEnergy):
warnings.warn(
"The ElectronicEnergy was not obtained from the grouped_property. "
"The grouped_property and reference_energy will not be set."
Expand Down Expand Up @@ -115,7 +113,7 @@ def to_numpy_array(self) -> np.ndarray:
def compute(
self,
ansatz: UCC | None = None,
grouped_property: GroupedSecondQuantizedProperty | None = None,
grouped_property: BaseProblem | None = None,
) -> None:
"""Compute the coefficients and energy corrections.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
import numpy as np

from qiskit.circuit.library import EvolvedOperatorAnsatz
from qiskit_nature.second_q.properties.second_quantized_property import (
GroupedSecondQuantizedProperty,
)
from qiskit_nature.second_q.problems import BaseProblem


class InitialPoint(ABC):
Expand All @@ -34,7 +32,7 @@ class InitialPoint(ABC):
@abstractmethod
def __init__(self):
self._ansatz: EvolvedOperatorAnsatz | None = None
self._grouped_property: GroupedSecondQuantizedProperty | None = None
self._grouped_property: BaseProblem | None = None

@property
@abstractmethod
Expand All @@ -51,7 +49,7 @@ def ansatz(self, ansatz: EvolvedOperatorAnsatz) -> None:
raise NotImplementedError

@property
def grouped_property(self) -> GroupedSecondQuantizedProperty | None:
def grouped_property(self) -> BaseProblem | None:
"""The grouped property.
Raises:
Expand All @@ -60,7 +58,7 @@ def grouped_property(self) -> GroupedSecondQuantizedProperty | None:
raise NotImplementedError

@grouped_property.setter
def grouped_property(self, grouped_property: GroupedSecondQuantizedProperty) -> None:
def grouped_property(self, grouped_property: BaseProblem) -> None:
raise NotImplementedError

@abstractmethod
Expand All @@ -75,7 +73,7 @@ def to_numpy_array(self) -> np.ndarray:
def compute(
self,
ansatz: EvolvedOperatorAnsatz | None = None,
grouped_property: GroupedSecondQuantizedProperty | None = None,
grouped_property: BaseProblem | None = None,
) -> None:
"""Compute the initial point array"""
raise NotImplementedError
Loading

0 comments on commit 271305e

Please sign in to comment.