From 136c0c37ff0a793983213967ffcffc2c49800a76 Mon Sep 17 00:00:00 2001 From: Vladimir Kozhukalov Date: Wed, 25 Oct 2023 23:51:21 -0500 Subject: [PATCH 01/10] Refactor calibration logs Introduce two different ways of logging calibration results: * As a 3 column table. benchmark_0 | strategy_0 | performance_00 benchmark_0 | strategy_1 | performance_01 benchmark_1 | strategy_0 | performance_10 benchmark_1 | strategy_1 | performance_11 * As a cartesian product of benchmark/strategy. In this case the performance is at intersection of particular benchmark and particular strategy. | | benchmark_0 | benchmark_1 | | strategy_0 | performance_00 | performance_01 | | strategy_1 | performance_10 | performance_11 | Benchmarks, strategies and performances are miltiline stringified dicts. This makes it easy to maintain the calibration logging for all new benchmarks/strategies as it only assumes implementing to_dict methods for them. Fixes #2012 --- mitiq/calibration/calibrator.py | 187 ++++++++++---------- mitiq/calibration/settings.py | 14 ++ mitiq/calibration/tests/test_calibration.py | 42 ++++- mitiq/calibration/tests/test_settings.py | 62 ++++++- 4 files changed, 201 insertions(+), 104 deletions(-) diff --git a/mitiq/calibration/calibrator.py b/mitiq/calibration/calibrator.py index 902807cd68..d707b9cecc 100644 --- a/mitiq/calibration/calibrator.py +++ b/mitiq/calibration/calibrator.py @@ -4,20 +4,8 @@ # LICENSE file in the root directory of this source tree. import warnings -from itertools import product -from typing import ( - Any, - Callable, - Dict, - Iterator, - List, - Optional, - Sequence, - Set, - Tuple, - Union, - cast, -) +from operator import itemgetter +from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union, cast import cirq import numpy as np @@ -33,7 +21,6 @@ ) from mitiq.calibration.settings import ( BenchmarkProblem, - MitigationTechnique, Settings, Strategy, ZNESettings, @@ -96,95 +83,96 @@ def _get_performance( performance_symbol = "✔" if mitigation_worked else "✘" return performance_symbol, noisy_error, mitigated_error - def unique_techniques(self) -> Set[MitigationTechnique]: - """Returns the unique mitigation techniques used across this - collection of experiment results.""" - return set(strategy.technique for strategy in self.strategies) - - def _technique_results( - self, technique: MitigationTechnique - ) -> Iterator[Tuple[BenchmarkProblem, Strategy, str, float, float]]: - """Yields the results from this collection of experiment results, - limited to a specific technique.""" - for strategy, problem in product(self.strategies, self.problems): - if strategy.technique is technique: - performance_symbol, nerr, merr = self._get_performance( + def log_results(self) -> None: + """Prints calibration results in the following form + ┌────────────────────────────────────────────┬────────────────────────────────┬─────────────────────────┐ + │ benchmark │ strategy │ performance │ + ├────────────────────────────────────────────┼────────────────────────────────┼─────────────────────────┤ + │ Type: rb │ Technique: ZNE │ ✔ │ + │ Ideal distribution: {'00': 1.0} │ Factory: RichardsonFactory │ Noisy error: 0.1053 │ + │ Num qubits: 2 │ Scale factors: [1.0, 2.0, 3.0] │ Mitigated error: 0.0146 │ + │ Circuit depth: 326 │ Scale method: fold_global │ │ + │ Two qubit gate count: 79 │ │ │ + ├────────────────────────────────────────────┼────────────────────────────────┼─────────────────────────┤ + │ Type: rb │ Technique: ZNE │ ✔ │ + │ Ideal distribution: {'00': 1.0} │ Factory: RichardsonFactory │ Noisy error: 0.1053 │ + │ Num qubits: 2 │ Scale factors: [1.0, 3.0, 5.0] │ Mitigated error: 0.0422 │ + │ Circuit depth: 326 │ Scale method: fold_global │ │ + │ Two qubit gate count: 79 │ │ │ + ├────────────────────────────────────────────┼────────────────────────────────┼─────────────────────────┤ + │ Type: ghz │ Technique: ZNE │ ✔ │ + │ Ideal distribution: {'00': 0.5, '11': 0.5} │ Factory: RichardsonFactory │ Noisy error: 0.0157 │ + │ Num qubits: 2 │ Scale factors: [1.0, 2.0, 3.0] │ Mitigated error: 0.0018 │ + │ Circuit depth: 2 │ Scale method: fold_global │ │ + │ Two qubit gate count: 1 │ │ │ + ├────────────────────────────────────────────┼────────────────────────────────┼─────────────────────────┤ + │ Type: ghz │ Technique: ZNE │ ✔ │ + │ Ideal distribution: {'00': 0.5, '11': 0.5} │ Factory: RichardsonFactory │ Noisy error: 0.0157 │ + │ Num qubits: 2 │ Scale factors: [1.0, 3.0, 5.0] │ Mitigated error: 0.0091 │ + │ Circuit depth: 2 │ Scale method: fold_global │ │ + │ Two qubit gate count: 1 │ │ │ + └────────────────────────────────────────────┴────────────────────────────────┴─────────────────────────┘ + """ # noqa: E501 + table: List[List[str | float]] = [] + headers: List[str] = ["benchmark", "strategy", "performance"] + for problem in self.problems: + row_group: List[List[str | float]] = [] + for strategy in self.strategies: + perf, nerr, merr = self._get_performance( strategy.id, problem.id ) - yield problem, strategy, performance_symbol, nerr, merr - - def log_technique(self, technique: MitigationTechnique) -> str: - """Creates a table displaying all results of a given mitigation - technique.""" - table: List[List[Union[str, float]]] = [] - for ( - problem, - strategy, - performance_symbol, - noisy_error, - mitigated_error, - ) in self._technique_results(technique): - row: List[Union[str, float]] = [ - performance_symbol, - problem.type, - technique.name, - ] - summary_dict = strategy.to_pretty_dict() - if strategy.technique is MitigationTechnique.ZNE: - row.extend( + row_group.append( [ - summary_dict["factory"], - summary_dict["scale_factors"], - summary_dict["scale_method"], + str(problem), + str(strategy), + f"{perf}\nNoisy error: {round(nerr, 4)}\n" + f"Mitigated error: {round(merr, 4)}", + # this is only for sorting + # removed after sorting + merr - nerr, ] ) - elif strategy.technique is MitigationTechnique.PEC: - row.extend( - [ - summary_dict["noise_level"], - summary_dict["noise_bias"], - summary_dict["representation_function"], - ] + row_group.sort(key=itemgetter(-1)) + table.extend([r[:-1] for r in row_group]) + return print(tabulate(table, headers, tablefmt="simple_grid")) + + def log_results_cartesian(self) -> None: + """Prints calibration results in the following form + ┌────────────────────────────────┬───────────────────────────────────┬──────────────────────────────────────────────┐ + │ strategy\benchmark │ Type: rb │ Type: ghz │ + │ │ Ideal distribution: {'00': 1.0} │ Ideal distribution: {'00': 0.5, '11': 0.5} │ + │ │ Num qubits: 2 │ Num qubits: 2 │ + │ │ Circuit depth: 326 │ Circuit depth: 2 │ + │ │ Two qubit gate count: 79 │ Two qubit gate count: 1 │ + │ │ │ │ + ├────────────────────────────────┼───────────────────────────────────┼──────────────────────────────────────────────┤ + │ Technique: ZNE │ ✔ │ ✔ │ + │ Factory: RichardsonFactory │ Noisy error: 0.1053 │ Noisy error: 0.0157 │ + │ Scale factors: [1.0, 2.0, 3.0] │ Mitigated error: 0.0146 │ Mitigated error: 0.0018 │ + │ Scale method: fold_global │ │ │ + ├────────────────────────────────┼───────────────────────────────────┼──────────────────────────────────────────────┤ + │ Technique: ZNE │ ✔ │ ✔ │ + │ Factory: RichardsonFactory │ Noisy error: 0.1053 │ Noisy error: 0.0157 │ + │ Scale factors: [1.0, 3.0, 5.0] │ Mitigated error: 0.0422 │ Mitigated error: 0.0091 │ + │ Scale method: fold_global │ │ │ + └────────────────────────────────┴───────────────────────────────────┴──────────────────────────────────────────────┘ + """ # noqa: E501 + table: List[List[str]] = [] + headers: List[str] = ["strategy\\benchmark"] + for problem in self.problems: + headers.append(str(problem)) + for strategy in self.strategies: + row: List[str] = [str(strategy)] + for problem in self.problems: + perf, nerr, merr = self._get_performance( + strategy.id, problem.id + ) + row.append( + f"{perf}\nNoisy error: {round(nerr, 4)}\n" + f"Mitigated error: {round(merr, 4)}", ) - row.extend([noisy_error, mitigated_error]) table.append(row) - - def _sort_best_perf(row: List[Any]) -> float: - return row[-1] - row[-2] - - table.sort(key=_sort_best_perf) - - if technique is MitigationTechnique.ZNE: - headers = [ - "performance", - "circuit type", - "method", - "extrapolation", - "scale_factors", - "scale method", - ] - elif technique is MitigationTechnique.PEC: - headers = [ - "performance", - "circuit type", - "method", - "noise level", - "noise bias", - "noise representation", - ] - - headers.extend(["noisy error", "mitigated error"]) - - return tabulate(table, headers, tablefmt="simple_grid") - - def log_results(self) -> None: - """Log results from entire calibration run. Logging is performed on - each mitigation technique individually to avoid confusion when many - techniques are used.""" - for mitigation_technique in self.unique_techniques(): - print(f"{mitigation_technique.name} results:") - print(self.log_technique(mitigation_technique)) - print() + return print(tabulate(table, headers, tablefmt="simple_grid")) def is_missing_data(self) -> bool: """Method to check if there is any missing data that was expected from @@ -308,7 +296,7 @@ def get_cost(self) -> Dict[str, int]: "ideal_executions": ideal, } - def run(self, log: bool = False) -> None: + def run(self, log: bool = False, log_cartesian: bool = False) -> None: """Runs all the circuits required for calibration.""" if not self.results.is_missing_data(): self.results.reset_data() @@ -342,6 +330,9 @@ def run(self, log: bool = False) -> None: if log: self.results.log_results() + if log_cartesian: + self.results.log_results_cartesian() + def best_strategy(self) -> Strategy: """Finds the best strategy by using the parameters that had the smallest error. diff --git a/mitiq/calibration/settings.py b/mitiq/calibration/settings.py index 3d3a4b5746..dd27a7ba11 100644 --- a/mitiq/calibration/settings.py +++ b/mitiq/calibration/settings.py @@ -126,6 +126,13 @@ def to_dict(self) -> Dict[str, Any]: def __repr__(self) -> str: return str(self.to_dict()) + def __str__(self) -> str: + result: str = "" + for key, value in self.to_dict().items(): + title: str = key.replace("_", " ").capitalize() + result += f"{title}: {value}\n" + return result.rstrip() + @dataclass class Strategy: @@ -239,6 +246,13 @@ def to_pretty_dict(self) -> Dict[str, str]: def __repr__(self) -> str: return str(self.to_dict()) + def __str__(self) -> str: + result: str = "" + for key, value in self.to_pretty_dict().items(): + title: str = key.replace("_", " ").capitalize() + result += f"{title}: {value}\n" + return result.rstrip() + def num_circuits_required(self) -> int: summary = self.to_dict() if self.technique is MitigationTechnique.ZNE: diff --git a/mitiq/calibration/tests/test_calibration.py b/mitiq/calibration/tests/test_calibration.py index 3da0b8fc65..7391df23b7 100644 --- a/mitiq/calibration/tests/test_calibration.py +++ b/mitiq/calibration/tests/test_calibration.py @@ -4,6 +4,7 @@ # LICENSE file in the root directory of this source tree. """Tests for the Clifford data regression top-level API.""" +import re from functools import partial import cirq @@ -379,11 +380,42 @@ def test_logging(capfd): ) cal.run(log=True) captured = capfd.readouterr() - assert "ZNE results:" in captured.out - assert "PEC results:" in captured.out - assert settings.get_strategy(0).technique.name in captured.out - assert "noisy error" in captured.out - assert "mitigated error" in captured.out + for s in cal.strategies: + for line in str(s).split(): + assert line in captured.out + for p in cal.problems: + for line in str(p).split(): + assert line in captured.out + mcount = 0 + ncount = 0 + for line in captured.out.split("\n"): + if "Mitigated error: " in line: + mcount += 1 + if "Noisy error: " in line: + ncount += 1 + assert mcount == (len(cal.strategies) * len(cal.problems)) + assert ncount == (len(cal.strategies) * len(cal.problems)) + + +def test_logging_cartesian(capfd): + cal = Calibrator( + damping_execute, frontend="cirq", settings=light_combined_settings + ) + cal.run(log_cartesian=True) + captured = capfd.readouterr() + for s in cal.strategies: + for line in str(s).split(): + assert line in captured.out + for p in cal.problems: + for line in str(p).split(): + assert line in captured.out + for line in captured.out.split("\n"): + if "Mitigated error: " in line: + assert len(re.findall("Mitigated error: ", line)) == len( + cal.problems + ) + if "Noisy error: " in line: + assert len(re.findall("Noisy error: ", line)) == len(cal.problems) def test_ExperimentResults_reset_data(): diff --git a/mitiq/calibration/tests/test_settings.py b/mitiq/calibration/tests/test_settings.py index 68622c65e6..d28c01428c 100644 --- a/mitiq/calibration/tests/test_settings.py +++ b/mitiq/calibration/tests/test_settings.py @@ -122,13 +122,73 @@ def test_basic_settings(): assert ghz_problem.two_qubit_gate_count == 1 assert ghz_problem.ideal_distribution == {"00": 0.5, "11": 0.5} + lines = str(ghz_problem).split("\n") + ghz_problem_dict = ghz_problem.to_dict() + for line in lines: + [title, value] = line.split(":", 1) + key = title.lower().replace(" ", "_") + value = value.strip() + assert key in ghz_problem_dict + assert value == str(ghz_problem_dict[key]) + strategies = settings.make_strategies() num_strategies = 4 assert len(strategies) == num_strategies - strategy_summary = str(strategies[0]).replace("'", '"') + strategy_summary = repr(strategies[0]).replace("'", '"') assert isinstance(json.loads(strategy_summary), dict) + lines = str(strategies[0]).split("\n") + strategy_dict = strategies[0].to_pretty_dict() + for line in lines: + [title, value] = line.split(":") + key = title.lower().replace(" ", "_") + value = value.strip() + assert key in strategy_dict + assert value == str(strategy_dict[key]) + + +def test_settings_pretty_dict(): + settings = Settings( + benchmarks=[ + { + "circuit_type": "ghz", + "num_qubits": 2, + "circuit_depth": 999, + } + ], + strategies=[ + { + "technique": "zne", + "scale_noise": fold_global, + "factory": RichardsonFactory([1.0, 2.0, 3.0]), + }, + { + "technique": "pec", + "representation_function": ( + represent_operation_with_local_depolarizing_noise + ), + "is_qubit_dependent": False, + "noise_level": 0.001, + "num_samples": 200, + }, + ], + ) + strategies = settings.make_strategies() + _dict = strategies[0].to_dict() + pretty_dict = strategies[0].to_pretty_dict() + if pretty_dict["technique"] == "ZNE": + assert pretty_dict["factory"] == _dict["factory"][:-7] + assert ( + pretty_dict["scale_factors"] == str(_dict["scale_factors"])[1:-1] + ) + elif pretty_dict["technique"] == "PEC": + assert pretty_dict["noise_bias"] == _dict.get("noise_bias", "N/A") + assert ( + pretty_dict["representation_function"] + == _dict["representation_function"][25:] + ) + def test_make_circuits_qv_circuits(): settings = Settings( From ba6be6e5f2eff2a126fdeb97efa1bff83b521e08 Mon Sep 17 00:00:00 2001 From: Vladimir Kozhukalov Date: Tue, 31 Oct 2023 22:42:01 -0500 Subject: [PATCH 02/10] Address review comments --- dev_requirements.txt | 1 + mitiq/calibration/calibrator.py | 152 +++++++++--------- mitiq/calibration/settings.py | 6 +- mitiq/calibration/tests/test_calibration.py | 114 ++++++++++---- mitiq/calibration/tests/test_settings.py | 161 ++++++++++---------- 5 files changed, 245 insertions(+), 189 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 1cd0b45239..9bbb1161d2 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -16,6 +16,7 @@ black==22.10 mypy==1.0.0 isort==5.12.0 types-tabulate +mock==5.1.0 # Documentation and examples. Sphinx==5.2.3 diff --git a/mitiq/calibration/calibrator.py b/mitiq/calibration/calibrator.py index d707b9cecc..d54968292d 100644 --- a/mitiq/calibration/calibrator.py +++ b/mitiq/calibration/calibrator.py @@ -4,6 +4,7 @@ # LICENSE file in the root directory of this source tree. import warnings +from enum import StrEnum, auto from operator import itemgetter from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union, cast @@ -32,6 +33,11 @@ class MissingResultsError(Exception): pass +class OutputForm(StrEnum): + flat = auto() + cartesian = auto() + + class ExperimentResults: """Class to store calibration experiment data, and provide helper methods for computing results based on it.""" @@ -60,73 +66,70 @@ def add_result( self.noisy[strategy.id, problem.id] = noisy_val self.ideal[strategy.id, problem.id] = ideal_val - def _get_performance( + @staticmethod + def _performance_str(noisy_error: float, mitigated_error: float): + """Get human readable performance representaion.""" + return ( + f"{'✔' if mitigated_error < noisy_error else '✘'}\n" + f"Noisy error: {round(noisy_error, 4)}\n" + f"Mitigated error: {round(mitigated_error, 4)}\n" + f"Improvement factor: {round(noisy_error / mitigated_error, 4)}" + ) + + def _get_errors( self, strategy_id: int, problem_id: int ) -> Tuple[str, float, float]: - """Get performance symbol and errors. + """Get errors for a given strategy/problem combination. Returns: - A performance tuple comprising: - - performance symbol either ✔ or ✘ depending - on whether mitigation technique works well or not. - It considered to work well if the mitigated error is less - than the noisy error. - - the absolute value of the noisy error - - the absolute value of the mitigated error + A tuple comprising: + - absolute value of the noisy error + - absolute value of the mitigated error """ mitigated = self.mitigated[strategy_id, problem_id] noisy = self.noisy[strategy_id, problem_id] ideal = self.ideal[strategy_id, problem_id] mitigated_error = abs(ideal - mitigated) noisy_error = abs(ideal - noisy) - mitigation_worked = mitigated_error < noisy_error - performance_symbol = "✔" if mitigation_worked else "✘" - return performance_symbol, noisy_error, mitigated_error + return noisy_error, mitigated_error - def log_results(self) -> None: + def log_results_flat(self) -> None: """Prints calibration results in the following form - ┌────────────────────────────────────────────┬────────────────────────────────┬─────────────────────────┐ - │ benchmark │ strategy │ performance │ - ├────────────────────────────────────────────┼────────────────────────────────┼─────────────────────────┤ - │ Type: rb │ Technique: ZNE │ ✔ │ - │ Ideal distribution: {'00': 1.0} │ Factory: RichardsonFactory │ Noisy error: 0.1053 │ - │ Num qubits: 2 │ Scale factors: [1.0, 2.0, 3.0] │ Mitigated error: 0.0146 │ - │ Circuit depth: 326 │ Scale method: fold_global │ │ - │ Two qubit gate count: 79 │ │ │ - ├────────────────────────────────────────────┼────────────────────────────────┼─────────────────────────┤ - │ Type: rb │ Technique: ZNE │ ✔ │ - │ Ideal distribution: {'00': 1.0} │ Factory: RichardsonFactory │ Noisy error: 0.1053 │ - │ Num qubits: 2 │ Scale factors: [1.0, 3.0, 5.0] │ Mitigated error: 0.0422 │ - │ Circuit depth: 326 │ Scale method: fold_global │ │ - │ Two qubit gate count: 79 │ │ │ - ├────────────────────────────────────────────┼────────────────────────────────┼─────────────────────────┤ - │ Type: ghz │ Technique: ZNE │ ✔ │ - │ Ideal distribution: {'00': 0.5, '11': 0.5} │ Factory: RichardsonFactory │ Noisy error: 0.0157 │ - │ Num qubits: 2 │ Scale factors: [1.0, 2.0, 3.0] │ Mitigated error: 0.0018 │ - │ Circuit depth: 2 │ Scale method: fold_global │ │ - │ Two qubit gate count: 1 │ │ │ - ├────────────────────────────────────────────┼────────────────────────────────┼─────────────────────────┤ - │ Type: ghz │ Technique: ZNE │ ✔ │ - │ Ideal distribution: {'00': 0.5, '11': 0.5} │ Factory: RichardsonFactory │ Noisy error: 0.0157 │ - │ Num qubits: 2 │ Scale factors: [1.0, 3.0, 5.0] │ Mitigated error: 0.0091 │ - │ Circuit depth: 2 │ Scale method: fold_global │ │ - │ Two qubit gate count: 1 │ │ │ - └────────────────────────────────────────────┴────────────────────────────────┴─────────────────────────┘ + ┌──────────────────────────┬──────────────────────────────┬────────────────────────────┐ + │ benchmark │ strategy │ performance │ + ├──────────────────────────┼──────────────────────────────┼────────────────────────────┤ + │ Type: rb │ Technique: ZNE │ ✔ │ + │ Num qubits: 2 │ Factory: Richardson │ Noisy error: 0.101 │ + │ Circuit depth: 323 │ Scale factors: 1.0, 3.0, 5.0 │ Mitigated error: 0.0294 │ + │ Two qubit gate count: 77 │ Scale method: fold_global │ Improvement factor: 3.4398 │ + ├──────────────────────────┼──────────────────────────────┼────────────────────────────┤ + │ Type: rb │ Technique: ZNE │ ✔ │ + │ Num qubits: 2 │ Factory: Richardson │ Noisy error: 0.101 │ + │ Circuit depth: 323 │ Scale factors: 1.0, 2.0, 3.0 │ Mitigated error: 0.0501 │ + │ Two qubit gate count: 77 │ Scale method: fold_global │ Improvement factor: 2.016 │ + ├──────────────────────────┼──────────────────────────────┼────────────────────────────┤ + │ Type: ghz │ Technique: ZNE │ ✔ │ + │ Num qubits: 2 │ Factory: Richardson │ Noisy error: 0.0128 │ + │ Circuit depth: 2 │ Scale factors: 1.0, 2.0, 3.0 │ Mitigated error: 0.0082 │ + │ Two qubit gate count: 1 │ Scale method: fold_global │ Improvement factor: 1.561 │ + ├──────────────────────────┼──────────────────────────────┼────────────────────────────┤ + │ Type: ghz │ Technique: ZNE │ ✘ │ + │ Num qubits: 2 │ Factory: Richardson │ Noisy error: 0.0128 │ + │ Circuit depth: 2 │ Scale factors: 1.0, 3.0, 5.0 │ Mitigated error: 0.0137 │ + │ Two qubit gate count: 1 │ Scale method: fold_global │ Improvement factor: 0.9369 │ + └──────────────────────────┴──────────────────────────────┴────────────────────────────┘ """ # noqa: E501 table: List[List[str | float]] = [] headers: List[str] = ["benchmark", "strategy", "performance"] for problem in self.problems: row_group: List[List[str | float]] = [] for strategy in self.strategies: - perf, nerr, merr = self._get_performance( - strategy.id, problem.id - ) + nerr, merr = self._get_errors(strategy.id, problem.id) row_group.append( [ str(problem), str(strategy), - f"{perf}\nNoisy error: {round(nerr, 4)}\n" - f"Mitigated error: {round(merr, 4)}", + self._performance_str(nerr, merr), # this is only for sorting # removed after sorting merr - nerr, @@ -138,24 +141,22 @@ def log_results(self) -> None: def log_results_cartesian(self) -> None: """Prints calibration results in the following form - ┌────────────────────────────────┬───────────────────────────────────┬──────────────────────────────────────────────┐ - │ strategy\benchmark │ Type: rb │ Type: ghz │ - │ │ Ideal distribution: {'00': 1.0} │ Ideal distribution: {'00': 0.5, '11': 0.5} │ - │ │ Num qubits: 2 │ Num qubits: 2 │ - │ │ Circuit depth: 326 │ Circuit depth: 2 │ - │ │ Two qubit gate count: 79 │ Two qubit gate count: 1 │ - │ │ │ │ - ├────────────────────────────────┼───────────────────────────────────┼──────────────────────────────────────────────┤ - │ Technique: ZNE │ ✔ │ ✔ │ - │ Factory: RichardsonFactory │ Noisy error: 0.1053 │ Noisy error: 0.0157 │ - │ Scale factors: [1.0, 2.0, 3.0] │ Mitigated error: 0.0146 │ Mitigated error: 0.0018 │ - │ Scale method: fold_global │ │ │ - ├────────────────────────────────┼───────────────────────────────────┼──────────────────────────────────────────────┤ - │ Technique: ZNE │ ✔ │ ✔ │ - │ Factory: RichardsonFactory │ Noisy error: 0.1053 │ Noisy error: 0.0157 │ - │ Scale factors: [1.0, 3.0, 5.0] │ Mitigated error: 0.0422 │ Mitigated error: 0.0091 │ - │ Scale method: fold_global │ │ │ - └────────────────────────────────┴───────────────────────────────────┴──────────────────────────────────────────────┘ + ┌──────────────────────────────┬────────────────────────────┬────────────────────────────┐ + │ strategy\benchmark │ Type: rb │ Type: ghz │ + │ │ Num qubits: 2 │ Num qubits: 2 │ + │ │ Circuit depth: 337 │ Circuit depth: 2 │ + │ │ Two qubit gate count: 80 │ Two qubit gate count: 1 │ + ├──────────────────────────────┼────────────────────────────┼────────────────────────────┤ + │ Technique: ZNE │ ✔ │ ✘ │ + │ Factory: Richardson │ Noisy error: 0.1128 │ Noisy error: 0.0117 │ + │ Scale factors: 1.0, 2.0, 3.0 │ Mitigated error: 0.0501 │ Mitigated error: 0.0439 │ + │ Scale method: fold_global │ Improvement factor: 2.2515 │ Improvement factor: 0.2665 │ + ├──────────────────────────────┼────────────────────────────┼────────────────────────────┤ + │ Technique: ZNE │ ✔ │ ✘ │ + │ Factory: Richardson │ Noisy error: 0.1128 │ Noisy error: 0.0117 │ + │ Scale factors: 1.0, 3.0, 5.0 │ Mitigated error: 0.0408 │ Mitigated error: 0.0171 │ + │ Scale method: fold_global │ Improvement factor: 2.7672 │ Improvement factor: 0.6852 │ + └──────────────────────────────┴────────────────────────────┴────────────────────────────┘ """ # noqa: E501 table: List[List[str]] = [] headers: List[str] = ["strategy\\benchmark"] @@ -164,13 +165,8 @@ def log_results_cartesian(self) -> None: for strategy in self.strategies: row: List[str] = [str(strategy)] for problem in self.problems: - perf, nerr, merr = self._get_performance( - strategy.id, problem.id - ) - row.append( - f"{perf}\nNoisy error: {round(nerr, 4)}\n" - f"Mitigated error: {round(merr, 4)}", - ) + nerr, merr = self._get_errors(strategy.id, problem.id) + row.append(self._performance_str(nerr, merr)) table.append(row) return print(tabulate(table, headers, tablefmt="simple_grid")) @@ -296,7 +292,7 @@ def get_cost(self) -> Dict[str, int]: "ideal_executions": ideal, } - def run(self, log: bool = False, log_cartesian: bool = False) -> None: + def run(self, log: OutputForm = None) -> None: """Runs all the circuits required for calibration.""" if not self.results.is_missing_data(): self.results.reset_data() @@ -327,11 +323,15 @@ def run(self, log: bool = False, log_cartesian: bool = False) -> None: self.results.ensure_full() - if log: - self.results.log_results() - - if log_cartesian: - self.results.log_results_cartesian() + if log is not None: + if log == OutputForm.flat: + self.results.log_results_flat() + elif log == OutputForm.cartesian: + self.results.log_results_cartesian() + else: + raise ValueError( + f"log parameter must be one of: {', '.join(OutputForm._member_names_)}" + ) def best_strategy(self) -> Strategy: """Finds the best strategy by using the parameters that had the diff --git a/mitiq/calibration/settings.py b/mitiq/calibration/settings.py index dd27a7ba11..bfea6d1d06 100644 --- a/mitiq/calibration/settings.py +++ b/mitiq/calibration/settings.py @@ -127,8 +127,10 @@ def __repr__(self) -> str: return str(self.to_dict()) def __str__(self) -> str: - result: str = "" + result = "" for key, value in self.to_dict().items(): + if key == "ideal_distribution": + continue title: str = key.replace("_", " ").capitalize() result += f"{title}: {value}\n" return result.rstrip() @@ -247,7 +249,7 @@ def __repr__(self) -> str: return str(self.to_dict()) def __str__(self) -> str: - result: str = "" + result = "" for key, value in self.to_pretty_dict().items(): title: str = key.replace("_", " ").capitalize() result += f"{title}: {value}\n" diff --git a/mitiq/calibration/tests/test_calibration.py b/mitiq/calibration/tests/test_calibration.py index 7391df23b7..4b20580907 100644 --- a/mitiq/calibration/tests/test_calibration.py +++ b/mitiq/calibration/tests/test_calibration.py @@ -10,6 +10,7 @@ import cirq import numpy as np import pytest +from mock import MagicMock, call from mitiq import SUPPORTED_PROGRAM_TYPES, Executor, MeasurementResult from mitiq.benchmarks import generate_rb_circuits @@ -374,48 +375,105 @@ def test_ExtrapolationResults_best_strategy(): ) -def test_logging(capfd): +def test_ExtrapolationResults_performance_str(): + nerr = 0.9876543 + merr = 0.1234567 + perf_str = ExperimentResults._performance_str(nerr, merr) + mandatory_lines_count = 0 + for line in perf_str.split("\n"): + if "Noisy error: " in line: + assert line.split("Noisy error: ")[1] == str(round(nerr, 4)) + mandatory_lines_count += 1 + if "Mitigated error: " in line: + assert line.split("Mitigated error: ")[1] == str(round(merr, 4)) + mandatory_lines_count += 1 + if "Improvement factor: " in line: + assert line.split("Improvement factor: ")[1] == str( + round(nerr / merr, 4) + ) + mandatory_lines_count += 1 + assert mandatory_lines_count == 3 + + +def test_Calibrator_log_argument(): cal = Calibrator( - damping_execute, frontend="cirq", settings=light_combined_settings + damping_execute, frontend="cirq", settings=Settings([], []) ) - cal.run(log=True) + cal.results = MagicMock() + cal.run() + cal.results.log_results_flat.assert_not_called() + + cal.run(log="flat") + cal.results.log_results_flat.assert_called_once() + + cal.run(log="cartesian") + cal.results.log_results_cartesian.assert_called_once() + + with pytest.raises(ValueError): + cal.run(log="foo") + + +def test_ExtrapolationResults_logging_results_flat(capfd): + settings = light_zne_settings + problems = settings.make_problems() + strategies = settings.make_strategies() + results = ExperimentResults(strategies, problems) + perf_str = "performance result" + results._performance_str = MagicMock(return_value=perf_str) + for s in strategies: + for p in problems: + results.add_result( + s, p, ideal_val=1.0, noisy_val=0.5, mitigated_val=0.6 + ) + results.log_results_flat() captured = capfd.readouterr() - for s in cal.strategies: + results._performance_str.assert_has_calls( + [call(0.5, 0.4)] * len(strategies) * len(problems) + ) + for s in strategies: for line in str(s).split(): assert line in captured.out - for p in cal.problems: + for p in problems: for line in str(p).split(): assert line in captured.out - mcount = 0 - ncount = 0 + # Here we make sure that we have len(strategies) * len(problems) + # performance results lines in the output. One performance result + # per strategy/problem combination. + perf_count = 0 for line in captured.out.split("\n"): - if "Mitigated error: " in line: - mcount += 1 - if "Noisy error: " in line: - ncount += 1 - assert mcount == (len(cal.strategies) * len(cal.problems)) - assert ncount == (len(cal.strategies) * len(cal.problems)) - - -def test_logging_cartesian(capfd): - cal = Calibrator( - damping_execute, frontend="cirq", settings=light_combined_settings - ) - cal.run(log_cartesian=True) + if perf_str in line: + perf_count += 1 + assert perf_count == (len(strategies) * len(problems)) + + +def test_ExtrapolationResults_logging_results_cartesian(capfd): + settings = light_zne_settings + problems = settings.make_problems() + strategies = settings.make_strategies() + results = ExperimentResults(strategies, problems) + perf_str = "performance result" + results._performance_str = MagicMock(return_value=perf_str) + for s in strategies: + for p in problems: + results.add_result( + s, p, ideal_val=1.0, noisy_val=0.5, mitigated_val=0.6 + ) + results.log_results_cartesian() captured = capfd.readouterr() - for s in cal.strategies: + results._performance_str.assert_has_calls( + [call(0.5, 0.4)] * len(strategies) * len(problems) + ) + for s in strategies: for line in str(s).split(): assert line in captured.out - for p in cal.problems: + for p in problems: for line in str(p).split(): assert line in captured.out + # Here we make sure the output contains multiple columns. + # One column per problem for line in captured.out.split("\n"): - if "Mitigated error: " in line: - assert len(re.findall("Mitigated error: ", line)) == len( - cal.problems - ) - if "Noisy error: " in line: - assert len(re.findall("Noisy error: ", line)) == len(cal.problems) + if perf_str in line: + assert len(re.findall(perf_str, line)) == len(problems) def test_ExperimentResults_reset_data(): diff --git a/mitiq/calibration/tests/test_settings.py b/mitiq/calibration/tests/test_settings.py index d28c01428c..e4eafbc18b 100644 --- a/mitiq/calibration/tests/test_settings.py +++ b/mitiq/calibration/tests/test_settings.py @@ -73,6 +73,33 @@ ) +basic_settings = Settings( + benchmarks=[ + { + "circuit_type": "ghz", + "num_qubits": 2, + "circuit_depth": 999, + } + ], + strategies=[ + { + "technique": "zne", + "scale_noise": fold_global, + "factory": RichardsonFactory([1.0, 2.0, 3.0]), + }, + { + "technique": "pec", + "representation_function": ( + represent_operation_with_local_depolarizing_noise + ), + "is_qubit_dependent": False, + "noise_level": 0.001, + "num_samples": 200, + }, + ], +) + + def test_MitigationTechnique(): pec_enum = MitigationTechnique.PEC assert pec_enum.mitigation_function == execute_with_pec @@ -83,110 +110,78 @@ def test_MitigationTechnique(): assert raw_enum.name == "RAW" -def test_basic_settings(): - settings = Settings( - benchmarks=[ - { - "circuit_type": "ghz", - "num_qubits": 2, - "circuit_depth": 999, - } - ], - strategies=[ - { - "technique": "zne", - "scale_noise": fold_global, - "factory": RichardsonFactory([1.0, 2.0, 3.0]), - }, - { - "technique": "zne", - "scale_noise": fold_global, - "factory": RichardsonFactory([1.0, 3.0, 5.0]), - }, - { - "technique": "zne", - "scale_noise": fold_global, - "factory": LinearFactory([1.0, 2.0, 3.0]), - }, - { - "technique": "zne", - "scale_noise": fold_global, - "factory": LinearFactory([1.0, 3.0, 5.0]), - }, - ], - ) - circuits = settings.make_problems() - assert len(circuits) == 1 - ghz_problem = circuits[0] +def test_BenchmarkProblem_make_problems(): + settings = basic_settings + problems = settings.make_problems() + assert len(problems) == 1 + ghz_problem = problems[0] assert len(ghz_problem.circuit) == 2 assert ghz_problem.two_qubit_gate_count == 1 assert ghz_problem.ideal_distribution == {"00": 0.5, "11": 0.5} - lines = str(ghz_problem).split("\n") - ghz_problem_dict = ghz_problem.to_dict() + +def test_BenchmarkProblem_repr(): + settings = basic_settings + problems = settings.make_problems() + problem_repr = repr(problems[0]).replace("'", '"') + assert isinstance(json.loads(problem_repr), dict) + + +def test_BenchmarkProblem_str(): + settings = basic_settings + circuits = settings.make_problems() + problem = circuits[0] + lines = str(problem).split("\n") + problem_dict = problem.to_dict() for line in lines: [title, value] = line.split(":", 1) key = title.lower().replace(" ", "_") value = value.strip() - assert key in ghz_problem_dict - assert value == str(ghz_problem_dict[key]) + assert key in problem_dict + assert value == str(problem_dict[key]) + assert "Ideal distribution: " not in str(problem) + +def test_Strategy_repr(): + settings = basic_settings strategies = settings.make_strategies() - num_strategies = 4 - assert len(strategies) == num_strategies + strategy_repr = repr(strategies[0]).replace("'", '"') + assert isinstance(json.loads(strategy_repr), dict) - strategy_summary = repr(strategies[0]).replace("'", '"') - assert isinstance(json.loads(strategy_summary), dict) - lines = str(strategies[0]).split("\n") - strategy_dict = strategies[0].to_pretty_dict() - for line in lines: +def test_Strategy_str(): + settings = basic_settings + strategies = settings.make_strategies() + + strategy_str = str(strategies[0]) + strategy_pretty_dict = strategies[0].to_pretty_dict() + for line in strategy_str.split("\n"): [title, value] = line.split(":") key = title.lower().replace(" ", "_") value = value.strip() - assert key in strategy_dict - assert value == str(strategy_dict[key]) + assert key in strategy_pretty_dict + assert value == str(strategy_pretty_dict[key]) -def test_settings_pretty_dict(): - settings = Settings( - benchmarks=[ - { - "circuit_type": "ghz", - "num_qubits": 2, - "circuit_depth": 999, - } - ], - strategies=[ - { - "technique": "zne", - "scale_noise": fold_global, - "factory": RichardsonFactory([1.0, 2.0, 3.0]), - }, - { - "technique": "pec", - "representation_function": ( - represent_operation_with_local_depolarizing_noise - ), - "is_qubit_dependent": False, - "noise_level": 0.001, - "num_samples": 200, - }, - ], - ) +def test_Strategy_pretty_dict(): + settings = basic_settings strategies = settings.make_strategies() - _dict = strategies[0].to_dict() - pretty_dict = strategies[0].to_pretty_dict() - if pretty_dict["technique"] == "ZNE": - assert pretty_dict["factory"] == _dict["factory"][:-7] + + strategy_dict = strategies[0].to_dict() + strategy_pretty_dict = strategies[0].to_pretty_dict() + if strategy_pretty_dict["technique"] == "ZNE": + assert strategy_pretty_dict["factory"] == strategy_dict["factory"][:-7] assert ( - pretty_dict["scale_factors"] == str(_dict["scale_factors"])[1:-1] + strategy_pretty_dict["scale_factors"] + == str(strategy_dict["scale_factors"])[1:-1] + ) + elif strategy_pretty_dict["technique"] == "PEC": + assert strategy_pretty_dict["noise_bias"] == strategy_dict.get( + "noise_bias", "N/A" ) - elif pretty_dict["technique"] == "PEC": - assert pretty_dict["noise_bias"] == _dict.get("noise_bias", "N/A") assert ( - pretty_dict["representation_function"] - == _dict["representation_function"][25:] + strategy_pretty_dict["representation_function"] + == strategy_dict["representation_function"][25:] ) From 9a6586ffe25bb63effc42f05e531a646eaf2d6a7 Mon Sep 17 00:00:00 2001 From: Vladimir Kozhukalov Date: Tue, 31 Oct 2023 22:52:26 -0500 Subject: [PATCH 03/10] Install strenum package StrEnum class is available in the standard library since 3.11. --- mitiq/calibration/calibrator.py | 3 ++- requirements.txt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mitiq/calibration/calibrator.py b/mitiq/calibration/calibrator.py index d54968292d..5301e331a4 100644 --- a/mitiq/calibration/calibrator.py +++ b/mitiq/calibration/calibrator.py @@ -4,13 +4,14 @@ # LICENSE file in the root directory of this source tree. import warnings -from enum import StrEnum, auto +from enum import auto from operator import itemgetter from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union, cast import cirq import numpy as np import numpy.typing as npt +from strenum import StrEnum from tabulate import tabulate from mitiq import ( diff --git a/requirements.txt b/requirements.txt index 66da07439d..f7c903cfa9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ numpy>=1.22.0 scipy>=1.5.0,<=1.11.3 cirq>=1.0.0,<1.3.0 tabulate +strenum From bd0daba1334640f94afd81c96b485676caa36d29 Mon Sep 17 00:00:00 2001 From: Vladimir Kozhukalov Date: Tue, 31 Oct 2023 23:08:11 -0500 Subject: [PATCH 04/10] Fix types for calibration module --- mitiq/calibration/calibrator.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mitiq/calibration/calibrator.py b/mitiq/calibration/calibrator.py index 5301e331a4..d60ba8ac8f 100644 --- a/mitiq/calibration/calibrator.py +++ b/mitiq/calibration/calibrator.py @@ -68,7 +68,7 @@ def add_result( self.ideal[strategy.id, problem.id] = ideal_val @staticmethod - def _performance_str(noisy_error: float, mitigated_error: float): + def _performance_str(noisy_error: float, mitigated_error: float) -> str: """Get human readable performance representaion.""" return ( f"{'✔' if mitigated_error < noisy_error else '✘'}\n" @@ -79,7 +79,7 @@ def _performance_str(noisy_error: float, mitigated_error: float): def _get_errors( self, strategy_id: int, problem_id: int - ) -> Tuple[str, float, float]: + ) -> Tuple[float, float]: """Get errors for a given strategy/problem combination. Returns: @@ -293,7 +293,7 @@ def get_cost(self) -> Dict[str, int]: "ideal_executions": ideal, } - def run(self, log: OutputForm = None) -> None: + def run(self, log: Optional[OutputForm] = None) -> None: """Runs all the circuits required for calibration.""" if not self.results.is_missing_data(): self.results.reset_data() @@ -331,7 +331,8 @@ def run(self, log: OutputForm = None) -> None: self.results.log_results_cartesian() else: raise ValueError( - f"log parameter must be one of: {', '.join(OutputForm._member_names_)}" + "log parameter must be one of: " + f"{', '.join(OutputForm._member_names_)}" ) def best_strategy(self) -> Strategy: From 010fecd7bce9cfaba2f022d4b46bcbbf1185bda3 Mon Sep 17 00:00:00 2001 From: Misty Wahl <82074193+Misty-W@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:56:14 -0800 Subject: [PATCH 05/10] Update with Nate's suggestions on imports, stay compatible with 3.9 --- dev_requirements.txt | 1 - mitiq/calibration/calibrator.py | 9 ++++----- requirements.txt | 1 - 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/dev_requirements.txt b/dev_requirements.txt index 9bbb1161d2..1cd0b45239 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -16,7 +16,6 @@ black==22.10 mypy==1.0.0 isort==5.12.0 types-tabulate -mock==5.1.0 # Documentation and examples. Sphinx==5.2.3 diff --git a/mitiq/calibration/calibrator.py b/mitiq/calibration/calibrator.py index d60ba8ac8f..25cad55494 100644 --- a/mitiq/calibration/calibrator.py +++ b/mitiq/calibration/calibrator.py @@ -4,14 +4,13 @@ # LICENSE file in the root directory of this source tree. import warnings -from enum import auto +from enum import auto, Enum from operator import itemgetter from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union, cast import cirq import numpy as np import numpy.typing as npt -from strenum import StrEnum from tabulate import tabulate from mitiq import ( @@ -34,7 +33,7 @@ class MissingResultsError(Exception): pass -class OutputForm(StrEnum): +class OutputForm(str, Enum): flat = auto() cartesian = auto() @@ -120,10 +119,10 @@ def log_results_flat(self) -> None: │ Two qubit gate count: 1 │ Scale method: fold_global │ Improvement factor: 0.9369 │ └──────────────────────────┴──────────────────────────────┴────────────────────────────┘ """ # noqa: E501 - table: List[List[str | float]] = [] + table: List[List[Union[str, float]]] = [] headers: List[str] = ["benchmark", "strategy", "performance"] for problem in self.problems: - row_group: List[List[str | float]] = [] + row_group: List[List[Union[str, float]]] = [] for strategy in self.strategies: nerr, merr = self._get_errors(strategy.id, problem.id) row_group.append( diff --git a/requirements.txt b/requirements.txt index f7c903cfa9..66da07439d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,3 @@ numpy>=1.22.0 scipy>=1.5.0,<=1.11.3 cirq>=1.0.0,<1.3.0 tabulate -strenum From ec0b6edebaf1f7fb12b9cda526395bb9eab20892 Mon Sep 17 00:00:00 2001 From: Misty Wahl <82074193+Misty-W@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:58:43 -0800 Subject: [PATCH 06/10] Use `unittest.mock` --- mitiq/calibration/tests/test_calibration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mitiq/calibration/tests/test_calibration.py b/mitiq/calibration/tests/test_calibration.py index 4b20580907..6b836d976b 100644 --- a/mitiq/calibration/tests/test_calibration.py +++ b/mitiq/calibration/tests/test_calibration.py @@ -10,7 +10,7 @@ import cirq import numpy as np import pytest -from mock import MagicMock, call +from unittest.mock import MagicMock, call from mitiq import SUPPORTED_PROGRAM_TYPES, Executor, MeasurementResult from mitiq.benchmarks import generate_rb_circuits From e272364ec3b503df8abfb99cf52f8805143814f7 Mon Sep 17 00:00:00 2001 From: Misty Wahl <82074193+Misty-W@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:42:50 -0800 Subject: [PATCH 07/10] Fix import order --- mitiq/calibration/calibrator.py | 2 +- mitiq/calibration/tests/test_calibration.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mitiq/calibration/calibrator.py b/mitiq/calibration/calibrator.py index 25cad55494..6f829c273e 100644 --- a/mitiq/calibration/calibrator.py +++ b/mitiq/calibration/calibrator.py @@ -4,7 +4,7 @@ # LICENSE file in the root directory of this source tree. import warnings -from enum import auto, Enum +from enum import Enum, auto from operator import itemgetter from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union, cast diff --git a/mitiq/calibration/tests/test_calibration.py b/mitiq/calibration/tests/test_calibration.py index 6b836d976b..bbf3eeede7 100644 --- a/mitiq/calibration/tests/test_calibration.py +++ b/mitiq/calibration/tests/test_calibration.py @@ -6,11 +6,11 @@ """Tests for the Clifford data regression top-level API.""" import re from functools import partial +from unittest.mock import MagicMock, call import cirq import numpy as np import pytest -from unittest.mock import MagicMock, call from mitiq import SUPPORTED_PROGRAM_TYPES, Executor, MeasurementResult from mitiq.benchmarks import generate_rb_circuits From cd14a6db03c0a1f49707da0c1dcea13e89d025ac Mon Sep 17 00:00:00 2001 From: Misty Wahl <82074193+Misty-W@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:05:18 -0800 Subject: [PATCH 08/10] Update `OutputForm` to have consistent naming --- mitiq/calibration/calibrator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mitiq/calibration/calibrator.py b/mitiq/calibration/calibrator.py index 6f829c273e..987dd4b168 100644 --- a/mitiq/calibration/calibrator.py +++ b/mitiq/calibration/calibrator.py @@ -34,8 +34,8 @@ class MissingResultsError(Exception): class OutputForm(str, Enum): - flat = auto() - cartesian = auto() + flat = "flat" + cartesian = "cartesian" class ExperimentResults: From 3a150cc3336531874737b62d4fc3ba50c8998d23 Mon Sep 17 00:00:00 2001 From: Misty Wahl <82074193+Misty-W@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:32:16 -0800 Subject: [PATCH 09/10] Update mitiq/calibration/calibrator.py --- mitiq/calibration/calibrator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mitiq/calibration/calibrator.py b/mitiq/calibration/calibrator.py index 987dd4b168..10217972c6 100644 --- a/mitiq/calibration/calibrator.py +++ b/mitiq/calibration/calibrator.py @@ -4,7 +4,7 @@ # LICENSE file in the root directory of this source tree. import warnings -from enum import Enum, auto +from enum import Enum from operator import itemgetter from typing import Callable, Dict, List, Optional, Sequence, Tuple, Union, cast From 54c142765a4ef154980e75b061e3ee9df8af01c1 Mon Sep 17 00:00:00 2001 From: nate stemen Date: Tue, 5 Dec 2023 16:19:15 -0800 Subject: [PATCH 10/10] update tutorial with new calibration interface --- docs/source/examples/calibration-tutorial.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/examples/calibration-tutorial.md b/docs/source/examples/calibration-tutorial.md index 113a6a6549..f6edf05751 100644 --- a/docs/source/examples/calibration-tutorial.md +++ b/docs/source/examples/calibration-tutorial.md @@ -16,14 +16,14 @@ kernelspec: -This tutorial helps answer the question: "What quantum error mitigation technique should I use for my problem?". +This tutorial helps answer the question: "What quantum error mitigation technique should I use for my problem?". The newly introduced `mitiq.calibration` module helps answer that in an optimized way, through `Benchmarks` and `Strategies`. More specifically, this tutorial covers: - Getting started with Mitiq's calibration module with ZNE - Use Qiskit noisy simulator with `FakeJakarta` as backend -- Run calibration with some special settings, `RBSettings`, using the `cal.run(log=True)` option +- Run calibration with some special settings, `RBSettings`, and logging the results ## Getting started with Mitiq @@ -110,12 +110,12 @@ We import from the calibration module the key ingredients to use `mitiq.calibrat Currently `mitiq.calibration` supports ZNE as a technique to calibrate from, tuning different scale factors, extrapolation methods and circuit scaling methods. -Let's run the calibration using an ad-hoc RBSettings and using the `log=True` option in order to print the list of experiments run. +Let's run the calibration using an ad-hoc RBSettings while logging the results for comparison. - benchmarks: Circuit type: "rb" - strategies: use various "zne" strategies, testing various "scale_noise" methods -(such as `mitiq.zne.scaling.folding.fold_global`, `mitiq.zne.scaling.folding.fold_gates_at_random`, and `mitiq.zne.scaling.folding.fold_all`), -and ZNE factories for extrapolation (such as `mitiq.zne.inference.RichardsonFactory` and `mitiq.zne.inference.LinearFactory`) + (such as `mitiq.zne.scaling.folding.fold_global`, `mitiq.zne.scaling.folding.fold_gates_at_random`, and `mitiq.zne.scaling.folding.fold_all`), + and ZNE factories for extrapolation (such as `mitiq.zne.inference.RichardsonFactory` and `mitiq.zne.inference.LinearFactory`) ```{code-cell} ipython3 RBSettings = Settings( @@ -189,17 +189,17 @@ RBSettings = Settings( "scale_noise": fold_all, "factory": LinearFactory([1.0, 3.0, 5.0]), }, - + ], ) ``` ```{code-cell} ipython3 cal = Calibrator(execute_calibration, frontend="qiskit", settings=RBSettings) -cal.run(log=True) +cal.run(log="flat") ``` -As you can see above, several experiments were run, and each one has either a red cross (❌) or a green check (✅) to signal whether the error mitigation experiment obtained an expectation value that is better than the non-mitigated one. +As you can see above, several experiments were run, and each one has either a cross (✘) or a check (✔) to signal whether the error mitigation experiment obtained an expectation value that is better than the non-mitigated one. ```{code-cell} ipython3 calibrated_mitigated=execute_with_mitigation(circuit, execute_circuit, calibrator=cal)