-
Notifications
You must be signed in to change notification settings - Fork 27
First implementation free fermion benchmark #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
3bdc933
17038d4
d5d66f0
93f18af
999a8c9
4fcbc85
c798d13
c021504
3002128
0b36f37
963ebd3
8d960bc
53f3e4d
95b3990
46a822f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Copyright 2022 The QUARK Authors. All Rights Reserved. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """ | ||
| Module containing all simulation applications | ||
| """ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| from typing import TypedDict | ||
|
|
||
| from qiskit_aer import AerSimulator as QiskitAS | ||
|
|
||
| from src.modules.applications.simulation.backends.backend_input import BackendInput | ||
| from src.modules.core import Core | ||
| from src.modules.applications.simulation.backends.backend_result import BackendResult | ||
| from src.utils import start_time_measurement, end_time_measurement | ||
|
|
||
|
|
||
| class AerSimulator(Core): | ||
|
|
||
| def __init__(self): | ||
| """ | ||
| Initializes the AerSimulator class. | ||
| """ | ||
| super().__init__("AerSimulator") | ||
| self.submodule_options = [] | ||
|
|
||
| @staticmethod | ||
| def get_requirements() -> list: | ||
| """ | ||
| Returns a list of requirements for the FreeFermion application. | ||
|
|
||
| :returns: A list of dictionaries containing the name and version of required packages | ||
| """ | ||
| return [ | ||
| {"name": "qiskit", "version": "1.3.0"}, | ||
| ] | ||
|
|
||
| def get_default_submodule(self, option: str) -> None: | ||
| """ | ||
| Given an option string by the user, this returns a submodule. | ||
|
|
||
| :param option: String with the chosen submodule | ||
| :return: Module of type Core | ||
| :raises NotImplementedError: If the option is not recognized | ||
| """ | ||
| raise NotImplementedError(f"Submodule Option {option} not implemented") | ||
|
|
||
| def get_parameter_options(self): | ||
| """ | ||
| Returns the parameter options for the application. | ||
| """ | ||
| return { | ||
| "n_shots": { | ||
| "values": [100, 200, 400, 800, 1600], | ||
| "description": "Number of shots?", | ||
| "allow_ranges": False, | ||
| "postproc": int | ||
| } | ||
| } | ||
|
|
||
| class AerSimConfig(TypedDict): | ||
| """ | ||
| """ | ||
| n_shots: int | ||
|
|
||
| def postprocess(self, input_data: BackendInput, config: AerSimConfig, **kwargs) -> tuple[any, float]: | ||
| """ | ||
| Processes data passed to this module from the submodule. | ||
|
|
||
| :param input_data: The input data for postprocessing | ||
| :param config: The configuration dictionary | ||
| :param **kwargs: Additional keyword arguments | ||
| :returns: A tuple containing the processed solution quality and the time taken for evaluation | ||
| """ | ||
| start = start_time_measurement() | ||
| backend = QiskitAS() | ||
| circuits = input_data.circuits | ||
| counts = [backend.run(circuit, shots=config['n_shots']).result().get_counts(circuit) for circuit in circuits] | ||
| results = BackendResult( | ||
| counts=counts, | ||
| n_shots=config['n_shots'] | ||
| ) | ||
| return results, end_time_measurement(start) | ||
|
|
||
| def validate(self, solution) -> tuple[bool, float]: | ||
trvto marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
| Validates the solution. | ||
|
|
||
| :param solution: The solution to validate | ||
| :return: A tuple containing the validity of the solution and the time taken for validation | ||
| """ | ||
| start = start_time_measurement() | ||
|
|
||
| # Check if solution is valid | ||
| if solution is None: | ||
| logging.error(f"Solution not valid") | ||
| return False, end_time_measurement(start) | ||
|
|
||
| logging.info(f"Solution valid") | ||
| return True, end_time_measurement(start) | ||
|
|
||
|
|
||
| def evaluate(self, solution) -> tuple[float, float]: | ||
| """ | ||
| Evaluates the solution. | ||
|
|
||
| :param solution: The solution to evaluate | ||
| :return: A tuple containing the evaluation metric and the time taken for evaluation | ||
| """ | ||
| start = start_time_measurement() | ||
|
|
||
| evaluation_metric = calculate_metric(solution) | ||
|
|
||
| return evaluation_metric, end_time_measurement(start) | ||
|
|
||
|
|
||
| def save(self, path, iter_count) -> None: | ||
| """ | ||
| Saves the application state. | ||
|
|
||
| :param path: The path where the application state should be saved | ||
| :param iter_count: The iteration count | ||
| :returns:None | ||
| """ | ||
| save_your_application(self.application, f"{path}/application.txt") | ||
|
|
||
| def save(self, path: str, iter_count: int) -> None: | ||
| """ | ||
| Save the current application state to a file. | ||
|
|
||
| :param path: The directory path where the file will be saved | ||
| :param iter_count: The iteration count to include in the filename | ||
| """ | ||
| with open(f"{path}/graph_iter_{iter_count}.gpickle", "wb") as file: | ||
| pickle.dump(self.application, file, pickle.HIGHEST_PROTOCOL) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| from dataclasses import dataclass | ||
| from qiskit import QuantumCircuit | ||
|
|
||
|
|
||
| @dataclass | ||
| class BackendInput: | ||
| """ | ||
| Input required for a quantum backend. | ||
| """ | ||
| circuits: list[QuantumCircuit] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| from dataclasses import dataclass | ||
|
|
||
|
|
||
| @dataclass | ||
| class BackendResult: | ||
| """ | ||
| Result returned from a quantum backend. | ||
| """ | ||
| counts: list[dict[str, int]] | ||
| n_shots: int |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Copyright 2022 The QUARK Authors. All Rights Reserved. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """ | ||
| Module for Free Fermion simulations | ||
| """ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| from typing import TypedDict, Union | ||
|
|
||
| import matplotlib.pyplot as plt | ||
| import numpy as np | ||
|
|
||
| from src.modules.applications.simulation.backends.aer_simulator import AerSimulator | ||
| from src.modules.applications.simulation.backends.backend_input import BackendInput | ||
| from src.modules.applications.simulation.backends.backend_result import BackendResult | ||
| from src.modules.applications.simulation.free_fermion.free_fermion_helpers import create_circuit, exact_values, \ | ||
| score_minimal_mean | ||
| from src.modules.applications.simulation.simulation import Simulation | ||
| from src.utils import start_time_measurement, end_time_measurement | ||
|
|
||
|
|
||
| class FreeFermion(Simulation): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think some logging during the simulation would help users and facilitate debugging/evaluation if needed. |
||
|
|
||
| def __init__(self): | ||
| """ | ||
| Initializes the FreeFermion class. | ||
| """ | ||
| super().__init__("FreeFermion") | ||
| self.submodule_options = ["AerSimulator"] | ||
|
|
||
| @staticmethod | ||
| def get_requirements() -> list: | ||
| """ | ||
| Returns a list of requirements for the FreeFermion application. | ||
|
|
||
| :returns: A list of dictionaries containing the name and version of required packages | ||
| """ | ||
| return [ | ||
| {"name": "qiskit", "version": "1.3.0"}, | ||
| {"name": "numpy", "version": "1.26.4"}, | ||
| {"name": "mathplotlib", "version": "3.9.3"}, | ||
| {"name": "qiskit_aer", "version": "0.15.1"}, | ||
| ] | ||
|
|
||
| def get_default_submodule(self, option: str) -> Union[AerSimulator]: | ||
| """ | ||
| Given an option string by the user, this returns a submodule. | ||
|
|
||
| :param option: String with the chosen submodule | ||
| :return: Module of type Core | ||
| :raises NotImplementedError: If the option is not recognized | ||
| """ | ||
| if option == "AerSimulator": | ||
| return AerSimulator() | ||
| else: | ||
| raise NotImplementedError(f"Submodule Option {option} not implemented") | ||
|
|
||
| def get_parameter_options(self): | ||
| """ | ||
| Returns the parameter options for the application. | ||
| """ | ||
| return { | ||
| "L": { | ||
| "values": [2, 4, 6], | ||
| "description": "What lattice size (L x L) to use for the simulation?", | ||
| "allow_ranges": False, | ||
| "postproc": int | ||
| }, | ||
| "trotter_dt": { | ||
| "values": [0.2, 0.4, 0.6], | ||
| "description": "Which time step size?", | ||
| "allow_ranges": False, | ||
| "postproc": float | ||
| }, | ||
| "trotter_n_step": { | ||
| "values": ["1L", "2L"], | ||
| "description": "Number of time steps (in multiples of L value)?", | ||
| "allow_ranges": False, | ||
| "postproc": str | ||
| }, | ||
| } | ||
|
|
||
| class Config(TypedDict): | ||
| """ | ||
| A configuration dictionary for the application. | ||
| """ | ||
| L: int | ||
| trotter_dt: float | ||
| trotter_n_step: str | ||
|
|
||
| def preprocess(self, input_data: any, conf: Config, **kwargs) -> tuple[BackendInput, float]: | ||
| """ | ||
| Generate data that gets passed to the next submodule. | ||
|
|
||
| :param input_data: The input data for preprocessing | ||
| :param conf: The configuration parameters | ||
| :return: A tuple containing the preprocessed output and the time taken for preprocessing | ||
| """ | ||
| start = start_time_measurement() | ||
| lattice_size = conf['L'] | ||
| trotter_n_step = int(conf['trotter_n_step'][:-1]) * lattice_size | ||
| circuits = [create_circuit(lattice_size, lattice_size, conf['trotter_dt'], n) for n in range(trotter_n_step)] | ||
| return BackendInput(circuits), end_time_measurement(start) | ||
|
|
||
| def postprocess(self, input_data: BackendResult, conf: Config, **kwargs) -> tuple[any, float]: | ||
| """ | ||
| Processes data passed to this module from the submodule. | ||
|
|
||
| :param input_data: The input data for postprocessing | ||
| :param conf: The configuration parameters | ||
| :returns: A tuple containing the processed solution quality and the time taken for evaluation | ||
| """ | ||
|
|
||
| start = start_time_measurement() | ||
| counts_per_circuit, n_shots = input_data.counts, input_data.n_shots | ||
| lx, ly, trotter_dt = conf['L'], conf['L'], conf['trotter_dt'] | ||
| trotter_n_step = int(conf['trotter_n_step'][:-1]) * lx | ||
| l_tot = lx * ly | ||
|
|
||
| to_plot: list = [] | ||
| for n in range(trotter_n_step): | ||
| res: float = 0 | ||
| var: float = 0 | ||
| counts = counts_per_circuit[n] | ||
| for s in counts: | ||
| a: float = 0 | ||
| for j in range(l_tot // 2): | ||
| if s[l_tot * 3 // 2 - 1 - j] == '1': | ||
| a += -1 / l_tot | ||
| else: | ||
| a += 1 / l_tot | ||
| if s[l_tot * 3 // 2 - 1 - j - l_tot // 2] == '1': | ||
| a += 1 / l_tot | ||
| else: | ||
| a += -1 / l_tot | ||
| res += a * counts[s] | ||
| var += a ** 2 * counts[s] | ||
| res = res / n_shots | ||
| var = var / n_shots | ||
| to_plot.append([n, res, np.sqrt(var - res ** 2) / np.sqrt(n_shots)]) | ||
|
|
||
| exact_list_array = np.real(np.array(exact_values(trotter_n_step, trotter_dt, lx, ly))) | ||
| to_plot_array: np.array = np.array(to_plot) | ||
| score, score_variance = score_minimal_mean( | ||
| exact_list_array[:, 1] - to_plot_array[:, 1], to_plot_array[:, 2], lx * ly) | ||
| self.metrics.add_metric_batch({"application_score_value": score, "application_score_variance": score_variance, "application_score_unit": "score", | ||
| "application_score_type": "float"}) | ||
| plt.plot(np.array(list(range(len(exact_list_array[:, 1])))), | ||
trvto marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| exact_list_array[:, 1], color="black", label="exact") | ||
| plt.errorbar(to_plot_array[:, 0], to_plot_array[:, 1], yerr=to_plot_array[:, 2], label="simulated") | ||
| plt.title("score=10^" + str(score) + " gates") | ||
| plt.xlabel("Trotter step") | ||
| plt.ylabel("Imbalance") | ||
| plt.legend() | ||
| store_dir = kwargs["store_dir"] | ||
| plt.savefig(f"{store_dir}/simulation_plot.pdf") | ||
| plt.close() | ||
|
|
||
| return score, end_time_measurement(start) | ||
|
|
||
| def save(self, path, iter_count) -> None: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, not needed, right? |
||
| """ | ||
| Saves the application state. | ||
|
|
||
| :param path: The path where the application state should be saved | ||
| :param iter_count: The iteration count | ||
| :returns:None | ||
| """ | ||
| pass | ||
Uh oh!
There was an error while loading. Please reload this page.