Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions qiskit_experiments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
:toctree: ../stubs/

ExperimentData
AnalysisResult


Experiment Base Classes
Expand All @@ -49,7 +48,7 @@
from .version import __version__

# Base Classes
from .experiment_data import ExperimentData, AnalysisResult
from .experiment_data import ExperimentData
from .base_analysis import BaseAnalysis
from .base_experiment import BaseExperiment

Expand Down
8 changes: 4 additions & 4 deletions qiskit_experiments/analysis/curve_fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import numpy as np
import scipy.optimize as opt
from qiskit.exceptions import QiskitError
from qiskit_experiments.experiment_data import AnalysisResult

from qiskit_experiments.analysis.data_processing import filter_data


Expand All @@ -31,7 +31,7 @@ def curve_fit(
sigma: Optional[np.ndarray] = None,
bounds: Optional[Union[Dict[str, Tuple[float, float]], Tuple[np.ndarray, np.ndarray]]] = None,
**kwargs,
) -> AnalysisResult:
) -> Dict:
r"""Perform a non-linear least squares to fit

This solves the optimization problem
Expand Down Expand Up @@ -139,7 +139,7 @@ def fit_func(x, *params):
"xrange": xdata_range,
}

return AnalysisResult(result)
return result


def multi_curve_fit(
Expand All @@ -152,7 +152,7 @@ def multi_curve_fit(
weights: Optional[np.ndarray] = None,
bounds: Optional[Union[Dict[str, Tuple[float, float]], Tuple[np.ndarray, np.ndarray]]] = None,
**kwargs,
) -> AnalysisResult:
) -> Dict:
r"""Perform a linearized multi-objective non-linear least squares fit.

This solves the optimization problem
Expand Down
7 changes: 3 additions & 4 deletions qiskit_experiments/analysis/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
"""
Plotting functions for experiment analysis
"""
from typing import Callable, Optional
from typing import Callable, Optional, Dict
import numpy as np

from qiskit_experiments.experiment_data import AnalysisResult
from qiskit_experiments.matplotlib import pyplot, requires_matplotlib

