Skip to content

Commit 418693c

Browse files
committed
added adams-bashforth method of order 2, 3, 4, 5
1 parent 3fa1efd commit 418693c

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
"""
2+
Use the Adams-Bashforth methods to solve Ordinary Differential Equations.
3+
4+
5+
https://en.wikipedia.org/wiki/Linear_multistep_method
6+
Author : Ravi Kumar
7+
"""
8+
from collections.abc import Callable
9+
10+
import numpy as np
11+
12+
class AdamsBashforth:
13+
def __init__(self, func: Callable[[float, float], float], x_initials: list[float], y_initials: list[float], step_size: float, x_final: float):
14+
15+
if x_initials[-1] >= x_final:
16+
raise ValueError("The final value of x must be greater than the initial values of x.")
17+
18+
if step_size <= 0:
19+
raise ValueError("Step size must be positive.")
20+
21+
if not all(x1 - x0 == step_size for x0, x1 in zip(x_initials, x_initials[1:])):
22+
raise ValueError("x-values must be equally spaced according to step size.")
23+
24+
self.func = func
25+
self.x_initials = x_initials
26+
self.y_initials = y_initials
27+
self.step_size = step_size
28+
self.x_final = x_final
29+
30+
"""
31+
args:
32+
func: An ordinary differential equation (ODE) as function of x and y.
33+
x_initials: List containing initial required values of x.
34+
y_initials: List containing initial required values of y.
35+
step_size: The increment value of x.
36+
x_final: The final value of x.
37+
38+
Returns: Solution of y at each nodal point
39+
40+
>>> def f(x, y):
41+
... return x
42+
>>> y = AdamsBashforth(f, [0, 0.2], [0, 0], 0.2, 1).step_2()
43+
>>> y
44+
array([0. 0. 0.06 0.178 0.3654 0.63722])
45+
46+
>>> def f(x,y):
47+
... return x + y
48+
>>> y = AdamsBashforth(f, [0, 0.2, 0.4, 0.6], [0, 0, 0.04, 0.128], 0.2, 1).step_4()
49+
>>> y[-1]
50+
0.57710833
51+
52+
>>> def f(x, y):
53+
... return x + y
54+
>>> y = AdamsBashforth(f, [0, 0.2, 1], [0, 0, 0.04], 0.2, 1).step_3()
55+
Traceback (most recent call last):
56+
...
57+
ValueError: The final value of x must be greater than the all initial values of x.
58+
59+
>>> def f(x, y):
60+
... return x + y
61+
>>> y = AdamsBashforth(f, [0, 0.2, 0.3], [0, 0, 0.04], 0.2, 1).step_3()
62+
Traceback (most recent call last):
63+
...
64+
ValueError: x-values must be equally spaced according to step size.
65+
66+
>>> def f(x, y):
67+
... return x
68+
>>> y = AdamsBashforth(f, [0, 0.2, 0.4, 0.6, 0.8], [0, 0, 0.04, 0.128, 0.307], -0.2, 1).step_5()
69+
Traceback (most recent call last):
70+
...
71+
ValueError: Step size must be positive.
72+
73+
>>> def f(x, y):
74+
... return (x -y)/2
75+
>>> y = AdamsBashforth(f, [0, 0.2, 0.4], [0, 0, 0.04], 0.2, 1).step_2()
76+
Traceback (most recent call last):
77+
...
78+
ValueError: Insufficient nodal points values information.
79+
"""
80+
81+
def step_2(self) -> np.ndarray:
82+
if len(self.x_initials) != 2 or len(self.y_initials) != 2:
83+
raise ValueError("Insufficient nodal points values information.")
84+
85+
x_0, x_1 = self.x_initials[:2]
86+
y_0, y_1 = self.y_initials[:2]
87+
88+
n = int((self.x_final - x_1) / self.step_size)
89+
y = np.zeros(n + 2)
90+
y[0] = y_0
91+
y[1] = y_1
92+
93+
for i in range(n):
94+
y[i+2] = y[i+1] + (self.step_size/2)*(3*self.func(x_1, y[i+1]) - self.func(x_0, y[i]))
95+
x_0 = x_1
96+
x_1 = x_1 + self.step_size
97+
98+
return y
99+
100+
def step_3(self) -> np.ndarray:
101+
if len(self.x_initials) != 3 or len(self.y_initials) != 3:
102+
raise ValueError("Insufficient nodal points information.")
103+
104+
x_0, x_1, x_2 = self.x_initials[:3]
105+
y_0, y_1, y_2 = self.y_initials[:3]
106+
107+
n = int((self.x_final - x_2) / self.step_size)
108+
y = np.zeros(n + 4)
109+
y[0] = y_0
110+
y[1] = y_1
111+
y[2] = y_2
112+
113+
for i in range(n+1):
114+
y[i+3] = y[i+2] + (self.step_size/12)*(23*self.func(x_2 , y[i+2]) -16*self.func(x_1, y[i+1]) + 5*self.func(x_0, y[i]))
115+
x_0 = x_1
116+
x_1 = x_2
117+
x_2 = x_2 + self.step_size
118+
119+
return y
120+
121+
def step_4(self) -> np.ndarray:
122+
if len(self.x_initials) != 4 or len(self.y_initials) != 4:
123+
raise ValueError("Insufficient nodal points information.")
124+
125+
x_0, x_1, x_2, x_3 = self.x_initials[:4]
126+
y_0, y_1, y_2, y_3 = self.y_initials[:4]
127+
128+
n = int((self.x_final - x_3) / self.step_size)
129+
y = np.zeros(n + 4)
130+
y[0] = y_0
131+
y[1] = y_1
132+
y[2] = y_2
133+
y[3] = y_3
134+
135+
for i in range(n):
136+
y[i+4] = y[i+3] + (self.step_size/24) * (55 * self.func(x_3, y[i+3]) - 59 * self.func(x_2 , y[i+2]) + 37*self.func(x_1, y[i+1]) - 9*self.func(x_0, y[i]))
137+
x_0 = x_1
138+
x_1 = x_2
139+
x_2 = x_3
140+
x_3 = x_3 + self.step_size
141+
142+
return y
143+
144+
def step_5(self) -> np.ndarray:
145+
if len(self.x_initials) != 5 or len(self.y_initials) != 5:
146+
raise ValueError("Insufficient nodal points information.")
147+
148+
x_0, x_1, x_2, x_3, x_4 = self.x_initials[:5]
149+
y_0, y_1, y_2, y_3, y_4 = self.y_initials[:5]
150+
151+
n = int((self.x_final - x_4) / self.step_size)
152+
y = np.zeros(n + 6)
153+
y[0] = y_0
154+
y[1] = y_1
155+
y[2] = y_2
156+
y[3] = y_3
157+
y[4] = y_4
158+
159+
for i in range(n+1):
160+
y[i+5] = y[i+4] + (self.step_size/720) * (1901 * self.func(x_4, y[i+4]) - 2774 * self.func(x_3, y[i+3]) - 2616 * self.func(x_2 , y[i+2]) - 1274 *self.func(x_1, y[i+1]) + 251*self.func(x_0, y[i]))
161+
x_0 = x_1
162+
x_1 = x_2
163+
x_2 = x_3
164+
x_3 = x_4
165+
x_4 = x_4 + self.step_size
166+
167+
return y
168+
169+
if __name__ == "__main__":
170+
import doctest
171+
172+
doctest.testmod()

0 commit comments

Comments
 (0)