qxmt 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. qxmt/__init__.py +45 -0
  2. qxmt/configs.py +70 -0
  3. qxmt/constants.py +24 -0
  4. qxmt/datasets/__init__.py +7 -0
  5. qxmt/datasets/builder.py +233 -0
  6. qxmt/datasets/dummy/__init__.py +5 -0
  7. qxmt/datasets/dummy/linear.py +26 -0
  8. qxmt/datasets/raw_preprocess/__init__.py +0 -0
  9. qxmt/datasets/raw_preprocess/sampling.py +22 -0
  10. qxmt/datasets/schema.py +32 -0
  11. qxmt/datasets/transform/__init__.py +0 -0
  12. qxmt/datasets/transform/reduction_by_pca.py +37 -0
  13. qxmt/decorators.py +10 -0
  14. qxmt/devices/__init__.py +5 -0
  15. qxmt/devices/base.py +32 -0
  16. qxmt/devices/builder.py +23 -0
  17. qxmt/devices/device_info.py +40 -0
  18. qxmt/evaluation/__init__.py +12 -0
  19. qxmt/evaluation/base.py +54 -0
  20. qxmt/evaluation/defaults.py +68 -0
  21. qxmt/evaluation/evaluation.py +131 -0
  22. qxmt/exceptions.py +42 -0
  23. qxmt/experiment/__init__.py +4 -0
  24. qxmt/experiment/experiment.py +528 -0
  25. qxmt/experiment/schema.py +31 -0
  26. qxmt/feature_maps/__init__.py +3 -0
  27. qxmt/feature_maps/base.py +68 -0
  28. qxmt/feature_maps/pennylane/__init__.py +15 -0
  29. qxmt/feature_maps/pennylane/defaults.py +81 -0
  30. qxmt/generators/__init__.py +5 -0
  31. qxmt/generators/description.py +71 -0
  32. qxmt/generators/prompts.py +21 -0
  33. qxmt/kernels/__init__.py +3 -0
  34. qxmt/kernels/base.py +153 -0
  35. qxmt/kernels/pennylane/__init__.py +5 -0
  36. qxmt/kernels/pennylane/fidelity_kernel.py +38 -0
  37. qxmt/logger.py +11 -0
  38. qxmt/models/__init__.py +10 -0
  39. qxmt/models/base.py +121 -0
  40. qxmt/models/builder.py +81 -0
  41. qxmt/models/qsvm.py +35 -0
  42. qxmt/types.py +6 -0
  43. qxmt/utils/__init__.py +21 -0
  44. qxmt/utils/github.py +117 -0
  45. qxmt/utils/yaml.py +102 -0
  46. qxmt/visualization/__init__.py +14 -0
  47. qxmt/visualization/graph_settings.py +9 -0
  48. qxmt/visualization/plot_dataset.py +75 -0
  49. qxmt/visualization/plot_metrics.py +126 -0
  50. qxmt/visualization/plot_model_performance.py +172 -0
  51. qxmt-0.0.0.dist-info/LICENSE +21 -0
  52. qxmt-0.0.0.dist-info/METADATA +149 -0
  53. qxmt-0.0.0.dist-info/RECORD +54 -0
  54. qxmt-0.0.0.dist-info/WHEEL +4 -0
