Skip to content

Commit

Permalink
Fix various problems with QML
Browse files Browse the repository at this point in the history
Co-authored-by: Florian Kiwit <[email protected]>
Co-authored-by: Maroua Marso <[email protected]>
  • Loading branch information
2 people authored and Philipp Ross committed Sep 21, 2023
1 parent befc866 commit 08728a6
Show file tree
Hide file tree
Showing 16 changed files with 264 additions and 195 deletions.
6 changes: 3 additions & 3 deletions src/modules/applications/QML/circuits/CircuitCardinality.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ def get_parameter_options(self) -> dict:
},
}

def get_default_submodule(self, library_option: str) -> LibraryQiskit:
if library_option == "LibraryQiskit":
def get_default_submodule(self, option: str) -> LibraryQiskit:
if option == "LibraryQiskit":
return LibraryQiskit()
else:
raise NotImplementedError(f"Library Option {library_option} not implemented")
raise NotImplementedError(f"Option {option} not implemented")

class Config(TypedDict):
"""
Expand Down
6 changes: 3 additions & 3 deletions src/modules/applications/QML/circuits/CircuitCopula.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ def get_parameter_options(self) -> dict:
},
}

def get_default_submodule(self, library_option: str) -> LibraryQiskit:
if library_option == "LibraryQiskit":
def get_default_submodule(self, option: str) -> LibraryQiskit:
if option == "LibraryQiskit":
return LibraryQiskit()
else:
raise NotImplementedError(f"Library Option {library_option} not implemented")
raise NotImplementedError(f"Option {option} not implemented")

class Config(TypedDict):
"""
Expand Down
7 changes: 3 additions & 4 deletions src/modules/applications/QML/circuits/CircuitStandard.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ def get_parameter_options(self) -> dict:
}
}

def get_default_submodule(self, library_option: str) -> LibraryQiskit:
if library_option == "LibraryQiskit":
def get_default_submodule(self, option: str) -> LibraryQiskit:
if option == "LibraryQiskit":
return LibraryQiskit()
else:
raise NotImplementedError(f"Library Option {library_option} not implemented")
raise NotImplementedError(f"Option {option} not implemented")

class Config(TypedDict):
"""
Expand All @@ -100,7 +100,6 @@ def generate_gate_sequence(self, input_data: dict, config: Config) -> dict:
n_registers = input_data["n_registers"]
n_qubits = input_data["n_qubits"]
depth = config["depth"]
n = n_qubits // n_registers

gate_sequence = []

Expand Down
18 changes: 10 additions & 8 deletions src/modules/applications/QML/data_handler/ContinuousData.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from utils import start_time_measurement, end_time_measurement
from typing import TypedDict, Union
import logging

import numpy as np

import pkg_resources
import logging

from utils import start_time_measurement, end_time_measurement

from modules.applications.QML.data_handler.DataHandler import *
from modules.applications.QML.transformations.MinMax import MinMax
Expand All @@ -43,6 +43,7 @@ def __init__(self):
self.dataset = None
self.n_registers = None
self.gc = None
self.n_qubits = None

@staticmethod
def get_requirements() -> list[dict]:
Expand All @@ -59,13 +60,13 @@ def get_requirements() -> list[dict]:
}
]

def get_default_submodule(self, transform_option: str) -> Union[PIT, MinMax]:
if transform_option == "MinMax":
def get_default_submodule(self, option: str) -> Union[PIT, MinMax]:
if option == "MinMax":
self.transformation = MinMax()
elif transform_option == "PIT":
elif option == "PIT":
self.transformation = PIT()
else:
raise NotImplementedError(f"Transformation Option {transform_option} not implemented")
raise NotImplementedError(f"Transformation Option {option} not implemented")
return self.transformation

def get_parameter_options(self) -> dict:
Expand Down Expand Up @@ -133,7 +134,8 @@ def evaluate(self, solution: list, **kwargs) -> (float, float):
"""
Calculate KL in original space.
:param solution: A dictionary-like object containing the solution data, including histogram_generated_original and histogram_train_original.
:param solution: A dictionary-like object containing the solution data,
including histogram_generated_original and histogram_train_original.
:type solution: list
:return: KL for the generated samples and the time it took to calculate it.
Expand Down
6 changes: 4 additions & 2 deletions src/modules/applications/QML/data_handler/DataHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

