From c589a2f6afc51d1f314a9956b4c885699fbf71a9 Mon Sep 17 00:00:00 2001 From: Joseph Weston <joseph@weston.cloud> Date: Thu, 22 Nov 2018 14:21:01 +0100 Subject: [PATCH] refactor adding all loss functions to tests involving a learner --- adaptive/tests/test_learners.py | 69 +++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/adaptive/tests/test_learners.py b/adaptive/tests/test_learners.py index e02f2478..b6da0725 100644 --- a/adaptive/tests/test_learners.py +++ b/adaptive/tests/test_learners.py @@ -28,6 +28,26 @@ except ModuleNotFoundError: SKOptLearner = None +LOSS_FUNCTIONS = { + Learner1D: ('loss_per_interval', ( + adaptive.learner.learner1D.default_loss, + adaptive.learner.learner1D.uniform_loss, + adaptive.learner.learner1D.curvature_loss_function(), + )), + Learner2D: ('loss_per_triangle', ( + adaptive.learner.learner2D.default_loss, + adaptive.learner.learner2D.uniform_loss, + adaptive.learner.learner2D.minimize_triangle_surface_loss, + adaptive.learner.learner2D.resolution_loss_function(), + )), + LearnerND: ('loss_per_simplex', ( + adaptive.learner.learnerND.default_loss, + adaptive.learner.learnerND.std_loss, + adaptive.learner.learnerND.uniform_loss, + )), +} + + def generate_random_parametrization(f): """Return a realization of 'f' with parameters bound to random values. @@ -75,38 +95,26 @@ def maybe_skip(learner): # All parameters except the first must be annotated with a callable that # returns a random value for that parameter. - -@learn_with(Learner1D, bounds=(-1, 1), loss_per_interval=adaptive.learner.learner1D.default_loss) -@learn_with(Learner1D, bounds=(-1, 1), loss_per_interval=adaptive.learner.learner1D.uniform_loss) -@learn_with(Learner1D, bounds=(-1, 1), loss_per_interval=adaptive.learner.learner1D.curvature_loss_function()) +@learn_with(Learner1D, bounds=(-1, 1)) def quadratic(x, m: uniform(0, 10), b: uniform(0, 1)): return m * x**2 + b -@learn_with(Learner1D, bounds=(-1, 1), loss_per_interval=adaptive.learner.learner1D.default_loss) -@learn_with(Learner1D, bounds=(-1, 1), loss_per_interval=adaptive.learner.learner1D.uniform_loss) -@learn_with(Learner1D, bounds=(-1, 1), loss_per_interval=adaptive.learner.learner1D.curvature_loss_function()) +@learn_with(Learner1D, bounds=(-1, 1)) def linear_with_peak(x, d: uniform(-1, 1)): a = 0.01 return x + a**2 / (a**2 + (x - d)**2) -@learn_with(LearnerND, bounds=((-1, 1), (-1, 1)), loss_per_simplex=adaptive.learner.learnerND.default_loss) -@learn_with(LearnerND, bounds=((-1, 1), (-1, 1)), loss_per_simplex=adaptive.learner.learnerND.std_loss) -@learn_with(LearnerND, bounds=((-1, 1), (-1, 1)), loss_per_simplex=adaptive.learner.learnerND.uniform_loss) -@learn_with(Learner2D, bounds=((-1, 1), (-1, 1)), loss_per_triangle=adaptive.learner.learner2D.default_loss) -@learn_with(Learner2D, bounds=((-1, 1), (-1, 1)), loss_per_triangle=adaptive.learner.learner2D.uniform_loss) -@learn_with(Learner2D, bounds=((-1, 1), (-1, 1)), loss_per_triangle=adaptive.learner.learner2D.minimize_triangle_surface_loss) -@learn_with(Learner2D, bounds=((-1, 1), (-1, 1)), loss_per_triangle=adaptive.learner.learner2D.resolution_loss_function()) +@learn_with(LearnerND, bounds=((-1, 1), (-1, 1))) +@learn_with(Learner2D, bounds=((-1, 1), (-1, 1))) def ring_of_fire(xy, d: uniform(0.2, 1)): a = 0.2 x, y = xy return x + math.exp(-(x**2 + y**2 - d**2)**2 / a**4) -@learn_with(LearnerND, bounds=((-1, 1), (-1, 1), (-1, 1)), loss_per_simplex=adaptive.learner.learnerND.default_loss) -@learn_with(LearnerND, bounds=((-1, 1), (-1, 1), (-1, 1)), loss_per_simplex=adaptive.learner.learnerND.std_loss) -@learn_with(LearnerND, bounds=((-1, 1), (-1, 1), (-1, 1)), loss_per_simplex=adaptive.learner.learnerND.uniform_loss) +@learn_with(LearnerND, bounds=((-1, 1), (-1, 1), (-1, 1))) def sphere_of_fire(xyz, d: uniform(0.2, 1)): a = 0.2 x, y, z = xyz @@ -120,6 +128,17 @@ def gaussian(n): # Decorators for tests. + +# Create a sequence of learner parameters by adding all +# possible loss functions to an existing parameter set. +def add_loss_to_params(learner_type, existing_params): + if learner_type not in LOSS_FUNCTIONS: + return [existing_params] + loss_param, loss_functions = LOSS_FUNCTIONS[learner_type] + loss_params = [{loss_param: f} for f in loss_functions] + return [dict(**existing_params, **lp) for lp in loss_params] + + def run_with(*learner_types): pars = [] for l in learner_types: @@ -127,13 +146,15 @@ def run_with(*learner_types): if has_marker: marker, l = l for f, k in learner_function_combos[l]: - # Check if learner was marked with our `xfail` decorator - # XXX: doesn't work when feeding kwargs to xfail. - if has_marker: - pars.append(pytest.param(l, f, dict(k), - marks=[marker])) - else: - pars.append((l, f, dict(k))) + ks = add_loss_to_params(l, k) + for k in ks: + # Check if learner was marked with our `xfail` decorator + # XXX: doesn't work when feeding kwargs to xfail. + if has_marker: + pars.append(pytest.param(l, f, dict(k), + marks=[marker])) + else: + pars.append((l, f, dict(k))) return pytest.mark.parametrize('learner_type, f, learner_kwargs', pars) -- GitLab