Skip to content

Commit 0784e65

Browse files
committed
Include evaluation numbers in return info
1 parent b9bd15c commit 0784e65

File tree

3 files changed

+53
-36
lines changed

3 files changed

+53
-36
lines changed

dfols/controller.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,12 @@ def initialise_coordinate_directions(self, number_of_samples, num_directions, pa
240240
# Handle exit conditions (f < min obj value or maxfun reached)
241241
if exit_info is not None:
242242
if num_samples_run > 0:
243-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
243+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
244244
x_in_abs_coords=True)
245245
return exit_info # return & quit
246246

247247
# Otherwise, add new results (increments model.npt_so_far)
248-
self.model.change_point(k+1, x - self.model.xbase, rvec_list[0, :]) # expect step, not absolute x
248+
self.model.change_point(k+1, x - self.model.xbase, rvec_list[0, :], self.nx) # expect step, not absolute x
249249
for i in range(1, num_samples_run):
250250
self.model.add_new_sample(k+1, rvec_extra=rvec_list[i, :])
251251

@@ -299,12 +299,12 @@ def initialise_coordinate_directions(self, number_of_samples, num_directions, pa
299299
# Handle exit conditions (f < min obj value or maxfun reached)
300300
if exit_info is not None:
301301
if num_samples_run > 0:
302-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
302+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
303303
x_in_abs_coords=True)
304304
return exit_info # return & quit
305305

306306
# Otherwise, add new results (increments model.npt_so_far)
307-
self.model.change_point(k, x - self.model.xbase, rvec_list[0, :]) # expect step, not absolute x
307+
self.model.change_point(k, x - self.model.xbase, rvec_list[0, :], self.nx) # expect step, not absolute x
308308
for i in range(1, num_samples_run):
309309
self.model.add_new_sample(k, rvec_extra=rvec_list[i, :])
310310

@@ -351,13 +351,13 @@ def initialise_random_directions(self, number_of_samples, num_directions, params
351351
# Handle exit conditions (f < min obj value or maxfun reached)
352352
if exit_info is not None:
353353
if num_samples_run > 0:
354-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
354+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
355355
x_in_abs_coords=True)
356356
return exit_info # return & quit
357357

358358
# Otherwise, add new results (increments model.npt_so_far)
359359
self.model.change_point(1 + ndirns, x - self.model.xbase,
360-
rvec_list[0, :]) # expect step, not absolute x
360+
rvec_list[0, :], self.nx) # expect step, not absolute x
361361
for i in range(1, num_samples_run):
362362
self.model.add_new_sample(1 + ndirns, rvec_extra=rvec_list[i, :])
363363
else:
@@ -371,12 +371,12 @@ def initialise_random_directions(self, number_of_samples, num_directions, params
371371
# Handle exit conditions (f < min obj value or maxfun reached)
372372
if exit_info is not None:
373373
if num_samples_run > 0:
374-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
374+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
375375
x_in_abs_coords=True)
376376
return exit_info # return & quit
377377

378378
# Otherwise, add new results (increments model.npt_so_far)
379-
self.model.change_point(1 + ndirns, x - self.model.xbase, rvec_list[0, :]) # expect step, not absolute x
379+
self.model.change_point(1 + ndirns, x - self.model.xbase, rvec_list[0, :], self.nx) # expect step, not absolute x
380380
for i in range(1, num_samples_run):
381381
self.model.add_new_sample(1 + ndirns, rvec_extra=rvec_list[i, :])
382382

@@ -408,7 +408,7 @@ def add_new_direction_while_growing(self, number_of_samples, params, min_num_ste
408408
# Handle exit conditions (f < min obj value or maxfun reached)
409409
if exit_info is not None:
410410
if num_samples_run > 0:
411-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
411+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
412412
x_in_abs_coords=True)
413413
return exit_info # return & quit
414414

@@ -422,7 +422,7 @@ def add_new_direction_while_growing(self, number_of_samples, params, min_num_ste
422422
return exit_info # return & quit
423423

424424
# Otherwise, add new results
425-
self.model.change_point(kmin, xnew, rvec_list[0, :]) # expect step, not absolute x
425+
self.model.change_point(kmin, xnew, rvec_list[0, :], self.nx) # expect step, not absolute x
426426
for i in range(1, num_samples_run):
427427
self.model.add_new_sample(kmin, rvec_extra=rvec_list[i, :])
428428

@@ -548,12 +548,12 @@ def geometry_step(self, knew, adelt, number_of_samples, params):
548548
# Handle exit conditions (f < min obj value or maxfun reached)
549549
if exit_info is not None:
550550
if num_samples_run > 0:
551-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
551+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
552552
x_in_abs_coords=True)
553553
return exit_info # didn't fix geometry - return & quit
554554

