Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8,622 changes: 4,330 additions & 4,292 deletions .settings/module_db.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions .settings/requirements_full.txt
Git LFS file not shown
3 changes: 2 additions & 1 deletion src/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ def __init__(self):
{"name": "GenerativeModeling", "class": "GenerativeModeling",
"module": "modules.applications.qml.generative_modeling.generative_modeling"},
{"name": "Classification", "class": "Classification",
"module": "modules.applications.qml.classification.classification"}
"module": "modules.applications.qml.classification.classification"},
{"name": "FreeFermion", "class": "FreeFermion", "module": "modules.applications.simulation.free_fermion.free_fermion"},

]

Expand Down
17 changes: 17 additions & 0 deletions src/modules/applications/simulation/__init__.py
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
"""
Empty file.
128 changes: 128 additions & 0 deletions src/modules/applications/simulation/backends/aer_simulator.py
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]:
"""
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)
10 changes: 10 additions & 0 deletions src/modules/applications/simulation/backends/backend_input.py
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]
10 changes: 10 additions & 0 deletions src/modules/applications/simulation/backends/backend_result.py
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
17 changes: 17 additions & 0 deletions src/modules/applications/simulation/free_fermion/__init__.py
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
"""
162 changes: 162 additions & 0 deletions src/modules/applications/simulation/free_fermion/free_fermion.py
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):
Copy link
Collaborator

Choose a reason for hiding this comment

The 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])))),
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:
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Loading