Skip to content

Commit

Permalink
Fix ComputeUncompute State Fidelity threading issue
Browse files Browse the repository at this point in the history
  • Loading branch information
woodsp-ibm committed Oct 16, 2023
1 parent 54796fb commit b263849
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 19 deletions.
13 changes: 6 additions & 7 deletions qiskit_algorithms/state_fidelities/base_state_fidelity.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from qiskit.circuit import ParameterVector

from ..algorithm_job import AlgorithmJob
from .state_fidelity_result import StateFidelityResult


class BaseStateFidelity(ABC):
Expand Down Expand Up @@ -246,7 +245,7 @@ def _run(
values_1: Sequence[float] | Sequence[Sequence[float]] | None = None,
values_2: Sequence[float] | Sequence[Sequence[float]] | None = None,
**options,
) -> StateFidelityResult:
) -> AlgorithmJob:
r"""
Computes the state overlap (fidelity) calculation between two
(parametrized) circuits (first and second) for a specific set of parameter
Expand All @@ -263,7 +262,7 @@ def _run(
Higher priority setting overrides lower priority setting.
Returns:
The result of the fidelity calculation.
A newly constructed algorithm job instance to get the fidelity result.
"""
raise NotImplementedError

Expand Down Expand Up @@ -293,15 +292,15 @@ def run(
Returns:
Primitive job for the fidelity calculation.
The job's result is an instance of ``StateFidelityResult``.
The job's result is an instance of :class:`.StateFidelityResult`.
"""

job = AlgorithmJob(self._run, circuits_1, circuits_2, values_1, values_2, **options)
job = self._run(circuits_1, circuits_2, values_1, values_2, **options)

job.submit()
return job

def _truncate_fidelities(self, fidelities: Sequence[float]) -> Sequence[float]:
@staticmethod
def _truncate_fidelities(fidelities: Sequence[float]) -> Sequence[float]:
"""
Ensures fidelity result in [0,1].
Expand Down
33 changes: 21 additions & 12 deletions qiskit_algorithms/state_fidelities/compute_uncompute.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@

from qiskit import QuantumCircuit
from qiskit.primitives import BaseSampler
from qiskit.primitives.primitive_job import PrimitiveJob
from qiskit.providers import Options

from ..exceptions import AlgorithmError
from .base_state_fidelity import BaseStateFidelity
from .state_fidelity_result import StateFidelityResult
from ..algorithm_job import AlgorithmJob


class ComputeUncompute(BaseStateFidelity):
Expand Down Expand Up @@ -118,7 +120,7 @@ def _run(
values_1: Sequence[float] | Sequence[Sequence[float]] | None = None,
values_2: Sequence[float] | Sequence[Sequence[float]] | None = None,
**options,
) -> StateFidelityResult:
) -> AlgorithmJob:
r"""
Computes the state overlap (fidelity) calculation between two
(parametrized) circuits (first and second) for a specific set of parameter
Expand All @@ -135,7 +137,7 @@ def _run(
Higher priority setting overrides lower priority setting.
Returns:
The result of the fidelity calculation.
An AlgorithmJOb for the fidelity calculation.
Raises:
ValueError: At least one pair of circuits must be defined.
Expand All @@ -155,29 +157,36 @@ def _run(
opts = copy(self._default_options)
opts.update_options(**options)

job = self._sampler.run(circuits=circuits, parameter_values=values, **opts.__dict__)
sampler_job = self._sampler.run(circuits=circuits, parameter_values=values, **opts.__dict__)

local_opts = self._get_local_options(opts.__dict__)
return AlgorithmJob(ComputeUncompute._call, sampler_job, circuits, self._local, local_opts)

@staticmethod
def _call(
job: PrimitiveJob, circuits: Sequence[QuantumCircuit], local: bool, local_opts: Options
) -> StateFidelityResult:
try:
result = job.result()
except Exception as exc:
raise AlgorithmError("Sampler job failed!") from exc

if self._local:
if local:
raw_fidelities = [
self._get_local_fidelity(prob_dist, circuit.num_qubits)
ComputeUncompute._get_local_fidelity(prob_dist, circuit.num_qubits)
for prob_dist, circuit in zip(result.quasi_dists, circuits)
]
else:
raw_fidelities = [
self._get_global_fidelity(prob_dist) for prob_dist in result.quasi_dists
ComputeUncompute._get_global_fidelity(prob_dist) for prob_dist in result.quasi_dists
]
fidelities = self._truncate_fidelities(raw_fidelities)
fidelities = ComputeUncompute._truncate_fidelities(raw_fidelities)

return StateFidelityResult(
fidelities=fidelities,
raw_fidelities=raw_fidelities,
metadata=result.metadata,
options=self._get_local_options(opts.__dict__),
options=local_opts,
)

@property
Expand Down Expand Up @@ -216,7 +225,8 @@ def _get_local_options(self, options: Options) -> Options:
opts.update_options(**options)
return opts

def _get_global_fidelity(self, probability_distribution: dict[int, float]) -> float:
@staticmethod
def _get_global_fidelity(probability_distribution: dict[int, float]) -> float:
"""Process the probability distribution of a measurement to determine the
global fidelity.
Expand All @@ -228,9 +238,8 @@ def _get_global_fidelity(self, probability_distribution: dict[int, float]) -> fl
"""
return probability_distribution.get(0, 0)

def _get_local_fidelity(
self, probability_distribution: dict[int, float], num_qubits: int
) -> float:
@staticmethod
def _get_local_fidelity(probability_distribution: dict[int, float], num_qubits: int) -> float:
"""Process the probability distribution of a measurement to determine the
local fidelity by averaging over single-qubit projectors.
Expand Down

0 comments on commit b263849

Please sign in to comment.