| """Numerical Methods for Holonomic Functions""" | |
| from sympy.core.sympify import sympify | |
| from sympy.holonomic.holonomic import DMFsubs | |
| from mpmath import mp | |
| def _evalf(func, points, derivatives=False, method='RK4'): | |
| """ | |
| Numerical methods for numerical integration along a given set of | |
| points in the complex plane. | |
| """ | |
| ann = func.annihilator | |
| a = ann.order | |
| R = ann.parent.base | |
| K = R.get_field() | |
| if method == 'Euler': | |
| meth = _euler | |
| else: | |
| meth = _rk4 | |
| dmf = [K.new(j.to_list()) for j in ann.listofpoly] | |
| red = [-dmf[i] / dmf[a] for i in range(a)] | |
| y0 = func.y0 | |
| if len(y0) < a: | |
| raise TypeError("Not Enough Initial Conditions") | |
| x0 = func.x0 | |
| sol = [meth(red, x0, points[0], y0, a)] | |
| for i, j in enumerate(points[1:]): | |
| sol.append(meth(red, points[i], j, sol[-1], a)) | |
| if not derivatives: | |
| return [sympify(i[0]) for i in sol] | |
| else: | |
| return sympify(sol) | |
| def _euler(red, x0, x1, y0, a): | |
| """ | |
| Euler's method for numerical integration. | |
| From x0 to x1 with initial values given at x0 as vector y0. | |
| """ | |
| A = sympify(x0)._to_mpmath(mp.prec) | |
| B = sympify(x1)._to_mpmath(mp.prec) | |
| y_0 = [sympify(i)._to_mpmath(mp.prec) for i in y0] | |
| h = B - A | |
| f_0 = y_0[1:] | |
| f_0_n = 0 | |
| for i in range(a): | |
| f_0_n += sympify(DMFsubs(red[i], A, mpm=True))._to_mpmath(mp.prec) * y_0[i] | |
| f_0.append(f_0_n) | |
| return [y_0[i] + h * f_0[i] for i in range(a)] | |
| def _rk4(red, x0, x1, y0, a): | |
| """ | |
| Runge-Kutta 4th order numerical method. | |
| """ | |
| A = sympify(x0)._to_mpmath(mp.prec) | |
| B = sympify(x1)._to_mpmath(mp.prec) | |
| y_0 = [sympify(i)._to_mpmath(mp.prec) for i in y0] | |
| h = B - A | |
| f_0_n = 0 | |
| f_1_n = 0 | |
| f_2_n = 0 | |
| f_3_n = 0 | |
| f_0 = y_0[1:] | |
| for i in range(a): | |
| f_0_n += sympify(DMFsubs(red[i], A, mpm=True))._to_mpmath(mp.prec) * y_0[i] | |
| f_0.append(f_0_n) | |
| f_1 = [y_0[i] + f_0[i]*h/2 for i in range(1, a)] | |
| for i in range(a): | |
| f_1_n += sympify(DMFsubs(red[i], A + h/2, mpm=True))._to_mpmath(mp.prec) * (y_0[i] + f_0[i]*h/2) | |
| f_1.append(f_1_n) | |
| f_2 = [y_0[i] + f_1[i]*h/2 for i in range(1, a)] | |
| for i in range(a): | |
| f_2_n += sympify(DMFsubs(red[i], A + h/2, mpm=True))._to_mpmath(mp.prec) * (y_0[i] + f_1[i]*h/2) | |
| f_2.append(f_2_n) | |
| f_3 = [y_0[i] + f_2[i]*h for i in range(1, a)] | |
| for i in range(a): | |
| f_3_n += sympify(DMFsubs(red[i], A + h, mpm=True))._to_mpmath(mp.prec) * (y_0[i] + f_2[i]*h) | |
| f_3.append(f_3_n) | |
| return [y_0[i] + h*(f_0[i]+2*f_1[i]+2*f_2[i]+f_3[i])/6 for i in range(a)] | |