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
2 changes: 1 addition & 1 deletion qiskit_experiments/framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@
from qiskit_experiments.database_service.db_analysis_result import DbAnalysisResultV1
from qiskit_experiments.database_service.db_fitval import FitVal
from .base_analysis import BaseAnalysis
from .base_experiment import BaseExperiment, ExperimentConfig, fix_class_docs
from .base_experiment import BaseExperiment, ExperimentConfig
from .analysis_result_data import AnalysisResultData
from .experiment_data import ExperimentData
from .composite import (
Expand Down
55 changes: 2 additions & 53 deletions qiskit_experiments/framework/base_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@

from abc import ABC, abstractmethod
import copy
import inspect
import dataclasses
from functools import wraps
from collections import OrderedDict
from typing import Sequence, Optional, Tuple, List, Dict, Union, Any

Expand All @@ -28,6 +26,7 @@
from qiskit.exceptions import QiskitError
from qiskit.qobj.utils import MeasLevel
from qiskit.providers.options import Options
from qiskit_experiments.framework.settings import Settings
from qiskit_experiments.framework.experiment_data import ExperimentData
from qiskit_experiments.version import __version__

Expand Down Expand Up @@ -86,7 +85,7 @@ def experiment(self) -> "BaseExperiment":
raise QiskitError("{}\nError Message:\n{}".format(msg, str(ex))) from ex


class BaseExperiment(ABC):
class BaseExperiment(ABC, Settings):
"""Abstract base class for experiments.

Class Attributes:
Expand Down Expand Up @@ -143,39 +142,6 @@ def __init__(
if isinstance(backend, (Backend, BaseBackend)):
self._set_backend(backend)

def __new__(cls, *args, **kwargs):
"""Store init args and kwargs for subclass __init__ methods"""
# This method automatically stores all arg and kwargs from subclass
# init methods for use in converting an experiment to config

# Get all non-self init args and kwarg names for subclass
spec = inspect.getfullargspec(cls.__init__)
init_arg_names = spec.args[1:]
num_init_kwargs = len(spec.defaults) if spec.defaults else 0
num_init_args = len(init_arg_names) - num_init_kwargs

# Convert passed values for args and kwargs into an ordered dict
# This will sort args passed as kwargs and kwargs passed as
# positional args in the function call
num_call_args = len(args)
ord_args = OrderedDict()
ord_kwargs = OrderedDict()
for i, argname in enumerate(init_arg_names):
if i < num_init_args:
update = ord_args
else:
update = ord_kwargs
if i < num_call_args:
update[argname] = args[i]
elif argname in kwargs:
update[argname] = kwargs[argname]

# pylint: disable = attribute-defined-outside-init
instance = super(BaseExperiment, cls).__new__(cls)
instance.__init_args__ = ord_args
instance.__init_kwargs__ = ord_kwargs
return instance

@property
def experiment_type(self) -> str:
"""Return experiment type."""
Expand Down Expand Up @@ -546,20 +512,3 @@ def _add_job_metadata(self, metadata: Dict[str, Any], jobs: BaseJob, **run_optio
"run_options": copy.copy(run_options),
}
]


def fix_class_docs(wrapped_cls):
"""Experiment class decorator to fix class doc formatting.

This fixes the BaseExperiment subclass documentation so that
the correct init arg and kwargs are shown for the class documentation,
rather than the generic args of the BaseExperiment.__new__ method.
"""

@wraps(wrapped_cls.__init__, assigned=("__annotations__",))
def __new__(cls, *args, **kwargs):
return super(wrapped_cls, cls).__new__(cls, *args, **kwargs)

wrapped_cls.__new__ = __new__

return wrapped_cls
2 changes: 0 additions & 2 deletions qiskit_experiments/framework/composite/batch_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@

from qiskit import QuantumCircuit
from qiskit.providers.backend import Backend
from qiskit_experiments.framework.base_experiment import fix_class_docs
from .composite_experiment import CompositeExperiment, BaseExperiment


@fix_class_docs
class BatchExperiment(CompositeExperiment):
"""Combine multiple experiments into a batch experiment.

Expand Down
2 changes: 0 additions & 2 deletions qiskit_experiments/framework/composite/parallel_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@

from qiskit import QuantumCircuit, ClassicalRegister
from qiskit.providers.backend import Backend
from qiskit_experiments.framework.base_experiment import fix_class_docs
from .composite_experiment import CompositeExperiment, BaseExperiment


@fix_class_docs
class ParallelExperiment(CompositeExperiment):
"""Combine multiple experiments into a parallel experiment.

Expand Down
114 changes: 114 additions & 0 deletions qiskit_experiments/framework/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# 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.
"""
Settings mixing class
"""

import inspect
from collections import OrderedDict
from functools import wraps
from typing import Dict, Any


class Settings:
"""Class mixing for storing instance init settings.

This mixin adds a ``__new__`` method that stores the values of args
and kwargs passed to the class instances ``__init__`` method and a
``settings`` property that returns an ordered dict of these values.

.. note::

This mixin is intended as a mixing for base classes so that when
creating subclasses, those subclasses can inherit the logic for
saving and returning settings.

Note that there is small performance overhead to initializing classes
with this mixin so it should not be used for adding settings to all
classes without consideration. For classes that already store values
required to recover the ``__init__`` args they should instead
implement an appropriate :meth:`settings` property directly.
"""

def __new__(cls, *args, **kwargs):
# This method automatically stores all arg and kwargs from subclass
# init methods
spec = inspect.getfullargspec(cls.__init__)
if spec.varargs:
# raise exception if class init accepts variadic positional args
raise TypeError(
"Settings mixin cannot be used with an init method that "
" accepts variadic positional args "
)

# Get lists of named args and kwargs for classes init method
init_args = spec.args[1:]
defaults_kwargs = spec.defaults or []
num_named_kwargs = len(defaults_kwargs)
num_named_args = len(init_args) - num_named_kwargs
named_args = init_args[0:num_named_args]
named_kwargs = init_args[num_named_args:]

# Initialize ordered dicts for named args and kwargs using the
# argspec ordering
ord_args = OrderedDict(zip(named_args, [None] * num_named_args))
ord_kwargs = OrderedDict(zip(named_kwargs, defaults_kwargs))

# Sort called positional args
for i, (argname, argval) in enumerate(zip(init_args, args)):
if i < num_named_args:
ord_args[argname] = argval
else:
ord_kwargs[argname] = argval

# Sort called kwargs
for argname, argval in kwargs.items():
if argname in named_args:
ord_args[argname] = argval
else:
ord_kwargs[argname] = argval

# pylint: disable = attribute-defined-outside-init
instance = super().__new__(cls)
instance.__init_args__ = ord_args
instance.__init_kwargs__ = ord_kwargs
return instance

def __init_subclass__(cls, **kwargs):
# This method fixes class documentations for subclass
# that inherit the base class new method
super().__init_subclass__(**kwargs)

# Copy the doc string and annotation from the subclasses
# init method to its new method to override base class
# __new__ documentation
@wraps(cls.__init__, assigned=("__annotations__",))
def __new__(sub_cls, *args, **kwargs):
return super(cls, sub_cls).__new__(sub_cls, *args, **kwargs)

# Monkey patch the subclass new method with the method with
# fixed documentation annotations
cls.__new__ = __new__

@property
def settings(self) -> Dict[str, Any]:
"""Return the settings used to initialize this instance."""
settings = {}
# Note that this relies on dicts entries being implicitly ordered
# to store init args as kwargs.
args = getattr(self, "__init_args__", {})
for key, val in args.items():
settings[key] = val
kwargs = getattr(self, "__init_kwargs__", {})
for key, val in kwargs.items():
settings[key] = val
return settings
5 changes: 1 addition & 4 deletions qiskit_experiments/library/calibration/fine_amplitude.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@
BackendCalibrations,
)
from qiskit_experiments.library.characterization import FineAmplitude
from qiskit_experiments.framework import ExperimentData, Options, fix_class_docs
from qiskit_experiments.framework import ExperimentData, Options
from qiskit_experiments.calibration_management.update_library import BaseUpdater
from qiskit_experiments.library.characterization.analysis import FineXAmplitudeAnalysis


@fix_class_docs
class FineAmplitudeCal(BaseCalibrationExperiment, FineAmplitude):
r"""A calibration version of the :class:`FineAmplitude` experiment.

Expand Down Expand Up @@ -148,7 +147,6 @@ def update_calibrations(self, experiment_data: ExperimentData):
)


