modulation-da 0.0.1__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.
- modulation_da/__init__.py +7 -0
- modulation_da/current_calculator.py +71 -0
- modulation_da/exempleBeltako.py +638 -0
- modulation_da/main.py +77 -0
- modulation_da/perturbations.py +103 -0
- modulation_da/pinn_model.py +243 -0
- modulation_da/quantum_config.py +50 -0
- modulation_da/quantum_solver.py +245 -0
- modulation_da/quantum_system.py +51 -0
- modulation_da/results_manager.py +56 -0
- modulation_da/visualization.py +48 -0
- modulation_da-0.0.1.dist-info/METADATA +90 -0
- modulation_da-0.0.1.dist-info/RECORD +16 -0
- modulation_da-0.0.1.dist-info/WHEEL +5 -0
- modulation_da-0.0.1.dist-info/entry_points.txt +2 -0
- modulation_da-0.0.1.dist-info/top_level.txt +1 -0
modulation_da/main.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# main.py
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import argparse
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
# Add the current directory to system path for imports
|
|
8
|
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
9
|
+
|
|
10
|
+
from quantum_solver import QuantumPINNSolver
|
|
11
|
+
from modulation_da import __version__
|
|
12
|
+
|
|
13
|
+
def run_simulation(perturbation_type):
|
|
14
|
+
"""
|
|
15
|
+
Launches the simulation with the given perturbation type.
|
|
16
|
+
"""
|
|
17
|
+
print("=== QUANTUM PINN SIMULATOR ===")
|
|
18
|
+
|
|
19
|
+
available_perturbations = ['gaussian', 'sinusoidal', 'rectangular',
|
|
20
|
+
'exponential', 'modulated_sinusoidal']
|
|
21
|
+
|
|
22
|
+
if perturbation_type not in available_perturbations:
|
|
23
|
+
print(f"Invalid perturbation type: {perturbation_type}. Please choose from: {', '.join(available_perturbations)}")
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
# Create solver instance
|
|
27
|
+
solver = QuantumPINNSolver(perturbation_type=perturbation_type)
|
|
28
|
+
|
|
29
|
+
print(f"\nStarting simulation with {perturbation_type} perturbation...")
|
|
30
|
+
print(f"Simulation ID: {solver.config.simulation_id}")
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
# LAUNCH COMPLETE SIMULATION
|
|
34
|
+
start_time = datetime.now()
|
|
35
|
+
solver.run_full_simulation()
|
|
36
|
+
end_time = datetime.now()
|
|
37
|
+
|
|
38
|
+
print(f"\nSimulation time: {end_time - start_time}")
|
|
39
|
+
|
|
40
|
+
# AUTOMATIC SAVING
|
|
41
|
+
solver.save_results()
|
|
42
|
+
|
|
43
|
+
# PLOT GENERATION
|
|
44
|
+
solver.generate_plots()
|
|
45
|
+
|
|
46
|
+
# DISPLAY SUMMARY
|
|
47
|
+
print(solver.get_simulation_summary())
|
|
48
|
+
|
|
49
|
+
except KeyboardInterrupt:
|
|
50
|
+
print("\n⚠ Simulation interrupted by user")
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print(f"\n❌ Simulation failed: {e}")
|
|
53
|
+
raise
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
"""
|
|
57
|
+
Main entry point for the CLI.
|
|
58
|
+
"""
|
|
59
|
+
parser = argparse.ArgumentParser(description="Quantum PINN Simulator for donor-acceptor modulation.")
|
|
60
|
+
parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
|
|
61
|
+
|
|
62
|
+
subparsers = parser.add_subparsers(dest='command', help='Available commands')
|
|
63
|
+
|
|
64
|
+
# 'run' command
|
|
65
|
+
run_parser = subparsers.add_parser('run', help='Run a simulation')
|
|
66
|
+
run_parser.add_argument('--perturbation-type', type=str, default='gaussian',
|
|
67
|
+
help='Type of perturbation to apply (e.g., gaussian, sinusoidal)')
|
|
68
|
+
|
|
69
|
+
args = parser.parse_args()
|
|
70
|
+
|
|
71
|
+
if args.command == 'run':
|
|
72
|
+
run_simulation(args.perturbation_type)
|
|
73
|
+
else:
|
|
74
|
+
parser.print_help()
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
main()
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# perturbations.py
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
class PerturbationManager:
|
|
5
|
+
"""Management of all time-dependent perturbations"""
|
|
6
|
+
|
|
7
|
+
def __init__(self, config):
|
|
8
|
+
self.config = config
|
|
9
|
+
self.setup_parameters()
|
|
10
|
+
|
|
11
|
+
def setup_parameters(self):
|
|
12
|
+
"""Perturbation parameters"""
|
|
13
|
+
self.cent = 15 * self.config.Power
|
|
14
|
+
self.he1, self.he2 = 0.500, 0.00
|
|
15
|
+
self.W0 = 5.0 * self.config.Power
|
|
16
|
+
self.W1 = 2 * self.config.molE / self.config.hbar
|
|
17
|
+
self.tau = 6 * self.config.Power
|
|
18
|
+
|
|
19
|
+
# Normalization
|
|
20
|
+
self.cent_norm = self.cent / self.config.t_scale
|
|
21
|
+
self.he1_norm = self.he1 / self.config.E_scale
|
|
22
|
+
self.he2_norm = self.he2 / self.config.E_scale
|
|
23
|
+
self.W0_norm = self.W0 / self.config.t_scale
|
|
24
|
+
self.W1_norm = self.W1 * self.config.t_scale
|
|
25
|
+
self.tau_norm = self.tau / self.config.t_scale
|
|
26
|
+
|
|
27
|
+
def gaussian_perturbation(self, t_norm):
|
|
28
|
+
"""Gaussian perturbation"""
|
|
29
|
+
How = np.zeros((self.config.n, self.config.n), dtype=complex)
|
|
30
|
+
|
|
31
|
+
term1 = (-self.he1_norm * np.cos(self.W1_norm * (t_norm - self.cent_norm)) *
|
|
32
|
+
np.exp(-((t_norm - self.cent_norm)**2) / (2 * self.W0_norm**2)))
|
|
33
|
+
|
|
34
|
+
term2 = (-self.he2_norm * np.cos(self.W1_norm * (t_norm - self.cent_norm - self.tau_norm)) *
|
|
35
|
+
np.exp(-((t_norm - self.cent_norm - self.tau_norm)**2) / (2 * self.W0_norm**2)))
|
|
36
|
+
|
|
37
|
+
valeur = term1 + term2
|
|
38
|
+
How[0, 1] = How[1, 0] = valeur
|
|
39
|
+
return How
|
|
40
|
+
|
|
41
|
+
def sinusoidal_perturbation(self, t_norm):
|
|
42
|
+
"""Continuous sinusoidal perturbation"""
|
|
43
|
+
How = np.zeros((self.config.n, self.config.n), dtype=complex)
|
|
44
|
+
amplitude = self.he1_norm
|
|
45
|
+
frequency = self.W1_norm
|
|
46
|
+
|
|
47
|
+
# Simple sine wave
|
|
48
|
+
valeur = -amplitude * np.cos(frequency * t_norm)
|
|
49
|
+
How[0, 1] = How[1, 0] = valeur
|
|
50
|
+
return How
|
|
51
|
+
|
|
52
|
+
def rectangular_perturbation(self, t_norm):
|
|
53
|
+
"""Rectangular pulse"""
|
|
54
|
+
How = np.zeros((self.config.n, self.config.n), dtype=complex)
|
|
55
|
+
t_start, t_duration = 10, 20
|
|
56
|
+
|
|
57
|
+
if t_start <= t_norm <= t_start + t_duration:
|
|
58
|
+
valeur = -self.he1_norm * np.cos(self.W1_norm * t_norm)
|
|
59
|
+
How[0, 1] = How[1, 0] = valeur
|
|
60
|
+
return How
|
|
61
|
+
|
|
62
|
+
def exponential_perturbation(self, t_norm):
|
|
63
|
+
"""Exponential perturbation"""
|
|
64
|
+
How = np.zeros((self.config.n, self.config.n), dtype=complex)
|
|
65
|
+
t_start = 10
|
|
66
|
+
|
|
67
|
+
if t_norm >= t_start:
|
|
68
|
+
decay = np.exp(-(t_norm - t_start) / self.W0_norm)
|
|
69
|
+
valeur = -self.he1_norm * decay * np.cos(self.W1_norm * t_norm)
|
|
70
|
+
How[0, 1] = How[1, 0] = valeur
|
|
71
|
+
return How
|
|
72
|
+
|
|
73
|
+
def modulated_sinusoidal(self, t_norm):
|
|
74
|
+
"""Amplitude-modulated sine wave"""
|
|
75
|
+
How = np.zeros((self.config.n, self.config.n), dtype=complex)
|
|
76
|
+
|
|
77
|
+
# Gaussian envelope
|
|
78
|
+
envelope = np.exp(-((t_norm - self.cent_norm)**2) / (2 * self.W0_norm**2))
|
|
79
|
+
carrier = np.cos(self.W1_norm * t_norm)
|
|
80
|
+
|
|
81
|
+
valeur = -self.he1_norm * envelope * carrier
|
|
82
|
+
How[0, 1] = How[1, 0] = valeur
|
|
83
|
+
return How
|
|
84
|
+
|
|
85
|
+
def get_perturbation(self, t_norm, perturbation_type='gaussian'):
|
|
86
|
+
"""Perturbation selector"""
|
|
87
|
+
perturbations = {
|
|
88
|
+
'gaussian': self.gaussian_perturbation,
|
|
89
|
+
'sinusoidal': self.sinusoidal_perturbation,
|
|
90
|
+
'rectangular': self.rectangular_perturbation,
|
|
91
|
+
'exponential': self.exponential_perturbation,
|
|
92
|
+
'modulated_sinusoidal': self.modulated_sinusoidal
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return perturbations[perturbation_type](t_norm)
|
|
96
|
+
|
|
97
|
+
def generate_pulse_profile(self, t_values, perturbation_type):
|
|
98
|
+
"""Generates pulse profile for visualization"""
|
|
99
|
+
profile = []
|
|
100
|
+
for t in t_values:
|
|
101
|
+
H_pert = self.get_perturbation(t, perturbation_type)
|
|
102
|
+
profile.append(np.real(H_pert[0, 1]))
|
|
103
|
+
return np.array(profile)
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# pinn_model.py
|
|
2
|
+
import tensorflow as tf
|
|
3
|
+
import numpy as np
|
|
4
|
+
from time import time
|
|
5
|
+
import scipy.optimize
|
|
6
|
+
|
|
7
|
+
class PINNModel:
|
|
8
|
+
"""Complete management of PINN model and hybrid training"""
|
|
9
|
+
def __init__(self, config):
|
|
10
|
+
self.config = config
|
|
11
|
+
self.N = 15000
|
|
12
|
+
self.N_0 = 2000
|
|
13
|
+
self.N_pred = 5000
|
|
14
|
+
self.N_train_adam = 90000
|
|
15
|
+
self.N_train_lbfgs = 3000
|
|
16
|
+
|
|
17
|
+
def create_model(self, num_hidden=4, num_neurons=64):
|
|
18
|
+
"""Creates the neural network"""
|
|
19
|
+
model = tf.keras.Sequential()
|
|
20
|
+
model.add(tf.keras.layers.InputLayer(input_shape=(1,)))
|
|
21
|
+
|
|
22
|
+
for _ in range(num_hidden):
|
|
23
|
+
model.add(tf.keras.layers.Dense(num_neurons, activation='tanh',
|
|
24
|
+
kernel_initializer='glorot_normal'))
|
|
25
|
+
|
|
26
|
+
model.add(tf.keras.layers.Dense(2 * self.config.n))
|
|
27
|
+
return model
|
|
28
|
+
|
|
29
|
+
def physics_loss(self, model, t_colloc_norm, E_norm, psi_st_tensor, HP_tensor, H_t_r, H_t_i):
|
|
30
|
+
"""Calculates physics loss based on PDE with consistent units"""
|
|
31
|
+
with tf.GradientTape(persistent=True) as tape:
|
|
32
|
+
tape.watch(t_colloc_norm)
|
|
33
|
+
psi_pred = model(t_colloc_norm)
|
|
34
|
+
u = psi_pred[:, :self.config.n]
|
|
35
|
+
v = psi_pred[:, self.config.n:]
|
|
36
|
+
|
|
37
|
+
u_t = tape.batch_jacobian(u, t_colloc_norm)[:, :, 0]
|
|
38
|
+
v_t = tape.batch_jacobian(v, t_colloc_norm)[:, :, 0]
|
|
39
|
+
del tape
|
|
40
|
+
# Source term S(t)
|
|
41
|
+
E_complex = tf.cast(E_norm, tf.complex64)
|
|
42
|
+
t_complex = tf.cast(tf.squeeze(t_colloc_norm), tf.complex64)
|
|
43
|
+
phase = tf.exp(-1j * E_complex * t_complex)
|
|
44
|
+
phase = tf.expand_dims(phase, axis=-1) # [N, 1]
|
|
45
|
+
|
|
46
|
+
psi_st_broadcast = tf.transpose(psi_st_tensor) # [1, n]
|
|
47
|
+
expo_term = phase * psi_st_broadcast # [N, n]
|
|
48
|
+
expo_term = tf.expand_dims(expo_term, axis=-1) # [N, n, 1]
|
|
49
|
+
|
|
50
|
+
S_complex = tf.matmul(HP_tensor, expo_term) # [N, n, n] @ [N, n, 1] = [N, n, 1]
|
|
51
|
+
S_complex = tf.squeeze(S_complex, axis=-1) # [N, n]
|
|
52
|
+
S_r = tf.math.real(S_complex)
|
|
53
|
+
S_i = tf.math.imag(S_complex)
|
|
54
|
+
|
|
55
|
+
# Residual equations
|
|
56
|
+
u_exp = tf.expand_dims(u, axis=-1) # [N, n, 1]
|
|
57
|
+
v_exp = tf.expand_dims(v, axis=-1) # [N, n, 1]
|
|
58
|
+
|
|
59
|
+
# H_t_r et H_t_i are [N, n, n]
|
|
60
|
+
# u_exp et v_exp are [N, n, 1]
|
|
61
|
+
# Result matmul: [N, n, 1] -> squeeze -> [N, n]
|
|
62
|
+
r_u = u_t - tf.squeeze(tf.matmul(H_t_r, v_exp), axis=-1) - tf.squeeze(tf.matmul(H_t_i, u_exp), axis=-1) - S_i
|
|
63
|
+
r_v = -v_t - tf.squeeze(tf.matmul(H_t_r, u_exp), axis=-1) + tf.squeeze(tf.matmul(H_t_i, v_exp), axis=-1) - S_r
|
|
64
|
+
|
|
65
|
+
pde_loss = tf.reduce_mean(tf.square(r_u)) + tf.reduce_mean(tf.square(r_v))
|
|
66
|
+
return pde_loss
|
|
67
|
+
|
|
68
|
+
def initial_conditions_loss(self, model, t_0_norm):
|
|
69
|
+
"""Loss for initial conditions (zero)"""
|
|
70
|
+
psi_pred_0 = model(t_0_norm)
|
|
71
|
+
u_0 = psi_pred_0[:, :self.config.n]
|
|
72
|
+
v_0 = psi_pred_0[:, self.config.n:]
|
|
73
|
+
ic_loss = tf.reduce_mean(tf.square(u_0)) + tf.reduce_mean(tf.square(v_0))
|
|
74
|
+
return ic_loss
|
|
75
|
+
|
|
76
|
+
def total_loss(self, model, t_colloc_norm, t_0_norm, E_norm, psi_st_tensor, HP_tensor, H_t_r, H_t_i):
|
|
77
|
+
"""Total loss combining physics and initial conditions"""
|
|
78
|
+
pde_loss = self.physics_loss(model, t_colloc_norm, E_norm, psi_st_tensor, HP_tensor, H_t_r, H_t_i)
|
|
79
|
+
ic_loss = self.initial_conditions_loss(model, t_0_norm)
|
|
80
|
+
|
|
81
|
+
lambda_pde = 1.0
|
|
82
|
+
lambda_ic = 1.0
|
|
83
|
+
|
|
84
|
+
return lambda_pde * pde_loss + lambda_ic * ic_loss, pde_loss, ic_loss
|
|
85
|
+
|
|
86
|
+
def get_weights(self, model):
|
|
87
|
+
"""Extracts model weights as 1D vector"""
|
|
88
|
+
weights = []
|
|
89
|
+
for layer in model.layers:
|
|
90
|
+
if hasattr(layer, 'get_weights') and layer.get_weights():
|
|
91
|
+
for w in layer.get_weights():
|
|
92
|
+
weights.append(w.flatten())
|
|
93
|
+
return np.concatenate(weights) if weights else np.array([])
|
|
94
|
+
|
|
95
|
+
def set_weights(self, model, weights_1d):
|
|
96
|
+
"""Restores model weights from 1D vector"""
|
|
97
|
+
idx = 0
|
|
98
|
+
for layer in model.layers:
|
|
99
|
+
if hasattr(layer, 'get_weights') and layer.get_weights():
|
|
100
|
+
layer_weights = []
|
|
101
|
+
for w in layer.get_weights():
|
|
102
|
+
size = w.size
|
|
103
|
+
new_w = weights_1d[idx:idx+size].reshape(w.shape)
|
|
104
|
+
layer_weights.append(new_w)
|
|
105
|
+
idx += size
|
|
106
|
+
layer.set_weights(layer_weights)
|
|
107
|
+
|
|
108
|
+
def lbfgs_loss_func(self, weights_1d, model, t_colloc_norm, t_0_norm, E_norm,
|
|
109
|
+
psi_st_tensor, HP_tensor, H_t_r, H_t_i):
|
|
110
|
+
"""Loss function for L-BFGS"""
|
|
111
|
+
self.set_weights(model, weights_1d)
|
|
112
|
+
|
|
113
|
+
with tf.GradientTape() as tape:
|
|
114
|
+
loss, pde_loss, ic_loss = self.total_loss(
|
|
115
|
+
model, t_colloc_norm, t_0_norm, E_norm, psi_st_tensor, HP_tensor, H_t_r, H_t_i
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
gradients = tape.gradient(loss, model.trainable_variables)
|
|
119
|
+
grad_1d = []
|
|
120
|
+
for grad in gradients:
|
|
121
|
+
if grad is not None:
|
|
122
|
+
grad_1d.append(grad.numpy().flatten())
|
|
123
|
+
grad_1d = np.concatenate(grad_1d) if grad_1d else np.array([])
|
|
124
|
+
|
|
125
|
+
return float(loss.numpy()), grad_1d.astype(np.float64)
|
|
126
|
+
|
|
127
|
+
def train_hybrid(self, model, t_colloc_norm, t_0_norm, E_norm, psi_st_tensor,
|
|
128
|
+
HP_tensor, H_t_r, H_t_i):
|
|
129
|
+
"""Trains the model with Adam then L-BFGS"""
|
|
130
|
+
|
|
131
|
+
# Phase 1: Adam training
|
|
132
|
+
print(" Phase 1: Adam training...")
|
|
133
|
+
|
|
134
|
+
# Adam optimizer with adaptive learning rate
|
|
135
|
+
initial_lr = 1e-3
|
|
136
|
+
lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
|
|
137
|
+
initial_learning_rate=initial_lr,
|
|
138
|
+
decay_steps=30000,
|
|
139
|
+
decay_rate=0.9,
|
|
140
|
+
staircase=True
|
|
141
|
+
)
|
|
142
|
+
optimizer_adam = tf.keras.optimizers.Adam(learning_rate=lr_schedule)
|
|
143
|
+
|
|
144
|
+
@tf.function
|
|
145
|
+
def adam_train_step():
|
|
146
|
+
with tf.GradientTape() as tape:
|
|
147
|
+
loss, pde_loss, ic_loss = self.total_loss(
|
|
148
|
+
model, t_colloc_norm, t_0_norm, E_norm, psi_st_tensor, HP_tensor, H_t_r, H_t_i
|
|
149
|
+
)
|
|
150
|
+
gradients = tape.gradient(loss, model.trainable_variables)
|
|
151
|
+
optimizer_adam.apply_gradients(zip(gradients, model.trainable_variables))
|
|
152
|
+
return loss, pde_loss, ic_loss
|
|
153
|
+
|
|
154
|
+
adam_history = {'total_loss': [], 'pde_loss': [], 'ic_loss': []}
|
|
155
|
+
t_start_adam = time()
|
|
156
|
+
|
|
157
|
+
for epoch in range(self.N_train_adam):
|
|
158
|
+
loss, pde_loss, ic_loss = adam_train_step()
|
|
159
|
+
|
|
160
|
+
if epoch % 1000 == 0:
|
|
161
|
+
print(f" Adam Epoch {epoch:5d}: Total = {loss:.6e}, PDE = {pde_loss:.6e}, IC = {ic_loss:.6e}")
|
|
162
|
+
|
|
163
|
+
adam_history['total_loss'].append(float(loss))
|
|
164
|
+
adam_history['pde_loss'].append(float(pde_loss))
|
|
165
|
+
adam_history['ic_loss'].append(float(ic_loss))
|
|
166
|
+
|
|
167
|
+
# Early stopping criterion for Adam
|
|
168
|
+
if epoch > 1000 and loss < 1e-4:
|
|
169
|
+
print(f" Early stopping at epoch {epoch}")
|
|
170
|
+
break
|
|
171
|
+
|
|
172
|
+
print(f" Adam training time: {time() - t_start_adam:.2f}s")
|
|
173
|
+
print(f" Final Adam loss: {loss:.6e}")
|
|
174
|
+
|
|
175
|
+
# Phase 2: L-BFGS refinement
|
|
176
|
+
print(" Phase 2: L-BFGS refinement...")
|
|
177
|
+
|
|
178
|
+
t_start_lbfgs = time()
|
|
179
|
+
|
|
180
|
+
# Initial weights for L-BFGS
|
|
181
|
+
initial_weights = self.get_weights(model)
|
|
182
|
+
|
|
183
|
+
# L-BFGS configuration
|
|
184
|
+
lbfgs_options = {
|
|
185
|
+
'maxiter': self.N_train_lbfgs,
|
|
186
|
+
'maxfun': self.N_train_lbfgs * 2,
|
|
187
|
+
'ftol': 1e-12,
|
|
188
|
+
'gtol': 1e-12,
|
|
189
|
+
'eps': 1e-8,
|
|
190
|
+
'disp': True
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
# Variables to track L-BFGS iterations
|
|
194
|
+
self.lbfgs_iter = 0
|
|
195
|
+
self.lbfgs_losses = []
|
|
196
|
+
|
|
197
|
+
def callback_func(xk):
|
|
198
|
+
self.lbfgs_iter += 1
|
|
199
|
+
if self.lbfgs_iter % 100 == 0:
|
|
200
|
+
self.set_weights(model, xk)
|
|
201
|
+
loss, pde_loss, ic_loss = self.total_loss(
|
|
202
|
+
model, t_colloc_norm, t_0_norm, E_norm, psi_st_tensor, HP_tensor, H_t_r, H_t_i
|
|
203
|
+
)
|
|
204
|
+
print(f" L-BFGS Iter {self.lbfgs_iter:4d}: Total = {loss:.6e}, PDE = {pde_loss:.6e}, IC = {ic_loss:.6e}")
|
|
205
|
+
self.lbfgs_losses.append(float(loss))
|
|
206
|
+
|
|
207
|
+
# L-BFGS optimization
|
|
208
|
+
try:
|
|
209
|
+
result = scipy.optimize.minimize(
|
|
210
|
+
fun=lambda w: self.lbfgs_loss_func(w, model, t_colloc_norm, t_0_norm,
|
|
211
|
+
E_norm, psi_st_tensor, HP_tensor, H_t_r, H_t_i),
|
|
212
|
+
x0=initial_weights.astype(np.float64),
|
|
213
|
+
method='L-BFGS-B',
|
|
214
|
+
jac=True,
|
|
215
|
+
callback=callback_func,
|
|
216
|
+
options=lbfgs_options
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
self.set_weights(model, result.x)
|
|
220
|
+
|
|
221
|
+
print(f" L-BFGS completed: {result.message}")
|
|
222
|
+
print(f" L-BFGS iterations: {result.nit}")
|
|
223
|
+
print(f" Final L-BFGS loss: {result.fun:.6e}")
|
|
224
|
+
|
|
225
|
+
except Exception as e:
|
|
226
|
+
print(f" L-BFGS error: {e}")
|
|
227
|
+
print(" Using Adam weights as final solution")
|
|
228
|
+
|
|
229
|
+
print(f" L-BFGS training time: {time() - t_start_lbfgs:.2f}s")
|
|
230
|
+
|
|
231
|
+
# Final Evaluation
|
|
232
|
+
final_loss, final_pde_loss, final_ic_loss = self.total_loss(
|
|
233
|
+
model, t_colloc_norm, t_0_norm, E_norm, psi_st_tensor, HP_tensor, H_t_r, H_t_i
|
|
234
|
+
)
|
|
235
|
+
print(f" Final combined loss: {final_loss:.6e}")
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
'adam_history': adam_history,
|
|
239
|
+
'lbfgs_losses': self.lbfgs_losses,
|
|
240
|
+
'final_loss': float(final_loss),
|
|
241
|
+
'final_pde_loss': float(final_pde_loss),
|
|
242
|
+
'final_ic_loss': float(final_ic_loss)
|
|
243
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# quantum_config.py
|
|
2
|
+
import numpy as np
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
class QuantumConfig:
|
|
6
|
+
"""Configuration of physical parameters and normalization"""
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
# Physical constants
|
|
10
|
+
self.e_charge = 1.602176634e-19
|
|
11
|
+
self.hbar = 6.582119514e-16
|
|
12
|
+
self.KB = 8.6173303e-5
|
|
13
|
+
self.pi = np.pi
|
|
14
|
+
|
|
15
|
+
# System parameters
|
|
16
|
+
self.n = 3
|
|
17
|
+
self.Ne = 20
|
|
18
|
+
self.E_min, self.E_max = -2.5, 2.5
|
|
19
|
+
self.Ef, self.Temp = 0.0, 10
|
|
20
|
+
|
|
21
|
+
# Hamiltonian
|
|
22
|
+
self.GammaL, self.GammaR = 0.05, 0.05
|
|
23
|
+
self.molE, self.tm = 0.7, 0.1
|
|
24
|
+
|
|
25
|
+
# Time
|
|
26
|
+
self.Power = 1.0e-15
|
|
27
|
+
self.t_min, self.t_max = 0, 100 * self.Power
|
|
28
|
+
|
|
29
|
+
self._setup_normalization()
|
|
30
|
+
self._record_simulation_time()
|
|
31
|
+
|
|
32
|
+
def _setup_normalization(self):
|
|
33
|
+
"""Normalization of physical units"""
|
|
34
|
+
self.E_scale = abs(self.molE)
|
|
35
|
+
self.t_scale = self.hbar / self.E_scale
|
|
36
|
+
self.current_scale = self.e_charge / self.t_scale
|
|
37
|
+
|
|
38
|
+
# Normalized parameters
|
|
39
|
+
self.E_min_norm = self.E_min / self.E_scale
|
|
40
|
+
self.E_max_norm = self.E_max / self.E_scale
|
|
41
|
+
self.dE_norm = (self.E_max_norm - self.E_min_norm) / self.Ne
|
|
42
|
+
self.Ef_norm = self.Ef / self.E_scale
|
|
43
|
+
self.KBT_norm = (self.KB * self.Temp) / self.E_scale
|
|
44
|
+
self.t_min_norm = self.t_min / self.t_scale
|
|
45
|
+
self.t_max_norm = self.t_max / self.t_scale
|
|
46
|
+
|
|
47
|
+
def _record_simulation_time(self):
|
|
48
|
+
"""Records the simulation start time"""
|
|
49
|
+
self.start_time = datetime.now()
|
|
50
|
+
self.simulation_id = self.start_time.strftime("%Y%m%d_%H%M%S")
|