# pylint: disable = unused-import
Expand All @@ -25,7 +24,7 @@
@requires_matplotlib
def plot_curve_fit(
func: Callable,
result: AnalysisResult,
result: Dict,
confidence_interval: bool = True,
ax=None,
num_fit_points: int = 100,
Expand All @@ -39,7 +38,7 @@ def plot_curve_fit(

Args:
func: the fit function for curve_fit.
result: an AnalysisResult from curve_fit.
result: a dictionary from curve_fit.
confidence_interval: if True plot the confidence interval from popt_err.
ax (matplotlib.axes.Axes): Optional, a matplotlib axes to add the plot to.
num_fit_points: the number of points to plot for xrange.
Expand Down
19 changes: 4 additions & 15 deletions qiskit_experiments/base_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

from qiskit.providers.options import Options
from qiskit.exceptions import QiskitError

from qiskit.providers.experiment import AnalysisResultV1

from .experiment_data import ExperimentData
Expand Down Expand Up @@ -93,24 +92,14 @@ def run(

# Run analysis
# pylint: disable=broad-except
try:
analysis_results, figures = self._run_analysis(experiment_data, **analysis_options)
analysis_results["success"] = True
except Exception as ex:
analysis_results = AnalysisResult(success=False, error_message=ex)
figures = None
analysis_results, figures = self._run_analysis(experiment_data, **analysis_options)

# Save to experiment data
if save:
if isinstance(analysis_results, AnalysisResultV1):
experiment_data.add_analysis_result(analysis_results)
else:
for res in analysis_results:
experiment_data.add_analysis_result(res)
experiment_data.add_analysis_results(analysis_results)
if figures:
for fig in figures:
experiment_data.add_figure(fig)

experiment_data.add_figures(figures)

if return_figures:
return analysis_results, figures
return analysis_results
Expand Down
15 changes: 10 additions & 5 deletions qiskit_experiments/base_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ def run(
else:
job = backend.run(circuits, **run_opts)

# Add Job to ExperimentData
experiment_data.add_data(job)

# Queue analysis of data for when job is finished
# Add Job to ExperimentData and add analysis for post processing.
run_analysis = None
if analysis and self.__analysis_class__ is not None:
self.run_analysis(experiment_data)
run_analysis = self.__analysis_class__().run

experiment_data.add_data(job, post_processing_callback=run_analysis)

# Return the ExperimentData future
return experiment_data
Expand Down Expand Up @@ -162,6 +162,11 @@ def physical_qubits(self) -> Tuple[int]:
"""Return the physical qubits for this experiment."""
return self._physical_qubits

@property
def experiment_type(self) -> str:
"""Return experiment type."""
return self._type

@classmethod
def analysis(cls):
"""Return the default Analysis class for the experiment."""
Expand Down
19 changes: 8 additions & 11 deletions qiskit_experiments/characterization/t1_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
from qiskit_experiments.analysis.curve_fitting import process_curve_data, curve_fit
from qiskit_experiments.analysis.data_processing import level2_probability
from qiskit_experiments.analysis import plotting
from qiskit_experiments import AnalysisResult


class T1Analysis(BaseAnalysis):
Expand Down Expand Up @@ -127,8 +126,7 @@ def fit_fun(x, a, tau, c):
bounds = {"a": amplitude_bounds, "tau": t1_bounds, "c": offset_bounds}
fit_result = curve_fit(fit_fun, xdata, ydata, init, sigma=sigma, bounds=bounds)

analysis_result = AnalysisResult(
{
result_data = {
"value": fit_result["popt"][1],
"stderr": fit_result["popt_err"][1],
"unit": "s",
Expand All @@ -138,11 +136,10 @@ def fit_fun(x, a, tau, c):
fit_result["popt"], fit_result["popt_err"], fit_result["reduced_chisq"]
),
}
)

analysis_result["fit"]["circuit_unit"] = unit
result_data["fit"]["circuit_unit"] = unit
if unit == "dt":
analysis_result["fit"]["dt"] = conversion_factor
result_data["fit"]["dt"] = conversion_factor

# Generate fit plot
if plot and plotting.HAS_MATPLOTLIB:
Expand All @@ -154,11 +151,11 @@ def fit_fun(x, a, tau, c):
figures = None

res_v1 = AnalysisResultV1(
analysis_result,
"T1",
[Qubit(data[0]["metadata"]["qubit"])],
experiment_data.experiment_id,
quality=analysis_result["quality"],
result_data=result_data,
result_type="T1",
device_components=[Qubit(data[0]["metadata"]["qubit"])],
experiment_id=experiment_data.experiment_id,
quality=result_data["quality"],
verified=True,
)

Expand Down
37 changes: 24 additions & 13 deletions qiskit_experiments/characterization/t2star_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
from qiskit.circuit import QuantumCircuit
from qiskit.utils import apply_prefix
from qiskit.providers.options import Options
from qiskit.providers.experiment import AnalysisResultV1, ResultQuality
from qiskit.providers.experiment.device_component import Qubit
from qiskit_experiments.base_experiment import BaseExperiment
from qiskit_experiments.base_analysis import BaseAnalysis
from qiskit_experiments.analysis.curve_fitting import curve_fit, process_curve_data
from qiskit_experiments.analysis.data_processing import level2_probability
from qiskit_experiments.analysis import plotting
from ..experiment_data import ExperimentData, AnalysisResult
from ..experiment_data import ExperimentData


# pylint: disable = invalid-name
class T2StarAnalysis(BaseAnalysis):
Expand All @@ -45,7 +48,7 @@ def _run_analysis(
plot: bool = True,
ax: Optional["AxesSubplot"] = None,
**kwargs,
) -> Tuple[AnalysisResult, List["matplotlib.figure.Figure"]]:
) -> Tuple[AnalysisResultV1, List["matplotlib.figure.Figure"]]:
r"""Calculate T2Star experiment.

The probability of measuring `+` is assumed to be of the form
Expand Down Expand Up @@ -82,12 +85,13 @@ def _format_plot(ax, unit):
ax.set_ylabel("Probability to measure |0>", fontsize=12)

# implementation of _run_analysis
unit = experiment_data._data[0]["metadata"]["unit"]
conversion_factor = experiment_data._data[0]["metadata"].get("dt_factor", None)
data = experiment_data.data()
unit = data[0]["metadata"]["unit"]
conversion_factor = data[0]["metadata"].get("dt_factor", None)
if conversion_factor is None:
conversion_factor = 1 if unit == "s" else apply_prefix(1, unit)
xdata, ydata, sigma = process_curve_data(
experiment_data._data, lambda datum: level2_probability(datum, "0")
data, lambda datum: level2_probability(datum, "0")
)

si_xdata = xdata * conversion_factor
Expand All @@ -110,8 +114,7 @@ def _format_plot(ax, unit):
figures = None

# Output unit is 'sec', regardless of the unit used in the input
analysis_result = AnalysisResult(
{
result_data = {
"t2star_value": fit_result["popt"][1],
"frequency_value": fit_result["popt"][2],
"stderr": fit_result["popt_err"][1],
Expand All @@ -122,11 +125,19 @@ def _format_plot(ax, unit):
fit_result["popt"], fit_result["popt_err"], fit_result["reduced_chisq"]
),
}
)

analysis_result["fit"]["circuit_unit"] = unit
result_data["fit"]["circuit_unit"] = unit
if unit == "dt":
analysis_result["fit"]["dt"] = conversion_factor
result_data["fit"]["dt"] = conversion_factor

analysis_result = AnalysisResultV1(
result_data=result_data,
result_type="T2Star",
device_components=[Qubit(data[0]["metadata"]["qubit"])],
experiment_id=experiment_data.experiment_id,
quality=result_data["quality"],
)

return analysis_result, figures

def _t2star_default_params(
Expand Down Expand Up @@ -179,9 +190,9 @@ def _fit_quality(fit_out, fit_err, reduced_chisq):
and (fit_err[1] is None or fit_err[1] < 0.1 * fit_out[1])
and (fit_err[2] is None or fit_err[2] < 0.1 * fit_out[2])
):
return "computer_good"
return ResultQuality.GOOD
else:
return "computer_bad"
return ResultQuality.BAD


class T2StarExperiment(BaseExperiment):
Expand Down Expand Up @@ -233,7 +244,7 @@ def circuits(self, backend: Optional[Backend] = None) -> List[QuantumCircuit]:
"""
if self._unit == "dt":
try:
dt_factor = getattr(backend._configuration, "dt")
dt_factor = getattr(backend.configuration(), "dt")
except AttributeError as no_dt:
raise AttributeError("Dt parameter is missing in backend configuration") from no_dt

Expand Down
14 changes: 10 additions & 4 deletions qiskit_experiments/composite/composite_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
"""

from qiskit.exceptions import QiskitError
from qiskit_experiments.base_analysis import BaseAnalysis, AnalysisResultV1
from qiskit.providers.experiment import AnalysisResultV1
from qiskit.providers.experiment.device_component import Qubit

from qiskit_experiments.base_analysis import BaseAnalysis
from .composite_experiment_data import CompositeExperimentData


Expand Down Expand Up @@ -70,11 +73,14 @@ def _run_analysis(self, experiment_data: CompositeExperimentData, **options):
sub_ids.append(expdata.experiment_id)
sub_qubits.append(expdata.experiment.physical_qubits)

analysis_result = AnalysisResult(
{
analysis_result = AnalysisResultV1(
result_data={
"experiment_types": sub_types,
"experiment_ids": sub_ids,
"experiment_qubits": sub_qubits,
}
},
result_type="composite",
device_components=[Qubit(qidx) for qidx in sub_qubits],
experiment_id=experiment_data.experiment_id
)
return analysis_result, None
13 changes: 11 additions & 2 deletions qiskit_experiments/composite/composite_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,17 @@ def num_experiments(self):
"""Return the number of sub experiments"""
return self._num_experiments

def component_experiment(self, index):
"""Return the component Experiment object"""
def component_experiment(self, index=None):
"""Return the component Experiment object.

Args:
index: Experiment index, or ``None`` if all experiments are to be returned.

Returns:
The component experiment(s).
"""
if index is None:
return self._experiments
return self._experiments[index]

def component_analysis(self, index, **analysis_options):
Expand Down
14 changes: 7 additions & 7 deletions qiskit_experiments/composite/composite_experiment_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ def __init__(
self,
experiment,
backend=None,
job_ids=None,
):
"""Initialize experiment data.

Args:
experiment (CompositeExperiment): experiment object that generated the data.
backend (Backend): Optional, Backend the experiment runs on. It can either be a
:class:`~qiskit.providers.Backend` instance or just backend name.
job_ids (list[str]): Optional, IDs of jobs submitted for the experiment.

Raises:
ExperimentError: If an input argument is invalid.
Expand All @@ -43,11 +41,11 @@ def __init__(
super().__init__(
experiment,
backend=backend,
job_ids=job_ids,
)

# Initialize sub experiments
self._components = [expr.__experiment_data__(expr) for expr in experiment._experiments]
self._components = [expr.__experiment_data__(expr)
for expr in experiment.component_experiment()]

def __str__(self):
line = 51 * "-"
Expand All @@ -57,14 +55,16 @@ def __str__(self):
ret += f"\nExperiment: {self.experiment_type}"
ret += f"\nExperiment ID: {self.experiment_id}"
ret += f"\nStatus: {status}"
if status == "ERROR":
ret += "\n "
ret += "\n ".join(self._errors)
ret += f"\nComponent Experiments: {len(self._components)}"
ret += f"\nCircuits: {len(self._data)}"
ret += f"\nAnalysis Results: {n_res}"
ret += "\n" + line
if n_res:
ret += "\nLast Analysis Result"
for key, value in self._analysis_results[-1].items():
ret += f"\n- {key}: {value}"
ret += "\nLast Analysis Result:"
ret += f"\n{str(self._analysis_results.values()[-1])}"
return ret

def component_experiment_data(
Expand Down
Loading