@fix_class_docs
class FineXAmplitudeCal(FineAmplitudeCal):
"""A calibration experiment to calibrate the amplitude of the X schedule."""

Expand Down Expand Up @@ -196,7 +194,6 @@ def _default_analysis_options(cls) -> Options:
return options


@fix_class_docs
class FineSXAmplitudeCal(FineAmplitudeCal):
"""A calibration experiment to calibrate the amplitude of the SX schedule."""

Expand Down
5 changes: 1 addition & 4 deletions qiskit_experiments/library/calibration/fine_drag_cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from qiskit.pulse import Play

from qiskit_experiments.exceptions import CalibrationError
from qiskit_experiments.framework import ExperimentData, fix_class_docs, Options
from qiskit_experiments.framework import ExperimentData, Options
from qiskit_experiments.calibration_management import (
BaseCalibrationExperiment,
BackendCalibrations,
Expand All @@ -29,7 +29,6 @@
from qiskit_experiments.library.characterization.fine_drag import FineDrag


@fix_class_docs
class FineDragCal(BaseCalibrationExperiment, FineDrag):
"""A calibration version of the fine drag experiment."""

Expand Down Expand Up @@ -150,7 +149,6 @@ def update_calibrations(self, experiment_data: ExperimentData):
)


@fix_class_docs
class FineXDragCal(FineDragCal):
"""Fine drag calibration of X gate."""

