Skip to content

Commit f514173

Browse files
committed
Fixed default cheap initialisation behaviour for inverse problems
1 parent 3ea00be commit f514173

21 files changed

+147
-74
lines changed

dfols/solver.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def __str__(self):
9494

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,
97-
diagnostic_info, scaling_changes, r0_avg_old=None, r0_nsamples_old=None):
97+
diagnostic_info, scaling_changes, r0_avg_old=None, r0_nsamples_old=None, default_growing_method_set_by_user=None):
9898
# Evaluate at x0 (keep nf, nx correct and check for f < 1e-12)
9999
# The hard bit is determining what m = len(r0) should be, and allocating memory appropriately
100100
if r0_avg_old is None:
@@ -142,6 +142,17 @@ def solve_main(objfun, x0, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns_so_f
142142
num_samples_run = r0_nsamples_old
143143
nf = nf_so_far
144144
nx = nx_so_far
145+
146+
# On the first run, set default growing method (unless the user has already done this)
147+
if default_growing_method_set_by_user is not None and (not default_growing_method_set_by_user):
148+
# If m>=n, the default growing method (use_full_rank_interp) is best
149+
# However, this can fail for m<n, so need to use an alternative method (perturb_trust_region_step)
150+
if m < len(x0):
151+
logging.debug("Inverse problem (m<n), switching default growing method")
152+
params('growing.full_rank.use_full_rank_interp', new_value=False)
153+
params('growing.perturb_trust_region_step', new_value=True)
154+
if not params.params_changed['growing.delta_scale_new_dirns']:
155+
params('growing.delta_scale_new_dirns', new_value=0.1)
145156

146157
# Initialise controller
147158
control = Controller(objfun, args, x0, r0_avg, num_samples_run, xl, xu, npt, rhobeg, rhoend, nf, nx, maxfun, params, scaling_changes)
@@ -836,6 +847,11 @@ def solve(objfun, x0, args=(), bounds=None, npt=None, rhobeg=None, rhoend=1e-8,
836847
if user_params is not None:
837848
for (key, val) in user_params.items():
838849
params(key, new_value=val)
850+
851+
# Default growing method depends on if m>=n or m<n - need to set this once we know m
852+
# But should only do this if the user hasn't forced a choice on us
853+
default_growing_method_set_by_user = user_params is not None and \
854+
('growing.full_rank.use_full_rank_interp' in user_params or 'growing.perturb_trust_region_step' in user_params)
839855

840856
scaling_changes = None
841857
if scaling_within_bounds:
@@ -948,7 +964,7 @@ def solve(objfun, x0, args=(), bounds=None, npt=None, rhobeg=None, rhoend=1e-8,
948964
nx = 0
949965
xmin, rmin, fmin, jacmin, nsamples_min, nf, nx, nruns, exit_info, diagnostic_info = \
950966
solve_main(objfun, x0, args, xl, xu, npt, rhobeg, rhoend, maxfun, nruns, nf, nx, nsamples, params,
951-
diagnostic_info, scaling_changes)
967+
diagnostic_info, scaling_changes, default_growing_method_set_by_user=default_growing_method_set_by_user)
952968

953969
# Hard restarts loop
954970
last_successful_run = nruns

dfols/tests/test_solver.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,41 @@ def runTest(self):
133133
self.assertTrue(array_compare(soln.jacobian, rosenbrock_jacobian(soln.x), thresh=3e-0), "Wrong Jacobian")
134134
self.assertTrue(abs(soln.f) < 1e-10, "Wrong fmin")
135135

136+
137+
class TestInverseProblem(unittest.TestCase):
138+
# Minimise an inverse problem
139+
def runTest(self):
140+
# Simple problem with global minimum at the origin, for instance
141+
objfun = lambda x: np.array([np.sin(x[0])**2, np.sin(x[1])**2, np.sum(np.sin(x[2:]**2))])
142+
jac = lambda x: np.array([[2*np.sin(x[0])*np.cos(x[0]), 0, 0, 0, 0],
143+
[0, 2*np.sin(x[1])*np.cos(x[1]), 0, 0, 0],
144+
[0, 0, 2*np.sin(x[2])*np.cos(x[2]), 2*np.sin(x[3])*np.cos(x[3]), 2*np.sin(x[4])*np.cos(x[4])]]) # for n=5 only
145+
x0 = np.ones((5,))
146+
np.random.seed(0)
147+
soln = dfols.solve(objfun, x0)
148+
self.assertTrue(array_compare(soln.x, np.zeros((5,)), thresh=1e-3), "Wrong xmin")
149+
self.assertTrue(array_compare(soln.resid, objfun(soln.x), thresh=1e-10), "Wrong resid")
150+
print(soln.jacobian)
151+
print(jac(soln.x))
152+
self.assertTrue(array_compare(soln.jacobian, jac(soln.x), thresh=1e-2), "Wrong Jacobian")
153+
self.assertTrue(abs(soln.f) < 1e-10, "Wrong fmin")
154+
155+
156+
class TestInverseProblemGrowing(unittest.TestCase):
157+
# Minimise an inverse problem, with growing
158+
def runTest(self):
159+
# Simple problem with global minimum at the origin, for instance
160+
objfun = lambda x: np.array([np.sin(x[0])**2, np.sin(x[1])**2, np.sum(np.sin(x[2:]**2))])
161+
jac = lambda x: np.array([[2*np.sin(x[0])*np.cos(x[0]), 0, 0, 0, 0],
162+
[0, 2*np.sin(x[1])*np.cos(x[1]), 0, 0, 0],
163+
[0, 0, 2*np.sin(x[2])*np.cos(x[2]), 2*np.sin(x[3])*np.cos(x[3]), 2*np.sin(x[4])*np.cos(x[4])]]) # for n=5 only
164+
x0 = np.ones((5,))
165+
np.random.seed(0)
166+
soln = dfols.solve(objfun, x0, user_params={'growing.ndirs_initial':2})
167+
self.assertTrue(array_compare(soln.x, np.zeros((5,)), thresh=1e-3), "Wrong xmin")
168+
self.assertTrue(array_compare(soln.resid, objfun(soln.x), thresh=1e-10), "Wrong resid")
169+
print(soln.jacobian)
170+
print(jac(soln.x))
171+
self.assertTrue(array_compare(soln.jacobian, jac(soln.x), thresh=1e-2), "Wrong Jacobian")
172+
self.assertTrue(abs(soln.f) < 1e-10, "Wrong fmin")
173+

dfols/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@
2222
2323
"""
2424

25-
__version__ = '1.0.2'
25+
__version__ = '1.1'

docs/advanced.rst

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,22 +85,22 @@ Multiple Restarts
8585

8686
Dynamically Growing Initial Set
8787
-------------------------------
88-
* :code:`growing.ndirs_initial` - Number of initial points to add (excluding :math:`x_k`). Default is :code:`npt-1`.
89-
* :code:`growing.num_new_dirns_each_iter` - Number of new search directions to add with each iteration where we do not have a full set of search directions. Default is 1.
90-
* :code:`growing.delta_scale_new_dirns` - When adding new search directions, the length of the step as a multiple of :math:`\Delta_k`. Default is 1, but if setting :code:`growing.perturb_trust_region_step=True` should be made smaller (e.g. 0.1).
91-
* :code:`growing.do_geom_steps` - While still growing the initial set, whether to do geometry-improving steps in the trust region algorithm, as per the usual algorithm. Default is :code:`False`.
92-
* :code:`growing.safety.do_safety_step` - While still growing the initial set, whether to perform safety steps, or the regular trust region steps. Default is :code:`True`.
93-
* :code:`growing.safety.reduce_delta` - While still growing the initial set, whether to reduce :math:`\Delta_k` in safety steps. Default is :code:`False`.
94-
* :code:`growing.safety.full_geom_step` - While still growing the initial set, whether to do a full geometry-improving step within safety steps (the same as the post-growing phase of the algorithm). Since this involves reducing :math:`\Delta_k`, cannot be :code:`True` if :code:`growing.safety.reduce_delta` is :code:`True`. Default is :code:`False`.
95-
* :code:`growing.full_rank.use_full_rank_interp` - Whether to perturb the interpolated :math:`J_k` to make it full rank, allowing the trust region step to include components in the full search space. If :code:`True`, setting :code:`growing.num_new_dirns_each_iter` to 0 is recommended. Default is :code:`False`.
88+
* :code:`growing.ndirs_initial` - Number of initial points to add (excluding :math:`x_k`). This should only be changed to a value less than :math:`n`, and only if the default setup cost of :math:`n+1` evaluations of :code:`objfun` is impractical. If this is set to be less than the default, the input value :code:`npt` should be set to :math:`n`. If the default is used, all the below parameters have no effect on DFO-LS. Default is :code:`npt-1`.
89+
* :code:`growing.full_rank.use_full_rank_interp` - If :code:`growing.ndirs_initial` is less than :code:`npt`, whether to perturb the interpolated :math:`J_k` to make it full rank, allowing the trust region step to include components in the full search space. Default is :code:`True` if :math:`m\geq n` and :code:`False` otherwise (opposite to :code:`growing.perturb_trust_region_step`).
90+
* :code:`growing.perturb_trust_region_step` - Whether to perturb the trust region step by an orthogonal direction not yet searched. This is an alternative to :code:`growing.full_rank.use_full_rank_interp`. Default is :code:`False` if :math:`m\geq n` and :code:`True` otherwise (opposite to :code:`growing.full_rank.use_full_rank_interp`).
91+
* :code:`growing.delta_scale_new_dirns` - When adding new search directions, the length of the step as a multiple of :math:`\Delta_k`. Default is 1, or 0.1 if :code:`growing.perturb_trust_region_step=True`.
9692
* :code:`growing.full_rank.scale_factor` - Magnitude of extra components added to :math:`J_k`. Default is :math:`10^{-2}`.
9793
* :code:`growing.full_rank.svd_scale_factor` - Floor singular values of :math:`J_k` at this factor of the last nonzero value. Default is 1.
9894
* :code:`growing.full_rank.min_sing_val` - Absolute floor on singular values of :math:`J_k`. Default is :math:`10^{-6}`.
9995
* :code:`growing.full_rank.svd_max_jac_cond` - Cap on condition number of :math:`J_k` after applying floors to singular values (effectively another floor on the smallest singular value, since the largest singular value is fixed). Default is :math:`10^8`.
96+
* :code:`growing.do_geom_steps` - While still growing the initial set, whether to do geometry-improving steps in the trust region algorithm, as per the usual algorithm. Default is :code:`False`.
97+
* :code:`growing.safety.do_safety_step` - While still growing the initial set, whether to perform safety steps, or the regular trust region steps. Default is :code:`True`.
98+
* :code:`growing.safety.reduce_delta` - While still growing the initial set, whether to reduce :math:`\Delta_k` in safety steps. Default is :code:`False`.
99+
* :code:`growing.safety.full_geom_step` - While still growing the initial set, whether to do a full geometry-improving step within safety steps (the same as the post-growing phase of the algorithm). Since this involves reducing :math:`\Delta_k`, cannot be :code:`True` if :code:`growing.safety.reduce_delta` is :code:`True`. Default is :code:`False`.
100100
* :code:`growing.reset_delta` - Whether or not to reset trust region radius :math:`\Delta_k` to its initial value at the end of the growing phase. Default is :code:`False`.
101101
* :code:`growing.reset_rho` - Whether or not to reset trust region radius lower bound :math:`\rho_k` to its initial value at the end of the growing phase. Default is :code:`False`.
102102
* :code:`growing.gamma_dec` - Trust region decrease parameter during the growing phase. Default is :code:`tr_radius.gamma_dec`.
103-
* :code:`growing.perturb_trust_region_step` - Whether to perturb the trust region step by an orthogonal direction not yet searched. This is an alternative to :code:`growing.full_rank.use_full_rank_interp`. Default is :code:`False`.
103+
* :code:`growing.num_new_dirns_each_iter` - Number of new search directions to add with each iteration where we do not have a full set of search directions. Default is 0, as this approach is not recommended.
104104

105105

106106
References

docs/build/html/.buildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Sphinx build info version 1
22
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3-
config: aa64240bdba1bf7c73696f2a8006ee55
3+
config: f6da778e78f2e0fc2cb0ba72618f9760
44
tags: 645f666f9bcd5a90fca523b33c5a78b7

docs/build/html/_sources/advanced.rst.txt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,22 +85,22 @@ Multiple Restarts
8585

8686
Dynamically Growing Initial Set
8787
-------------------------------
88-
* :code:`growing.ndirs_initial` - Number of initial points to add (excluding :math:`x_k`). Default is :code:`npt-1`.
89-
* :code:`growing.num_new_dirns_each_iter` - Number of new search directions to add with each iteration where we do not have a full set of search directions. Default is 1.
90-
* :code:`growing.delta_scale_new_dirns` - When adding new search directions, the length of the step as a multiple of :math:`\Delta_k`. Default is 1, but if setting :code:`growing.perturb_trust_region_step=True` should be made smaller (e.g. 0.1).
91-
* :code:`growing.do_geom_steps` - While still growing the initial set, whether to do geometry-improving steps in the trust region algorithm, as per the usual algorithm. Default is :code:`False`.
92-
* :code:`growing.safety.do_safety_step` - While still growing the initial set, whether to perform safety steps, or the regular trust region steps. Default is :code:`True`.
93-
* :code:`growing.safety.reduce_delta` - While still growing the initial set, whether to reduce :math:`\Delta_k` in safety steps. Default is :code:`False`.
94-
* :code:`growing.safety.full_geom_step` - While still growing the initial set, whether to do a full geometry-improving step within safety steps (the same as the post-growing phase of the algorithm). Since this involves reducing :math:`\Delta_k`, cannot be :code:`True` if :code:`growing.safety.reduce_delta` is :code:`True`. Default is :code:`False`.
95-
* :code:`growing.full_rank.use_full_rank_interp` - Whether to perturb the interpolated :math:`J_k` to make it full rank, allowing the trust region step to include components in the full search space. If :code:`True`, setting :code:`growing.num_new_dirns_each_iter` to 0 is recommended. Default is :code:`False`.
88+
* :code:`growing.ndirs_initial` - Number of initial points to add (excluding :math:`x_k`). This should only be changed to a value less than :math:`n`, and only if the default setup cost of :math:`n+1` evaluations of :code:`objfun` is impractical. If this is set to be less than the default, the input value :code:`npt` should be set to :math:`n`. If the default is used, all the below parameters have no effect on DFO-LS. Default is :code:`npt-1`.
89+
* :code:`growing.full_rank.use_full_rank_interp` - If :code:`growing.ndirs_initial` is less than :code:`npt`, whether to perturb the interpolated :math:`J_k` to make it full rank, allowing the trust region step to include components in the full search space. Default is :code:`True` if :math:`m\geq n` and :code:`False` otherwise (opposite to :code:`growing.perturb_trust_region_step`).
90+
* :code:`growing.perturb_trust_region_step` - Whether to perturb the trust region step by an orthogonal direction not yet searched. This is an alternative to :code:`growing.full_rank.use_full_rank_interp`. Default is :code:`False` if :math:`m\geq n` and :code:`True` otherwise (opposite to :code:`growing.full_rank.use_full_rank_interp`).
91+
* :code:`growing.delta_scale_new_dirns` - When adding new search directions, the length of the step as a multiple of :math:`\Delta_k`. Default is 1, or 0.1 if :code:`growing.perturb_trust_region_step=True`.
9692
* :code:`growing.full_rank.scale_factor` - Magnitude of extra components added to :math:`J_k`. Default is :math:`10^{-2}`.
9793
* :code:`growing.full_rank.svd_scale_factor` - Floor singular values of :math:`J_k` at this factor of the last nonzero value. Default is 1.
9894
* :code:`growing.full_rank.min_sing_val` - Absolute floor on singular values of :math:`J_k`. Default is :math:`10^{-6}`.
9995
* :code:`growing.full_rank.svd_max_jac_cond` - Cap on condition number of :math:`J_k` after applying floors to singular values (effectively another floor on the smallest singular value, since the largest singular value is fixed). Default is :math:`10^8`.
96+
* :code:`growing.do_geom_steps` - While still growing the initial set, whether to do geometry-improving steps in the trust region algorithm, as per the usual algorithm. Default is :code:`False`.
97+
* :code:`growing.safety.do_safety_step` - While still growing the initial set, whether to perform safety steps, or the regular trust region steps. Default is :code:`True`.
98+
* :code:`growing.safety.reduce_delta` - While still growing the initial set, whether to reduce :math:`\Delta_k` in safety steps. Default is :code:`False`.
99+
* :code:`growing.safety.full_geom_step` - While still growing the initial set, whether to do a full geometry-improving step within safety steps (the same as the post-growing phase of the algorithm). Since this involves reducing :math:`\Delta_k`, cannot be :code:`True` if :code:`growing.safety.reduce_delta` is :code:`True`. Default is :code:`False`.
100100
* :code:`growing.reset_delta` - Whether or not to reset trust region radius :math:`\Delta_k` to its initial value at the end of the growing phase. Default is :code:`False`.
101101
* :code:`growing.reset_rho` - Whether or not to reset trust region radius lower bound :math:`\rho_k` to its initial value at the end of the growing phase. Default is :code:`False`.
102102
* :code:`growing.gamma_dec` - Trust region decrease parameter during the growing phase. Default is :code:`tr_radius.gamma_dec`.
103-
* :code:`growing.perturb_trust_region_step` - Whether to perturb the trust region step by an orthogonal direction not yet searched. This is an alternative to :code:`growing.full_rank.use_full_rank_interp`. Default is :code:`False`.
103+
* :code:`growing.num_new_dirns_each_iter` - Number of new search directions to add with each iteration where we do not have a full set of search directions. Default is 0, as this approach is not recommended.
104104

105105

106106
References

docs/build/html/_sources/history.rst.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,10 @@ Version 1.0.1 (20 Feb 2018)
1313
Version 1.0.2 (20 Jun 2018)
1414
---------------------------
1515
* Extra optional input :code:`args` which passes through arguments for :code:`objfun`.
16-
* Bug fixes: default parameters for reduced initialization cost regime, returning correct value from safety steps, retrieving dependencies during installation.
16+
* Bug fixes: default parameters for reduced initialization cost regime, returning correct value if exiting from within a safety step, retrieving dependencies during installation.
17+
18+
Version 1.1 (16 Jan 2019)
19+
-------------------------
20+
* Use different default reduced initialization cost method for inverse problems to ensure whole space is searched correctly.
21+
* Bug fixes: default trust region radius when scaling feasible region, exit correctly when no Jacobian returned, handling overflow at initial value
1722

0 commit comments

Comments
 (0)