- Student: Matvey Abramov
- Variant: 3
- Programming language: Python 3
- GUI library: TKinter + matplotlib
- Additional libraries: Sympy (calculating exact solution); numba, numpy (fast calculations in python)
cos(x) = 0 ---> x = PI*n - PI/2; n is Integer Number
On large range(For example n from 10 to 200) there are a lot of spontaneous picks. The largest errors are in improved euler method, then runge-kutta method with smaller picks, and with almost no pick goes euler method.
I used MVC (Model-View-Controller) design pattern to organize my program.
- Model class - aggregates different methods (Exact solution and Butcher Schema methods) and manages them.
- View class - interacts directly with matplotlib and tkinter, draws everything to screen.
- Controller class - aggregates Model and View and manages them. On actions, gets new points from Model instance and updates graphics by calling View instance.
- Exact solution method - implemented using library sympy. Parts of code for calculating:
# Solve ODE
def calc_solved_func(self):
x, y, c = self._symbols.x, self._symbols.y, self._symbols.const
if self._eq != parse_expr("1/cos(x) - y*tg(x)", local_dict={"x": x, "y": y, "tg": tan_sympy}):
dydx = Derivative(y, x)
solved = ode.dsolve(Eq(dydx, self._eq), y)
return solved
return parse_expr("-y(x) + C1*cos(x) + sin(x)")# Solve IVP ODE
def solve_ivp(self, x0, y0, _):
x, y, c = self._symbols.x, self._symbols.y, self._symbols.const
f = self.solved_func
if c in f.free_symbols:
f_for_const = f.subs([(x, x0), (self._symbols.y_without_params(x0), y0)])
constant = {c: solveset(f_for_const, c, domain=S.Reals).args[0]}
solved_ivp, = solve(f.subs(constant), y)
else:
solved_ivp = solve(f, [y])
return lambdify([x], solved_ivp)- Butcher Schema Method - General method for all methods, using butcher schema (euler method, improved euler method, runge-kutta method)
# Generate next y
def calc(h_coefs, yk_coefs, k_coefs, x, y, h, f):
shape = k_coefs.shape[0]
k = np.zeros((shape,), dtype=np.float64)
for i in range(shape):
ck = np.sum(np.multiply(k[:i], yk_coefs[i][:i]))
k[i] = h * f(x + h_coefs[i] * h, y + ck)
return y + np.sum(np.multiply(k, k_coefs))# Initialize butcher methods
def load_butcher_schemas() -> Iterable[ButcherSchema]:
euler_method = ButcherSchema(
np.array([0], dtype=np.float64),
np.array([[0]], dtype=np.float64),
np.array([1], dtype=np.float64),
"Euler method"
)
improved_euler_method = ButcherSchema(
np.array([0, 1], dtype=np.float64),
np.array([[0, 0], [1, 0]], dtype=np.float64),
np.array([0.5, 0.5], dtype=np.float64),
"Improved euler method"
)
runge_kutta_method = ButcherSchema(
np.array([0, 0.5, 0.5, 1], dtype=np.float64),
np.array([[0, 0, 0, 0],
[0.5, 0, 0, 0],
[0, 0.5, 0, 0],
[0, 0, 1, 0]], dtype=np.float64),
np.array([1 / 6, 2 / 6, 2 / 6, 1 / 6], dtype=np.float64),
"Runge-Kutta method"
)
return euler_method, improved_euler_method, runge_kutta_methoddef calc_error(self, right_y, y):
return [abs(y_exact - y) for y_exact, y in zip(right_y, y)]