Expand Down Expand Up @@ -182,7 +180,6 @@ def __init__(
)


@fix_class_docs
class FineSXDragCal(FineDragCal):
"""Fine drag calibration of X gate."""

Expand Down
3 changes: 1 addition & 2 deletions qiskit_experiments/library/calibration/frequency_cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from qiskit import QuantumCircuit
from qiskit.providers.backend import Backend

from qiskit_experiments.framework import ExperimentData, fix_class_docs
from qiskit_experiments.framework import ExperimentData
from qiskit_experiments.library.characterization.ramsey_xy import RamseyXY
from qiskit_experiments.calibration_management.backend_calibrations import BackendCalibrations
from qiskit_experiments.calibration_management.update_library import BaseUpdater
Expand All @@ -26,7 +26,6 @@
)


@fix_class_docs
class FrequencyCal(BaseCalibrationExperiment, RamseyXY):
"""A qubit frequency calibration experiment based on the Ramsey XY experiment.

Expand Down
3 changes: 1 addition & 2 deletions qiskit_experiments/library/calibration/half_angle_cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from qiskit import QuantumCircuit
from qiskit.providers.backend import Backend

from qiskit_experiments.framework import ExperimentData, fix_class_docs
from qiskit_experiments.framework import ExperimentData
from qiskit_experiments.calibration_management import (
BaseCalibrationExperiment,
BackendCalibrations,
Expand All @@ -27,7 +27,6 @@
from qiskit_experiments.calibration_management.update_library import BaseUpdater


@fix_class_docs
class HalfAngleCal(BaseCalibrationExperiment, HalfAngle):
"""Calibration version of the half-angle experiment."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from qiskit.circuit import Parameter
from qiskit.providers.backend import Backend

from qiskit_experiments.framework import ExperimentData, Options, fix_class_docs
from qiskit_experiments.framework import ExperimentData, Options
from qiskit_experiments.calibration_management import BaseCalibrationExperiment, BackendCalibrations
from qiskit_experiments.library.characterization import Rabi
from qiskit_experiments.calibration_management.update_library import BaseUpdater
Expand All @@ -31,7 +31,6 @@
)


@fix_class_docs
class RoughAmplitudeCal(BaseCalibrationExperiment, Rabi):
"""A calibration version of the Rabi experiment.

Expand Down Expand Up @@ -198,7 +197,6 @@ def update_calibrations(self, experiment_data: ExperimentData):
)


@fix_class_docs
class RoughXSXAmplitudeCal(RoughAmplitudeCal):
"""A rough amplitude calibration of x and sx gates."""

Expand Down Expand Up @@ -228,7 +226,6 @@ def __init__(
]


@fix_class_docs
class EFRoughXSXAmplitudeCal(RoughAmplitudeCal):
"""A rough amplitude calibration of x and sx gates on the 1<->2 transition."""

Expand Down
3 changes: 1 addition & 2 deletions qiskit_experiments/library/calibration/rough_drag_cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from qiskit.circuit import Parameter
from qiskit.providers.backend import Backend

from qiskit_experiments.framework import ExperimentData, fix_class_docs
from qiskit_experiments.framework import ExperimentData
from qiskit_experiments.calibration_management import (
BaseCalibrationExperiment,
BackendCalibrations,
Expand All @@ -27,7 +27,6 @@
from qiskit_experiments.library.characterization.drag import RoughDrag


@fix_class_docs
class RoughDragCal(BaseCalibrationExperiment, RoughDrag):
"""A calibration version of the Drag experiment.

Expand Down
Loading