Skip to content

Commit 808e425

Browse files
committed
Add optional per-iteration printing option
1 parent b593de3 commit 808e425

File tree

4 files changed

+18
-10
lines changed

4 files changed

+18
-10
lines changed

dfols/solver.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def __str__(self):
9595

9696
def solve_main(objfun, x0, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns_so_far, nf_so_far, nx_so_far, nsamples, params,
9797
diagnostic_info, scaling_changes, r0_avg_old=None, r0_nsamples_old=None, default_growing_method_set_by_user=None,
98-
do_logging=True):
98+
do_logging=True, print_progress=False):
9999
# Evaluate at x0 (keep nf, nx correct and check for f < 1e-12)
100100
# The hard bit is determining what m = len(r0) should be, and allocating memory appropriately
101101
if r0_avg_old is None:
@@ -196,6 +196,8 @@ def solve_main(objfun, x0, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns_so_f
196196
current_iter = -1
197197
if do_logging:
198198
logging.info("Beginning main loop")
199+
if print_progress:
200+
print("{:^5}{:^7}{:^10}{:^10}{:^10}{:^10}{:^7}".format("Run", "Iter", "Obj", "Grad", "Delta", "rho", "Evals"))
199201
while True:
200202
current_iter += 1
201203

@@ -274,6 +276,9 @@ def solve_main(objfun, x0, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns_so_f
274276
xnew = control.model.xopt() + d
275277
dnorm = min(LA.norm(d), control.delta)
276278

279+
if print_progress:
280+
print("{:^5}{:^7}{:^10.2e}{:^10.2e}{:^10.2e}{:^10.2e}{:^7}".format(nruns_so_far+1, current_iter+1, control.model.fopt(), np.linalg.norm(gopt), control.delta, control.rho, control.nf))
281+
277282
if params("logging.save_diagnostic_info"):
278283
diagnostic_info.save_info_from_control(control, nruns_so_far, current_iter,
279284
save_poisedness=params("logging.save_poisedness"))
@@ -719,9 +724,9 @@ def solve_main(objfun, x0, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns_so_f
719724
# no very successful iterations, and twice as many unsuccessful than moderately successful iterations
720725

721726
# If delta criteria met, check chgJ criteria
722-
# Fit line to k vs. log(||chgJ||_F)
727+
# Fit line to k vs. log(||chgJ||_F), but floor ||chgJ||_F away from zero
723728
slope, intercept, r_value, p_value, std_err = STAT.linregress(np.arange(len(restart_auto_detect_chgJ)),
724-
np.log(restart_auto_detect_chgJ))
729+
np.log(np.maximum(restart_auto_detect_chgJ, 1e-15)))
725730

726731
if do_logging:
727732
logging.debug("Iter %g: (slope, intercept, r_value) = (%g, %g, %g)" % (current_iter, slope, intercept, r_value))
@@ -846,7 +851,7 @@ def solve_main(objfun, x0, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns_so_f
846851

847852

848853
def solve(objfun, x0, args=(), bounds=None, npt=None, rhobeg=None, rhoend=1e-8, maxfun=None, nsamples=None, user_params=None,
849-
objfun_has_noise=False, scaling_within_bounds=False, do_logging=True):
854+
objfun_has_noise=False, scaling_within_bounds=False, do_logging=True, print_progress=False):
850855
x0 = x0.astype(np.float)
851856
n = len(x0)
852857

@@ -989,7 +994,7 @@ def solve(objfun, x0, args=(), bounds=None, npt=None, rhobeg=None, rhoend=1e-8,
989994
xmin, rmin, fmin, jacmin, nsamples_min, nf, nx, nruns, exit_info, diagnostic_info = \
990995
solve_main(objfun, x0, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns, nf, nx, nsamples, params,
991996
diagnostic_info, scaling_changes, default_growing_method_set_by_user=default_growing_method_set_by_user,
992-
do_logging=do_logging)
997+
do_logging=do_logging, print_progress=print_progress)
993998

994999
# Hard restarts loop
9951000
last_successful_run = nruns
@@ -1008,11 +1013,11 @@ def solve(objfun, x0, args=(), bounds=None, npt=None, rhobeg=None, rhoend=1e-8,
10081013
xmin2, rmin2, fmin2, jacmin2, nsamples2, nf, nx, nruns, exit_info, diagnostic_info = \
10091014
solve_main(objfun, xmin, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns, nf, nx, nsamples, params,
10101015
diagnostic_info, scaling_changes, r0_avg_old=rmin, r0_nsamples_old=nsamples_min,
1011-
do_logging=do_logging)
1016+
do_logging=do_logging, print_progress=print_progress)
10121017
else:
10131018
xmin2, rmin2, fmin2, jacmin2, nsamples2, nf, nx, nruns, exit_info, diagnostic_info = \
10141019
solve_main(objfun, xmin, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns, nf, nx, nsamples, params,
1015-
diagnostic_info, scaling_changes, do_logging=do_logging)
1020+
diagnostic_info, scaling_changes, do_logging=do_logging, print_progress=print_progress)
10161021

10171022
if fmin2 < fmin or np.isnan(fmin):
10181023
if do_logging:

docs/advanced.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Logging and Output
2222

2323
Initialization of Points
2424
------------------------
25-
* :code:`init.random_initial_directions` - Build the initial interpolation set using random directions (as opposed to coordinate directions). Default is :code:`True`.
25+
* :code:`init.random_initial_directions` - Build the initial interpolation set using random directions (as opposed to coordinate directions). Default as of version 1.2 is :code:`False`.
2626
* :code:`init.random_directions_make_orthogonal` - If building initial interpolation set with random directions, whether or not these should be orthogonalized. Default is :code:`True`.
2727
* :code:`init.run_in_parallel` - If using random directions, whether or not to ask for all :code:`objfun` to be evaluated at all points without any intermediate processing. Default is :code:`False`.
2828

docs/history.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ Version 1.2
3131
* Faster trust-region and geometry subproblem solutions in Fortran using the `trustregion <https://github.com/lindonroberts/trust-region>`_ package.
3232
* Faster interpolation solution for multiple right-hand sides.
3333
* Don't adjust starting point if it is close to the bounds (as long as it is feasible).
34-
* Bugfix: correctly handle 1-sided bounds as inputs.
34+
* Option to stop default logging behavior and/or enable per-iteration printing.
35+
* Bugfix: correctly handle 1-sided bounds as inputs, avoid divide-by-zero warnings when auto-detecting restarts.

docs/userguide.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ The :code:`solve` function has several optional arguments which the user may pro
6868
dfols.solve(objfun, x0, args=(), bounds=None, npt=None, rhobeg=None,
6969
rhoend=1e-8, maxfun=None, nsamples=None,
7070
user_params=None, objfun_has_noise=False,
71-
scaling_within_bounds=False, do_logging=True)
71+
scaling_within_bounds=False,
72+
do_logging=True, print_progress=False)
7273
7374
These arguments are:
7475

