executable-engineering 0.1.42__tar.gz → 0.1.43__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.
Files changed (23) hide show
  1. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/PKG-INFO +1 -1
  2. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/__init__.py +4 -1
  3. executable_engineering-0.1.43/executable_engineering/functions_module.py +278 -0
  4. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering.egg-info/PKG-INFO +1 -1
  5. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/setup.py +1 -1
  6. executable_engineering-0.1.42/executable_engineering/functions_module.py +0 -151
  7. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/LICENSE +0 -0
  8. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/README.md +0 -0
  9. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/closedoptimization_module.py +0 -0
  10. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/closedrootfinder_module.py +0 -0
  11. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/gradientoptimization_module.py +0 -0
  12. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/linearsystems_module.py +0 -0
  13. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/neuralnet_module.py +0 -0
  14. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/numerical_error_module.py +0 -0
  15. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/openoptimization_module.py +0 -0
  16. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/openrootfinder_module.py +0 -0
  17. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering/optimization_module.py +0 -0
  18. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering.egg-info/SOURCES.txt +0 -0
  19. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering.egg-info/dependency_links.txt +0 -0
  20. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering.egg-info/requires.txt +0 -0
  21. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/executable_engineering.egg-info/top_level.txt +0 -0
  22. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/pyproject.toml +0 -0
  23. {executable_engineering-0.1.42 → executable_engineering-0.1.43}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: executable_engineering
3
- Version: 0.1.42
3
+ Version: 0.1.43
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
@@ -6,7 +6,7 @@ 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
9
+ from .functions_module import weierstrass, plot_and_find_roots, complex_plot_with_shadow, plot_polynomial_roots_companion, plot_secant, plot_newton_raphson, plot_basins_of_attraction
10
10
  from .optimization_module import visual_golden_section, visual_powell_2d, newton_method
11
11
 
