Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added V2 and ISA support #197

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
634dc69
Added TODO
tnemoz Aug 7, 2024
ecee0c6
Started to adapt to V2 primitives
tnemoz Aug 7, 2024
529c5da
Modified phase estimators to work with V2 primitives and ISA circuits
tnemoz Aug 9, 2024
e390037
Merge branch 'main' into main
woodsp-ibm Aug 9, 2024
da40425
Changed PassManager to more generic transpiler
tnemoz Aug 12, 2024
d83408c
Changed custom types to support Python 3.8
tnemoz Aug 12, 2024
0e6e6f0
Added transpiler to .pylintdict
tnemoz Aug 12, 2024
0e8ac74
Adapted Grover to V2 primitives
tnemoz Aug 12, 2024
3d79bb5
Changed custom types using __future__ annotations
tnemoz Aug 16, 2024
eae6cff
Adapated VQD to V2 primitives
tnemoz Aug 16, 2024
df69ea7
Adapted tests for Grover
tnemoz Aug 16, 2024
111188b
Fixed styling
tnemoz Aug 16, 2024
4b46931
Merge branch 'main' into main
tnemoz Aug 24, 2024
c03c3a1
Merge branch 'min_eigen' into dev
Aug 25, 2024
93af774
removed useless comments in vqe and changed SamplingVQE to match v2 p…
Aug 26, 2024
63332e9
changed AdaptVQE
Aug 26, 2024
1b58074
changed diagonal_estimators from v1 to v2 primitives
Aug 26, 2024
b48c66d
updated TODO
Aug 30, 2024
1cd76ff
changed qaoa to match v2 and isa
Aug 30, 2024
4f734d9
modified all associated tests for min_eigen classes
Aug 30, 2024
d7b671f
changed todo
Aug 30, 2024
d2bc2db
ComputeUncompute done
tnemoz Sep 2, 2024
cae37b2
QNSPSA done
tnemoz Sep 2, 2024
84128a8
Restored SPSA test to previous values
tnemoz Sep 10, 2024
b3c32a3
Refactored test for transpilers, make linter happy, test_vqd not passing
tnemoz Sep 10, 2024
dc075d0
Updated TODO
tnemoz Sep 10, 2024
0790c53
PVQD done
tnemoz Sep 10, 2024
7ff3ab6
Slight modifications of VQD, still isn't working
tnemoz Sep 18, 2024
bf6a931
Adapted PVQD to EstimatorV2
tnemoz Aug 16, 2024
dea4bd0
Adapted Trotter QRTE to EstimatorV2
tnemoz Aug 16, 2024
8d9a54c
Trotter QRTE
tnemoz Sep 18, 2024
8c87b8d
Time evolvers
tnemoz Dec 5, 2024
e3150cf
Merge branch 'main' into main
tnemoz Dec 5, 2024
3fcc265
Fixed linting and VQD test
tnemoz Dec 5, 2024
a1171dc
Updated TODO
tnemoz Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pylintdict
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ trainability
transpilation
transpile
transpiled
transpiler
trotterization
trotterized
uncompute
Expand Down
39 changes: 39 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
- [x] phase_estimators/hamiltonian_phase_estimation.py
- [x] phase_estimators/ipe.py
- [x] phase_estimators/phase_estimation.py
- [x] eigensolvers/vqd.py
- [x] amplitude_amplifiers/grover.py (test doesn't work but should probably be refactored or removed)
- [x] time_evolvers/pvqd/utils.py
- [x] time_evolvers/pvqd/pvqd.py
- [x] time_evolvers/trotterization/trotter_qrte.py
- [x] time_evolvers/variational/variational_principles/imaginary_mc_lachlan_principle.py
- [x] time_evolvers/variational/variational_principles/real_mc_lachlan_principle.py
- [x] time_evolvers/variational/var_qite.py
- [x] time_evolvers/variational/var_qrte.py
- [x] time_evolvers/variational/var_qte.py
- [x] state_fidelities/compute_uncompute.py
- [x] optimizers/qnspsa.py
- [x] optimizers/umda.py
- [x] optimizers/spsa.py
- [x] observables_evaluator.py
- [] gradients/reverse/reverse_gradient.py
- [] gradients/reverse/reverse_qgt.py
- [] gradients/finite_diff/finite_diff_estimator_gradient.py
- [] gradients/finite_diff/finite_diff_sampler_gradient.py
- [] gradients/spsa/spsa_estimator_gradient.py
- [] gradients/spsa/spsa_sampler_gradient.py
- [] gradients/lin_comb/lin_comb_sampler_gradient.py
- [] gradients/lin_comb/lin_comb_estimator_gradient.py
- [] gradients/lin_comb/lin_comb_qgt.py
- [] gradients/base/base_sampler_gradient.py
- [] gradients/base/base_qgt.py
- [] gradients/base/base_estimator_gradient.py
- [x] minimum_eigensolvers/vqe.py
- [x] minimum_eigensolvers/adapt_vqe.py
- [x] minimum_eigensolvers/qaoa.py
- [x] minimum_eigensolvers/diagonal_estimator.py
- [x] minimum_eigensolvers/sampling_vqe.py
- [] amplitude_estimators/mlae.py
- [] amplitude_estimators/fae.py
- [] amplitude_estimators/iae.py
- [] amplitude_estimators/ae.py
2 changes: 1 addition & 1 deletion docs/tutorials/10_pvqd.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
"metadata": {},
"outputs": [],
"source": [
"from qiskit.primitives import Sampler, Estimator\n",
"from qiskit.primitives import Sampler, StatevectorEstimator as Estimator\n",
"from qiskit_algorithms.state_fidelities import ComputeUncompute\n",
"\n",
"# the fidelity is used to evaluate the objective: the overlap of the variational form and the trotter step\n",
Expand Down
22 changes: 1 addition & 21 deletions qiskit_algorithms/algorithm_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,6 @@ class AlgorithmJob(PrimitiveJob):
"""
This class is introduced for typing purposes and provides no
additional function beyond that inherited from its parents.

Update: :meth:`AlgorithmJob.submit()` method added. See its
documentation for more info.
"""

def submit(self) -> None:
"""
Submit the job for execution.

For V1 primitives, Qiskit ``PrimitiveJob`` subclassed JobV1 and defined ``submit()``.
``PrimitiveJob`` was updated for V2 primitives, no longer subclasses ``JobV1``, and
now has a private ``_submit()`` method, with ``submit()`` being deprecated as of
Qiskit version 0.46. This maintains the ``submit()`` for ``AlgorithmJob`` here as
it's called in many places for such a job. An alternative could be to make
0.46 the required minimum version and alter all algorithm's call sites to use
``_submit()`` and make this an empty class again as it once was. For now this
way maintains compatibility with the current min version of 0.44.
"""
# TODO: Considering changing this in the future - see above docstring.
try:
super()._submit()
except AttributeError:
super().submit()
pass
59 changes: 36 additions & 23 deletions qiskit_algorithms/amplitude_amplifiers/grover.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@
from typing import Any

import numpy as np

from qiskit import ClassicalRegister, QuantumCircuit
from qiskit.primitives import BaseSampler
from qiskit.quantum_info import Statevector
from qiskit.primitives import BaseSamplerV2

from qiskit_algorithms.exceptions import AlgorithmError
from qiskit_algorithms.utils import algorithm_globals

from .amplification_problem import AmplificationProblem
from .amplitude_amplifier import AmplitudeAmplifier, AmplitudeAmplifierResult
from ..custom_types import Transpiler


class Grover(AmplitudeAmplifier):
Expand Down Expand Up @@ -116,7 +114,9 @@ def __init__(
iterations: list[int] | Iterator[int] | int | None = None,
growth_rate: float | None = None,
sample_from_iterations: bool = False,
sampler: BaseSampler | None = None,
sampler: BaseSamplerV2 | None = None,
transpiler: Transpiler | None = None,
transpiler_options: dict[str, Any] | None = None,
) -> None:
r"""
Args:
Expand All @@ -136,6 +136,11 @@ def __init__(
powers of the Grover operator, a random integer sample between 0 and smaller value
than the iteration is used as a power, see [1], Section 4.
sampler: A Sampler to use for sampling the results of the circuits.
transpiler: An optional object with a `run` method allowing to transpile the circuits
that are produced within this algorithm. If set to `None`, these won't be
transpiled.
transpiler_options: A dictionary of options to be passed to the transpiler's `run`
method as keyword arguments.

Raises:
ValueError: If ``growth_rate`` is a float but not larger than 1.
Expand Down Expand Up @@ -165,9 +170,11 @@ def __init__(
self._sampler = sampler
self._sample_from_iterations = sample_from_iterations
self._iterations_arg = iterations
self._transpiler = transpiler
self._transpiler_options = transpiler_options if transpiler_options is not None else {}

@property
def sampler(self) -> BaseSampler | None:
def sampler(self) -> BaseSamplerV2 | None:
"""Get the sampler.

Returns:
Expand All @@ -176,7 +183,7 @@ def sampler(self) -> BaseSampler | None:
return self._sampler

@sampler.setter
def sampler(self, sampler: BaseSampler) -> None:
def sampler(self, sampler: BaseSamplerV2) -> None:
"""Set the sampler.

Args:
Expand Down Expand Up @@ -234,23 +241,29 @@ def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult"
# sample from [0, power) if specified
if self._sample_from_iterations:
power = algorithm_globals.random.integers(power)

# Run a grover experiment for a given power of the Grover operator.
if self._sampler is not None:
qc = self.construct_circuit(amplification_problem, power, measurement=True)
job = self._sampler.run([qc])

try:
results = job.result()
except Exception as exc:
raise AlgorithmError("Sampler job failed.") from exc

num_bits = len(amplification_problem.objective_qubits)
circuit_results: dict[str, Any] | Statevector | np.ndarray = {
np.binary_repr(k, num_bits): v for k, v in results.quasi_dists[0].items()
}
top_measurement, max_probability = max(
circuit_results.items(), key=lambda x: x[1] # type: ignore[union-attr]
)
qc = self.construct_circuit(amplification_problem, power, measurement=True)

if self._transpiler is not None:
qc = self._transpiler.run(qc, **self._transpiler_options)

job = self._sampler.run([qc])

try:
results = job.result()
except Exception as exc:
raise AlgorithmError("Sampler job failed.") from exc

circuit_results = getattr(results[0].data, qc.cregs[0].name)
circuit_results = {
label: value / circuit_results.num_shots
for label, value in circuit_results.get_counts().items()
}

top_measurement, max_probability = max(
circuit_results.items(), key=lambda x: x[1] # type: ignore[union-attr]
)

all_circuit_results.append(circuit_results)

Expand Down
28 changes: 28 additions & 0 deletions qiskit_algorithms/custom_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2024.
#
# 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.

"""Types used by the qiskit-algorithms package."""
from __future__ import annotations

from typing import Any, Protocol, Union

from qiskit import QuantumCircuit

_Circuits = Union[list[QuantumCircuit], QuantumCircuit]


class Transpiler(Protocol):
"""A Generic type to represent a transpiler."""

def run(self, circuits: _Circuits, **options: Any) -> _Circuits:
"""Transpile a circuit or a list of quantum circuits."""
pass
25 changes: 11 additions & 14 deletions qiskit_algorithms/eigensolvers/vqd.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import numpy as np

from qiskit.circuit import QuantumCircuit
from qiskit.primitives import BaseEstimator
from qiskit.primitives import BaseEstimatorV2
from qiskit.quantum_info.operators.base_operator import BaseOperator
from qiskit.quantum_info import SparsePauliOp

Expand Down Expand Up @@ -88,7 +88,7 @@ class VQD(VariationalAlgorithm, Eigensolver):
updated once the VQD object has been constructed.

Attributes:
estimator (BaseEstimator): The primitive instance used to perform the expectation
estimator (BaseEstimatorV2): The primitive instance used to perform the expectation
estimation as indicated in the VQD paper.
fidelity (BaseStateFidelity): The fidelity class instance used to compute the
overlap estimation as indicated in the VQD paper.
Expand All @@ -115,7 +115,7 @@ class VQD(VariationalAlgorithm, Eigensolver):

def __init__(
self,
estimator: BaseEstimator,
estimator: BaseEstimatorV2,
fidelity: BaseStateFidelity,
ansatz: QuantumCircuit,
optimizer: Optimizer | Minimizer | Sequence[Optimizer | Minimizer],
Expand Down Expand Up @@ -363,9 +363,9 @@ def compute_eigenvalues(

raise AlgorithmError(
f"Convergence threshold is set to {self.convergence_threshold} but an "
f"average fidelity of {average_fidelity:.5f} with the previous eigenstates"
f"has been observed during the evaluation of the {step}{suffix} lowest"
f"eigenvalue."
f"average (weighted by the betas) fidelity of {average_fidelity:.5f} with "
f"the previous eigenstates has been observed during the evaluation of the "
f"{step}{suffix} lowest eigenvalue."
)
logger.info(
(
Expand Down Expand Up @@ -433,9 +433,7 @@ def evaluate_energy(parameters: np.ndarray) -> float | np.ndarray:
parameters = np.reshape(parameters, (-1, num_parameters))
batch_size = len(parameters)

estimator_job = self.estimator.run(
batch_size * [self.ansatz], batch_size * [operator], parameters
)
estimator_job = self.estimator.run([(self.ansatz, operator, parameters)])

total_cost = np.zeros(batch_size)

Expand All @@ -454,18 +452,17 @@ def evaluate_energy(parameters: np.ndarray) -> float | np.ndarray:
total_cost += np.real(betas[state] * cost)

try:
estimator_result = estimator_job.result()
estimator_result = estimator_job.result()[0]

except Exception as exc:
raise AlgorithmError("The primitive job to evaluate the energy failed!") from exc

values = estimator_result.values + total_cost
values = estimator_result.data.evs + total_cost

if self.callback is not None:
metadata = estimator_result.metadata
for params, value, meta in zip(parameters, values, metadata):
for params, value in zip(parameters, values):
self._eval_count += 1
self.callback(self._eval_count, params, value, meta, step)
self.callback(self._eval_count, params, value, estimator_result.metadata, step)
else:
self._eval_count += len(values)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ def run(
job = AlgorithmJob(
self._run, circuits, observables, parameter_values, parameters, **opts.__dict__
)
job.submit()
job._submit()
return job

@abstractmethod
Expand Down
2 changes: 1 addition & 1 deletion qiskit_algorithms/gradients/base/base_qgt.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def run(
opts = copy(self._default_options)
opts.update_options(**options)
job = AlgorithmJob(self._run, circuits, parameter_values, parameters, **opts.__dict__)
job.submit()
job._submit()
return job

@abstractmethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def run(
opts = copy(self._default_options)
opts.update_options(**options)
job = AlgorithmJob(self._run, circuits, parameter_values, parameters, **opts.__dict__)
job.submit()
job._submit()
return job

@abstractmethod
Expand Down
2 changes: 1 addition & 1 deletion qiskit_algorithms/gradients/qfi.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def run(
opts = copy(self._default_options)
opts.update_options(**options)
job = AlgorithmJob(self._run, circuits, parameter_values, parameters, **opts.__dict__)
job.submit()
job._submit()
return job

def _run(
Expand Down
4 changes: 2 additions & 2 deletions qiskit_algorithms/minimum_eigensolvers/adapt_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class AdaptVQE(VariationalAlgorithm, MinimumEigensolver):

from qiskit_algorithms.minimum_eigensolvers import AdaptVQE, VQE
from qiskit_algorithms.optimizers import SLSQP
from qiskit.primitives import Estimator
from qiskit.primitives import StatevectorEstimator
from qiskit.circuit.library import EvolvedOperatorAnsatz

# get your Hamiltonian
Expand All @@ -71,7 +71,7 @@ class AdaptVQE(VariationalAlgorithm, MinimumEigensolver):
# construct your ansatz
ansatz = EvolvedOperatorAnsatz(...)

vqe = VQE(Estimator(), ansatz, SLSQP())
vqe = VQE(StatevectorEstimator(), ansatz, SLSQP())

adapt_vqe = AdaptVQE(vqe)

Expand Down
Loading
Loading