classical_shadow

ShadowUnveil - Classical Shadow with The Results of Second Order Renyi Entropy (qurry.qurrent.classical_shadow)

References

Note

  • Predicting many properties of a quantum system from very few measurements -

Huang, Hsin-Yuan and Kueng, Richard and Preskill, John [doi:10.1038/s41567-020-0932-7](

  • The randomized measurement toolbox -

Elben, Andreas and Flammia, Steven T. and Huang, Hsin-Yuan and Kueng, Richard and Preskill, John and Vermersch, Benoît and Zoller, Peter [doi:10.1038/s42254-022-00535-2](

@article{cite-key,
    abstract = {
        Predicting the properties of complex,
        large-scale quantum systems is essential for developing quantum technologies.
        We present an efficient method for constructing an approximate classical
        description of a quantum state using very few measurements of the state.
        different properties; order
        {\$}{\$}{\{}{$\backslash$}mathrm{\{}log{\}}{\}}{$\backslash$},(M){\$}{\$}
        measurements suffice to accurately predict M different functions of the state
        with high success probability. The number of measurements is independent of
        the system size and saturates information-theoretic lower bounds. Moreover,
        target properties to predict can be
        selected after the measurements are completed.
        We support our theoretical findings with extensive numerical experiments.
        We apply classical shadows to predict quantum fidelities,
        entanglement entropies, two-point correlation functions,
        expectation values of local observables and the energy variance of
        many-body local Hamiltonians.
        The numerical results highlight the advantages of classical shadows relative to
        previously known methods.},
    author = {Huang, Hsin-Yuan and Kueng, Richard and Preskill, John},
    date = {2020/10/01},
    date-added = {2024-12-03 15:00:55 +0800},
    date-modified = {2024-12-03 15:00:55 +0800},
    doi = {10.1038/s41567-020-0932-7},
    id = {Huang2020},
    isbn = {1745-2481},
    journal = {Nature Physics},
    number = {10},
    pages = {1050--1057},
    title = {Predicting many properties of a quantum system from very few measurements},
    url = {https://doi.org/10.1038/s41567-020-0932-7},
    volume = {16},
    year = {2020},
    bdsk-url-1 = {https://doi.org/10.1038/s41567-020-0932-7}
}

@article{cite-key,
    abstract = {
        Programmable quantum simulators and quantum computers are opening unprecedented
        opportunities for exploring and exploiting the properties of highly entangled
        complex quantum systems. The complexity of large quantum systems is the source
        of computational power but also makes them difficult to control precisely or
        characterize accurately using measured classical data. We review protocols
        for probing the properties of complex many-qubit systems using measurement
        schemes that are practical using today's quantum platforms. In these protocols,
        a quantum state is repeatedly prepared and measured in a randomly chosen basis;
        then a classical computer processes the measurement outcomes to estimate the
        desired property. The randomization of the measurement procedure has distinct
        advantages. For example, a single data set can be used multiple times to pursue
        a variety of applications, and imperfections in the measurements are mapped to
        a simplified noise model that can more easily be mitigated.
        We discuss a range of cases that have already been realized in quantum devices,
        including Hamiltonian simulation tasks, probes of quantum chaos,
        measurements of non-local order parameters,
        and comparison of quantum states produced in distantly separated
        laboratories. By providing a workable method for translating a complex quantum
        state into a succinct classical representation that preserves a rich variety of
        relevant physical properties, the randomized measurement toolbox strengthens our
        ability to grasp and control the quantum world.},
    author = {
        Elben, Andreas and Flammia, Steven T. and Huang, Hsin-Yuan and Kueng,
        Richard and Preskill, John and Vermersch, Beno{\^\i}t and Zoller, Peter},
    date = {2023/01/01},
    date-added = {2024-12-03 15:06:15 +0800},
    date-modified = {2024-12-03 15:06:15 +0800},
    doi = {10.1038/s42254-022-00535-2},
    id = {Elben2023},
    isbn = {2522-5820},
    journal = {Nature Reviews Physics},
    number = {1},
    pages = {9--24},
    title = {The randomized measurement toolbox},
    url = {https://doi.org/10.1038/s42254-022-00535-2},
    volume = {5},
    year = {2023},
    bdsk-url-1 = {https://doi.org/10.1038/s42254-022-00535-2}
}

arguments

ShadowUnveil - Arguments (qurry.qurrent.classical_shadow.arguments)

class qurry.qurrent.classical_shadow.arguments.ShadowUnveilAnalyzeArgs[source]

The input of the analyze method.

backend: Literal['Cython', 'Rust', 'Python'] | str

The backend for the process.

counts_used: Iterable[int] | None

The index of the counts used.

selected_qubits: list[int] | None

The selected qubits.

class qurry.qurrent.classical_shadow.arguments.ShadowUnveilArguments(exp_name: str = 'exps', times: int = 100, qubits_measured: list[int] | None = None, registers_mapping: dict[int, int] | None = None, actual_num_qubits: int = 0, unitary_located: list[int] | None = None, random_unitary_seeds: dict[int, dict[int, int]] | None = None)[source]

Arguments for the experiment.

actual_num_qubits: int = 0

The actual number of qubits.

exp_name: str = 'exps'

The name of the experiment. Naming this experiment to recognize it when the jobs are pending to IBMQ Service. This name is also used for creating a folder to store the exports. Defaults to ‘experiment’.

qubits_measured: list[int] | None = None

The measure range.

random_unitary_seeds: dict[int, dict[int, int]] | None = None

The seeds for all random unitary operator. This argument only takes input as type of dict[int, dict[int, int]]. The first key is the index for the random unitary operator. The second key is the index for the qubit.

If you want to generate the seeds for all random unitary operator, you can use the function generate_random_unitary_seeds in qurry.qurrium.utils.random_unitary.

registers_mapping: dict[int, int] | None = None

The mapping of the classical registers with quantum registers.

The key is the index of the quantum register with the numerical order. The value is the index of the classical register with the numerical order.

times: int = 100

The number of random unitary operator. It will denote as N_U in the experiment name.

unitary_located: list[int] | None = None

The range of the unitary operator.

class qurry.qurrent.classical_shadow.arguments.ShadowUnveilMeasureArgs[source]

Output arguments for output().

measure: tuple[int, int] | int | list[int] | None

The measure range.

random_unitary_seeds: dict[int, dict[int, int]] | None

The seeds for all random unitary operator. This argument only takes input as type of dict[int, dict[int, int]]. The first key is the index for the random unitary operator. The second key is the index for the qubit.

If you want to generate the seeds for all random unitary operator, you can use the function generate_random_unitary_seeds in qurry.qurrium.utils.random_unitary.

times: int

The number of random unitary operator. It will denote as N_U in the experiment name.

unitary_loc: tuple[int, int] | int | list[int] | None

The range of the unitary operator.

unitary_loc_not_cover_measure: bool

Whether the range of the unitary operator is not cover the measure range.

wave: QuantumCircuit | Hashable | None

The key or the circuit to execute.

class qurry.qurrent.classical_shadow.arguments.ShadowUnveilOutputArgs[source]

Output arguments for output().

measure: tuple[int, int] | int | list[int] | None

The measure range.

random_unitary_seeds: dict[int, dict[int, int]] | None

The seeds for all random unitary operator. This argument only takes input as type of dict[int, dict[int, int]]. The first key is the index for the random unitary operator. The second key is the index for the qubit.

If you want to generate the seeds for all random unitary operator, you can use the function generate_random_unitary_seeds in qurry.qurrium.utils.random_unitary.

times: int

The number of random unitary operator. It will denote as N_U in the experiment name.

unitary_loc: tuple[int, int] | int | list[int] | None

The range of the unitary operator.

unitary_loc_not_cover_measure: bool

Whether the range of the unitary operator is not cover the measure range.

analysis

ShadowUnveil - Analysis (qurry.qurrent.classical_shadow.analysis)

class qurry.qurrent.classical_shadow.analysis.SUAnalysisContent(average_classical_snapshots_rho: dict[int, ndarray[tuple[int, int], dtype[complex128]]], classical_registers_actually: list[int], taking_time: float, mean_of_rho: ndarray[tuple[int, int], dtype[complex128]], purity: float, entropy: float, estimate_of_given_operators: list[ndarray[tuple[int, int], dtype[complex128]]], corresponding_rhos: list[ndarray[tuple[int, ...], dtype[complex128]]], accuracy_prob_comp_delta: float, num_of_estimators_k: int, accuracy_predict_epsilon: float, maximum_shadow_norm: float)[source]

The content of the analysis.

accuracy_predict_epsilon: float

The prediction of accuracy, which used the notation \(\epsilon\) and mentioned in Theorem S1 in the supplementary material, the equation (S13) in the supplementary material.

We can calculate the prediction of accuracy \(\epsilon\) from the equation (S13) in the supplementary material, the equation (S13) is as follows, .. math:

N = \frac{34}{\epsilon^2} \max_{1 \leq i \leq M}
|| O_i - \frac{\text{tr}(O_i)}{2^n} ||_{\text{shadow}}^2

where \(\epsilon\) is the prediction of accuracy, and \(M\) is the number of given operatorsm and \(N\) is the number of classical snapshots. The \(|| O_i - \frac{\text{tr}(O_i)}{2^n} ||_{\text{shadow}}^2\) is maximum shadow norm, which is defined in the supplementary material with value between 0 and 1.

accuracy_prob_comp_delta: float

The probabiltiy complement of accuracy, which used the notation \(\delta\) and mentioned in Theorem S1 in the supplementary material, the equation (S13) in the supplementary material. The probabiltiy of accuracy is \(1 - \delta\).

The number of given operators and the accuracy parameters will be used to decide the number of estimators K from the equation (S13) in the supplementary material.

\[K = 2 \log(2M / \delta)\]

where \(\delta\) is the probabiltiy complement of accuracy, and \(M\) is the number of given operators.

But we can see \(K\) will be not the integer value of the result of the equation. So, we will use the ceil value of the result of the equation. And recalculate the probabiltiy complement of accuracy from this new value of \(K\).

average_classical_snapshots_rho: dict[int, ndarray[tuple[int, int], dtype[complex128]]]

The dictionary of Rho M.

classical_registers_actually: list[int]

The list of the selected_classical_registers.

corresponding_rhos: list[ndarray[tuple[int, ...], dtype[complex128]]]

The corresponding rho of measurement primitive \(\mathcal{U}\).

entropy: float

The entropy calculated by classical shadow.

estimate_of_given_operators: list[ndarray[tuple[int, int], dtype[complex128]]]

The result of measurement primitive \(\mathcal{U}\).

maximum_shadow_norm: float

The maximum shadow norm, which is defined in the supplementary material with value between 0 and 1. The maximum shadow norm is used to calculate the prediction of accuracy \(\epsilon\) from the equation (S13) in the supplementary material.

We can calculate the prediction of accuracy \(\epsilon\) from the equation (S13) in the supplementary material, the equation (S13) is as follows, .. math:

N = \frac{34}{\epsilon^2} \max_{1 \leq i \leq M}
|| O_i - \frac{\text{tr}(O_i)}{2^n} ||_{\text{shadow}}^2

where \(\epsilon\) is the prediction of accuracy, and \(M\) is the number of given operatorsm and \(N\) is the number of classical snapshots. The \(|| O_i - \frac{\text{tr}(O_i)}{2^n} ||_{\text{shadow}}^2\) is maximum shadow norm, which is defined in the supplementary material with value between 0 and 1.

Due to maximum shadow norm is complex and it is a norm, we suppose we have the worst case scenario, where the maximum shadow norm is 1 as default. Thus, we can simplify the equation to: .. math:

N = \frac{34}{\epsilon^2}
mean_of_rho: ndarray[tuple[int, int], dtype[complex128]]

The expectation value of Rho.

num_of_estimators_k: int

The number of esitmators, which used the notation K and mentioned in Algorithm 1 in the paper, Theorem S1 in the supplementary material, the equation (S13) in the supplementary material.

We can calculate the number of esitmator K from the equation (S13) in the supplementary material, the equation (S13) is as follows, .. math:

K = 2 \log(2M / \delta)

where \(\delta\) is the probabiltiy complement of accuracy, and \(M\) is the number of given operators.

But we can see \(K\) will be not the integer value of the result of the equation. So, we will use the ceil value of the result of the equation. And recalculate the probabiltiy complement of accuracy from this new value of \(K\).

purity: float

The purity calculated by classical shadow.

taking_time: float

The time taken for the calculation.

class qurry.qurrent.classical_shadow.analysis.SUAnalysisInput(shots: int, num_qubits: int, selected_qubits: list[int], registers_mapping: dict[int, int], bitstring_mapping: dict[int, int] | None, unitary_located: list[int] | None = None)[source]

To set the analysis.

bitstring_mapping: dict[int, int] | None

The mapping of the bitstring with the classical registers. When there are mulitple classical registers, the bitstring is the concatenation of the classical registers with space on bitstring. For example, there are three registers with the size of 4, 4, and 6, which the first six bits are for the randomized measurement.

So, the mapping will be like this.

{
    0: 10, # The classical register 0 is mapped to the bitstring on the index 0.
    1: 11, # The classical register 0 is mapped to the bitstring on the index 1.
    2: 12, # The classical register 0 is mapped to the bitstring on the index 2.
    3: 13, # The classical register 0 is mapped to the bitstring on the index 3.
    4: 14, # The classical register 0 is mapped to the bitstring on the index 4.
    5: 15, # The classical register 0 is mapped to the bitstring on the index 5.
}

But, if there is only one classical register, the bitstring will map to the classical register directly.

Will be like this.

num_qubits: int

The number of qubits.

registers_mapping: dict[int, int]

The mapping of the classical registers with quantum registers.

The key is the index of the quantum register with the numerical order. The value is the index of the classical register with the numerical order.

selected_qubits: list[int]

The selected qubits.

shots: int

The number of shots.

unitary_located: list[int] | None

The range of the unitary operator.

class qurry.qurrent.classical_shadow.analysis.ShadowUnveilAnalysis(*, serial: int, log: dict[str, Any] | None = None, datatime: str | None = None, **other_kwargs)[source]

The container for the analysis of :cls:`EntropyRandomizedExperiment`.

classmethod content_type() Type[SUAnalysisContent][source]

The type of the content for the analysis.

classmethod deprecated_fields_converts(main: dict[str, Any], side: dict[str, Any]) tuple[dict[str, Any], dict[str, Any]][source]

Convert deprecated fields to new fields.

This method should be implemented in the subclass if there are deprecated fields that need to be converted.

Parameters:
  • main (dict[str, Any]) – The main product dict.

  • side (dict[str, Any]) – The side product dict.

Returns:

The converted main and side product dicts.

Return type:

tuple[dict[str, Any], dict[str, Any]]

classmethod input_type() Type[SUAnalysisInput][source]

The type of the input for the analysis.

property side_product_fields: Iterable[str]

The fields that will be stored as side product.

utils

ShadowUnveil - Utils (qurry.qurrent.classical_shadow.utils)

qurry.qurrent.classical_shadow.utils.circuit_method_core(idx: int, target_circuit: QuantumCircuit, target_key: Hashable, exp_name: str, registers_mapping: dict[int, int], single_unitary_um: dict[int, int]) QuantumCircuit[source]

Build the circuit for the experiment.

Parameters:
  • idx (int) – Index of the quantum circuit.

  • target_circuit (QuantumCircuit) – Target circuit.

  • target_key (Hashable) – Target key.

  • exp_name (str) – Experiment name.

  • registers_mapping (dict[int, int]) – The mapping of the index of selected qubits to the index of the classical register.

  • single_unitary_dict (dict[int, Operator]) – The dictionary of the unitary operator.

Returns:

The circuit for the experiment.

Return type:

QuantumCircuit

experiment

ShadowUnveil - Experiment (qurry.qurrent.classical_shadow.experiment)

class qurry.qurrent.classical_shadow.experiment.OutsideAnalyzeInput[source]

The input for the outside analyze.

class qurry.qurrent.classical_shadow.experiment.ShadowUnveilExperiment(arguments: _A | dict[str, Any], commonparams: Commonparams | dict[str, Any], outfields: dict[str, Any], beforewards: Before | None = None, afterwards: After | None = None, reports: AnalysesContainer | None = None)[source]

The instance of experiment.

property analysis_instance: Type[ShadowUnveilAnalysis]

The analysis instance for this experiment.

analyze(selected_qubits: Iterable[int] | None = None, given_operators: list[ndarray[tuple[int, int], dtype[complex128]]] | None = None, accuracy_prob_comp_delta: float = 0.01, max_shadow_norm: float | None = None, rho_method: Literal['numpy', 'numpy_precomputed'] | str | Literal['numpy_flatten', 'jax_flatten'] = 'numpy_precomputed', trace_method: Literal['trace_of_matmul', 'quick_trace_of_matmul', 'einsum_ij_ji'] | str | Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] = 'einsum_aij_bji_to_ab_jax', estimate_trace_method: Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] | str = 'einsum_aij_bji_to_ab_jax', counts_used: Iterable[int] | None = None, pbar: tqdm | None = None) ShadowUnveilAnalysis[source]

Calculate entangled entropy with more information combined.

Parameters:
  • selected_qubits (Optional[Iterable[int]], optional) – The selected qubits. Defaults to None.

  • given_operators (Optional[list[np.ndarray[tuple[int, int], np.dtype[np.complex128]]]]) – The list of the operators to estimate. Defaults to None.

  • accuracy_prob_comp_delta (float, optional) – The accuracy probability component delta. Defaults to 0.01.

  • max_shadow_norm (Optional[float], optional) – The maximum shadow norm. Defaults to None. If it is None, it will be calculated by the largest shadow norm upper bound. If it is not None, it must be a positive float number. It is \(|| O_i - \frac{\text{tr}(O_i)}{2^n} ||_{\text{shadow}}^2\) in equation.

  • rho_method (RhoMCoreMethod, optional) – The method to use for the calculation. Defaults to “numpy_precomputed”. It can be either “numpy”, “numpy_precomputed”, “jax_flatten”, or “numpy_flatten”. - “numpy”: Use Numpy to calculate the rho_m. - “numpy_precomputed”: Use Numpy to calculate the rho_m with precomputed values. - “numpy_flatten”: Use Numpy to calculate the rho_m with a flattening workflow. Currently, “numpy_precomputed” is the best option for performance.

  • trace_method (Union[SingleTraceRhoMethod, AllTraceRhoMethod], optional) –

    The method to calculate the trace of Rho square. - “trace_of_matmul”:

    Use np.trace(np.matmul(rho_m1, rho_m2)) to calculate the each summation item in rho_m_list.

    • ”quick_trace_of_matmul” or “einsum_ij_ji”:

      Use np.einsum(“ij,ji”, rho_m1, rho_m2) to calculate the each summation item in rho_m_list.

    • ”einsum_aij_bji_to_ab_numpy”:

      Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • estimate_trace_method (AllTraceRhoMethod, optional) –

    The method to calculate the trace for searching esitmator. - “einsum_aij_bji_to_ab_numpy”:

    Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • counts_used (Optional[Iterable[int]], optional) – The index of the counts used. Defaults to None.

  • pbar (Optional[tqdm.tqdm], optional) – The progress bar. Defaults to None.

Returns:

The result of the analysis.

Return type:

ShadowUnveilAnalysis

property arguments_instance: Type[ShadowUnveilArguments]

The arguments instance for this experiment.

classmethod method(targets: list[tuple[Hashable, QuantumCircuit]], arguments: ShadowUnveilArguments, pbar: tqdm | None = None, multiprocess: bool = True) tuple[list[QuantumCircuit], dict[str, Any]][source]

The method to construct circuit.

Parameters:
  • targets (list[tuple[Hashable, QuantumCircuit]]) – The circuits of the experiment.

  • arguments (EntropyMeasureRandomizedArguments) – The arguments of the experiment.

  • pbar (Optional[tqdm.tqdm], optional) – The progress bar for showing the progress of the experiment. Defaults to None.

  • multiprocess (bool, optional) – Whether to use multiprocessing. Defaults to True.

Returns:

The circuits of the experiment and the side products.

Return type:

tuple[list[QuantumCircuit], dict[str, Any]]

outside_analysis_recover(analysis: ShadowUnveilAnalysis) ShadowUnveilAnalysis[source]

Recover the analysis from the outside.

Parameters:

analysis (ShadowUnveilAnalysis) – The analysis to recover.

Returns:

The recovered analysis.

Return type:

ShadowUnveilAnalysis

classmethod params_control(targets: list[tuple[Hashable, QuantumCircuit]], exp_name: str = 'exps', times: int = 100, measure: tuple[int, int] | int | list[int] | None = None, unitary_loc: tuple[int, int] | int | list[int] | None = None, unitary_loc_not_cover_measure: bool = False, random_unitary_seeds: dict[int, dict[int, int]] | None = None, **custom_kwargs: Any) tuple[ShadowUnveilArguments, Commonparams, dict[str, Any]][source]

Handling all arguments and initializing a single experiment.

Parameters:
  • targets (list[tuple[Hashable, QuantumCircuit]]) – The circuits of the experiment.

  • exp_name (str, optional) – The name of the experiment. Naming this experiment to recognize it when the jobs are pending to IBMQ Service. This name is also used for creating a folder to store the exports. Defaults to ‘exps’.

  • times (int, optional) – The number of random unitary operator. Defaults to 100. It will denote as N_U in the experiment name.

  • measure (Optional[Union[list[int], tuple[int, int], int]], optional) – The measure range. Defaults to None.

  • unitary_loc (Optional[Union[list[int], tuple[int, int], int]], optional) – The range of the unitary operator. Defaults to None.

  • unitary_loc_not_cover_measure (bool, optional) – Confirm that not all unitary operator are covered by the measure. If True, then close the warning. Defaults to False.

  • random_unitary_seeds (Optional[dict[int, dict[int, int]]], optional) –

    The seeds for all random unitary operator. This argument only takes input as type of dict[int, dict[int, int]]. The first key is the index for the random unitary operator. The second key is the index for the qubit.

    If you want to generate the seeds for all random unitary operator, you can use the function generate_random_unitary_seeds in qurry.qurrium.utils.random_unitary.

  • custom_kwargs (Any) – The custom parameters.

Raises:
  • ValueError – If the number of targets is not one.

  • TypeError – If times is not an integer.

  • ValueError – If the range of measure is not in the range of unitary_loc.

Returns:

The arguments of the experiment, the common parameters, and the custom parameters.

Return type:

tuple[EntropyMeasureRandomizedArguments, Commonparams, dict[str, Any]]

classmethod quantities(shots: int | None = None, counts: list[dict[str, int]] | None = None, random_unitary_ids: dict[int, dict[int, Literal[0, 1, 2] | int]] | None = None, selected_classical_registers: Iterable[int] | None = None, given_operators: list[ndarray[tuple[int, int], dtype[complex128]]] | None = None, accuracy_prob_comp_delta: float = 0.01, max_shadow_norm: float | None = None, rho_method: Literal['numpy', 'numpy_precomputed'] | str | Literal['numpy_flatten', 'jax_flatten'] = 'numpy_precomputed', trace_method: Literal['trace_of_matmul', 'quick_trace_of_matmul', 'einsum_ij_ji'] | str | Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] = 'einsum_aij_bji_to_ab_jax', estimate_trace_method: Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] | str = 'einsum_aij_bji_to_ab_jax', pbar: tqdm | None = None) ClassicalShadowComplex[source]

Randomized entangled entropy with complex.

Parameters:
  • shots (int) – The number of shots.

  • counts (list[dict[str, int]]) – The list of the counts.

  • random_unitary_ids (dict[int, dict[int, Union[Literal[0, 1, 2], int]]]) – The shadow direction of the unitary operators.

  • selected_classical_registers (Iterable[int]) – The list of the index of the selected_classical_registers.

  • given_operators (Optional[list[np.ndarray[tuple[int, int], np.dtype[np.complex128]]]]) – The list of the operators to estimate. Defaults to None.

  • accuracy_prob_comp_delta (float, optional) – The accuracy probability component delta. Defaults to 0.01.

  • max_shadow_norm (Optional[float], optional) – The maximum shadow norm. Defaults to None. If it is None, it will be calculated by the largest shadow norm upper bound. If it is not None, it must be a positive float number. It is \(|| O_i - \frac{\text{tr}(O_i)}{2^n} ||_{\text{shadow}}^2\) in equation.

  • rho_method (RhoMCoreMethod, optional) – The method to use for the calculation. Defaults to “numpy_precomputed”. It can be either “numpy”, “numpy_precomputed”, “jax_flatten”, or “numpy_flatten”. - “numpy”: Use Numpy to calculate the rho_m. - “numpy_precomputed”: Use Numpy to calculate the rho_m with precomputed values. - “numpy_flatten”: Use Numpy to calculate the rho_m with a flattening workflow. Currently, “numpy_precomputed” is the best option for performance.

  • trace_method (TraceRhoMethod, optional) –

    The method to calculate the trace of Rho square. - “trace_of_matmul”:

    Use np.trace(np.matmul(rho_m1, rho_m2)) to calculate the each summation item in rho_m_list.

    • ”quick_trace_of_matmul” or “einsum_ij_ji”:

      Use np.einsum(“ij,ji”, rho_m1, rho_m2) to calculate the each summation item in rho_m_list.

    • ”einsum_aij_bji_to_ab_numpy”:

      Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • estimate_trace_method (AllTraceRhoMethod, optional) –

    The method to calculate the trace for searching esitmator. - “einsum_aij_bji_to_ab_numpy”:

    Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • pbar (Optional[tqdm.tqdm], optional) – The progress bar. Defaults to None.

Returns:

The result of the classical shadow.

Return type:

ClassicalShadowComplex

qurry.qurrent.classical_shadow.experiment.outside_analyze(exp_id: str, shots: int, counts: list[dict[str, int]], random_unitary_ids: dict[int, dict[int, Literal[0, 1, 2] | int]], selected_classical_registers: Iterable[int], num_qubits: int, selected_qubits: list[int], registers_mapping: dict[int, int], bitstring_mapping: dict[int, int], unitary_located: list[int], given_operators: list[ndarray[tuple[int, int], dtype[complex128]]] | None, accuracy_prob_comp_delta: float, max_shadow_norm: float | None, serial: int, rho_method: Literal['numpy', 'numpy_precomputed'] | str | Literal['numpy_flatten', 'jax_flatten'] = 'numpy_precomputed', trace_method: Literal['trace_of_matmul', 'quick_trace_of_matmul', 'einsum_ij_ji'] | str | Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] = 'einsum_aij_bji_to_ab_jax', estimate_trace_method: Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] | str = 'einsum_aij_bji_to_ab_jax', counts_used: Iterable[int] | None = None) tuple[str, ShadowUnveilAnalysis][source]

