Skip to content

Commit

Permalink
Update experiments
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseclectic committed May 5, 2021
1 parent ac3681a commit 21c1a85
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 40 deletions.
49 changes: 33 additions & 16 deletions qiskit_experiments/characterization/t1_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from qiskit.circuit import QuantumCircuit
from qiskit.utils import apply_prefix
from qiskit.providers.options import Options

from qiskit_experiments.base_experiment import BaseExperiment
from qiskit_experiments.base_analysis import BaseAnalysis
Expand All @@ -27,9 +28,29 @@


class T1Analysis(BaseAnalysis):
"""T1 Experiment result analysis class."""
"""T1 Experiment result analysis class.
Analysis Options:
t1_guess (float): Optional, an initial guess of T1
amplitude_guess (float): Optional, an initial guess of the coefficient of the exponent
offset_guess (float): Optional, an initial guess of the offset
t1_bounds (list of two floats): Optional, lower bound and upper bound to T1
amplitude_bounds (list of two floats): Optional, lower bound and upper bound to the amplitude
offset_bounds (list of two floats): Optional, lower bound and upper bound to the offset
"""

@classmethod
def _default_options(cls):
return Options(
t1_guess=None,
amplitude_guess=None,
offset_guess=None,
t1_bounds=None,
amplitude_bounds=None,
offset_bounds=None,
)

