def bdf2_imr_ptinfos(pt): # assert pt == sRat(1,2) return [ PTInfo(pt.time, None, "imr"), PTInfo(pt.time + sRat(1, 2) + 1, "corr_val", None), PTInfo(pt.time + sRat(1, 2) + 2, "corr_val", None) ]
def test_sum_dts(): # Check a simple fractional case utils.assert_sym_eq(sum_dts(sRat(1, 2), 1), Sdts[0]/2) # Check two numbers the same gives zero always utils.assert_sym_eq(sum_dts(1, 1), 0) utils.assert_sym_eq(sum_dts(0, 0), 0) utils.assert_sym_eq(sum_dts(sRat(1, 2), sRat(1, 2)), 0) def check_sum_dts(a, b): # Check we can swap the sign utils.assert_sym_eq(sum_dts(a, b), -sum_dts(b, a)) # Starting half a step earlier utils.assert_sym_eq(sum_dts(a, b + sRat(1, 2)), sRat(1, 2)*Sdts[int(b)] + sum_dts(a, b)) # Check that we can split it up utils.assert_sym_eq(sum_dts(a, b), sum_dts(a, b-1) + sum_dts(b-1, b)) # Make sure b>=1 or the last check will fail due to negative b. cases = [(0, 1), (5, 8), (sRat(1, 2), 3), (sRat(2, 2), 3), (sRat(3, 2), 3), (0, sRat(9, 7)), (sRat(3/4), sRat(9, 7)), ] for a, b in cases: yield check_sum_dts, a, b
def test_exact_predictors(): def check_exact_predictors(exact, predictor): p1_func, y_np1_p1_expr = \ generate_predictor_scheme(*predictor, symbolic=exact) # LTE for IMR: just natural lte: y_np1_imr_expr = y_np1_exact - imr_lte(Sdts[0], Sdddynph, SFddynph) # Generate another predictor p2_func, y_np1_p2_expr = \ generate_predictor_scheme([PTInfo(sRat(1, 2), None, "imr"), PTInfo(2, "corr_val", None), PTInfo(3, "corr_val", None)], "ibdf2") A = system2matrix([y_np1_p1_expr, y_np1_p2_expr, y_np1_imr_expr], [Sdddynph, SFddynph, y_np1_exact]) # Solve for dddy and Fddy: x = A.inv() dddy_symb = sum([ y_est * xi.factor() for xi, y_est in zip(x.row(0), [y_np1_p1, y_np1_p2, y_np1_imr]) ]) Fddy_symb = sum([ y_est * xi.factor() for xi, y_est in zip(x.row(1), [y_np1_p1, y_np1_p2, y_np1_imr]) ]) # Check we got the right matrix and the right formulae if predictor[1] == "use exact dddy": assert A.row(0) == sympy.Matrix([[1, 0, 0]]).row(0) utils.assert_sym_eq(dddy_symb.simplify(), y_np1_p1) elif predictor[1] == "use exact Fddy": assert A.row(0) == sympy.Matrix([[0, 1, 0]]).row(0) utils.assert_sym_eq(Fddy_symb.simplify(), y_np1_p1) else: assert False exacts = [ sympy.exp(St), sympy.sin(St), ] exact_predictors = [ ([PTInfo(0, None, None), PTInfo(sRat(1, 2), None, None)], "use exact dddy"), ([PTInfo(0, None, None), PTInfo(sRat(1, 2), None, None)], "use exact Fddy"), ] for exact in exacts: for p in exact_predictors: yield check_exact_predictors, exact, p
def test_exact_predictors(): def check_exact_predictors(exact, predictor): p1_func, y_np1_p1_expr = \ generate_predictor_scheme(*predictor, symbolic=exact) # LTE for IMR: just natural lte: y_np1_imr_expr = y_np1_exact - imr_lte(Sdts[0], Sdddynph, SFddynph) # Generate another predictor p2_func, y_np1_p2_expr = \ generate_predictor_scheme([PTInfo(sRat(1, 2), None, "imr"), PTInfo(2, "corr_val", None), PTInfo(3, "corr_val", None)], "ibdf2") A = system2matrix([y_np1_p1_expr, y_np1_p2_expr, y_np1_imr_expr], [Sdddynph, SFddynph, y_np1_exact]) # Solve for dddy and Fddy: x = A.inv() dddy_symb = sum([y_est * xi.factor() for xi, y_est in zip(x.row(0), [y_np1_p1, y_np1_p2, y_np1_imr])]) Fddy_symb = sum([y_est * xi.factor() for xi, y_est in zip(x.row(1), [y_np1_p1, y_np1_p2, y_np1_imr])]) # Check we got the right matrix and the right formulae if predictor[1] == "use exact dddy": assert A.row(0) == sympy.Matrix([[1, 0, 0]]).row(0) utils.assert_sym_eq(dddy_symb.simplify(), y_np1_p1) elif predictor[1] == "use exact Fddy": assert A.row(0) == sympy.Matrix([[0, 1, 0]]).row(0) utils.assert_sym_eq(Fddy_symb.simplify(), y_np1_p1) else: assert False exacts = [sympy.exp(St), sympy.sin(St), ] exact_predictors = [ ([PTInfo(0, None, None), PTInfo(sRat(1, 2), None, None)], "use exact dddy"), ([PTInfo(0, None, None), PTInfo(sRat(1, 2), None, None)], "use exact Fddy"), ] for exact in exacts: for p in exact_predictors: yield check_exact_predictors, exact, p
def check_sum_dts(a, b): # Check we can swap the sign utils.assert_sym_eq(sum_dts(a, b), -sum_dts(b, a)) # Starting half a step earlier utils.assert_sym_eq(sum_dts(a, b + sRat(1, 2)), sRat(1, 2) * Sdts[int(b)] + sum_dts(a, b)) # Check that we can split it up utils.assert_sym_eq(sum_dts(a, b), sum_dts(a, b - 1) + sum_dts(b - 1, b))
def check_sum_dts(a, b): # Check we can swap the sign utils.assert_sym_eq(sum_dts(a, b), -sum_dts(b, a)) # Starting half a step earlier utils.assert_sym_eq(sum_dts(a, b + sRat(1, 2)), sRat(1, 2)*Sdts[int(b)] + sum_dts(a, b)) # Check that we can split it up utils.assert_sym_eq(sum_dts(a, b), sum_dts(a, b-1) + sum_dts(b-1, b))
def test_t_at_time_point(): tests = [ 0, 1, sRat(1, 2), sRat(3, 2), 1 + sRat(1, 2), sRat(4, 5), ] ts = range(11) for pt in tests: utils.assert_almost_equal(t_at_time_point(ts, pt), 10 - pt)
def test_is_half_integer(): tests = [(0, False), (1239012424481273, False), (sRat(1, 2), True), (1.5, True), (sRat(5, 2), True), (2.0 + sRat(1, 2), True), (1.5 + 1e-15, False), # 1e-16 fails (gives true) (1.51, False), ] for t, result in tests: assert is_half_integer(t) == result
def test_is_half_integer(): tests = [ (0, False), (1239012424481273, False), (sRat(1, 2), True), (1.5, True), (sRat(5, 2), True), (2.0 + sRat(1, 2), True), (1.5 + 1e-15, False), # 1e-16 fails (gives true) (1.51, False), ] for t, result in tests: assert is_half_integer(t) == result
def rational_as_mixed(x): """Convert a rational number to (integer_part, remainder_as_fraction) """ assert is_rational(x) # Don't want to accidentally end up with floats # in here as very long rationals! x_int = int(x) return x_int, sRat(x - x_int)
def test_const_step_explicit(): # Get explicit BDF2 (implicit midpoint)'s dydt approximation G&S pg 715 a = sympy.solve(-y0 + y1 + (1 + Delta0/Delta1)*Delta0*Dy1 - (Delta0/Delta1)**2*(y1 - y2), Dy1) assert(len(a) == 1) IMR_bdf_form = a[0].subs({k: Delta0 for k in dts}) orders = [1, 2, 3] exacts = [(y0 - y1)/Delta0, IMR_bdf_form, #Hairer pg 364 (sRat(1, 3)*y0 + sRat(1, 2)*y1 - y2 + sRat(1, 6)*y3)/Delta0 ] for order, exact in zip(orders, exacts): yield check_const_step, order, exact, 1
def test_imr_predictor_equivalence(): # Check the we generate imr's lte if we plug in the right times and # approximations to ebdf2 (~explicit midpoint rule). ynp1_imr = y_np1_exact - imr_lte(Sdts[0], Sdddynph, SFddynph) _, ynp1_p1 = generate_predictor_scheme([ PTInfo(0, None, None), PTInfo(sRat(1, 2), "bdf2 imr", "imr"), PTInfo(1, "corr_val", None) ], "ebdf2") utils.assert_sym_eq(ynp1_imr, ynp1_p1) # Should be exactly the same with ebdf to calculate the y-est at the # midpoint (because the midpoint value is ignored). _, ynp1_p2 = generate_predictor_scheme([ PTInfo(0, None, None), PTInfo(sRat(1, 2), "bdf3 imr", "imr"), PTInfo(1, "corr_val", None) ], "ebdf2") utils.assert_sym_eq(ynp1_imr, ynp1_p2)
def test_imr_predictor_equivalence(): # Check the we generate imr's lte if we plug in the right times and # approximations to ebdf2 (~explicit midpoint rule). ynp1_imr = y_np1_exact - imr_lte(Sdts[0], Sdddynph, SFddynph) _, ynp1_p1 = generate_predictor_scheme([PTInfo(0, None, None), PTInfo( sRat(1, 2), "bdf2 imr", "imr"), PTInfo(1, "corr_val", None)], "ebdf2") utils.assert_sym_eq(ynp1_imr, ynp1_p1) # Should be exactly the same with ebdf to calculate the y-est at the # midpoint (because the midpoint value is ignored). _, ynp1_p2 = generate_predictor_scheme([PTInfo(0, None, None), PTInfo( sRat(1, 2), "bdf3 imr", "imr"), PTInfo(1, "corr_val", None)], "ebdf2") utils.assert_sym_eq(ynp1_imr, ynp1_p2)
def test_const_step_implicit(): """Check that the implicit methods are correct for fixed step size by comparison with Hairer et. al. 1991 pg 366. """ exacts = [(y0 - y1)/Delta0, (sRat(3, 2)*y0 - 2*y1 + sRat(1, 2)*y2)/Delta0, (sRat(11, 6)*y0 - 3*y1 + sRat(3, 2)*y2 - sRat(1, 3)*y3)/Delta0, (sRat(25, 12)*y0 - 4*y1 + 3*y2 - sRat(4, 3)*y3 + sRat(1, 4)*y4)/Delta0] orders = [1, 2, 3, 4] for order, exact in zip(orders, exacts): yield check_const_step, order, exact, 0
def test_sum_dts(): # Check a simple fractional case utils.assert_sym_eq(sum_dts(sRat(1, 2), 1), Sdts[0] / 2) # Check two numbers the same gives zero always utils.assert_sym_eq(sum_dts(1, 1), 0) utils.assert_sym_eq(sum_dts(0, 0), 0) utils.assert_sym_eq(sum_dts(sRat(1, 2), sRat(1, 2)), 0) def check_sum_dts(a, b): # Check we can swap the sign utils.assert_sym_eq(sum_dts(a, b), -sum_dts(b, a)) # Starting half a step earlier utils.assert_sym_eq(sum_dts(a, b + sRat(1, 2)), sRat(1, 2) * Sdts[int(b)] + sum_dts(a, b)) # Check that we can split it up utils.assert_sym_eq(sum_dts(a, b), sum_dts(a, b - 1) + sum_dts(b - 1, b)) # Make sure b>=1 or the last check will fail due to negative b. cases = [ (0, 1), (5, 8), (sRat(1, 2), 3), (sRat(2, 2), 3), (sRat(3, 2), 3), (0, sRat(9, 7)), (sRat(3 / 4), sRat(9, 7)), ] for a, b in cases: yield check_sum_dts, a, b
def ptinfo2dyfunc(pt, dydt_func): """Construct a python function to calculate the approximation to dy requested. If not using "exact" for dy_est the dydt_func can/should be None. """ # Use the dy estimate from implicit midpoint rule (obviously only works # at the midpoint). if pt.dy_est == "imr": if not is_half_integer(pt.time): raise ValueError( "imr dy approximation Only works for half integer " + "points but given the point " + str(pt.time)) # Need to drop some of the later time values if time point is # not the midpoint of 0 and 1 points (i.e. 1/2). Specifically, we # need: # 1/2 -> ts[:None] # 3/2 -> ts[:-1] # 5/2 -> ts[:-2] if pt.time == sRat(1, 2): # dy_func = ode.imr_dydt def dy_func(ts, ys): val = ode.imr_dydt(ts, ys) print "imr f(t) =", val return val else: x = -math.floor(pt.time) def dy_func(ts, ys): val = ode.imr_dydt(ts[:-int(math.floor(pt.time))], ys[:-int(math.floor(pt.time))]) print "imr f(t) =", val return val elif pt.dy_est == "fd4": def dy_func(ts, ys): coeffs = [-1 / 12, -2 / 3, 0, 2 / 3, -1 / 12] ys_used = ys[-6:-1] assert len(ys) >= 6 # ??ds only for constant step! with variable steps we can put # the 0 at a midpoint to reduce n-prev-steps needed. return sp.dot(coeffs, ys_used) # ??ds Use real dydt for exp elif pt.dy_est == "exp test": def dy_func(ts, ys): return ys[-(pt.time + 1)] # Use the provided f with known values to calculate dydt. Only for # integer time points (i.e. where we already know y etc.) for now. elif pt.dy_est == "exact": assert dydt_func is not None if is_integer(pt.time): dy_func = lambda ts, ys: dydt_func(ts[pt.time]) else: # Can't get y without additional approximations... def dy_func(ts, ys): val = dydt_func(t_at_time_point(ts, pt.time)) print "f(t) =", val return val # None = "this value at this point should not be used" elif pt.dy_est is None: dy_func = None else: raise ValueError("Unrecognised dy_est name in function construction " + str(pt.dy_est)) return dy_func
def ptinfo2dyfunc(pt, dydt_func): """Construct a python function to calculate the approximation to dy requested. If not using "exact" for dy_est the dydt_func can/should be None. """ # Use the dy estimate from implicit midpoint rule (obviously only works # at the midpoint). if pt.dy_est == "imr": if not is_half_integer(pt.time): raise ValueError("imr dy approximation Only works for half integer " + "points but given the point " + str(pt.time)) # Need to drop some of the later time values if time point is # not the midpoint of 0 and 1 points (i.e. 1/2). Specifically, we # need: # 1/2 -> ts[:None] # 3/2 -> ts[:-1] # 5/2 -> ts[:-2] if pt.time == sRat(1, 2): # dy_func = ode.imr_dydt def dy_func(ts, ys): val = ode.imr_dydt(ts, ys) print "imr f(t) =", val return val else: x = -math.floor(pt.time) def dy_func(ts, ys): val = ode.imr_dydt(ts[:-int(math.floor(pt.time))], ys[:-int(math.floor(pt.time))]) print "imr f(t) =", val return val elif pt.dy_est == "fd4": def dy_func(ts, ys): coeffs = [-1/12, -2/3, 0, 2/3, -1/12] ys_used = ys[-6:-1] assert len(ys) >= 6 # ??ds only for constant step! with variable steps we can put # the 0 at a midpoint to reduce n-prev-steps needed. return sp.dot(coeffs, ys_used) # ??ds Use real dydt for exp elif pt.dy_est == "exp test": def dy_func(ts, ys): return ys[-(pt.time+1)] # Use the provided f with known values to calculate dydt. Only for # integer time points (i.e. where we already know y etc.) for now. elif pt.dy_est == "exact": assert dydt_func is not None if is_integer(pt.time): dy_func = lambda ts, ys: dydt_func(ts[pt.time]) else: # Can't get y without additional approximations... def dy_func(ts, ys): val = dydt_func(t_at_time_point(ts, pt.time)) print "f(t) =", val return val # None = "this value at this point should not be used" elif pt.dy_est is None: dy_func = None else: raise ValueError( "Unrecognised dy_est name in function construction " + str(pt.dy_est)) return dy_func
def bdf3_imr_ptinfos(pt): # assert pt == sRat(1,2) return [PTInfo(pt.time, None, "imr"), PTInfo(pt.time + sRat(1, 2), "corr_val", None), PTInfo(pt.time + sRat(1, 2) + 1, "corr_val", None), PTInfo(pt.time + sRat(1, 2) + 2, "corr_val", None)]
def test_t_at_time_point(): tests = [0, 1, sRat(1, 2), sRat(3, 2), 1 + sRat(1, 2), sRat(4, 5), ] ts = range(11) for pt in tests: utils.assert_almost_equal(t_at_time_point(ts, pt), 10 - pt)
def is_half_integer(x): return x == (math.floor(x) + float(sRat(1, 2)))