def test_split(self): # If this ever breaks, we should solve the problem by writing a # new kind of Function that turns a lambda expression into a # Function. That way, it will never be simplified. # f = x^2 + x f = Function.sum(Function.power(Function.identity(), Function.constant(2)), Function.identity()) # f(-1) = 0, f(0) = 0, f(-.5) = -.25 # The range of f on [-1,0] is [-.25,0] # f([-1,0]) = [-1,1] # f([-1,-.5]) = [-.75,.5] # f([-.5,0]) = [-.5,.25] # This will never finish, since it asks for the exact bounds self.assertTrue(is_bounded(f, Interval(-1,0), Interval(-1/4,0)) is None) # g = 1/2*x^3-3/2*x g = Function.sum(Function.product( Function.constant(.5), Function.power(Function.identity(), Function.constant(3))), Function.product(Function.constant(-1.5), Function.identity())) self.assertEqual(is_bounded(g, Interval(-1.5,1.5), Interval(-.9,1)), False) # This encounters a ValueError on the first split so should # return None h = Function.quotient(Function.constant(1), Function.identity()) self.assertTrue(is_bounded(h, Interval(-1,1), Interval(-5,5)) is None)
def test_simple(self): self.assertTrue(is_bounded(Function.identity(), Interval(0,1), Interval(0,1))) self.assertFalse(is_bounded(Function.identity(), Interval(0,2), Interval(0,1))) self.assertFalse(is_bounded(Function.identity(), Interval(-1,1), Interval(0,1))) self.assertFalse(is_bounded(Function.identity(), Interval(-1,2), Interval(0,1))) self.assertTrue(is_bounded(Function.constant(0.), Interval(3,4), Interval(0,1))) self.assertFalse(is_bounded(Function.constant(2.), Interval(3,4), Interval(0,1)))
def test_cubic_derivative(self): a = cubic_derivative_approximation( Function.power(Function.identity(), Function.constant(3)), 4, 10) self.assertAlmostEqual(a(0), 0) self.assertAlmostEqual(a(2), 8) self.assertAlmostEqual(a(5), 125) self.assertAlmostEqual(a(12), 1728) self.assertAlmostEqual(a(-3), -27)
def test_approx(self): f = Function.identity() for a in approximate(f, 0, 1): self.assertTrue(isinstance(a, Function)) self.assertTrue(isinstance(a, CubicSpline)) # We may someday generate approximations that don't go through # the endpoints, and remove these tests. Until then, they # help verify that the approximation formulas are correct. self.assertEqual(a(0.0), 0.0) self.assertEqual(a(1.0), 1.0) # (-x^2 - 1) ^ .5 bad = Function.power(Function.sum(Function.product( Function.constant(-1), Function.power(Function.identity(), Function.constant(2))), Function.constant(-1)), Function.constant(.5)) self.assertEqual(list(approximate(bad, 0, 1)), [])
def recursively_generate_cubics(f, left, right, depth = 5): for a in approximate(f, left, right): if is_bounded(Function.sum(f, Function.product( Function.constant(-1), a)), Interval(left, right), Interval(-.008, .008)): return [a] if depth == 0: return [] middle = (left + right) / 2 return recursively_generate_cubics(f, left, middle, depth-1) + \ recursively_generate_cubics(f, middle, right, depth-1)
def __init__(self, t0, t1, f0, c0, c1, f1): self.__t0 = t0 self.__t1 = t1 self.__f0 = f0 self.__c0 = c0 self.__c1 = c1 self.__f1 = f1 # The specific functional form will have consequences for the # efficiency of interval arithmetic (and thus, slicing). # t' = (t-t0)/(t1-t0) t = Function.product(Function.sum(Function.identity(), Function.constant(-t0)), Function.constant(1.0/(t1-t0))) omt = Function.sum(Function.constant(1), Function.product(Function.constant(-1), t)) def term(const, f): return Function.sum(Function.constant(const), Function.product(t, f)) a = -f0 + 3*c0 - 3*c1 + f1 b = 3*f0 - 6*c0 + 3*c1 c = -3*f0 + 3*c0 d = f0 WrappedFunction.__init__(self, term(d, term(c, term(b, Function.constant(a)))).weak_simplify())
def test_depth(self): f = Function.sum(Function.power(Function.identity(), Function.constant(2)), Function.identity()) # This requires splitting in half self.assertTrue(is_bounded(f, Interval(-1,0), Interval(-3/4,1/2), 1)) self.assertEqual(is_bounded(f, Interval(-1,0), Interval(-3/4,1/2), 0), None) # This requires 3 levels of recursion self.assertTrue(is_bounded(f, Interval(-1,0), Interval(-3/8,1/8), 3)) self.assertEqual(is_bounded(f, Interval(-1,0), Interval(-3/8,1/8), 2), None) # This requires 8 levels of recursion self.assertTrue(is_bounded(f, Interval(-1,0), Interval(-65/256,1/256), 8)) self.assertEqual(is_bounded(f, Interval(-1,0), Interval(-65/256,1/256), 7), None)
def term(const, f): return Function.sum(Function.constant(const), Function.product(t, f))