supermarq.qcvv ============== .. py:module:: supermarq.qcvv .. autoapi-nested-parse:: A toolkit of QCVV routines. Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/supermarq/qcvv/base_experiment/index /autoapi/supermarq/qcvv/irb/index /autoapi/supermarq/qcvv/xeb/index Classes ------- .. autoapisummary:: supermarq.qcvv.IRB supermarq.qcvv.IRBResults supermarq.qcvv.QCVVExperiment supermarq.qcvv.QCVVResults supermarq.qcvv.Sample supermarq.qcvv.XEB supermarq.qcvv.XEBResults Package Contents ---------------- .. py:class:: IRB(num_circuits: int, cycle_depths: collections.abc.Iterable[int], interleaved_gate: cirq.Gate | None = cirq.Z, num_qubits: int | None = 1, clifford_op_gateset: cirq.CompilationTargetGateset = cirq.CZTargetGateset(), *, random_seed: int | numpy.random.Generator | None = None) Bases: :py:obj:`supermarq.qcvv.base_experiment.QCVVExperiment`\ [\ :py:obj:`_RBResultsBase`\ ] Interleaved random benchmarking (IRB) experiment. IRB estimates the gate error of specified Clifford gate, :math:`\mathcal{C}^*`. This is achieved by first choosing a random sequence, :math:`\{\mathcal{C_i}\}_m` of :math:`m` Clifford gates and then using this to generate two circuits. The first is generated by appending to this sequence the single gate that corresponds to the inverse of the original sequence. The second circuit it obtained by inserting the interleaving gate, :math:`\mathcal{C}^*` after each gate in the sequence and then again appending the corresponding inverse element of the new circuit. Thus both circuits correspond to the identity operation. We run both circuits on the specified target and calculate the probability of measuring the resulting state in the ground state, :math:`p(0...0)`. This gives the circuit fidelity .. math:: f(m) = 2p(0...0) - 1 We can then fit an exponential decay :math:`\log(f) \sim m` to this circuit fidelity for each circuit, with decay rates :math:`\alpha` and :math:`\tilde{\alpha}` for the circuit without and with interleaving respectively. Finally the gate error of the specified gate, :math:`\mathcal{C}^*` is estimated as .. math:: e_{\mathcal{C}^*} = \frac{1}{2} \left(1 - \frac{\tilde{\alpha}}{\alpha}\right) For more details see: https://arxiv.org/abs/1203.4550 .. py:method:: gates_per_clifford(samples: int = 500) -> dict[str, float] Samples a number of random Clifford operations and calculates the average number of single and two qubit gates used to implement them. Note this depends on the gateset chosen for the experiment. :param samples: Number of samples to use. Defaults to 500. :returns: A dictionary with the average number of one and two qubit gates used. .. py:method:: random_clifford() -> cirq.CliffordGate Returns: A random clifford gate with the correct number of qubits for the current experiment. .. py:method:: random_single_qubit_clifford() -> cirq.SingleQubitCliffordGate Choose a random single qubit clifford gate. :returns: The random clifford gate. .. py:method:: random_two_qubit_clifford() -> cirq.CliffordGate Choose a random two qubit clifford gate. For algorithm details see https://arxiv.org/abs/1402.4848 & https://arxiv.org/abs/1210.7011. :returns: The random clifford gate. .. py:attribute:: clifford_op_gateset The gateset to use when implementing Clifford operations. .. py:class:: IRBResults Bases: :py:obj:`_RBResultsBase` Data structure for the IRB experiment results. .. py:method:: plot_results() -> None Plot the exponential decay of the circuit fidelity with cycle depth. :raises RuntimeError: If no data is stored. .. py:method:: print_results() -> None Prints the key results data. .. py:property:: average_interleaved_gate_error :type: float Returns: Estimate of the interleaving gate error. .. py:property:: average_interleaved_gate_error_std :type: float Returns: Standard deviation of the estimate for the interleaving gate error. .. py:property:: irb_decay_coefficient :type: float Returns: Decay coefficient estimate with the interleaving gate. .. py:property:: irb_decay_coefficient_std :type: float Returns: Standard deviation of the decay coefficient estimate with the interleaving gate. .. py:class:: QCVVExperiment(num_qubits: int, num_circuits: int, cycle_depths: collections.abc.Iterable[int], *, random_seed: int | numpy.random.Generator | None = None, results_cls: type[ResultsT], **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) results = 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:`results.data_ready` attribute will return :code:`True` when all data has been collected and is ready to be analyzed. .. code:: if results.data_ready(): results.analyze(<>) When implementing a new experiment, 4 methods need to be implemented: #. :meth:`experiment._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:`results._analyse_results()`: Analyse the experimental data and store the final results, for example some fidelities. #. :meth:`results.plot_results()`: Produce any relevant plots that are useful for understanding the results of the experiment. #. :meth:`results.print_results()`: Prints the results to the console. .. py:method:: run_on_device(target: str, repetitions: int = 10000, method: str | None = None, **target_options: Any) -> ResultsT 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. :returns: The experiment results object. .. py:method:: run_with_callable(circuit_eval_func: collections.abc.Callable[[cirq.Circuit], dict[str | int, float]], **kwargs: Any) -> ResultsT 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 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. :returns: The experiment results object. .. py:method:: run_with_simulator(simulator: cirq.Sampler | None = None, repetitions: int = 10000) -> ResultsT 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. :returns: The experiment results object. .. py:attribute:: cycle_depths The different cycle depths to test at. .. py:attribute:: num_circuits The number of circuits to build for each cycle depth. .. 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:attribute:: samples Create all the samples needed for the experiment. .. py:class:: QCVVResults Bases: :py:obj:`abc.ABC` A dataclass for storing the data and analyze results of the experiment. Requires subclassing for each new experiment type. .. py:method:: analyze(plot_results: bool = True, print_results: bool = True) -> None Perform the experiment analysis and store the results in the `results` attribute. :param plot_results: Whether to generate plots of the results. Defaults to True. :param print_results: Whether to print the final results. Defaults to True. .. py:method:: plot_results() -> None :abstractmethod: Plot the results of the experiment .. py:method:: print_results() -> None :abstractmethod: Prints the key results data. .. py:attribute:: data :type: pandas.DataFrame | None :value: None The raw data generated. .. py:property:: data_ready :type: bool Whether the experimental data is ready to analyse. :raises RuntimeError: If their is no stored data and no Superstaq job to use to collect the results. .. py:attribute:: experiment :type: QCVVExperiment[QCVVResults] Reference to the underlying experiment that generated these results experiment. .. py:attribute:: job :type: cirq_superstaq.Job | None :value: None The associated Superstaq job (if applicable). .. py:property:: num_circuits :type: int Returns: The number of circuits in the experiment. .. py:property:: num_qubits :type: int Returns: The number of qubits in the experiment. .. py:property:: samples :type: collections.abc.Sequence[Sample] Returns: The number of samples used. .. py:attribute:: target :type: str The target device that was used. .. py:class:: Sample A sample circuit to use along with any data about the circuit that is needed for analysis .. py:attribute:: circuit :type: cirq.Circuit The raw (i.e. pre-compiled) sample circuit. .. py:attribute:: circuit_index :type: int The index of the circuit. There will be D samples with matching circuit index, one for each cycle depth being measured. This index is useful for grouping results during analysis. .. py:attribute:: data :type: dict[str, Any] The corresponding data about the circuit that is needed when analyzing results (e.g. cycle depth). .. py:class:: XEB(num_circuits: int, cycle_depths: collections.abc.Iterable[int], single_qubit_gate_set: list[cirq.Gate] | None = None, two_qubit_gate: cirq.Gate | None = cirq.CZ, *, random_seed: int | numpy.random.Generator | None = None) Bases: :py:obj:`supermarq.qcvv.base_experiment.QCVVExperiment`\ [\ :py:obj:`XEBResults`\ ] Cross-entropy benchmarking (XEB) experiment. The XEB experiment can be used to estimate the combined fidelity of a repeating cycle of gates. In our case, where we restrict ourselves to two qubits, we use cycles made up of two randomly selected single qubit phased XZ gates and a constant two qubit gate. This is illustrated as follows: For each randomly generated circuit, with a given number of cycle, we compare the simulated state probabilities, :math:`p(x)` with those achieved by running the circuit on a given target, :math:`\hat{p}(x)`. The fidelity of a circuit containing :math:`d` cycles, :math:`f_d` can then be estimated as .. math:: \sum_{x \in \{0, 1\}^n} p(x) \hat{p}(x) - \frac{1}{2^n} = f_d \left(\sum_{x \in \{0, 1\}^n} p(x)^2 - \frac{1}{2^n}\right) We can therefore fit a linear model to estimate the value of :math:`f_d`. We the estimate the fidelity of the cycle, :math:`f_{\mathrm{cycle}}` as .. math:: f_d = A(f_{cycle})^d Thus fitting another linear model to :math:`\log(f_d) \sim d` provides us with an estimate of the cycle fidelity. For more details see: https://www.nature.com/articles/s41586-019-1666-5 .. py:attribute:: single_qubit_gate_set :type: list[cirq.Gate] The single qubit gates to randomly sample from .. py:attribute:: two_qubit_gate :type: cirq.Gate | None The two qubit gate to use for interleaving. .. py:class:: XEBResults Bases: :py:obj:`supermarq.qcvv.base_experiment.QCVVResults` Results from an XEB experiment. .. py:method:: plot_results() -> None Plot the experiment data and the corresponding fits. :raises RuntimeError: If there is no data stored. .. py:method:: plot_speckle() -> None Creates the speckle plot of the XEB data. See Fig. S18 of https://arxiv.org/abs/1910.11333 for an explanation of this plot. .. py:method:: print_results() -> None .. py:property:: cycle_fidelity_estimate :type: float Returns: Estimated cycle fidelity. .. py:property:: cycle_fidelity_estimate_std :type: float Returns: Standard deviation for the cycle fidelity estimate.