12
12
  __all__ = [
@@ -14,6 +14,9 @@ __all__ = [
14
14
  "visual_powell_2d",
15
15
  "visual_golden_section",
16
16
  "plot_polynomial_roots_companion",
17
+ "plot_secant",
18
+ "plot_newton_raphson",
19
+ "plot_basins_of_attraction",
17
20
  "plot_and_find_roots",
18
21
  "complex_plot_with_shadow",
19
22
  "weierstrass",
@@ -0,0 +1,278 @@
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
+
152
+
153
+ def plot_secant(f, x0, x1, tolerance=1e-6, max_iterations=100, title='Secant Method'):
154
+ import plotly.graph_objects as go
155
+ import plotly.express as px
156
+ import numpy as np
157
+
158
+ x_values = [x0, x1]
159
+ for i in range(max_iterations):
160
+ denom = f(x1) - f(x0)
161
+ if denom == 0:
162
+ break
163
+ x_new = x1 - f(x1) * (x1 - x0) / denom
164
+ x_values.append(x_new)
165
+ if abs(f(x_new)) < tolerance:
166
+ break
167
+ x0 = x1
168
+ x1 = x_new
169
+
170
+ root = x_values[-1] if abs(f(x_values[-1])) < tolerance else None
171
+
172
+ # Determine plot bounds
173
+ min_x = min(x_values)
174
+ max_x = max(x_values)
175
+ padding = (max_x - min_x) * 0.2 if max_x > min_x else 0.5
176
+ x = np.linspace(min_x - padding, max_x + padding, 100)
177
+
178
+ fig = go.Figure()
179
+ fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines', name='f(x)'))
180
+ fig.add_trace(go.Scatter(x=[x_values[0], x_values[1]], y=[f(x_values[0]), f(x_values[1])], mode='markers', name='Initial guesses'))
181
+
182
+ colors = px.colors.qualitative.Plotly
183
+
184
+ for i in range(1, len(x_values) - 1):
185
+ # Secant line intersecting x-axis
186
+ fig.add_trace(go.Scatter(x=[x_values[i-1], x_values[i], x_values[i+1]], y=[f(x_values[i-1]), f(x_values[i]), 0], mode='lines', line=dict(dash='dash', color='gray'), showlegend=False))
187
+ # Vertical dotted line to the curve
188
+ fig.add_trace(go.Scatter(x=[x_values[i+1], x_values[i+1]], y=[0, f(x_values[i+1])], mode='lines', line=dict(dash='dot', color='gray'), showlegend=False))
189
+
190
+ color = colors[(i-1) % len(colors)]
191
+ fig.add_trace(go.Scatter(x=[x_values[i+1]], y=[0], mode='markers', marker=dict(size=10, color=color), name=f'x_{i+1}'))
192
+
193
+ if root:
194
+ fig.add_trace(go.Scatter(x=[root], y=[0], mode='markers', marker=dict(color='green', size=10), name='Approximate root'))
195
+
196
+ fig.update_layout(title=title, xaxis_title='x', yaxis_title='f(x)')
197
+ fig.show()
198
+ return root, x_values
199
+
200
+ def plot_newton_raphson(f, df, x0, tolerance=1e-6, max_iterations=100, title='Newton-Raphson Method', xrange=None, yrange=None):
201
+ import plotly.graph_objects as go
202
+ import plotly.express as px
203
+ import numpy as np
204
+
205
+ x_values = [x0]
206
+ for i in range(max_iterations):
207
+ d = df(x0)
208
+ if d == 0:
209
+ break
210
+ x_new = x0 - f(x0) / d
211
+ x_values.append(x_new)
212
+ if abs(f(x_new)) < tolerance or abs(x_new - x0) < tolerance:
213
+ break
214
+ x0 = x_new
215
+
216
+ root = x_values[-1] if abs(f(x_values[-1])) < tolerance or abs(x_values[-1] - x_values[-2]) < tolerance else None
217
+
218
+ # Determine plot bounds
219
+ if xrange is None:
220
+ min_x = min(x_values)
221
+ max_x = max(x_values)
222
+ padding = (max_x - min_x) * 0.2 if max_x > min_x else 0.5
223
+ x = np.linspace(min_x - padding, max_x + padding, 100)
224
+ else:
225
+ x = np.linspace(xrange[0], xrange[1], 100)
226
+
227
+ fig = go.Figure()
228
+ fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines', name='f(x)'))
229
+ fig.add_trace(go.Scatter(x=[x_values[0]], y=[f(x_values[0])], mode='markers', name='Initial guess'))
230
+
231
+ colors = px.colors.qualitative.Plotly
232
+
233
+ for i in range(len(x_values) - 1):
234
+ # Tangent line intersecting x-axis
235
+ fig.add_trace(go.Scatter(x=[x_values[i], x_values[i+1]], y=[f(x_values[i]), 0], mode='lines', line=dict(dash='dash', color='gray'), showlegend=False))
236
+ # Vertical dotted line to the curve
237
+ fig.add_trace(go.Scatter(x=[x_values[i+1], x_values[i+1]], y=[0, f(x_values[i+1])], mode='lines', line=dict(dash='dot', color='gray'), showlegend=False))
238
+
239
+ color = colors[i % len(colors)]
240
+ fig.add_trace(go.Scatter(x=[x_values[i+1]], y=[0], mode='markers', marker=dict(size=10, color=color), name=f'x_{i+1}'))
241
+
242
+ if root:
243
+ fig.add_trace(go.Scatter(x=[root], y=[0], mode='markers', marker=dict(color='green', size=10), name='Approximate root'))
244
+
245
+ fig.update_layout(title=title, xaxis_title='x', yaxis_title='f(x)')
246
+ if xrange:
247
+ fig.update_layout(xaxis_range=xrange)
248
+ if yrange:
249
+ fig.update_layout(yaxis_range=yrange)
250
+
251
+ fig.show()
252
+ return root, x_values
253
+
254
+ def plot_basins_of_attraction(f, df, known_roots, title, real_range=(-2, 2), imag_range=(-2, 2), grid_size=500, th=1e-3):
255
+ import numpy as np
256
+ import plotly.graph_objects as go
257
+ from scipy.optimize import newton
258
+
259
+ real_values = np.linspace(real_range[0], real_range[1], grid_size)
260
+ imag_values = np.linspace(imag_range[0], imag_range[1], grid_size)
261
+ z_grid = np.array([[complex(r, i) for r in real_values] for i in imag_values])
262
+
263
+ roots = newton(f, z_grid, fprime=df)
264
+
265
+ colors = np.zeros((grid_size, grid_size))
266
+ for i in range(grid_size):
267
+ for j in range(grid_size):
268
+ if roots[i, j] is None:
269
+ colors[i, j] = 0
270
+ else:
271
+ for idx, r in enumerate(known_roots):
272
+ if abs(roots[i, j] - r) < th:
273
+ colors[i, j] = idx + 1
274
+ break
275
+
276
+ fig = go.Figure(data=go.Heatmap(z=colors, x=real_values, y=imag_values, colorscale='Viridis', showscale=False))
277
+ fig.update_layout(title=title, xaxis_title='Real', yaxis_title='Imaginary', width=600, height=600)
278
+ fig.show()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: executable_engineering
3
- Version: 0.1.42
3
+ Version: 0.1.43
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
@@ -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.42",
9
+ version="0.1.43",
10
10
  description="Demonstrative tools for the Executable Engineering textbook",
11
11
  long_description=long_description,
12
12
  long_description_content_type="text/markdown",
@@ -1,151 +0,0 @@
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
-