IBB-Helper 0.3.2__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.
- ibb_helper-0.3.2/IBB_Helper/__init__.py +12 -0
- ibb_helper-0.3.2/IBB_Helper/combine_plots.py +239 -0
- ibb_helper-0.3.2/IBB_Helper/display.py +42 -0
- ibb_helper-0.3.2/IBB_Helper/display_eigen.py +133 -0
- ibb_helper-0.3.2/IBB_Helper/display_matrix.py +39 -0
- ibb_helper-0.3.2/IBB_Helper/extend_plot.py +240 -0
- ibb_helper-0.3.2/IBB_Helper/minimize.py +62 -0
- ibb_helper-0.3.2/IBB_Helper/num_int.py +48 -0
- ibb_helper-0.3.2/IBB_Helper/plot_2d.py +139 -0
- ibb_helper-0.3.2/IBB_Helper/plot_3d.py +80 -0
- ibb_helper-0.3.2/IBB_Helper/plot_param_grid.py +102 -0
- ibb_helper-0.3.2/IBB_Helper/symbolic_BSpline.py +48 -0
- ibb_helper-0.3.2/IBB_Helper.egg-info/PKG-INFO +87 -0
- ibb_helper-0.3.2/IBB_Helper.egg-info/SOURCES.txt +21 -0
- ibb_helper-0.3.2/IBB_Helper.egg-info/dependency_links.txt +1 -0
- ibb_helper-0.3.2/IBB_Helper.egg-info/requires.txt +7 -0
- ibb_helper-0.3.2/IBB_Helper.egg-info/top_level.txt +1 -0
- ibb_helper-0.3.2/LICENSE +28 -0
- ibb_helper-0.3.2/PKG-INFO +87 -0
- ibb_helper-0.3.2/README.md +66 -0
- ibb_helper-0.3.2/pyproject.toml +27 -0
- ibb_helper-0.3.2/setup.cfg +4 -0
- ibb_helper-0.3.2/setup.py +3 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
__version__ = "0.3.2"
|
|
2
|
+
from .display_matrix import display_matrix
|
|
3
|
+
from .display import display
|
|
4
|
+
from .display_eigen import display_eigen
|
|
5
|
+
from .plot_2d import plot_2d
|
|
6
|
+
from .plot_3d import plot_3d
|
|
7
|
+
from .extend_plot import extend_plot
|
|
8
|
+
from .combine_plots import combine_plots
|
|
9
|
+
from .symbolic_BSpline import symbolic_BSpline
|
|
10
|
+
from .minimize import minimize
|
|
11
|
+
from .num_int import num_int
|
|
12
|
+
from .plot_param_grid import plot_param_grid
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import plotly.graph_objects as go
|
|
3
|
+
import copy
|
|
4
|
+
from sympy import latex
|
|
5
|
+
|
|
6
|
+
def combine_plots(plot_list, labels=None, line_styles=None, colors=None,
|
|
7
|
+
swap_axes=False, show=True, grid=False,
|
|
8
|
+
xlim=None, ylim=None, title=None, xlabel=None, ylabel=None):
|
|
9
|
+
"""
|
|
10
|
+
Combines multiple individual matplotlib Axes or plotly Figures into a single figure.
|
|
11
|
+
|
|
12
|
+
This function intelligently detects the type of plot object (Matplotlib or Plotly)
|
|
13
|
+
and merges all data onto a new, single canvas. It preserves the original styling
|
|
14
|
+
of each plot by default, but allows for explicit overrides for the final output.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
plot_list : list
|
|
19
|
+
The list of plot objects to be combined. All plots must be of the same type.
|
|
20
|
+
- Syntax: `[ax1, ax2]` or `[fig1, fig2]`
|
|
21
|
+
|
|
22
|
+
labels : list[str], optional
|
|
23
|
+
A list of new labels to override the existing ones for the legend.
|
|
24
|
+
- Syntax: `['Plot A', 'Plot B']`
|
|
25
|
+
|
|
26
|
+
line_styles : list[str], optional
|
|
27
|
+
A list of new line styles to override the existing ones. Can also be used
|
|
28
|
+
to force a marker style (e.g., 'o', 'x').
|
|
29
|
+
- Syntax: `['solid', 'dashed', 'o', 'x']`
|
|
30
|
+
|
|
31
|
+
colors : list[str], optional
|
|
32
|
+
A list of new colors to override the existing ones.
|
|
33
|
+
- Syntax: `['red', 'blue', '#FF5733']`
|
|
34
|
+
|
|
35
|
+
swap_axes : bool, optional
|
|
36
|
+
If True, the x and y data for all plots will be swapped.
|
|
37
|
+
- Syntax: `True` or `False`
|
|
38
|
+
|
|
39
|
+
show : bool, optional
|
|
40
|
+
If True, displays the combined plot immediately. If False, the plot object
|
|
41
|
+
is returned without being shown.
|
|
42
|
+
- Syntax: `True` or `False`
|
|
43
|
+
|
|
44
|
+
grid : bool, optional
|
|
45
|
+
If True, displays a grid on the final combined plot.
|
|
46
|
+
- Syntax: `True` or `False`
|
|
47
|
+
|
|
48
|
+
xlim : tuple[float, float], optional
|
|
49
|
+
Sets the x-axis limits for the combined plot.
|
|
50
|
+
- Syntax: `(0, 10)`
|
|
51
|
+
|
|
52
|
+
ylim : tuple[float, float], optional
|
|
53
|
+
Sets the y-axis limits for the combined plot.
|
|
54
|
+
- Syntax: `(-5, 5)`
|
|
55
|
+
|
|
56
|
+
title : str, optional
|
|
57
|
+
The main title for the combined plot.
|
|
58
|
+
- Syntax: `"My Combined Plot"`
|
|
59
|
+
|
|
60
|
+
xlabel : str, optional
|
|
61
|
+
The x-axis label for the combined plot.
|
|
62
|
+
- Syntax: `"Time (s)"`
|
|
63
|
+
|
|
64
|
+
ylabel : str, optional
|
|
65
|
+
The y-axis label for the combined plot.
|
|
66
|
+
- Syntax: `"Value"`
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
matplotlib.axes.Axes or plotly.graph_objects.Figure
|
|
71
|
+
The final combined plot object, either a Matplotlib Axes or a Plotly Figure,
|
|
72
|
+
depending on the input.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
# === SMART LABEL HELPER ===
|
|
76
|
+
def smart_label(lbl):
|
|
77
|
+
"""Converts label or SymPy expression into LaTeX-compatible string."""
|
|
78
|
+
if lbl is None:
|
|
79
|
+
return None
|
|
80
|
+
if isinstance(lbl, str):
|
|
81
|
+
if any(c in lbl for c in ['_', '^', '\\']):
|
|
82
|
+
return f"${lbl}$"
|
|
83
|
+
else:
|
|
84
|
+
return lbl
|
|
85
|
+
else:
|
|
86
|
+
return f"${latex(lbl)}$"
|
|
87
|
+
|
|
88
|
+
if not plot_list:
|
|
89
|
+
raise ValueError("plot_list cannot be empty")
|
|
90
|
+
|
|
91
|
+
first_plot = plot_list[0]
|
|
92
|
+
|
|
93
|
+
# Define marker symbols for internal check (same list used in plot_2d)
|
|
94
|
+
marker_symbols = ['o', 's', '^', 'x', '*', 'D', 'p', '+', 'v', '<', '>', '1', '2', '3', '4']
|
|
95
|
+
|
|
96
|
+
# === MATPLOTLIB AXES HANDLING ===
|
|
97
|
+
if hasattr(first_plot, 'lines'):
|
|
98
|
+
fig, ax = plt.subplots()
|
|
99
|
+
|
|
100
|
+
for i, plot_ax in enumerate(plot_list):
|
|
101
|
+
first_line = True
|
|
102
|
+
|
|
103
|
+
for line in plot_ax.lines:
|
|
104
|
+
x, y = line.get_data()
|
|
105
|
+
|
|
106
|
+
# --- 1. Retrieve Existing Properties (Default Transfer) ---
|
|
107
|
+
current_color = line.get_color()
|
|
108
|
+
current_marker = line.get_marker()
|
|
109
|
+
current_linestyle = line.get_linestyle()
|
|
110
|
+
current_markersize = line.get_markersize()
|
|
111
|
+
current_linewidth = line.get_linewidth()
|
|
112
|
+
|
|
113
|
+
# Normalize marker: Convert 'None' string to None
|
|
114
|
+
if current_marker in ['None', '']:
|
|
115
|
+
current_marker = None
|
|
116
|
+
|
|
117
|
+
# Normalize linestyle: Matplotlib stores '' for marker-only plots
|
|
118
|
+
if current_linestyle in ['None', '']:
|
|
119
|
+
current_linestyle = 'None'
|
|
120
|
+
|
|
121
|
+
# --- 2. Apply Overrides from append_plots Arguments (Only for First Line) ---
|
|
122
|
+
if first_line:
|
|
123
|
+
# Override Line Style / Marker
|
|
124
|
+
if line_styles and i < len(line_styles):
|
|
125
|
+
new_style = line_styles[i]
|
|
126
|
+
|
|
127
|
+
if new_style in marker_symbols:
|
|
128
|
+
# Case A: User forces a marker (e.g., 'o', 'x')
|
|
129
|
+
current_marker = new_style
|
|
130
|
+
current_linestyle = 'None' # Ensure linestyle is off
|
|
131
|
+
else:
|
|
132
|
+
# Case B: User forces a line style (e.g., '--', 'solid')
|
|
133
|
+
current_linestyle = new_style
|
|
134
|
+
current_marker = None # Clear the marker
|
|
135
|
+
|
|
136
|
+
# Override Color
|
|
137
|
+
if colors and i < len(colors):
|
|
138
|
+
current_color = colors[i]
|
|
139
|
+
|
|
140
|
+
# Apply label only to the first line of the current plot_ax
|
|
141
|
+
label_to_use = smart_label(labels[i]) if first_line and labels and i < len(labels) else None
|
|
142
|
+
|
|
143
|
+
# --- 3. Plot on the New Axes ---
|
|
144
|
+
if swap_axes:
|
|
145
|
+
ax.plot(y, x,
|
|
146
|
+
color=current_color,
|
|
147
|
+
linestyle=current_linestyle if current_linestyle != 'None' else '',
|
|
148
|
+
marker=current_marker if current_marker else '',
|
|
149
|
+
markersize=current_markersize,
|
|
150
|
+
linewidth=current_linewidth,
|
|
151
|
+
label=label_to_use)
|
|
152
|
+
else:
|
|
153
|
+
ax.plot(x, y,
|
|
154
|
+
color=current_color,
|
|
155
|
+
linestyle=current_linestyle if current_linestyle != 'None' else '',
|
|
156
|
+
marker=current_marker if current_marker else '',
|
|
157
|
+
markersize=current_markersize,
|
|
158
|
+
linewidth=current_linewidth,
|
|
159
|
+
label=label_to_use)
|
|
160
|
+
|
|
161
|
+
first_line = False
|
|
162
|
+
|
|
163
|
+
# --- 4. Final Axis/Figure Configuration (Matplotlib) ---
|
|
164
|
+
if xlim:
|
|
165
|
+
ax.set_xlim(xlim)
|
|
166
|
+
if ylim:
|
|
167
|
+
ax.set_ylim(ylim)
|
|
168
|
+
if title:
|
|
169
|
+
ax.set_title(smart_label(title))
|
|
170
|
+
if xlabel:
|
|
171
|
+
ax.set_xlabel(smart_label(xlabel))
|
|
172
|
+
if ylabel:
|
|
173
|
+
ax.set_ylabel(smart_label(ylabel))
|
|
174
|
+
if grid:
|
|
175
|
+
ax.grid()
|
|
176
|
+
if labels:
|
|
177
|
+
ax.legend()
|
|
178
|
+
if show:
|
|
179
|
+
plt.show()
|
|
180
|
+
else:
|
|
181
|
+
plt.close(fig)
|
|
182
|
+
|
|
183
|
+
return ax
|
|
184
|
+
|
|
185
|
+
# === PLOTLY FIGURE HANDLING ===
|
|
186
|
+
elif hasattr(first_plot, 'data'):
|
|
187
|
+
combined_fig = go.Figure()
|
|
188
|
+
|
|
189
|
+
for i, fig in enumerate(plot_list):
|
|
190
|
+
for trace in fig.data:
|
|
191
|
+
# Use deepcopy for safe cloning of the trace
|
|
192
|
+
new_trace = copy.deepcopy(trace)
|
|
193
|
+
|
|
194
|
+
# Apply colors if provided and trace supports it
|
|
195
|
+
if colors and i < len(colors):
|
|
196
|
+
if hasattr(new_trace, 'line'):
|
|
197
|
+
new_trace.line.color = colors[i]
|
|
198
|
+
if hasattr(new_trace, 'marker'):
|
|
199
|
+
new_trace.marker.color = colors[i]
|
|
200
|
+
|
|
201
|
+
# Apply line styles (dash) if provided
|
|
202
|
+
if line_styles and i < len(line_styles):
|
|
203
|
+
if isinstance(new_trace, go.Scatter) and hasattr(new_trace, 'line'):
|
|
204
|
+
new_trace.line.dash = line_styles[i]
|
|
205
|
+
|
|
206
|
+
# Apply labels/name
|
|
207
|
+
if labels and i < len(labels):
|
|
208
|
+
new_trace.name = labels[i]
|
|
209
|
+
|
|
210
|
+
# Handle Axis Swap (if requested)
|
|
211
|
+
if swap_axes and hasattr(new_trace, 'x') and hasattr(new_trace, 'y'):
|
|
212
|
+
new_trace.x, new_trace.y = new_trace.y, new_trace.x
|
|
213
|
+
|
|
214
|
+
combined_fig.add_trace(new_trace)
|
|
215
|
+
|
|
216
|
+
# Final Layout Configuration (Plotly)
|
|
217
|
+
if title:
|
|
218
|
+
combined_fig.update_layout(title=smart_label(title))
|
|
219
|
+
|
|
220
|
+
final_xlabel = smart_label(ylabel) if swap_axes and ylabel else smart_label(xlabel) if xlabel else None
|
|
221
|
+
final_ylabel = smart_label(xlabel) if swap_axes and xlabel else smart_label(ylabel) if ylabel else None
|
|
222
|
+
|
|
223
|
+
if final_xlabel or final_ylabel:
|
|
224
|
+
combined_fig.update_layout(xaxis_title=final_xlabel,
|
|
225
|
+
yaxis_title=final_ylabel)
|
|
226
|
+
if xlim:
|
|
227
|
+
combined_fig.update_xaxes(range=xlim if not swap_axes else ylim)
|
|
228
|
+
if ylim:
|
|
229
|
+
combined_fig.update_yaxes(range=ylim if not swap_axes else xlim)
|
|
230
|
+
if grid:
|
|
231
|
+
combined_fig.update_layout(xaxis_showgrid=True, yaxis_showgrid=True)
|
|
232
|
+
|
|
233
|
+
if show:
|
|
234
|
+
combined_fig.show()
|
|
235
|
+
|
|
236
|
+
return combined_fig
|
|
237
|
+
|
|
238
|
+
else:
|
|
239
|
+
raise TypeError("Unknown plot object type passed to combine_plots.")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sympy as sp
|
|
3
|
+
from sympy import latex
|
|
4
|
+
import IPython.display as ipy
|
|
5
|
+
|
|
6
|
+
def display(obj, name="obj", evalf=False, prec=5):
|
|
7
|
+
"""
|
|
8
|
+
Converts vectors/numbers/matrices to LaTeX with optional simplification.
|
|
9
|
+
|
|
10
|
+
Parameters:
|
|
11
|
+
obj : Input (SymPy or NumPy object)
|
|
12
|
+
name : Name used for display
|
|
13
|
+
evalf : If T, evaluates to decimal form before display (default: F)
|
|
14
|
+
prec : Evaluate the given formula to an accuracy of prec digits (default: 5)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def safe_eval_entry(x):
|
|
18
|
+
try:
|
|
19
|
+
return sp.sympify(x).evalf(prec)
|
|
20
|
+
except (TypeError, ValueError):
|
|
21
|
+
return x
|
|
22
|
+
|
|
23
|
+
if evalf:
|
|
24
|
+
if isinstance(obj, np.ndarray):
|
|
25
|
+
obj = sp.Matrix(obj).applyfunc(safe_eval_entry)
|
|
26
|
+
elif isinstance(obj, (sp.Matrix, list)):
|
|
27
|
+
obj = sp.Matrix(obj).applyfunc(safe_eval_entry)
|
|
28
|
+
elif isinstance(obj, sp.NDimArray):
|
|
29
|
+
obj = obj.applyfunc(safe_eval_entry)
|
|
30
|
+
else:
|
|
31
|
+
obj = safe_eval_entry(obj)
|
|
32
|
+
else:
|
|
33
|
+
if isinstance(obj, np.ndarray):
|
|
34
|
+
obj = sp.Matrix(obj)
|
|
35
|
+
elif isinstance(obj, (sp.Matrix, list)):
|
|
36
|
+
obj = sp.Matrix(obj)
|
|
37
|
+
elif isinstance(obj, sp.NDimArray):
|
|
38
|
+
obj = obj
|
|
39
|
+
else:
|
|
40
|
+
obj = sp.sympify(obj)
|
|
41
|
+
|
|
42
|
+
ipy.display(ipy.Math(f"{name} = {latex(obj)}"))
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sympy as sp
|
|
3
|
+
from sympy import latex
|
|
4
|
+
import IPython.display as ipy
|
|
5
|
+
|
|
6
|
+
def display_eigen(A, name="Matrix", evalf=False, prec=5, return_data=False, show="both", output="default"):
|
|
7
|
+
"""
|
|
8
|
+
Computes and displays the eigenvalues and eigenvectors of a matrix A with enhanced display options.
|
|
9
|
+
|
|
10
|
+
Parameters:
|
|
11
|
+
A : Square matrix (SymPy Matrix or NumPy ndarray)
|
|
12
|
+
name : Label used for the matrix (default="Matrix")
|
|
13
|
+
evalf : If True, show decimal values (default=False)
|
|
14
|
+
prec : Evaluate the given formula to an accuracy of prec digits (default: 5)
|
|
15
|
+
show : What to display ("both", "eigvals", "eigvecs", or "none") (default="both")
|
|
16
|
+
output : How to display ("default", "compact", "Maple") (default="default")
|
|
17
|
+
return_data: If True, also returns eigenvalues and eigenvectors (default=False)
|
|
18
|
+
|
|
19
|
+
Returns (if return_data=True):
|
|
20
|
+
- eigvals: List of eigenvalues
|
|
21
|
+
- eigvecs: List of corresponding eigenvectors (each as a SymPy Matrix)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
threshold = 10**(-10)
|
|
25
|
+
|
|
26
|
+
# Track original input type
|
|
27
|
+
is_numpy_input = isinstance(A, np.ndarray)
|
|
28
|
+
|
|
29
|
+
def safe_eval(x):
|
|
30
|
+
try:
|
|
31
|
+
val = x.evalf(prec)
|
|
32
|
+
if abs(val) < threshold:
|
|
33
|
+
return sp.Integer(0)
|
|
34
|
+
return round(float(val), prec)
|
|
35
|
+
except (TypeError, ValueError):
|
|
36
|
+
return x
|
|
37
|
+
|
|
38
|
+
# Convert NumPy array to SymPy Matrix if needed
|
|
39
|
+
if isinstance(A, np.ndarray):
|
|
40
|
+
A = sp.Matrix(A)
|
|
41
|
+
|
|
42
|
+
# Compute eigenvalues and eigenvectors
|
|
43
|
+
eigen_data = A.eigenvects()
|
|
44
|
+
|
|
45
|
+
# To store raw data
|
|
46
|
+
eigvals = []
|
|
47
|
+
eigvecs = []
|
|
48
|
+
|
|
49
|
+
# Validate show parameter
|
|
50
|
+
show = show.lower()
|
|
51
|
+
valid_options = ["both", "eigvals", "eigvecs", "none"]
|
|
52
|
+
if show not in valid_options:
|
|
53
|
+
raise ValueError(f"show must be one of {valid_options}")
|
|
54
|
+
|
|
55
|
+
# Validate output parameter
|
|
56
|
+
output = output.lower()
|
|
57
|
+
valid_options_output = ["default", "compact", "maple"]
|
|
58
|
+
if output not in valid_options_output:
|
|
59
|
+
raise ValueError(f"output must be one of {valid_options_output}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Prepare data
|
|
63
|
+
counter = 1
|
|
64
|
+
for eigval, mult, vects in eigen_data:
|
|
65
|
+
for i in range(mult):
|
|
66
|
+
val_disp = safe_eval(eigval) if evalf else eigval
|
|
67
|
+
v_disp = vects[i].applyfunc(safe_eval) if evalf else vects[i]
|
|
68
|
+
eigvals.append(val_disp)
|
|
69
|
+
eigvecs.append(v_disp)
|
|
70
|
+
counter += 1
|
|
71
|
+
|
|
72
|
+
# Sort eigenvalues and eigenvectors
|
|
73
|
+
def sort_key(pair):
|
|
74
|
+
val = pair[0]
|
|
75
|
+
try:
|
|
76
|
+
# Try to get numerical value
|
|
77
|
+
if hasattr(val, 'evalf'):
|
|
78
|
+
num_val = complex(val.evalf())
|
|
79
|
+
else:
|
|
80
|
+
num_val = complex(val)
|
|
81
|
+
|
|
82
|
+
# For numpy inputs: simple ascending sort
|
|
83
|
+
if is_numpy_input:
|
|
84
|
+
return (num_val.real, num_val.imag)
|
|
85
|
+
|
|
86
|
+
# For sympy inputs: zeros first, then ascending
|
|
87
|
+
is_zero = abs(num_val) < threshold
|
|
88
|
+
return (not is_zero, num_val.real, num_val.imag)
|
|
89
|
+
|
|
90
|
+
except (TypeError, ValueError, AttributeError):
|
|
91
|
+
# Fallback for symbolic values
|
|
92
|
+
return (0, 0, 0)
|
|
93
|
+
|
|
94
|
+
paired = list(zip(eigvals, eigvecs))
|
|
95
|
+
paired.sort(key=sort_key)
|
|
96
|
+
eigvals, eigvecs = zip(*paired) if paired else ([], [])
|
|
97
|
+
eigvals = list(eigvals)
|
|
98
|
+
eigvecs = list(eigvecs)
|
|
99
|
+
|
|
100
|
+
# Build LaTeX string based on options
|
|
101
|
+
if show == "both":
|
|
102
|
+
latex_str = f"\\text{{Eigenvalues and Eigenvectors of }} {name}:"
|
|
103
|
+
ipy.display(ipy.Math(latex_str))
|
|
104
|
+
if output == "default":
|
|
105
|
+
latex_str = f""
|
|
106
|
+
for i, (val, vec) in enumerate(zip(eigvals, eigvecs)):
|
|
107
|
+
latex_str += f"\\lambda_{{{i+1}}} = {latex(val)}, v_{{{i+1}}} = {latex(vec)} \\qquad"
|
|
108
|
+
ipy.display(ipy.Math(latex_str))
|
|
109
|
+
elif output == "compact":
|
|
110
|
+
for i, (val, vec) in enumerate(zip(eigvals, eigvecs)):
|
|
111
|
+
latex_str = f"\\lambda_{{{i+1}}} = {latex(val)}, \\quad v_{{{i+1}}} = {latex(vec.T)}"
|
|
112
|
+
ipy.display(ipy.Math(latex_str))
|
|
113
|
+
elif output == "maple":
|
|
114
|
+
latex_str = f"\\lambda = {latex(sp.Matrix(eigvals))}"
|
|
115
|
+
ipy.display(ipy.Math(latex_str))
|
|
116
|
+
V = sp.Matrix.hstack(*[vec for vec in eigvecs])
|
|
117
|
+
latex_str = f"v = {latex(V)}"
|
|
118
|
+
ipy.display(ipy.Math(latex_str))
|
|
119
|
+
|
|
120
|
+
elif show == "eigvals":
|
|
121
|
+
# Default vector display for eigenvalues only
|
|
122
|
+
latex_str = f"\\text{{Eigenvalues of }} {name}: \\quad \\lambda = {latex(sp.Matrix(eigvals))}"
|
|
123
|
+
ipy.display(ipy.Math(latex_str))
|
|
124
|
+
|
|
125
|
+
elif show == "eigvecs":
|
|
126
|
+
# Default matrix display for eigenvectors only
|
|
127
|
+
V = sp.Matrix.hstack(*[vec for vec in eigvecs])
|
|
128
|
+
latex_str = f"\\text{{Eigenvectors of }} {name}: \\quad v = {latex(V)}"
|
|
129
|
+
ipy.display(ipy.Math(latex_str))
|
|
130
|
+
|
|
131
|
+
if return_data:
|
|
132
|
+
return eigvals, eigvecs
|
|
133
|
+
return None
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sympy as sp
|
|
3
|
+
from sympy import latex
|
|
4
|
+
import IPython.display as ipy
|
|
5
|
+
|
|
6
|
+
def display_matrix(matrix, name="Matrix", r=8, c=8, evalf=False, prec=5):
|
|
7
|
+
"""
|
|
8
|
+
Displays a truncated matrix with optional numerical evaluation and rational simplification.
|
|
9
|
+
|
|
10
|
+
Parameters:
|
|
11
|
+
matrix : Input matrix (NumPy array, SymPy Matrix, or list)
|
|
12
|
+
name : Display name (default: "Matrix")
|
|
13
|
+
r : Max rows to display (default: 8)
|
|
14
|
+
c : Max columns to display (default: 8)
|
|
15
|
+
evalf : If T, apply numerical evaluation (default: F)
|
|
16
|
+
prec : Evaluate the given formula to an accuracy of prec digits
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
# Convert to SymPy Matrix if needed
|
|
20
|
+
if isinstance(matrix, (np.ndarray, list)):
|
|
21
|
+
matrix = sp.Matrix(matrix)
|
|
22
|
+
|
|
23
|
+
# Truncate to r rows and c columns
|
|
24
|
+
submatrix = matrix[:min(r, matrix.rows), :min(c, matrix.cols)]
|
|
25
|
+
|
|
26
|
+
# Apply evaluation if needed
|
|
27
|
+
if evalf:
|
|
28
|
+
processed = submatrix.applyfunc(lambda x: x.evalf(prec))
|
|
29
|
+
else:
|
|
30
|
+
processed = submatrix
|
|
31
|
+
|
|
32
|
+
# Display matrix
|
|
33
|
+
ipy.display(ipy.Math(f"{name} = {latex(processed)}"))
|
|
34
|
+
|
|
35
|
+
m = matrix.rows
|
|
36
|
+
n = matrix.cols
|
|
37
|
+
# Show truncation message if applicable
|
|
38
|
+
if matrix.rows > r or matrix.cols > c:
|
|
39
|
+
ipy.display(ipy.Math(rf"\text{{... Truncated to first {r}x{c} out of {m}x{n} matrix}}"))
|