Repository navigation
Expand file tree
/
Copy patheigenAnimation.py
More file actions
130 lines (105 loc) · 4.96 KB
/
Copy patheigenAnimation.py
File metadata and controls
130 lines (105 loc) · 4.96 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
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.widgets import Slider
# Description:
# This program creates an eigenvalue visualizer for n x n matrices.
# Something that can be overlooked in a linear algebra class is the instability of
# eigenvalues based on matrix coefficients. With this visualizer, one can show how small
# perturbations in matrices (especially those generated in numerical PDEs) can yield
# wildly different eigenvalues.
# Importantly, random matrix theory will imply that, for matrices with entries generated by
# uniform random variables, the eigenvalues will follow a circle law via random matrix theory.
# Inputs:
# A - the matrix we want to find the eigenvalues of / visualize
# grid_view - boolean to turn on matrix changing grid. False by default.
# range - list [x1,x2,y1,y2] determining range of eval plot. Chosen by program by default.
# Outputs:
# An interactive eigenvalue plot.
def animation(A,grid_view=False,Range=[0,0,0,0]):
# get the eigenvalues
evals = np.linalg.eigvals(A)
# eigenvalue scatter plot if there is no grid view
if not grid_view:
fig, ax = plt.subplots()
ax.scatter(evals.real, evals.imag, color="blue", s=200/np.log(4*len(A)))
if (Range[0]<Range[1]) and (Range[2] < Range[3]): # range is of the form [xmin,xmax,ymin,ymax]
ax.set_xlim(Range[0],Range[1])
ax.set_ylim(Range[2],Range[3])
ax.axhline(0, color="gray", lw=1)
ax.axvline(0, color="gray", lw=1)
ax.set_title("Eigenvalues")
ax.set_xlabel("Real")
ax.set_ylabel("Imag")
else:
# make two subplots (left evals, right matrix)
fig, (ax_left, ax_right) = plt.subplots(1, 2, figsize=(10, 5))
plt.subplots_adjust(bottom=0.25, wspace=0.4)
# eval scatterplot
scat = ax_left.scatter(evals.real, evals.imag, color="blue", s=200/np.log(4*len(A)))
# optional range arguments
if (Range[0]<Range[1]) and (Range[2] < Range[3]):
ax_left.set_xlim(Range[0],Range[1])
ax_left.set_ylim(Range[2],Range[3])
ax_left.axhline(0, color="gray", lw=1)
ax_left.axvline(0, color="gray", lw=1)
ax_left.set_title("Eigenvalues")
ax_left.set_xlabel("Real")
ax_left.set_ylabel("Imag")
# right matrix colormap plot
cax = ax_right.matshow(A, cmap="coolwarm",vmin=-5,vmax=5)
fig.colorbar(cax, ax=ax_right)
ax_right.set_title("Matrix A")
# Add text annotations to show values
texts = []
for row in range(A.shape[0]):
row_texts = []
for col in range(A.shape[1]):
txt = ax_right.text(col, row, f"{A[row,col]:.2f}",
ha="center", va="center", color="black")
row_texts.append(txt)
texts.append(row_texts)
# initial highlighted values
i, j = 0,0
initial_value = A[i,j]
# Add highlight rectangle
highlight = patches.Rectangle((j-0.5, i-0.5), 1, 1,
edgecolor="yellow", facecolor="none", lw=2)
ax_right.add_patch(highlight)
# set up sliders
ax_slider = plt.axes([0.55, 0.1, 0.35, 0.03]) # x, y, width, height
slider = Slider(ax_slider, f"A[{i},{j}]", -5.0, 5.0, valinit=initial_value)
# define the update function, which changes with new slider values
def update(val):
A[i, j] = slider.val
eigvals = np.linalg.eigvals(A)
# Update eigenvalue scatter plot
scat.set_offsets(np.c_[eigvals.real, eigvals.imag])
# Auto-update plot limits
margin = 0.5
ax_left.set_xlim(eigvals.real.min() - margin, eigvals.real.max() + margin)
ax_left.set_ylim(eigvals.imag.min() - margin, eigvals.imag.max() + margin)
# Update matrix heatmap
cax.set_data(A)
#cax.set_clim(vmin=A.min(), vmax=A.max()) # auto-adjust color scale# Update matrix heatmap
# Update matrix text
for row in range(A.shape[0]):
for col in range(A.shape[1]):
texts[row][col].set_text(f"{A[row,col]:.2f}")
fig.canvas.draw_idle()
slider.on_changed(update)
# update matrix value by clicking on an entry
def on_click(event):
nonlocal i, j
if event.inaxes == ax_right:
j = int(round(event.xdata))
i = int(round(event.ydata))
if 0 <= i < A.shape[0] and 0 <= j < A.shape[1]:
# Update slider label and value
slider.label.set_text(f"A[{i},{j}]")
slider.set_val(A[i, j]) # triggers update
# Move highlight rectangle
highlight.set_xy((j-0.5, i-0.5))
fig.canvas.draw_idle()
fig.canvas.mpl_connect("button_press_event", on_click)
plt.show()