def test_fit_pwl_unimodal_with_forced_direction(self): x = np.arange(51, dtype=float) / 10 y = pwlcurve.PWLCurve([(0, 0), (1, 2), (2, 5), (3, 2), (4, 0)]).eval(x) # y is concave down, so a concave solution is ideal. concave_curve = fitter.fit_pwl( x, y, num_segments=4, fx=transform.identity, mono=fitter.MonoType.bitonic_concave_down) convex_curve = fitter.fit_pwl(x, y, num_segments=4, fx=transform.identity, mono=fitter.MonoType.bitonic_concave_up) self.assert_allclose(y, concave_curve.eval(x)) self.assert_notallclose(y, convex_curve.eval(x)) # -y is concave up, so a convex solution is ideal. concave_curve = fitter.fit_pwl( x, -y, num_segments=4, fx=transform.identity, mono=fitter.MonoType.bitonic_concave_down) convex_curve = fitter.fit_pwl(x, -y, num_segments=4, fx=transform.identity, mono=fitter.MonoType.bitonic_concave_up) self.assert_allclose(-y, convex_curve.eval(x)) self.assert_notallclose(-y, concave_curve.eval(x))
def test_learn_ends_has_no_effect_when_endpoints_are_ideal(self): # In this case, the ideal fit uses the endpoints, so no need to learn ends. x = np.arange(51, dtype=float) y = pwlcurve.PWLCurve([(0, 0), (10, 1), (25, 25), (50, 60)]).eval(x) w = np.ones_like(x) self.assertEqual(fitter.fit_pwl(x, y, w, 3, learn_ends=True), fitter.fit_pwl(x, y, w, 3, learn_ends=False))
def test_one_segment_pwl_with_flat_ends(self): x = np.arange(51, dtype=float) y = pwlcurve.PWLCurve([(0, 0), (10, 0), (40, 50), (50, 50)]).eval(x) w = np.ones_like(x) # A one-segment PWLCurve can fit fn perfectly, but only if its knots are # [(10, 0), (40, 50)]. This test confirms that fit_pwl learns those knots. curve = fitter.fit_pwl(x, y, w, 1) self.assert_allclose([(10, 0), (40, 50)], curve.points) self.assert_allclose(y, curve.eval(x)) self.assertEqual(transform.identity, curve.fx)
def test_non_mono_increasing_two_segment_pwl_with_flat_ends(self): x = np.arange(51, dtype=float) y = pwlcurve.PWLCurve([(0, 0), (10, 0), (25, 15), (40, 0), (50, 0)]).eval(x) w = np.ones_like(x) # A two-segment PWLCurve can fit fn perfectly, but only if its knots are # [(10, 0), (25, 15), (40, 0)]. This test confirms that fit_pwl will learn # those knots. curve = fitter.fit_pwl(x, y, w, 2, mono=False, fx=transform.identity) self.assert_allclose([(10, 0), (25, 15), (40, 0)], curve.points) self.assert_allclose(y, curve.eval(x))
def test_one_segment_pwl_with_flat_ends_but_no_learning_ends(self): x = np.arange(51, dtype=float) y = pwlcurve.PWLCurve([(0, 0), (10, 0), (40, 50), (50, 50)]).eval(x) w = np.ones_like(x) # A one-segment PWLCurve can fit fn perfectly, but only if its knots are # [(10, 0), (40, 50)]. In this test, we disable learn_ends, and show that # the fitter can't learn the ideal fit because it's forced to use 0 and 50 # as control points. curve = fitter.fit_pwl(x, y, w, 1, learn_ends=False) self.assertEqual([0, 50], curve.xs) self.assert_notallclose(y, curve.eval(x))
def test_fit_pwl_unimodal_on_non_unimodal_data(self): x = np.arange(51, dtype=float) / 10 y = pwlcurve.PWLCurve([(0, 0), (1, 2), (2, 0), (3, 2), (4, 0)]).eval(x) curve = fitter.fit_pwl(x, y, num_segments=4, fx=transform.identity, mono=fitter.MonoType.bitonic) # An unrestricted fit should change directions three times, but a unimodal # curve will only change once. self.assertEqual(1, count_slope_inversions(curve.ys)) # Should be concave down -- increasing at first, decreasing at the end. self.assertLess(curve.ys[0], curve.ys[1]) self.assertLess(curve.ys[-1], curve.ys[-2])
def test_mono_type_enum_and_bool(self): np.random.seed(48440) x = np.sort(np.random.uniform(size=100)) y = np.random.normal(size=100) w = np.random.uniform(size=100) # fit_pwl treats mono=True as synonymous to mono=MonoType.mono. self.assertEqual(fitter.fit_pwl(x, y, w, 2, mono=True), fitter.fit_pwl(x, y, w, 2, mono=fitter.MonoType.mono)) self.assertNotEqual( fitter.fit_pwl(x, y, w, 2, mono=True), fitter.fit_pwl(x, y, w, 2, mono=fitter.MonoType.nonmono)) # fit_pwl treats mono=False as synonymous to mono=MonoType.nonmono. self.assertEqual(fitter.fit_pwl(x, y, w, 2, mono=False), fitter.fit_pwl(x, y, w, 2, mono=fitter.MonoType.nonmono)) self.assertNotEqual(fitter.fit_pwl(x, y, w, 2, mono=False), fitter.fit_pwl(x, y, w, 2, mono=fitter.MonoType.mono))
def pwl_predict(x, y, *args, **kwargs): """Test utility to fit and evaluate a curve in one function.""" return fitter.fit_pwl(x, y, *args, **kwargs).eval(x)