executable-engineering 0.1.40__tar.gz → 0.1.42__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/PKG-INFO +1 -1
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/__init__.py +9 -0
- executable_engineering-0.1.42/executable_engineering/functions_module.py +151 -0
- executable_engineering-0.1.42/executable_engineering/optimization_module.py +561 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering.egg-info/PKG-INFO +1 -1
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering.egg-info/SOURCES.txt +2 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/setup.py +1 -1
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/LICENSE +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/README.md +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/closedoptimization_module.py +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/closedrootfinder_module.py +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/gradientoptimization_module.py +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/linearsystems_module.py +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/neuralnet_module.py +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/numerical_error_module.py +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/openoptimization_module.py +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/openrootfinder_module.py +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering.egg-info/dependency_links.txt +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering.egg-info/requires.txt +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering.egg-info/top_level.txt +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/pyproject.toml +0 -0
- {executable_engineering-0.1.40 → executable_engineering-0.1.42}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: executable_engineering
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.42
|
|
4
4
|
Summary: Demonstrative tools for the Executable Engineering textbook
|
|
5
5
|
Home-page: https://github.com/themintlab/ExecutableEngineering/tree/main/executable_engineering
|
|
6
6
|
Author: Michael Welland
|
{executable_engineering-0.1.40 → executable_engineering-0.1.42}/executable_engineering/__init__.py
RENAMED
|
@@ -6,8 +6,17 @@ from .openoptimization_module import OptimizerOpen
|
|
|
6
6
|
from .gradientoptimization_module import OptimizerGrad
|
|
7
7
|
from .numerical_error_module import python_internal_binary, decimal_to_binary
|
|
8
8
|
from .linearsystems_module import visual_solve_2d, visualize_conditioning, visualize_matrix_norms, jacobi_step, gauss_seidel_step, sor_step, visualize_convergence_2d, create_diagonally_dominant_matrix, iter_solve, steepest_descent, conjugate_gradient, IterationTracker, discretise_poisson, spai
|
|
9
|
+
from .functions_module import weierstrass, plot_and_find_roots, complex_plot_with_shadow, plot_polynomial_roots_companion
|
|
10
|
+
from .optimization_module import visual_golden_section, visual_powell_2d, newton_method
|
|
9
11
|
|
|
10
12
|
__all__ = [
|
|
13
|
+
"newton_method",
|
|
14
|
+
"visual_powell_2d",
|
|
15
|
+
"visual_golden_section",
|
|
16
|
+
"plot_polynomial_roots_companion",
|
|
17
|
+
"plot_and_find_roots",
|
|
18
|
+
"complex_plot_with_shadow",
|
|
19
|
+
"weierstrass",
|
|
11
20
|
"discretise_poisson",
|
|
12
21
|
"spai",
|
|
13
22
|
"visual_solve_2d",
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def weierstrass(x, a=0.5, b=13, iterations=100):
|
|
4
|
+
"""
|
|
5
|
+
Computes the Weierstrass function.
|
|
6
|
+
|
|
7
|
+
Parameters:
|
|
8
|
+
x (array-like): The points at which to evaluate the function.
|
|
9
|
+
a (float): Parameter 0 < a < 1.
|
|
10
|
+
b (float): Parameter such that ab > 1 + 3pi/2.
|
|
11
|
+
iterations (int): Number of terms in the sum.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
y (ndarray): The function values.
|
|
15
|
+
"""
|
|
16
|
+
x = np.asarray(x)
|
|
17
|
+
y = np.zeros_like(x, dtype=float)
|
|
18
|
+
for n in range(iterations):
|
|
19
|
+
y += (a**n) * np.cos((b**n) * np.pi * x)
|
|
20
|
+
return y
|
|
21
|
+
|
|
22
|
+
import plotly.graph_objects as go
|
|
23
|
+
from scipy.optimize import root
|
|
24
|
+
|
|
25
|
+
def plot_and_find_roots(func):
|
|
26
|
+
x = np.linspace(-10, 10, 400)
|
|
27
|
+
y = func(x)
|
|
28
|
+
|
|
29
|
+
# Find roots
|
|
30
|
+
x0s = np.arange(-10, 10, 1)
|
|
31
|
+
roots = []
|
|
32
|
+
for x0 in x0s:
|
|
33
|
+
r = root(func, x0=x0)
|
|
34
|
+
if r.success:
|
|
35
|
+
roots.append((r.x[0], r.fun[0]))
|
|
36
|
+
|
|
37
|
+
# Remove duplicate roots (within tolerance)
|
|
38
|
+
unique_roots = []
|
|
39
|
+
tol = 1e-6
|
|
40
|
+
for rx, ry in roots:
|
|
41
|
+
if not any(abs(rx - ux) < tol for ux, _ in unique_roots):
|
|
42
|
+
unique_roots.append((rx, ry))
|
|
43
|
+
|
|
44
|
+
# Plot with Plotly
|
|
45
|
+
fig = go.Figure()
|
|
46
|
+
fig.add_trace(go.Scatter(x=x, y=y, mode='lines', name='f(x)'))
|
|
47
|
+
fig.add_hline(y=0, line_dash='dash', line_color='black', annotation_text='x-axis')
|
|
48
|
+
if unique_roots:
|
|
49
|
+
rx_vals = [rx for rx, _ in unique_roots]
|
|
50
|
+
fig.add_trace(go.Scatter(x=rx_vals, y=[0]*len(unique_roots), mode='markers', marker=dict(color='red', size=10), name='Roots'))
|
|
51
|
+
fig.update_layout(title='Plot of f(x) and its Roots', xaxis_title='x', yaxis_title='f(x)', xaxis_range=[-10, 10], yaxis_range=[-10, 10])
|
|
52
|
+
fig.show()
|
|
53
|
+
|
|
54
|
+
def complex_plot_with_shadow(func, title='Complex Plot of f(z)', roots=None, plot_range=(-3, 3)):
|
|
55
|
+
real_range = np.linspace(plot_range[0], plot_range[1], 100)
|
|
56
|
+
imag_range = np.linspace(plot_range[0], plot_range[1], 100)
|
|
57
|
+
abs_val = np.empty((len(real_range), len(imag_range)))
|
|
58
|
+
|
|
59
|
+
for i, real in enumerate(real_range):
|
|
60
|
+
for j, imag in enumerate(imag_range):
|
|
61
|
+
z = complex(real, imag)
|
|
62
|
+
result = func(z)
|
|
63
|
+
abs_val[i, j] = abs(result)
|
|
64
|
+
|
|
65
|
+
surface = go.Surface(
|
|
66
|
+
z=abs_val.T,
|
|
67
|
+
x=real_range,
|
|
68
|
+
y=imag_range,
|
|
69
|
+
colorscale='Viridis',
|
|
70
|
+
contours=dict(z=dict(show=True, usecolormap=True, highlightcolor="limegreen", project_z=True))
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
data = [surface]
|
|
74
|
+
|
|
75
|
+
if roots is not None:
|
|
76
|
+
root_x = [r.real for r in roots]
|
|
77
|
+
root_y = [r.imag for r in roots]
|
|
78
|
+
# Calculate z for roots (which is abs(func(r)) = 0)
|
|
79
|
+
root_z = [0 for r in roots]
|
|
80
|
+
|
|
81
|
+
data.append(go.Scatter3d(
|
|
82
|
+
x=root_x, y=root_y, z=root_z,
|
|
83
|
+
mode='markers',
|
|
84
|
+
marker=dict(color='red', size=5, symbol='circle'),
|
|
85
|
+
name='Roots'
|
|
86
|
+
))
|
|
87
|
+
|
|
88
|
+
fig = go.Figure(data=data)
|
|
89
|
+
fig.update_layout(
|
|
90
|
+
title=title,
|
|
91
|
+
scene=dict(xaxis_title='Real', yaxis_title='Imaginary', zaxis_title='|f(z)|')
|
|
92
|
+
)
|
|
93
|
+
fig.show()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
from scipy.linalg import companion
|
|
97
|
+
|
|
98
|
+
def plot_polynomial_roots_companion(n):
|
|
99
|
+
"""
|
|
100
|
+
Creates a random polynomial of order n, solves for its roots
|
|
101
|
+
using the companion matrix method, and plots the polynomial
|
|
102
|
+
along with the roots as a complex surface with a shadow.
|
|
103
|
+
"""
|
|
104
|
+
# Create random coefficients from -10 to 10
|
|
105
|
+
# A polynomial of order n has n+1 coefficients. Let's make the leading coefficient non-zero.
|
|
106
|
+
coeffs = np.random.uniform(-10, 10, n + 1)
|
|
107
|
+
|
|
108
|
+
# Make sure leading coefficient is not too small
|
|
109
|
+
if abs(coeffs[0]) < 1:
|
|
110
|
+
coeffs[0] = 1.0 * np.sign(coeffs[0]) if coeffs[0] != 0 else 1.0
|
|
111
|
+
|
|
112
|
+
poly_func = np.poly1d(coeffs)
|
|
113
|
+
|
|
114
|
+
print("Polynomial:")
|
|
115
|
+
print(poly_func)
|
|
116
|
+
|
|
117
|
+
# Solve for roots using companion matrix
|
|
118
|
+
comp_matrix = companion(coeffs)
|
|
119
|
+
|
|
120
|
+
print("\nCompanion Matrix:")
|
|
121
|
+
print(np.round(comp_matrix, 2))
|
|
122
|
+
|
|
123
|
+
roots = np.linalg.eigvals(comp_matrix)
|
|
124
|
+
|
|
125
|
+
print("\nRoots from eigenvalues of companion matrix:")
|
|
126
|
+
print(np.round(roots, 2))
|
|
127
|
+
|
|
128
|
+
# Determine plot range based on roots
|
|
129
|
+
max_abs_root = max(abs(roots)) if len(roots) > 0 else 1.0
|
|
130
|
+
plot_limit = max_abs_root * 1.5
|
|
131
|
+
|
|
132
|
+
# --- Real-space plot ---
|
|
133
|
+
x_real = np.linspace(-plot_limit, plot_limit, 400)
|
|
134
|
+
y_real = poly_func(x_real)
|
|
135
|
+
|
|
136
|
+
fig_real = go.Figure()
|
|
137
|
+
fig_real.add_trace(go.Scatter(x=x_real, y=y_real, mode='lines', name='f(x)'))
|
|
138
|
+
fig_real.add_hline(y=0, line_dash='dash', line_color='black', annotation_text='x-axis')
|
|
139
|
+
|
|
140
|
+
# Find real roots (imaginary part near 0)
|
|
141
|
+
real_roots = [r.real for r in roots if abs(r.imag) < 1e-6]
|
|
142
|
+
if real_roots:
|
|
143
|
+
fig_real.add_trace(go.Scatter(x=real_roots, y=[0]*len(real_roots), mode='markers', marker=dict(color='red', size=10), name='Real Roots'))
|
|
144
|
+
|
|
145
|
+
fig_real.update_layout(title=f"Real Space Plot: Roots of Order {n} Polynomial", xaxis_title='x', yaxis_title='f(x)')
|
|
146
|
+
fig_real.show()
|
|
147
|
+
|
|
148
|
+
# Plot using complex_plot_with_shadow
|
|
149
|
+
title = f"Complex Space Plot: Roots of Order {n} Polynomial"
|
|
150
|
+
complex_plot_with_shadow(poly_func, title=title, roots=roots, plot_range=(-plot_limit, plot_limit))
|
|
151
|
+
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import plotly.graph_objects as go
|
|
3
|
+
from scipy.optimize import minimize_scalar
|
|
4
|
+
from scipy.linalg import solve
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def visual_golden_section(f, x1, x3, tol=1e-5, max_iter=None):
|
|
8
|
+
|
|
9
|
+
phi = (1 + np.sqrt(5)) / 2
|
|
10
|
+
invphi = 1 / phi
|
|
11
|
+
|
|
12
|
+
states = []
|
|
13
|
+
all_evals = []
|
|
14
|
+
|
|
15
|
+
initial_width = abs(x3 - x1)
|
|
16
|
+
|
|
17
|
+
if initial_width <= tol:
|
|
18
|
+
required_iter = 0
|
|
19
|
+
else:
|
|
20
|
+
required_iter = int(
|
|
21
|
+
np.ceil(np.log(tol / initial_width) / np.log(invphi))
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if max_iter is not None:
|
|
25
|
+
required_iter = min(required_iter, max_iter)
|
|
26
|
+
|
|
27
|
+
curr_x1 = x1
|
|
28
|
+
curr_x3 = x3
|
|
29
|
+
|
|
30
|
+
curr_x2 = curr_x3 - invphi * (curr_x3 - curr_x1)
|
|
31
|
+
curr_x4 = curr_x1 + invphi * (curr_x3 - curr_x1)
|
|
32
|
+
|
|
33
|
+
f1 = f(curr_x1)
|
|
34
|
+
f3 = f(curr_x3)
|
|
35
|
+
f2 = f(curr_x2)
|
|
36
|
+
f4 = f(curr_x4)
|
|
37
|
+
|
|
38
|
+
all_evals.extend([
|
|
39
|
+
curr_x1,
|
|
40
|
+
curr_x2,
|
|
41
|
+
curr_x4,
|
|
42
|
+
curr_x3
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
states.append({
|
|
46
|
+
'iter': 0,
|
|
47
|
+
'active': [
|
|
48
|
+
(curr_x1, f1, 'x1'),
|
|
49
|
+
(curr_x2, f2, 'x2'),
|
|
50
|
+
(curr_x4, f4, 'x4'),
|
|
51
|
+
(curr_x3, f3, 'x3')
|
|
52
|
+
],
|
|
53
|
+
'history': list(dict.fromkeys(all_evals))
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
for i in range(1, required_iter + 1):
|
|
57
|
+
|
|
58
|
+
if abs(curr_x3 - curr_x1) <= tol:
|
|
59
|
+
break
|
|
60
|
+
|
|
61
|
+
if f4 < f2:
|
|
62
|
+
|
|
63
|
+
curr_x1, f1 = curr_x2, f2
|
|
64
|
+
curr_x2, f2 = curr_x4, f4
|
|
65
|
+
|
|
66
|
+
curr_x4 = curr_x1 + invphi * (curr_x3 - curr_x1)
|
|
67
|
+
f4 = f(curr_x4)
|
|
68
|
+
|
|
69
|
+
all_evals.append(curr_x4)
|
|
70
|
+
|
|
71
|
+
else:
|
|
72
|
+
|
|
73
|
+
curr_x3, f3 = curr_x4, f4
|
|
74
|
+
curr_x4, f4 = curr_x2, f2
|
|
75
|
+
|
|
76
|
+
curr_x2 = curr_x3 - invphi * (curr_x3 - curr_x1)
|
|
77
|
+
f2 = f(curr_x2)
|
|
78
|
+
|
|
79
|
+
all_evals.append(curr_x2)
|
|
80
|
+
|
|
81
|
+
states.append({
|
|
82
|
+
'iter': i,
|
|
83
|
+
'active': [
|
|
84
|
+
(curr_x1, f1, 'x1'),
|
|
85
|
+
(curr_x2, f2, 'x2'),
|
|
86
|
+
(curr_x4, f4, 'x4'),
|
|
87
|
+
(curr_x3, f3, 'x3')
|
|
88
|
+
],
|
|
89
|
+
'history': list(dict.fromkeys(all_evals))
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
x_min = min(all_evals)
|
|
93
|
+
x_max = max(all_evals)
|
|
94
|
+
|
|
95
|
+
margin = (x_max - x_min) * 0.15
|
|
96
|
+
|
|
97
|
+
if margin == 0:
|
|
98
|
+
margin = 1
|
|
99
|
+
|
|
100
|
+
x_vals = np.linspace(
|
|
101
|
+
x_min - margin,
|
|
102
|
+
x_max + margin,
|
|
103
|
+
400
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
y_vals = f(x_vals)
|
|
107
|
+
|
|
108
|
+
fig = go.Figure()
|
|
109
|
+
|
|
110
|
+
fig.add_trace(go.Scatter(
|
|
111
|
+
x=x_vals,
|
|
112
|
+
y=y_vals,
|
|
113
|
+
mode='lines',
|
|
114
|
+
name='f(x)',
|
|
115
|
+
line=dict(
|
|
116
|
+
color='lightgray',
|
|
117
|
+
width=2
|
|
118
|
+
),
|
|
119
|
+
hoverinfo='skip'
|
|
120
|
+
))
|
|
121
|
+
|
|
122
|
+
init_state = states[0]
|
|
123
|
+
|
|
124
|
+
ax = [pt[0] for pt in init_state['active']]
|
|
125
|
+
ay = [pt[1] for pt in init_state['active']]
|
|
126
|
+
alabels = [pt[2] for pt in init_state['active']]
|
|
127
|
+
|
|
128
|
+
hx = [
|
|
129
|
+
x for x in init_state['history']
|
|
130
|
+
if x not in ax
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
hy = [f(x) for x in hx]
|
|
134
|
+
|
|
135
|
+
fig.add_trace(go.Scatter(
|
|
136
|
+
x=hx,
|
|
137
|
+
y=hy,
|
|
138
|
+
mode='markers',
|
|
139
|
+
name='Past Evaluations',
|
|
140
|
+
marker=dict(
|
|
141
|
+
color='gray',
|
|
142
|
+
size=7,
|
|
143
|
+
opacity=0.5
|
|
144
|
+
)
|
|
145
|
+
))
|
|
146
|
+
|
|
147
|
+
fig.add_trace(go.Scatter(
|
|
148
|
+
x=ax,
|
|
149
|
+
y=ay,
|
|
150
|
+
mode='markers+text',
|
|
151
|
+
name='Active Bracket',
|
|
152
|
+
text=alabels,
|
|
153
|
+
textposition='top center',
|
|
154
|
+
textfont=dict(
|
|
155
|
+
color='black',
|
|
156
|
+
size=13
|
|
157
|
+
),
|
|
158
|
+
marker=dict(
|
|
159
|
+
color='crimson',
|
|
160
|
+
size=12,
|
|
161
|
+
symbol='circle',
|
|
162
|
+
line=dict(
|
|
163
|
+
color='black',
|
|
164
|
+
width=1
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
))
|
|
168
|
+
|
|
169
|
+
frames = []
|
|
170
|
+
|
|
171
|
+
for state in states:
|
|
172
|
+
|
|
173
|
+
cur_ax = [pt[0] for pt in state['active']]
|
|
174
|
+
cur_ay = [pt[1] for pt in state['active']]
|
|
175
|
+
cur_alabels = [pt[2] for pt in state['active']]
|
|
176
|
+
|
|
177
|
+
cur_hx = [
|
|
178
|
+
x for x in state['history']
|
|
179
|
+
if x not in cur_ax
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
cur_hy = [f(x) for x in cur_hx]
|
|
183
|
+
|
|
184
|
+
frames.append(
|
|
185
|
+
go.Frame(
|
|
186
|
+
data=[
|
|
187
|
+
go.Scatter(
|
|
188
|
+
x=cur_hx,
|
|
189
|
+
y=cur_hy
|
|
190
|
+
),
|
|
191
|
+
go.Scatter(
|
|
192
|
+
x=cur_ax,
|
|
193
|
+
y=cur_ay,
|
|
194
|
+
text=cur_alabels
|
|
195
|
+
)
|
|
196
|
+
],
|
|
197
|
+
name=f"Iter {state['iter']}",
|
|
198
|
+
traces=[1, 2]
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
fig.frames = frames
|
|
203
|
+
|
|
204
|
+
sliders = [{
|
|
205
|
+
"active": 0,
|
|
206
|
+
"y": -0.05,
|
|
207
|
+
"yanchor": "top",
|
|
208
|
+
"xanchor": "left",
|
|
209
|
+
"currentvalue": {
|
|
210
|
+
"font": {
|
|
211
|
+
"size": 16
|
|
212
|
+
},
|
|
213
|
+
"prefix": "Step: ",
|
|
214
|
+
"visible": True,
|
|
215
|
+
"xanchor": "right"
|
|
216
|
+
},
|
|
217
|
+
"transition": {
|
|
218
|
+
"duration": 0
|
|
219
|
+
},
|
|
220
|
+
"pad": {
|
|
221
|
+
"b": 10,
|
|
222
|
+
"t": 20
|
|
223
|
+
},
|
|
224
|
+
"steps": [
|
|
225
|
+
{
|
|
226
|
+
"args": [
|
|
227
|
+
[frame.name],
|
|
228
|
+
{
|
|
229
|
+
"frame": {
|
|
230
|
+
"duration": 0,
|
|
231
|
+
"redraw": True
|
|
232
|
+
},
|
|
233
|
+
"mode": "immediate"
|
|
234
|
+
}
|
|
235
|
+
],
|
|
236
|
+
"label": str(k),
|
|
237
|
+
"method": "animate"
|
|
238
|
+
}
|
|
239
|
+
for k, frame in enumerate(frames)
|
|
240
|
+
]
|
|
241
|
+
}]
|
|
242
|
+
|
|
243
|
+
updatemenus = [{
|
|
244
|
+
"buttons": [
|
|
245
|
+
{
|
|
246
|
+
"args": [
|
|
247
|
+
None,
|
|
248
|
+
{
|
|
249
|
+
"frame": {
|
|
250
|
+
"duration": 600,
|
|
251
|
+
"redraw": True
|
|
252
|
+
},
|
|
253
|
+
"fromcurrent": True
|
|
254
|
+
}
|
|
255
|
+
],
|
|
256
|
+
"label": "Play",
|
|
257
|
+
"method": "animate"
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"args": [
|
|
261
|
+
[None],
|
|
262
|
+
{
|
|
263
|
+
"frame": {
|
|
264
|
+
"duration": 0,
|
|
265
|
+
"redraw": True
|
|
266
|
+
},
|
|
267
|
+
"mode": "immediate"
|
|
268
|
+
}
|
|
269
|
+
],
|
|
270
|
+
"label": "Pause",
|
|
271
|
+
"method": "animate"
|
|
272
|
+
}
|
|
273
|
+
],
|
|
274
|
+
"direction": "left",
|
|
275
|
+
"pad": {
|
|
276
|
+
"r": 10,
|
|
277
|
+
"t": 10
|
|
278
|
+
},
|
|
279
|
+
"showactive": False,
|
|
280
|
+
"type": "buttons",
|
|
281
|
+
"x": 0.5,
|
|
282
|
+
"xanchor": "center",
|
|
283
|
+
"y": -0.32,
|
|
284
|
+
"yanchor": "top"
|
|
285
|
+
}]
|
|
286
|
+
|
|
287
|
+
fig.update_layout(
|
|
288
|
+
title=(
|
|
289
|
+
f"Golden Section Search"
|
|
290
|
+
),
|
|
291
|
+
xaxis_title="x",
|
|
292
|
+
yaxis_title="f(x)",
|
|
293
|
+
width=800,
|
|
294
|
+
height=620,
|
|
295
|
+
margin=dict(
|
|
296
|
+
l=25,
|
|
297
|
+
r=25,
|
|
298
|
+
t=50,
|
|
299
|
+
b=160
|
|
300
|
+
),
|
|
301
|
+
plot_bgcolor='white',
|
|
302
|
+
xaxis=dict(
|
|
303
|
+
showgrid=True,
|
|
304
|
+
gridcolor='#eaeaea'
|
|
305
|
+
),
|
|
306
|
+
yaxis=dict(
|
|
307
|
+
showgrid=True,
|
|
308
|
+
gridcolor='#eaeaea'
|
|
309
|
+
),
|
|
310
|
+
updatemenus=updatemenus,
|
|
311
|
+
sliders=sliders
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
return fig
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def visual_powell_2d(
|
|
318
|
+
f,
|
|
319
|
+
point0,
|
|
320
|
+
h1=(1, 0),
|
|
321
|
+
h2=(0, 1),
|
|
322
|
+
tol=1e-6,
|
|
323
|
+
max_iter=100
|
|
324
|
+
):
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
point = np.array(point0, dtype=float)
|
|
328
|
+
h1 = np.array(h1, dtype=float)
|
|
329
|
+
h2 = np.array(h2, dtype=float)
|
|
330
|
+
|
|
331
|
+
def evaluate(pt):
|
|
332
|
+
return f(pt[0], pt[1])
|
|
333
|
+
|
|
334
|
+
def line_search(pt, direction):
|
|
335
|
+
if np.linalg.norm(direction) < 1e-12:
|
|
336
|
+
return pt.copy()
|
|
337
|
+
|
|
338
|
+
res = minimize_scalar(
|
|
339
|
+
lambda alpha: evaluate(pt + alpha * direction),
|
|
340
|
+
method="brent"
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
return pt + res.x * direction
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
points = [point.copy()]
|
|
347
|
+
|
|
348
|
+
directions = [h1, h2]
|
|
349
|
+
|
|
350
|
+
converged = False
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
for iteration in range(max_iter):
|
|
354
|
+
|
|
355
|
+
iteration_start = point.copy()
|
|
356
|
+
f_start = evaluate(point)
|
|
357
|
+
|
|
358
|
+
for direction in directions:
|
|
359
|
+
point = line_search(point, direction)
|
|
360
|
+
points.append(point.copy())
|
|
361
|
+
|
|
362
|
+
new_direction = point - iteration_start
|
|
363
|
+
|
|
364
|
+
if np.linalg.norm(new_direction) < tol:
|
|
365
|
+
converged = True
|
|
366
|
+
break
|
|
367
|
+
|
|
368
|
+
point_before_new_direction = point.copy()
|
|
369
|
+
|
|
370
|
+
point = line_search(point, new_direction)
|
|
371
|
+
points.append(point.copy())
|
|
372
|
+
|
|
373
|
+
f_new = evaluate(point)
|
|
374
|
+
|
|
375
|
+
point_change = np.linalg.norm(
|
|
376
|
+
point - point_before_new_direction
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
objective_change = abs(f_new - f_start)
|
|
380
|
+
|
|
381
|
+
if point_change < tol and objective_change < tol:
|
|
382
|
+
converged = True
|
|
383
|
+
break
|
|
384
|
+
|
|
385
|
+
directions = [
|
|
386
|
+
directions[1],
|
|
387
|
+
new_direction
|
|
388
|
+
]
|
|
389
|
+
|
|
390
|
+
x_coords = [p[0] for p in points]
|
|
391
|
+
y_coords = [p[1] for p in points]
|
|
392
|
+
|
|
393
|
+
labels = [str(i) for i in range(len(points))]
|
|
394
|
+
|
|
395
|
+
labels[-1] = "Min"
|
|
396
|
+
|
|
397
|
+
padding = 2.0
|
|
398
|
+
|
|
399
|
+
x_min = min(x_coords) - padding
|
|
400
|
+
x_max = max(x_coords) + padding
|
|
401
|
+
|
|
402
|
+
y_min = min(y_coords) - padding
|
|
403
|
+
y_max = max(y_coords) + padding
|
|
404
|
+
|
|
405
|
+
x_values = np.linspace(x_min, x_max, 200)
|
|
406
|
+
y_values = np.linspace(y_min, y_max, 200)
|
|
407
|
+
|
|
408
|
+
X, Y = np.meshgrid(x_values, y_values)
|
|
409
|
+
|
|
410
|
+
Z = np.zeros_like(X)
|
|
411
|
+
|
|
412
|
+
for i in range(X.shape[0]):
|
|
413
|
+
for j in range(X.shape[1]):
|
|
414
|
+
Z[i, j] = f(X[i, j], Y[i, j])
|
|
415
|
+
|
|
416
|
+
fig = go.Figure()
|
|
417
|
+
|
|
418
|
+
fig.add_trace(
|
|
419
|
+
go.Contour(
|
|
420
|
+
x=x_values,
|
|
421
|
+
y=y_values,
|
|
422
|
+
z=Z,
|
|
423
|
+
colorscale="Blues_r",
|
|
424
|
+
showscale=False,
|
|
425
|
+
contours=dict(
|
|
426
|
+
start=np.min(Z),
|
|
427
|
+
end=np.max(Z),
|
|
428
|
+
size=(np.max(Z) - np.min(Z)) / 15
|
|
429
|
+
),
|
|
430
|
+
line=dict(width=1),
|
|
431
|
+
name="Objective Contour"
|
|
432
|
+
)
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
fig.add_trace(
|
|
436
|
+
go.Scatter(
|
|
437
|
+
x=x_coords,
|
|
438
|
+
y=y_coords,
|
|
439
|
+
mode="lines",
|
|
440
|
+
name="Search Path",
|
|
441
|
+
line=dict(
|
|
442
|
+
color="black",
|
|
443
|
+
width=2
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
fig.add_trace(
|
|
449
|
+
go.Scatter(
|
|
450
|
+
x=x_coords,
|
|
451
|
+
y=y_coords,
|
|
452
|
+
mode="markers+text",
|
|
453
|
+
name="Evaluation Points",
|
|
454
|
+
text=labels,
|
|
455
|
+
textposition="top center",
|
|
456
|
+
textfont=dict(
|
|
457
|
+
size=12,
|
|
458
|
+
color="black"
|
|
459
|
+
),
|
|
460
|
+
marker=dict(
|
|
461
|
+
color=["black"] * (len(points) - 1) + ["red"],
|
|
462
|
+
size=[8] * (len(points) - 1) + [14],
|
|
463
|
+
symbol=["circle"] * (len(points) - 1) + ["star"]
|
|
464
|
+
),
|
|
465
|
+
hoverinfo="text",
|
|
466
|
+
hovertext=[
|
|
467
|
+
(
|
|
468
|
+
f"Point {label}"
|
|
469
|
+
f"<br>x: {x:.6f}"
|
|
470
|
+
f"<br>y: {y:.6f}"
|
|
471
|
+
f"<br>f(x,y): {f(x, y):.6f}"
|
|
472
|
+
)
|
|
473
|
+
for label, x, y in zip(
|
|
474
|
+
labels,
|
|
475
|
+
x_coords,
|
|
476
|
+
y_coords
|
|
477
|
+
)
|
|
478
|
+
]
|
|
479
|
+
)
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
if converged:
|
|
483
|
+
title = (
|
|
484
|
+
f"Powell's Method: Converged "
|
|
485
|
+
f"({iteration + 1} iterations)"
|
|
486
|
+
)
|
|
487
|
+
else:
|
|
488
|
+
title = (
|
|
489
|
+
f"Powell's Method: Maximum Iterations Reached "
|
|
490
|
+
f"({max_iter} iterations)"
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
fig.update_layout(
|
|
494
|
+
title=title,
|
|
495
|
+
xaxis_title="x",
|
|
496
|
+
yaxis_title="y",
|
|
497
|
+
width=800,
|
|
498
|
+
height=600,
|
|
499
|
+
margin=dict(
|
|
500
|
+
l=25,
|
|
501
|
+
r=25,
|
|
502
|
+
t=50,
|
|
503
|
+
b=25
|
|
504
|
+
),
|
|
505
|
+
plot_bgcolor="white"
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
return fig
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def newton_method(f, grad_f, hessian_f, x0, tol=1e-6, max_iter=100):
|
|
512
|
+
x = x0
|
|
513
|
+
print(x)
|
|
514
|
+
guesses = [x]
|
|
515
|
+
for _ in range(max_iter):
|
|
516
|
+
grad = grad_f(x)
|
|
517
|
+
hess = hessian_f(x)
|
|
518
|
+
|
|
519
|
+
# ~~ What goes here?
|
|
520
|
+
|
|
521
|
+
###
|
|
522
|
+
delta_x = solve(hess, -grad, assume_a = 'sym')
|
|
523
|
+
###
|
|
524
|
+
x = x + delta_x
|
|
525
|
+
guesses.append(x)
|
|
526
|
+
print(x)
|
|
527
|
+
if np.linalg.norm(grad) < tol:
|
|
528
|
+
break
|
|
529
|
+
|
|
530
|
+
# Create a surface plot of the function
|
|
531
|
+
x = np.linspace(-2, 2, 100)
|
|
532
|
+
y = np.linspace(-2, 2, 100)
|
|
533
|
+
X, Y = np.meshgrid(x, y)
|
|
534
|
+
Z = f([X, Y])
|
|
535
|
+
|
|
536
|
+
fig = go.Figure(data=[go.Surface(x=X, y=Y, z=Z)])
|
|
537
|
+
|
|
538
|
+
# Add markers for each guess
|
|
539
|
+
for guess in guesses:
|
|
540
|
+
fig.add_trace(go.Scatter3d(
|
|
541
|
+
x=[guess[0]],
|
|
542
|
+
y=[guess[1]],
|
|
543
|
+
z=[f(guess)],
|
|
544
|
+
mode='markers',
|
|
545
|
+
marker=dict(
|
|
546
|
+
size=5,
|
|
547
|
+
color='red'
|
|
548
|
+
)
|
|
549
|
+
))
|
|
550
|
+
|
|
551
|
+
fig.update_layout(
|
|
552
|
+
title='Newton\'s Method Optimization',
|
|
553
|
+
scene=dict(
|
|
554
|
+
xaxis_title='x',
|
|
555
|
+
yaxis_title='y',
|
|
556
|
+
zaxis_title='f(x,y)'
|
|
557
|
+
)
|
|
558
|
+
)
|
|
559
|
+
fig.show()
|
|
560
|
+
|
|
561
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: executable_engineering
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.42
|
|
4
4
|
Summary: Demonstrative tools for the Executable Engineering textbook
|
|
5
5
|
Home-page: https://github.com/themintlab/ExecutableEngineering/tree/main/executable_engineering
|
|
6
6
|
Author: Michael Welland
|
|
@@ -5,12 +5,14 @@ setup.py
|
|
|
5
5
|
executable_engineering/__init__.py
|
|
6
6
|
executable_engineering/closedoptimization_module.py
|
|
7
7
|
executable_engineering/closedrootfinder_module.py
|
|
8
|
+
executable_engineering/functions_module.py
|
|
8
9
|
executable_engineering/gradientoptimization_module.py
|
|
9
10
|
executable_engineering/linearsystems_module.py
|
|
10
11
|
executable_engineering/neuralnet_module.py
|
|
11
12
|
executable_engineering/numerical_error_module.py
|
|
12
13
|
executable_engineering/openoptimization_module.py
|
|
13
14
|
executable_engineering/openrootfinder_module.py
|
|
15
|
+
executable_engineering/optimization_module.py
|
|
14
16
|
executable_engineering.egg-info/PKG-INFO
|
|
15
17
|
executable_engineering.egg-info/SOURCES.txt
|
|
16
18
|
executable_engineering.egg-info/dependency_links.txt
|
|
@@ -6,7 +6,7 @@ long_description = (this_directory / "README.md").read_text(encoding="utf-8")
|
|
|
6
6
|
|
|
7
7
|
setup(
|
|
8
8
|
name="executable_engineering",
|
|
9
|
-
version="0.1.
|
|
9
|
+
version="0.1.42",
|
|
10
10
|
description="Demonstrative tools for the Executable Engineering textbook",
|
|
11
11
|
long_description=long_description,
|
|
12
12
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|