supermarq.qcvv.base_experiment

Base experiment class and tools used across all experiments.

Attributes

ResultsT

Classes

BenchmarkingExperiment

Base class for gate benchmarking experiments.

BenchmarkingResults

A dataclass for storing the results of the experiment. Requires subclassing for

Sample

A sample circuit to use along with any data about the circuit

Module Contents

class supermarq.qcvv.base_experiment.BenchmarkingExperiment(num_qubits: int, *, random_seed: int | numpy.random.Generator | None = None, **kwargs: Any)

Bases: abc.ABC, Generic[ResultsT]

Base class for gate benchmarking experiments.

The interface for implementing these experiments is as follows:

  1. First instantiate the desired experiment object

    experiment = ExampleExperiment(<<args/kwargs>>)
    
  2. 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:

    noise_model = cirq.depolarize(p=0.01, n_qubits=1)
    sim = cirq.DensityMatrixSimulator(noise=noise_model)
    
    experiment.prepare_experiment(<<args/kwargs>>)
    experiment.run_with_simulator(simulator=sim, <<args/kwargs>>)
    
  3. Then we analyse the results. If the target was a local simulator this will be available as soon as the 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 collect_data() will return True when all data has been collected and is ready to be analysed.

    if experiment.collect_data():
        results = experiment.analyze_results(<<args>>)
    
  4. The final results of the experiment will be stored in the results attribute as a BenchmarkingResults of values, while all the data from the experiment will be stored in the raw_data attribute as a DataFrame. Some experiments may include additional data attributes for data generated during the analysis.

    results = experiment.results
    data = experiment.raw_data
    

Additionally it is possible to pre-compile the experimental circuits for a given device using

experiment.prepare_experiment(<<args/kwargs>>)
experiment.compile_circuits(target=<<target_name>>)

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.

experiment.run_with_callable(<<function_name>>)

When implementing a new experiment, 4 methods need to be implemented:

  1. _build_circuits(): Given a number of circuits and an iterable of the different numbers

    of layers to use, return a list of Sample objects that need to be sampled during the experiment.

  2. _process_probabilities(): Take the probability distribution over the

    computational basis resulting from running each circuit and combine the relevant details into a pandas.DataFrame.

  3. analyze_results(): Analyse the data in the raw_data dataframe and return a

    BenchmarkingResults object containing the results of the experiment.

  4. plot_results(): Produce any relevant plots that are useful for understanding the

    results of the experiment.

Additionally the BenchmarkingResults dataclass needs subclassing to hold the specific results of the new experiment.

abstract analyze_results(plot_results: bool = True) ResultsT

Perform the experiment analysis and store the results in the results attribute.

Parameters:

plot_results – Whether to generate plots of the results. Defaults to False.

Returns:

A named tuple of the final results from the experiment.

collect_data(force: bool = False) bool

Collect the data from the samples and process it into the raw_data attribute.

If the data is successfully stored in the raw_data attribute then the function will return 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 raw_data and the function will return False. This check can be overridden with force=True in which case only the samples which have probability results will be used to generate the results dataframe.

Parameters:

force – Whether to override the check that all data is present. Defaults to False.

Raises:
  • RuntimeError – If force=True but there are no samples with any data.

  • RuntimeError – If the experiment has not yet been run.

Returns:

Whether the results dataframe has been successfully created.

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.

Parameters:
  • target – The device to compile to.

  • kwargs – Additional desired compile options.

abstract plot_results() None

Plot the results of the experiment

prepare_experiment(num_circuits: int, cycle_depths: collections.abc.Iterable[int], overwrite: bool = False) None

Prepares the circuits needed for the experiment

Parameters:
  • num_circuits – Number of circuits to run.

  • cycle_depths – An iterable of the different layer depths to use during the experiment.

  • 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

  • ValueError – If any of the cycle depths provided negative or zero.

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.

Parameters:
  • target – The name of a Superstaq target.

  • repetitions – The number of shots to sample. Defaults to 10,000.

  • method – Optional method to use on the Superstaq device. Defaults to None corresponding to normal running.

  • target_options – Optional configuration dictionary passed when submitting the job.

  • 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.

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).

Parameters:
  • circuit_eval_func – The custom function to use when evaluating circuit probabilities.

  • overwrite – Whether to force an experiment run even if there is existing data that would be over written in the process. Defaults to False.

  • 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.

  • RuntimeError – If the returned probabilities dictionary values do not sum to 1.0.

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.

Parameters:
  • simulator – A local Sampler to use. If None then the default cirq.Simulator simulator is used. Defaults to None.

  • repetitions – The number of shots to sample. Defaults to 10,000.

  • overwrite – Whether to force an experiment run even if there is existing data that would be over written in the process. Defaults to False.

property num_qubits: int

Returns: The number of qubits used in the experiment

qubits

The qubits used in the experiment.

property raw_data: pandas.DataFrame

The data from the most recently run experiment.

Raises:

RuntimeError – If no results are available.

property results: ResultsT

The results from the most recently run experiment.

Raises:

RuntimeError – If no results are available.

property samples: collections.abc.Sequence[Sample]

The samples generated during the experiment.

Raises:

RuntimeError – If no samples are available.

property targets: frozenset[str]

Returns: A set of the unique target that each sample was submitted to

class supermarq.qcvv.base_experiment.BenchmarkingResults

Bases: abc.ABC

A dataclass for storing the results of the experiment. Requires subclassing for each new experiment type.

experiment_name: str

The name of the experiment.

target: str

The target device that was used.

total_circuits: int

The total number of circuits used in the experiment.

class supermarq.qcvv.base_experiment.Sample

A sample circuit to use along with any data about the circuit that is needed for analysis

property circuit: cirq.Circuit

Returns: The circuit used for the experiment. Defaults to the compiled circuit if available and if not returns the raw circuit.

compiled_circuit: cirq.Circuit | None = None

The compiled circuit. Only used if the circuits are compiled for a specific target.

data: dict[str, Any]

The corresponding data about the circuit

job: cirq_superstaq.Job | None = None

The superstaq job corresponding to the sample. Defaults to None if no job is associated with the sample.

probabilities: dict[str, float] | None = None

The probabilities of the computational basis states

raw_circuit: cirq.Circuit

The raw (i.e. pre-compiled) sample circuit.

property target: str

Returns: The name of the target that the sample was submitted to.

supermarq.qcvv.base_experiment.ResultsT