def _default_options(cls) -> Options: """Default analysis options common to all analyzes.""" options = Options() # figure names can be set for each analysis by calling # experiment_obj.analysis.set_options(figure_names=FIGURE_NAMES) options.figure_names = None return options
def test_parallel_options(self): """ Test parallel experiments overriding sub-experiment run and transpile options. """ # These options will all be overridden exp0 = FakeExperiment([0]) exp0.set_transpile_options(optimization_level=1) exp2 = FakeExperiment([2]) exp2.set_experiment_options(dummyoption="test") exp2.set_run_options(shots=2000) exp2.set_transpile_options(optimization_level=1) exp2.analysis.set_options(dummyoption="test") par_exp = ParallelExperiment([exp0, exp2]) with self.assertWarnsRegex( Warning, "Sub-experiment run and transpile options" " are overridden by composite experiment options.", ): self.assertEqual(par_exp.experiment_options, Options()) self.assertEqual(par_exp.run_options, Options(meas_level=2)) self.assertEqual(par_exp.transpile_options, Options(optimization_level=0)) self.assertEqual(par_exp.analysis.options, Options()) par_exp.run(FakeBackend())
def test_parallel_options(self): """ Test parallel experiments overriding sub-experiment run and transpile options. """ # These options will all be overridden exp0 = FakeExperiment([0]) exp0.set_transpile_options(optimization_level=1) exp2 = FakeExperiment([2]) exp2.set_experiment_options(dummyoption="test") exp2.set_run_options(shots=2000) exp2.set_transpile_options(optimization_level=1) exp2.analysis.set_options(dummyoption="test") par_exp = ParallelExperiment([exp0, exp2]) self.assertEqual(par_exp.experiment_options, par_exp._default_experiment_options()) self.assertEqual(par_exp.run_options, Options(meas_level=2)) self.assertEqual(par_exp.transpile_options, Options(optimization_level=0)) self.assertEqual(par_exp.analysis.options, par_exp.analysis._default_options()) with self.assertWarns(UserWarning): expdata = par_exp.run(FakeBackend()) self.assertExperimentDone(expdata)
def _default_parameters(cls) -> Options: """Parameters of trainable nodes. The parameters defined here should be assigned a `None` to indicate that the node has not been trained. """ return Options()
def _default_options(self): """Default options of the test backend.""" return Options( shots=1024, meas_level=MeasLevel.KERNELED, meas_return="single", )
def _default_analysis_options(cls) -> Options: """Default options for analysis of experiment results.""" # Experiment subclasses can override this method if they need # to set specific analysis options defaults that are different # from the Analysis subclass `_default_options` values. if cls.__analysis_class__: return cls.__analysis_class__._default_options() return Options()
def _default_experiment_options(cls) -> Options: """Default kwarg options for experiment""" # Experiment subclasses should override this method to return # an `Options` object containing all the supported options for # that experiment and their default values. Only options listed # here can be modified later by the different methods for # setting options. return Options()
def _default_analysis_options(cls) -> Options: """Default analysis options.""" options = Options() options.result_parameters = [ParameterRepr("freq", "rabi_rate")] options.xlabel = "Amplitude" options.ylabel = "Signal (arb. units)" options.normalization = True return options
def test_enable_restless(self): """Test the enable_restless method.""" error = -np.pi * 0.01 backend = MockRestlessFineAmp(error, np.pi, "x") amp_exp = FineXAmplitude(0, backend) amp_exp.enable_restless(rep_delay=2e-6) self.assertTrue( amp_exp.run_options, Options(meas_level=2, rep_delay=2e-6, init_qubits=False, memory=True, use_measure_esp=False), )
def _default_options(cls): """Default options of the test backend.""" return Options(shots=1024)
def _default_options(cls) -> Options: return Options()
def _default_experiment_options(cls) -> Options: return Options(dummyoption=None)
def get_processor(experiment_data: ExperimentData, analysis_options: Options) -> DataProcessor: """Get a DataProcessor that produces a continuous signal given the options. Args: experiment_data: The experiment data that holds all the data and metadata needed to determine the data processor to use to process the data for analysis. analysis_options: The analysis options with which to analyze the data. The options that are relevant for the configuration of a data processor are: - normalization (bool): A boolean to specify if the data should be normalized to the interval [0, 1]. The default is True. This option is only relevant if kerneled data is used. - dimensionality_reduction: An optional string or instance of :class:`ProjectorType` to represent the dimensionality reduction node for Kerneled data. For the supported nodes, see :class:`ProjectorType`. Typically, these nodes convert complex IQ data to real data, for example by performing a singular-value decomposition. This argument is only needed for Kerneled data (i.e. level 1) and can thus be ignored if Classified data (the default) is used. - outcome (string): The measurement outcome that will be passed to a Probability node. The default value is a string of 1's where the length of the string is the number of qubits, e.g. '111' for three qubits. Returns: An instance of DataProcessor capable of processing the data for the corresponding job. Notes: The `physical_qubits` argument is extracted from the `experiment_data` metadata and is used to determine the default `outcome` to extract from classified data if it was not given in the analysis options. Raises: DataProcessorError: if the measurement level is not supported. """ metadata = experiment_data.metadata if "job_metadata" in metadata: # Backwards compatibility for old experiment data # remove job metadata and add required fields to new location in metadata job_meta = metadata.pop("job_metadata") run_options = job_meta[-1].get("run_options", {}) for opt in ["meas_level", "meas_return"]: if opt in run_options: metadata[opt] = run_options[opt] warnings.warn( "The analyzed ExperimentData contains deprecated data processor " " job_metadata which has been been updated to current metadat format. " "If this data was loaded from a database servide you should re-save it " "to update the metadata in the database.", DeprecationWarning, ) meas_level = metadata.get("meas_level", MeasLevel.CLASSIFIED) meas_return = metadata.get("meas_return", MeasReturnType.AVERAGE) normalize = analysis_options.get("normalization", True) dimensionality_reduction = analysis_options.get("dimensionality_reduction", ProjectorType.SVD) if meas_level == MeasLevel.CLASSIFIED: num_qubits = len(metadata.get("physical_qubits", [0])) outcome = analysis_options.get("outcome", "1" * num_qubits) return DataProcessor("counts", [nodes.Probability(outcome)]) if meas_level == MeasLevel.KERNELED: return get_kerneled_processor(dimensionality_reduction, meas_return, normalize) raise DataProcessorError(f"Unsupported measurement level {meas_level}.")
def _default_run_options(cls) -> Options: """Default options values for the experiment :meth:`run` method.""" return Options(meas_level=MeasLevel.CLASSIFIED)
def _default_transpile_options(cls) -> Options: """Default transpiler options for transpilation of circuits""" # Experiment subclasses can override this method if they need # to set specific default transpiler options to transpile the # experiment circuits. return Options(optimization_level=0)
def _default_analysis_options(cls) -> Options: """Default analysis options.""" options = Options() options.normalization = True return options
def _default_options(cls): return Options()
def get_processor( experiment_data: ExperimentData, analysis_options: Options, index: int = -1, ) -> DataProcessor: """Get a DataProcessor that produces a continuous signal given the options. Args: experiment_data: The experiment data that holds all the data and metadata needed to determine the data processor to use to process the data for analysis. analysis_options: The analysis options with which to analyze the data. The options that are relevant for the configuration of a data processor are: - normalization (bool): A boolean to specify if the data should be normalized to the interval [0, 1]. The default is True. This option is only relevant if kerneled data is used. - dimensionality_reduction: An optional string or instance of :class:`ProjectorType` to represent the dimensionality reduction node for Kerneled data. For the supported nodes, see :class:`ProjectorType`. Typically, these nodes convert complex IQ data to real data, for example by performing a singular-value decomposition. This argument is only needed for Kerneled data (i.e. level 1) and can thus be ignored if Classified data (the default) is used. - outcome (string): The measurement outcome that will be passed to a Probability node. The default value is a string of 1's where the length of the string is the number of qubits, e.g. '111' for three qubits. index: The index of the job for which to get a data processor. The default value is -1. Returns: An instance of DataProcessor capable of processing the data for the corresponding job. Notes: The `num_qubits` argument is extracted from the `experiment_data` metadata and is used to determine the default `outcome` to extract from classified data if it was not given in the analysis options. Raises: DataProcessorError: if the measurement level is not supported. DataProcessorError: if the wrong dimensionality reduction for kerneled data is specified. """ run_options = experiment_data.metadata["job_metadata"][index].get( "run_options", {}) meas_level = run_options.get("meas_level", MeasLevel.CLASSIFIED) meas_return = run_options.get("meas_return", MeasReturnType.AVERAGE) normalize = analysis_options.get("normalization", True) dimensionality_reduction = analysis_options.get("dimensionality_reduction", ProjectorType.SVD) if meas_level == MeasLevel.CLASSIFIED: num_qubits = experiment_data.metadata.get("num_qubits", 1) outcome = analysis_options.get("outcome", "1" * num_qubits) return DataProcessor("counts", [nodes.Probability(outcome)]) if meas_level == MeasLevel.KERNELED: try: if isinstance(dimensionality_reduction, ProjectorType): projector_name = dimensionality_reduction.name else: projector_name = dimensionality_reduction projector = ProjectorType[projector_name].value except KeyError as error: raise DataProcessorError( f"Invalid dimensionality reduction: {dimensionality_reduction}." ) from error if meas_return == "single": processor = DataProcessor("memory", [nodes.AverageData(axis=1), projector()]) else: processor = DataProcessor("memory", [projector()]) if normalize: processor.append(nodes.MinMaxNormalize()) return processor raise DataProcessorError(f"Unsupported measurement level {meas_level}.")
class RestlessMixin: """A mixin to facilitate restless experiments. This class defines the following methods - :meth:`enable_restless` - :meth:`_get_restless_processor` - :meth:`_t1_check` A restless enabled experiment is an experiment that can be run in a restless measurement setting. In restless measurements, the qubit is not reset after each measurement. Instead, the outcome of the previous quantum non-demolition measurement is the initial state for the current circuit. Restless measurements therefore require special data processing which is provided by sub-classes of the :code:`RestlessNode`. Restless experiments are a fast alternative for several calibration and characterization tasks, for details see https://arxiv.org/pdf/2202.06981.pdf. This class makes it possible for users to enter a restless run-mode without having to manually set all the required run options and the data processor. The required options are ``rep_delay``, ``init_qubits``, ``memory``, and ``meas_level``. Furthermore, subclasses can override the :meth:`_get_restless_processor` method if they require more complex restless data processing such as two-qubit calibrations. In addition, this class makes it easy to determine if restless measurements are supported for a given experiments. """ analysis: BaseAnalysis _default_run_options: Options() set_run_options: Callable _backend: Backend _physical_qubits: Sequence[int] _num_qubits: int def enable_restless(self, rep_delay: Optional[float] = None, override_processor_by_restless: bool = True): """Enables a restless experiment by setting the restless run options and the restless data processor. Args: rep_delay: The repetition delay. This is the delay between a measurement and the subsequent quantum circuit. Since the backends have dynamic repetition rates, the repetition delay can be set to a small value which is required for restless experiments. Typical values are 1 us or less. override_processor_by_restless: If False, a data processor that is specified in the analysis options of the experiment is not overridden by the restless data processor. The default is True. Raises: DataProcessorError: if the attribute rep_delay_range is not defined for the backend. DataProcessorError: if a data processor has already been set but override_processor_by_restless is True. DataProcessorError: if the experiment analysis does not have the data_processor option. DataProcessorError: if the rep_delay is equal to or greater than the T1 time of one of the physical qubits in the experiment. """ try: if not rep_delay: rep_delay = self._backend.configuration().rep_delay_range[0] except AttributeError as error: raise DataProcessorError( "The restless experiment can not be enabled because " "the attribute rep_delay_range is not defined for this backend " "and a minimum rep_delay can not be set.") from error # The excited state promotion readout analysis option is set to # False because it is not compatible with a restless experiment. if self._t1_check(rep_delay): meas_level = self._default_run_options().get( "meas_level", MeasLevel.CLASSIFIED) meas_return = self._default_run_options().get( "meas_return", MeasReturnType.SINGLE) if not self.analysis.options.get("data_processor", None): self.set_run_options( rep_delay=rep_delay, init_qubits=False, memory=True, meas_level=meas_level, meas_return=meas_return, use_measure_esp=False, ) if hasattr(self.analysis.options, "data_processor"): self.analysis.set_options( data_processor=self._get_restless_processor( meas_level=meas_level)) else: raise DataProcessorError( "The restless data processor can not be set since the experiment analysis" "does not have the data_processor option.") else: if not override_processor_by_restless: self.set_run_options( rep_delay=rep_delay, init_qubits=False, memory=True, meas_level=meas_level, meas_return=meas_return, use_measure_esp=False, ) else: raise DataProcessorError( "Cannot enable restless. Data processor has already been set and " "override_processor_by_restless is True.") else: raise DataProcessorError( f"The specified repetition delay {rep_delay} is equal to or greater " f"than the T1 time of one of the physical qubits" f"{self._physical_qubits} in the experiment. Consider choosing " f"a smaller repetition delay for the restless experiment.") def _get_restless_processor(self, meas_level: int = MeasLevel.CLASSIFIED ) -> DataProcessor: """Returns the restless experiments data processor. Notes: Sub-classes can override this method if they need more complex data processing. """ outcome = self.analysis.options.get("outcome", "1" * self._num_qubits) meas_return = self.analysis.options.get("meas_return", MeasReturnType.SINGLE) normalize = self.analysis.options.get("normalization", True) dimensionality_reduction = self.analysis.options.get( "dimensionality_reduction", ProjectorType.SVD) if meas_level == MeasLevel.KERNELED: return get_kerneled_processor(dimensionality_reduction, meas_return, normalize, [nodes.RestlessToIQ()]) else: return DataProcessor( "memory", [ nodes.RestlessToCounts(self._num_qubits), nodes.Probability(outcome), ], ) def _t1_check(self, rep_delay: float) -> bool: """Check that repetition delay < T1 of the physical qubits in the experiment. Args: rep_delay: The repetition delay. This is the delay between a measurement and the subsequent quantum circuit. Returns: True if the repetition delay is smaller than the qubit T1 times. Raises: DataProcessorError: if the T1 values are not defined for the qubits of the used backend. """ try: t1_values = [ self._backend.properties().qubit_property(physical_qubit)["T1"] [0] for physical_qubit in self._physical_qubits ] if all(rep_delay / t1_value < 1.0 for t1_value in t1_values): return True except AttributeError as error: raise DataProcessorError( "The restless experiment can not be enabled since " "T1 values are not defined for the qubits of the used backend." ) from error return False
def _default_options(cls) -> Options: """Return default draw options. Draw Options: axis (Any): Arbitrary object that can be used as a drawing canvas. subplots (Tuple[int, int]): Number of rows and columns when the experimental result is drawn in the multiple windows. xlabel (Union[str, List[str]]): X-axis label string of the output figure. If there are multiple columns in the canvas, this could be a list of labels. ylabel (Union[str, List[str]]): Y-axis label string of the output figure. If there are multiple rows in the canvas, this could be a list of labels. xlim (Tuple[float, float]): Min and max value of the horizontal axis. If not provided, it is automatically scaled based on the input data points. ylim (Tuple[float, float]): Min and max value of the vertical axis. If not provided, it is automatically scaled based on the input data points. xval_unit (str): SI unit of x values. No prefix is needed here. For example, when the x values represent time, this option will be just "s" rather than "ms". In the output figure, the prefix is automatically selected based on the maximum value in this axis. If your x values are in [1e-3, 1e-4], they are displayed as [1 ms, 10 ms]. This option is likely provided by the analysis class rather than end-users. However, users can still override if they need different unit notation. By default, this option is set to ``None``, and no scaling is applied. If nothing is provided, the axis numbers will be displayed in the scientific notation. yval_unit (str): Unit of y values. See ``xval_unit`` for details. figsize (Tuple[int, int]): A tuple of two numbers representing the size of the output figure (width, height). Note that this is applicable only when ``axis`` object is not provided. If any canvas object is provided, the figure size associated with the axis is preferentially applied. legend_loc (str): Vertical and horizontal location of the curve legend window in a single string separated by a space. This defaults to ``center right``. Vertical position can be ``upper``, ``center``, ``lower``. Horizontal position can be ``right``, ``center``, ``left``. tick_label_size (int): Size of text representing the axis tick numbers. axis_label_size (int): Size of text representing the axis label. fit_report_rpos (Tuple[int, int]): A tuple of numbers showing the location of the fit report window. These numbers are horizontal and vertical position of the top left corner of the window in the relative coordinate on the output figure, i.e. ``[0, 1]``. The fit report window shows the selected fit parameters and the reduced chi-squared value. fit_report_text_size (int): Size of text in the fit report window. plot_sigma (List[Tuple[float, float]]): A list of two number tuples showing the configuration to write confidence intervals for the fit curve. The first argument is the relative sigma (n_sigma), and the second argument is the transparency of the interval plot in ``[0, 1]``. Multiple n_sigma intervals can be drawn for the single curve. """ return Options( axis=None, subplots=(1, 1), xlabel=None, ylabel=None, xlim=None, ylim=None, xval_unit=None, yval_unit=None, figsize=(8, 5), legend_loc="center right", tick_label_size=14, axis_label_size=16, fit_report_rpos=(0.6, 0.95), fit_report_text_size=14, plot_sigma=[(1.0, 0.7), (3.0, 0.3)], )