Skip to content

Commit

Permalink
Refactor multistart fallback solver
Browse files Browse the repository at this point in the history
  • Loading branch information
timmens committed Jan 16, 2024
1 parent 4ca4af2 commit 268c979
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 55 deletions.
4 changes: 2 additions & 2 deletions src/tranquilo/solve_subproblem.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
robust_sphere_solver_inscribed_cube,
robust_sphere_solver_norm_constraint,
robust_sphere_solver_reparametrized,
robust_solver_multistart,
robust_cube_solver_multistart,
)


Expand Down Expand Up @@ -83,8 +83,8 @@ def get_subsolver(sphere_solver, cube_solver, retry_with_fallback, user_options=
built_in_cube_solvers = {
"bntr": bntr,
"bntr_fast": bntr_fast,
"fallback_multistart": robust_solver_multistart,
"fallback_cube": robust_cube_solver,
"fallback_multistart": robust_cube_solver_multistart,
}

_sphere_subsolver = get_component(
Expand Down
118 changes: 65 additions & 53 deletions src/tranquilo/subsolvers/fallback_subsolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,40 @@
from tranquilo.exploration_sample import draw_exploration_sample


def robust_solver_multistart(model, x_candidate, lower_bounds, upper_bounds):
# ======================================================================================
# Cube solvers
# ======================================================================================
def robust_cube_solver(model, x_candidate, radius=1.0):
"""Robust cube solver.
Argument `radius` corresponds to half of the side length of the cube.
"""
crit, grad = _get_crit_and_grad(model)

Check warning on line 17 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L17

Added line #L17 was not covered by tests

lower_bounds = -radius * np.ones(len(x_candidate))
upper_bounds = radius * np.ones(len(x_candidate))

Check warning on line 20 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L19-L20

Added lines #L19 - L20 were not covered by tests

res = minimize(

Check warning on line 22 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L22

Added line #L22 was not covered by tests
crit,
x_candidate,
method="L-BFGS-B",
jac=grad,
bounds=Bounds(lower_bounds, upper_bounds),
options={
"maxiter": len(x_candidate),
},
)

return {

Check warning on line 33 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L33

Added line #L33 was not covered by tests
"x": res.x,
"criterion": res.fun,
"n_iterations": res.nit,
"success": True,
}


def robust_cube_solver_multistart(model, x_candidate, lower_bounds, upper_bounds):
np.random.seed(12345)
start_values = draw_exploration_sample(

Check warning on line 43 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L42-L43

Added lines #L42 - L43 were not covered by tests
x=x_candidate,
Expand All @@ -17,33 +50,45 @@ def robust_solver_multistart(model, x_candidate, lower_bounds, upper_bounds):
seed=1234,
)

def crit(x):
return model.predict(x)

bounds = Bounds(lower_bounds, upper_bounds)

best_crit = np.inf
accepted_x = None
critvals = []

Check warning on line 55 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L53-L55

Added lines #L53 - L55 were not covered by tests

for x in start_values:
res = minimize(
crit,
x,
method="L-BFGS-B",
bounds=bounds,
)
if res.fun <= best_crit:
accepted_x = res.x
critvals.append(res.fun)
res = robust_cube_solver(model, x)

Check warning on line 58 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L57-L58

Added lines #L57 - L58 were not covered by tests

if res["criterion"] <= best_crit:
accepted_x = res["x"]
best_crit = res["criterion"]

Check warning on line 62 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L60-L62

Added lines #L60 - L62 were not covered by tests

critvals.append(res["criterion"])

Check warning on line 64 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L64

Added line #L64 was not covered by tests

return {

Check warning on line 66 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L66

Added line #L66 was not covered by tests
"x": accepted_x,
"criterion": best_crit,
"std": np.std(critvals),
"n_iterations": None,
"success": None,
}


# ======================================================================================
# Sphere solvers
# ======================================================================================


def robust_sphere_solver_inscribed_cube(model, x_candidate):
"""Robust sphere solver that uses a cube solver in an inscribed cube.
We let x be in the largest cube that is inscribed inside the unit sphere. Formula
is taken from http://tinyurl.com/4astpuwn.
This solver cannot find solutions on the hull of the sphere.
"""
return robust_cube_solver(model, x_candidate, radius=1 / np.sqrt(len(x_candidate)))

Check warning on line 89 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L89

Added line #L89 was not covered by tests


def robust_sphere_solver_reparametrized(model, x_candidate):
"""Robust sphere solver that uses reparametrization.
Expand Down Expand Up @@ -90,44 +135,6 @@ def crit(x):
}


def robust_cube_solver(model, x_candidate, radius=1.0):
"""Robust cube solver."""
crit, grad = _get_crit_and_grad(model)

lower_bounds = -radius * np.ones(len(x_candidate))
upper_bounds = radius * np.ones(len(x_candidate))

res = minimize(
crit,
x_candidate,
method="L-BFGS-B",
jac=grad,
bounds=Bounds(lower_bounds, upper_bounds),
options={
"maxiter": len(x_candidate),
},
)

return {
"x": res.x,
"criterion": res.fun,
"n_iterations": res.nit,
"success": True,
}


def robust_sphere_solver_inscribed_cube(model, x_candidate):
"""Robust sphere solver that uses a cube solver in an inscribed cube.
We let x be in the largest cube that is inscribed inside the unit sphere. Formula
is taken from http://tinyurl.com/4astpuwn.
This solver cannot find solutions on the hull of the sphere.
"""
return robust_cube_solver(model, x_candidate, radius=1 / np.sqrt(len(x_candidate)))


def robust_sphere_solver_norm_constraint(model, x_candidate):
"""Robust sphere solver that uses ||x|| <= 1 as a nonlinear constraint.
Expand All @@ -154,6 +161,11 @@ def robust_sphere_solver_norm_constraint(model, x_candidate):
}


# ======================================================================================
# Criterion, gradient, and spherical constraint
# ======================================================================================


def _get_crit_and_grad(model):
def _crit(x, c, g, h):
return c + x @ g + 0.5 * x @ h @ x

Check warning on line 171 in src/tranquilo/subsolvers/fallback_subsolvers.py

View check run for this annotation

Codecov / codecov/patch

src/tranquilo/subsolvers/fallback_subsolvers.py#L170-L171

Added lines #L170 - L171 were not covered by tests
Expand Down

0 comments on commit 268c979

Please sign in to comment.