From 6614641f5fd67b702ccb99c47fc68dd04b94327f Mon Sep 17 00:00:00 2001 From: Purva Thakre Date: Wed, 11 Sep 2024 09:11:02 -0500 Subject: [PATCH] main functions without corrected mypy errors, unit tests and docstrings --- docs/source/apidoc.md | 5 ++ mitiq/lre/__init__.py | 4 +- mitiq/lre/lre.py | 112 ++++++++++++++++++++++++++++++++++++ mitiq/lre/tests/test_lre.py | 30 ++++++++++ 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 mitiq/lre/lre.py create mode 100644 mitiq/lre/tests/test_lre.py diff --git a/docs/source/apidoc.md b/docs/source/apidoc.md index 9a3fbf10f..e9e5fe65a 100644 --- a/docs/source/apidoc.md +++ b/docs/source/apidoc.md @@ -89,6 +89,11 @@ See Ref. {cite}`Czarnik_2021_Quantum` for more details on these methods. ### Layerwise Richardson Extrapolation +```{eval-rst} +.. automodule:: mitiq.lre.lre + :members: +``` + ```{eval-rst} .. automodule:: mitiq.lre.multivariate_scaling.layerwise_folding :members: diff --git a/mitiq/lre/__init__.py b/mitiq/lre/__init__.py index 71e51c78e..353568cdd 100644 --- a/mitiq/lre/__init__.py +++ b/mitiq/lre/__init__.py @@ -10,4 +10,6 @@ from mitiq.lre.inference.multivariate_richardson import ( multivariate_richardson_coefficients, sample_matrix, -) \ No newline at end of file +) + +from mitiq.lre.lre import execute_with_lre, mitigate_executor, lre_decorator \ No newline at end of file diff --git a/mitiq/lre/lre.py b/mitiq/lre/lre.py new file mode 100644 index 000000000..dd0354897 --- /dev/null +++ b/mitiq/lre/lre.py @@ -0,0 +1,112 @@ +# Copyright (C) Unitary Fund +# +# This source code is licensed under the GPL license (v3) found in the +# LICENSE file in the root directory of this source tree. + +"""Extrapolation methods for Layerwise Richardson Extrapolation (LRE)""" + +from functools import wraps +from typing import Callable, Optional, Union + +import numpy as np +from cirq import Circuit + +from mitiq import Executor, Observable, QuantumResult +from mitiq.lre import ( + multivariate_layer_scaling, + multivariate_richardson_coefficients, +) +from mitiq.zne.scaling import fold_gates_at_random + + +def execute_with_lre( + input_circuit: Circuit, + executor: Union[Executor, Callable[[Circuit], QuantumResult]], + shots: int, + degree: int, + fold_multiplier: int, + folding_method: Callable[[Circuit, float], Circuit] = fold_gates_at_random, + num_chunks: Optional[int] = None, + observable: Optional[Observable] = None, +)-> float: + noise_scaled_circuits = multivariate_layer_scaling( + input_circuit, degree, fold_multiplier, num_chunks, folding_method + ) + linear_combination_coeffs = multivariate_richardson_coefficients( + input_circuit, degree, fold_multiplier, num_chunks + ) + normalized_shots_list = shots // len(linear_combination_coeffs) + rescaled_shots_list = [normalized_shots_list] * len( + linear_combination_coeffs + ) + + lre_exp_values = [] + for circuit_shots, scaled_circuit in zip( + rescaled_shots_list, noise_scaled_circuits + ): + circ_exp_val = executor(scaled_circuit, shots=circuit_shots) + lre_exp_values.append(circ_exp_val) + + # verify the linear combination coefficients and the calculated expectation + # values have the same length + assert len(lre_exp_values) == len(linear_combination_coeffs) + + return np.dot(lre_exp_values, linear_combination_coeffs) + + +def mitigate_executor( + executor: Union[Executor, Callable[[Circuit], QuantumResult]], + shots: int, + degree: int, + fold_multiplier: int, + folding_method: Callable[[Circuit, float], Circuit] = fold_gates_at_random, + num_chunks: Optional[int] = None, + observable: Optional[Observable] = None, +) -> Callable[[Circuit], float]: + @wraps(executor) + def new_executor(input_circuit: Circuit) -> float: + return execute_with_lre( + input_circuit, + executor, + shots, + degree, + fold_multiplier, + folding_method, + num_chunks, + observable, + ) + + return new_executor + + +def lre_decorator( + shots: int, + degree: int, + fold_multiplier: int, + folding_method: Callable[[Circuit, float], Circuit] = fold_gates_at_random, + num_chunks: Optional[int] = None, + observable: Optional[Observable] = None, +) -> Callable[ + [Callable[[Circuit], QuantumResult]], Callable[[Circuit], float] +]: + # Raise an error if the decorator is used without parenthesis + if callable(observable): + raise TypeError( + "Decorator must be used with parentheses (i.e., @lre_decorator()) " + "with explicit arguments for shots, degree and fold_multiplier." + ) + + def decorator( + executor: Callable[[Circuit], QuantumResult], + ) -> Callable[[Circuit], float]: + return mitigate_executor( + executor, + shots, + degree, + fold_multiplier, + folding_method, + num_chunks, + observable, + ) + + return decorator diff --git a/mitiq/lre/tests/test_lre.py b/mitiq/lre/tests/test_lre.py new file mode 100644 index 000000000..e4ef5c4e9 --- /dev/null +++ b/mitiq/lre/tests/test_lre.py @@ -0,0 +1,30 @@ +"""Unit tests for the LRE extrapolation methods.""" + +from cirq import DensityMatrixSimulator, depolarize + +from mitiq import benchmarks +from mitiq.lre import execute_with_lre + +test_cirq = benchmarks.generate_rb_circuits( + n_qubits=1, + num_cliffords=2, +)[0] + + +def execute(circuit, noise_level=0.025, shots=1000): + """Returns Tr[ρ |0⟩⟨0|] where ρ is the state prepared by the circuit + executed with depolarizing noise. + """ + # Replace with code based on your frontend and backend. + mitiq_circuit = circuit + noisy_circuit = mitiq_circuit.with_noise(depolarize(p=noise_level)) + rho = DensityMatrixSimulator().simulate(noisy_circuit).final_density_matrix + return rho[0, 0].real + + +def test_lre_exp_value(): + noisy_val = execute(test_cirq) + ideal_val = execute(test_cirq, noise_level=0, shots=1000) + assert abs(ideal_val - noisy_val) > 0 + lre_exp_val = execute_with_lre(test_cirq, execute, 1000, 2, 2) + assert lre_exp_val > noisy_val