supermarq.qcvv.base_experiment ============================== .. py:module:: supermarq.qcvv.base_experiment .. autoapi-nested-parse:: Base experiment class and tools used across all experiments. Attributes ---------- .. autoapisummary:: supermarq.qcvv.base_experiment.ResultsT Classes ------- .. autoapisummary:: supermarq.qcvv.base_experiment.BenchmarkingExperiment supermarq.qcvv.base_experiment.BenchmarkingResults supermarq.qcvv.base_experiment.Sample Module Contents --------------- .. py:class:: BenchmarkingExperiment(num_qubits: int, *, random_seed: int | numpy.random.Generator | None = None, **kwargs: Any) Bases: :py:obj:`abc.ABC`, :py:obj:`Generic`\ [\ :py:obj:`ResultsT`\ ] Base class for gate benchmarking experiments. The interface for implementing these experiments is as follows: #. First instantiate the desired experiment object .. code:: experiment = ExampleExperiment(<>) #. Prepare the circuits and run the experiment on the desired target. This can either be a custom simulator or a real device name. For example: .. code:: noise_model = cirq.depolarize(p=0.01, n_qubits=1) sim = cirq.DensityMatrixSimulator(noise=noise_model) experiment.prepare_experiment(<>) experiment.run_with_simulator(simulator=sim, <>) #. Then we analyse the results. If the target was a local simulator this will be available as soon as the :code:`run_with_simulator()` method has finished executing. On the other hand if a real device was accessed via Superstaq then it may take time for the data to be available from the server. The :code:`collect_data()` will return :code:`True` when all data has been collected and is ready to be analysed. .. code:: if experiment.collect_data(): results = experiment.analyze_results(<>) #. The final results of the experiment will be stored in the :code:`results` attribute as a :class:`BenchmarkingResults` of values, while all the data from the experiment will be stored in the :code:`raw_data` attribute as a :class:`~pandas.DataFrame`. Some experiments may include additional data attributes for data generated during the analysis. .. code:: results = experiment.results data = experiment.raw_data Additionally it is possible to pre-compile the experimental circuits for a given device using .. code:: experiment.prepare_experiment(<>) experiment.compile_circuits(target=<>) And then to run the experiment using a custom callable function for evaluating the circuits. For example this could be a function that uses a connection to a local device. .. code:: experiment.run_with_callable(<>) When implementing a new experiment, 4 methods need to be implemented: #. :meth:`_build_circuits`: Given a number of circuits and an iterable of the different numbers of layers to use, return a list of :class:`Sample` objects that need to be sampled during the experiment. #. :meth:`_process_probabilities`: Take the probability distribution over the computational basis resulting from running each circuit and combine the relevant details into a :class:`pandas.DataFrame`. #. :meth:`analyze_results`: Analyse the data in the :attr:`raw_data` dataframe and return a :class:`BenchmarkingResults` object containing the results of the experiment. #. :meth:`plot_results`: Produce any relevant plots that are useful for understanding the results of the experiment. Additionally the :class:`BenchmarkingResults` dataclass needs subclassing to hold the specific results of the new experiment. .. py:method:: analyze_results(plot_results: bool = True) -> ResultsT :abstractmethod: Perform the experiment analysis and store the results in the `results` attribute. :param plot_results: Whether to generate plots of the results. Defaults to False. :returns: A named tuple of the final results from the experiment. .. py:method:: collect_data(force: bool = False) -> bool Collect the data from the samples and process it into the :attr:`raw_data` attribute. If the data is successfully stored in the :attr:`raw_data` attribute then the function will return :code:`True`. If either not all jobs submitted to the server have completed, or not all samples have probability results then no data will be saved in :attr:`raw_data` and the function will return :code:`False`. This check can be overridden with :code:`force=True` in which case only the samples which have probability results will be used to generate the results dataframe. :param force: Whether to override the check that all data is present. Defaults to False. :raises RuntimeError: If :code:`force=True` but there are no samples with any data. :raises RuntimeError: If the experiment has not yet been run. :returns: Whether the results dataframe has been successfully created. .. py:method:: compile_circuits(target: str, **kwargs: Any) -> None Compiles the experiment circuits for the given device. Useful if the samples are not going to be run via Superstaq. :param target: The device to compile to. :param kwargs: Additional desired compile options. .. py:method:: plot_results() -> None :abstractmethod: Plot the results of the experiment .. py:method:: prepare_experiment(num_circuits: int, cycle_depths: collections.abc.Iterable[int], overwrite: bool = False) -> None Prepares the circuits needed for the experiment :param num_circuits: Number of circuits to run. :param cycle_depths: An iterable of the different layer depths to use during the experiment. :param overwrite: Whether to force an experiment run even if there is existing data that would be over written in the process. Defaults to False. :raises RuntimeError: If the experiment has already been run once and the `overwrite` argument is not True :raises ValueError: If any of the cycle depths provided negative or zero. .. py:method:: run_on_device(target: str, repetitions: int = 10000, method: str | None = None, overwrite: bool = False, **target_options: Any) -> cirq_superstaq.Job Submit the circuit samples to the desired target device and store the resulting probabilities. The set of circuits is partitioned as necessary to not exceed the maximum circuits that can be submitted to the given target device. The function then waits for the jobs to complete before saving the resulting probability distributions. :param target: The name of a Superstaq target. :param repetitions: The number of shots to sample. Defaults to 10,000. :param method: Optional method to use on the Superstaq device. Defaults to None corresponding to normal running. :param target_options: Optional configuration dictionary passed when submitting the job. :param overwrite: Whether to force an experiment run even if there is existing data that would be over written in the process. Defaults to False. :returns: The superstaq job containing all the circuits submitted as part of the experiment. .. py:method:: run_with_callable(circuit_eval_func: collections.abc.Callable[[cirq.Circuit], dict[str, float]], overwrite: bool = False, **kwargs: Any) -> None Evaluates the probabilities for each circuit using a user provided callable function. This function should take a circuit as input and return a dictionary of probabilities for each bitstring (including states with zero probability). :param circuit_eval_func: The custom function to use when evaluating circuit probabilities. :param overwrite: Whether to force an experiment run even if there is existing data that would be over written in the process. Defaults to False. :param kwargs: Additional arguments to pass to the custom function. :raises RuntimeError: If the returned probabilities dictionary keys is missing include an incorrect number of bits. :raises RuntimeError: If the returned probabilities dictionary values do not sum to 1.0. .. py:method:: run_with_simulator(simulator: cirq.Sampler | None = None, repetitions: int = 10000, overwrite: bool = False) -> None Use the local simulator to sample the circuits and store the resulting probabilities. :param simulator: A local :class:`~cirq.Sampler` to use. If None then the default :class:`cirq.Simulator` simulator is used. Defaults to None. :param repetitions: The number of shots to sample. Defaults to 10,000. :param overwrite: Whether to force an experiment run even if there is existing data that would be over written in the process. Defaults to False. .. py:property:: num_qubits :type: int Returns: The number of qubits used in the experiment .. py:attribute:: qubits The qubits used in the experiment. .. py:property:: raw_data :type: pandas.DataFrame The data from the most recently run experiment. :raises RuntimeError: If no results are available. .. py:property:: results :type: ResultsT The results from the most recently run experiment. :raises RuntimeError: If no results are available. .. py:property:: samples :type: collections.abc.Sequence[Sample] The samples generated during the experiment. :raises RuntimeError: If no samples are available. .. py:property:: targets :type: frozenset[str] Returns: A set of the unique target that each sample was submitted to .. py:class:: BenchmarkingResults Bases: :py:obj:`abc.ABC` A dataclass for storing the results of the experiment. Requires subclassing for each new experiment type. .. py:attribute:: experiment_name :type: str The name of the experiment. .. py:attribute:: target :type: str The target device that was used. .. py:attribute:: total_circuits :type: int The total number of circuits used in the experiment. .. py:class:: Sample A sample circuit to use along with any data about the circuit that is needed for analysis .. py:property:: circuit :type: cirq.Circuit Returns: The circuit used for the experiment. Defaults to the compiled circuit if available and if not returns the raw circuit. .. py:attribute:: compiled_circuit :type: cirq.Circuit | None :value: None The compiled circuit. Only used if the circuits are compiled for a specific target. .. py:attribute:: data :type: dict[str, Any] The corresponding data about the circuit .. py:attribute:: job :type: cirq_superstaq.Job | None :value: None The superstaq job corresponding to the sample. Defaults to None if no job is associated with the sample. .. py:attribute:: probabilities :type: dict[str, float] | None :value: None The probabilities of the computational basis states .. py:attribute:: raw_circuit :type: cirq.Circuit The raw (i.e. pre-compiled) sample circuit. .. py:property:: target :type: str Returns: The name of the target that the sample was submitted to. .. py:data:: ResultsT