mirror of
https://github.com/3b1b/videos.git
synced 2025-08-05 16:48:47 +00:00
Beginning matrix_exp project
This commit is contained in:
parent
bbce93e3d2
commit
fbbbf6fa69
4 changed files with 308 additions and 11 deletions
297
_2021/matrix_exp.py
Normal file
297
_2021/matrix_exp.py
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
from manim_imports_ext import *
|
||||||
|
|
||||||
|
|
||||||
|
def get_integer_matrix_exponential(matrix):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_vector_field_and_stream_lines(func, coordinate_system,
|
||||||
|
magnitude_range=(0.5, 4),
|
||||||
|
vector_opacity=0.75,
|
||||||
|
vector_thickness=0.03,
|
||||||
|
color_by_magnitude=True,
|
||||||
|
line_color=GREY_A,
|
||||||
|
line_width=3,
|
||||||
|
sample_freq=3,
|
||||||
|
n_samples_per_line=8,
|
||||||
|
arc_len=3,
|
||||||
|
time_width=0.5,
|
||||||
|
):
|
||||||
|
vector_field = VectorField(
|
||||||
|
func, coordinate_system,
|
||||||
|
magnitude_range=magnitude_range,
|
||||||
|
vector_config={
|
||||||
|
"fill_opacity": vector_opacity,
|
||||||
|
"thickness": vector_thickness,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
stream_lines = StreamLines(
|
||||||
|
func, coordinate_system,
|
||||||
|
step_multiple=1.0 / sample_freq,
|
||||||
|
n_samples_per_line=n_samples_per_line,
|
||||||
|
arc_len=arc_len,
|
||||||
|
magnitude_range=magnitude_range,
|
||||||
|
color_by_magnitude=color_by_magnitude,
|
||||||
|
stroke_color=line_color,
|
||||||
|
stroke_width=line_width,
|
||||||
|
)
|
||||||
|
animated_lines = AnimatedStreamLines(
|
||||||
|
stream_lines,
|
||||||
|
line_anim_config={
|
||||||
|
"time_width": time_width,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return vector_field, animated_lines
|
||||||
|
|
||||||
|
|
||||||
|
# Scenes
|
||||||
|
|
||||||
|
class ShowConfusionAtMatrixExponenent(Scene):
|
||||||
|
def construct(self):
|
||||||
|
# Sticking a matrix in an exponent like this might strike you as...
|
||||||
|
base = Tex("e")
|
||||||
|
base.set_height(1.0)
|
||||||
|
matrix = IntegerMatrix(
|
||||||
|
[[3, 1, 4],
|
||||||
|
[1, 5, 9],
|
||||||
|
[2, 6, 5]],
|
||||||
|
)
|
||||||
|
matrix.move_to(base.get_corner(UR), DL)
|
||||||
|
matrix_exp = VGroup(base, matrix)
|
||||||
|
matrix_exp.set_height(2)
|
||||||
|
matrix_exp.to_corner(UL)
|
||||||
|
matrix_exp.shift(3 * RIGHT)
|
||||||
|
|
||||||
|
randy = Randolph()
|
||||||
|
randy.set_height(2)
|
||||||
|
randy.to_corner(DL)
|
||||||
|
|
||||||
|
matrix.save_state()
|
||||||
|
matrix.center()
|
||||||
|
matrix.set_height(2.5)
|
||||||
|
|
||||||
|
self.add(randy)
|
||||||
|
self.play(
|
||||||
|
randy.animate.change("pondering", matrix),
|
||||||
|
Write(matrix.get_brackets()),
|
||||||
|
ShowIncreasingSubsets(matrix.get_entries()),
|
||||||
|
)
|
||||||
|
self.play(Blink(randy))
|
||||||
|
self.play(
|
||||||
|
matrix.animate.restore(),
|
||||||
|
Write(base),
|
||||||
|
randy.animate.change("erm", base),
|
||||||
|
)
|
||||||
|
self.play(Blink(randy))
|
||||||
|
|
||||||
|
# ...odd, to say the least.
|
||||||
|
rhs = Tex("= e \\cdot e \\dots e \\cdot e")
|
||||||
|
rhs.set_height(0.75 * base.get_height())
|
||||||
|
rhs.next_to(matrix_exp, RIGHT)
|
||||||
|
rhs.align_to(base, DOWN)
|
||||||
|
brace = Brace(rhs[0][1:], DOWN)
|
||||||
|
matrix_copy = matrix.copy()
|
||||||
|
matrix_copy.scale(0.5)
|
||||||
|
brace_label = VGroup(
|
||||||
|
matrix.copy().scale(0.5),
|
||||||
|
Text("times?")
|
||||||
|
)
|
||||||
|
brace_label.arrange(RIGHT)
|
||||||
|
brace_label.next_to(brace, DOWN, SMALL_BUFF)
|
||||||
|
|
||||||
|
bubble = randy.get_bubble(
|
||||||
|
TexText("I'm sorry,\\\\what?!").scale(0.75),
|
||||||
|
height=2,
|
||||||
|
width=3,
|
||||||
|
bubble_class=SpeechBubble,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
TransformMatchingParts(
|
||||||
|
base.copy(), rhs,
|
||||||
|
path_arc=10 * DEGREES,
|
||||||
|
lag_ratio=0.01,
|
||||||
|
),
|
||||||
|
GrowFromCenter(brace),
|
||||||
|
ReplacementTransform(
|
||||||
|
matrix.copy(), brace_label[0],
|
||||||
|
path_arc=30 * DEGREES,
|
||||||
|
run_time=2,
|
||||||
|
rate_func=squish_rate_func(smooth, 0.3, 1),
|
||||||
|
),
|
||||||
|
Write(
|
||||||
|
brace_label[1],
|
||||||
|
run_time=2,
|
||||||
|
rate_func=squish_rate_func(smooth, 0.5, 1),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
randy.animate.change("angry", rhs),
|
||||||
|
ShowCreation(bubble),
|
||||||
|
Write(bubble.content),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
false_equation = VGroup(
|
||||||
|
matrix_exp, rhs, brace, brace_label
|
||||||
|
)
|
||||||
|
|
||||||
|
# The short version of this video would be to say the notation is misleading,
|
||||||
|
# having little to do with the number e being multiplying it by itself.
|
||||||
|
morty = Mortimer()
|
||||||
|
morty.match_height(randy)
|
||||||
|
morty.to_corner(DR)
|
||||||
|
false_equation.generate_target()
|
||||||
|
false_equation.target.scale(0.5)
|
||||||
|
false_equation.target.next_to(morty, UL)
|
||||||
|
fe_rect = SurroundingRectangle(false_equation.target)
|
||||||
|
fe_rect.set_color(GREY_BROWN)
|
||||||
|
cross = Cross(false_equation.target[1])
|
||||||
|
cross.set_stroke(RED, 5)
|
||||||
|
nonsense = Text("This would be nonsense")
|
||||||
|
nonsense.match_width(fe_rect)
|
||||||
|
nonsense.next_to(fe_rect, UP)
|
||||||
|
nonsense.set_color(RED)
|
||||||
|
|
||||||
|
randy.bubble = bubble
|
||||||
|
self.play(
|
||||||
|
MoveToTarget(false_equation),
|
||||||
|
RemovePiCreatureBubble(randy, target_mode="hesitant"),
|
||||||
|
morty.animate.change("raise_right_hand"),
|
||||||
|
ShowCreation(fe_rect),
|
||||||
|
VFadeIn(morty),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(cross),
|
||||||
|
FadeIn(nonsense),
|
||||||
|
)
|
||||||
|
self.play(Blink(morty))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Writing e with a matrix up top is shorthand for plugging in the matrix to a certain infinite polynomial.
|
||||||
|
real_equation = Tex(
|
||||||
|
"e^X := X^0 + X^1 + \\frac{1}{2} X^2 + \\frac{1}{6} X^3 + \\cdots + \\frac{1}{n!} X^n + \\cdots",
|
||||||
|
isolate=["X"]
|
||||||
|
)
|
||||||
|
xs = real_equation.get_parts_by_tex("X")
|
||||||
|
xs.set_color(TEAL)
|
||||||
|
real_equation.set_width(FRAME_WIDTH - 2.0)
|
||||||
|
real_equation.to_edge(UP)
|
||||||
|
|
||||||
|
by_definition = Text("(by definition)", font_size=24)
|
||||||
|
by_definition.set_fill(GREY_B)
|
||||||
|
bd_arrow = Vector(0.5 * UP)
|
||||||
|
bd_arrow.match_color(by_definition)
|
||||||
|
bd_arrow.next_to(real_equation.get_part_by_tex("="), DOWN, SMALL_BUFF)
|
||||||
|
by_definition.next_to(bd_arrow, DOWN)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(base, real_equation[0]),
|
||||||
|
FadeTransform(matrix.copy(), real_equation[1]),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
GrowArrow(bd_arrow),
|
||||||
|
FadeIn(by_definition, DOWN),
|
||||||
|
Write(real_equation[2]),
|
||||||
|
FadeTransformPieces(xs[:1].copy(), xs[1:], path_arc=20 * DEGREES),
|
||||||
|
LaggedStart(*(
|
||||||
|
FadeIn(part)
|
||||||
|
for part in real_equation[4:]
|
||||||
|
if part not in xs
|
||||||
|
))
|
||||||
|
)
|
||||||
|
self.add(real_equation)
|
||||||
|
self.play(
|
||||||
|
randy.animate.change("pondering", real_equation),
|
||||||
|
morty.animate.change("pondering", real_equation),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
# Still, that’s a complicated thing to do, which takes some explaining in its own right,
|
||||||
|
# and without context this definition does very little to explain how to think about this operation.
|
||||||
|
rhs_tex = "X^0 + X^1 + \\frac{1}{2} X^2 + \\frac{1}{6} X^3 + \\cdots + \\frac{1}{n!} X^n + \\cdots"
|
||||||
|
mat_tex = "\\left[ \\begin{array}{ccc} 3 & 1 & 4 \\\\ 1 & 5 & 9 \\\\ 2 & 6 & 5 \\end{array} \\right]"
|
||||||
|
ex_rhs = Tex(
|
||||||
|
rhs_tex.replace("X", mat_tex),
|
||||||
|
tex_to_color_map={mat_tex: TEAL},
|
||||||
|
)
|
||||||
|
ex_rhs.scale(0.5)
|
||||||
|
|
||||||
|
ex_eq = VGroup(matrix_exp.copy(), Tex("="), ex_rhs)
|
||||||
|
ex_eq[0][1].set_color(TEAL)
|
||||||
|
ex_eq.arrange(RIGHT)
|
||||||
|
ex_rhs.align_to(ex_eq[0], DOWN)
|
||||||
|
ex_eq = VGroup(ex_eq[0], ex_eq[1], *ex_rhs)
|
||||||
|
ex_eq[1:].shift(0.1 * DOWN)
|
||||||
|
ex_eq.next_to(real_equation, DOWN, buff=2, aligned_edge=DOWN)
|
||||||
|
|
||||||
|
false_group = VGroup(false_equation, fe_rect, cross, nonsense)
|
||||||
|
complicated = Text("Rather complicated!")
|
||||||
|
complicated.set_color(YELLOW)
|
||||||
|
complicated.next_to(ex_eq, DOWN, LARGE_BUFF)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
TransformFromCopy(matrix_exp, ex_eq[0]),
|
||||||
|
FadeOut(false_group, DOWN),
|
||||||
|
FadeTransformPieces(
|
||||||
|
real_equation.copy()[2:], ex_eq[1:], run_time=2,
|
||||||
|
),
|
||||||
|
randy.animate.change("hesitant"),
|
||||||
|
morty.animate.change("raise_right_hand"),
|
||||||
|
)
|
||||||
|
self.play(Blink(randy))
|
||||||
|
self.play(
|
||||||
|
FadeIn(complicated, shift=DOWN),
|
||||||
|
randy.animate.change("confused"),
|
||||||
|
morty.animate.change("hesitant"),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
|
class CircularPhaseFlow(Scene):
|
||||||
|
def construct(self):
|
||||||
|
plane = NumberPlane(
|
||||||
|
x_range=[-4, 4],
|
||||||
|
y_range=[-2, 2],
|
||||||
|
height=8,
|
||||||
|
width=16,
|
||||||
|
)
|
||||||
|
plane.add_coordinate_labels()
|
||||||
|
|
||||||
|
vector_field, animated_lines = get_vector_field_and_stream_lines(
|
||||||
|
self.func, plane
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add(plane)
|
||||||
|
self.add(vector_field)
|
||||||
|
self.add(animated_lines)
|
||||||
|
|
||||||
|
self.wait(10)
|
||||||
|
|
||||||
|
#
|
||||||
|
self.embed()
|
||||||
|
|
||||||
|
def func(self, x, y):
|
||||||
|
return (-y, x)
|
||||||
|
|
||||||
|
def get_label(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class HyperbolicPhaseFlow(CircularPhaseFlow):
|
||||||
|
def func(self, x, y):
|
||||||
|
return (x, -y)
|
||||||
|
|
||||||
|
|
||||||
|
class WhyWedCare(Scene):
|
||||||
|
def construct(self):
|
||||||
|
pass
|
||||||
|
# ...how to think about this operation, or more importantly why we’d care.
|
||||||
|
|
||||||
|
# So if it’s alright with you I think we should hold off explaining this definition, or justifying the
|
||||||
|
# abuse of notation, until it’s a bit clearer what problems it helps us to solve.
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
self.embed()
|
|
@ -8,7 +8,7 @@ from manimlib.mobject.mobject import Mobject
|
||||||
from manimlib.mobject.geometry import Circle
|
from manimlib.mobject.geometry import Circle
|
||||||
from manimlib.mobject.svg.drawings import ThoughtBubble
|
from manimlib.mobject.svg.drawings import ThoughtBubble
|
||||||
from manimlib.mobject.svg.svg_mobject import SVGMobject
|
from manimlib.mobject.svg.svg_mobject import SVGMobject
|
||||||
from manimlib.mobject.svg.tex_mobject import TexText
|
from manimlib.mobject.svg.text_mobject import Text
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.config_ops import digest_config
|
from manimlib.utils.config_ops import digest_config
|
||||||
|
@ -224,14 +224,14 @@ class PiCreature(SVGMobject):
|
||||||
self.to_corner(DOWN + LEFT, **kwargs)
|
self.to_corner(DOWN + LEFT, **kwargs)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_bubble(self, *content, **kwargs):
|
def get_bubble(self, content, **kwargs):
|
||||||
bubble_class = kwargs.get("bubble_class", ThoughtBubble)
|
bubble_class = kwargs.get("bubble_class", ThoughtBubble)
|
||||||
bubble = bubble_class(**kwargs)
|
bubble = bubble_class(**kwargs)
|
||||||
if len(content) > 0:
|
if len(content) > 0:
|
||||||
if isinstance(content[0], str):
|
if isinstance(content[0], str):
|
||||||
content_mob = TexText(*content)
|
content_mob = Text(content)
|
||||||
else:
|
else:
|
||||||
content_mob = content[0]
|
content_mob = content
|
||||||
bubble.add_content(content_mob)
|
bubble.add_content(content_mob)
|
||||||
if "height" not in kwargs and "width" not in kwargs:
|
if "height" not in kwargs and "width" not in kwargs:
|
||||||
bubble.resize_to_content()
|
bubble.resize_to_content()
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
directories:
|
directories:
|
||||||
mirror_module_path: True
|
mirror_module_path: True
|
||||||
output: "/Users/grant/Dropbox/3Blue1Brown Team Folder/videos"
|
output: "/Users/grant/Dropbox/3Blue1Brown/videos"
|
||||||
raster_images: "/Users/grant/Dropbox/3Blue1Brown Team Folder/images/raster"
|
raster_images: "/Users/grant/Dropbox/3Blue1Brown/images/raster"
|
||||||
vector_images: "/Users/grant/Dropbox/3Blue1Brown Team Folder/images/vector"
|
vector_images: "/Users/grant/Dropbox/3Blue1Brown/images/vector"
|
||||||
pi_creature_images: "/Users/grant/Dropbox/3Blue1Brown Team Folder/images/pi_creature"
|
pi_creature_images: "/Users/grant/Dropbox/3Blue1Brown/images/pi_creature"
|
||||||
sounds: "/Users/grant/Dropbox/3Blue1Brown Team Folder/sounds"
|
sounds: "/Users/grant/Dropbox/3Blue1Brown/sounds"
|
||||||
data: "/Users/grant/Dropbox/3Blue1Brown Team Folder/data"
|
data: "/Users/grant/Dropbox/3Blue1Brown/data"
|
||||||
temporary_storage: ""
|
temporary_storage: ""
|
||||||
universal_import_line: "from manim_imports_ext import *"
|
universal_import_line: "from manim_imports_ext import *"
|
||||||
# tex:
|
# tex:
|
||||||
|
@ -17,5 +17,5 @@ style:
|
||||||
background_color: "#000000"
|
background_color: "#000000"
|
||||||
camera_qualities:
|
camera_qualities:
|
||||||
default_quality: "ultra_high"
|
default_quality: "ultra_high"
|
||||||
window_position: UL
|
window_position: UR
|
||||||
window_monitor: 1
|
window_monitor: 1
|
BIN
program.prof
BIN
program.prof
Binary file not shown.
Loading…
Add table
Reference in a new issue