-
Notifications
You must be signed in to change notification settings - Fork 0
/
MultipointSolver.py
93 lines (79 loc) · 4.66 KB
/
MultipointSolver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# TODO: Use the Multipoint Iteration Method for finding out the real roots of a function f.
# Note: The Multipoint Iteration Method has a third order of convergence (3)
# Note: |The Multipoint Iteration Method is used in cases where f'' does not exist but we would still like to have a third order of convergence
# The Multipoint Iteration Method can be proved in the following way:
# Using Taylor's expansion, f(x) = f(x_0) + (x - x_0).f'(x_0) + (x - x_0)^2.f''(x_0) + O(x), where O(x) stands for higher order functions which can be ignored for the purpose of this proof
# x - x_0 = -f(x_0) / (f'(x_0) + (1/2).(x - x_0).f''(x_0)) -(0)
# Expanding f'(x) about x_0 using Taylor's series about x_0,
# f'(x) = f'(x_0) + (x - x_0).f''(x_0) + Z(x), where Z(x) stands for higher order polynomials which can be ignored for the purpose of this proof
# f'(x_0 + (1/2).(x* - x_0)) = f'(x_0) + (1/2).(x* - x_0).f''(x_0) which is the denominator of (0)
# Therefore, x_new = x_0 - f(x_0)/f'(x_0 + (1/2).(x_new - x_0)), where x_new - x_0 can be approximated using the Newton-Rhapson Method
# Note: Stop iteration in one of two situations:
# |f(x)| < epsilon
# |x(k + 1) - x(k)| < epsilon
# Note: Set manual approximations while calculating roots of f using init_approx
# Note: Instead of using manual approximations, set approximations of f using Bisection Method through self.set_approximations(*args, **kwargs)
from typing import Callable, Tuple, List, Sequence, Dict
from NumericalMethodSolver import NumericalMethodSolver
import random
class MultipointSolver(NumericalMethodSolver):
def __init__(self, func : Callable, der_func : Callable, intervals : Sequence = None, epsilon : float = 1e-5) -> None:
super(MultipointSolver, self).__init__(func, intervals, epsilon)
self.der_func = der_func
def set_roots_1(self, init_approx : float = None) -> None:
if init_approx == None:
assert not self.approximations == None, "Approximations need to be set before calculating roots by calling self.set_approximations(*args, **kwargs)"
self.roots = []
for approximation in self.approximations.keys():
x_0 = approximation
f_x_k = self.func(x_0)
while(abs(f_x_k) >= self.epsilon):
x_new = x_0 - f_x_k / self.der_func(x_0 - 0.5 * (f_x_k / self.der_func(x_0)))
x_0 = x_new
f_x_k = self.func(x_new)
self.roots.append(x_0)
else:
self.roots = []
if isinstance(init_approx, float) or isinstance(init_approx, int):
init_approx = [init_approx]
for i in range(len(init_approx)):
x_0 = init_approx[i]
f_x_k = self.func(x_0)
while(abs(f_x_k) >= self.epsilon):
x_new = x_0 - f_x_k / self.der_func(x_0 + 0.5 * (-f_x_k / self.der_func(x_0)))
x_0 = x_new
f_x_k = self.func(x_new)
self.roots.append(x_0)
def set_roots_2(self, init_approx : float = None) -> None:
if init_approx == None:
assert not self.approximations == None, "Approximations need to be set before calculating roots by calling self.set_approximations(*args, **kwargs)"
self.roots = []
for approximation in self.approximations.keys():
x_0 = approximation
f_x_k = self.func(x_0)
while(abs(f_x_k) >= self.epsilon):
der_f_x_k = self.der_func(x_0)
x_inter = f_x_k / der_f_x_k
x_new = x_0 - x_inter - self.func(x_0 - x_inter) / der_f_x_k
x_0 = x_new
f_x_k = self.func(x_new)
self.roots.append(x_0)
else:
self.roots = []
if isinstance(init_approx, float) or isinstance(init_approx, int):
init_approx = [init_approx]
for i in range(len(init_approx)):
x_0 = init_approx[i]
f_x_k = self.func(x_0)
while(abs(f_x_k) >= self.epsilon):
der_f_x_k = self.der_func(x_0)
x_inter = f_x_k / der_f_x_k
x_new = x_0 - x_inter - self.func(x_0 - x_inter) / der_f_x_k
x_0 = x_new
f_x_k = self.func(x_new)
self.roots.append(x_0)
def set_roots(self, init_approx : float = None, use_method : int = 0) -> None:
if use_method:
self.set_roots_2(init_approx)
else:
self.set_roots_1(init_approx)