Example #1
0
    def test_solver_squared_error_in_the_underdetermined_case(self):
        x = np.array([0., 4., 5.])
        y = np.array([0., 0., 2.])
        w = np.ones_like(x)
        knots = np.array([1., 2., 3.])

        # The x=1 and x=3 knots affect the squared error, so their y-values are
        # determined by the system. However, the x=2 knot has no effect, so it can
        # take on an infinite range of values.
        # The expected curve is [(1., 0), (2., unknown), (3., 1.)].
        expected_error = 2.0

        nonmono_solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w)
        _, nonmono_error = nonmono_solver.solve(knots)
        self.assertAlmostEqual(expected_error, nonmono_error)

        mono_up_solver = fitter._WeightedLeastSquaresPWLSolver(x,
                                                               y,
                                                               w,
                                                               min_slope=0)
        _, mono_up_error = mono_up_solver.solve(knots)
        self.assertAlmostEqual(expected_error, mono_up_error)

        # The mono-down case is fully constrained, with best curve y = mean = 2/3.
        mono_down_solver = fitter._WeightedLeastSquaresPWLSolver(x,
                                                                 y,
                                                                 w,
                                                                 max_slope=0)
        _, mono_down_error = mono_down_solver.solve(knots)
        self.assertAlmostEqual(8. / 3, mono_down_error)
Example #2
0
    def test_bitonic_solver_suffers_when_peak_or_direction_is_wrong(self):
        np.random.seed(58444)
        x = np.sort(np.random.uniform(size=100))
        y = (0.5 -
             x)**2  # y is a concave-up parabole with its minimum at x=0.6.
        w = np.random.uniform(size=100)
        knots = [.1, .3, .5, .7, .9]

        non_mono_solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w)
        concave_down_solver = fitter._WeightedLeastSquaresPWLSolver(
            x, y, w, bitonic_peak=0.5, bitonic_concave_down=True)
        peak_too_low_solver = fitter._WeightedLeastSquaresPWLSolver(
            x, y, w, bitonic_peak=0.2, bitonic_concave_down=False)
        peak_too_high_solver = fitter._WeightedLeastSquaresPWLSolver(
            x, y, w, bitonic_peak=0.8, bitonic_concave_down=False)

        non_mono_y, non_mono_error = non_mono_solver.solve(knots)
        concave_down_y, concave_down_error = concave_down_solver.solve(knots)
        peak_too_low_y, peak_too_low_error = peak_too_low_solver.solve(knots)
        peak_too_high_y, peak_too_high_error = peak_too_high_solver.solve(
            knots)

        # The bitonic restrictions prevent us from learning the good solution.
        self.assert_notallclose(non_mono_y, concave_down_y)
        self.assert_notallclose(non_mono_y, peak_too_low_y)
        self.assert_notallclose(non_mono_y, peak_too_high_y)

        self.assertNotAlmostEqual(concave_down_error, non_mono_error)
        self.assertNotAlmostEqual(peak_too_low_error, non_mono_error)
        self.assertNotAlmostEqual(peak_too_high_error, non_mono_error)
Example #3
0
  def test_mono_solver_returns_mono_solution(self):
    np.random.seed(58443)
    x = np.sort(np.random.uniform(size=10))
    y = np.random.normal(scale=.5, size=10)
    w = np.random.uniform(size=10)
    knots = np.sort(np.random.choice(x, size=4, replace=False))
    mono_up_solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w, min_slope=0)
    mono_down_solver = fitter._WeightedLeastSquaresPWLSolver(
        x, y, w, max_slope=0)

    mono_up_y, _ = mono_up_solver.solve(knots)
    self.assert_increasing(mono_up_y)
    mono_down_y, _ = mono_down_solver.solve(knots)
    self.assert_decreasing(mono_down_y)
Example #4
0
 def test_mono_down_equals_flipped_mono_up(self):
   # Fitting monoup should be equivalent to flipping y and fitting monodown.
   np.random.seed(58442)
   x = np.sort(np.random.uniform(size=100))
   y = x**2 + np.random.normal(scale=.2, size=100)
   w = np.random.uniform(size=100)
   mono_up_solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w, min_slope=0)
   mono_down_solver = fitter._WeightedLeastSquaresPWLSolver(
       x, -y, w, max_slope=0)
   knots = [.2, .5, .8, .9]
   mono_up_y, mono_up_error = mono_up_solver.solve(knots)
   mono_down_y, mono_down_error = mono_down_solver.solve(knots)
   self.assertAlmostEqual(mono_up_error, mono_down_error)
   self.assert_allclose(mono_up_y, -mono_down_y)
Example #5
0
  def test_mono_solver_same_as_non_mono_for_mono_task(self):
    # The monotonicity restriction shouldn't matter if the natural fit is
    # monotonic.
    np.random.seed(58444)
    x = np.sort(np.random.uniform(size=100))
    y = x**2
    w = np.random.uniform(size=100)
    knots = [.2, .5, .8, .9]

    mono_up_solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w, min_slope=0)
    non_mono_solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w)
    mono_up_y, mono_up_error = mono_up_solver.solve(knots)
    non_mono_y, non_mono_error = non_mono_solver.solve(knots)
    np.testing.assert_array_equal(non_mono_y, mono_up_y)
    self.assertAlmostEqual(mono_up_error, non_mono_error)
