-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnewtons_method.py
More file actions
198 lines (162 loc) · 5.94 KB
/
newtons_method.py
File metadata and controls
198 lines (162 loc) · 5.94 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
"""
Add description dude...
The axes could use some love
"""
from uplog import log
import sys
import numpy as np
import matplotlib.pyplot as plt
import pylab
from tqdm import tqdm
import math
def newtons_method(f, df, zi, e, max_iteration=1000):
"""
Find a root using Newtown's method
:param f: function to search for root on
:param df: derivative of above
:param x0: initial guess
:param e: epsilon to stop iteration at
:return:
"""
# Initialize the iteration
delta = abs(0 - f(zi))
iteration = 0
while (delta > e) and (iteration < max_iteration):
iteration += 1
zi = zi - f(zi)/df(zi)
delta = abs(0 - f(zi))
return zi, iteration
def make_polynomial(coefficients, powers):
"""
Returns the value of a polynomial "c1*x^p1 + c2*x^p2 +..." at x
:param coefficients: c1, c2, ...
:param powers: p1, p2, ...
:param x: where to evaluate
:return: value of polynomial at x
"""
if len(coefficients) != len(powers):
log.out.error("Length of coefficient array must equal length of power array")
raise ValueError
def polynomial(x):
value = 0
for i in range(len(coefficients)):
value += coefficients[i] * x**powers[i]
return value
return polynomial
def make_derivative_polynomial(coefficients, powers):
"""
Returns the derivative of a polynomial "c1*x^p1 + c2*x^p2 +..." at x
:param coefficients: c1, c2, ...
:param powers: p1, p2, ...
:param x: where to evaluate
:return: derivative of polynomial at x
"""
if len(coefficients) != len(powers):
log.out.error("Length of coefficient array must equal length of power array")
raise ValueError
def derivative(x):
value = 0
for i in range(len(coefficients)):
if powers[i] != 0:
value += (coefficients[i] * powers[i]) * x**(powers[i]-1)
return value
return derivative
def test_method():
# Define z^4 -1
coefs = [1.0, -1.0]
pows = [4, 0]
f = make_polynomial(coefs, pows)
df = make_derivative_polynomial(coefs, pows)
# Make initial guesses and find roots
guesses = [0.1+0.0j, -2.2-1.0j, -0.4+1.0j, -1.1-2.0j]
for guess in guesses:
root, iterations = newtons_method(f, df, guess, 1e-5)
log.out.info("Guess | Result | Iterations: " + str(guess) + " | " +
str(root) + " | " + str(iterations))
def calc_basins(f, df, x_axis, y_axis):
# Define test space
xmat = np.repeat(x_axis[:,np.newaxis], len(y_axis), 1)
ymat = np.repeat(y_axis[:,np.newaxis], len(x_axis), 1).transpose()
z = xmat + (ymat * 1j)
root_map = np.zeros([len(x_axis), len(y_axis)], np.complex64)
iteration_map = np.zeros([len(x_axis), len(y_axis)])
# Loop through map and save solution
for i in tqdm(range(len(x_axis))):
for j in range(len(y_axis)):
root, iterations = newtons_method(f, df, z[i, j],
1e-5, max_iteration=1000)
root_map[i, j] = root
iteration_map[i, j] = iterations
return root_map, iteration_map
def plot_basins(xmin, xmax, ymin, ymax, xres, yres,
f=None, df=None):
# Define test space
x_axis = np.arange(xmin, xmax, xres, dtype=np.float32)
y_axis = np.arange(ymin, ymax, yres, dtype=np.float32)
# Get the estimated basins
if (f is None) or (df is None):
# Define z^4 -1 as a default function
coefs = [1.0, -1.0]
pows = [4, 0]
f = make_polynomial(coefs, pows)
df = make_derivative_polynomial(coefs, pows)
basin_map, iter_map = calc_basins(f, df, x_axis, y_axis)
# Break it up for visualization
basin_map_real = np.zeros([len(x_axis), len(y_axis)])
basin_map_imag = np.zeros([len(x_axis), len(y_axis)])
for i in range(len(x_axis)):
for j in range(len(y_axis)):
basin_map_real[i, j] = basin_map[i, j].real
basin_map_imag[i, j] = basin_map[i, j].imag
# Normalize for coloring
# Shift to positive
outliers = 1.0
basin_map_real[basin_map_real > outliers] = outliers
basin_map_real[basin_map_real < -outliers] = -outliers
basin_map_imag[basin_map_imag > outliers] = outliers
basin_map_imag[basin_map_imag < -outliers] = -outliers
basin_map_real = np.nan_to_num(basin_map_real)
basin_map_imag = np.nan_to_num(basin_map_imag)
max_real_val = 1.0
min_real_val = np.min(basin_map_real)
basin_map_real = basin_map_real + abs(min_real_val)
max_imag_val = 1.0
min_imag_val = np.min(basin_map_imag)
basin_map_imag = basin_map_imag + abs(min_imag_val)
basin_map_imag = 2.0 * max_real_val * (basin_map_imag + max_imag_val)
basin_colors = basin_map_real + basin_map_imag
# pylab.matshow(basin_colors.transpose(), cmap=plt.cm.hot)
pylab.matshow(basin_colors.transpose(), cmap=plt.get_cmap('viridis'))
# pylab.matshow(basin_colors.transpose(), cmap=plt.get_cmap('pink'))
pylab.draw()
def d_arctan():
def func(x):
return (1.0 / (1.0 + x**2))
return func
#########################
# DRIVER #
#########################
if __name__ == '__main__':
log.setLevel('INFO') # Set the logging level
# Report basic system info
log.out.info("Python version: " + sys.version)
# test_method()
# Define z^4 -1 as a the function
coefs = [1.0, -1.0]
pows = [4, 0]
f = make_polynomial(coefs, pows)
df = make_derivative_polynomial(coefs, pows)
# plot_basins(-1.5, 1.5, -1.5, 1.5, 0.05, 0.05,
# f=f, df=df)
# plot_basins(-1.5, 1.5, -1.5, 1.5, 0.01, 0.01,
# f=f, df=df)
plot_basins(1.0, 1.35, 1.0, 1.35, 0.001, 0.001,
f=f, df=df)
# f = np.sin
# df = np.cos
# plot_basins(1.15, 1.4, -0.2, 0.2, 0.003, 0.003, f=f, df=df)
# f = math.atan
# df = d_arctan()
# plot_basins(-2.0, 2.0, -2.0, 2.0, 0.01, 0.01, f=f, df=df)
# Stop Logging
log.stopLog()