wings-quantum 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.
- wings/__init__.py +251 -0
- wings/adam.py +132 -0
- wings/ansatz.py +207 -0
- wings/benchmarks.py +605 -0
- wings/campaign.py +661 -0
- wings/cli.py +377 -0
- wings/compat.py +132 -0
- wings/config.py +443 -0
- wings/convenience.py +259 -0
- wings/evaluators/__init__.py +19 -0
- wings/evaluators/cpu.py +72 -0
- wings/evaluators/custatevec.py +783 -0
- wings/evaluators/gpu.py +220 -0
- wings/export.py +243 -0
- wings/optimizer.py +1898 -0
- wings/paths.py +295 -0
- wings/py.typed +2 -0
- wings/results.py +255 -0
- wings/types.py +14 -0
- wings_quantum-0.1.0.dist-info/METADATA +491 -0
- wings_quantum-0.1.0.dist-info/RECORD +25 -0
- wings_quantum-0.1.0.dist-info/WHEEL +5 -0
- wings_quantum-0.1.0.dist-info/entry_points.txt +2 -0
- wings_quantum-0.1.0.dist-info/licenses/LICENSE.txt +21 -0
- wings_quantum-0.1.0.dist-info/top_level.txt +1 -0
wings/config.py
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
"""Configuration classes for optimization."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import multiprocessing as mp
|
|
5
|
+
import os
|
|
6
|
+
from dataclasses import asdict, dataclass, field
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Any, Callable, Optional
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
from numpy.typing import NDArray
|
|
13
|
+
|
|
14
|
+
from .ansatz import AnsatzProtocol
|
|
15
|
+
from .paths import get_path_config
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"TargetFunction",
|
|
19
|
+
"OptimizerConfig",
|
|
20
|
+
"OptimizationPipeline",
|
|
21
|
+
"OptimizationStrategy",
|
|
22
|
+
"CampaignConfig",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TargetFunction(Enum):
|
|
27
|
+
"""Available target wavefunction types."""
|
|
28
|
+
|
|
29
|
+
GAUSSIAN = "gaussian"
|
|
30
|
+
LORENTZIAN = "lorentzian"
|
|
31
|
+
SECH = "sech" # Hyperbolic secant (soliton-like)
|
|
32
|
+
CUSTOM = "custom"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class OptimizerConfig:
|
|
37
|
+
"""
|
|
38
|
+
Configuration for the GaussianOptimizer.
|
|
39
|
+
|
|
40
|
+
Supports multiple target wavefunctions, custom ansatze, and multi-GPU execution.
|
|
41
|
+
|
|
42
|
+
Examples
|
|
43
|
+
--------
|
|
44
|
+
Basic Gaussian:
|
|
45
|
+
|
|
46
|
+
>>> config = OptimizerConfig(n_qubits=8, sigma=0.5)
|
|
47
|
+
|
|
48
|
+
Shifted Gaussian:
|
|
49
|
+
|
|
50
|
+
>>> config = OptimizerConfig(n_qubits=8, sigma=0.5, x0=2.0) # Center at x=2.0
|
|
51
|
+
|
|
52
|
+
Lorentzian:
|
|
53
|
+
|
|
54
|
+
>>> config = OptimizerConfig(
|
|
55
|
+
... n_qubits=8,
|
|
56
|
+
... target_function=TargetFunction.LORENTZIAN,
|
|
57
|
+
... gamma=0.3, # Width parameter
|
|
58
|
+
... x0=1.5, # Shift to x=1.5
|
|
59
|
+
... )
|
|
60
|
+
|
|
61
|
+
Custom wavefunction:
|
|
62
|
+
|
|
63
|
+
>>> def double_gaussian(x):
|
|
64
|
+
... return np.exp(-((x-1)**2)/0.5) + np.exp(-((x+1)**2)/0.5)
|
|
65
|
+
>>>
|
|
66
|
+
>>> config = OptimizerConfig(
|
|
67
|
+
... n_qubits=10,
|
|
68
|
+
... target_function=TargetFunction.CUSTOM,
|
|
69
|
+
... custom_target_fn=double_gaussian,
|
|
70
|
+
... )
|
|
71
|
+
|
|
72
|
+
Multi-GPU:
|
|
73
|
+
|
|
74
|
+
>>> config = OptimizerConfig(
|
|
75
|
+
... n_qubits=14,
|
|
76
|
+
... use_multi_gpu=True,
|
|
77
|
+
... gpu_device_ids=[0, 1, 2, 3],
|
|
78
|
+
... )
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# ========================================
|
|
82
|
+
# Problem Parameters
|
|
83
|
+
# ========================================
|
|
84
|
+
n_qubits: int = 9
|
|
85
|
+
sigma: float = 1.0
|
|
86
|
+
x0: float = 0.0 # Center position (shift wavefunction left/right)
|
|
87
|
+
box_size: Optional[float] = None
|
|
88
|
+
|
|
89
|
+
# ========================================
|
|
90
|
+
# Target Wavefunction
|
|
91
|
+
# ========================================
|
|
92
|
+
target_function: TargetFunction = TargetFunction.GAUSSIAN
|
|
93
|
+
gamma: Optional[float] = None # Width for Lorentzian (uses sigma if None)
|
|
94
|
+
custom_target_fn: Optional[Callable[[np.ndarray], np.ndarray]] = None
|
|
95
|
+
|
|
96
|
+
# ========================================
|
|
97
|
+
# Ansatz Configuration
|
|
98
|
+
# ========================================
|
|
99
|
+
ansatz: Optional[AnsatzProtocol] = None
|
|
100
|
+
ansatz_depth: Optional[int] = None
|
|
101
|
+
ansatz_kwargs: Optional[dict[str, Any]] = None
|
|
102
|
+
|
|
103
|
+
# ========================================
|
|
104
|
+
# Optimizer Settings
|
|
105
|
+
# ========================================
|
|
106
|
+
method: str = "L-BFGS-B"
|
|
107
|
+
max_iter: int = 10000
|
|
108
|
+
max_fun: int = 50000
|
|
109
|
+
tolerance: float = 1e-12
|
|
110
|
+
gtol: float = 1e-12
|
|
111
|
+
use_analytic_gradients: bool = True
|
|
112
|
+
|
|
113
|
+
# High precision mode
|
|
114
|
+
high_precision: bool = True
|
|
115
|
+
adaptive_tolerance: bool = True
|
|
116
|
+
|
|
117
|
+
# For difficult cases
|
|
118
|
+
use_multistart: bool = False
|
|
119
|
+
n_restarts: int = 5
|
|
120
|
+
|
|
121
|
+
# Refinement stage
|
|
122
|
+
enable_refinement: bool = True
|
|
123
|
+
target_fidelity: float = 0.9999999
|
|
124
|
+
refinement_iter: int = 5000
|
|
125
|
+
|
|
126
|
+
# ========================================
|
|
127
|
+
# Visualization
|
|
128
|
+
# ========================================
|
|
129
|
+
plot_result: bool = True
|
|
130
|
+
save_plot: bool = True
|
|
131
|
+
|
|
132
|
+
# ========================================
|
|
133
|
+
# GPU Configuration
|
|
134
|
+
# ========================================
|
|
135
|
+
use_gpu: bool = True
|
|
136
|
+
gpu_precision: str = "double" # 'double' or 'single'
|
|
137
|
+
gpu_batch_size: int = 64
|
|
138
|
+
gpu_blocking: bool = True
|
|
139
|
+
|
|
140
|
+
# cuStateVec
|
|
141
|
+
use_custatevec: bool = True
|
|
142
|
+
custatevec_batch_size: int = 128
|
|
143
|
+
|
|
144
|
+
# Multi-GPU support
|
|
145
|
+
use_multi_gpu: bool = False
|
|
146
|
+
gpu_device_ids: Optional[list[int]] = None # None = auto-detect all GPUs
|
|
147
|
+
simulators_per_gpu: int = 2 # Number of simulators per GPU for batching
|
|
148
|
+
|
|
149
|
+
# ========================================
|
|
150
|
+
# Parallelization
|
|
151
|
+
# ========================================
|
|
152
|
+
n_workers: Optional[int] = None # None = auto-detect
|
|
153
|
+
parallel_gradients: bool = True
|
|
154
|
+
parallel_backend: str = "thread" # 'thread' or 'process'
|
|
155
|
+
gradient_chunk_size: int = 8
|
|
156
|
+
|
|
157
|
+
# ========================================
|
|
158
|
+
# Output
|
|
159
|
+
# ========================================
|
|
160
|
+
verbose: bool = True
|
|
161
|
+
|
|
162
|
+
def __post_init__(self):
|
|
163
|
+
# Auto-detect number of workers if not specified
|
|
164
|
+
if self.n_workers is None:
|
|
165
|
+
self.n_workers = max(1, mp.cpu_count() - 1)
|
|
166
|
+
|
|
167
|
+
# Only auto-calculate box_size if not explicitly provided
|
|
168
|
+
if self.box_size is None:
|
|
169
|
+
if self.sigma < 0.5:
|
|
170
|
+
self.box_size = max(4.0, 12 * self.sigma)
|
|
171
|
+
else:
|
|
172
|
+
self.box_size = 10.0
|
|
173
|
+
|
|
174
|
+
# Ensure adequate coverage
|
|
175
|
+
min_box = 8 * self.sigma
|
|
176
|
+
if self.box_size < min_box:
|
|
177
|
+
if self.verbose:
|
|
178
|
+
print(
|
|
179
|
+
f"Info: Auto-adjusting box size from {self.box_size:.2f} to {min_box:.2f} for sigma={self.sigma}"
|
|
180
|
+
)
|
|
181
|
+
self.box_size = min_box
|
|
182
|
+
else:
|
|
183
|
+
if self.verbose:
|
|
184
|
+
print(f"Using user-specified box size: +/-{self.box_size:.2f}")
|
|
185
|
+
|
|
186
|
+
# Validate custom target function
|
|
187
|
+
if self.target_function == TargetFunction.CUSTOM and self.custom_target_fn is None:
|
|
188
|
+
raise ValueError("custom_target_fn must be provided when target_function is CUSTOM")
|
|
189
|
+
|
|
190
|
+
if self.verbose:
|
|
191
|
+
print(f"Target function: {self.target_function.value}")
|
|
192
|
+
if self.x0 != 0.0:
|
|
193
|
+
print(f"Wavefunction center: x0 = {self.x0}")
|
|
194
|
+
print(f"Parallelization: {self.n_workers} workers, backend='{self.parallel_backend}'")
|
|
195
|
+
if self.use_multi_gpu:
|
|
196
|
+
gpu_str = f"devices {self.gpu_device_ids}" if self.gpu_device_ids else "auto-detect"
|
|
197
|
+
print(f"Multi-GPU enabled: {gpu_str}")
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def n_params(self) -> int:
|
|
201
|
+
"""Number of variational parameters."""
|
|
202
|
+
if self.ansatz is not None and hasattr(self.ansatz, 'n_params'):
|
|
203
|
+
return self.ansatz.n_params
|
|
204
|
+
return self.n_qubits * self.n_qubits
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def n_states(self) -> int:
|
|
208
|
+
"""Number of basis states (2^n_qubits)."""
|
|
209
|
+
return 2**self.n_qubits
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def positions(self) -> NDArray[np.float64]:
|
|
213
|
+
"""Position grid for wavefunction."""
|
|
214
|
+
return np.linspace(-self.box_size, self.box_size, self.n_states)
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def delta_x(self) -> float:
|
|
218
|
+
"""Grid spacing."""
|
|
219
|
+
return 2 * self.box_size / (self.n_states - 1)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@dataclass
|
|
223
|
+
class OptimizationPipeline:
|
|
224
|
+
"""Configuration for optimization pipeline stages."""
|
|
225
|
+
|
|
226
|
+
mode: str = "adaptive" # 'adaptive', 'ultra', 'hybrid', 'single_stage'
|
|
227
|
+
|
|
228
|
+
target_fidelity: float = 0.9999
|
|
229
|
+
target_infidelity: Optional[float] = None
|
|
230
|
+
|
|
231
|
+
max_total_time: float = 3600 # seconds
|
|
232
|
+
|
|
233
|
+
use_init_search: bool = True
|
|
234
|
+
init_strategies: Optional[list[str]] = None
|
|
235
|
+
|
|
236
|
+
use_adam_stage: bool = True
|
|
237
|
+
adam_max_steps: int = 1000
|
|
238
|
+
adam_lr: float = 0.01
|
|
239
|
+
adam_max_time: Optional[float] = None
|
|
240
|
+
adam_time_fraction: float = 0.4
|
|
241
|
+
|
|
242
|
+
use_basin_hopping: bool = False
|
|
243
|
+
basin_hopping_threshold: float = 0.9999
|
|
244
|
+
basin_hopping_iterations: int = 30
|
|
245
|
+
|
|
246
|
+
use_lbfgs_refinement: bool = True
|
|
247
|
+
lbfgs_tolerances: Optional[list[float]] = None
|
|
248
|
+
lbfgs_time_fraction: float = 0.8
|
|
249
|
+
|
|
250
|
+
use_fine_tuning: bool = True
|
|
251
|
+
fine_tuning_threshold: float = 0.9999
|
|
252
|
+
|
|
253
|
+
verbose: bool = True
|
|
254
|
+
|
|
255
|
+
def __post_init__(self):
|
|
256
|
+
if self.target_infidelity is None:
|
|
257
|
+
self.target_infidelity = 1 - self.target_fidelity
|
|
258
|
+
if self.init_strategies is None:
|
|
259
|
+
self.init_strategies = ["smart", "gaussian_product", "random", "random", "random"]
|
|
260
|
+
if self.lbfgs_tolerances is None:
|
|
261
|
+
self.lbfgs_tolerances = [1e-10, 1e-12, 1e-14]
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class OptimizationStrategy(Enum):
|
|
265
|
+
"""Available optimization strategies for campaign runs."""
|
|
266
|
+
|
|
267
|
+
SMART = "smart"
|
|
268
|
+
GAUSSIAN_PRODUCT = "gaussian_product"
|
|
269
|
+
RANDOM = "random"
|
|
270
|
+
PERTURB_BEST = "perturb_best"
|
|
271
|
+
ZERO = "zero"
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@dataclass
|
|
275
|
+
class CampaignConfig:
|
|
276
|
+
"""
|
|
277
|
+
Configuration for large-scale optimization campaigns.
|
|
278
|
+
|
|
279
|
+
Designed for running thousands of optimizations to guarantee
|
|
280
|
+
finding the global minimum with machine precision.
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
# ========================================
|
|
284
|
+
# Problem Parameters
|
|
285
|
+
# ========================================
|
|
286
|
+
n_qubits: int = 8
|
|
287
|
+
sigma: float = 0.5
|
|
288
|
+
x0: float = 0.0
|
|
289
|
+
box_size: Optional[float] = None
|
|
290
|
+
target_function: TargetFunction = TargetFunction.GAUSSIAN
|
|
291
|
+
gamma: Optional[float] = None
|
|
292
|
+
custom_target_fn: Optional[Callable[[np.ndarray], np.ndarray]] = None
|
|
293
|
+
|
|
294
|
+
# ========================================
|
|
295
|
+
# Campaign Scale
|
|
296
|
+
# ========================================
|
|
297
|
+
total_runs: int = 1000
|
|
298
|
+
runs_per_batch: int = 50
|
|
299
|
+
|
|
300
|
+
# ========================================
|
|
301
|
+
# Optimization Targets
|
|
302
|
+
# ========================================
|
|
303
|
+
target_infidelity: float = 1e-10
|
|
304
|
+
target_fidelity: Optional[float] = None
|
|
305
|
+
acceptable_infidelity: float = 1e-8
|
|
306
|
+
|
|
307
|
+
# ========================================
|
|
308
|
+
# Strategy Distribution
|
|
309
|
+
# ========================================
|
|
310
|
+
strategy_weights: dict[str, float] = field(
|
|
311
|
+
default_factory=lambda: {
|
|
312
|
+
"smart": 0.3,
|
|
313
|
+
"gaussian_product": 0.2,
|
|
314
|
+
"random": 0.4,
|
|
315
|
+
"perturb_best": 0.1,
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# ========================================
|
|
320
|
+
# Per-Run Settings
|
|
321
|
+
# ========================================
|
|
322
|
+
max_iter_per_run: int = 5000
|
|
323
|
+
tolerance_per_run: float = 1e-12
|
|
324
|
+
use_ultra_precision: bool = True
|
|
325
|
+
ultra_precision_time_limit: float = 300
|
|
326
|
+
|
|
327
|
+
# ========================================
|
|
328
|
+
# Refinement Settings
|
|
329
|
+
# ========================================
|
|
330
|
+
refine_top_n: int = 10
|
|
331
|
+
refinement_time_limit: float = 600
|
|
332
|
+
|
|
333
|
+
# ========================================
|
|
334
|
+
# Parallelization
|
|
335
|
+
# ========================================
|
|
336
|
+
n_parallel_runs: Optional[int] = None
|
|
337
|
+
use_gpu: bool = True
|
|
338
|
+
use_custatevec: bool = True
|
|
339
|
+
use_multi_gpu: bool = False
|
|
340
|
+
gpu_device_ids: Optional[list[int]] = None
|
|
341
|
+
gpu_precision: str = "double"
|
|
342
|
+
|
|
343
|
+
# ========================================
|
|
344
|
+
# Checkpointing
|
|
345
|
+
# ========================================
|
|
346
|
+
checkpoint_interval: int = 10
|
|
347
|
+
checkpoint_dir: Optional[str] = None
|
|
348
|
+
resume_from_checkpoint: bool = True
|
|
349
|
+
|
|
350
|
+
# ========================================
|
|
351
|
+
# Output Settings
|
|
352
|
+
# ========================================
|
|
353
|
+
campaign_name: Optional[str] = None
|
|
354
|
+
output_dir: Optional[str] = None
|
|
355
|
+
save_all_results: bool = False
|
|
356
|
+
save_top_n_results: int = 100
|
|
357
|
+
verbose: int = 1
|
|
358
|
+
|
|
359
|
+
# ========================================
|
|
360
|
+
# Seed Management
|
|
361
|
+
# ========================================
|
|
362
|
+
base_seed: int = 42
|
|
363
|
+
|
|
364
|
+
def __post_init__(self):
|
|
365
|
+
"""Initialize computed fields."""
|
|
366
|
+
if self.target_fidelity is None:
|
|
367
|
+
self.target_fidelity = 1 - self.target_infidelity
|
|
368
|
+
|
|
369
|
+
if self.box_size is None:
|
|
370
|
+
self.box_size = max(4 * self.sigma, 2.0)
|
|
371
|
+
|
|
372
|
+
if self.n_parallel_runs is None:
|
|
373
|
+
self.n_parallel_runs = max(1, mp.cpu_count() - 2)
|
|
374
|
+
|
|
375
|
+
if self.campaign_name is None:
|
|
376
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
377
|
+
fn_suffix = (
|
|
378
|
+
f"_{self.target_function.value}"
|
|
379
|
+
if self.target_function != TargetFunction.GAUSSIAN
|
|
380
|
+
else ""
|
|
381
|
+
)
|
|
382
|
+
self.campaign_name = (
|
|
383
|
+
f"campaign_q{self.n_qubits}_s{self.sigma:.2f}{fn_suffix}_{timestamp}"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
if self.checkpoint_dir is None:
|
|
387
|
+
self.checkpoint_dir = str(
|
|
388
|
+
get_path_config(verbose=False).checkpoint_dir / self.campaign_name
|
|
389
|
+
)
|
|
390
|
+
if self.output_dir is None:
|
|
391
|
+
self.output_dir = str(get_path_config(verbose=False).campaign_dir / self.campaign_name)
|
|
392
|
+
|
|
393
|
+
os.makedirs(self.checkpoint_dir, exist_ok=True)
|
|
394
|
+
os.makedirs(self.output_dir, exist_ok=True)
|
|
395
|
+
|
|
396
|
+
total_weight = sum(self.strategy_weights.values())
|
|
397
|
+
self.strategy_weights = {k: v / total_weight for k, v in self.strategy_weights.items()}
|
|
398
|
+
|
|
399
|
+
@property
|
|
400
|
+
def n_params(self) -> int:
|
|
401
|
+
return self.n_qubits * self.n_qubits
|
|
402
|
+
|
|
403
|
+
def get_strategy_for_run(self, run_id: int) -> str:
|
|
404
|
+
"""Deterministically assign strategy based on run_id."""
|
|
405
|
+
np.random.seed(self.base_seed + run_id)
|
|
406
|
+
strategies = list(self.strategy_weights.keys())
|
|
407
|
+
weights = list(self.strategy_weights.values())
|
|
408
|
+
return np.random.choice(strategies, p=weights)
|
|
409
|
+
|
|
410
|
+
def get_seed_for_run(self, run_id: int) -> int:
|
|
411
|
+
"""Get reproducible seed for run."""
|
|
412
|
+
return self.base_seed + run_id * 1000
|
|
413
|
+
|
|
414
|
+
def to_dict(self) -> dict:
|
|
415
|
+
"""Convert to dictionary for serialization."""
|
|
416
|
+
d = asdict(self)
|
|
417
|
+
d["target_function"] = self.target_function.value
|
|
418
|
+
d["custom_target_fn"] = None # Cannot serialize functions
|
|
419
|
+
return d
|
|
420
|
+
|
|
421
|
+
def save(self, filepath: Optional[str] = None) -> str:
|
|
422
|
+
"""Save configuration to JSON."""
|
|
423
|
+
if filepath is None:
|
|
424
|
+
filepath = os.path.join(self.output_dir, "campaign_config.json")
|
|
425
|
+
|
|
426
|
+
with open(filepath, "w") as f:
|
|
427
|
+
json.dump(self.to_dict(), f, indent=2, default=str)
|
|
428
|
+
|
|
429
|
+
return filepath
|
|
430
|
+
|
|
431
|
+
@classmethod
|
|
432
|
+
def load(cls, filepath: str) -> "CampaignConfig":
|
|
433
|
+
"""Load configuration from JSON."""
|
|
434
|
+
with open(filepath) as f:
|
|
435
|
+
data = json.load(f)
|
|
436
|
+
|
|
437
|
+
if isinstance(data.get("target_function"), str):
|
|
438
|
+
data["target_function"] = TargetFunction(data["target_function"])
|
|
439
|
+
|
|
440
|
+
if isinstance(data.get("strategy_weights"), str):
|
|
441
|
+
data["strategy_weights"] = json.loads(data["strategy_weights"])
|
|
442
|
+
|
|
443
|
+
return cls(**data)
|
wings/convenience.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""High-level convenience functions for interactive use."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from .config import OptimizerConfig, TargetFunction
|
|
6
|
+
from .optimizer import GaussianOptimizer
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"optimize_gaussian_state",
|
|
10
|
+
"quick_optimize",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def optimize_gaussian_state(
|
|
15
|
+
n_qubits: int = 8,
|
|
16
|
+
sigma: float = 1.0,
|
|
17
|
+
x0: float = 0.0,
|
|
18
|
+
box_size: Optional[float] = None,
|
|
19
|
+
target_function: "TargetFunction" = None,
|
|
20
|
+
gamma: Optional[float] = None,
|
|
21
|
+
custom_target_fn=None,
|
|
22
|
+
target_fidelity: float = 0.999999,
|
|
23
|
+
target_infidelity: Optional[float] = None,
|
|
24
|
+
high_precision: bool = True,
|
|
25
|
+
max_time: float = 1800,
|
|
26
|
+
use_gpu: bool = True,
|
|
27
|
+
use_custatevec: bool = True,
|
|
28
|
+
use_multi_gpu: bool = False,
|
|
29
|
+
gpu_device_ids: Optional[list] = None,
|
|
30
|
+
plot: bool = True,
|
|
31
|
+
save: bool = True,
|
|
32
|
+
verbose: bool = True,
|
|
33
|
+
) -> tuple[dict[str, Any], GaussianOptimizer]:
|
|
34
|
+
"""
|
|
35
|
+
Main optimization function with high precision capabilities.
|
|
36
|
+
|
|
37
|
+
This is the recommended entry point for interactive use.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
n_qubits : int
|
|
42
|
+
Number of qubits (default: 8)
|
|
43
|
+
sigma : float
|
|
44
|
+
Gaussian width parameter (default: 1.0)
|
|
45
|
+
box_size : float, optional
|
|
46
|
+
Box size for position grid. If None, auto-calculated based on sigma.
|
|
47
|
+
target_fidelity : float
|
|
48
|
+
Target fidelity to achieve (default: 0.999999)
|
|
49
|
+
target_infidelity : float, optional
|
|
50
|
+
Alternative to target_fidelity: specify 1-F directly (e.g., 1e-10)
|
|
51
|
+
high_precision : bool
|
|
52
|
+
Enable high precision mode (default: True)
|
|
53
|
+
max_time : float
|
|
54
|
+
Maximum optimization time in seconds (default: 1800 = 30 min)
|
|
55
|
+
use_gpu : bool
|
|
56
|
+
Use GPU acceleration if available (default: True)
|
|
57
|
+
use_custatevec : bool
|
|
58
|
+
Use cuStateVec if available (default: True)
|
|
59
|
+
plot : bool
|
|
60
|
+
Generate result plots (default: True)
|
|
61
|
+
save : bool
|
|
62
|
+
Save results to files (default: True)
|
|
63
|
+
verbose : bool
|
|
64
|
+
Print progress information (default: True)
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
results : dict
|
|
69
|
+
Dictionary containing optimization results:
|
|
70
|
+
- 'fidelity': Final fidelity achieved
|
|
71
|
+
- 'infidelity': 1 - fidelity
|
|
72
|
+
- 'params': Optimal parameters
|
|
73
|
+
- 'time': Total optimization time
|
|
74
|
+
- 'n_evaluations': Number of function evaluations
|
|
75
|
+
- 'circuit_mean', 'circuit_std': Statistics of prepared state
|
|
76
|
+
- 'target_mean', 'target_std': Statistics of target Gaussian
|
|
77
|
+
optimizer : GaussianOptimizer
|
|
78
|
+
The optimizer instance (for further analysis)
|
|
79
|
+
|
|
80
|
+
Examples
|
|
81
|
+
--------
|
|
82
|
+
Basic usage:
|
|
83
|
+
|
|
84
|
+
>>> results, opt = optimize_gaussian_state(n_qubits=8, sigma=0.5)
|
|
85
|
+
>>> print(f"Fidelity: {results['fidelity']:.12f}")
|
|
86
|
+
|
|
87
|
+
High precision with specific target:
|
|
88
|
+
|
|
89
|
+
>>> results, opt = optimize_gaussian_state(
|
|
90
|
+
... n_qubits=10,
|
|
91
|
+
... sigma=0.5,
|
|
92
|
+
... target_infidelity=1e-11,
|
|
93
|
+
... max_time=3600,
|
|
94
|
+
... )
|
|
95
|
+
"""
|
|
96
|
+
if verbose:
|
|
97
|
+
print("=" * 80)
|
|
98
|
+
print("HIGH PRECISION GAUSSIAN STATE OPTIMIZER")
|
|
99
|
+
print("=" * 80)
|
|
100
|
+
|
|
101
|
+
# Determine target
|
|
102
|
+
if target_infidelity is not None:
|
|
103
|
+
effective_target_infidelity = target_infidelity
|
|
104
|
+
else:
|
|
105
|
+
effective_target_infidelity = 1 - target_fidelity
|
|
106
|
+
|
|
107
|
+
# Auto-calculate box size if not provided
|
|
108
|
+
if box_size is None:
|
|
109
|
+
box_size = max(10.0, 8 * sigma)
|
|
110
|
+
|
|
111
|
+
# Create configuration
|
|
112
|
+
if target_function is None:
|
|
113
|
+
target_function = TargetFunction.GAUSSIAN
|
|
114
|
+
|
|
115
|
+
# Create configuration
|
|
116
|
+
config = OptimizerConfig(
|
|
117
|
+
n_qubits=n_qubits,
|
|
118
|
+
sigma=sigma,
|
|
119
|
+
x0=x0,
|
|
120
|
+
box_size=box_size,
|
|
121
|
+
# Target function
|
|
122
|
+
target_function=target_function,
|
|
123
|
+
gamma=gamma,
|
|
124
|
+
custom_target_fn=custom_target_fn,
|
|
125
|
+
# Optimizer settings
|
|
126
|
+
method="L-BFGS-B",
|
|
127
|
+
max_iter=10000,
|
|
128
|
+
max_fun=200000,
|
|
129
|
+
tolerance=1e-14,
|
|
130
|
+
gtol=1e-14,
|
|
131
|
+
high_precision=high_precision,
|
|
132
|
+
use_analytic_gradients=True,
|
|
133
|
+
use_gpu=use_gpu,
|
|
134
|
+
use_custatevec=use_custatevec,
|
|
135
|
+
use_multi_gpu=use_multi_gpu,
|
|
136
|
+
gpu_device_ids=gpu_device_ids,
|
|
137
|
+
gpu_precision="double",
|
|
138
|
+
verbose=verbose,
|
|
139
|
+
target_fidelity=1 - effective_target_infidelity,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if verbose:
|
|
143
|
+
print("\nConfiguration:")
|
|
144
|
+
print(f" n_qubits: {config.n_qubits}")
|
|
145
|
+
print(f" Target function: {target_function.value}")
|
|
146
|
+
print(f" Target sigma: {config.sigma:.6f}")
|
|
147
|
+
if x0 != 0.0:
|
|
148
|
+
print(f" Center (x0): {x0:.6f}")
|
|
149
|
+
print(f" Box size: ±{config.box_size:.4f}")
|
|
150
|
+
print(f" Grid points: {config.n_states}")
|
|
151
|
+
print(f" Grid spacing: {config.delta_x:.8f}")
|
|
152
|
+
print(f" Target infidelity: {effective_target_infidelity:.2e}")
|
|
153
|
+
print()
|
|
154
|
+
|
|
155
|
+
# Create optimizer
|
|
156
|
+
optimizer = GaussianOptimizer(config)
|
|
157
|
+
|
|
158
|
+
# Run ultra-precision optimization
|
|
159
|
+
results = optimizer.optimize_ultra_precision(
|
|
160
|
+
target_infidelity=effective_target_infidelity,
|
|
161
|
+
max_total_time=max_time,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Display results
|
|
165
|
+
if verbose:
|
|
166
|
+
print("\n" + "=" * 80)
|
|
167
|
+
print("OPTIMIZATION COMPLETE")
|
|
168
|
+
print("=" * 80)
|
|
169
|
+
print(f"Fidelity achieved: {results['fidelity']:.15f}")
|
|
170
|
+
print(f"Infidelity (1-F): {results['infidelity']:.3e}")
|
|
171
|
+
print(f"Time taken: {results['time']:.2f} seconds")
|
|
172
|
+
print(f"Function evaluations: {results['n_evaluations']}")
|
|
173
|
+
print()
|
|
174
|
+
print("State properties:")
|
|
175
|
+
print(
|
|
176
|
+
f" Circuit mean: {results['circuit_mean']:.10f} (target: {results['target_mean']:.6f})"
|
|
177
|
+
)
|
|
178
|
+
print(
|
|
179
|
+
f" Circuit std: {results['circuit_std']:.10f} (target: {results['target_std']:.6f})"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if results["fidelity"] >= (1 - effective_target_infidelity):
|
|
183
|
+
print(f"\n✓ SUCCESS: Achieved target infidelity of {effective_target_infidelity:.2e}")
|
|
184
|
+
else:
|
|
185
|
+
print(f"\n⚠ Target not achieved. Current: 1-F = {results['infidelity']:.3e}")
|
|
186
|
+
_print_suggestions(results["fidelity"])
|
|
187
|
+
|
|
188
|
+
# Plot results
|
|
189
|
+
if plot:
|
|
190
|
+
plot_file = f"gaussian_q{n_qubits}_s{sigma:.4f}.png" if save else None
|
|
191
|
+
optimizer.plot_results(results, save_path=plot_file)
|
|
192
|
+
|
|
193
|
+
# Save results
|
|
194
|
+
if save:
|
|
195
|
+
optimizer.save_results(results)
|
|
196
|
+
|
|
197
|
+
return results, optimizer
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _print_suggestions(fidelity: float) -> None:
|
|
201
|
+
"""Print optimization suggestions based on achieved fidelity."""
|
|
202
|
+
if fidelity < 0.999:
|
|
203
|
+
print("\nSuggestions to improve fidelity:")
|
|
204
|
+
print(" 1. Increase max_time")
|
|
205
|
+
print(" 2. Ensure high_precision=True")
|
|
206
|
+
print(" 3. Check box_size matches Gaussian extent (~8*sigma)")
|
|
207
|
+
print(" 4. Increase n_qubits for better resolution")
|
|
208
|
+
else:
|
|
209
|
+
print("\nFor higher precision:")
|
|
210
|
+
print(" - Increase max_time (current optimization may need more iterations)")
|
|
211
|
+
print(" - Try running multiple times (different initial conditions)")
|
|
212
|
+
print(" - Use run_production_campaign() for systematic search")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def quick_optimize(
|
|
216
|
+
n_qubits: int,
|
|
217
|
+
sigma: float,
|
|
218
|
+
x0: float = 0.0,
|
|
219
|
+
target_function: "TargetFunction" = None,
|
|
220
|
+
verbose: bool = True,
|
|
221
|
+
) -> tuple[float, dict[str, Any]]:
|
|
222
|
+
"""
|
|
223
|
+
Quick optimization with sensible defaults.
|
|
224
|
+
|
|
225
|
+
Useful for testing or when you just want a quick result.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
n_qubits : int
|
|
230
|
+
Number of qubits
|
|
231
|
+
sigma : float
|
|
232
|
+
Gaussian width
|
|
233
|
+
verbose : bool
|
|
234
|
+
Print progress (default: True)
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
fidelity : float
|
|
239
|
+
Best fidelity achieved
|
|
240
|
+
results : dict
|
|
241
|
+
Full results dictionary
|
|
242
|
+
|
|
243
|
+
Examples
|
|
244
|
+
--------
|
|
245
|
+
>>> fidelity, results = quick_optimize(8, 0.5)
|
|
246
|
+
>>> print(f"Achieved fidelity: {fidelity:.10f}")
|
|
247
|
+
"""
|
|
248
|
+
results, _ = optimize_gaussian_state(
|
|
249
|
+
n_qubits=n_qubits,
|
|
250
|
+
sigma=sigma,
|
|
251
|
+
x0=x0,
|
|
252
|
+
target_function=target_function,
|
|
253
|
+
target_infidelity=1e-10,
|
|
254
|
+
max_time=300, # 5 minutes
|
|
255
|
+
plot=False,
|
|
256
|
+
save=False,
|
|
257
|
+
verbose=verbose,
|
|
258
|
+
)
|
|
259
|
+
return results["fidelity"], results
|