mirror of
https://github.com/3b1b/videos.git
synced 2025-08-31 21:58:59 +00:00
370 lines
12 KiB
Python
370 lines
12 KiB
Python
from manim_imports_ext import *
|
|
from _2021.matrix_exp import *
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
def get_intensity_colors(values, cmap_name='viridis'):
|
|
"""
|
|
Convert a value between 0 and 1 to a color using a perceptually uniform colormap.
|
|
|
|
Args:
|
|
value (np.array): Array of values between 0 and 1
|
|
cmap_name (str): Name of colormap (default: 'viridis')
|
|
|
|
Returns:
|
|
np.array: RGB color values as (r,g,b) where each component is between 0 and 1
|
|
"""
|
|
cmap = plt.get_cmap(cmap_name)
|
|
return cmap(values)[:, :3] # Only return RGB, exclude alpha
|
|
|
|
|
|
class TexScratchPad(InteractiveScene):
|
|
def construct(self):
|
|
# ODE
|
|
tex = Tex(R"""
|
|
\left[\begin{array}{c} x'(t) \\ y'(t) \end{array}\right] =
|
|
\left[\begin{array}{cc} 1 & 2 \\ 3 & 1 \end{array}\right]
|
|
\left[\begin{array}{c} x(t) \\ y(t) \end{array}\right]
|
|
""")
|
|
|
|
# ODE in new coordinates
|
|
tex = Tex(
|
|
R"""
|
|
\left[\begin{array}{c} \tilde{x}'(t) \\ \tilde{y}'(t) \end{array}\right] =
|
|
\left[\begin{array}{cc} \lambda_1 & 0 \\ 0 & \lambda_2 \end{array}\right]
|
|
\left[\begin{array}{c} \tilde{x}(t) \\ \tilde{y}(t) \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
|
|
# Solution to ODE in new coordinates
|
|
tex = Tex(
|
|
R"""
|
|
\tilde{x}(t) = \tilde{x}_0 e^{\lambda_1 t} \\
|
|
\tilde{y}(t) = \tilde{y}_0 e^{\lambda_2 t} \\
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(tex)
|
|
|
|
# Meaning of the alternate coordinates
|
|
tex = Tex(
|
|
R"""
|
|
x \hat{\textbf{\i}} + y \hat{\textbf{\j}} =
|
|
\tilde{x} \vec{\textbf{v}}_1 + \tilde{y} \vec{\textbf{v}}_2
|
|
""",
|
|
t2c={
|
|
R"\lambda_1": TEAL,
|
|
R"\lambda_2": YELLOW,
|
|
R"\vec{\textbf{v}}_1": TEAL,
|
|
R"\vec{\textbf{v}}_2": YELLOW,
|
|
R"\hat{\textbf{\i}}": GREEN,
|
|
R"\hat{\textbf{\j}}": RED,
|
|
}
|
|
)
|
|
self.add(tex)
|
|
|
|
# Change of basis matrix
|
|
mat_tex = Tex(
|
|
R"""
|
|
\left[\begin{array}{cc} a & b \\ c & d \end{array}\right]
|
|
""",
|
|
)
|
|
old_cols = VGroup(
|
|
mat_tex[1:4:2],
|
|
mat_tex[2:5:2],
|
|
)
|
|
old_cols.set_opacity(0)
|
|
new_cols = VGroup(
|
|
Tex(R"\vec{\textbf{v}}_1").set_color(TEAL),
|
|
Tex(R"\vec{\textbf{v}}_2").set_color(YELLOW),
|
|
)
|
|
for new_col, old_col in zip(new_cols, old_cols):
|
|
lines = Line(UP, DOWN).set_height(0.35).replicate(2)
|
|
lines[0].next_to(new_col, UP, SMALL_BUFF)
|
|
lines[1].next_to(new_col, DOWN, SMALL_BUFF)
|
|
new_col.add(lines)
|
|
new_col.match_height(mat_tex)
|
|
new_col.move_to(old_col)
|
|
new_cols[1].align_to(new_cols[0], UP)
|
|
cob_matrix = VGroup(mat_tex, new_cols)
|
|
|
|
og_matrix = Tex(
|
|
R"""
|
|
\left[\begin{array}{cc} a & b \\ c & d \end{array}\right]
|
|
""",
|
|
)
|
|
|
|
inv_cob = cob_matrix.copy()
|
|
inv_cob.add(Tex(R"-1", font_size=24).next_to(inv_cob, RIGHT, SMALL_BUFF, aligned_edge=UP))
|
|
|
|
diag_matrix = Tex(
|
|
R"""
|
|
\left[\begin{array}{cc} \lambda_1 & 0 \\ 0 & \lambda_2 \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
|
|
expr = VGroup(
|
|
inv_cob, og_matrix, cob_matrix,
|
|
Tex("="), diag_matrix
|
|
)
|
|
expr.arrange(RIGHT, buff=MED_SMALL_BUFF)
|
|
|
|
self.add(expr)
|
|
|
|
# Show powers
|
|
diag_matrix = Tex(
|
|
R"""
|
|
\left[\begin{array}{cc} \lambda_1 & 0 \\ 0 & \lambda_2 \end{array}\right]^n =
|
|
\left[\begin{array}{cc} \lambda_1^n & 0 \\ 0 & \lambda_2^n \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(diag_matrix)
|
|
|
|
# Solve
|
|
diag_matrix = Tex(
|
|
R"""
|
|
\left[\begin{array}{cc} 0 & 1 \\ 1 & 1 \end{array}\right]^n =
|
|
\left[\begin{array}{cc} \lambda_1^n & 0 \\ 0 & \lambda_2^n \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(diag_matrix)
|
|
|
|
# Write eigenvectors
|
|
eigen1_equation = Tex(
|
|
R"""
|
|
\left[\begin{array}{cc} 0 & 1 \\ 1 & 1 \end{array}\right]
|
|
\left[\begin{array}{c} 1 \\ \lambda_1 \end{array}\right] =
|
|
\left[\begin{array}{c} \lambda_1 \\ \lambda_1 + 1 \end{array}\right] =
|
|
\left[\begin{array}{c} \lambda_1 \\ \lambda_1^2 \end{array}\right] =
|
|
\lambda_1 \left[\begin{array}{c} 1 \\ \lambda_1 \end{array}\right] =
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
eigen1 = Tex(
|
|
R"""
|
|
\vec{\textbf{v}}_1 = \left[\begin{array}{c} 1 \\ \lambda_1 \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
eigen2 = Tex(
|
|
R"""
|
|
\vec{\textbf{v}}_2 = \left[\begin{array}{c} 1 \\ \lambda_2 \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(eigen2)
|
|
|
|
# Eigen matrix
|
|
fib_cob = Tex(
|
|
R"""
|
|
S = \left[\begin{array}{cc} 1 & 1 \\ \lambda_1 & \lambda_2 \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(fib_cob)
|
|
|
|
# Fibonacci expression
|
|
fib_expr = Tex(
|
|
R"""
|
|
A^n \left[\begin{array}{c} 0 \\ 1 \end{array}\right] =
|
|
\left[\begin{array}{c} F_n \\ F_{n + 1} \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(fib_expr)
|
|
|
|
# Fib formula
|
|
fib_formula = Tex(
|
|
R"""
|
|
F_n = \frac{\lambda_1^n - \lambda_2^n}{\lambda_1 - \lambda_2}
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(fib_formula)
|
|
|
|
# Translation
|
|
diag_matrix = Tex(
|
|
R"""
|
|
S \left[\begin{array}{cc} \lambda_1^n & 0 \\ 0 & \lambda_2^n \end{array}\right] S^{-1} =
|
|
A^n
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(diag_matrix)
|
|
|
|
# Matrix
|
|
tex = Tex(R"""
|
|
\left[\begin{array}{cc} 3 & 1 \\ 0 & 2 \end{array}\right]
|
|
""")
|
|
tex[1:4:2].set_color(GREEN)
|
|
tex[2:5:2].set_color(RED)
|
|
self.add(tex)
|
|
|
|
# Basis
|
|
basis_syms = VGroup(
|
|
Tex(R"\hat{\textbf{\i}}").set_color(GREEN),
|
|
Tex(R"\hat{\textbf{\j}}").set_color(RED),
|
|
)
|
|
basis_syms.arrange(RIGHT)
|
|
self.add(basis_syms)
|
|
|
|
# Equation
|
|
tex = Tex(R"""
|
|
\left[\begin{array}{c} x \\ y \end{array}\right] =
|
|
\left[\begin{array}{c} 1 \\ 1 + \sqrt{5} \end{array}\right]
|
|
""", t2c={R"\lambda": TEAL})
|
|
self.add(tex)
|
|
|
|
# List bases
|
|
tex = Tex(R"""
|
|
\left[\begin{array}{c} 1 \\ 0 \end{array}\right],
|
|
\left[\begin{array}{c} 0 \\ 1 \end{array}\right]
|
|
""")
|
|
self.add(tex)
|
|
|
|
# Diagonal matrix
|
|
tex = Tex(
|
|
R"""
|
|
\left[\begin{array}{cc} \lambda_1 & 0 \\ 0 & \lambda_2 \end{array}\right]
|
|
""",
|
|
t2c={R"\lambda_1": TEAL, R"\lambda_2": YELLOW}
|
|
)
|
|
self.add(tex)
|
|
|
|
|
|
def get_vector_field_and_stream_lines(
|
|
func, coordinate_system,
|
|
# Vector config
|
|
vector_stroke_width=5,
|
|
vector_opacity=0.5,
|
|
density=4,
|
|
# Streamline config
|
|
sample_freq=5,
|
|
n_samples_per_line=10,
|
|
solution_time=1,
|
|
arc_len=3, # Does nothing
|
|
time_width=0.5,
|
|
line_color=WHITE,
|
|
line_width=3,
|
|
line_opacity=1.0,
|
|
):
|
|
# Vector field
|
|
vector_field = VectorField(
|
|
func, axes,
|
|
density=density,
|
|
stroke_width=vector_stroke_width,
|
|
stroke_opacity=vector_opacity,
|
|
)
|
|
|
|
# Streamlines
|
|
stream_lines = StreamLines(
|
|
func, axes,
|
|
density=sample_freq,
|
|
n_samples_per_line=n_samples_per_line,
|
|
solution_time=solution_time,
|
|
magnitude_range=vector_field.magnitude_range,
|
|
color_by_magnitude=False,
|
|
stroke_color=line_color,
|
|
stroke_width=line_width,
|
|
stroke_opacity=line_opacity,
|
|
)
|
|
animated_lines = AnimatedStreamLines(
|
|
stream_lines,
|
|
line_anim_config=dict(time_width=time_width),
|
|
rate_multiple=0.25,
|
|
)
|
|
|
|
return vector_field, animated_lines
|
|
|
|
|
|
class VectorFieldSolution(InteractiveScene):
|
|
def construct(self):
|
|
# Add axes
|
|
mat = np.array([[1, 2], [3, 1]])
|
|
# mat = np.array([[2, 0], [0, -1]])
|
|
axes = NumberPlane((-4, 4), (-2, 2), faded_line_ratio=1)
|
|
axes.set_height(FRAME_HEIGHT)
|
|
axes.background_lines.set_stroke(BLUE, 1)
|
|
axes.faded_lines.set_stroke(BLUE, 0.5, 0.5)
|
|
axes.add_coordinate_labels(font_size=36)
|
|
|
|
def func(v):
|
|
return 0.5 * np.dot(v, mat.T)
|
|
|
|
self.add(axes)
|
|
|
|
# Calculate eigen vectors
|
|
eigenvalues, eigenvectors = np.linalg.eig(mat)
|
|
eigenlines = VGroup(
|
|
Line(-v, v).set_length(10)
|
|
for v in eigenvectors.T
|
|
)
|
|
eigenlines.set_stroke(TEAL, 5)
|
|
|
|
# Show the flow
|
|
config = dict()
|
|
# config = dict(step_multiple=0.5, vector_stroke_width=8)
|
|
vector_field, animated_lines = get_vector_field_and_stream_lines(func, axes, **config)
|
|
|
|
self.add(vector_field, animated_lines)
|
|
vector_field.set_stroke(opacity=1)
|
|
self.play(vector_field.animate.set_stroke(opacity=0.5))
|
|
self.wait(10)
|
|
self.play(ShowCreation(eigenlines))
|
|
self.wait(10)
|
|
|
|
|
|
class Transformation(InteractiveScene):
|
|
def construct(self):
|
|
# Apply matrix
|
|
mat = np.array([[1, 2], [3, 1]])
|
|
|
|
ghost_plane = NumberPlane(faded_line_ratio=0)
|
|
ghost_plane.set_stroke(GREY, 1)
|
|
plane = self.get_plane()
|
|
basis = VGroup(
|
|
self.get_updated_vector((1, 0), plane, GREEN),
|
|
self.get_updated_vector((0, 1), plane, RED),
|
|
)
|
|
|
|
self.add(ghost_plane, plane, basis)
|
|
self.play(
|
|
plane.animate.apply_matrix(mat),
|
|
run_time=4
|
|
)
|
|
self.wait()
|
|
|
|
# Fade out
|
|
self.play(FadeOut(VGroup(ghost_plane, plane, basis)))
|
|
|
|
# Show matrix in new coordinate system
|
|
eigenvalues, eigenvectors = np.linalg.eig(mat)
|
|
eigenplane = self.get_plane()
|
|
eigenplane.apply_matrix(eigenvectors, about_point=ORIGIN)
|
|
|
|
eigenbasis = VGroup(
|
|
self.get_updated_vector((1, 0), eigenplane, TEAL),
|
|
self.get_updated_vector((0, 1), eigenplane, YELLOW),
|
|
)
|
|
|
|
self.add(eigenplane, eigenbasis)
|
|
self.play(
|
|
eigenplane.animate.apply_matrix(mat),
|
|
run_time=4
|
|
)
|
|
self.wait()
|
|
|
|
def get_plane(self, x_range=(-16, 16), y_range=(-8, 8)):
|
|
return NumberPlane(x_range, y_range, faded_line_ratio=1)
|
|
|
|
def get_updated_vector(self, coords, coord_system, color=YELLOW, thickness=4, **kwargs):
|
|
vect = Vector(RIGHT, fill_color=color, thickness=thickness, **kwargs)
|
|
vect.add_updater(lambda m: m.put_start_and_end_on(
|
|
coord_system.get_origin(),
|
|
coord_system.c2p(*coords),
|
|
))
|
|
return vect
|