@@ -83,6 +84,7 @@ These arguments are:
8384
* :code:`objfun_has_noise` - a flag to indicate whether or not :code:`objfun` has stochastic noise; i.e. will calling :code:`objfun(x)` multiple times at the same value of :code:`x` give different results? This is used to set some sensible default parameters (including using multiple restarts), all of which can be overridden by the values provided in :code:`user_params`.
8485
* :code:`scaling_within_bounds` - a flag to indicate whether the algorithm should internally shift and scale the entries of :code:`x` so that the bounds become :math:`0 \leq x \leq 1`. This is useful is you are setting :code:`bounds` and the bounds have different orders of magnitude. If :code:`scaling_within_bounds=True`, the values of :code:`rhobeg` and :code:`rhoend` apply to the *shifted* variables.
8586
* :code:`do_logging` - a flag to indicate whether logging output should be produced. This is not automatically visible unless you use the Python `logging <https://docs.python.org/3/library/logging.html>`_ module (see below for simple usage).
87+
* :code:`print_progress` - a flag to indicate whether to print a per-iteration progress log to terminal.
8688

8789
In general when using optimization software, it is good practice to scale your variables so that moving each by a given amount has approximately the same impact on the objective function.
8890
The :code:`scaling_within_bounds` flag is designed to provide an easy way to achieve this, if you have set the bounds :code:`lower` and :code:`upper`.

0 commit comments

Comments
 (0)