superquantx 0.1.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.
- superquantx/__init__.py +321 -0
- superquantx/algorithms/__init__.py +55 -0
- superquantx/algorithms/base_algorithm.py +413 -0
- superquantx/algorithms/hybrid_classifier.py +628 -0
- superquantx/algorithms/qaoa.py +406 -0
- superquantx/algorithms/quantum_agents.py +1006 -0
- superquantx/algorithms/quantum_kmeans.py +575 -0
- superquantx/algorithms/quantum_nn.py +544 -0
- superquantx/algorithms/quantum_pca.py +499 -0
- superquantx/algorithms/quantum_svm.py +346 -0
- superquantx/algorithms/vqe.py +553 -0
- superquantx/algorithms.py +863 -0
- superquantx/backends/__init__.py +265 -0
- superquantx/backends/base_backend.py +321 -0
- superquantx/backends/braket_backend.py +420 -0
- superquantx/backends/cirq_backend.py +466 -0
- superquantx/backends/ocean_backend.py +491 -0
- superquantx/backends/pennylane_backend.py +419 -0
- superquantx/backends/qiskit_backend.py +451 -0
- superquantx/backends/simulator_backend.py +455 -0
- superquantx/backends/tket_backend.py +519 -0
- superquantx/circuits.py +447 -0
- superquantx/cli/__init__.py +28 -0
- superquantx/cli/commands.py +528 -0
- superquantx/cli/main.py +254 -0
- superquantx/client.py +298 -0
- superquantx/config.py +326 -0
- superquantx/exceptions.py +287 -0
- superquantx/gates.py +588 -0
- superquantx/logging_config.py +347 -0
- superquantx/measurements.py +702 -0
- superquantx/ml.py +936 -0
- superquantx/noise.py +760 -0
- superquantx/utils/__init__.py +83 -0
- superquantx/utils/benchmarking.py +523 -0
- superquantx/utils/classical_utils.py +575 -0
- superquantx/utils/feature_mapping.py +467 -0
- superquantx/utils/optimization.py +410 -0
- superquantx/utils/quantum_utils.py +456 -0
- superquantx/utils/visualization.py +654 -0
- superquantx/version.py +33 -0
- superquantx-0.1.0.dist-info/METADATA +365 -0
- superquantx-0.1.0.dist-info/RECORD +46 -0
- superquantx-0.1.0.dist-info/WHEEL +4 -0
- superquantx-0.1.0.dist-info/entry_points.txt +2 -0
- superquantx-0.1.0.dist-info/licenses/LICENSE +21 -0
superquantx/config.py
ADDED
@@ -0,0 +1,326 @@
|
|
1
|
+
"""Global configuration for SuperQuantX.
|
2
|
+
|
3
|
+
This module provides configuration management for the SuperQuantX package,
|
4
|
+
including backend settings, default parameters, and runtime options.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import json
|
8
|
+
import logging
|
9
|
+
import os
|
10
|
+
from pathlib import Path
|
11
|
+
from typing import Any, Dict, Optional, Union
|
12
|
+
|
13
|
+
import yaml
|
14
|
+
|
15
|
+
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
class Config:
|
19
|
+
"""Global configuration class for SuperQuantX."""
|
20
|
+
|
21
|
+
def __init__(self) -> None:
|
22
|
+
"""Initialize configuration with defaults."""
|
23
|
+
self._config: Dict[str, Any] = {
|
24
|
+
# Backend configuration
|
25
|
+
"backends": {
|
26
|
+
"default": "auto",
|
27
|
+
"auto_selection_strategy": "performance", # "performance", "cost", "speed", "accuracy"
|
28
|
+
"fallback_backend": "pennylane",
|
29
|
+
"timeout": 300, # seconds
|
30
|
+
"max_retries": 3,
|
31
|
+
},
|
32
|
+
|
33
|
+
# Algorithm defaults
|
34
|
+
"algorithms": {
|
35
|
+
"default_shots": 1024,
|
36
|
+
"optimization_level": 1,
|
37
|
+
"seed": None,
|
38
|
+
"max_iterations": 1000,
|
39
|
+
"convergence_tolerance": 1e-6,
|
40
|
+
},
|
41
|
+
|
42
|
+
# Visualization settings
|
43
|
+
"visualization": {
|
44
|
+
"backend": "matplotlib", # "matplotlib", "plotly", "both"
|
45
|
+
"style": "default",
|
46
|
+
"save_figures": False,
|
47
|
+
"figure_format": "png",
|
48
|
+
"dpi": 150,
|
49
|
+
},
|
50
|
+
|
51
|
+
# Benchmarking configuration
|
52
|
+
"benchmarks": {
|
53
|
+
"default_runs": 5,
|
54
|
+
"include_classical": True,
|
55
|
+
"save_results": True,
|
56
|
+
"results_dir": "benchmark_results",
|
57
|
+
},
|
58
|
+
|
59
|
+
# Logging configuration
|
60
|
+
"logging": {
|
61
|
+
"level": "INFO",
|
62
|
+
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
63
|
+
"file": None, # Log file path, None for console only
|
64
|
+
},
|
65
|
+
|
66
|
+
# Cache settings
|
67
|
+
"cache": {
|
68
|
+
"enabled": True,
|
69
|
+
"directory": ".superquantx_cache",
|
70
|
+
"max_size_mb": 1000,
|
71
|
+
"ttl_hours": 24,
|
72
|
+
},
|
73
|
+
|
74
|
+
# Platform-specific settings
|
75
|
+
"platforms": {
|
76
|
+
"ibm": {
|
77
|
+
"token": None,
|
78
|
+
"hub": "ibm-q",
|
79
|
+
"group": "open",
|
80
|
+
"project": "main",
|
81
|
+
},
|
82
|
+
"braket": {
|
83
|
+
"s3_folder": None,
|
84
|
+
"device_arn": None,
|
85
|
+
},
|
86
|
+
"azure": {
|
87
|
+
"resource_id": None,
|
88
|
+
"location": None,
|
89
|
+
},
|
90
|
+
"dwave": {
|
91
|
+
"token": None,
|
92
|
+
"solver": None,
|
93
|
+
},
|
94
|
+
"rigetti": {
|
95
|
+
"api_key": None,
|
96
|
+
"user_id": None,
|
97
|
+
},
|
98
|
+
},
|
99
|
+
|
100
|
+
# Development settings
|
101
|
+
"development": {
|
102
|
+
"debug": False,
|
103
|
+
"profile": False,
|
104
|
+
"warnings": True,
|
105
|
+
},
|
106
|
+
}
|
107
|
+
|
108
|
+
# Load configuration from files
|
109
|
+
self._load_config_files()
|
110
|
+
|
111
|
+
# Override with environment variables
|
112
|
+
self._load_environment_variables()
|
113
|
+
|
114
|
+
def _load_config_files(self) -> None:
|
115
|
+
"""Load configuration from YAML/JSON files."""
|
116
|
+
config_paths = [
|
117
|
+
Path.home() / ".superquantx" / "config.yaml",
|
118
|
+
Path.home() / ".superquantx" / "config.yml",
|
119
|
+
Path.home() / ".superquantx" / "config.json",
|
120
|
+
Path.cwd() / "superquantx_config.yaml",
|
121
|
+
Path.cwd() / "superquantx_config.yml",
|
122
|
+
Path.cwd() / "superquantx_config.json",
|
123
|
+
]
|
124
|
+
|
125
|
+
for config_path in config_paths:
|
126
|
+
if config_path.exists():
|
127
|
+
try:
|
128
|
+
with open(config_path) as f:
|
129
|
+
if config_path.suffix.lower() in [".yaml", ".yml"]:
|
130
|
+
file_config = yaml.safe_load(f)
|
131
|
+
else:
|
132
|
+
file_config = json.load(f)
|
133
|
+
|
134
|
+
if file_config:
|
135
|
+
self._merge_config(file_config)
|
136
|
+
logger.info(f"Loaded configuration from {config_path}")
|
137
|
+
|
138
|
+
except Exception as e:
|
139
|
+
logger.warning(f"Failed to load config from {config_path}: {e}")
|
140
|
+
|
141
|
+
def _load_environment_variables(self) -> None:
|
142
|
+
"""Load configuration from environment variables."""
|
143
|
+
env_mappings = {
|
144
|
+
"SUPERQUANTX_DEFAULT_BACKEND": ("backends", "default"),
|
145
|
+
"SUPERQUANTX_DEFAULT_SHOTS": ("algorithms", "default_shots"),
|
146
|
+
"SUPERQUANTX_LOG_LEVEL": ("logging", "level"),
|
147
|
+
"SUPERQUANTX_CACHE_ENABLED": ("cache", "enabled"),
|
148
|
+
"SUPERQUANTX_DEBUG": ("development", "debug"),
|
149
|
+
|
150
|
+
# Platform tokens
|
151
|
+
"IBM_QUANTUM_TOKEN": ("platforms", "ibm", "token"),
|
152
|
+
"AWS_BRAKET_S3_FOLDER": ("platforms", "braket", "s3_folder"),
|
153
|
+
"AZURE_QUANTUM_RESOURCE_ID": ("platforms", "azure", "resource_id"),
|
154
|
+
"DWAVE_API_TOKEN": ("platforms", "dwave", "token"),
|
155
|
+
"RIGETTI_API_KEY": ("platforms", "rigetti", "api_key"),
|
156
|
+
}
|
157
|
+
|
158
|
+
for env_var, config_path in env_mappings.items():
|
159
|
+
value = os.getenv(env_var)
|
160
|
+
if value is not None:
|
161
|
+
self._set_nested_value(config_path, value)
|
162
|
+
|
163
|
+
def _merge_config(self, new_config: Dict[str, Any]) -> None:
|
164
|
+
"""Merge new configuration with existing configuration."""
|
165
|
+
def merge_dicts(base: Dict, update: Dict) -> Dict:
|
166
|
+
for key, value in update.items():
|
167
|
+
if key in base and isinstance(base[key], dict) and isinstance(value, dict):
|
168
|
+
base[key] = merge_dicts(base[key], value)
|
169
|
+
else:
|
170
|
+
base[key] = value
|
171
|
+
return base
|
172
|
+
|
173
|
+
self._config = merge_dicts(self._config, new_config)
|
174
|
+
|
175
|
+
def _set_nested_value(self, path: tuple, value: Any) -> None:
|
176
|
+
"""Set a nested configuration value."""
|
177
|
+
current = self._config
|
178
|
+
for key in path[:-1]:
|
179
|
+
if key not in current:
|
180
|
+
current[key] = {}
|
181
|
+
current = current[key]
|
182
|
+
|
183
|
+
# Type conversion for common types
|
184
|
+
if isinstance(value, str):
|
185
|
+
if value.lower() in ["true", "false"]:
|
186
|
+
value = value.lower() == "true"
|
187
|
+
elif value.isdigit():
|
188
|
+
value = int(value)
|
189
|
+
elif "." in value and value.replace(".", "").isdigit():
|
190
|
+
value = float(value)
|
191
|
+
|
192
|
+
current[path[-1]] = value
|
193
|
+
|
194
|
+
def get(self, key: str, default: Any = None) -> Any:
|
195
|
+
"""Get configuration value using dot notation."""
|
196
|
+
keys = key.split(".")
|
197
|
+
current = self._config
|
198
|
+
|
199
|
+
try:
|
200
|
+
for k in keys:
|
201
|
+
current = current[k]
|
202
|
+
return current
|
203
|
+
except (KeyError, TypeError):
|
204
|
+
return default
|
205
|
+
|
206
|
+
def set(self, key: str, value: Any) -> None:
|
207
|
+
"""Set configuration value using dot notation."""
|
208
|
+
keys = key.split(".")
|
209
|
+
current = self._config
|
210
|
+
|
211
|
+
for k in keys[:-1]:
|
212
|
+
if k not in current:
|
213
|
+
current[k] = {}
|
214
|
+
current = current[k]
|
215
|
+
|
216
|
+
current[keys[-1]] = value
|
217
|
+
|
218
|
+
def update(self, updates: Dict[str, Any]) -> None:
|
219
|
+
"""Update configuration with a dictionary of values."""
|
220
|
+
self._merge_config(updates)
|
221
|
+
|
222
|
+
def save(self, path: Optional[Union[str, Path]] = None) -> None:
|
223
|
+
"""Save current configuration to file."""
|
224
|
+
if path is None:
|
225
|
+
path = Path.home() / ".superquantx" / "config.yaml"
|
226
|
+
else:
|
227
|
+
path = Path(path)
|
228
|
+
|
229
|
+
# Create directory if it doesn't exist
|
230
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
231
|
+
|
232
|
+
try:
|
233
|
+
with open(path, "w") as f:
|
234
|
+
yaml.dump(self._config, f, default_flow_style=False, indent=2)
|
235
|
+
logger.info(f"Configuration saved to {path}")
|
236
|
+
except Exception as e:
|
237
|
+
logger.error(f"Failed to save configuration to {path}: {e}")
|
238
|
+
|
239
|
+
def reset(self) -> None:
|
240
|
+
"""Reset configuration to defaults."""
|
241
|
+
self.__init__()
|
242
|
+
|
243
|
+
def to_dict(self) -> Dict[str, Any]:
|
244
|
+
"""Get configuration as dictionary."""
|
245
|
+
return self._config.copy()
|
246
|
+
|
247
|
+
def print_config(self) -> None:
|
248
|
+
"""Print current configuration."""
|
249
|
+
import pprint
|
250
|
+
pprint.pprint(self._config, indent=2)
|
251
|
+
|
252
|
+
def validate(self) -> bool:
|
253
|
+
"""Validate current configuration."""
|
254
|
+
# Add validation logic here
|
255
|
+
return True
|
256
|
+
|
257
|
+
# Global configuration instance
|
258
|
+
config = Config()
|
259
|
+
|
260
|
+
def configure(
|
261
|
+
backend: Optional[str] = None,
|
262
|
+
shots: Optional[int] = None,
|
263
|
+
debug: Optional[bool] = None,
|
264
|
+
**kwargs
|
265
|
+
) -> None:
|
266
|
+
"""Configure SuperQuantX settings.
|
267
|
+
|
268
|
+
Args:
|
269
|
+
backend: Default backend to use
|
270
|
+
shots: Default number of shots for quantum circuits
|
271
|
+
debug: Enable debug mode
|
272
|
+
**kwargs: Additional configuration parameters
|
273
|
+
|
274
|
+
"""
|
275
|
+
updates = {}
|
276
|
+
|
277
|
+
if backend is not None:
|
278
|
+
updates["backends.default"] = backend
|
279
|
+
|
280
|
+
if shots is not None:
|
281
|
+
updates["algorithms.default_shots"] = shots
|
282
|
+
|
283
|
+
if debug is not None:
|
284
|
+
updates["development.debug"] = debug
|
285
|
+
|
286
|
+
# Handle additional keyword arguments
|
287
|
+
for key, value in kwargs.items():
|
288
|
+
updates[key] = value
|
289
|
+
|
290
|
+
# Apply updates
|
291
|
+
for key, value in updates.items():
|
292
|
+
config.set(key, value)
|
293
|
+
|
294
|
+
# Setup logging if level changed
|
295
|
+
if "logging.level" in updates:
|
296
|
+
setup_logging()
|
297
|
+
|
298
|
+
def setup_logging() -> None:
|
299
|
+
"""Setup logging configuration."""
|
300
|
+
level = getattr(logging, config.get("logging.level", "INFO").upper())
|
301
|
+
format_str = config.get("logging.format")
|
302
|
+
log_file = config.get("logging.file")
|
303
|
+
|
304
|
+
# Configure root logger
|
305
|
+
logging.basicConfig(
|
306
|
+
level=level,
|
307
|
+
format=format_str,
|
308
|
+
filename=log_file,
|
309
|
+
filemode="a" if log_file else None,
|
310
|
+
force=True,
|
311
|
+
)
|
312
|
+
|
313
|
+
def get_platform_config(platform: str) -> Dict[str, Any]:
|
314
|
+
"""Get configuration for a specific platform.
|
315
|
+
|
316
|
+
Args:
|
317
|
+
platform: Platform name (e.g., 'ibm', 'braket', 'azure')
|
318
|
+
|
319
|
+
Returns:
|
320
|
+
Platform configuration dictionary
|
321
|
+
|
322
|
+
"""
|
323
|
+
return config.get(f"platforms.{platform}", {})
|
324
|
+
|
325
|
+
# Initialize logging on import
|
326
|
+
setup_logging()
|
@@ -0,0 +1,287 @@
|
|
1
|
+
"""Custom exceptions for SuperQuantX.
|
2
|
+
|
3
|
+
This module defines custom exceptions used throughout the SuperQuantX library
|
4
|
+
to provide clear error messages and proper error handling for quantum-agentic
|
5
|
+
AI research workflows.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Optional
|
9
|
+
|
10
|
+
|
11
|
+
class SuperQuantXError(Exception):
|
12
|
+
"""Base exception for all SuperQuantX errors."""
|
13
|
+
|
14
|
+
def __init__(self, message: str, error_code: Optional[str] = None) -> None:
|
15
|
+
"""Initialize SuperQuantX base exception.
|
16
|
+
|
17
|
+
Args:
|
18
|
+
message: Human-readable error message
|
19
|
+
error_code: Optional error code for programmatic handling
|
20
|
+
|
21
|
+
"""
|
22
|
+
super().__init__(message)
|
23
|
+
self.message = message
|
24
|
+
self.error_code = error_code
|
25
|
+
|
26
|
+
def __str__(self) -> str:
|
27
|
+
"""Return string representation of the error."""
|
28
|
+
if self.error_code:
|
29
|
+
return f"[{self.error_code}] {self.message}"
|
30
|
+
return self.message
|
31
|
+
|
32
|
+
|
33
|
+
class BackendError(SuperQuantXError):
|
34
|
+
"""Raised when quantum backend operations fail."""
|
35
|
+
|
36
|
+
def __init__(
|
37
|
+
self,
|
38
|
+
message: str,
|
39
|
+
backend_name: Optional[str] = None,
|
40
|
+
original_error: Optional[Exception] = None,
|
41
|
+
) -> None:
|
42
|
+
"""Initialize backend error.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
message: Error description
|
46
|
+
backend_name: Name of the backend that failed
|
47
|
+
original_error: Original exception that caused this error
|
48
|
+
|
49
|
+
"""
|
50
|
+
if backend_name:
|
51
|
+
full_message = f"Backend '{backend_name}': {message}"
|
52
|
+
else:
|
53
|
+
full_message = message
|
54
|
+
|
55
|
+
super().__init__(full_message, error_code="BACKEND_ERROR")
|
56
|
+
self.backend_name = backend_name
|
57
|
+
self.original_error = original_error
|
58
|
+
|
59
|
+
|
60
|
+
class BackendNotAvailableError(BackendError):
|
61
|
+
"""Raised when a required quantum backend is not available."""
|
62
|
+
|
63
|
+
def __init__(self, backend_name: str, missing_dependencies: Optional[list[str]] = None) -> None:
|
64
|
+
"""Initialize backend not available error.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
backend_name: Name of the unavailable backend
|
68
|
+
missing_dependencies: List of missing dependency packages
|
69
|
+
|
70
|
+
"""
|
71
|
+
if missing_dependencies:
|
72
|
+
deps = ", ".join(missing_dependencies)
|
73
|
+
message = f"Backend not available. Missing dependencies: {deps}. Install with: pip install superquantx[{backend_name}]"
|
74
|
+
else:
|
75
|
+
message = f"Backend not available. Install with: pip install superquantx[{backend_name}]"
|
76
|
+
|
77
|
+
super().__init__(message, backend_name, error_code="BACKEND_NOT_AVAILABLE")
|
78
|
+
self.missing_dependencies = missing_dependencies or []
|
79
|
+
|
80
|
+
|
81
|
+
class AlgorithmError(SuperQuantXError):
|
82
|
+
"""Raised when quantum algorithm operations fail."""
|
83
|
+
|
84
|
+
def __init__(
|
85
|
+
self,
|
86
|
+
message: str,
|
87
|
+
algorithm_name: Optional[str] = None,
|
88
|
+
step: Optional[str] = None,
|
89
|
+
) -> None:
|
90
|
+
"""Initialize algorithm error.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
message: Error description
|
94
|
+
algorithm_name: Name of the algorithm that failed
|
95
|
+
step: Specific step where the error occurred (e.g., 'fit', 'predict')
|
96
|
+
|
97
|
+
"""
|
98
|
+
if algorithm_name and step:
|
99
|
+
full_message = f"{algorithm_name}.{step}(): {message}"
|
100
|
+
elif algorithm_name:
|
101
|
+
full_message = f"{algorithm_name}: {message}"
|
102
|
+
else:
|
103
|
+
full_message = message
|
104
|
+
|
105
|
+
super().__init__(full_message, error_code="ALGORITHM_ERROR")
|
106
|
+
self.algorithm_name = algorithm_name
|
107
|
+
self.step = step
|
108
|
+
|
109
|
+
|
110
|
+
class NotFittedError(AlgorithmError):
|
111
|
+
"""Raised when an algorithm is used before being fitted."""
|
112
|
+
|
113
|
+
def __init__(self, algorithm_name: str) -> None:
|
114
|
+
"""Initialize not fitted error.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
algorithm_name: Name of the algorithm that needs fitting
|
118
|
+
|
119
|
+
"""
|
120
|
+
message = "This algorithm instance is not fitted yet. Call 'fit' with appropriate arguments before using this method."
|
121
|
+
super().__init__(message, algorithm_name, error_code="NOT_FITTED")
|
122
|
+
|
123
|
+
|
124
|
+
class InvalidParameterError(SuperQuantXError):
|
125
|
+
"""Raised when invalid parameters are provided to algorithms or backends."""
|
126
|
+
|
127
|
+
def __init__(
|
128
|
+
self,
|
129
|
+
parameter_name: str,
|
130
|
+
parameter_value: Any,
|
131
|
+
expected_type: Optional[str] = None,
|
132
|
+
valid_values: Optional[list[Any]] = None,
|
133
|
+
) -> None:
|
134
|
+
"""Initialize invalid parameter error.
|
135
|
+
|
136
|
+
Args:
|
137
|
+
parameter_name: Name of the invalid parameter
|
138
|
+
parameter_value: The invalid value provided
|
139
|
+
expected_type: Expected type description
|
140
|
+
valid_values: List of valid values (for enumerated parameters)
|
141
|
+
|
142
|
+
"""
|
143
|
+
if valid_values:
|
144
|
+
valid_str = ", ".join(map(str, valid_values))
|
145
|
+
message = f"Invalid value for parameter '{parameter_name}': {parameter_value}. Valid values are: {valid_str}"
|
146
|
+
elif expected_type:
|
147
|
+
message = f"Invalid type for parameter '{parameter_name}': got {type(parameter_value).__name__}, expected {expected_type}"
|
148
|
+
else:
|
149
|
+
message = f"Invalid value for parameter '{parameter_name}': {parameter_value}"
|
150
|
+
|
151
|
+
super().__init__(message, error_code="INVALID_PARAMETER")
|
152
|
+
self.parameter_name = parameter_name
|
153
|
+
self.parameter_value = parameter_value
|
154
|
+
self.expected_type = expected_type
|
155
|
+
self.valid_values = valid_values
|
156
|
+
|
157
|
+
|
158
|
+
class QuantumCircuitError(SuperQuantXError):
|
159
|
+
"""Raised when quantum circuit operations fail."""
|
160
|
+
|
161
|
+
def __init__(
|
162
|
+
self,
|
163
|
+
message: str,
|
164
|
+
circuit_info: Optional[dict[str, Any]] = None,
|
165
|
+
) -> None:
|
166
|
+
"""Initialize quantum circuit error.
|
167
|
+
|
168
|
+
Args:
|
169
|
+
message: Error description
|
170
|
+
circuit_info: Dictionary with circuit information (qubits, gates, etc.)
|
171
|
+
|
172
|
+
"""
|
173
|
+
super().__init__(message, error_code="CIRCUIT_ERROR")
|
174
|
+
self.circuit_info = circuit_info or {}
|
175
|
+
|
176
|
+
|
177
|
+
class ConfigurationError(SuperQuantXError):
|
178
|
+
"""Raised when configuration issues are detected."""
|
179
|
+
|
180
|
+
def __init__(self, message: str, config_key: Optional[str] = None) -> None:
|
181
|
+
"""Initialize configuration error.
|
182
|
+
|
183
|
+
Args:
|
184
|
+
message: Error description
|
185
|
+
config_key: The configuration key that caused the error
|
186
|
+
|
187
|
+
"""
|
188
|
+
if config_key:
|
189
|
+
full_message = f"Configuration error for '{config_key}': {message}"
|
190
|
+
else:
|
191
|
+
full_message = f"Configuration error: {message}"
|
192
|
+
|
193
|
+
super().__init__(full_message, error_code="CONFIG_ERROR")
|
194
|
+
self.config_key = config_key
|
195
|
+
|
196
|
+
|
197
|
+
class ResearchModeError(SuperQuantXError):
|
198
|
+
"""Raised when research-only features are used incorrectly."""
|
199
|
+
|
200
|
+
def __init__(self, message: str, feature_name: Optional[str] = None) -> None:
|
201
|
+
"""Initialize research mode error.
|
202
|
+
|
203
|
+
Args:
|
204
|
+
message: Error description
|
205
|
+
feature_name: Name of the research feature
|
206
|
+
|
207
|
+
"""
|
208
|
+
warning_prefix = "⚠️ RESEARCH SOFTWARE WARNING: "
|
209
|
+
if feature_name:
|
210
|
+
full_message = f"{warning_prefix}{feature_name}: {message}"
|
211
|
+
else:
|
212
|
+
full_message = f"{warning_prefix}{message}"
|
213
|
+
|
214
|
+
super().__init__(full_message, error_code="RESEARCH_MODE")
|
215
|
+
self.feature_name = feature_name
|
216
|
+
|
217
|
+
|
218
|
+
# Utility functions for error handling
|
219
|
+
def validate_backend_available(backend_name: str, backend_instance: Any) -> None:
|
220
|
+
"""Validate that a backend is available and properly initialized.
|
221
|
+
|
222
|
+
Args:
|
223
|
+
backend_name: Name of the backend to validate
|
224
|
+
backend_instance: The backend instance to check
|
225
|
+
|
226
|
+
Raises:
|
227
|
+
BackendNotAvailableError: If backend is not available
|
228
|
+
BackendError: If backend is improperly initialized
|
229
|
+
|
230
|
+
"""
|
231
|
+
if backend_instance is None:
|
232
|
+
raise BackendNotAvailableError(backend_name)
|
233
|
+
|
234
|
+
if hasattr(backend_instance, 'is_available') and not backend_instance.is_available():
|
235
|
+
raise BackendNotAvailableError(backend_name)
|
236
|
+
|
237
|
+
|
238
|
+
def validate_fitted(algorithm_instance: Any, algorithm_name: str) -> None:
|
239
|
+
"""Validate that an algorithm has been fitted.
|
240
|
+
|
241
|
+
Args:
|
242
|
+
algorithm_instance: The algorithm instance to check
|
243
|
+
algorithm_name: Name of the algorithm
|
244
|
+
|
245
|
+
Raises:
|
246
|
+
NotFittedError: If algorithm is not fitted
|
247
|
+
|
248
|
+
"""
|
249
|
+
if not getattr(algorithm_instance, 'is_fitted', False):
|
250
|
+
raise NotFittedError(algorithm_name)
|
251
|
+
|
252
|
+
|
253
|
+
def validate_parameter(
|
254
|
+
parameter_name: str,
|
255
|
+
parameter_value: Any,
|
256
|
+
expected_type: Optional[type] = None,
|
257
|
+
valid_values: Optional[list[Any]] = None,
|
258
|
+
allow_none: bool = False,
|
259
|
+
) -> None:
|
260
|
+
"""Validate a parameter value.
|
261
|
+
|
262
|
+
Args:
|
263
|
+
parameter_name: Name of the parameter
|
264
|
+
parameter_value: Value to validate
|
265
|
+
expected_type: Expected Python type
|
266
|
+
valid_values: List of valid values
|
267
|
+
allow_none: Whether None is an acceptable value
|
268
|
+
|
269
|
+
Raises:
|
270
|
+
InvalidParameterError: If parameter is invalid
|
271
|
+
|
272
|
+
"""
|
273
|
+
if parameter_value is None and allow_none:
|
274
|
+
return
|
275
|
+
|
276
|
+
if parameter_value is None and not allow_none:
|
277
|
+
raise InvalidParameterError(parameter_name, parameter_value, "non-None value")
|
278
|
+
|
279
|
+
if expected_type and not isinstance(parameter_value, expected_type):
|
280
|
+
raise InvalidParameterError(
|
281
|
+
parameter_name,
|
282
|
+
parameter_value,
|
283
|
+
expected_type.__name__
|
284
|
+
)
|
285
|
+
|
286
|
+
if valid_values and parameter_value not in valid_values:
|
287
|
+
raise InvalidParameterError(parameter_name, parameter_value, valid_values=valid_values)
|