def test_bracket_batching(self): """Tests that bracketing works in batching mode.""" wolfe_threshold = 1e-6 # We build an example function with 4 batches, each for one of the # following cases: # - a) Minimum bracketed from the beginning. # - b) Minimum bracketed after one expansion. # - c) Needs bisect from the beginning. # - d) Needs one round of expansion and then bisect. x = tf.constant([0.0, 1.0, 5.0]) y = tf.constant([[1.0, 1.2, 1.1], [1.0, 0.9, 1.2], [1.0, 1.1, 1.2], [1.0, 0.9, 1.1]]) dy = tf.constant([[-0.8, 0.6, -0.8], [-0.8, -0.7, 0.6], [-0.8, -0.7, -0.8], [-0.8, -0.7, -0.8]]) fun = test_function_x_y_dy(x, y, dy, eps=0.1) val_a = hzl._apply(fun, tf.zeros(4)) # Values at zero. val_b = hzl._apply(fun, tf.ones(4)) # Values at initial step. f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) expected_left = np.array([0.0, 1.0, 0.0, 0.0]) expected_right = np.array([1.0, 5.0, 0.5, 2.5]) result = self.evaluate( hzl.bracket(fun, val_a, val_b, f_lim, max_iterations=5)) self.assertEqual(result.num_evals, 2) # Once bracketing, once bisecting. 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 test_bisect_batching(self): """Tests that bisect works in batching mode.""" wolfe_threshold = 1e-6 # Let's build our example function with 4 batches, each evaluating a # different poly. They all have negative slopes both on 0.0 and 1.0, # but different slopes (positive, negative) and values (low enough, too # high) on their midpoint. x = np.array([0.0, 0.5, 1.0]) y = np.array([[1.0, 0.6, 1.2], [1.0, 0.6, 1.2], [1.0, 1.6, 1.2], [1.0, 1.6, 1.2]]) dy = np.array([[-0.8, 0.6, -0.7], [-0.8, -0.4, -0.7], [-0.8, 0.8, -0.7], [-0.8, -0.4, -0.7]]) 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. f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) expected_left = np.array([0.0, 0.5, 0.0, 0.0]) expected_right = np.array([0.5, 0.75, 0.5, 0.25]) result = self.evaluate(hzl.bisect(fun, val_a, val_b, f_lim)) self.assertTrue(np.all(result.stopped)) self.assertTrue(np.all(~result.failed)) self.assertTrue(np.all(result.left.df < 0)) 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 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 test_bisect_simple(self): """Tests that bisect works on a 1 variable scalar valued function.""" wolfe_threshold = 1e-6 x = np.array([0.0, 0.5, 1.0]) y = np.array([1.0, 0.6, 1.2]) dy = np.array([-0.8, 0.6, -0.7]) fun = test_function_x_y_dy(x, y, dy) val_a = hzl._apply(fun, 0.0) # Value at zero. val_b = hzl._apply(fun, 1.0) # Value at initial step. f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) result = self.evaluate(hzl.bisect(fun, val_a, val_b, f_lim)) self.assertEqual(result.right.x, 0.5)
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_bracket_simple(self): """Tests that bracketing works on a 1 variable scalar valued function.""" # Example crafted to require one expansion during bracketing, and then # some bisection; same as case (d) in test_bracket_batching below. wolfe_threshold = 1e-6 x = np.array([0.0, 1.0, 2.5, 5.0]) y = np.array([1.0, 0.9, -2.0, 1.1]) dy = np.array([-0.8, -0.7, 1.6, -0.8]) fun = test_function_x_y_dy(x, y, dy) val_a = hzl._apply(fun, 0.0) # Value at zero. val_b = hzl._apply(fun, 1.0) # Value at initial step. f_lim = val_a.f + (wolfe_threshold * tf.abs(val_a.f)) result = self.evaluate( hzl.bracket(fun, val_a, val_b, f_lim, max_iterations=5)) self.assertEqual(result.iteration, 1) # One expansion. self.assertEqual(result.num_evals, 2) # Once bracketing, once bisecting. self.assertEqual(result.left.x, 0.0) self.assertEqual(result.right.x, 2.5) self.assertLess(result.left.df, 0) # Opposite slopes. self.assertGreaterEqual(result.right.df, 0)
def eval_secant2(fun, p=lambda x: x): val_0 = hzl._apply(fun, p(0.0)) val_a = hzl._apply(fun, p(1.0)) val_b = hzl._apply(fun, p(2.0)) f_lim = val_0.f + (wolfe_threshold * tf.abs(val_0.f)) return self.evaluate(hzl.secant2(fun, val_0, val_a, val_b, f_lim))