Randomized entangled entropy with complex.

Parameters:
  • exp_id (str) – The ID of the experiment.

  • shots (int) – The number of shots.

  • counts (list[dict[str, int]]) – The list of the counts.

  • random_unitary_ids (dict[int, dict[int, Union[Literal[0, 1, 2], int]]]) – The shadow direction of the unitary operators.

  • selected_classical_registers (Iterable[int]) – The list of the index of the selected_classical_registers.

  • num_qubits (int) – The number of qubits.

  • selected_qubits (list[int]) – The selected qubits.

  • registers_mapping (dict[int, int]) – The mapping of the index of selected qubits to the index of the classical register.

  • bitstring_mapping (dict[str, int]) – The mapping of the bitstring to the index of the classical register.

  • unitary_located (list[int]) – The range of the unitary operator.

  • given_operators (Optional[list[np.ndarray[tuple[int, int], np.dtype[np.complex128]]]]) – The list of the operators to estimate. Defaults to None.

  • accuracy_prob_comp_delta (float, optional) – The accuracy probability component delta. Defaults to 0.01.

  • max_shadow_norm (Optional[float], optional) – The maximum shadow norm. Defaults to None. If it is None, it will be calculated by the largest shadow norm upper bound. If it is not None, it must be a positive float number. It is \(|| O_i - \frac{\text{tr}(O_i)}{2^n} ||_{\text{shadow}}^2\) in equation.

  • serial (int) – The serial number of the experiment.

  • rho_method (RhoMCoreMethod, optional) – The method to use for the calculation. Defaults to “numpy_precomputed”. It can be either “numpy”, “numpy_precomputed”, “jax_flatten”, or “numpy_flatten”. - “numpy”: Use Numpy to calculate the rho_m. - “numpy_precomputed”: Use Numpy to calculate the rho_m with precomputed values. - “numpy_flatten”: Use Numpy to calculate the rho_m with a flattening workflow. Currently, “numpy_precomputed” is the best option for performance.

  • trace_method (TraceRhoMethod, optional) –

    The method to calculate the trace of Rho square. - “trace_of_matmul”:

    Use np.trace(np.matmul(rho_m1, rho_m2)) to calculate the each summation item in rho_m_list.

    • ”quick_trace_of_matmul” or “einsum_ij_ji”:

      Use np.einsum(“ij,ji”, rho_m1, rho_m2) to calculate the each summation item in rho_m_list.

    • ”einsum_aij_bji_to_ab_numpy”:

      Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • estimate_trace_method (AllTraceRhoMethod, optional) –

    The method to calculate the trace for searching esitmator. - “einsum_aij_bji_to_ab_numpy”:

    Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • backend (PostProcessingBackend, optional) – Backend for the process. Defaults to DEFAULT_PROCESS_BACKEND.

  • counts_used (Optional[Iterable[int]], optional) – The index of the counts used. Defaults to None.