555555
# Otherwise, add new results
556-
self.model.change_point(knew, xnew, rvec_list[0, :]) # expect step, not absolute x
556+
self.model.change_point(knew, xnew, rvec_list[0, :], self.nx) # expect step, not absolute x
557557
for i in range(1, num_samples_run):
558558
self.model.add_new_sample(knew, rvec_extra=rvec_list[i, :])
559559

@@ -768,8 +768,8 @@ def soft_restart(self, number_of_samples, nruns_so_far, params, x_in_abs_coords_
768768
if x_in_abs_coords_to_save is not None:
769769
assert rvec_to_save is not None, "Soft restart: specified x_to_save but not rvec_to_save"
770770
assert nsamples_to_save is not None, "Soft restart: specified x_to_save but not nsamples_to_save"
771-
self.model.save_point(x_in_abs_coords_to_save, rvec_to_save, nsamples_to_save, x_in_abs_coords=True)
772-
self.model.save_point(self.model.xopt(abs_coordinates=True), self.model.ropt(),
771+
self.model.save_point(x_in_abs_coords_to_save, rvec_to_save, nsamples_to_save, self.nx, x_in_abs_coords=True)
772+
self.model.save_point(self.model.xopt(abs_coordinates=True), self.model.ropt(), self.nx,
773773
self.model.nsamples[self.model.kopt], x_in_abs_coords=True)
774774

775775
if self.do_logging:
@@ -820,12 +820,12 @@ def soft_restart(self, number_of_samples, nruns_so_far, params, x_in_abs_coords_
820820
# Handle exit conditions (f < min obj value or maxfun reached)
821821
if exit_info is not None:
822822
if num_samples_run > 0:
823-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
823+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
824824
x_in_abs_coords=True)
825825
return exit_info # return & quit
826826

827827
# Otherwise, add new results
828-
self.model.add_new_point(xnew, rvec_list[0, :]) # expect step, not absolute x
828+
self.model.add_new_point(xnew, rvec_list[0, :], self.nx) # expect step, not absolute x
829829
for i in range(1, num_samples_run):
830830
self.model.add_new_sample(self.model.npt() - 1, rvec_extra=rvec_list[i, :])
831831

@@ -900,12 +900,12 @@ def move_furthest_points_momentum(self, d, number_of_samples, num_pts_to_move, p
900900
# Handle exit conditions (f < min obj value or maxfun reached)
901901
if exit_info is not None:
902902
if num_samples_run > 0:
903-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
903+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
904904
x_in_abs_coords=True)
905905
return exit_info # return & quit
906906

907907
# Otherwise, add new results
908-
self.model.change_point(knew, xnew, rvec_list[0, :]) # expect step, not absolute x
908+
self.model.change_point(knew, xnew, rvec_list[0, :], self.nx) # expect step, not absolute x
909909
for i in range(1, num_samples_run):
910910
self.model.add_new_sample(knew, rvec_extra=rvec_list[i, :])
911911
return None

dfols/model.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ def __init__(self, npt, x0, r0, xl, xu, projections, r0_nsamples, h=None, argsh=
8585
self.nsamples = np.zeros((npt,), dtype=int) # number of samples used to evaluate objective at each point
8686
self.nsamples[0] = r0_nsamples
8787
self.objbeg = self.objval[0] # f(x0), saved to check for sufficient reduction
88+
self.eval_num = np.zeros((npt,), dtype=int) # which evaluation number (1-indexed, nx not nf) is currently stored self.points[k,:]
89+
self.eval_num[0] = 1
8890

8991
# Termination criteria
9092
self.abs_tol = abs_tol
@@ -93,13 +95,16 @@ def __init__(self, npt, x0, r0, xl, xu, projections, r0_nsamples, h=None, argsh=
9395
# Model information
9496
self.model_const = np.zeros((m, )) # constant term for model m(s) = c + J*s
9597
self.model_jac = np.zeros((m, n)) # Jacobian term for model m(s) = c + J*s
98+
self.model_jac_eval_nums = np.zeros((npt,), dtype=int) # which evaluation numbers (1-indexed, nx not nf) were used to build model_jac
9699

97100
# Saved point (in absolute coordinates) - always check this value before quitting solver
98101
self.xsave = None
99102
self.rsave = None
100103
self.objsave = None
101104
self.jacsave = None
102105
self.nsamples_save = None
106+
self.eval_num_save = None
107+
self.jacsave_eval_nums = None
103108

104109
# Factorisation of interpolation matrix
105110
self.factorisation_current = False
@@ -174,7 +179,7 @@ def distances_to_xopt(self):
174179
sq_distances[k] = sumsq(self.points[k, :] - xopt)
175180
return sq_distances
176181

177-
def change_point(self, k, x, rvec, allow_kopt_update=True):
182+
def change_point(self, k, x, rvec, eval_num, allow_kopt_update=True):
178183
# Update point k to x (w.r.t. xbase), with residual values fvec
179184
if k >= self.npt_so_far and self.npt_so_far < self.num_pts:
180185
assert k == self.npt_so_far, "Growing: updating wrong point"
@@ -188,6 +193,7 @@ def change_point(self, k, x, rvec, allow_kopt_update=True):
188193
if self.h is not None:
189194
self.objval[k] += self.h(remove_scaling(self.xbase + x, self.scaling_changes), *self.argsh)
190195
self.nsamples[k] = 1
196+
self.eval_num[k] = eval_num
191197
self.factorisation_current = False
192198

193199
if allow_kopt_update and self.objval[k] < self.objopt():
@@ -198,6 +204,7 @@ def swap_points(self, k1, k2):
198204
self.points[[k1, k2], :] = self.points[[k2, k1], :]
199205
self.fval_v[[k1, k2], :] = self.fval_v[[k2, k1], :]
200206
self.objval[[k1, k2]] = self.objval[[k2, k1]]
207+
self.eval_num[[k1, k2]] = self.eval_num[[k2, k1]]
201208
if self.kopt == k1:
202209
self.kopt = k2
203210
elif self.kopt == k2:
@@ -219,14 +226,15 @@ def add_new_sample(self, k, rvec_extra):
219226
self.kopt = np.argmin(self.objval[:self.npt()]) # make sure kopt is always the best value we have
220227
return
221228

222-
def add_new_point(self, x, rvec):
229+
def add_new_point(self, x, rvec, eval_num):
223230
self.points = np.append(self.points, x.reshape((1, self.n())), axis=0) # append row to xpt
224231
self.fval_v = np.append(self.fval_v, rvec.reshape((1, self.m())), axis=0) # append row to fval_v
225232
obj = sumsq(rvec)
226233
if self.h is not None:
227234
obj += self.h(remove_scaling(self.xbase + x, self.scaling_changes), *self.argsh)
228235
self.objval = np.append(self.objval, obj) # append entry to fval
229236
self.nsamples = np.append(self.nsamples, 1) # add new sample number
237+
self.eval_num = np.append(self.eval_num, eval_num) # add new evaluation number
230238
self.num_pts += 1 # make sure npt is updated
231239
self.npt_so_far += 1
232240

@@ -249,7 +257,7 @@ def shift_base(self, xbase_shift):
249257
self.model_const += np.dot(self.model_jac, xbase_shift)
250258
return
251259

252-
def save_point(self, x, rvec, nsamples, x_in_abs_coords=True):
260+
def save_point(self, x, rvec, nsamples, eval_num, x_in_abs_coords=True):
253261
xabs = x.copy() if x_in_abs_coords else self.as_absolute_coordinates(x)
254262
obj = sumsq(rvec)
255263
if self.h is not None:
@@ -260,16 +268,18 @@ def save_point(self, x, rvec, nsamples, x_in_abs_coords=True):
260268
self.objsave = obj
261269
self.jacsave = self.model_jac.copy()
262270
self.nsamples_save = nsamples
271+
self.eval_num_save = eval_num
272+
self.jacsave_eval_nums = self.model_jac_eval_nums.copy()
263273
return True
264274
else:
265275
return False # this value is worse than what we have already - didn't save
266276

267277
def get_final_results(self):
268278
# Return x and objval for optimal point (either from xsave+objsave or kopt)
269279
if self.objsave is None or self.objopt() <= self.objsave: # optimal has changed since xsave+objsave were last set
270-
return self.xopt(abs_coordinates=True).copy(), self.ropt().copy(), self.objopt(), self.model_jac.copy(), self.nsamples[self.kopt]
280+
return self.xopt(abs_coordinates=True).copy(), self.ropt().copy(), self.objopt(), self.model_jac.copy(), self.nsamples[self.kopt], self.eval_num[self.kopt], self.model_jac_eval_nums
271281
else:
272-
return self.xsave.copy(), self.rsave.copy(), self.objsave, self.jacsave, self.nsamples_save
282+
return self.xsave.copy(), self.rsave.copy(), self.objsave, self.jacsave, self.nsamples_save, self.eval_num_save, self.jacsave_eval_nums
273283

274284
def min_objective_value(self):
275285
# Get termination criterion for f small: f <= abs_tol or f <= rel_tol * f0
@@ -367,6 +377,7 @@ def interpolate_mini_models_svd(self, verbose=False, make_full_rank=False, min_s
367377
J_old = self.model_jac.copy()
368378
self.model_jac = dg[1:,:].T
369379
self.model_const = dg[0,:] - np.dot(self.model_jac, xopt) # shift base to xbase
380+
self.model_jac_eval_nums = self.eval_num.copy()
370381
if verbose or get_chg_J:
371382
norm_J_error = np.linalg.norm(self.model_jac - J_old, ord='fro')**2
372383
linalg_resid = np.linalg.norm(W.dot(dg) - rhs)**2

0 commit comments

Comments
 (0)