Source code for povm_toolbox.library.classical_shadows

# (C) Copyright IBM 2024.
#
# 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.

"""ClassicalShadows."""

from __future__ import annotations

import numpy as np
from numpy.random import Generator

from .locally_biased_classical_shadows import LocallyBiasedClassicalShadows


[docs] class ClassicalShadows(LocallyBiasedClassicalShadows): """A classical shadows POVM. This is a special case of the :class:`LocallyBiasedClassicalShadows`, where the bias is taken to be uniform. That is, there is an equal probability to perform a measurement in the Z, X and Y bases. The example below shows how you construct a classical shadows POVM. It plots a visual representation of the POVM's definition to exemplify the equal measurement probabilities. .. plot:: :include-source: >>> from povm_toolbox.library import ClassicalShadows >>> povm = ClassicalShadows(2, measurement_twirl=True, shot_repetitions=10) >>> print(povm) ClassicalShadows(num_qubits=2) >>> povm.definition().draw_bloch() <Figure size 1000x500 with 2 Axes> """ def __init__( self, num_qubits: int, *, measurement_layout: list[int] | None = None, # TODO: add | Layout measurement_twirl: bool = False, shot_repetitions: int = 1, insert_barriers: bool = False, seed: int | Generator | None = None, ) -> None: """Initialize a classical shadows POVM. Args: num_qubits: the number of qubits. measurement_layout: optional list of indices specifying on which qubits the POVM acts. See :attr:`.measurement_layout` for more details. measurement_twirl: whether to randomly twirl the measurements. For each single-qubit projective measurement, random twirling is equivalent to randomly flipping the measurement. This is equivalent to randomly taking the opposite Bloch vector in the Bloch sphere representation. shot_repetitions: number of times the measurement is repeated for each sampled PVM. More precisely, a new PVM is sampled for all ``shots`` (i.e. the number of times as specified by the user via the ``shots`` argument of the method :meth:`.POVMSampler.run`). Then, the parameter ``shot_repetitions`` states how many times we repeat the measurement for each sampled PVM (default is 1). Therefore, the effective total number of measurement shots is ``shots`` multiplied by ``shot_repetitions``. insert_barriers: whether to insert a barrier between the composed circuits. This is not done by default but can prove useful when visualizing the composed circuit. seed: optional seed to fix the :class:`numpy.random.Generator` used to sample PVMs. The user can also directly provide a random generator. If ``None``, a random seed will be used. """ bias = 1.0 / 3.0 * np.ones(3) super().__init__( num_qubits=num_qubits, bias=bias, measurement_twirl=measurement_twirl, measurement_layout=measurement_layout, shot_repetitions=shot_repetitions, insert_barriers=insert_barriers, seed=seed, ) def __repr__(self) -> str: """Return the string representation of a ClassicalShadows instance.""" return f"{self.__class__.__name__}(num_qubits={self.num_qubits})"