DFO-LS 1.5.0__py3-none-any.whl → 1.5.2__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.
Potentially problematic release.
This version of DFO-LS might be problematic. Click here for more details.
- {DFO_LS-1.5.0.dist-info → DFO_LS-1.5.2.dist-info}/METADATA +1 -1
- DFO_LS-1.5.2.dist-info/RECORD +14 -0
- {DFO_LS-1.5.0.dist-info → DFO_LS-1.5.2.dist-info}/WHEEL +1 -1
- dfols/__init__.py +1 -1
- dfols/controller.py +102 -70
- dfols/diagnostic_info.py +1 -1
- dfols/model.py +17 -6
- dfols/solver.py +19 -13
- DFO_LS-1.5.0.dist-info/RECORD +0 -14
- {DFO_LS-1.5.0.dist-info → DFO_LS-1.5.2.dist-info}/LICENSE.txt +0 -0
- {DFO_LS-1.5.0.dist-info → DFO_LS-1.5.2.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: DFO-LS
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.2
|
|
4
4
|
Summary: A flexible derivative-free solver for (bound constrained) nonlinear least-squares minimization
|
|
5
5
|
Author-email: Lindon Roberts <lindon.roberts@sydney.edu.au>
|
|
6
6
|
Maintainer-email: Lindon Roberts <lindon.roberts@sydney.edu.au>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
dfols/__init__.py,sha256=EGaYvogmtsNfmB_MVMFwglCwZbhl8O18rsOldwjIr5Q,1605
|
|
2
|
+
dfols/controller.py,sha256=Jffyao_z7wcQf1WEQtv2smnNew8HXGguWuUPLbgVuCc,52487
|
|
3
|
+
dfols/diagnostic_info.py,sha256=kEcFCjD2rk39XRa90ocEaQvJWc0wj_ZPpQkOulVIM-k,6106
|
|
4
|
+
dfols/hessian.py,sha256=sExx4J4KoGwHItbthX2odosB2ONbQFvLdlcod7PIh4k,4262
|
|
5
|
+
dfols/model.py,sha256=1Npj3fJvMv66bKu_RIzLLI-2tyzPWOsKuyv-YUjcv2c,20711
|
|
6
|
+
dfols/params.py,sha256=GzJGO0TByH1X3B0NbLOCOqmYG8dRiKPKjjX7or_fOqI,18342
|
|
7
|
+
dfols/solver.py,sha256=tvDdZgQ3AtybIZCTJf2DvGo6YvbV1NNACrzT2RUF000,63910
|
|
8
|
+
dfols/trust_region.py,sha256=JbHLBDw7H88a3cIMuialh7kpMNGjL3Lp9JsjrBNpDWQ,28231
|
|
9
|
+
dfols/util.py,sha256=efGVAKPb7YrHya1IOgyzacwa_h0u2jHHs5FhuxUlYDg,10282
|
|
10
|
+
DFO_LS-1.5.2.dist-info/LICENSE.txt,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
11
|
+
DFO_LS-1.5.2.dist-info/METADATA,sha256=eTT5r3VaND81vjaSdiW8AcUn6HsISxXAgV5UNXAHnBk,8069
|
|
12
|
+
DFO_LS-1.5.2.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
|
13
|
+
DFO_LS-1.5.2.dist-info/top_level.txt,sha256=UfxRhaDN8HQx2_l17KbrDrERJ90OCN7VKkDMpYYbRLU,6
|
|
14
|
+
DFO_LS-1.5.2.dist-info/RECORD,,
|
dfols/__init__.py
CHANGED
dfols/controller.py
CHANGED
|
@@ -240,12 +240,12 @@ class Controller(object):
|
|
|
240
240
|
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
241
241
|
if exit_info is not None:
|
|
242
242
|
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,
|
|
244
244
|
x_in_abs_coords=True)
|
|
245
245
|
return exit_info # return & quit
|
|
246
246
|
|
|
247
247
|
# 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
|
|
249
249
|
for i in range(1, num_samples_run):
|
|
250
250
|
self.model.add_new_sample(k+1, rvec_extra=rvec_list[i, :])
|
|
251
251
|
|
|
@@ -253,69 +253,101 @@ class Controller(object):
|
|
|
253
253
|
|
|
254
254
|
at_lower_boundary = (self.model.sl > -0.01 * self.delta) # sl = xl - x0, should be -ve, actually < -rhobeg
|
|
255
255
|
at_upper_boundary = (self.model.su < 0.01 * self.delta) # su = xu - x0, should be +ve, actually > rhobeg
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
#
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
256
|
+
|
|
257
|
+
if params("init.run_in_parallel") and num_directions <= self.n():
|
|
258
|
+
# Can do all the evaluation in parallel if <= n+1 interpolation points, but if larger
|
|
259
|
+
# then the step depends on the function value at previous steps and does point swapping
|
|
260
|
+
xpts_added = np.zeros((num_directions + 1, self.n()))
|
|
261
|
+
eval_obj_results = []
|
|
262
|
+
for k in range(1, num_directions + 1): # k = 1, ..., num_directions
|
|
263
|
+
# always have k = 1, ..., n since num_directions <= n
|
|
263
264
|
dirn = k - 1 # direction to move in (0,...,n-1)
|
|
264
265
|
stepa = self.delta if not at_upper_boundary[dirn] else -self.delta # take a +delta step if at lower, -delta if at upper
|
|
265
266
|
stepb = None
|
|
266
267
|
xpts_added[k, dirn] = stepa # set new (relative) point to the step since we haven't done any moving, so relative point is all zeros.
|
|
268
|
+
|
|
269
|
+
# Evaluate objective at this new point
|
|
270
|
+
x = self.model.as_absolute_coordinates(xpts_added[k, :])
|
|
271
|
+
eval_obj_results.append(self.evaluate_objective(x, number_of_samples, params))
|
|
272
|
+
|
|
273
|
+
# Evaluations done, now add to the model
|
|
274
|
+
for k in range(1, num_directions + 1):
|
|
275
|
+
x = self.model.as_absolute_coordinates(xpts_added[k, :])
|
|
276
|
+
rvec_list, obj_list, num_samples_run, exit_info = eval_obj_results[k-1]
|
|
277
|
+
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
278
|
+
if exit_info is not None:
|
|
279
|
+
if num_samples_run > 0:
|
|
280
|
+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
|
|
281
|
+
x_in_abs_coords=True)
|
|
282
|
+
return exit_info # return & quit
|
|
267
283
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
284
|
+
# Otherwise, add new results (increments model.npt_so_far)
|
|
285
|
+
self.model.change_point(k, x - self.model.xbase, rvec_list[0, :], self.nx) # expect step, not absolute x
|
|
286
|
+
for i in range(1, num_samples_run):
|
|
287
|
+
self.model.add_new_sample(k, rvec_extra=rvec_list[i, :])
|
|
288
|
+
else:
|
|
289
|
+
xpts_added = np.zeros((num_directions + 1, self.n()))
|
|
290
|
+
for k in range(1, num_directions + 1):
|
|
291
|
+
# k = 0 --> base point (xpt = 0) [ not here]
|
|
292
|
+
# k = 1, ..., 2n --> coordinate directions [1,...,n and n+1,...,2n]
|
|
293
|
+
# k = 2n+1, ..., (n+1)(n+2)/2 --> off-diagonal directions
|
|
294
|
+
if 1 <= k < self.n() + 1: # first step along coord directions
|
|
295
|
+
dirn = k - 1 # direction to move in (0,...,n-1)
|
|
296
|
+
stepa = self.delta if not at_upper_boundary[dirn] else -self.delta # take a +delta step if at lower, -delta if at upper
|
|
297
|
+
stepb = None
|
|
298
|
+
xpts_added[k, dirn] = stepa # set new (relative) point to the step since we haven't done any moving, so relative point is all zeros.
|
|
299
|
+
|
|
300
|
+
elif self.n() + 1 <= k < 2 * self.n() + 1: # second step along coord directions
|
|
301
|
+
dirn = k - self.n() - 1 # direction to move in (0,...,n-1)
|
|
302
|
+
stepa = xpts_added[k - self.n(), dirn] # previous step
|
|
303
|
+
stepb = -self.delta # new step
|
|
304
|
+
if at_lower_boundary[dirn]:
|
|
305
|
+
# if at lower boundary, set the second step to be +ve
|
|
306
|
+
stepb = min(2.0 * self.delta, self.model.su[dirn]) # su = xu - x0, should be +ve
|
|
307
|
+
if at_upper_boundary[dirn]:
|
|
308
|
+
# if at upper boundary, set the second step to be -ve
|
|
309
|
+
stepb = max(-2.0 * self.delta, self.model.sl[dirn]) # sl = xl - x0, should be -ve
|
|
310
|
+
xpts_added[k, dirn] = stepb
|
|
311
|
+
|
|
312
|
+
else: # k = 2n+1, ..., (n+1)(n+2)/2
|
|
313
|
+
# p = (k - 1) % n + 1 # cycles through (1,...,n), starting at 2n+1 --> 1
|
|
314
|
+
# l = (k - 2 * n - 1) / n + 1 # (1,...,1, 2, ..., 2, etc.) where each number appears n times
|
|
315
|
+
# q = (p + l if p + l <= n else p + l - n)
|
|
316
|
+
stepa = None
|
|
317
|
+
stepb = None
|
|
318
|
+
itemp = (k - self.n() - 1) // self.n()
|
|
319
|
+
q = k - itemp * self.n() - self.n()
|
|
320
|
+
p = q + itemp
|
|
321
|
+
if p > self.n():
|
|
322
|
+
p, q = q, p - self.n() # does swap correctly in Python
|
|
323
|
+
|
|
324
|
+
xpts_added[k, p - 1] = xpts_added[p, p - 1]
|
|
325
|
+
xpts_added[k, q - 1] = xpts_added[q, q - 1]
|
|
294
326
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
327
|
+
# Evaluate objective at this new point
|
|
328
|
+
x = self.model.as_absolute_coordinates(xpts_added[k, :])
|
|
329
|
+
rvec_list, obj_list, num_samples_run, exit_info = self.evaluate_objective(x, number_of_samples, params)
|
|
298
330
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
331
|
+
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
332
|
+
if exit_info is not None:
|
|
333
|
+
if num_samples_run > 0:
|
|
334
|
+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
|
|
335
|
+
x_in_abs_coords=True)
|
|
336
|
+
return exit_info # return & quit
|
|
305
337
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
338
|
+
# Otherwise, add new results (increments model.npt_so_far)
|
|
339
|
+
self.model.change_point(k, x - self.model.xbase, rvec_list[0, :], self.nx) # expect step, not absolute x
|
|
340
|
+
for i in range(1, num_samples_run):
|
|
341
|
+
self.model.add_new_sample(k, rvec_extra=rvec_list[i, :])
|
|
310
342
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
343
|
+
# If k exceeds N+1, then the positions of the k-th and (k-N)-th interpolation
|
|
344
|
+
# points may be switched, in order that the function value at the first of them
|
|
345
|
+
# contributes to the off-diagonal second derivative terms of the initial quadratic model.
|
|
346
|
+
# Note: this works because the steps for (k) and (k-n) points were in the same coordinate direction
|
|
347
|
+
if self.n() + 1 <= k < 2 * self.n() + 1:
|
|
348
|
+
# Only swap if steps were in different directions AND new pt has lower objective
|
|
349
|
+
if stepa * stepb < 0.0 and self.model.objval[k] < self.model.objval[k - self.n()]:
|
|
350
|
+
xpts_added[[k, k-self.n()]] = xpts_added[[k-self.n(), k]]
|
|
319
351
|
|
|
320
352
|
return None # return & continue
|
|
321
353
|
|
|
@@ -351,13 +383,13 @@ class Controller(object):
|
|
|
351
383
|
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
352
384
|
if exit_info is not None:
|
|
353
385
|
if num_samples_run > 0:
|
|
354
|
-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
|
|
386
|
+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
|
|
355
387
|
x_in_abs_coords=True)
|
|
356
388
|
return exit_info # return & quit
|
|
357
389
|
|
|
358
390
|
# Otherwise, add new results (increments model.npt_so_far)
|
|
359
391
|
self.model.change_point(1 + ndirns, x - self.model.xbase,
|
|
360
|
-
rvec_list[0, :]) # expect step, not absolute x
|
|
392
|
+
rvec_list[0, :], self.nx) # expect step, not absolute x
|
|
361
393
|
for i in range(1, num_samples_run):
|
|
362
394
|
self.model.add_new_sample(1 + ndirns, rvec_extra=rvec_list[i, :])
|
|
363
395
|
else:
|
|
@@ -371,12 +403,12 @@ class Controller(object):
|
|
|
371
403
|
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
372
404
|
if exit_info is not None:
|
|
373
405
|
if num_samples_run > 0:
|
|
374
|
-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
|
|
406
|
+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
|
|
375
407
|
x_in_abs_coords=True)
|
|
376
408
|
return exit_info # return & quit
|
|
377
409
|
|
|
378
410
|
# 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
|
|
411
|
+
self.model.change_point(1 + ndirns, x - self.model.xbase, rvec_list[0, :], self.nx) # expect step, not absolute x
|
|
380
412
|
for i in range(1, num_samples_run):
|
|
381
413
|
self.model.add_new_sample(1 + ndirns, rvec_extra=rvec_list[i, :])
|
|
382
414
|
|
|
@@ -408,7 +440,7 @@ class Controller(object):
|
|
|
408
440
|
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
409
441
|
if exit_info is not None:
|
|
410
442
|
if num_samples_run > 0:
|
|
411
|
-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
|
|
443
|
+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
|
|
412
444
|
x_in_abs_coords=True)
|
|
413
445
|
return exit_info # return & quit
|
|
414
446
|
|
|
@@ -422,7 +454,7 @@ class Controller(object):
|
|
|
422
454
|
return exit_info # return & quit
|
|
423
455
|
|
|
424
456
|
# Otherwise, add new results
|
|
425
|
-
self.model.change_point(kmin, xnew, rvec_list[0, :]) # expect step, not absolute x
|
|
457
|
+
self.model.change_point(kmin, xnew, rvec_list[0, :], self.nx) # expect step, not absolute x
|
|
426
458
|
for i in range(1, num_samples_run):
|
|
427
459
|
self.model.add_new_sample(kmin, rvec_extra=rvec_list[i, :])
|
|
428
460
|
|
|
@@ -548,12 +580,12 @@ class Controller(object):
|
|
|
548
580
|
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
549
581
|
if exit_info is not None:
|
|
550
582
|
if num_samples_run > 0:
|
|
551
|
-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
|
|
583
|
+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
|
|
552
584
|
x_in_abs_coords=True)
|
|
553
585
|
return exit_info # didn't fix geometry - return & quit
|
|
554
586
|
|
|
555
587
|
# Otherwise, add new results
|
|
556
|
-
self.model.change_point(knew, xnew, rvec_list[0, :]) # expect step, not absolute x
|
|
588
|
+
self.model.change_point(knew, xnew, rvec_list[0, :], self.nx) # expect step, not absolute x
|
|
557
589
|
for i in range(1, num_samples_run):
|
|
558
590
|
self.model.add_new_sample(knew, rvec_extra=rvec_list[i, :])
|
|
559
591
|
|
|
@@ -768,8 +800,8 @@ class Controller(object):
|
|
|
768
800
|
if x_in_abs_coords_to_save is not None:
|
|
769
801
|
assert rvec_to_save is not None, "Soft restart: specified x_to_save but not rvec_to_save"
|
|
770
802
|
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(),
|
|
803
|
+
self.model.save_point(x_in_abs_coords_to_save, rvec_to_save, nsamples_to_save, self.nx, x_in_abs_coords=True)
|
|
804
|
+
self.model.save_point(self.model.xopt(abs_coordinates=True), self.model.ropt(), self.nx,
|
|
773
805
|
self.model.nsamples[self.model.kopt], x_in_abs_coords=True)
|
|
774
806
|
|
|
775
807
|
if self.do_logging:
|
|
@@ -820,12 +852,12 @@ class Controller(object):
|
|
|
820
852
|
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
821
853
|
if exit_info is not None:
|
|
822
854
|
if num_samples_run > 0:
|
|
823
|
-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
|
|
855
|
+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
|
|
824
856
|
x_in_abs_coords=True)
|
|
825
857
|
return exit_info # return & quit
|
|
826
858
|
|
|
827
859
|
# Otherwise, add new results
|
|
828
|
-
self.model.add_new_point(xnew, rvec_list[0, :]) # expect step, not absolute x
|
|
860
|
+
self.model.add_new_point(xnew, rvec_list[0, :], self.nx) # expect step, not absolute x
|
|
829
861
|
for i in range(1, num_samples_run):
|
|
830
862
|
self.model.add_new_sample(self.model.npt() - 1, rvec_extra=rvec_list[i, :])
|
|
831
863
|
|
|
@@ -900,12 +932,12 @@ class Controller(object):
|
|
|
900
932
|
# Handle exit conditions (f < min obj value or maxfun reached)
|
|
901
933
|
if exit_info is not None:
|
|
902
934
|
if num_samples_run > 0:
|
|
903
|
-
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
|
|
935
|
+
self.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, self.nx,
|
|
904
936
|
x_in_abs_coords=True)
|
|
905
937
|
return exit_info # return & quit
|
|
906
938
|
|
|
907
939
|
# Otherwise, add new results
|
|
908
|
-
self.model.change_point(knew, xnew, rvec_list[0, :]) # expect step, not absolute x
|
|
940
|
+
self.model.change_point(knew, xnew, rvec_list[0, :], self.nx) # expect step, not absolute x
|
|
909
941
|
for i in range(1, num_samples_run):
|
|
910
942
|
self.model.add_new_sample(knew, rvec_extra=rvec_list[i, :])
|
|
911
943
|
return None
|
dfols/diagnostic_info.py
CHANGED
|
@@ -102,7 +102,7 @@ class DiagnosticInfo(object):
|
|
|
102
102
|
self.data["rho"].append(control.rho)
|
|
103
103
|
# And from a model?
|
|
104
104
|
self.data["npt"].append(control.model.npt())
|
|
105
|
-
x, rvec, f, jac, nsamples = control.model.get_final_results()
|
|
105
|
+
x, rvec, f, jac, nsamples, eval_num, jac_eval_nums = control.model.get_final_results()
|
|
106
106
|
self.data["xk"].append(remove_scaling(x, control.scaling_changes))
|
|
107
107
|
self.data["rk"].append(rvec)
|
|
108
108
|
self.data["fk"].append(f)
|
dfols/model.py
CHANGED
|
@@ -85,6 +85,8 @@ class Model(object):
|
|
|
85
85
|
self.nsamples = np.zeros((npt,), dtype=int) # number of samples used to evaluate objective at each point
|
|
86
86
|
self.nsamples[0] = r0_nsamples
|
|
87
87
|
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
|
|
88
90
|
|
|
89
91
|
# Termination criteria
|
|
90
92
|
self.abs_tol = abs_tol
|
|
@@ -93,6 +95,7 @@ class Model(object):
|
|
|
93
95
|
# Model information
|
|
94
96
|
self.model_const = np.zeros((m, )) # constant term for model m(s) = c + J*s
|
|
95
97
|
self.model_jac = np.zeros((m, n)) # Jacobian term for model m(s) = c + J*s
|
|
98
|
+
self.model_jac_eval_nums = None # which evaluation numbers (1-indexed, nx not nf) were used to build model_jac
|
|
96
99
|
|
|
97
100
|
# Saved point (in absolute coordinates) - always check this value before quitting solver
|
|
98
101
|
self.xsave = None
|
|
@@ -100,6 +103,8 @@ class Model(object):
|
|
|
100
103
|
self.objsave = None
|
|
101
104
|
self.jacsave = None
|
|
102
105
|
self.nsamples_save = None
|
|
106
|
+
self.eval_num_save = None
|
|
107
|
+
self.jacsave_eval_nums = None
|
|
103
108
|
|
|
104
109
|
# Factorisation of interpolation matrix
|
|
105
110
|
self.factorisation_current = False
|
|
@@ -174,7 +179,7 @@ class Model(object):
|
|
|
174
179
|
sq_distances[k] = sumsq(self.points[k, :] - xopt)
|
|
175
180
|
return sq_distances
|
|
176
181
|
|
|
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):
|
|
178
183
|
# Update point k to x (w.r.t. xbase), with residual values fvec
|
|
179
184
|
if k >= self.npt_so_far and self.npt_so_far < self.num_pts:
|
|
180
185
|
assert k == self.npt_so_far, "Growing: updating wrong point"
|
|
@@ -188,6 +193,7 @@ class Model(object):
|
|
|
188
193
|
if self.h is not None:
|
|
189
194
|
self.objval[k] += self.h(remove_scaling(self.xbase + x, self.scaling_changes), *self.argsh)
|
|
190
195
|
self.nsamples[k] = 1
|
|
196
|
+
self.eval_num[k] = eval_num
|
|
191
197
|
self.factorisation_current = False
|
|
192
198
|
|
|
193
199
|
if allow_kopt_update and self.objval[k] < self.objopt():
|
|
@@ -198,6 +204,7 @@ class Model(object):
|
|
|
198
204
|
self.points[[k1, k2], :] = self.points[[k2, k1], :]
|
|
199
205
|
self.fval_v[[k1, k2], :] = self.fval_v[[k2, k1], :]
|
|
200
206
|
self.objval[[k1, k2]] = self.objval[[k2, k1]]
|
|
207
|
+
self.eval_num[[k1, k2]] = self.eval_num[[k2, k1]]
|
|
201
208
|
if self.kopt == k1:
|
|
202
209
|
self.kopt = k2
|
|
203
210
|
elif self.kopt == k2:
|
|
@@ -219,7 +226,7 @@ class Model(object):
|
|
|
219
226
|
self.kopt = np.argmin(self.objval[:self.npt()]) # make sure kopt is always the best value we have
|
|
220
227
|
return
|
|
221
228
|
|
|
222
|
-
def add_new_point(self, x, rvec):
|
|
229
|
+
def add_new_point(self, x, rvec, eval_num):
|
|
223
230
|
self.points = np.append(self.points, x.reshape((1, self.n())), axis=0) # append row to xpt
|
|
224
231
|
self.fval_v = np.append(self.fval_v, rvec.reshape((1, self.m())), axis=0) # append row to fval_v
|
|
225
232
|
obj = sumsq(rvec)
|
|
@@ -227,6 +234,7 @@ class Model(object):
|
|
|
227
234
|
obj += self.h(remove_scaling(self.xbase + x, self.scaling_changes), *self.argsh)
|
|
228
235
|
self.objval = np.append(self.objval, obj) # append entry to fval
|
|
229
236
|
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
|
|
230
238
|
self.num_pts += 1 # make sure npt is updated
|
|
231
239
|
self.npt_so_far += 1
|
|
232
240
|
|
|
@@ -249,7 +257,7 @@ class Model(object):
|
|
|
249
257
|
self.model_const += np.dot(self.model_jac, xbase_shift)
|
|
250
258
|
return
|
|
251
259
|
|
|
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):
|
|
253
261
|
xabs = x.copy() if x_in_abs_coords else self.as_absolute_coordinates(x)
|
|
254
262
|
obj = sumsq(rvec)
|
|
255
263
|
if self.h is not None:
|
|
@@ -258,8 +266,10 @@ class Model(object):
|
|
|
258
266
|
self.xsave = xabs
|
|
259
267
|
self.rsave = rvec.copy()
|
|
260
268
|
self.objsave = obj
|
|
261
|
-
self.jacsave = self.model_jac.copy()
|
|
269
|
+
self.jacsave = self.model_jac.copy() if self.model_jac is not None else None
|
|
262
270
|
self.nsamples_save = nsamples
|
|
271
|
+
self.eval_num_save = eval_num
|
|
272
|
+
self.jacsave_eval_nums = self.model_jac_eval_nums.copy() if self.model_jac_eval_nums is not None else None
|
|
263
273
|
return True
|
|
264
274
|
else:
|
|
265
275
|
return False # this value is worse than what we have already - didn't save
|
|
@@ -267,9 +277,9 @@ class Model(object):
|
|
|
267
277
|
def get_final_results(self):
|
|
268
278
|
# Return x and objval for optimal point (either from xsave+objsave or kopt)
|
|
269
279
|
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
|
|
271
281
|
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
|
|
273
283
|
|
|
274
284
|
def min_objective_value(self):
|
|
275
285
|
# Get termination criterion for f small: f <= abs_tol or f <= rel_tol * f0
|
|
@@ -367,6 +377,7 @@ class Model(object):
|
|
|
367
377
|
J_old = self.model_jac.copy()
|
|
368
378
|
self.model_jac = dg[1:,:].T
|
|
369
379
|
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()
|
|
370
381
|
if verbose or get_chg_J:
|
|
371
382
|
norm_J_error = np.linalg.norm(self.model_jac - J_old, ord='fro')**2
|
|
372
383
|
linalg_resid = np.linalg.norm(W.dot(dg) - rhs)**2
|
dfols/solver.py
CHANGED
|
@@ -48,7 +48,7 @@ module_logger = logging.getLogger(__name__)
|
|
|
48
48
|
|
|
49
49
|
# A container for the results of the optimization routine
|
|
50
50
|
class OptimResults(object):
|
|
51
|
-
def __init__(self, xmin, rmin, objmin, jacmin, nf, nx, nruns, exit_flag, exit_msg):
|
|
51
|
+
def __init__(self, xmin, rmin, objmin, jacmin, nf, nx, nruns, exit_flag, exit_msg, xmin_eval_num, jacmin_eval_nums):
|
|
52
52
|
self.x = xmin
|
|
53
53
|
self.resid = rmin
|
|
54
54
|
self.obj = objmin
|
|
@@ -59,6 +59,8 @@ class OptimResults(object):
|
|
|
59
59
|
self.flag = exit_flag
|
|
60
60
|
self.msg = exit_msg
|
|
61
61
|
self.diagnostic_info = None
|
|
62
|
+
self.xmin_eval_num = xmin_eval_num
|
|
63
|
+
self.jacmin_eval_nums = jacmin_eval_nums
|
|
62
64
|
# Set standard names for exit flags
|
|
63
65
|
self.EXIT_SLOW_WARNING = EXIT_SLOW_WARNING
|
|
64
66
|
self.EXIT_MAXFUN_WARNING = EXIT_MAXFUN_WARNING
|
|
@@ -89,6 +91,9 @@ class OptimResults(object):
|
|
|
89
91
|
output += "Not showing approximate Jacobian because it is too long; check self.jacobian\n"
|
|
90
92
|
if self.diagnostic_info is not None:
|
|
91
93
|
output += "Diagnostic information available; check self.diagnostic_info\n"
|
|
94
|
+
output += "Solution xmin was evaluation point %g\n" % self.xmin_eval_num
|
|
95
|
+
if len(self.jacmin_eval_nums) < 100:
|
|
96
|
+
output += "Approximate Jacobian formed using evaluation points %s\n" % str(self.jacmin_eval_nums)
|
|
92
97
|
output += "Exit flag = %g\n" % self.flag
|
|
93
98
|
output += "%s\n" % self.msg
|
|
94
99
|
output += "****************************\n"
|
|
@@ -182,8 +187,8 @@ def solve_main(objfun, x0, argsf, xl, xu, projections, npt, rhobeg, rhoend, maxf
|
|
|
182
187
|
module_logger.info("Initialising (coordinate directions)")
|
|
183
188
|
exit_info = control.initialise_coordinate_directions(number_of_samples, num_directions, params)
|
|
184
189
|
if exit_info is not None:
|
|
185
|
-
x, rvec, obj, jacmin, nsamples = control.model.get_final_results()
|
|
186
|
-
return x, rvec, obj, None, nsamples, control.nf, control.nx, nruns_so_far + 1, exit_info, diagnostic_info
|
|
190
|
+
x, rvec, obj, jacmin, nsamples, x_eval_num, jac_eval_nums = control.model.get_final_results()
|
|
191
|
+
return x, rvec, obj, None, nsamples, control.nf, control.nx, nruns_so_far + 1, exit_info, diagnostic_info, x_eval_num, jac_eval_nums
|
|
187
192
|
|
|
188
193
|
finished_growing = (control.model.npt() >= control.model.num_pts) # have we finished growing the initial set yet?
|
|
189
194
|
|
|
@@ -464,7 +469,7 @@ def solve_main(objfun, x0, argsf, xl, xu, projections, npt, rhobeg, rhoend, maxf
|
|
|
464
469
|
|
|
465
470
|
if num_samples_run > 0:
|
|
466
471
|
control.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0),
|
|
467
|
-
num_samples_run, x_in_abs_coords=True)
|
|
472
|
+
num_samples_run, control.nx, x_in_abs_coords=True)
|
|
468
473
|
|
|
469
474
|
if exit_info is not None:
|
|
470
475
|
nruns_so_far += 1
|
|
@@ -549,7 +554,7 @@ def solve_main(objfun, x0, argsf, xl, xu, projections, npt, rhobeg, rhoend, maxf
|
|
|
549
554
|
break # quit
|
|
550
555
|
if exit_info is not None:
|
|
551
556
|
if num_samples_run > 0:
|
|
552
|
-
control.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run,
|
|
557
|
+
control.model.save_point(x, np.mean(rvec_list[:num_samples_run, :], axis=0), num_samples_run, control.nx,
|
|
553
558
|
x_in_abs_coords=True)
|
|
554
559
|
nruns_so_far += 1
|
|
555
560
|
break # quit
|
|
@@ -638,7 +643,7 @@ def solve_main(objfun, x0, argsf, xl, xu, projections, npt, rhobeg, rhoend, maxf
|
|
|
638
643
|
|
|
639
644
|
if do_logging:
|
|
640
645
|
module_logger.debug("Updating with knew = %i" % knew)
|
|
641
|
-
control.model.change_point(knew, xnew, rvec_list[0, :]) # expect step, not absolute x
|
|
646
|
+
control.model.change_point(knew, xnew, rvec_list[0, :], control.nx) # expect step, not absolute x
|
|
642
647
|
for i in range(1, num_samples_run):
|
|
643
648
|
control.model.add_new_sample(knew, rvec_extra=rvec_list[i, :])
|
|
644
649
|
|
|
@@ -877,11 +882,11 @@ def solve_main(objfun, x0, argsf, xl, xu, projections, npt, rhobeg, rhoend, maxf
|
|
|
877
882
|
# (end main loop)
|
|
878
883
|
|
|
879
884
|
# Quit & return the important information
|
|
880
|
-
x, rvec, obj, jacmin, nsamples = control.model.get_final_results()
|
|
885
|
+
x, rvec, obj, jacmin, nsamples, x_eval_num, jac_eval_nums = control.model.get_final_results()
|
|
881
886
|
if do_logging:
|
|
882
887
|
module_logger.debug("At return from DFO-LS, number of function evals = %i" % nf)
|
|
883
888
|
module_logger.debug("Smallest objective value = %.15g at x = " % obj + str(x))
|
|
884
|
-
return x, rvec, obj, jacmin, nsamples, control.nf, control.nx, nruns_so_far, exit_info, diagnostic_info
|
|
889
|
+
return x, rvec, obj, jacmin, nsamples, control.nf, control.nx, nruns_so_far, exit_info, diagnostic_info, x_eval_num, jac_eval_nums
|
|
885
890
|
|
|
886
891
|
|
|
887
892
|
def solve(objfun, x0, h=None, lh=None, prox_uh=None, argsf=(), argsh=(), argsprox=(), bounds=None, projections=[], npt=None, rhobeg=None, rhoend=1e-8, maxfun=None, nsamples=None, user_params=None,
|
|
@@ -1056,7 +1061,7 @@ def solve(objfun, x0, h=None, lh=None, prox_uh=None, argsf=(), argsh=(), argspro
|
|
|
1056
1061
|
nruns = 0
|
|
1057
1062
|
nf = 0
|
|
1058
1063
|
nx = 0
|
|
1059
|
-
xmin, rmin, objmin, jacmin, nsamples_min, nf, nx, nruns, exit_info, diagnostic_info = \
|
|
1064
|
+
xmin, rmin, objmin, jacmin, nsamples_min, nf, nx, nruns, exit_info, diagnostic_info, xmin_eval_num, jacmin_eval_nums = \
|
|
1060
1065
|
solve_main(objfun, x0, argsf, xl, xu, projections, npt, rhobeg, rhoend, maxfun, nruns, nf, nx, nsamples, params,
|
|
1061
1066
|
diagnostic_info, scaling_changes, h, lh, argsh, prox_uh, argsprox, default_growing_method_set_by_user=default_growing_method_set_by_user,
|
|
1062
1067
|
do_logging=do_logging, print_progress=print_progress)
|
|
@@ -1075,12 +1080,12 @@ def solve(objfun, x0, h=None, lh=None, prox_uh=None, argsf=(), argsh=(), argspro
|
|
|
1075
1080
|
module_logger.info("Restarting from finish point (f = %g) after %g function evals; using rhobeg = %g and rhoend = %g"
|
|
1076
1081
|
% (objmin, nf, rhobeg, rhoend))
|
|
1077
1082
|
if params("restarts.hard.use_old_rk"):
|
|
1078
|
-
xmin2, rmin2, objmin2, jacmin2, nsamples2, nf, nx, nruns, exit_info, diagnostic_info = \
|
|
1083
|
+
xmin2, rmin2, objmin2, jacmin2, nsamples2, nf, nx, nruns, exit_info, diagnostic_info, xmin_eval_num2, jacmin_eval_nums2 = \
|
|
1079
1084
|
solve_main(objfun, xmin, argsf, xl, xu, projections, npt, rhobeg, rhoend, maxfun, nruns, nf, nx, nsamples, params,
|
|
1080
1085
|
diagnostic_info, scaling_changes, h, lh, argsh, prox_uh, argsprox, r0_avg_old=rmin, r0_nsamples_old=nsamples_min,
|
|
1081
1086
|
do_logging=do_logging, print_progress=print_progress)
|
|
1082
1087
|
else:
|
|
1083
|
-
xmin2, rmin2, objmin2, jacmin2, nsamples2, nf, nx, nruns, exit_info, diagnostic_info = \
|
|
1088
|
+
xmin2, rmin2, objmin2, jacmin2, nsamples2, nf, nx, nruns, exit_info, diagnostic_info, xmin_eval_num2, jacmin_eval_nums2 = \
|
|
1084
1089
|
solve_main(objfun, xmin, argsf, xl, xu, projections, npt, rhobeg, rhoend, maxfun, nruns, nf, nx, nsamples, params,
|
|
1085
1090
|
diagnostic_info, scaling_changes, h, lh, argsh, prox_uh, argsprox, do_logging=do_logging, print_progress=print_progress)
|
|
1086
1091
|
|
|
@@ -1088,9 +1093,10 @@ def solve(objfun, x0, h=None, lh=None, prox_uh=None, argsf=(), argsh=(), argspro
|
|
|
1088
1093
|
if do_logging:
|
|
1089
1094
|
module_logger.info("Successful run with new f = %s compared to old f = %s" % (objmin2, objmin))
|
|
1090
1095
|
last_successful_run = nruns
|
|
1091
|
-
(xmin, rmin, objmin, nsamples_min) = (xmin2, rmin2, objmin2, nsamples2)
|
|
1096
|
+
(xmin, rmin, objmin, nsamples_min, xmin_eval_num) = (xmin2, rmin2, objmin2, nsamples2, xmin_eval_num2)
|
|
1092
1097
|
if jacmin2 is not None: # may be None if finished during setup phase, in which case just use old Jacobian
|
|
1093
1098
|
jacmin = jacmin2
|
|
1099
|
+
jacmin_eval_nums = jacmin_eval_nums2
|
|
1094
1100
|
else:
|
|
1095
1101
|
if do_logging:
|
|
1096
1102
|
module_logger.info("Unsuccessful run with new f = %s compared to old f = %s" % (objmin2, objmin))
|
|
@@ -1105,7 +1111,7 @@ def solve(objfun, x0, h=None, lh=None, prox_uh=None, argsf=(), argsh=(), argspro
|
|
|
1105
1111
|
if scaling_changes is not None and jacmin is not None:
|
|
1106
1112
|
for i in range(n):
|
|
1107
1113
|
jacmin[:, i] = jacmin[:, i] / scaling_changes[1][i]
|
|
1108
|
-
results = OptimResults(remove_scaling(xmin, scaling_changes), rmin, objmin, jacmin, nf, nx, nruns, exit_flag, exit_msg)
|
|
1114
|
+
results = OptimResults(remove_scaling(xmin, scaling_changes), rmin, objmin, jacmin, nf, nx, nruns, exit_flag, exit_msg, xmin_eval_num, jacmin_eval_nums)
|
|
1109
1115
|
if params("logging.save_diagnostic_info"):
|
|
1110
1116
|
df = diagnostic_info.to_dataframe(with_xk=params("logging.save_xk"), with_rk=params("logging.save_rk"))
|
|
1111
1117
|
results.diagnostic_info = df
|
DFO_LS-1.5.0.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
dfols/__init__.py,sha256=nMJ4G3JcmjQ82lYXV2ywxjHWQqd9nq7Ak6GIlrN70Tw,1605
|
|
2
|
-
dfols/controller.py,sha256=gz4yGpk8KyfsWxrAkI8y69K5ckSHZ3Xdq0fEVFtIcPk,49925
|
|
3
|
-
dfols/diagnostic_info.py,sha256=2kEUkL-MS4eDENUf1r2hOWsntP8OxMDKi_kyHmrC9V4,6081
|
|
4
|
-
dfols/hessian.py,sha256=sExx4J4KoGwHItbthX2odosB2ONbQFvLdlcod7PIh4k,4262
|
|
5
|
-
dfols/model.py,sha256=i-TcGNFAeYt4uu3R_-THTk2rOCDvgU_mcZQQXfE1ODA,19786
|
|
6
|
-
dfols/params.py,sha256=GzJGO0TByH1X3B0NbLOCOqmYG8dRiKPKjjX7or_fOqI,18342
|
|
7
|
-
dfols/solver.py,sha256=QUF84UYnSitvlpVssKLdcMF9e_zdA9qlZlg5e8IegeQ,63173
|
|
8
|
-
dfols/trust_region.py,sha256=JbHLBDw7H88a3cIMuialh7kpMNGjL3Lp9JsjrBNpDWQ,28231
|
|
9
|
-
dfols/util.py,sha256=efGVAKPb7YrHya1IOgyzacwa_h0u2jHHs5FhuxUlYDg,10282
|
|
10
|
-
DFO_LS-1.5.0.dist-info/LICENSE.txt,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
|
11
|
-
DFO_LS-1.5.0.dist-info/METADATA,sha256=JIQNs15kBtVr5_cA7JnXDbT-uQ06pqTd3RD_MRYmB7w,8069
|
|
12
|
-
DFO_LS-1.5.0.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
|
13
|
-
DFO_LS-1.5.0.dist-info/top_level.txt,sha256=UfxRhaDN8HQx2_l17KbrDrERJ90OCN7VKkDMpYYbRLU,6
|
|
14
|
-
DFO_LS-1.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|