qxmt/__init__.py ADDED
@@ -0,0 +1,45 @@
1
+ from qxmt.configs import (
2
+ DatasetConfig,
3
+ DeviceConfig,
4
+ EvaluationConfig,
5
+ ExperimentConfig,
6
+ FeatureMapConfig,
7
+ KernelConfig,
8
+ ModelConfig,
9
+ PathConfig,
10
+ )
11
+ from qxmt.exceptions import (
12
+ ExperimentNotInitializedError,
13
+ ExperimentRunSettingError,
14
+ InputShapeError,
15
+ InvalidFileExtensionError,
16
+ InvalidModelNameError,
17
+ InvalidPlatformError,
18
+ InvalidQunatumDeviceError,
19
+ JsonEncodingError,
20
+ ModelSettingError,
21
+ ReproductionError,
22
+ )
23
+ from qxmt.experiment.experiment import Experiment
24
+
25
+ __all__ = [
26
+ "ExperimentNotInitializedError",
27
+ "ExperimentRunSettingError",
28
+ "InputShapeError",
29
+ "InvalidFileExtensionError",
30
+ "InvalidModelNameError",
31
+ "InvalidPlatformError",
32
+ "InvalidQunatumDeviceError",
33
+ "JsonEncodingError",
34
+ "ModelSettingError",
35
+ "ReproductionError",
36
+ "Experiment",
37
+ "ExperimentConfig",
38
+ "DatasetConfig",
39
+ "DeviceConfig",
40
+ "EvaluationConfig",
41
+ "FeatureMapConfig",
42
+ "KernelConfig",
43
+ "ModelConfig",
44
+ "PathConfig",
45
+ ]
qxmt/configs.py ADDED
@@ -0,0 +1,70 @@
1
+ from pathlib import Path
2
+ from typing import Any, Literal, Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from qxmt.constants import MODULE_HOME
7
+
8
+
9
+ class PathConfig(BaseModel):
10
+ data: Path | str
11
+ label: Path | str
12
+
13
+ def model_post_init(self, __context: dict[str, Any]) -> None:
14
+ if not Path(self.data).is_absolute():
15
+ self.data = MODULE_HOME / self.data
16
+
17
+ if not Path(self.label).is_absolute():
18
+ self.label = MODULE_HOME / self.label
19
+
20
+
21
+ class DatasetConfig(BaseModel):
22
+ type: Literal["file", "generate"]
23
+ path: PathConfig
24
+ random_seed: int
25
+ test_size: float = Field(ge=0.0, le=1.0)
26
+ features: Optional[list[str]] = None
27
+ raw_preprocess_logic: Optional[dict[str, Any]] = None
28
+ transform_logic: Optional[dict[str, Any]] = None
29
+
30
+
31
+ class DeviceConfig(BaseModel):
32
+ platform: str
33
+ name: str
34
+ n_qubits: int
35
+ shots: Optional[int] = None
36
+
37
+
38
+ class FeatureMapConfig(BaseModel):
39
+ module_name: str
40
+ implement_name: str
41
+ params: Optional[dict[str, Any]] = None
42
+
43
+
44
+ class KernelConfig(BaseModel):
45
+ module_name: str
46
+ implement_name: str
47
+ params: Optional[dict[str, Any]] = None
48
+
49
+
50
+ class ModelConfig(BaseModel):
51
+ name: str
52
+ file_name: str
53
+ params: dict[str, Any]
54
+ feature_map: Optional[FeatureMapConfig] = None
55
+ kernel: Optional[KernelConfig] = None
56
+
57
+
58
+ class EvaluationConfig(BaseModel):
59
+ default_metrics: list[str]
60
+
61
+
62
+ class ExperimentConfig(BaseModel):
63
+ path: Path | str = ""
64
+ description: str = ""
65
+ dataset: DatasetConfig
66
+ device: DeviceConfig
67
+ feature_map: Optional[FeatureMapConfig] = None
68
+ kernel: Optional[KernelConfig] = None
69
+ model: ModelConfig
70
+ evaluation: EvaluationConfig
qxmt/constants.py ADDED
@@ -0,0 +1,24 @@
1
+ from pathlib import Path
2
+ from typing import Any
3
+
4
+ import pennylane as qml
5
+ import pytz
6
+
7
+ MODULE_HOME: Path = Path(__file__).resolve().parents[1]
8
+ MODULE_SRC: Path = Path(__file__).resolve().parents[0]
9
+
10
+ DEFAULT_N_JOBS = 3
11
+
12
+ DEFAULT_EXP_DIRC: Path = MODULE_HOME / "experiments"
13
+ DEFAULT_EXP_DB_FILE: Path = Path("experiment.json")
14
+
15
+ SUPPORTED_PLATFORMS: list[str] = ["pennylane"]
16
+ PENNYLANE_DEVICES: tuple[Any, ...] = (qml.devices.Device, qml.Device, qml.QubitDevice)
17
+
18
+ DEFAULT_MODEL_NAME: str = "model.pkl"
19
+
20
+ DEFAULT_METRICS_NAME: list[str] = ["accuracy", "precision", "recall", "f1_score"]
21
+
22
+ LLM_MODEL_PATH = "microsoft/Phi-3-mini-128k-instruct"
23
+
24
+ TZ: pytz.BaseTzInfo = pytz.timezone("Asia/Tokyo")
@@ -0,0 +1,7 @@
1
+ from qxmt.datasets.builder import DatasetBuilder
2
+ from qxmt.datasets.schema import Dataset
3
+
4
+ __all__ = [
5
+ "DatasetBuilder",
6
+ "Dataset",
7
+ ]
@@ -0,0 +1,233 @@
1
+ import inspect
2
+ from logging import Logger
3
+ from typing import Callable, Optional, get_type_hints
4
+
5
+ import numpy as np
6
+ from sklearn.model_selection import train_test_split
7
+
8
+ from qxmt.configs import ExperimentConfig
9
+ from qxmt.datasets.dummy import generate_linear_separable_data
10
+ from qxmt.datasets.schema import Dataset
11
+ from qxmt.logger import set_default_logger
12
+ from qxmt.utils import load_object_from_yaml
13
+
14
+ RAW_DATA_TYPE = np.ndarray
15
+ RAW_LABEL_TYPE = np.ndarray
16
+ RAW_DATASET_TYPE = tuple[RAW_DATA_TYPE, RAW_LABEL_TYPE]
17
+ PROCESSCED_DATASET_TYPE = tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]
18
+
19
+ LOGGER = set_default_logger(__name__)
20
+
21
+
22
+ class DatasetBuilder:
23
+ def __init__(self, config: ExperimentConfig, logger: Logger = LOGGER) -> None:
24
+ self.config: ExperimentConfig = config
25
+ self.logger: Logger = logger
26
+
27
+ if self.config.dataset.raw_preprocess_logic is not None:
28
+ raw_preprocess_logic = load_object_from_yaml(self.config.dataset.raw_preprocess_logic)
29
+ self._validate_raw_preprocess_logic(raw_preprocess_logic, self.logger)
30
+ self.custom_raw_preprocess: Optional[Callable] = raw_preprocess_logic
31
+ else:
32
+ self.custom_raw_preprocess = None
33
+
34
+ if self.config.dataset.transform_logic is not None:
35
+ transform_logic = load_object_from_yaml(self.config.dataset.transform_logic)
36
+ self._validate_transform_logic(transform_logic, self.logger)
37
+ self.custom_transform: Optional[Callable] = transform_logic
38
+ else:
39
+ self.custom_transform = None
40
+
41
+ @staticmethod
42
+ def _validate_raw_preprocess_logic(raw_preprocess_logic: Callable, logger: Logger) -> None:
43
+ """Validate the custom raw preprocess function.
44
+
45
+ Args:
46
+ raw_preprocess_logic (Callable): custom raw preprocess function
47
+ logger (Logger): logger for output messages
48
+
49
+ Raises:
50
+ ValueError: argment lenght of the custom raw preprocess function is less than 2
51
+ ValueError: return type of the custom raw preprocess function is not a tuple of numpy arrays
52
+ ValueError: argument type of the custom raw preprocess function is not numpy array
53
+ """
54
+ type_hint_dict = get_type_hints(raw_preprocess_logic)
55
+ parameter_dict = inspect.signature(raw_preprocess_logic).parameters
56
+
57
+ # check argment length. -1 means return type
58
+ if len(type_hint_dict) - 1 != len(parameter_dict):
59
+ logger.warning(
60
+ "All arguments of the custom raw preprocess function assigned to the type hint."
61
+ "Input and return type validation will be skipped."
62
+ )
63
+ return
64
+ elif len(type_hint_dict) - 1 < 2:
65
+ raise ValueError("The custom raw preprocess function must have at least 2 arguments (X, y).")
66
+
67
+ # check argument type and return type
68
+ for arg_name, arg_type in type_hint_dict.items():
69
+ if (arg_name == "return") and (arg_type != RAW_DATASET_TYPE):
70
+ raise ValueError(
71
+ "The return type of the custom raw preprocess function must be a tuple of numpy arrays."
72
+ )
73
+ # [TODO]: Handle anthor data types
74
+ # elif (arg_name != "return") and (arg_type != RAW_DATA_TYPE):
75
+ # raise ValueError(f'The arguments of the custom raw preprocess function must be "{RAW_DATA_TYPE}".')
76
+
77
+ @staticmethod
78
+ def _validate_transform_logic(transform_logic: Callable, logger: Logger) -> None:
79
+ """Validate the custom transform function.
80
+
81
+ Args:
82
+ transform_logic (Callable): custom transform function
83
+ logger (Logger): logger for output messages
84
+
85
+ Raises:
86
+ ValueError: argment lenght of the custom transform function is less than 4
87
+ ValueError: return type of the custom transform function is not a tuple of numpy arrays
88
+ ValueError: argument type of the custom transform function is not numpy array
89
+
90
+ """
91
+ type_hint_dict = get_type_hints(transform_logic)
92
+ parameter_dict = inspect.signature(transform_logic).parameters
93
+
94
+ # check argment length. -1 means return type
95
+ if len(type_hint_dict) - 1 != len(parameter_dict):
96
+ logger.warning(
97
+ "All arguments of the custom raw preprocess function assigned to the type hint."
98
+ "Input and return type validation will be skipped."
99
+ )
100
+ return
101
+ elif len(type_hint_dict) - 1 < 4:
102
+ raise ValueError(
103
+ "The custom transform function must have at least 4 arguments (X_train, y_train, X_test, y_test)."
104
+ )
105
+
106
+ # check argument type and return type
107
+ for arg_name, arg_type in type_hint_dict.items():
108
+ if (arg_name == "return") and (arg_type != PROCESSCED_DATASET_TYPE):
109
+ raise ValueError("The return type of the custom transform function must be a tuple of numpy arrays.")
110
+ # # [TODO]: Handle athor data types
111
+ # elif (arg_name != "return") and (arg_type != RAW_DATA_TYPE):
112
+ # raise ValueError(f'The arguments of the custom transform function must be "{RAW_DATA_TYPE}".')
113
+
114
+ def load(self) -> RAW_DATASET_TYPE:
115
+ """Load the dataset from the path defined in config.
116
+
117
+ Returns:
118
+ RAW_DATASET_TYPE: features and labels of the dataset
119
+ """
120
+ if self.config.dataset.type == "file":
121
+ # [TODO]: Implement other file formats
122
+ X = np.load(self.config.dataset.path.data, allow_pickle=True)
123
+ y = np.load(self.config.dataset.path.label, allow_pickle=True)
124
+ elif self.config.dataset.type == "generate":
125
+ # [TODO]: Implement other dataset generation methods
126
+ X, y = generate_linear_separable_data()
127
+ else:
128
+ raise ValueError(f"Invalid dataset type: {self.config.dataset.type}")
129
+
130
+ return X, y
131
+
132
+ def default_raw_preprocess(self, X: np.ndarray, y: np.ndarray) -> RAW_DATASET_TYPE:
133
+ """Default raw preprocess method. This method does not apply any preprocess.
134
+
135
+ Args:
136
+ X (np.ndarray): raw features of the dataset
137
+ y (np.ndarray): raw labels of the dataset
138
+
139
+ Returns:
140
+ RAW_DATASET_TYPE: raw features and labels of the dataset
141
+ """
142
+ return X, y
143
+
144
+ def raw_preprocess(self, X: np.ndarray, y: np.ndarray) -> RAW_DATASET_TYPE:
145
+ """Preprocess the raw dataset. This process executes before splitting the dataset.
146
+ ex) filtering, data sampling, etc.
147
+
148
+ Args:
149
+ X (np.ndarray): raw features of the dataset
150
+ y (np.ndarray): raw labels of the dataset
151
+
152
+ Returns:
153
+ RAW_DATASET_TYPE: preprocessed features and labels of the dataset
154
+ """
155
+ if self.custom_raw_preprocess is not None:
156
+ return self.custom_raw_preprocess(X, y)
157
+ else:
158
+ return self.default_raw_preprocess(X, y)
159
+
160
+ def split(self, X: np.ndarray, y: np.ndarray) -> PROCESSCED_DATASET_TYPE:
161
+ """Split the dataset into train and test sets.
162
+ Test set size is defined in the config.
163
+
164
+ Args:
165
+ X (np.ndarray): raw features of the dataset
166
+ y (np.ndarray): raw labels of the dataset
167
+
168
+ Returns:
169
+ PROCESSCED_DATASET_TYPE: train and test split of dataset (features and labels)
170
+ """
171
+ X_train, X_test, y_train, y_test = train_test_split(
172
+ X, y, test_size=self.config.dataset.test_size, random_state=self.config.dataset.random_seed
173
+ )
174
+
175
+ return X_train, y_train, X_test, y_test
176
+
177
+ def default_transform(
178
+ self,
179
+ X_train: np.ndarray,
180
+ y_train: np.ndarray,
181
+ X_test: np.ndarray,
182
+ y_test: np.ndarray,
183
+ ) -> PROCESSCED_DATASET_TYPE:
184
+ """Default transform method. This method does not apply any transformation.
185
+
186
+ Args:
187
+ X_train (np.ndarray): raw features of the training data
188
+ y_train (np.ndarray): raw labels of the training data
189
+ X_test (np.ndarray): raw features of the test data
190
+ y_test (np.ndarray): raw labels of the test data
191
+
192
+ Returns:
193
+ PROCESSCED_DATASET_TYPE: train and test split of dataset (features and labels)
194
+ """
195
+ return X_train, y_train, X_test, y_test
196
+
197
+ def transform(
198
+ self,
199
+ X_train: np.ndarray,
200
+ y_train: np.ndarray,
201
+ X_test: np.ndarray,
202
+ y_test: np.ndarray,
203
+ ) -> PROCESSCED_DATASET_TYPE:
204
+ """Transform the dataset.
205
+ ex) feature scaling, dimension reduction, etc.
206
+
207
+ Args:
208
+ X_train (np.ndarray): raw features of the training data
209
+ y_train (np.ndarray): raw labels of the training data
210
+ X_test (np.ndarray): raw features of the test data
211
+ y_test (np.ndarray): raw labels of the test data
212
+
213
+ Returns:
214
+ PROCESSCED_DATASET_TYPE: transformed train and test split of dataset (features and labels)
215
+ """
216
+ if self.custom_transform is not None:
217
+ return self.custom_transform(X_train, y_train, X_test, y_test)
218
+ else:
219
+ return self.default_transform(X_train, y_train, X_test, y_test)
220
+
221
+ def build(self) -> Dataset:
222
+ X, y = self.load()
223
+ X, y = self.raw_preprocess(X, y)
224
+ X_train, y_train, X_test, y_test = self.split(X, y)
225
+ X_train_trs, y_train_trs, X_test_trs, y_test_trs = self.transform(X_train, y_train, X_test, y_test)
226
+
227
+ return Dataset(
228
+ X_train=X_train_trs,
229
+ y_train=y_train_trs,
230
+ X_test=X_test_trs,
231
+ y_test=y_test_trs,
232
+ config=self.config.dataset,
233
+ )
@@ -0,0 +1,5 @@
1
+ from qxmt.datasets.dummy.linear import generate_linear_separable_data
2
+
3
+ __all__ = [
4
+ "generate_linear_separable_data",
5
+ ]
@@ -0,0 +1,26 @@
1
+ import numpy as np
2
+
3
+
4
+ def generate_linear_separable_data(
5
+ n_samples: int = 100,
6
+ n_features: int = 2,
7
+ noise: float = 0.1,
8
+ scale: float = 1.0,
9
+ ) -> tuple[np.ndarray, np.ndarray]:
10
+ """Generate random linear separable data.
11
+
12
+ Args:
13
+ n_samples (int, optional): sample size. Defaults to 100.
14
+ n_features (int, optional): dimension of feature. Defaults to 2.
15
+ noise (float, optional): noise level. Defaults to 0.1.
16
+ scale (float, optional): scale of data. Defaults to 1.0.
17
+
18
+ Returns:
19
+ tuple[np.ndarray, np.ndarray]: generated data and label
20
+ """
21
+ X = scale * np.random.randn(n_samples, n_features)
22
+ w = scale * np.random.randn(n_features)
23
+ bias = scale * np.random.randn(n_samples) * noise
24
+ y = np.sign(X @ w + bias)
25
+
26
+ return X, y
File without changes
@@ -0,0 +1,22 @@
1
+ import numpy as np
2
+
3
+ from qxmt.datasets.builder import RAW_DATASET_TYPE
4
+
5
+
6
+ def sampling_by_each_class(X: np.ndarray, y: np.ndarray, n_samples: int) -> RAW_DATASET_TYPE:
7
+ """Data sampling by each class
8
+
9
+ Args:
10
+ X (np.ndarray): input data
11
+ y (np.ndarray): label of input data
12
+
13
+ Returns:
14
+ RAW_DATASET_TYPE: sampled data
15
+ """
16
+ labels = [0, 1]
17
+ # n_sample = 100
18
+ y = np.array([int(label) for label in y])
19
+ indices = np.where(np.isin(y, labels))[0]
20
+ X, y = X[indices][:n_samples], y[indices][:n_samples]
21
+
22
+ return X, y
@@ -0,0 +1,32 @@
1
+ from typing import Annotated, Any
2
+
3
+ import numpy as np
4
+ from pydantic import BaseModel, PlainSerializer, PlainValidator
5
+
6
+ from qxmt.configs import DatasetConfig
7
+
8
+
9
+ def validate(v: Any) -> np.ndarray:
10
+ if isinstance(v, np.ndarray):
11
+ return v
12
+ else:
13
+ raise TypeError(f"Expected numpy array, got {type(v)}")
14
+
15
+
16
+ def serialize(v: np.ndarray) -> list[list[float]]:
17
+ return v.tolist()
18
+
19
+
20
+ DataArray = Annotated[
21
+ np.ndarray,
22
+ PlainValidator(validate),
23
+ PlainSerializer(serialize),
24
+ ]
25
+
26
+
27
+ class Dataset(BaseModel):
28
+ X_train: DataArray
29
+ y_train: DataArray
30
+ X_test: DataArray
31
+ y_test: DataArray
32
+ config: DatasetConfig
File without changes
@@ -0,0 +1,37 @@
1
+ import numpy as np
2
+ from sklearn.decomposition import PCA
3
+ from sklearn.preprocessing import StandardScaler
4
+
5
+ from qxmt.datasets.builder import PROCESSCED_DATASET_TYPE
6
+
7
+
8
+ def dimension_reduction_by_pca(
9
+ X_train: np.ndarray,
10
+ y_train: np.ndarray,
11
+ X_test: np.ndarray,
12
+ y_test: np.ndarray,
13
+ n_components: int,
14
+ ) -> PROCESSCED_DATASET_TYPE:
15
+ """Dimension reduction by PCA
16
+
17
+ Args:
18
+ X_train (np.ndarray): numpy array of training data
19
+ y_train (np.ndarray): numpy array of training label
20
+ X_test (np.ndarray): numpy array of testing data
21
+ y_test (np.ndarray): numpy array of testing label
22
+ n_components (int): number of components to keep
23
+
24
+ Returns:
25
+ PROCESSCED_DATASET_TYPE: tuple of processed dataset
26
+ """
27
+ scaler = StandardScaler()
28
+ scaler.fit(X_train)
29
+ x_train_scaled = scaler.transform(X_train)
30
+ x_test_scaled = scaler.transform(X_test)
31
+
32
+ pca = PCA(n_components=n_components)
33
+ pca.fit(x_train_scaled)
34
+ X_train_pca = pca.transform(x_train_scaled)
35
+ X_test_pca = pca.transform(x_test_scaled)
36
+
37
+ return X_train_pca, y_train, X_test_pca, y_test
qxmt/decorators.py ADDED
@@ -0,0 +1,10 @@
1
+ from typing import Any, Callable
2
+
3
+
4
+ def notify_long_running(func: Callable) -> Callable:
5
+ def wrapper(*args: dict, **kwargs: dict) -> Any:
6
+ print(f'Executing "{func.__name__}". This may take some time...')
7
+ result = func(*args, **kwargs)
8
+ return result
9
+
10
+ return wrapper
@@ -0,0 +1,5 @@
1
+ from qxmt.devices.base import BaseDevice
2
+ from qxmt.devices.builder import DeviceBuilder
3
+ from qxmt.devices.device_info import get_number_of_qubits, get_platform_from_device
4
+
5
+ __all__ = ["DeviceBuilder", "BaseDevice", "get_number_of_qubits", "get_platform_from_device"]
qxmt/devices/base.py ADDED
@@ -0,0 +1,32 @@
1
+ from typing import Any, Optional
2
+
3
+ from qxmt.exceptions import InvalidPlatformError
4
+
5
+
6
+ class BaseDevice:
7
+ def __init__(self, platform: str, name: str, n_qubits: int, shots: Optional[int]) -> None:
8
+ self.platform = platform
9
+ self.name = name
10
+ self.n_qubits = n_qubits
11
+ self.shots = shots
12
+ self._set_device()
13
+
14
+ def __call__(self) -> Any:
15
+ return self.device
16
+
17
+ def _set_device(self) -> None:
18
+ """Set quantum device.
19
+
20
+ Raises:
21
+ InvalidPlatformError: platform is not implemented.
22
+ """
23
+ if self.platform == "pennylane":
24
+ from pennylane import qml
25
+
26
+ self.device = qml.device(
27
+ name=self.name,
28
+ wires=self.n_qubits,
29
+ shots=self.shots,
30
+ )
31
+ else:
32
+ raise InvalidPlatformError(f'"{self.platform}" is not implemented.')
@@ -0,0 +1,23 @@
1
+ from qxmt.configs import DeviceConfig
2
+ from qxmt.devices.base import BaseDevice
3
+
4
+
5
+ class DeviceBuilder:
6
+ def __init__(
7
+ self,
8
+ config: DeviceConfig,
9
+ ) -> None:
10
+ self.config: DeviceConfig = config
11
+
12
+ def build(self) -> BaseDevice:
13
+ """Build a quantum device. it can be a general-purpose device overseeing multiple platforms.
14
+
15
+ Returns:
16
+ BaseDevice: General-purpose device overseeing multiple platforms
17
+ """
18
+ return BaseDevice(
19
+ platform=self.config.platform,
20
+ name=self.config.name,
21
+ n_qubits=self.config.n_qubits,
22
+ shots=self.config.shots,
23
+ )
@@ -0,0 +1,40 @@
1
+ from qxmt.constants import PENNYLANE_DEVICES
2
+ from qxmt.devices.base import BaseDevice
3
+ from qxmt.exceptions import InvalidQunatumDeviceError
4
+ from qxmt.types import QuantumDeviceType
5
+
6
+
7
+ def get_platform_from_device(device: BaseDevice | QuantumDeviceType) -> str:
8
+ """Get the platform name from the device.
9
+
10
+ Args:
11
+ device (BaseDevice | QuantumDeviceType): quantum device
12
+
13
+ Returns:
14
+ str: platform name
15
+ """
16
+ if isinstance(device, BaseDevice):
17
+ return device.platform
18
+
19
+ if isinstance(device, PENNYLANE_DEVICES):
20
+ return "pennylane"
21
+ else:
22
+ raise InvalidQunatumDeviceError(f"Device {device} is not supported.")
23
+
24
+
25
+ def get_number_of_qubits(device: BaseDevice | QuantumDeviceType) -> int:
26
+ """Get the number of qubits from the device.
27
+
28
+ Args:
29
+ device (BaseDevice | QuantumDeviceType): quantum device
30
+
31
+ Returns:
32
+ int: number of qubits
33
+ """
34
+ if isinstance(device, BaseDevice):
35
+ return device.n_qubits
36
+
37
+ if isinstance(device, PENNYLANE_DEVICES):
38
+ return len(device.wires)
39
+ else:
40
+ raise InvalidQunatumDeviceError(f"Device {device} is not supported.")
@@ -0,0 +1,12 @@
1
+ from qxmt.evaluation.base import BaseMetric
2
+ from qxmt.evaluation.defaults import Accuracy, F1Score, Precision, Recall
3
+ from qxmt.evaluation.evaluation import Evaluation
4
+
5
+ __all__ = [
6
+ "BaseMetric",
7
+ "Accuracy",
8
+ "Recall",
9
+ "Precision",
10
+ "F1Score",
11
+ "Evaluation",
12
+ ]