class DataHandler(Core, ABC):
"""
The task of the DataHandler module is to translate the application’s data and problem specification into preproccesed format.
The task of the DataHandler module is to translate the application’s data
and problem specification into preproccesed format.
"""

def __init__(self, name):
Expand Down Expand Up @@ -196,7 +197,8 @@ def evaluate(self, solution: any) -> (dict, float):
@staticmethod
def tb_to_pd(logdir: str, rep: str) -> None:
"""
Converts TensorBoard event files in the specified log directory into a pandas DataFrame and saves it as a pickle file.
Converts TensorBoard event files in the specified log directory
into a pandas DataFrame and saves it as a pickle file.
:param logdir: path to the log directory containing TensorBoard event files
:type logdir: str
Expand Down
55 changes: 33 additions & 22 deletions src/modules/applications/QML/data_handler/DiscreteData.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,22 @@

class DiscreteData(DataHandler):
"""
A data handler for discrete datasets with cardinality constraints. This class creates a dataset with a cardinality constraint and provides
A data handler for discrete datasets with cardinality constraints.
This class creates a dataset with a cardinality constraint and provides
methods for generalisation metrics computing and evaluation.
"""

def __init__(self):
super().__init__("")
self.submodule_options = ["CircuitCardinality"]
self.dataset_name = None
self.n_registers = None
self.n_qubits = None
self.train_size = None
self.histogram_train = None
self.histogram_solution = None
self.generalization_metrics = None
self.samples = None


@staticmethod
def get_requirements() -> list[dict]:
Expand All @@ -51,13 +58,13 @@ def get_requirements() -> list[dict]:
}
]

def get_default_submodule(self, circuit_option: str) -> CircuitCardinality:
def get_default_submodule(self, option: str) -> CircuitCardinality:

if circuit_option == "CircuitCardinality":
if option == "CircuitCardinality":
return CircuitCardinality()
else:
raise NotImplementedError(
f"Circuit Option {circuit_option} not implemented")
f"Circuit Option {option} not implemented")

def get_parameter_options(self) -> dict:
"""
Expand Down Expand Up @@ -106,17 +113,17 @@ def data_load(self, gen_mod: dict, config: Config) -> dict:
:return: Must always return the mapped problem and the time it took to create the mapping
:rtype: tuple(any, float)
"""
self.dataset_name = "Cardinality_Constraint"
dataset_name = "Cardinality_Constraint"
self.n_qubits = gen_mod["n_qubits"]
self.train_size = config["train_size"]
self.num_ones = self.n_qubits // 2
num_ones = self.n_qubits // 2

# Generate all possible binary permutations of length n_qubits using NumPy arrays
search_space = np.array(list(itertools.product([0, 1], repeat=self.n_qubits)))
search_space = np.apply_along_axis(lambda x: ''.join(str(bit) for bit in x), 1, search_space)

# Filter the binary permutations based on the cardinality constraint using np.char.count
cardinality_constraint = np.char.count(search_space, '1') == self.num_ones
cardinality_constraint = np.char.count(search_space, '1') == num_ones
solution_set = search_space[cardinality_constraint]

# Use np.random.choice to select the desired number of rows randomly
Expand All @@ -126,23 +133,23 @@ def data_load(self, gen_mod: dict, config: Config) -> dict:

# Create the histogram solution data
self.histogram_solution = np.zeros(2 ** self.n_qubits)
self.solution_set = np.array([int(i, 2) for i in solution_set])
self.histogram_solution[self.solution_set] = 1 / len(self.solution_set)
solution_set = np.array([int(i, 2) for i in solution_set])
self.histogram_solution[solution_set] = 1 / len(solution_set)

# Create the histogram training data
self.histogram_train = np.zeros(2 ** self.n_qubits)
self.train_set = np.array([int(i, 2) for i in train_set])
self.histogram_train[self.train_set] = 1 / len(self.train_set)
train_set = np.array([int(i, 2) for i in train_set])
self.histogram_train[train_set] = 1 / len(train_set)

self.generalization_metrics = MetricsGeneralization(
train_set=self.train_set,
train_set=train_set,
train_size=self.train_size,
solution_set=self.solution_set,
solution_set=solution_set,
n_qubits=self.n_qubits,
)

application_config = {
"dataset_name": self.dataset_name,
"dataset_name": dataset_name,
"n_qubits": self.n_qubits,
"histogram_solution": self.histogram_solution,
"histogram_train": self.histogram_train,
Expand All @@ -157,7 +164,8 @@ def generalisation(self, solution: list) -> (dict, float):
:param solution: A list representing the solution to be evaluated.
:type solution: list
:return: A tuple containing a dictionary of generalization metrics and the execution time in seconds.
:return: A tuple containing a dictionary of generalization metrics
and the execution time in seconds.
:rtype: tuple
"""
start = start_time_measurement()
Expand All @@ -169,19 +177,22 @@ def generalisation(self, solution: list) -> (dict, float):

