def eval_update(fun): val_a = fun(0.0) val_b = fun(1.0) val_trial = fun(0.6) f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) return self.evaluate( hzl.update(fun, val_a, val_b, val_trial, f_lim))
def eval_update(fun, p=lambda x: x): val_a = hzl._apply(fun, p(0.0)) val_b = hzl._apply(fun, p(1.0)) val_trial = hzl._apply(fun, p(0.6)) f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) return self.evaluate( hzl.update(fun, val_a, val_b, val_trial, f_lim))
def test_update_batching(self): """Tests that update function works in batching mode.""" wolfe_threshold = 1e-6 # We build an example function with 4 batches, each for one of the # following cases: # - a) Trial point has positive slope, so works as right end point. # - b) Trial point has negative slope and value is not too large, # so works as left end point. # - c) Trial point has negative slope but the value is too high, # bisect is used to squeeze the interval. # - d) Trial point is outside of the (a, b) interval. x = np.array([0.0, 0.6, 1.0]) y = np.array([[1.0, 1.2, 1.1], [1.0, 0.9, 1.2], [1.0, 1.1, 1.2], [1.0, 1.1, 1.2]]) dy = np.array([[-0.8, 0.6, 0.6], [-0.8, -0.7, 0.6], [-0.8, -0.7, 0.6], [-0.8, -0.7, 0.6]]) fun = test_function_x_y_dy(x, y, dy) val_a = hzl._apply(fun, tf.zeros(4)) # Values at zero. val_b = hzl._apply(fun, tf.ones(4)) # Values at initial step. val_trial = hzl._apply(fun, [0.6, 0.6, 0.6, 1.5]) f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) expected_left = np.array([0.0, 0.6, 0.0, 0.0]) expected_right = np.array([0.6, 1.0, 0.3, 1.0]) result = self.evaluate(hzl.update(fun, val_a, val_b, val_trial, f_lim)) self.assertEqual(result.num_evals, 1) # Had to do bisect once. self.assertTrue(np.all(result.stopped)) self.assertTrue(np.all(~result.failed)) self.assertTrue(np.all(result.left.df < 0)) # Opposite slopes. self.assertTrue(np.all(result.right.df >= 0)) self.assertArrayNear(result.left.x, expected_left, 1e-5) self.assertArrayNear(result.right.x, expected_right, 1e-5)
def _success_fn(): """Action to take if the midpoint evaluation succeeded.""" update_result = hzl.update(value_and_gradients_function, val_left, val_right, val_mid, f_lim) return _UpdateResult(failed=update_result.failed, num_evals=update_result.num_evals + 1, left=update_result.left, right=update_result.right)
def _apply_update(): update_result = hzl.update( value_and_gradients_function, next_inteval.left, next_inteval.right, val_mid, f_lim, active=still_active) return HagerZhangLineSearchResult( converged=next_inteval.converged, failed=next_inteval.failed | update_result.failed, iterations=next_inteval.iterations + update_result.iteration, func_evals=next_inteval.func_evals + update_result.num_evals, left=update_result.left, right=update_result.right)
def test_update_simple(self): """Tests that update works on a single line function.""" # Example where trial point works as new left end point. wolfe_threshold = 1e-6 x = np.array([0.0, 0.6, 1.0]) y = np.array([1.0, 0.9, 1.2]) dy = np.array([-0.8, -0.7, 0.6]) fun = test_function_x_y_dy(x, y, dy) val_a = hzl._apply(fun, 0.0) val_b = hzl._apply(fun, 1.0) val_trial = hzl._apply(fun, 0.6) f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) result = self.evaluate(hzl.update(fun, val_a, val_b, val_trial, f_lim)) self.assertEqual(result.num_evals, 0) # No extra evaluations needed. self.assertTrue(result.stopped) self.assertTrue(~result.failed) self.assertAlmostEqual(result.left.x, 0.6) self.assertAlmostEqual(result.right.x, 1.0) self.assertLess(result.left.df, 0) # Opposite slopes. self.assertGreaterEqual(result.right.df, 0)
def test_update_batching(self): """Tests that update function works in batching mode.""" wolfe_threshold = 1e-6 # We build an example function with 5 batches, each for one of the # following cases: # - a) Trial point has positive slope, so works as right end point. # - b) Trial point has negative slope and value is not too large, # so works as left end point. # - c) Trial point has negative slope but the value is too high, # bisect is used to squeeze the interval. # - d) Trial point is outside of the (a, b) interval. # - e) Left, right, and trial are all the same, and this batch member is # not active. (This is how hager_zhang._line_search_inner_bisection # passes already-converged points to `update`.) x = np.array([0.0, 0.6, 1.0]) y = np.array([[1.0, 1.2, 1.1], [1.0, 0.9, 1.2], [1.0, 1.1, 1.2], [1.0, 1.1, 1.2], [1.0, 0.9, 1.2]]) dy = np.array([[-0.8, 0.6, 0.6], [-0.8, -0.7, 0.6], [-0.8, -0.7, 0.6], [-0.8, -0.7, 0.6], [-0.8, -0.7, 0.6]]) fun = _test_function_x_y_dy(x, y, dy) val_a = fun([0.0] * 5) # Values at zero. val_b = fun([1.0] * 4 + [0.0]) # Values at initial step. val_trial = fun([0.6, 0.6, 0.6, 1.5, 0.0]) active = np.array([True] * 4 + [False]) f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) expected_left = np.array([0.0, 0.6, 0.0, 0.0, 0.0]) expected_right = np.array([0.6, 1.0, 0.3, 1.0, 0.0]) result = self.evaluate( hzl.update(fun, val_a, val_b, val_trial, f_lim, active=active)) self.assertEqual(result.num_evals, 1) # Had to do bisect once. self.assertTrue(np.all(result.stopped)) self.assertTrue(np.all(~result.failed)) self.assertTrue(np.all(result.left.df[:-1] < 0)) # Opposite slopes. self.assertTrue(np.all(result.right.df[:-1] >= 0)) self.assertArrayNear(result.left.x, expected_left, 1e-5) self.assertArrayNear(result.right.x, expected_right, 1e-5)