Example #6
0
    def test_knots_between_xs(self):
        x = np.array([0., 1., 2., 3., 4., 5., 6., 7.])
        y = np.array([0., 0., 0., 1., 2., 3., 4., 4.])
        w = np.ones_like(x)
        knots = [2.5, 3.5, 4.5, 5.5]

        # This problem has a perfect monotone increasing solution, which the solver
        # should find whether it's set to mono-up or non-mono.
        solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w)
        solution, residue = solver.solve(knots)
        self.assert_allclose(solution, [0, 2, 2, 4])
        self.assertAlmostEqual(residue, 0)

        solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w, min_slope=0)
        solution, residue = solver.solve(knots)
        self.assert_allclose(list(solution), [0, 2, 2, 4])
        self.assertAlmostEqual(residue, 0)
Example #7
0
    def test_solver_works_without_bounding_knots(self):
        x = np.array([0., 1., 2., 3., 4., 5., 6., 7.])
        y = np.array([0., 0., 0., 1., 2., 3., 4., 4.])
        w = np.ones_like(x)
        knots = [2., 3., 5., 6.]

        # This problem has a perfect monotone increasing solution, which the solver
        # should find whether it's set to mono-up or non-mono.
        solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w)
        solution, error = solver.solve(knots)
        self.assert_allclose(solution, [0, 1, 3, 4])
        self.assertAlmostEqual(error, 0)

        solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w, min_slope=0)
        solution, error = solver.solve(knots)
        self.assert_allclose(solution, [0, 1, 3, 4])
        self.assertAlmostEqual(error, 0)
Example #8
0
 def test_min_and_max_slope(self):
   x = np.array([0., 1., 2., 3., 4.])
   y = np.array([0., 2., 0., -2, 0.])
   w = np.ones_like(x)
   knots = x
   slope_solver = fitter._WeightedLeastSquaresPWLSolver(
       x, y, w, min_slope=-1.0, max_slope=1.0)
   knot_ys, _ = slope_solver.solve(knots)
   self.assert_allclose(knot_ys, [0, 1, 0, -1, 0])
Example #9
0
 def test_max_slope_smooths_out_spike(self):
   x = np.array([0., 2., 4., 6., 8., 10.])
   y = np.array([0., 0., 0., 6., 6., 6.])
   w = np.ones_like(x)
   knots = x
   max_slope_solver = fitter._WeightedLeastSquaresPWLSolver(
       x, y, w, max_slope=1.0)
   max_slope_y, _ = max_slope_solver.solve(knots)
   self.assert_allclose(max_slope_y, [0, 0, 2, 4, 6, 6])
Example #10
0
  def test_reports_correct_error(self):
    np.random.seed(58440)
    x = np.sort(np.random.uniform(size=100))
    y = x**2 + np.random.normal(scale=.2, size=100)
    w = np.random.uniform(size=100)
    knots = [.2, .5, .8, .9]

    solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w)
    knot_ys, reported_error = solver.solve(knots)
    points = list(zip(knots, knot_ys))
    true_error = _curve_error_on_points(x, y, w, pwlcurve.PWLCurve(points))
    self.assertAlmostEqual(true_error, reported_error)

    mono_solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w, min_slope=0)
    knot_ys, reported_error = mono_solver.solve(knots)
    points = list(zip(knots, knot_ys))
    true_error = _curve_error_on_points(x, y, w, pwlcurve.PWLCurve(points))
    self.assertAlmostEqual(true_error, reported_error)
Example #11
0
  def test_bitonic_solver_same_as_non_mono_for_bitonic_task(self):
    # The bitonic restriction shouldn't matter if the natural fit is bitonic.
    np.random.seed(58444)
    x = np.sort(np.random.uniform(size=100))
    y = (0.6 - x)**2  # y is a concave-up parabole with its minimum at x=0.6.
    w = np.random.uniform(size=100)
    knots = [.2, .5, .7, .9]

    concave_up_solver = fitter._WeightedLeastSquaresPWLSolver(
        x, y, w, bitonic_peak=0.6, bitonic_concave_down=False)
    non_mono_solver = fitter._WeightedLeastSquaresPWLSolver(x, y, w)

    concave_up_y, concave_up_error = concave_up_solver.solve(knots)
    non_mono_y, non_mono_error = non_mono_solver.solve(knots)

    # The problem is bitonic concave up, so the concave up solution matches the
    # non-mono.
    np.testing.assert_array_equal(non_mono_y, concave_up_y)
    self.assertAlmostEqual(concave_up_error, non_mono_error)
Example #12
0
  def test_sloped_solver_returns_sloped_solution(self):
    np.random.seed(58443)
    x = np.sort(np.random.uniform(size=10))
    y = np.random.normal(scale=.5, size=10)
    w = np.random.uniform(size=10)
    knots = np.sort(np.random.choice(x, size=4, replace=False))
    min_slope_solver = fitter._WeightedLeastSquaresPWLSolver(
        x, y, w, min_slope=5.)
    max_slope_solver = fitter._WeightedLeastSquaresPWLSolver(
        x, y, w, max_slope=-5.)

    min_slope_y, _ = min_slope_solver.solve(knots)
    slopes = (min_slope_y[1:] - min_slope_y[:1]) / (knots[1:] - knots[:-1])
    for slope in slopes:
      self.assertGreaterEqual(slope, 5)

    max_slope_y, _ = max_slope_solver.solve(knots)
    slopes = (max_slope_y[1:] - max_slope_y[:1]) / (knots[1:] - knots[:-1])
    for slope in slopes:
      self.assertLessEqual(slope, -5)