Returns:

The ID of the experiment and the result of the classical shadow.

Return type:

tuple[str, ShadowUnveilAnalysis]

qurry.qurrent.classical_shadow.experiment.outside_analyze_wrapper(all_arguments: OutsideAnalyzeInput) tuple[str, ShadowUnveilAnalysis][source]

Wrapper for the outside analyze.

Parameters:

all_arguments (OutsideAnalyzeInput) – The arguments for the outside analyze.

Returns:

The ID of the experiment and the result of the classical shadow.

Return type:

tuple[str, ShadowUnveilAnalysis]

qurry.qurrent.classical_shadow.experiment.quantities_input_collecter(current_exps: ShadowUnveilExperiment, selected_qubits: Iterable[int] | None = None, given_operators: list[ndarray[tuple[int, int], dtype[complex128]]] | None = None, accuracy_prob_comp_delta: float = 0.01, max_shadow_norm: float | None = None, rho_method: Literal['numpy', 'numpy_precomputed'] | str | Literal['numpy_flatten', 'jax_flatten'] = 'numpy_precomputed', trace_method: Literal['trace_of_matmul', 'quick_trace_of_matmul', 'einsum_ij_ji'] | str | Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] = 'einsum_aij_bji_to_ab_jax', estimate_trace_method: Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] | str = 'einsum_aij_bji_to_ab_jax', counts_used: Iterable[int] | None = None) OutsideAnalyzeInput[source]

