UGO#
purpose#
The ugo
package aims to find the global minimizer of a univariate
twice-continuously differentiable function \(f(x)\) of a single
variable over the finite interval \(x^l <= x <= x^u\). Function
and derivative values are provided via a subroutine call.
Second derivatives may be used to advantage if they are available.
See Section 4 of $GALAHAD/doc/ugo.pdf for additional details.
method#
The algorithm starts by splitting the interval \([x^l,x^u]\) into a specified number of subintervals \([x_i,x_{i+1}]\) of equal length, and evaluating \(f\) and its derivatives at each \(x_i\). A surrogate (approximating) lower bound function is constructed on each subinterval using the function and derivative values at each end, and an estimate of the first- and second-derivative Lipschitz constant. This surrogate is minimized, the true objective evaluated at the best predicted point, and the corresponding interval split again at this point. Any interval whose surrogate lower bound value exceeds an evaluated actual value is discarded. The method continues until only one interval of a maximum permitted width remains.
reference#
Many ingredients in the algorithm are based on the paper
D. Lera and Ya. D. Sergeyev, “Acceleration of univariate global optimization algorithms working with Lipschitz functions and Lipschitz first derivatives” SIAM J. Optimization 23(1), (2013) 508–529
but adapted to use second derivatives.
functions#
- ugo.initialize()#
Set default option values and initialize private data.
Returns:
- optionsdict
- dictionary containing default control options:
- errorint
error and warning diagnostics occur on stream error.
- outint
general output occurs on stream out.
- print_levelint
the level of output required. Possible values are:
<= 0
no output
1
a one-line summary for every improvement
2
a summary of each iteration
>= 3
increasingly verbose (debugging) output.
- start_printint
any printing will start on this iteration.
- stop_printint
any printing will stop on this iteration.
- print_gapint
the number of iterations between printing.
- maxitint
the maximum number of iterations allowed.
- initial_pointint
the number of initial (uniformly-spaced) evaluation points (<2 reset to 2).
- storage_incrementint
incremenets of storage allocated (less that 1000 will be reset to 1000).
- bufferint
unit for any out-of-core writing when expanding arrays.
- lipschitz_estimate_usedint
what sort of Lipschitz constant estimate will be used:
1
the global contant provided
2
an estimated global contant
3
estimated local costants.
- next_interval_selectionint
how is the next interval for examination chosen:
1
traditional
2
local_improvement.
- refine_with_newtonint
try refine_with_newton Newton steps from the vacinity of the global minimizer to try to improve the estimate.
- alive_unitint
removal of the file alive_file from unit alive_unit terminates execution.
- alive_filestr
see alive_unit.
- stop_lengthfloat
overall convergence tolerances. The iteration will terminate when the step is less than
stop_length
.- small_g_for_newtonfloat
if the absolute value of the gradient is smaller than small_g_for_newton, the next evaluation point may be at a Newton estimate of a local minimizer.
- small_gfloat
if the absolute value of the gradient at the end of the interval search is smaller than small_g, no Newton search is necessary.
- obj_sufficientfloat
stop if the objective function is smaller than a specified value.
- global_lipschitz_constantfloat
the global Lipschitz constant for the gradient (-ve means unknown).
- reliability_parameterfloat
the reliability parameter that is used to boost insufficiently large estimates of the Lipschitz constant (-ve means that default values will be chosen depending on whether second derivatives are provided or not).
- lipschitz_lower_boundfloat
a lower bound on the Lipschitz constant for the gradient (not zero unless the function is constant).
- cpu_time_limitfloat
the maximum CPU time allowed (-ve means infinite).
- clock_time_limitfloat
the maximum elapsed clock time allowed (-ve means infinite).
- second_derivative_availablebool
if
second_derivative_available
is True, the user must provide them when requested. The package is generally more effective if second derivatives are available.- space_criticalbool
if
space_critical
is True, every effort will be made to use as little space as possible. This may result in longer computation time.- deallocate_error_fatalbool
if
deallocate_error_fatal
is True, any array/pointer deallocation error will terminate execution. Otherwise, computation will continue.- prefixstr
all output lines will be prefixed by the string contained in quotes within
prefix
, e.g. ‘word’ (note the qutoes) will result in the prefix word.
- ugo.load(x_l, x_u, options=None)#
Import problem data into internal storage prior to solution.
Parameters:
- x_ldouble
holds the value \(x^l\) of the lower bound on the optimization variable \(x\).
- x_udouble
holds the value \(x^u\) of the upper bound on the optimization variable \(x\).
- optionsdict, optional
dictionary of control options (see ugo.initialize).
- ugo.solve(eval_fgh)#
Find an approximation to the global minimizer of a given univariate function with a Lipschitz gradient in an interval.
Parameters:
- eval_fghcallable
a user-defined function that must have the signature:
f, g, h = eval_fgh(x)
The value of the objective function \(f(x)\) and its first derivative \(f'(x)\) evaluated at \(x\) must be assigned to
f
andg
respectively. In addition, if options[‘second_derivatives_available’] has been set to True when callingugo.load
, the user must also assign the value of the second derivative \(f''(x)\) toh
; it need not be assigned otherwise.Returns:
- xdouble
holds the value of the approximate global minimizer \(x\) after a successful call.
- fdouble
holds the value of the objective function \(f(x)\) at the approximate global minimizer \(x\) after a successful call.
- gdouble
holds the value of the gradient of the objective function \(f'(x)\) at the approximate global minimizer \(x\) after a successful call.
- hdouble
holds the value of the second derivative of the objective function \(f''(x)\) at the approximate global minimizer \(x\) after a successful call.
- [optional] ugo.information()
Provide optional output information.
Returns:
- informdict
- dictionary containing output information:
- statusint
return status. Possible values are:
0
The run was successful.
-1
An allocation error occurred. A message indicating the offending array is written on unit options[‘error’], and the returned allocation status and a string containing the name of the offending array are held in inform[‘alloc_status’] and inform[‘bad_alloc’] respectively.
-2
A deallocation error occurred. A message indicating the offending array is written on unit options[‘error’] and the returned allocation status and a string containing the name of the offending array are held in inform[‘alloc_status’] and inform[‘bad_alloc’] respectively.
-7
The objective function appears to be unbounded from below.
-18
Too many iterations have been performed. This may happen if options[‘maxit’] is too small, but may also be symptomatic of a badly scaled problem.
-19
The CPU time limit has been reached. This may happen if options[‘cpu_time_limit’] is too small, but may also be symptomatic of a badly scaled problem.
-40
The user has forced termination of the solver by removing the file named options[‘alive_file’] from unit options[‘alive_unit’].
- alloc_statusint
the status of the last attempted internal array. allocation/deallocation
- bad_allocstr
the name of the array for which an internal array allocation/deallocation error occurred.
- iterint
the total number of iterations performed
- f_evalint
the total number of evaluations of the objective function.
- g_evalint
the total number of evaluations of the gradient of the objective function.
- h_evalint
the total number of evaluations of the Hessian of the objective function.
- timedict
- dictionary containing tim information:
- totalfloat
the total CPU time spent in the package.
- clock_totalfloat
the total clock time spent in the package.
- ugo.terminate()#
Deallocate all internal private storage.
example code#
from galahad import ugo
import numpy as np
np.set_printoptions(precision=4,suppress=True,floatmode='fixed')
print("\n** python test: ugo")
# allocate internal data and set default options
options = ugo.initialize()
# set some non-default options
options['print_level'] = 0
#print("options:", options)
# load data (and optionally non-default options)
x_l = -1
x_u = 2
ugo.load(x_l,x_u,options=options)
# define objective function
# NB python functions have access to external variables
# So no need for userdata like in C or Fortran
def eval_fgh(x):
a = 10
f = x * x * np.cos( a*x )
g = - a * x * x * np.sin( a*x ) + 2.0 * x * np.cos( a*x )
h = - a * a* x * x * np.cos( a*x ) - 4.0 * a * x * np.sin( a*x ) \
+ 2.0 * np.cos( a*x )
return f, g, h
# find optimum
x, f, g, h = ugo.solve(eval_fgh)
print(" x: %.4f" % x)
print(" f: %.4f" % f)
print(" g: %.4f" % g)
print(" h: %.4f" % h)
# get information
inform = ugo.information()
print('** ugo exit status:', inform['status'])
# deallocate internal data
ugo.terminate()
This example code is available in $GALAHAD/src/ugo/Python/test_ugo.py .