Skip to content

Commit 6d8eed0

Browse files
committed
make the code working
1 parent feec8c1 commit 6d8eed0

File tree

1 file changed

+57
-48
lines changed

1 file changed

+57
-48
lines changed

src/qutip_qoc/_genetic.py

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ def __init__(
4141
super(_GENETIC, self).__init__()
4242
self.objectives = objectives
4343
self.control_parameters = control_parameters
44-
self.time_interval = time_interval
45-
self.time_options = time_options
4644
self.alg_kwargs = alg_kwargs
4745
self._integrator_kwargs = integrator_kwargs
4846
self._rtol = self._integrator_kwargs.get("rtol", 1e-5)
@@ -55,7 +53,7 @@ def __init__(
5553
self.N_parents = int(np.floor(self.N_pop * self.parent_rate))
5654
self.N_survivors = max(1, int(self.N_parents * alg_kwargs.get('survival_rate', 1.0)))
5755
self.N_offspring = self.N_pop - self.N_survivors
58-
self.mutation_rate = alg_kwargs.get('mutation_rate', 0.1)
56+
self.mutation_rate = alg_kwargs.get('mutation_rate', 0.2)
5957
self.workers = alg_kwargs.get('workers', 1)
6058

6159
self._Hd_lst, self._Hc_lst = [], []
@@ -138,8 +136,10 @@ def create_pulse_func(idx):
138136
self._integrator_kwargs = integrator_kwargs
139137
self._rtol = self._integrator_kwargs.get("rtol", 1e-5)
140138
self._atol = self._integrator_kwargs.get("atol", 1e-5)
141-
142-
self._step_duration = 10
139+
#self.max_episode_time = time_interval.evo_time # maximum time for an episode
140+
#self.max_steps = time_interval.n_tslots # maximum number of steps in an episode
141+
self._step_duration = 2
142+
143143
# create the solver
144144
if self._Hd_lst[0].issuper:
145145
self._fid_type = self._alg_kwargs.get("fid_type", "TRACEDIFF")
@@ -169,25 +169,26 @@ def _infid(self, args):
169169
X = self._solver.run(
170170
self._state, [0.0, self._step_duration], args=args
171171
).final_state
172-
self._state = X
172+
#self._state = X
173173

174-
target_dm = qt.ket2dm(self._target)
175-
if self._fid_type == "TRACEDIFF":
176-
diff = X - target_dm
177-
# to prevent if/else in qobj.dag() and qobj.tr()
178-
diff_dag = qt.Qobj(diff.data.adjoint(), dims=diff.dims)
179-
g = 1 / 2 * (diff_dag * diff).data.trace()
180-
infid = np.real(self._norm_fac * g)
174+
# target_dm = qt.ket2dm(self._target)
175+
# if self._fid_type == "TRACEDIFF":
176+
# diff = X - target_dm
177+
# # to prevent if/else in qobj.dag() and qobj.tr()
178+
# diff_dag = qt.Qobj(diff.data.adjoint(), dims=diff.dims)
179+
# g = 1 / 2 * (diff_dag * diff).data.trace()
180+
# infid = np.real(self._norm_fac * g)
181+
# else:
182+
# g = self._norm_fac * self._target.overlap(X)
183+
# if self._fid_type == "PSU": # f_PSU (drop global phase)
184+
# infid = 1 - np.abs(g)
185+
# elif self._fid_type == "SU": # f_SU (incl global phase)
186+
# infid = 1 - np.real(g)
187+
if self._target.isket:
188+
return 1-qt.fidelity(self._target, X)
181189
else:
182-
g = self._norm_fac * self._target.overlap(X)
183-
if self._fid_type == "PSU": # f_PSU (drop global phase)
184-
infid = 1 - np.abs(g)
185-
elif self._fid_type == "SU": # f_SU (incl global phase)
186-
infid = 1 - np.real(g)
187-
return infid
190+
return 1-qt.fidelity(qt.ket2dm(self._target), X)
188191

189-
190-
"Initialize a first population"
191192
def initial_population(self):
192193
"""Randomly generates an initial popuation to act as generation 0.
193194
@@ -312,50 +313,58 @@ def mutate(self, population):
312313
mutated_population = np.copy(population)
313314
# for some reason clipped normal noise works better.
314315
# mutated_population[row_indices, col_indices] = np.random.uniform(-1,1,size=number_of_mutations) # for uniform random mutation
315-
mutated_population[row_indices, col_indices] = np.clip(population[row_indices, col_indices] + np.random.normal(loc=0, scale=0.2, size=number_of_mutations), a_min=-1, a_max=1) # for normal mutation
316+
mutated_population[row_indices, col_indices] = np.clip(
317+
population[row_indices, col_indices] + np.random.normal(loc=0, scale=0.2, size=number_of_mutations), a_min=-1, a_max=1)
316318
return mutated_population
317319

318320

319321
def optimize(self, iterations=1000):
320-
population = self.initial_population()
321-
best_fitness = -np.inf
322-
best_chromosome = None
323-
fitness_history = []
324-
325-
for count in range(iterations):
326-
print(f"Generation {count + 1}/{iterations}")
327-
322+
population = self.initial_population() # initialise a random seroth generation
323+
bench = -1e10 # the initial benchmark fitness - we assume fitness >= 0
324+
done = False # terminal flag
325+
count = 0 # iteration count
326+
maxfit_hist = [] # to record the maximum fitness at each gen during optimizing
327+
while not done:
328328
fitness_ls = []
329329
for chromosome in population:
330+
# The fitness function should be able to take a raw chromosome (whose constituent values will be \in [-1, 1])
331+
# scale the values to get the true parameter values, then use those parameters to do "stuff" (in our case
332+
# build a control pulse and simulate the dynamics) then return a figure of merit which tells us how good
333+
# this set of parameters has performed (could be fidelity to a target state for example).
334+
# essentially all of the problem under consideration happens inside fitness_func
330335
alphas = [
331336
((chromosome[i] + 1) / 2 * (self._ubound[i] - self._lbound[i]) + self._lbound[i])
332337
for i in range(len(chromosome))
333338
]
334-
args = {f"alpha{i+1}": float(val) for i, val in enumerate(alphas)} # force float
335-
infid_val = self._infid(args)
336-
fitness_ls.append(infid_val)
337-
339+
args = {f"alpha{i+1}": float(val) for i, val in enumerate(alphas)}
340+
f = 1-self._infid(args)
341+
fitness_ls.append(f)
338342
fitness = np.array(fitness_ls)
339-
max_fit = np.min(fitness)
340-
fitness_history.append(max_fit)
341-
342-
if max_fit > best_fitness:
343-
best_fitness = max_fit
344-
best_index = np.argmax(fitness)
345-
best_chromosome = population[best_index, :]
346-
347-
print(f" Best infidelity: {-max_fit:.6f}, Avg fitness: {np.mean(fitness):.6f}")
348-
349-
survivors, survivor_fitness = self.darwin(population, fitness)
343+
max_fit = np.max(fitness)
344+
if max_fit>bench:
345+
print(" EPISODE: {}, average cost: {}, Best Cost: {}".format(count, np.round(np.average(fitness), decimals=4),
346+
np.max(fitness)))
347+
# update benchmark and add max fitness to history
348+
bench = max_fit
349+
maxfit_hist.append(max_fit)
350+
best_index = np.argmax(fitness) # get best index
351+
best_chromosome = population[best_index,:] # so that we can get the best chromosome
352+
353+
# May want to checkpoint here
354+
if count == iterations: # if max iterations reached
355+
done=True
356+
357+
survivors, survivor_fitness = self.darwin(population,fitness)
350358
mothers, fathers = self.pairing(survivors, survivor_fitness)
351359
offspring = self.mating_procedure(ma=mothers, da=fathers)
352-
unmutated_next_gen = self.build_next_gen(survivors, offspring)
360+
unmutated_next_gen = self.build_next_gen(survivors,offspring)
353361
mutated_next_gen = self.mutate(unmutated_next_gen)
354362
population = mutated_next_gen
363+
count+=1
364+
365+
return max_fit, best_chromosome, maxfit_hist
355366

356-
return best_fitness, best_chromosome, fitness_history
357367

358-
359368
def _save_result(self):
360369
"""
361370
Save the results of the optimization process, including the optimized

0 commit comments

Comments
 (0)