Collect the inputs for the quantities.

Parameters:
  • current_exps (ShadowUnveilExperiment) – The current experiment instance.

  • selected_qubits (Optional[Iterable[int]], optional) – The selected qubits. Defaults to None.

  • given_operators (Optional[list[np.ndarray[tuple[int, int], np.dtype[np.complex128]]]]) – The list of the operators to estimate. Defaults to None.

  • accuracy_prob_comp_delta (float, optional) – The accuracy probability component delta. Defaults to 0.01.

  • max_shadow_norm (Optional[float], optional) – The maximum shadow norm. Defaults to None. If it is None, it will be calculated by the largest shadow norm upper bound. If it is not None, it must be a positive float number. It is \(|| O_i - \frac{\text{tr}(O_i)}{2^n} ||_{\text{shadow}}^2\) in equation.

  • backend (PostProcessingBackendLabel, optional) – The backend for the process. Defaults to DEFAULT_PROCESS_BACKEND.

  • rho_method (RhoMCoreMethod, optional) – The method to use for the calculation. Defaults to “numpy_precomputed”. It can be either “numpy”, “numpy_precomputed”, “jax_flatten”, or “numpy_flatten”. - “numpy”: Use Numpy to calculate the rho_m. - “numpy_precomputed”: Use Numpy to calculate the rho_m with precomputed values. - “numpy_flatten”: Use Numpy to calculate the rho_m with a flattening workflow. Currently, “numpy_precomputed” is the best option for performance.

  • trace_method (Union[SingleTraceRhoMethod, AllTraceRhoMethod], optional) –

    The method to calculate the trace of Rho square. - “trace_of_matmul”:

    Use np.trace(np.matmul(rho_m1, rho_m2)) to calculate the each summation item in rho_m_list.

    • ”quick_trace_of_matmul” or “einsum_ij_ji”:

      Use np.einsum(“ij,ji”, rho_m1, rho_m2) to calculate the each summation item in rho_m_list.

    • ”einsum_aij_bji_to_ab_numpy”:

      Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • estimate_trace_method (AllTraceRhoMethod, optional) –

    The method to calculate the trace for searching esitmator. - “einsum_aij_bji_to_ab_numpy”:

    Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • counts_used (Optional[Iterable[int]], optional) – The index of the counts used. Defaults to None.