def evaluate(self, solution: list) -> (dict, float):
"""
Evaluates a given solution and calculates the histogram of generated samples and the minimum KL divergence value.
Evaluates a given solution and calculates the histogram of generated samples
and the minimum KL divergence value.
:param solution: A dictionary-like object containing the solution data, including generated samples and KL divergence values.
:param solution: A dictionary-like object containing the solution data,
including generated samples and KL divergence values.
:type solution: list
:return: A tuple containing a dictionary with the histogram of generated samples and the minimum KL divergence value,
and the time it took to evaluate the solution in milliseconds.
:return: A tuple containing a dictionary with the histogram of generated samples
and the minimum KL divergence value, and the time it took to evaluate
the solution in milliseconds.
:rtype: (dict, float)
"""
start = start_time_measurement()
self.samples = solution["best_sample"]
self.n_shots = np.sum(self.samples)
n_shots = np.sum(self.samples)

histogram_generated = np.asarray(self.samples) / self.n_shots
histogram_generated = np.asarray(self.samples) / n_shots
histogram_generated[histogram_generated == 0] = 1e-8

KL_list = solution["KL"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

import math

import numpy as np
from typing import Tuple, Dict
import numpy as np


class MetricsGeneralization:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from utils import start_time_measurement, end_time_measurement
from typing import Union
from utils import start_time_measurement, end_time_measurement

from modules.applications.Application import *
from modules.applications.QML.QML import QML
Expand All @@ -36,15 +36,6 @@ def __init__(self):
super().__init__("GenerativeModeling")
self.submodule_options = ["Continuous Data", "Discrete Data"]
self.data = None
self.dataset_name = None
self.n_registers = None
self.true_probability = None
self.cc = None
self.gc = None
self.bitstrings = None
self.bitstrings_train = None
self.constrained_bitstring = None
self.processed_solution = None

@staticmethod
def get_requirements() -> list[dict]:
Expand Down
20 changes: 16 additions & 4 deletions src/modules/applications/QML/libraries/Library.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
# limitations under the License.

from abc import ABC, abstractmethod
from typing import TypedDict
import logging
from typing import TypedDict
from qiskit import QuantumCircuit
from qiskit.providers import Backend

from utils import start_time_measurement, end_time_measurement

from modules.Core import Core
Expand Down Expand Up @@ -62,8 +65,11 @@ def preprocess(self, input_data: dict, config: Config, **kwargs):

output = self.sequence_to_circuit(input_data)
backend = self.select_backend(config["backend"])
output["execute_circuit"] = self.get_execute_circuit(output["circuit"], backend, config["backend"],
config["n_shots"])
output["execute_circuit"] = self.get_execute_circuit(
output["circuit"],
backend,
config["backend"],
config["n_shots"])
output["backend"] = config["backend"]
output["n_shots"] = config["n_shots"]
logging.info("Library created")
Expand Down Expand Up @@ -92,6 +98,12 @@ def postprocess(self, input_data: dict, config: dict, **kwargs):
def sequence_to_circuit(self, input_data):
pass

@staticmethod
@abstractmethod
def get_execute_circuit(circuit: QuantumCircuit, backend: Backend, config: str, n_shots: int):
pass

@staticmethod
@abstractmethod
def get_execute_circuit(self):
def select_backend(config: str):
pass
Loading

0 comments on commit 08728a6

Please sign in to comment.