def Nystrom(k): r""" Construct the k-step explicit Nystrom linear multistep method. The methods are explicit and have order k. They have the form: `y_{n+1} = y_{n-1} + h \sum_{j=0}^{k-1} \beta_j f(y_n-k+j+1)` They are generated using equations (1.13) and (1.7) from [hairer1993]_ III.1, along with the binomial expansion and the relation in exercise 4 on p. 367. Note that the term "Nystrom method" is also commonly used to refer to a class of methods for second-order ODEs; those are NOT the methods generated by this function. **Examples**:: >>> import nodepy.linear_multistep_method as lm >>> nys3=lm.Nystrom(6) >>> nys3.order() 6 References: #. [hairer1993]_ """ import sympy from sympy import Rational one = Rational(1, 1) alpha = snp.zeros(k + 1) alpha[k] = one alpha[k - 2] = -one beta = snp.zeros(k + 1) kappa = snp.zeros(k) gamma = snp.zeros(k) gamma[0] = one kappa[0] = 2 * one beta[k - 1] = 2 * one betaj = snp.zeros(k + 1) for j in range(1, k): gamma[j] = one - sum(gamma[:j] / snp.arange(j + 1, 1, -1)) kappa[j] = 2 * gamma[j] - gamma[j - 1] for i in range(0, j + 1): betaj[k - i - 1] = (-one)**i * sympy.combinatorial.factorials.binomial( j, i) * kappa[j] beta = beta + betaj name = str(k) + '-step Nystrom' return LinearMultistepMethod(alpha, beta, name=name, shortname='Nys' + str(k))
def Milne_Simpson(k): r""" Construct the k-step, Milne-Simpson method. The methods are implicit and (for k>=3) have order k+1. They have the form: `y_{n+1} = y_{n-1} + h \sum_{j=0}^{k} \beta_j f(y_n-k+j+1)` They are generated using equation (1.15), the equation in Exercise 3, and the relation in exercise 4, all from Hairer & Wanner III.1, along with the binomial expansion. **Examples**:: >>> import nodepy.linear_multistep_method as lm >>> ms3=lm.Milne_Simpson(3) >>> ms3.order() 4 References: [hairer1993]_ """ import sympy alpha = snp.zeros(k + 1) beta = snp.zeros(k + 1) alpha[k] = 1 alpha[k - 2] = -1 gamma = snp.zeros(k + 1) kappa = snp.zeros(k + 1) gamma[0] = 1 kappa[0] = 2 beta[k] = 2 betaj = snp.zeros(k + 1) for j in range(1, k + 1): gamma[j] = -sum(gamma[:j] / snp.arange(j + 1, 1, -1)) kappa[j] = 2 * gamma[j] - gamma[j - 1] for i in range(0, j + 1): betaj[k - i] = (-1)**i * sympy.combinatorial.factorials.binomial( j, i) * kappa[j] beta = beta + betaj name = str(k) + '-step Milne-Simpson' return LinearMultistepMethod(alpha, beta, name=name, shortname='MS' + str(k))
def Adams_Bashforth(k): r""" Construct the k-step, Adams-Bashforth method. The methods are explicit and have order k. They have the form: `y_{n+1} = y_n + h \sum_{j=0}^{k-1} \beta_j f(y_n-k+j+1)` They are generated using equations (1.5) and (1.7) from [hairer1993]_ III.1, along with the binomial expansion. **Examples**:: >>> import nodepy.linear_multistep_method as lm >>> ab3=lm.Adams_Bashforth(3) >>> ab3.order() 3 References: #. [hairer1993]_ """ import sympy from sympy import Rational one = Rational(1, 1) alpha = snp.zeros(k + 1) beta = snp.zeros(k + 1) alpha[k] = one alpha[k - 1] = -one gamma = snp.zeros(k) gamma[0] = one beta[k - 1] = one betaj = snp.zeros(k + 1) for j in range(1, k): gamma[j] = one - sum(gamma[:j] / snp.arange(j + 1, 1, -1)) for i in range(0, j + 1): betaj[k - i - 1] = (-one)**i * sympy.combinatorial.factorials.binomial( j, i) * gamma[j] beta = beta + betaj name = str(k) + '-step Adams-Bashforth' return LinearMultistepMethod(alpha, beta, name=name, shortname='AB' + str(k))
def Adams_Moulton(k): r""" Construct the k-step, Adams-Moulton method. The methods are implicit and have order k+1. They have the form: `y_{n+1} = y_n + h \sum_{j=0}^{k} \beta_j f(y_n-k+j+1)` They are generated using equation (1.9) and the equation in Exercise 3 from Hairer & Wanner III.1, along with the binomial expansion. **Examples**:: >>> import nodepy.linear_multistep_method as lm >>> am3=lm.Adams_Moulton(3) >>> am3.order() 4 References: [hairer1993]_ """ import sympy alpha = snp.zeros(k + 1) beta = snp.zeros(k + 1) alpha[k] = 1 alpha[k - 1] = -1 gamma = snp.zeros(k + 1) gamma[0] = 1 beta[k] = 1 betaj = snp.zeros(k + 1) for j in range(1, k + 1): gamma[j] = -sum(gamma[:j] / snp.arange(j + 1, 1, -1)) for i in range(0, j + 1): betaj[k - i] = (-1)**i * sympy.combinatorial.factorials.binomial( j, i) * gamma[j] beta = beta + betaj name = str(k) + '-step Adams-Moulton' return LinearMultistepMethod(alpha, beta, name=name, shortname='AM' + str(k))
def elm_ssp2(k): r""" Returns the optimal SSP k-step linear multistep method of order 2. **Examples**:: >>> import nodepy.linear_multistep_method as lm >>> lm10=lm.elm_ssp2(10) >>> lm10.ssp_coefficient() 8/9 """ import sympy alpha = snp.zeros(k + 1) beta = snp.zeros(k + 1) alpha[-1] = sympy.Rational(1, 1) alpha[0] = sympy.Rational(-1, (k - 1)**2) alpha[k - 1] = sympy.Rational(-(k - 1)**2 + 1, (k - 1)**2) beta[k - 1] = sympy.Rational(k, k - 1) name = 'Optimal ' + str(k) + '-step, 2nd order SSP method.' return LinearMultistepMethod(alpha, beta, name=name)
def sand_cc(s): r""" Construct Sand's circle-contractive method of order `p=2(s+1)` that uses `2^s + 1` steps. **Examples**:: >>> import nodepy.linear_multistep_method as lm >>> cc4 = lm.sand_cc(4) >>> cc4.order() 10 >>> cc4.ssp_coefficient() 1/8 **References**: #. [sand1986]_ """ import sympy one = sympy.Rational(1) zero = sympy.Rational(0) k = 2**s + 1 Jn = [k, k - 1] for i in range(1, s + 1): Jn.append(k - 1 - 2**i) alpha = snp.zeros(k + 1) beta = snp.zeros(k + 1) # This is inefficient for j in Jn: tau_product = one tau_sum = zero tau = [one / (j - i) for i in Jn if i != j] tau_product = np.prod(tau) tau_sum = np.sum(tau) beta[j] = tau_product**2 alpha[j] = 2 * beta[j] * tau_sum return LinearMultistepMethod(alpha, beta, 'Sand circle-contractive')
def backward_difference_formula(k): r""" Construct the k-step backward differentiation method. The methods are implicit and have order k. They have the form: `\sum_{j=0}^{k} \alpha_j y_{n+k-j+1} = h \beta_j f(y_{n+1})` They are generated using equation (1.22') from Hairer & Wanner III.1, along with the binomial expansion. **Examples**:: >>> import nodepy.linear_multistep_method as lm >>> bdf4=lm.backward_difference_formula(4) >>> bdf4.A_alpha_stability() 73 **References**: #.[hairer1993]_ pp. 364-365 """ import sympy alpha = snp.zeros(k + 1) beta = snp.zeros(k + 1) beta[k] = 1 gamma = snp.zeros(k + 1) gamma[0] = 1 alphaj = snp.zeros(k + 1) for j in range(1, k + 1): gamma[j] = sympy.Rational(1, j) for i in range(0, j + 1): alphaj[k - i] = (-1)**i * sympy.combinatorial.factorials.binomial( j, i) * gamma[j] alpha = alpha + alphaj name = str(k) + '-step BDF' return LinearMultistepMethod(alpha, beta, name=name, shortname='BDF' + str(k))
def stability_matrix(self, z): r""" Constructs the stability matrix of a two-step Runge-Kutta method. Right now just for a specific value of z. We ought to use Sage to do it symbolically. **Output**: M -- stability matrix evaluated at z WARNING: This only works for Type I & Type II methods right now!!! """ s = self.Ahat.shape[1] if self.type == 'General': # J Y^n = K Y^{n-1} K1 = np.column_stack((z * self.Ahat, self.d, 1 - self.d)) K2 = snp.zeros(s + 2) K2[-1] = 1 K3 = np.concatenate( (z * self.bhat, np.array((self.theta, 1 - self.theta)))) K = np.vstack((K1, K2, K3)) J = snp.eye(s + 2) J[:s, :s] = J[:s, :s] - z * self.A J[-1, :s] = z * self.b M = snp.solve(J.astype('complex64'), K.astype('complex64')) #M = snp.solve(J, K) # This version is slower else: D = np.hstack([1. - self.d, self.d]) thet = np.hstack([1. - self.theta, self.theta]) A, b = self.A, self.b if self.type == 'Type II': ahat = np.zeros([self.s, 1]) ahat[:, 0] = self.Ahat[:, 0] bh = np.zeros([1, 1]) bh[0, 0] = self.bhat[0] A = np.hstack([ahat, self.A]) A = np.vstack([np.zeros([1, self.s + 1]), A]) b = np.vstack([bh, self.b]) M1 = np.linalg.solve(np.eye(self.s) - z * self.A, D) L1 = thet + z * np.dot(self.b.T, M1) M = np.vstack([L1, [1., 0.]]) return M