Returns:

The inputs for the quantities.

Return type:

OutsideAnalyzeInput

qurry

ShadowUnveil - Qurrium (qurry.qurrent.classical_shadow.qurry)

class qurry.qurrent.classical_shadow.qurry.ShadowUnveil[source]

Classical Shadow with The Results of Second Order Renyi Entropy.

References

Note

  • Predicting many properties of a quantum system from very few measurements -

Huang, Hsin-Yuan and Kueng, Richard and Preskill, John [doi:10.1038/s41567-020-0932-7](

  • The randomized measurement toolbox -

Elben, Andreas and Flammia, Steven T. and Huang, Hsin-Yuan and Kueng, Richard and Preskill, John and Vermersch, Benoît and Zoller, Peter [doi:10.1038/s42254-022-00535-2](

@article{cite-key,
    abstract = {
        Predicting the properties of complex,
        large-scale quantum systems is essential for developing quantum technologies.
        We present an efficient method for constructing an approximate classical
        description of a quantum state using very few measurements of the state.
        different properties; order
        {\$}{\$}{\{}{$\backslash$}mathrm{\{}log{\}}{\}}{$\backslash$},(M){\$}{\$}
        measurements suffice to accurately predict M different functions of the state
        with high success probability. The number of measurements is independent of
        the system size and saturates information-theoretic lower bounds. Moreover,
        target properties to predict can be
        selected after the measurements are completed.
        We support our theoretical findings with extensive numerical experiments.
        We apply classical shadows to predict quantum fidelities,
        entanglement entropies, two-point correlation functions,
        expectation values of local observables and the energy variance of
        many-body local Hamiltonians.
        The numerical results highlight the advantages of classical shadows relative to
        previously known methods.},
    author = {Huang, Hsin-Yuan and Kueng, Richard and Preskill, John},
    date = {2020/10/01},
    date-added = {2024-12-03 15:00:55 +0800},
    date-modified = {2024-12-03 15:00:55 +0800},
    doi = {10.1038/s41567-020-0932-7},
    id = {Huang2020},
    isbn = {1745-2481},
    journal = {Nature Physics},
    number = {10},
    pages = {1050--1057},
    title = {Predicting many properties of a quantum system from very few measurements},
    url = {https://doi.org/10.1038/s41567-020-0932-7},
    volume = {16},
    year = {2020},
    bdsk-url-1 = {https://doi.org/10.1038/s41567-020-0932-7}
}

@article{cite-key,
    abstract = {
        Programmable quantum simulators and quantum computers are opening unprecedented
        opportunities for exploring and exploiting the properties of highly entangled
        complex quantum systems. The complexity of large quantum systems is the source
        of computational power but also makes them difficult to control precisely or
        characterize accurately using measured classical data. We review protocols
        for probing the properties of complex many-qubit systems using measurement
        schemes that are practical using today's quantum platforms. In these protocols,
        a quantum state is repeatedly prepared and measured in a randomly chosen basis;
        then a classical computer processes the measurement outcomes to estimate the
        desired property. The randomization of the measurement procedure has distinct
        advantages. For example, a single data set can be used multiple times to pursue
        a variety of applications, and imperfections in the measurements are mapped to
        a simplified noise model that can more easily be mitigated.
        We discuss a range of cases that have already been realized in quantum devices,
        including Hamiltonian simulation tasks, probes of quantum chaos,
        measurements of non-local order parameters,
        and comparison of quantum states produced in distantly separated
        laboratories. By providing a workable method for translating a complex quantum
        state into a succinct classical representation that preserves a rich variety of
        relevant physical properties, the randomized measurement toolbox strengthens our
        ability to grasp and control the quantum world.},
    author = {
        Elben, Andreas and Flammia, Steven T. and Huang, Hsin-Yuan and Kueng,
        Richard and Preskill, John and Vermersch, Beno{\^\i}t and Zoller, Peter},
    date = {2023/01/01},
    date-added = {2024-12-03 15:06:15 +0800},
    date-modified = {2024-12-03 15:06:15 +0800},
    doi = {10.1038/s42254-022-00535-2},
    id = {Elben2023},
    isbn = {2522-5820},
    journal = {Nature Reviews Physics},
    number = {1},
    pages = {9--24},
    title = {The randomized measurement toolbox},
    url = {https://doi.org/10.1038/s42254-022-00535-2},
    volume = {5},
    year = {2023},
    bdsk-url-1 = {https://doi.org/10.1038/s42254-022-00535-2}
}
property experiment_instance: Type[ShadowUnveilExperiment]

The container class responding to this QurryV5 class.

measure(wave: QuantumCircuit | Hashable | None = None, times: int = 100, measure: tuple[int, int] | int | list[int] | None = None, unitary_loc: tuple[int, int] | int | list[int] | None = None, unitary_loc_not_cover_measure: bool = False, random_unitary_seeds: dict[int, dict[int, int]] | None = None, shots: int = 1024, backend: Backend | None = None, exp_name: str = 'experiment', run_args: BaseRunArgs | dict[str, Any] | None = None, transpile_args: TranspileArgs | None = None, passmanager: str | PassManager | tuple[str, PassManager] | None = None, tags: tuple[str, ...] | None = None, qasm_version: Literal['qasm2', 'qasm3'] = 'qasm3', export: bool = False, save_location: Path | str | None = None, pbar: tqdm | None = None) str[source]

Execute the experiment.

Parameters:
  • wave (Union[QuantumCircuit, Hashable]) – The key or the circuit to execute.

  • times (int, optional) – The number of random unitary operator. It will denote as N_U in the experiment name. Defaults to 100.

  • measure (Optional[Union[list[int], tuple[int, int], int]], optional) – The measure range. Defaults to None.

  • unitary_loc (Optional[Union[list[int], tuple[int, int], int]], optional) – The range of the unitary operator. Defaults to None.

  • unitary_loc_not_cover_measure (bool, optional) – Whether the range of the unitary operator is not cover the measure range. Defaults to False.

  • random_unitary_seeds (Optional[dict[int, dict[int, int]]], optional) –

    The seeds for all random unitary operator. This argument only takes input as type of dict[int, dict[int, int]]. The first key is the index for the random unitary operator. The second key is the index for the qubit.

    If you want to generate the seeds for all random unitary operator, you can use the function generate_random_unitary_seeds in qurry.qurrium.utils.random_unitary.

  • shots (int, optional) – Shots of the job. Defaults to 1024.

  • backend (Optional[Backend], optional) – The quantum backend. Defaults to None.

  • exp_name (str, optional) – The name of the experiment. Naming this experiment to recognize it when the jobs are pending to IBMQ Service. This name is also used for creating a folder to store the exports. Defaults to ‘exps’.

  • run_args (RunArgsType, optional) – Arguments for Backend.run(). Defaults to None.

  • transpile_args (Optional[TranspileArgs], optional) – Arguments of transpile() from qiskit.compiler.transpiler. Defaults to None.

  • passmanager (PassManagerType, optional) – The passmanager. Defaults to None.

  • tags (Optional[tuple[str, ...]], optional) – The tags of the experiment. Defaults to None.

  • qasm_version (Literal["qasm2", "qasm3"], optional) – The version of OpenQASM. Defaults to “qasm3”.

  • export (bool, optional) – Whether to export the experiment. Defaults to False.

  • save_location (Optional[Union[Path, str]], optional) – The location to save the experiment. Defaults to None.

  • pbar (Optional[tqdm.tqdm], optional) – The progress bar for showing the progress of the experiment. Defaults to None.

Returns:

The experiment ID.

Return type:

str

measure_to_output(wave: QuantumCircuit | Hashable | None = None, times: int = 100, measure: tuple[int, int] | int | list[int] | None = None, unitary_loc: tuple[int, int] | int | list[int] | None = None, unitary_loc_not_cover_measure: bool = False, random_unitary_seeds: dict[int, dict[int, int]] | None = None, shots: int = 1024, backend: Backend | None = None, exp_name: str = 'experiment', run_args: BaseRunArgs | dict[str, Any] | None = None, transpile_args: TranspileArgs | None = None, passmanager: str | PassManager | tuple[str, PassManager] | None = None, tags: tuple[str, ...] | None = None, qasm_version: Literal['qasm2', 'qasm3'] = 'qasm3', export: bool = False, save_location: Path | str | None = None, pbar: tqdm | None = None) ShadowUnveilOutputArgs[source]

Trasnform measure() arguments form into output() form.

Parameters:
  • wave (Union[QuantumCircuit, Hashable]) – The key or the circuit to execute.

  • times (int, optional) – The number of random unitary operator. It will denote as N_U in the experiment name. Defaults to 100.

  • measure (Optional[Union[list[int], tuple[int, int], int]], optional) – The measure range. Defaults to None.

  • unitary_loc (Optional[Union[list[int], tuple[int, int], int]], optional) – The range of the unitary operator. Defaults to None.

  • unitary_loc_not_cover_measure (bool, optional) – Whether the range of the unitary operator is not cover the measure range. Defaults to False.

  • random_unitary_seeds (Optional[dict[int, dict[int, int]]], optional) –

    The seeds for all random unitary operator. This argument only takes input as type of dict[int, dict[int, int]]. The first key is the index for the random unitary operator. The second key is the index for the qubit.

    If you want to generate the seeds for all random unitary operator, you can use the function generate_random_unitary_seeds in qurry.qurrium.utils.random_unitary.

  • shots (int, optional) – Shots of the job. Defaults to 1024.

  • backend (Optional[Backend], optional) – The quantum backend. Defaults to None.

  • exp_name (str, optional) – The name of the experiment. Naming this experiment to recognize it when the jobs are pending to IBMQ Service. This name is also used for creating a folder to store the exports. Defaults to ‘exps’.

  • run_args (RunArgsType, optional) – Arguments for Backend.run(). Defaults to None.

  • transpile_args (Optional[TranspileArgs], optional) – Arguments of transpile() from qiskit.compiler.transpiler. Defaults to None.

  • passmanager (PassManagerType, optional) – The passmanager. Defaults to None.

  • tags (Optional[tuple[str, ...]], optional) – The tags of the experiment. Defaults to None.

  • qasm_version (Literal["qasm2", "qasm3"], optional) – The version of OpenQASM. Defaults to “qasm3”.

  • export (bool, optional) – Whether to export the experiment. Defaults to False.

  • save_location (Optional[Union[Path, str]], optional) – The location to save the experiment. Defaults to None.

  • pbar (Optional[tqdm.tqdm], optional) – The progress bar for showing the progress of the experiment. Defaults to None.

Returns:

The output arguments.

Return type:

ShadowUnveilOutputArgs

multiAnalysis(summoner_id: str, *, analysis_name: str = 'report', no_serialize: bool = False, specific_analysis_args: dict[Hashable, ShadowUnveilAnalyzeArgs | dict[str, Any] | bool] | None = None, skip_write: bool = False, multiprocess_write: bool = False, multiprocess_analysis: bool = False, selected_qubits: list[int] | None = None, rho_method: Literal['numpy', 'numpy_precomputed'] | str | Literal['numpy_flatten', 'jax_flatten'] = 'numpy_precomputed', trace_method: Literal['trace_of_matmul', 'quick_trace_of_matmul', 'einsum_ij_ji'] | str | Literal['einsum_aij_bji_to_ab_numpy', 'einsum_aij_bji_to_ab_jax'] = 'einsum_aij_bji_to_ab_jax', counts_used: Iterable[int] | None = None, **analysis_args) str[source]

Run the analysis for multiple experiments.

Parameters:
  • summoner_id (str) – The summoner_id of multimanager.

  • analysis_name (str, optional) – The name of analysis. Defaults to ‘report’.

  • no_serialize (bool, optional) – Whether to serialize the analysis. Defaults to False.

  • specific_analysis_args (SpecificAnalsisArgs[ShadowUnveilAnalyzeArgs], optional) – The specific arguments for analysis. Defaults to None.

  • compress (bool, optional) – Whether to compress the export file. Defaults to False.

  • skip_write (bool, optional) – Whether to skip the file writing during the analysis. Defaults to False.

  • multiprocess_write (bool, optional) – Whether use multiprocess for writing. Defaults to False.

  • multiprocess_analysis (bool, optional) – Whether use multiprocess for analysis. Defaults to False.

  • selected_qubits (Optional[list[int]], optional) – The selected qubits. Defaults to None.

  • rho_method (RhoMCoreMethod, optional) – The method to use for the calculation. Defaults to “numpy_precomputed”. It can be either “numpy”, “numpy_precomputed”, “numpy_flatten”. - “numpy”: Use Numpy to calculate the rho_m. - “numpy_precomputed”: Use Numpy to calculate the rho_m with precomputed values. - “numpy_flatten”: Use Numpy to calculate the rho_m with a flattening workflow. Currently, “numpy_precomputed” is the best option for performance.

  • trace_method (TraceRhoMethod, optional) –

    The method to calculate the trace of Rho square. - “trace_of_matmul”:

    Use np.trace(np.matmul(rho_m1, rho_m2)) to calculate the trace.

    • ”quick_trace_of_matmul” or “einsum_ij_ji”:

      Use np.einsum(“ij,ji”, rho_m1, rho_m2) to calculate the trace. Which is the fastest method to calculate the trace. Due to handle all computation in einsum.

    • ”einsum_aij_bji_to_ab_numpy”:

      Use np.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

    • ”einsum_aij_bji_to_ab_jax”:

      Use jnp.einsum(“aij,bji->ab”, rho_m_list, rho_m_list) to calculate the trace.

  • counts_used (Optional[Iterable[int]], optional) – The counts used for the analysis. Defaults to None.

Returns:

The summoner_id of multimanager.

Return type:

str

short_name = 'qurshady_entropy'

The short name of Qurrium.