# pylint: disable=arguments-differ, unused-argument
# pylint: disable=arguments-differ
def _run_analysis(
self,
experiment_data,
Expand All @@ -39,7 +60,6 @@ def _run_analysis(
t1_bounds=None,
amplitude_bounds=None,
offset_bounds=None,
**kwargs,
) -> Tuple[AnalysisResult, None]:
"""
Calculate T1
Expand All @@ -52,12 +72,10 @@ def _run_analysis(
t1_bounds (list of two floats): Optional, lower bound and upper bound to T1
amplitude_bounds (list of two floats): Optional, lower bound and upper bound to the amplitude
offset_bounds (list of two floats): Optional, lower bound and upper bound to the offset
kwargs: Trailing unused function parameters
Returns:
The analysis result with the estimated T1
"""

unit = experiment_data._data[0]["metadata"]["unit"]
conversion_factor = experiment_data._data[0]["metadata"].get("dt_factor", None)
if conversion_factor is None:
Expand Down Expand Up @@ -135,6 +153,10 @@ class T1Experiment(BaseExperiment):

__analysis_class__ = T1Analysis

@classmethod
def _default_options(cls) -> Options:
return Options(delays=None, unit="s")

def __init__(
self,
qubit: int,
Expand All @@ -154,12 +176,8 @@ def __init__(
"""
if len(delays) < 3:
raise ValueError("T1 experiment: number of delays must be at least 3")
super().__init__([qubit], delays=delays, unit=unit)

self._delays = delays
self._unit = unit
super().__init__([qubit])

# pylint: disable=arguments-differ
def circuits(self, backend: Optional["Backend"] = None) -> List[QuantumCircuit]:
"""
Return a list of experiment circuits
Expand All @@ -173,31 +191,30 @@ def circuits(self, backend: Optional["Backend"] = None) -> List[QuantumCircuit]:
Raises:
AttributeError: if unit is dt but dt parameter is missing in the backend configuration
"""

if self._unit == "dt":
if self.options.unit == "dt":
try:
dt_factor = getattr(backend.configuration(), "dt")
except AttributeError as no_dt:
raise AttributeError("Dt parameter is missing in backend configuration") from no_dt

circuits = []

for delay in self._delays:
for delay in self.options.delays:
circ = QuantumCircuit(1, 1)
circ.x(0)
circ.barrier(0)
circ.delay(delay, 0, self._unit)
circ.delay(delay, 0, self.options.unit)
circ.barrier(0)
circ.measure(0, 0)

circ.metadata = {
"experiment_type": self._type,
"qubit": self.physical_qubits[0],
"xval": delay,
"unit": self._unit,
"unit": self.options.unit,
}

if self._unit == "dt":
if self.options.unit == "dt":
circ.metadata["dt_factor"] = dt_factor

circuits.append(circ)
Expand Down
4 changes: 2 additions & 2 deletions qiskit_experiments/composite/batch_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __init__(self, experiments):
qubits = tuple(self._qubit_map.keys())
super().__init__(experiments, qubits)

def circuits(self, backend=None, **circuit_options):
def circuits(self, backend=None):

batch_circuits = []

Expand All @@ -51,7 +51,7 @@ def circuits(self, backend=None, **circuit_options):
qubit_mapping = None
else:
qubit_mapping = [self._qubit_map[qubit] for qubit in expr.physical_qubits]
for circuit in expr.circuits(**circuit_options):
for circuit in expr.circuits(backend):
# Update metadata
circuit.metadata = {
"experiment_type": self._type,
Expand Down
2 changes: 1 addition & 1 deletion qiskit_experiments/composite/composite_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def _run_analysis(self, experiment_data, **options):
for expr, expr_data in zip(
experiment_data._experiment._experiments, experiment_data._composite_expdata
):
expr.analysis().run(expr_data, **options)
expr.analysis(**expr.analysis_options.__dict__).run(expr_data, **options)

# Add sub-experiment metadata as result of batch experiment
# Note: if Analysis results had ID's these should be included here
Expand Down
13 changes: 5 additions & 8 deletions qiskit_experiments/composite/composite_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,21 @@ class CompositeExperiment(BaseExperiment):
__analysis_class__ = CompositeAnalysis
__experiment_data__ = CompositeExperimentData

def __init__(self, experiments, qubits, experiment_type=None, circuit_options=None):
def __init__(self, experiments, qubits, experiment_type=None):
"""Initialize the composite experiment object.
Args:
experiments (List[BaseExperiment]): a list of experiment objects.
qubits (int or Iterable[int]): the number of qubits or list of
physical qubits for the experiment.
experiment_type (str): Optional, composite experiment subclass name.
circuit_options (str): Optional, Optional, dictionary of allowed
kwargs and default values for the `circuit`
method.
"""
self._experiments = experiments
self._num_experiments = len(experiments)
super().__init__(qubits, experiment_type=experiment_type, circuit_options=circuit_options)
super().__init__(qubits, experiment_type=experiment_type)

@abstractmethod
def circuits(self, backend=None, **circuit_options):
def circuits(self, backend=None):
pass

@property
Expand All @@ -55,6 +52,6 @@ def component_experiment(self, index):
"""Return the component Experiment object"""
return self._experiments[index]

def component_analysis(self, index, **kwargs):
def component_analysis(self, index, **analysis_options):
"""Return the component experiment Analysis object"""
return self.component_experiment(index).analysis(**kwargs)
return self.component_experiment(index).analysis(**analysis_options)
4 changes: 2 additions & 2 deletions qiskit_experiments/composite/parallel_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self, experiments):
qubits += exp.physical_qubits
super().__init__(experiments, qubits)

def circuits(self, backend=None, **circuit_options):
def circuits(self, backend=None):

sub_circuits = []
sub_qubits = []
Expand All @@ -42,7 +42,7 @@ def circuits(self, backend=None, **circuit_options):
# Generate data for combination
for expr in self._experiments:
# Add subcircuits
circs = expr.circuits(**circuit_options)
circs = expr.circuits(backend)
sub_circuits.append(circs)
sub_size.append(len(circs))

Expand Down
36 changes: 26 additions & 10 deletions qiskit_experiments/randomized_benchmarking/rb_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,23 @@


class RBAnalysis(BaseAnalysis):
"""RB Analysis class."""
"""RB Analysis class.
# pylint: disable = arguments-differ, invalid-name, attribute-defined-outside-init
Analysis Options:
p0: Optional, initial parameter values for curve_fit.
plot: If True generate a plot of fitted data.
ax: Optional, matplotlib axis to add plot to.
"""

@classmethod
def _default_options(cls):
return Options(
p0=None,
plot=True,
ax=None,
)

# pylint: disable = arguments-differ, invalid-name
def _run_analysis(
self,
experiment_data,
Expand All @@ -56,21 +70,21 @@ def _run_analysis(
AnalysisResult objects, and ``figures`` may be
None, a single figure, or a list of figures.
"""
self._num_qubits = len(experiment_data.data[0]["metadata"]["qubits"])
num_qubits = len(experiment_data.data[0]["metadata"]["qubits"])
xdata, ydata, ydata_sigma = self._extract_data(experiment_data)

def fit_fun(x, a, alpha, b):
return a * alpha ** x + b

p0 = self._p0(xdata, ydata)
p0 = self._p0(xdata, ydata, num_qubits)
analysis_result = curve_fit(
fit_fun, xdata, ydata, p0, ydata_sigma, bounds=([0, 0, 0], [1, 1, 1])
)

# Add EPC data
popt = analysis_result["popt"]
popt_err = analysis_result["popt_err"]
scale = (2 ** self._num_qubits - 1) / (2 ** self._num_qubits)
scale = (2 ** num_qubits - 1) / (2 ** num_qubits)
analysis_result["EPC"] = scale * (1 - popt[1])
analysis_result["EPC_err"] = scale * popt_err[1] / popt[1]
analysis_result["plabels"] = ["A", "alpha", "B"]
Expand All @@ -83,9 +97,10 @@ def fit_fun(x, a, alpha, b):
analysis_result.plt = plt
return analysis_result, None

def _p0(self, xdata, ydata):
@staticmethod
def _p0(xdata, ydata, num_qubits):
"""Initial guess for the fitting function"""
fit_guess = [0.95, 0.99, 1 / 2 ** self._num_qubits]
fit_guess = [0.95, 0.99, 1 / 2 ** num_qubits]
# Use the first two points to guess the decay param
dcliff = xdata[1] - xdata[0]
dy = (ydata[1] - fit_guess[2]) / (ydata[0] - fit_guess[2])
Expand All @@ -98,7 +113,8 @@ def _p0(self, xdata, ydata):

return fit_guess

def _extract_data(self, experiment_data, **filters):
@staticmethod
def _extract_data(experiment_data, **filters):
"""Extract the base data for the fitter from the experiment data.
Args:
data: the experiment data to analyze
Expand All @@ -122,8 +138,8 @@ def _extract_data(self, experiment_data, **filters):
xdata, ydata, ydata_sigma = mean_xy_data(xdata, ydata, ydata_sigma)
return (xdata, ydata, ydata_sigma)

@classmethod
def _format_plot(cls, ax, analysis_result, add_label=True):
@staticmethod
def _format_plot(ax, analysis_result, add_label=True):
"""Format curve fit plot"""
# Formatting
ax.tick_params(labelsize=14)
Expand Down
2 changes: 1 addition & 1 deletion test/data_processing/test_data_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def __init__(self):
self._type = None
super().__init__((0,), "fake_test_experiment")

def circuits(self, backend=None, **circuit_options):
def circuits(self, backend=None):
"""Fake circuits."""
return []

Expand Down

0 comments on commit 21c1a85

Please sign in to comment.