3b1b-videos/_2021/quick_eigen.py
2024-10-13 21:05:24 -05:00

2819 lines
91 KiB
Python

from manim_imports_ext import *
# Colors
COL_COLORS = [MAROON_B, MAROON_C]
EIGEN_COLORS = [TEAL_A, TEAL_D]
MEAN_COLOR = BLUE_B
PROD_COLOR = BLUE_D
def det_path_anim(matrix, run_time=2):
path = VMobject()
path.set_points_smoothly([
matrix.get_corner(UL),
*[
matrix.get_entries()[i].get_center()
for i in [0, 3, 1, 2]
],
matrix.get_corner(DL),
])
path.set_stroke(BLUE, 3)
return VShowPassingFlash(path, time_width=1, run_time=run_time, rate_function=linear)
def get_diag_rects(matrix, color=MEAN_COLOR, off_diagonal=False):
if off_diagonal:
entries = matrix.get_entries()[1:3]
else:
entries = matrix.get_entries()[0::3]
return VGroup(*(
SurroundingRectangle(entry, buff=SMALL_BUFF, color=color)
for entry in entries
))
def get_prism(verts, depth=2):
result = VGroup()
result.add(Polygon(*verts))
zv = depth * OUT
for v1, v2 in zip(verts, [*verts[1:], verts[0]]):
result.add(Polygon(v1, v2, v2 + zv, v1 + zv))
result.add(Polygon(*verts).shift(zv))
result.set_stroke(width=0)
result.set_fill(GREY, 1)
result.set_gloss(1)
for mob in list(result):
m2 = mob.copy()
m2.reverse_points()
result.add(m2)
result.apply_depth_test()
return result
def get_mod_mat(matrix, h_buff=1.3, v_buff=0.8, t2c={}):
t2c["\\lambda"] = EIGEN_COLORS[1]
mod_mat = Matrix(
[
[matrix[0][0] + " - \\lambda", matrix[0][1]],
[matrix[1][0], matrix[1][1] + " - \\lambda"],
],
element_to_mobject_config={"tex_to_color_map": t2c},
h_buff=h_buff,
v_buff=v_buff,
)
return mod_mat
def get_det_mod_mat(mod_mat):
parens = OldTex("(", ")")
parens.stretch(2, 1)
parens.match_height(mod_mat)
parens[0].next_to(mod_mat, LEFT, SMALL_BUFF)
parens[1].next_to(mod_mat, RIGHT, SMALL_BUFF)
det = Text("det")
det.next_to(parens, LEFT, SMALL_BUFF)
return VGroup(det, parens, mod_mat)
def get_shadow(vmobject, width=50, n_copies=25):
shadow = VGroup()
for w in np.linspace(width, 0, n_copies):
part = vmobject.copy()
part.set_fill(opacity=0)
part.set_stroke(BLACK, width=w, opacity=1.0 / width)
shadow.add(part)
return shadow
# Scenes
class Thumbnail(Scene):
def construct(self):
grid = NumberPlane(faded_line_ratio=0)
grid.apply_matrix([[3, 1], [0, 2]])
grid.set_opacity(0.5)
self.add(grid)
mat_mob = IntegerMatrix([[3, 1], [4, 3]], h_buff=0.8)
mat_mob.set_height(3)
mat_mob.add_to_back(BackgroundRectangle(mat_mob))
mat_mob.to_edge(UP, buff=MED_SMALL_BUFF)
self.add(mat_mob)
a, b, c, d = mat_mob.get_entries()
a_to_d = d.get_center() - a.get_center()
rect = Rectangle(height=1, width=get_norm(a_to_d) + 1)
rect.round_corners()
rect.set_stroke(MEAN_COLOR, 3)
rect.rotate(angle_of_vector(a_to_d))
rect.move_to(VGroup(a, d))
rect2 = rect.copy()
rect2.set_color(PROD_COLOR)
rect2.rotate(-2 * angle_of_vector(a_to_d))
rect2.move_to(rect)
dashed_rects = VGroup(
DashedVMobject(rect.insert_n_curves(100), num_dashes=50),
DashedVMobject(rect2.insert_n_curves(100), num_dashes=50),
)
self.add(dashed_rects)
answer = OldTex(
"\\lambda_1, \\lambda_2 = {3} \\pm \\sqrt{\\,{3}^2 - {5}} = 5, 1",
tex_to_color_map={"{3}": MEAN_COLOR, "{p}": PROD_COLOR}
)
answer.add_background_rectangle()
answer.set_width(12)
answer.to_edge(DOWN)
# answer.shift(SMALL_BUFF * UP)
self.add(answer)
arrow = Arrow(mat_mob, answer, fill_color=YELLOW, thickness=0.15, buff=0.3)
arrow.set_stroke(BLACK, 30, background=True)
arrow.shift(0.2 * UP)
self.add(arrow)
return
#old
backdrop = ImageMobject("QuickEigenThumbnailBackdrop")
backdrop.set_height(FRAME_HEIGHT)
backdrop.set_opacity(0.5)
self.add(backdrop)
buff = 0.75
det = get_det_mod_mat(get_mod_mat([["3", "1"], ["4", "1"]]))
det.set_height(2.25)
det.to_corner(UR, buff=buff)
self.add(get_shadow(det), det)
not_this = OldTexText("Not\\\\this")
not_this.match_height(det)
not_this.to_corner(UL, buff=buff)
not_this.set_color(RED)
self.add(not_this)
kw = {
"tex_to_color_map": {
"\\lambda_1": EIGEN_COLORS[0],
"\\lambda_2": EIGEN_COLORS[1],
"M": YELLOW,
}
}
facts = VGroup(
OldTex("\\text{det}(M) = \\,\\, \\lambda_1 \\cdot \\lambda_2", **kw),
OldTex("\\text{tr}(M) = \\lambda_1 + \\lambda_2", **kw),
)
facts.arrange(DOWN, buff=MED_SMALL_BUFF, index_of_submobject_to_align=2)
facts.match_height(det)
facts.match_x(det)
facts.set_y(-det.get_y())
self.add(get_shadow(facts), facts)
use_these = OldTexText("Use\\\\these")
use_these.match_height(not_this)
use_these.to_corner(DL, buff=buff)
use_these.set_color(BLUE)
not_this.match_x(use_these)
self.add(use_these)
not_arrow = Arrow(not_this, det, fill_color=RED, thickness=0.1, buff=0.5)
use_arrow = Arrow(use_these, facts, fill_color=BLUE, thickness=0.1, buff=0.5)
self.add(get_shadow(not_arrow), not_arrow)
self.add(get_shadow(use_arrow), use_arrow)
## DELETE
# mp = OldTex(
# "{m} \\pm \\sqrt{\\,{m}^2 - {p}}",
# tex_to_color_map={
# "{m}": MEAN_COLOR,
# "{p}": MEAN_COLOR,
# }
# )
# for mob, u in [(det, -1), (mp, 1)]:
# mob.set_width(FRAME_WIDTH / 2 - 1)
# mob.set_x(u * FRAME_WIDTH / 4)
# mob.set_y(-1)
# v_line = DashedLine(4 * UP, 4 * DOWN)
# ex = Exmark()
# check = Checkmark()
# VGroup(ex, check).scale(5)
# ex.move_to(det).to_edge(UP, LARGE_BUFF)
# check.move_to(mp).to_edge(UP, LARGE_BUFF)
# self.add(v_line)
# self.add(det)
# self.add(mp)
# self.add(ex)
# self.add(check)
self.embed()
class Assumptions(TeacherStudentsScene):
def construct(self):
self.play(
PiCreatureSays(self.teacher, OldTexText("I'm assuming you know\\\\ what eigenvalues are.")),
self.change_students(
"erm", "happy", "tease",
look_at=ORIGIN,
),
run_time=2,
)
self.play(self.students[0].change("guilty").look(LEFT))
self.wait()
eigen_expression = OldTex("""
\\text{det}\\left( \\left[ \\begin{array}{cc}
3 - \\lambda & 1 \\\\
4 & 1 - \\lambda
\\end{array} \\right] \\right)
""")
eigen_expression.move_to(self.hold_up_spot, DOWN)
eigen_expression.to_edge(RIGHT, buff=2)
VGroup(eigen_expression[0][7], eigen_expression[0][12]).set_color(TEAL)
cross = Cross(eigen_expression)
cross.set_stroke(RED, width=(2, 5, 5, 2))
words = Text("Not this!")
words.set_color(RED)
words.next_to(cross, UP, MED_LARGE_BUFF)
self.play(
RemovePiCreatureBubble(self.teacher, target_mode="raise_right_hand"),
FadeIn(eigen_expression, UP),
self.students[1].change("hesitant"),
self.students[2].change("sassy"),
)
self.play(
ShowCreation(cross),
FadeIn(words, 0.25 * UP),
self.teacher.change("tease", cross),
self.students[1].change("pondering", cross),
self.students[2].change("hesitant", cross),
)
self.wait(3)
fade_rect = FullScreenFadeRectangle()
fade_rect.set_fill(BLACK, opacity=0.7)
self.add(fade_rect, self.students[0])
self.play(FadeIn(fade_rect))
self.play(self.students[0].change("maybe", cross))
self.play(Blink(self.students[0]))
class ExamplesStart(Scene):
def construct(self):
words = OldTexText("Examples start\\\\at", " 4:53")
words.set_width(8)
words[-1].set_color(YELLOW)
self.play(Write(words, run_time=1))
self.play(FlashAround(words[1], stroke_width=8))
self.wait()
class PreviousVideoWrapper(Scene):
def construct(self):
self.add(FullScreenRectangle())
screen = ScreenRectangle(height=6)
screen.set_fill(BLACK, 1)
screen.set_stroke(BLUE, 3)
screen.to_edge(DOWN)
im = ImageMobject("eigen_thumbnail")
im.replace(screen)
screen = Group(screen, im)
title = Text("Introduction", font_size=48)
# title.match_width(screen)
title.next_to(screen, UP, MED_LARGE_BUFF)
# screen.next_to(title, DOWN)
self.add(screen)
self.play(Write(title))
self.wait(2)
self.play(
FadeOut(title, UP),
screen.animate.set_height(7).center(),
)
self.wait()
class GoalOfRediscovery(TeacherStudentsScene):
def construct(self):
self.play(
PiCreatureSays(self.teacher, OldTexText("The goal is\\\\rediscovery")),
self.change_students(
"happy", "tease", "hooray",
run_time=1.5,
)
)
self.wait(2)
recap_words = Text("Quick recap")
recap_words.move_to(self.screen, UP)
self.play(
RemovePiCreatureBubble(self.teacher, target_mode="raise_right_hand", look_at=recap_words),
self.change_students("pondering", "hesitant", "pondering", look_at=recap_words),
GrowFromPoint(recap_words, self.teacher.get_corner(UL)),
)
self.wait(4)
class RecapWrapper(Scene):
def construct(self):
self.add(FullScreenRectangle())
screen = ScreenRectangle(height=6.75)
screen.set_stroke(BLUE_B, 2)
screen.set_fill(BLACK, 1)
screen.to_edge(DOWN, buff=0.25)
title = Text("Quick review")
title.to_edge(UP, buff=0.25)
self.add(title, screen)
class VisualizeEigenvector(Scene):
def construct(self):
plane = NumberPlane(faded_line_ratio=0)
plane.set_stroke(width=3)
coords = [-1, 1]
vector = Vector(plane.c2p(*coords), fill_color=YELLOW)
array = IntegerMatrix([[-1], [1]], v_buff=0.9)
array.scale(0.7)
array.set_color(vector.get_color())
array.add_to_back(BackgroundRectangle(array))
array.generate_target()
array.next_to(vector.get_end(), LEFT)
array.target.next_to(2 * vector.get_end(), LEFT)
two_times = OldTex("2 \\cdot")
two_times.set_stroke(BLACK, 8, background=True)
two_times.next_to(array.target, LEFT)
span_line = Line(-4 * vector.get_end(), 4 * vector.get_end())
span_line.set_stroke(YELLOW_E, 1)
matrix = [[3, 1], [0, 2]]
mat_mob = IntegerMatrix(matrix)
mat_mob.set_x(4).to_edge(UP)
mat_mob.set_column_colors(GREEN, RED)
mat_mob.add_to_back(BackgroundRectangle(mat_mob))
plane.set_stroke(background=True)
bases = VGroup(
Vector(RIGHT, fill_color=GREEN_E),
Vector(UP, fill_color=RED_E),
)
faint_plane = plane.copy()
faint_plane.set_stroke(GREY, width=1, opacity=0.5)
self.add(faint_plane, plane, bases)
self.add(vector)
self.add(mat_mob)
self.play(Write(array))
self.add(span_line, vector, array)
self.play(ShowCreation(span_line))
self.wait()
self.play(
plane.animate.apply_matrix(matrix),
bases[0].animate.put_start_and_end_on(ORIGIN, plane.c2p(3, 0)),
bases[1].animate.put_start_and_end_on(ORIGIN, plane.c2p(1, 2)),
vector.animate.scale(2, about_point=ORIGIN),
MoveToTarget(array),
GrowFromPoint(two_times, array.get_left() + SMALL_BUFF * LEFT),
run_time=3,
path_arc=0,
)
self.wait()
self.remove(mat_mob)
self.remove(array)
self.remove(two_times)
class EigenvalueEquationRearranging(Scene):
def construct(self):
v_tex = "\\vec{\\textbf{v}}"
zero_tex = "\\vec{\\textbf{0}}"
kw = {
"tex_to_color_map": {
"A": BLUE,
"\\lambda": EIGEN_COLORS[1],
v_tex: YELLOW,
"=": WHITE,
}
}
lines = VGroup(
OldTex("A", v_tex, "=", "\\lambda ", v_tex, **kw),
OldTex("A", v_tex, "=", "\\lambda ", " I ", v_tex, **kw),
OldTex("A", v_tex, "-", "\\lambda ", " I ", v_tex, "=", zero_tex, **kw),
OldTex("(A", "-", "\\lambda ", "I)", v_tex, "=", zero_tex, **kw),
OldTex("\\text{det}", "(A", "-", "\\lambda ", "I)", "=", "0", **kw),
)
for line in lines:
line.shift(-line.get_part_by_tex("=").get_center())
mat_prod_brace = Brace(lines[0][:2])
mat_prod_label = Text("Matrix product", color=BLUE, font_size=24)
mat_prod_label.next_to(mat_prod_brace, DOWN, SMALL_BUFF, aligned_edge=RIGHT)
scalar_prod_brace = Brace(lines[0][3:5])
scalar_prod_label = Text("Scalar product", color=EIGEN_COLORS[1], font_size=24)
scalar_prod_label.next_to(scalar_prod_brace, DOWN, SMALL_BUFF, aligned_edge=LEFT)
self.add(lines[0])
self.play(
LaggedStart(
GrowFromCenter(mat_prod_brace),
GrowFromCenter(scalar_prod_brace),
lag_ratio=0.3
),
LaggedStart(
FadeIn(mat_prod_label, 0.25 * DOWN),
FadeIn(scalar_prod_label, 0.25 * DOWN),
lag_ratio=0.3
)
)
self.wait()
id_brace = Brace(lines[1].get_part_by_tex("I"))
id_label = Text("Identity matrix", font_size=24)
id_label.next_to(id_brace, DOWN, SMALL_BUFF, aligned_edge=LEFT)
self.play(
TransformMatchingTex(lines[0], lines[1]),
ReplacementTransform(scalar_prod_brace, id_brace),
FadeTransform(scalar_prod_label, id_label),
VGroup(mat_prod_label, mat_prod_brace).animate.shift(lines[1].get_left() - lines[0].get_left()),
)
self.wait()
for shift_value, line in zip(it.count(1), lines[2:5]):
line.shift(shift_value * 0.75 * DOWN)
self.play(
TransformMatchingTex(lines[1].copy(), lines[2], path_arc=-45 * DEGREES),
FadeOut(VGroup(mat_prod_label, mat_prod_brace, id_brace, id_label), 0.5 * DOWN)
)
self.wait()
self.play(
TransformMatchingShapes(lines[2].copy(), lines[3]),
)
self.wait()
v_part = lines[3].get_part_by_tex(v_tex)
nz_label = Text("Non-zero vector", color=YELLOW, font_size=24)
nz_label.next_to(v_part, DR, MED_LARGE_BUFF)
arrow = Arrow(nz_label.get_corner(UL), v_part, fill_color=YELLOW, buff=0.1, thickness=0.025)
self.play(
Write(nz_label, run_time=2),
GrowArrow(arrow)
)
self.wait()
self.play(
TransformMatchingShapes(lines[3].copy(), lines[4]),
FadeOut(nz_label),
FadeOut(arrow),
)
self.wait()
class SneakierEigenVector(ExternallyAnimatedScene):
pass
class TypicalComputation(Scene):
def construct(self):
# Task
words, mat = task = VGroup(
Text("Find the eigenvalues of ", t2c={"eigenvalues": TEAL}),
IntegerMatrix([[3, 1], [4, 1]]).set_height(1),
)
task.arrange(RIGHT, buff=MED_LARGE_BUFF)
task.to_edge(UP)
self.add(task)
# Top line of the computation
det_expression = OldTex("""
\\text{det}\\left( \\left[ \\begin{array}{cc}
3 - \\lambda & 1 \\\\
4 & 1 - \\lambda
\\end{array} \\right] \\right)
""")[0]
lambdas = VGroup(det_expression[7], det_expression[12])
lambdas.set_color(TEAL)
t0, t1, t2, t3 = terms = VGroup(
det_expression[5:8],
det_expression[8:9],
det_expression[9:10],
det_expression[10:13],
).copy()
p_height = terms[0].get_height() * 1.5
for term in terms:
parens = OldTex("(", ")")
parens.set_height(p_height)
parens[0].next_to(term, LEFT, 0.5 * SMALL_BUFF)
parens[1].next_to(term, RIGHT, 0.5 * SMALL_BUFF)
term.parens = parens
term.parens.set_opacity(0)
term.add(term.parens)
eq = OldTex("=")
eq.next_to(det_expression, RIGHT)
rhs = VGroup(
t0.copy(), t3.copy(), OldTex("-"), t1.copy(), t2.copy()
)
rhs.arrange(RIGHT)
rhs.next_to(eq, RIGHT)
rhs.set_opacity(1)
VGroup(det_expression, terms, eq, rhs).next_to(task, DOWN, LARGE_BUFF)
movers = VGroup(det_expression[5], det_expression[10])
movers.save_state()
movers[0].move_to(det_expression[6])
movers[1].move_to(det_expression[11])
self.play(
TransformFromCopy(mat.get_brackets(), VGroup(*(det_expression[i] for i in [4, 13]))),
TransformFromCopy(mat.get_entries(), VGroup(*(det_expression[i] for i in [5, 8, 9, 10]))),
FadeTransform(
mat.get_brackets().copy().set_opacity(0),
VGroup(*(det_expression[i] for i in [0, 1, 2, 3, 14]))
),
run_time=1
)
self.play(
Write(VGroup(*(det_expression[i] for i in [6, 7, 11, 12]))),
Restore(movers),
)
self.wait()
self.add(det_expression)
self.play(
FadeIn(eq),
TransformFromCopy(VGroup(t0, t3), rhs[:2]),
)
self.play(
FadeIn(rhs[2]),
TransformFromCopy(VGroup(t1, t2), rhs[3:])
)
self.wait()
# Line 2
eq2 = eq.copy()
eq2.shift(DOWN)
self.add(eq2)
rhs2 = OldTex("\\left( 3 - 4\\lambda + \\lambda^2 \\right) - 4")[0]
rhs2.next_to(eq2, RIGHT)
VGroup(rhs2[4], rhs2[6]).set_color(TEAL)
top_terms = VGroup(
VGroup(rhs[0][0], rhs[0][2]),
VGroup(rhs[1][0], rhs[1][2]),
)
alt_mid = OldTex("-3\\lambda", tex_to_color_map={"\\lambda": TEAL})
alt_mid.move_to(rhs2[2:5], DL)
bottom_terms = VGroup(rhs2[1], alt_mid, rhs2[2:5], rhs2[5:8])
for pair, bt in zip(it.product(*top_terms), bottom_terms):
rects = VGroup(*(SurroundingRectangle(t, buff=SMALL_BUFF) for t in pair))
self.add(rects)
self.add(bt)
self.wait(0.5)
if bt is alt_mid:
self.remove(bt)
self.remove(rects)
self.play(
FadeIn(VGroup(rhs2[0], rhs2[8])),
FadeTransform(rhs[2:].copy(), rhs2[9:])
)
self.wait()
# Line 3
eq3 = eq2.copy().shift(DOWN)
rhs3 = OldTex("\\lambda^2 - 4 \\lambda - 1")[0]
rhs3.next_to(eq3, RIGHT)
VGroup(rhs3[0], rhs3[4]).set_color(TEAL)
kw = {"path_arc": 45 * DEGREES}
self.play(LaggedStart(
TransformFromCopy(eq2, eq3, **kw),
Transform(rhs2[1].copy(), rhs3[6].copy(), remover=True, **kw),
TransformFromCopy(rhs2[9:11], rhs3[5:], **kw),
TransformFromCopy(rhs2[2:5], rhs3[2:5], **kw),
TransformFromCopy(rhs2[6:8], rhs3[0:2], **kw),
run_time=1.5, lag_ratio=0.02,
))
self.wait()
# Characteristic polynomial
brace = Brace(rhs3, DOWN)
char_poly = VGroup(
Text("Characteristic polynomial of", font_size=30, fill_color=BLUE),
mat.copy()
)
char_poly.arrange(RIGHT)
char_poly.next_to(brace, DOWN)
char_poly.shift_onto_screen()
self.play(
GrowFromCenter(brace),
FadeIn(char_poly, DOWN),
)
self.wait()
# Roots
equals_zero = OldTex("= 0")
equals_zero.next_to(rhs3, RIGHT)
root_words = OldTex(
"\\lambda_1, \\lambda_2 \\,=\\, \\text{roots}",
tex_to_color_map={
"\\lambda_1": TEAL_C,
"\\lambda_2": TEAL_B,
"=": WHITE,
}
)
new_rhs3 = VGroup(rhs3, equals_zero)
root_words.next_to(brace, DOWN)
root_words.match_x(new_rhs3)
self.play(
FadeIn(root_words, DOWN),
FadeOut(char_poly, DOWN),
brace.animate.become(Brace(new_rhs3)),
Write(equals_zero),
)
self.wait()
# Quadratic formula
formula = OldTex("\\frac{4 \\pm \\sqrt{4^2 - 4(1)(-1)}}{2}")
formula2 = OldTex("=\\frac{4 \\pm \\sqrt{20}}{2}")
formula3 = OldTex("= 2 \\pm \\sqrt{5}")
formula.move_to(root_words[-1], LEFT)
formula.shift(0.5 * DL)
self.play(
TransformMatchingShapes(rhs3.copy(), formula),
FadeOut(root_words[-1]),
root_words[:-1].animate.shift(0.5 * DL),
)
self.wait()
solution = VGroup(root_words[:-1], formula)
self.play(
solution.animate.shift(formula2.get_width() * LEFT),
)
formula2.next_to(formula, RIGHT)
self.play(FadeIn(formula2))
self.wait()
solution.add(formula2)
self.play(
solution.animate.shift(formula3.get_width() * LEFT),
)
formula3.next_to(formula2, RIGHT)
self.play(FadeIn(formula3))
self.wait()
# Straight line
full_rect = FullScreenFadeRectangle()
arrow = Arrow(mat, formula3, thickness=0.05)
arrow.set_fill(YELLOW)
self.add(full_rect, task, formula3)
self.play(FadeIn(full_rect))
self.play(GrowArrow(arrow))
self.wait()
class TweakDiagonalValue(ExternallyAnimatedScene):
pass
class DetEquationLineOfReasoning(ExternallyAnimatedScene):
pass
class OutlineThreeFacts(Scene):
def construct(self):
# Matrix to lambdas
mat = Matrix([["a", "b"], ["c", "d"]], v_buff=0.8, h_buff=0.8)
mat.set_column_colors(COL_COLORS[0], COL_COLORS[1])
lambdas = OldTex("\\lambda_1", "\\,,\\,", "\\lambda_2")
lambdas[0].set_color(EIGEN_COLORS[0])
lambdas[2].set_color(EIGEN_COLORS[1])
arrow = Vector(1.5 * RIGHT)
group = VGroup(mat, arrow, lambdas)
group.arrange(RIGHT)
arrow_label = Text("Quick?", font_size=24)
arrow_label.next_to(arrow, UP, buff=0)
self.add(mat)
self.play(
GrowArrow(arrow),
Write(arrow_label, run_time=1),
LaggedStart(*(
AnimationGroup(*(
Transform(entry, lambdas[i])
for entry in mat.get_entries().deepcopy()
))
for i in [0, 2]
), lag_ratio=0.3),
FadeIn(lambdas[1]),
)
self.clear()
self.add(group, arrow_label)
self.wait()
# Three steps
indices = VGroup(*(Text(str(i) + ")", font_size=48) for i in range(1, 4)))
indices.set_color(GREY_B)
indices.arrange(DOWN, aligned_edge=LEFT, buff=2)
indices.to_edge(LEFT)
group.generate_target()
group.target[1].rotate(-90 * DEGREES)
group.target[1].scale(0.5)
group.target.arrange(DOWN)
group.target.to_corner(DR)
self.play(
LaggedStartMap(FadeIn, indices, shift=0.25 * UP, lag_ratio=0.3),
FadeOut(arrow_label),
MoveToTarget(group),
)
self.wait()
# Trace
tr_mat = mat.deepcopy()
tr = OldTex("\\text{tr}", "\\Big(", "\\Big)", font_size=60)
tr[1:].match_height(tr_mat, stretch=True)
tr.set_submobjects([*tr[:-1], tr_mat, tr[-1]])
tr.arrange(RIGHT, buff=SMALL_BUFF)
tr.next_to(indices[0], RIGHT, MED_LARGE_BUFF)
tr_rects = VGroup(
SurroundingRectangle(tr_mat.get_entries()[0]),
SurroundingRectangle(tr_mat.get_entries()[3]),
)
tr_rects.set_color(BLUE_C)
moving_tr_rects = tr_rects.copy()
moving_tr_rects.generate_target()
tex_kw = {
"tex_to_color_map": {
"a": COL_COLORS[0],
"b": COL_COLORS[1],
"c": COL_COLORS[0],
"d": COL_COLORS[1],
"=": WHITE,
"\\lambda_1": EIGEN_COLORS[0],
"\\lambda_2": EIGEN_COLORS[1],
}
}
tr_rhs = OldTex("= a + d = \\lambda_1 + \\lambda_2", **tex_kw)
tr_rhs.next_to(tr, RIGHT)
for term, rect in zip(tr_rhs[1:4:2], moving_tr_rects.target):
rect.move_to(term)
self.play(
TransformFromCopy(mat, tr_mat),
Write(VGroup(*tr[:2], tr[-1])),
)
self.play(LaggedStart(*map(ShowCreation, tr_rects)))
tr.add(tr_rects)
self.play(
MoveToTarget(moving_tr_rects),
TransformFromCopy(tr_mat.get_entries()[0], tr_rhs.get_part_by_tex("a")),
TransformFromCopy(tr_mat.get_entries()[3], tr_rhs.get_part_by_tex("d")),
FadeIn(tr_rhs[0:4:2]),
)
self.play(FadeOut(moving_tr_rects))
self.wait()
self.play(
TransformMatchingShapes(lambdas.copy(), tr_rhs[4:]),
)
self.wait()
# Mean of eigenvalues
half = OldTex("1 \\over 2")
half.move_to(tr, LEFT)
tr.generate_target()
tr.target.next_to(half, RIGHT, SMALL_BUFF)
new_tr_rhs = OldTex("= {a + d \\over 2} = {\\lambda_1 + \\lambda_2 \\over 2}", **tex_kw)
new_tr_rhs.next_to(tr.target, RIGHT)
self.play(
GrowFromCenter(half),
MoveToTarget(tr),
TransformMatchingShapes(tr_rhs, new_tr_rhs),
)
self.wait()
# Determinant
det_mat = mat.deepcopy()
det = OldTex("\\text{det}", "\\Big(", "\\Big)", font_size=60)
det[1:].match_height(det_mat, stretch=True)
det.set_submobjects([*det[:-1], det_mat, det[-1]])
det.arrange(RIGHT, buff=SMALL_BUFF)
det.next_to(indices[1], RIGHT, MED_LARGE_BUFF)
det_rhs = OldTex("= ad - bc = \\lambda_1 \\lambda_2", **tex_kw)
det_rhs.next_to(det, RIGHT)
self.play(
TransformFromCopy(mat, det_mat),
Write(VGroup(*det[:2], det[-1])),
)
self.play(
det_path_anim(det_mat),
LaggedStart(
Animation(Mobject(), remover=True),
FadeIn(det_rhs[:3]),
FadeIn(det_rhs[3:6]),
lag_ratio=0.7,
)
)
self.wait()
self.play(
TransformMatchingShapes(lambdas.copy(), det_rhs[6:])
)
self.wait()
# Mean and product
eq_m = OldTexText("=", " $m$", "\\quad (mean)")
eq_m[1].set_color(MEAN_COLOR)
eq_m.next_to(new_tr_rhs, RIGHT)
eq_p = OldTexText("=", " $p$", "\\quad (product)")
eq_p[1].set_color(PROD_COLOR)
eq_p.next_to(det_rhs, RIGHT)
form_lhs = lambdas.copy()
form_rhs = OldTex("= {m} \\pm \\sqrt{\\,{m}^2 - {p}}", tex_to_color_map={"{m}": MEAN_COLOR, "{p}": PROD_COLOR})
form_lhs.next_to(indices[2], RIGHT)
form_rhs.next_to(form_lhs, RIGHT)
third_point_placeholder = Text("(We'll get to this...)", font_size=30)
third_point_placeholder.set_fill(GREY_C)
third_point_placeholder.next_to(indices[2], RIGHT, MED_LARGE_BUFF)
form = VGroup(indices[2], form_lhs, form_rhs)
rect = SurroundingRectangle(VGroup(form), buff=MED_SMALL_BUFF)
randy = Randolph(height=2)
randy.next_to(rect, RIGHT)
randy.to_edge(DOWN)
self.play(
FadeIn(third_point_placeholder),
VFadeIn(randy),
randy.change("erm", third_point_placeholder)
)
self.play(Blink(randy))
self.wait()
self.play(
randy.change("thinking", eq_m),
Write(eq_m)
)
self.play(
randy.animate.look_at(eq_p),
Write(eq_p),
mat.animate.set_height(1, about_edge=DOWN)
)
self.play(Blink(randy))
self.wait()
# Example matrix
ex_mat = IntegerMatrix([[8, 4], [2, 6]])
ex_mat.set_height(1.25)
ex_mat.set_column_colors(COL_COLORS[0], COL_COLORS[1])
ex_mat.next_to(randy, RIGHT, aligned_edge=UP)
kw = {"tex_to_color_map": {"m": MEAN_COLOR, "p": PROD_COLOR, "=": WHITE, "-": WHITE}}
m_eq = OldTex("m = 7", **kw)
p_eq1 = OldTex("p = 48 - 8", **kw)
p_eq2 = OldTex("p = 40", **kw)
for mob in (m_eq, p_eq1, p_eq2):
mob.next_to(ex_mat, RIGHT, buff=MED_LARGE_BUFF)
m_eq.shift(0.5 * UP)
VGroup(p_eq1, p_eq2).shift(0.5 * DOWN)
diag_rects = VGroup(
SurroundingRectangle(ex_mat.get_entries()[0]),
SurroundingRectangle(ex_mat.get_entries()[3]),
)
off_diag_rects = VGroup(
SurroundingRectangle(ex_mat.get_entries()[1]),
SurroundingRectangle(ex_mat.get_entries()[2]),
)
diag_rects.set_color(PROD_COLOR)
off_diag_rects.set_color(PROD_COLOR)
mean_rect = SurroundingRectangle(m_eq[2])
mean_rect.set_color(MEAN_COLOR)
tr_rect = SurroundingRectangle(VGroup(indices[0], tr, eq_m)).set_stroke(MEAN_COLOR)
det_rect = SurroundingRectangle(VGroup(indices[1], det, eq_p)).set_stroke(PROD_COLOR)
self.play(
randy.change("raise_right_hand", ex_mat),
FadeIn(ex_mat, RIGHT),
FadeOut(group, RIGHT),
)
self.play(Blink(randy))
self.wait()
self.play(FadeIn(tr_rect), randy.change("pondering", tr_rhs))
self.play(
Write(m_eq[:2]),
LaggedStartMap(ShowCreation, diag_rects, lag_ratio=0.5, run_time=1),
randy.animate.look_at(m_eq),
)
self.wait()
self.remove(diag_rects)
self.play(
TransformFromCopy(diag_rects, mean_rect),
FadeTransform(ex_mat.get_entries()[0].copy(), m_eq[2]),
FadeTransform(ex_mat.get_entries()[3].copy(), m_eq[2]),
)
self.play(Blink(randy))
self.wait()
self.play(FadeOut(tr_rect), FadeIn(det_rect), randy.animate.look_at(tr_rhs))
self.play(
Write(p_eq1[:2]),
randy.change("hesitant", p_eq1),
FadeOut(mean_rect),
)
self.play(det_path_anim(ex_mat))
self.wait()
self.play(
FadeIn(diag_rects),
FadeIn(p_eq1[2]),
)
self.play(
FadeOut(diag_rects),
FadeIn(off_diag_rects),
FadeIn(p_eq1[3:]),
)
self.play(
FadeOut(off_diag_rects),
)
self.play(
randy.change("tease", p_eq2),
FadeOut(p_eq1),
FadeIn(p_eq2),
)
self.play(FadeOut(det_rect))
self.wait()
# Let other stuff happen up top
full_rect = FullScreenFadeRectangle()
full_rect.set_fill(BLACK, 1)
ex = VGroup(ex_mat, m_eq, p_eq2)
self.add(full_rect, randy, ex)
self.play(
FadeIn(full_rect),
randy.change("pondering", ORIGIN)
)
for x in range(10):
if random.random() < 0.5:
self.play(Blink(randy))
else:
self.wait()
# Show final formula
ex_rect = SurroundingRectangle(ex, buff=0.35)
ex_rect.set_stroke(GREY_A)
ex_rect.set_fill(interpolate_color(GREY_E, BLACK, 0.25))
ex_rect.set_opacity(0)
ex_group = VGroup(ex_rect, ex)
ex_group.generate_target()
ex_group.target.set_height(1.5)
ex_group.target.to_corner(DR)
ex_group.target[0].set_opacity(1)
self.play(
FadeOut(full_rect),
FadeOut(randy),
MoveToTarget(ex_group)
)
self.play(
FadeIn(form_lhs, 0.25 * UP),
FadeOut(third_point_placeholder, 0.25 * UP)
)
self.play(TransformMatchingShapes(
VGroup(m_eq[0], p_eq2[0]).copy(),
form_rhs,
))
self.play(ShowCreation(rect))
self.wait()
class MeansMatch(Scene):
def construct(self):
t2c = {
"{a}": COL_COLORS[0],
"{d}": COL_COLORS[1],
"\\lambda_1": EIGEN_COLORS[0],
"\\lambda_2": EIGEN_COLORS[1],
"=": WHITE,
}
equation = OldTex(
"{{a} + {d} \\over 2} = {\\lambda_1 + \\lambda_2 \\over 2}",
tex_to_color_map=t2c
)
mean_eq = OldTex(
"\\text{mean}({a}, {d}) = \\text{mean}(\\lambda_1, \\lambda_2)",
tex_to_color_map=t2c
)
mean_eq.next_to(equation, DOWN, LARGE_BUFF)
self.add(equation)
self.play(TransformMatchingShapes(equation[5:].copy(), mean_eq[6:]))
self.wait()
self.play(TransformMatchingShapes(equation[:5].copy(), mean_eq[:6]))
self.wait()
class ShowSquishingAndStretching(Scene):
def construct(self):
self.camera.frame.set_height((3 / 4) * FRAME_HEIGHT)
# Transform
plane = NumberPlane(
(-20, 20), (-20, 20),
background_line_style={"stroke_width": 3},
faded_line_ratio=0,
)
plane.axes.set_stroke(BLUE, 3)
back_plane = NumberPlane(
faded_line_ratio=0,
)
back_plane.set_stroke(GREY_B, 1, opacity=0.5)
mat = [[2, 2], [1, 2]]
eigenvalues, eigenvectors = np.linalg.eig(mat)
eigenvectors = [normalize(v) for v in eigenvectors.T]
eigenlines = VGroup(*(
Line(10 * ev, -10 * ev, color=color)
for ev, color in zip(eigenvectors, EIGEN_COLORS)
))
eigenlines.set_stroke(GREY_B, 2)
eigenvect_mobs = VGroup(*(
Vector(ev, fill_color=color)
for ev, color in zip(eigenvectors, EIGEN_COLORS)
))
eigenvect_mobs[0].add_updater(lambda m: m.put_start_and_end_on(plane.c2p(0, 0), plane.c2p(*eigenvectors[0][:2])))
eigenvect_mobs[1].add_updater(lambda m: m.put_start_and_end_on(plane.c2p(0, 0), plane.c2p(*eigenvectors[1][:2])))
# Basis vectors
bases = VGroup(
Vector(RIGHT, fill_color=GREEN, thickness=0.05),
Vector(UP, fill_color=RED, thickness=0.05),
)
bases[0].add_updater(lambda m: m.put_start_and_end_on(plane.c2p(0, 0), plane.c2p(1, 0)))
bases[1].add_updater(lambda m: m.put_start_and_end_on(plane.c2p(0, 0), plane.c2p(0, 1)))
disk = Circle(radius=1)
disk.set_fill(YELLOW, 0.25)
disk.set_stroke(YELLOW, 3)
morpher = VGroup(plane, disk)
self.add(back_plane, morpher, eigenlines, *eigenvect_mobs)
self.play(
morpher.animate.apply_matrix(mat), run_time=4
)
self.wait()
# Labels
labels = VGroup(*(
OldTex("\\text{Stretch by }", f"\\lambda_{i}", color=color, font_size=30)
for i, color in zip((1, 2), EIGEN_COLORS)
))
for label, vect in zip(labels, eigenvect_mobs):
label.next_to(
vect.get_center(),
rotate_vector(normalize(vect.get_vector()), 90 * DEGREES),
buff=0.1,
index_of_submobject_to_align=1,
)
labels.set_stroke(BLACK, 5, background=True)
self.play(LaggedStartMap(FadeIn, labels, lag_ratio=0.7))
self.wait()
class MeanProductExample(Scene):
def construct(self):
# Number line and midpoint
number_line = NumberLine((0, 14))
number_line.add_numbers()
number_line.set_width(FRAME_WIDTH - 1)
number_line.to_edge(UP, buff=1.5)
nl = number_line
mean = 7
m_dot = Dot(nl.n2p(mean))
m_dot.set_color(MEAN_COLOR)
m_label = OldTex("m", color=MEAN_COLOR)
m_label.next_to(m_dot, UP, buff=MED_SMALL_BUFF)
label7 = OldTex("7", color=MEAN_COLOR)
# Distance tracking
d_tracker = ValueTracker(4)
def get_l1_point():
return nl.n2p(mean - d_tracker.get_value())
def get_l2_point():
return nl.n2p(mean + d_tracker.get_value())
l1_dot, l2_dot = (Dot(color=TEAL) for x in range(2))
l1_label = OldTex("\\lambda_1", color=EIGEN_COLORS[0])
l2_label = OldTex("\\lambda_2", color=EIGEN_COLORS[1])
l1_arrow, l2_arrow = (Arrow(color=WHITE) for x in range(2))
l1_dot.add_updater(lambda m: m.move_to(get_l1_point()))
l2_dot.add_updater(lambda m: m.move_to(get_l2_point()))
always(l1_label.next_to, l1_dot, UP, buff=MED_SMALL_BUFF)
always(l2_label.next_to, l2_dot, UP, buff=MED_SMALL_BUFF)
m_label.match_y(l1_label)
l1_arrow.add_updater(lambda m: m.set_points_by_ends(
m_label.get_left() + SMALL_BUFF * LEFT, l1_label.get_right() + SMALL_BUFF * RIGHT,
))
l2_arrow.add_updater(lambda m: m.set_points_by_ends(
m_label.get_right() + SMALL_BUFF * RIGHT, l2_label.get_left() + SMALL_BUFF * LEFT,
))
minus_d = OldTex("-d")
plus_d = OldTex("+d")
always(minus_d.next_to, l1_arrow, UP, SMALL_BUFF)
always(plus_d.next_to, l2_arrow, UP, SMALL_BUFF)
plus_qm = OldTex("+??")
minus_qm = OldTex("-??")
always(plus_qm.move_to, plus_d)
always(minus_qm.move_to, minus_d)
VGroup(plus_d, minus_d).set_opacity(0)
label7.move_to(m_label)
self.add(number_line)
self.add(m_dot)
self.add(label7)
self.add(l1_dot)
self.add(l2_dot)
self.add(l1_label)
self.add(l2_label)
self.add(l1_arrow)
self.add(l2_arrow)
self.add(minus_d)
self.add(plus_d)
self.add(minus_qm)
self.add(plus_qm)
d_tracker.add_updater(lambda m: m.set_value(4 - 2.5 * np.sin(0.25 * self.time)))
self.add(d_tracker)
self.wait(20)
self.play(
UpdateFromAlphaFunc(
Mobject(),
lambda m, a: VGroup(plus_d, minus_d).set_opacity(a),
remover=True
),
UpdateFromAlphaFunc(
Mobject(),
lambda m, a: VGroup(plus_qm, minus_qm).set_opacity(1 - a),
remover=True
),
)
self.remove(plus_qm, minus_qm)
self.wait(5)
# Write the product
kw = {"tex_to_color_map": {"\\,7": MEAN_COLOR, "d": GREY_A, "=": WHITE, "-": WHITE}}
texs = VGroup(*(OldTex(tex, **kw) for tex in [
"(\\,7 + d\\,)(\\,7 - d\\,)",
"\\,7^2 - d^2 = ",
"40 =",
"d^2 = \\,7^2 - 40",
"d^2 = 9",
"d = 3",
]))
texs[:3].arrange(LEFT)
texs[1].align_to(texs[2], DOWN)
texs[:3].next_to(nl, DOWN, MED_LARGE_BUFF).to_edge(LEFT, buff=LARGE_BUFF)
for t1, t2 in zip(texs[2:], texs[3:]):
t2.next_to(t1, DOWN, MED_LARGE_BUFF)
t2.shift((t1.get_part_by_tex("=").get_x() - t2.get_part_by_tex("=").get_x()) * RIGHT)
texs[0].save_state()
texs[0].move_to(texs[1], UL)
self.play(Write(VGroup(texs[2], texs[0])))
self.wait(3)
self.play(Restore(texs[0]))
self.play(TransformMatchingShapes(texs[0].copy(), texs[1], path_arc=45 * DEGREES))
self.wait(3)
self.play(LaggedStart(
TransformFromCopy(texs[2][0], texs[3][6], path_arc=-45 * DEGREES),
TransformFromCopy(texs[2][1], texs[3][2]),
TransformFromCopy(texs[1][:3], texs[3][3:6]),
TransformFromCopy(texs[1][3:5], texs[3][0:2], path_arc=45 * DEGREES),
lag_ratio=0.1
))
self.wait(2)
self.play(FadeIn(texs[4], DOWN))
self.wait()
self.play(FadeIn(texs[5], DOWN))
# Show final d geometrically
d_tracker.clear_updaters()
self.play(d_tracker.animate.set_value(3), rate_func=rush_into)
minus_d.clear_updaters()
plus_d.clear_updaters()
plus_3 = OldTex("+3").move_to(plus_d)
minus_3 = OldTex("-3").move_to(minus_d)
self.play(
LaggedStart(
FadeOut(minus_d, 0.25 * UP),
FadeOut(plus_d, 0.25 * UP),
lag_ratio=0.25
),
LaggedStart(
FadeIn(minus_3, 0.25 * UP),
FadeIn(plus_3, 0.25 * UP),
lag_ratio=0.25
)
)
self.wait()
# Highlight solutions
rects = VGroup(
SurroundingRectangle(VGroup(l1_label, nl.numbers[4])),
SurroundingRectangle(VGroup(l2_label, nl.numbers[10])),
)
self.play(LaggedStartMap(ShowCreation, rects), lag_ratio=0.3)
self.wait(2)
self.play(LaggedStartMap(FadeOut, rects), lag_ratio=0.3)
# Replace with general variables
ms = VGroup(m_label.copy())
sevens = VGroup(label7)
ps = VGroup()
fourties = VGroup()
for tex in texs[:-2]:
for fourty in tex.get_parts_by_tex("40"):
fourties.add(fourty)
ps.add(OldTex("p").set_color(PROD_COLOR).move_to(fourty).shift(0.1 * DOWN))
for seven in tex.get_parts_by_tex("7"):
sevens.add(seven)
m = OldTex("m").set_color(MEAN_COLOR)
m.scale(0.95)
m.move_to(seven, DR)
m.shift(0.04 * RIGHT)
ms.add(m)
ps[0].shift(0.05 * RIGHT)
ps[1].shift(0.1 * LEFT)
for g1, g2 in ((sevens, ms), (fourties, ps)):
self.play(
LaggedStartMap(FadeOut, g1, shift=0.25 * UP, lag_ratio=0.3),
LaggedStartMap(FadeIn, g2, shift=0.25 * UP, lag_ratio=0.3),
texs[-2:].animate.set_opacity(0)
)
self.remove(texs[-2:])
self.wait()
self.play(FlashUnder(texs[3]))
self.wait()
plus_form, minus_form = [
OldTex(
c + "\\sqrt{\\,m^2 - p}",
tex_to_color_map={"m": MEAN_COLOR, "p": PROD_COLOR},
font_size=24,
).move_to(d, DOWN)
for c, d in [("+", plus_d), ("-", minus_d)]
]
pre_group = VGroup(*texs[3][4:6], ms[-1], ps[-1])
self.play(
TransformMatchingShapes(pre_group.copy(), minus_form),
TransformMatchingShapes(pre_group.copy(), plus_form),
FadeOut(VGroup(minus_3, plus_3), shift=0.25 * UP),
)
self.wait()
class AskIfThatsBetter(Scene):
def construct(self):
randy = Randolph(height=2)
randy.to_edge(DOWN)
randy.change("pondering", UL)
self.add(BackgroundRectangle(randy, fill_opacity=1), randy)
self.play(
PiCreatureSays(
randy, "Is that better?", target_mode="sassy",
bubble_config={"direction": LEFT, "width": 4, "height": 2},
look_at=UL
)
)
self.play(Blink(randy))
self.wait()
self.play(
RemovePiCreatureBubble(randy, target_mode="thinking")
)
self.wait(2)
self.play(randy.change("pondering", UL))
self.play(Blink(randy))
self.wait()
class OutstandingChannel(Scene):
def construct(self):
morty = Mortimer()
morty.to_corner(DR)
self.play(PiCreatureSays(
morty, OldTexText("Outstanding\\\\channel"), target_mode="hooray",
bubble_config={"height": 3, "width": 4}
))
self.play(Blink(morty))
self.wait()
class JingleAnimation(Scene):
def construct(self):
form = OldTex("m \\pm \\sqrt{m^2 - p}")[0]
form.set_height(2)
m, old_pm, sqrt, root, m2, squared, minus, p = form
VGroup(m, m2).set_color(MEAN_COLOR)
VGroup(p).set_color(PROD_COLOR)
m.refresh_bounding_box() # Why?
p.refresh_bounding_box() # Why?
pm = VGroup(OldTex("+"), OldTex("-"))
pm.arrange(DOWN, buff=0)
pm.replace(old_pm)
pm.save_state()
pm.scale(1.5)
pm.arrange(DOWN, buff=2)
mean = Text("mean", color=WHITE).next_to(m, DOWN)
product = Text("product", color=WHITE).next_to(p, DOWN)
plus_word = Text("plus", color=YELLOW).next_to(pm, UP)
minus_word = Text("minus", color=YELLOW).next_to(pm, DOWN)
def snap(t):
return t**5
self.play(FadeIn(m, scale=0.5, rate_func=snap, run_time=0.5))
self.add(mean)
self.wait(0.4)
self.add(pm[0], plus_word)
self.wait(0.2)
self.add(pm[1], minus_word)
self.wait(0.2)
self.play(
Restore(pm, rate_func=smooth, run_time=0.5),
FadeOut(VGroup(plus_word, minus_word, mean), run_time=0.5),
)
sqrt_outline = sqrt.copy()
sqrt_outline.set_stroke(YELLOW, 10)
sqrt_outline.set_fill(opacity=0)
sqrt_outline.insert_n_curves(100)
root.save_state()
root.stretch(0, 0, about_edge=LEFT)
self.play(
FadeIn(sqrt, rate_func=squish_rate_func(smooth, 0.25, 0.75)),
VShowPassingFlash(sqrt_outline, time_width=2),
Restore(root, rate_func=squish_rate_func(snap, 0.25, 1)),
run_time=1,
)
self.play(
TransformFromCopy(m, m2, path_arc=-120 * DEGREES, rate_func=smooth, run_time=0.6),
)
self.play(FadeTransform(m2.copy(), squared, run_time=0.5))
minus.save_state()
minus.stretch(0, 0, about_edge=LEFT)
self.play(Restore(minus), run_time=0.5, rate_func=smooth)
self.wait(0.2)
self.play(
FadeIn(p, scale=0.5, rate_func=snap, run_time=0.5),
)
self.add(product)
self.wait(0.4)
self.play(
Flash(p, flash_radius=0.8 * p.get_height(), run_time=0.5),
)
self.wait(0.3)
self.remove(product)
self.wait()
class Example1(TeacherStudentsScene):
def construct(self):
# Show matrix
mat = IntegerMatrix([[3, 1], [4, 1]])
mat.set_column_colors(*COL_COLORS)
mat.move_to(self.hold_up_spot, DOWN)
self.play(
self.teacher.change("raise_right_hand", mat),
FadeIn(mat.get_brackets(), UP)
)
self.play(
Write(mat.get_entries(), lag_ratio=0.1, run_time=2),
self.change_students("pondering", "pondering", "thinking", look_at=mat)
)
self.wait()
bubble = ThoughtBubble(height=4, width=7)
bubble.pin_to(self.students[2])
self.play(
self.students[2].change("tease", bubble),
Write(bubble)
)
self.wait(3)
# Write formula
shift_vect = 5 * LEFT
arrow = Vector(0.75 * RIGHT)
arrow.next_to(mat, RIGHT)
formula = OldTex(
"\\lambda_1, \\, \\lambda_2 = {2} \\pm \\sqrt{\\,{2}^2 - (-1)}",
tex_to_color_map={
"\\lambda_1": EIGEN_COLORS[0],
"\\lambda_2": EIGEN_COLORS[1],
"(-1)": PROD_COLOR,
"{2}": MEAN_COLOR,
}
)
formula.next_to(arrow, RIGHT)
VGroup(formula, arrow).shift(shift_vect)
twos = formula.get_parts_by_tex("{2}")
min1 = formula.get_part_by_tex("(-1)")
m_rects = VGroup(*(SurroundingRectangle(two, buff=0) for two in twos))
m_rects.set_stroke(MEAN_COLOR)
p_rect = SurroundingRectangle(min1, buff=0)
p_rect.set_stroke(PROD_COLOR)
form_rects = VGroup(m_rects, p_rect)
VGroup(twos, min1).set_opacity(0)
self.play(
FadeOut(bubble),
mat.animate.shift(shift_vect),
FadeIn(arrow, shift_vect),
FadeIn(formula),
FadeIn(form_rects),
self.change_students("pondering", "pondering", "pondering", look_at=ORIGIN)
)
self.play(
self.teacher.change("tease", formula),
*(pi.animate.look_at(formula) for pi in self.students),
)
self.wait()
# Show mean
m_eq = OldTex("m", "=", "2", tex_to_color_map={"m": MEAN_COLOR, "2": MEAN_COLOR})
m_eq.next_to(mat, UP, LARGE_BUFF)
t2c = {
"\\lambda_1": EIGEN_COLORS[0],
"\\lambda_2": EIGEN_COLORS[1],
"{m}": MEAN_COLOR,
"{p}": PROD_COLOR,
"=": WHITE,
}
diag_rects = VGroup(*(SurroundingRectangle(mat.get_entries()[i]) for i in [0, 3]))
diag_rects.set_stroke(MEAN_COLOR)
two_rect = SurroundingRectangle(m_eq[2])
two_rect.set_stroke(MEAN_COLOR)
self.play(
Write(m_eq[:2]),
*(pi.animate.look_at(m_eq) for pi in self.pi_creatures)
)
self.play(
ShowCreation(diag_rects),
self.teacher.change("tease", diag_rects),
*(pi.animate.look_at(diag_rects) for pi in self.students),
)
self.wait()
self.play(
FadeTransform(diag_rects[0].copy(), m_eq[2], remover=True),
FadeTransform(diag_rects[1].copy(), m_eq[2], remover=True),
*(pi.animate.look_at(two_rect) for pi in self.pi_creatures),
)
self.wait(2)
self.remove(twos, min1)
VGroup(twos, min1).set_opacity(1)
for i in (0, 1):
self.play(
TransformFromCopy(m_eq[2], twos[i]),
FadeOut(m_rects[i]),
self.teacher.change("raise_right_hand", twos),
*(pi.change("thinking", twos) for pi in self.students)
)
self.wait()
# Show product
prod_eq = OldTex(
# "\\lambda_1 \\lambda_2 =",
"{p} = {3} - {4} = -1",
tex_to_color_map={
"{3}": COL_COLORS[0],
"{4}": COL_COLORS[0],
**t2c
}
)
prod_eq.next_to(mat, UP, LARGE_BUFF)
prod_eq.set_x(mat.get_center()[0], LEFT)
lhs = prod_eq[:1]
self.play(
m_eq.animate.scale(0.7).next_to(mat, LEFT).to_edge(LEFT),
FadeIn(lhs),
FadeOut(diag_rects),
self.teacher.change("happy"),
self.change_students("erm", "hesitant", "thinking", look_at=prod_eq)
)
self.play(det_path_anim(mat, run_time=1))
self.play(
FadeIn(prod_eq[1]),
FadeTransform(mat.get_entries()[0].copy(), prod_eq[2]),
FadeTransform(mat.get_entries()[3].copy(), prod_eq[2]),
VFadeInThenOut(VGroup(*(SurroundingRectangle(e) for e in mat.get_entries()[0::3]))),
self.teacher.change("coin_flip_1"),
)
self.play(
FadeIn(prod_eq[3]),
FadeTransform(mat.get_entries()[1].copy(), prod_eq[4]),
FadeTransform(mat.get_entries()[2].copy(), prod_eq[4]),
VFadeInThenOut(VGroup(*(SurroundingRectangle(e) for e in mat.get_entries()[1:3]))),
)
self.wait()
self.play(
Write(prod_eq[-2:]),
self.change_students("tease", "tease", "happy", look_at=prod_eq.get_right()),
)
self.play(
FadeOut(p_rect),
FadeTransform(prod_eq[-1].copy(), min1),
self.teacher.change("raise_left_hand", min1),
*(pi.animate.look_at(min1) for pi in self.students)
)
self.wait()
self.play(
prod_eq.animate.scale(0.7).next_to(m_eq, UP).to_edge(LEFT)
)
self.wait(2)
# Final answer
rhs = OldTex("2\\pm\\sqrt{5}")
rhs.move_to(formula[4], LEFT)
self.play(
self.teacher.change("tease", rhs),
TransformMatchingShapes(formula[4:6].copy(), rhs),
formula[4:].animate.shift(UP),
)
self.wait()
class SameExampleWrapper(Scene):
def construct(self):
self.add(FullScreenRectangle())
screen = ScreenRectangle()
screen.set_fill(BLACK, 1)
screen.set_stroke(BLUE, 3)
screen.set_height(6)
screen.to_edge(DOWN)
title = Text("Same example from before")
title.next_to(screen, UP)
self.add(screen, title)
class GeneralExample(Scene):
matrix = [[3, 1], [4, 1]]
def construct(self):
mat = IntegerMatrix(self.matrix)
mat.move_to(2 * LEFT)
mat.set_column_colors(*COL_COLORS)
matrix = np.array(self.matrix)
a, b, c, d = matrix.flatten()
mean = (a + d) / 2
if (a + d) % 2 == 0:
mean = int(mean)
prod = a * d - b * c
mean_str = "{" + str(mean) + "}"
prod_str = "{" + str(prod) + "}"
self.add(mat)
self.wait()
mean_eq = OldTex(f"m = {mean_str}", tex_to_color_map={"m": MEAN_COLOR, mean_str: MEAN_COLOR})
mean_eq.next_to(mat, UP, LARGE_BUFF)
diag_rects = get_diag_rects(mat)
self.play(
FadeIn(mean_eq[:2], 0.25 * UP)
)
self.play(LaggedStartMap(ShowCreation, diag_rects, lag_ratio=0.3, run_time=1))
self.play(
FadeTransform(diag_rects[0].copy(), mean_eq[2]),
FadeTransform(diag_rects[1].copy(), mean_eq[2]),
)
self.play(FadeOut(diag_rects))
last_part = "(" + prod_str + ")}" if prod < 0 else prod_str + "}"
formula = OldTex(
mean_str, "\\pm \\sqrt{\\,", mean_str, "^2 - ", last_part,
font_size=60,
tex_to_color_map={mean_str: MEAN_COLOR, prod_str: PROD_COLOR},
)
formula.next_to(mat, RIGHT, buff=1.5)
self.play(
Write(formula[1]),
Write(formula[3]),
FadeTransform(mean_eq[2].copy(), formula[0]),
FadeTransform(mean_eq[2].copy(), formula[2]),
)
self.wait()
# Product
prod_eq = OldTex(
f"p = {a * d} - {b * c} = {prod}",
tex_to_color_map={
str(prod): PROD_COLOR,
str(a * d): COL_COLORS[0],
str(b * c): COL_COLORS[0],
"=": WHITE,
}
)
prod_eq.move_to(mean_eq)
self.play(
FadeIn(prod_eq[:2]),
mean_eq.animate.shift(UP),
)
self.play(
FadeIn(prod_eq[2]),
VFadeInThenOut(get_diag_rects(mat, color=YELLOW), run_time=1.5),
)
self.play(
FadeIn(prod_eq[3:5]),
VFadeInThenOut(get_diag_rects(mat, color=YELLOW, off_diagonal=True), run_time=1.5),
)
self.wait()
self.play(Write(prod_eq[5:]))
self.play(FadeTransform(prod_eq[-1].copy(), formula[4:]))
self.wait()
# Simplify
line2 = OldTex(mean_str, "\\pm", "\\sqrt{\\,", str(mean**2 - prod), "}", font_size=60)
if mean == 0:
line2[0].scale(0, about_point=line2[1].get_left())
line2[0].set_color(MEAN_COLOR)
line2.next_to(formula, DOWN, buff=0.7, aligned_edge=LEFT)
self.play(FadeTransform(formula.copy()[:4], line2))
self.wait()
line3 = self.get_final_simplification()
if line3:
line3.next_to(line2, DOWN, buff=0.7, aligned_edge=LEFT)
self.play(FadeIn(line3, DOWN))
self.wait()
answer = line3[-1]
else:
answer = line2
self.play(ShowCreation(SurroundingRectangle(answer)))
self.wait()
def get_final_simplification(self):
return None
class Example2(GeneralExample):
matrix = [[2, 7], [1, 8]]
def get_final_simplification(self):
return OldTex("5 \\pm 4", "=", "9,\\,1", font_size=60)
class Example3(GeneralExample):
matrix = [[3, 11], [1, 11]]
class Example4(GeneralExample):
matrix = [[2, -1], [2, 0]]
class Example5(GeneralExample):
matrix = [[2, 3], [5, 7]]
class PauliMatrices(Scene):
def construct(self):
self.camera.frame.focal_distance = 20
# Matrices
colors = [RED, GREEN, BLUE]
lhss, matrices = self.get_lhss_and_matrices(colors)
self.add(lhss)
self.play(LaggedStartMap(FadeIn, matrices, shift=0.25 * RIGHT, lag_ratio=0.3, run_time=2))
self.wait()
# Title
title = Text("Pauli spin matrices")
title.next_to(group, RIGHT, buff=1.25)
self.play(Write(title, run_time=2))
self.wait()
# Axes
axes = ThreeDAxes(
(-2, 2), (-2, 2), (-2, 2),
axis_config={"include_tip": False}
)
axes.set_flat_stroke(False)
axes.rotate(20 * DEGREES, OUT)
axes.rotate(70 * DEGREES, LEFT)
axes.set_height(1.7)
axes.labels = VGroup(*(
OldTex(ch, font_size=24).next_to(axis.get_end(), vect, buff=SMALL_BUFF)
for ch, axis, vect in zip("xyz", axes.get_axes(), [RIGHT, UP, UP])
))
axes.add(axes.labels)
axes.set_stroke(background=True)
axes_copies = VGroup()
for i, matrix, color, (v1, v2) in zip(it.count(), matrices, colors, [(UP, DOWN), (LEFT, RIGHT), (RIGHT, RIGHT)]):
ac = axes.deepcopy()
ac.labels.set_fill(GREY_C, 1)
ac.labels[i].set_fill(color, 1)
axis = ac.get_axes()[i]
vp1, vm1 = (
Arrow(axis.n2p(0), axis.n2p(n), buff=0.05, fill_color=color, thickness=0.05)
for n in (2, -2)
)
for vector, tex, v in [(vp1, "+1", v1), (vm1, "-1", v2)]:
vector.add(OldTex(tex, font_size=24).next_to(vector, v, buff=0.05))
ac.add(vp1, vm1)
ac.next_to(matrix, RIGHT, buff=LARGE_BUFF)
axes_copies.add(ac)
acx, acy, acz = axes_copies
self.play(
FadeOut(title),
Write(acx, stroke_width=0.5),
)
self.wait()
self.play(TransformFromCopy(acx, acy))
self.wait()
self.play(TransformFromCopy(acy, acz))
self.wait()
self.play(LaggedStartMap(FadeOut, axes_copies, shift=0.25 * DOWN, run_time=1, lag_ratio=0.3))
# Compute means
means_rects = VGroup(*(
get_diag_rects(matrix)
for matrix in matrices
))
mean_eqs = VGroup(*(
OldTex("m = 0", tex_to_color_map={"m": MEAN_COLOR, "0": WHITE}).next_to(matrix, RIGHT, buff=MED_LARGE_BUFF)
for matrix in matrices
))
kw = {
"tex_to_color_map": {
"0": MEAN_COLOR,
"{p}": PROD_COLOR,
}
}
forms = VGroup(*(
OldTex("0 \\pm \\sqrt{\\,0^2 - {p}}", **kw).next_to(matrix, RIGHT).to_edge(RIGHT)
for matrix in matrices
))
simple_forms = VGroup(*(
OldTex("\\pm \\sqrt{-{p}}", **kw).move_to(form, LEFT)
for form in forms
))
for me, mr, form in zip(mean_eqs, means_rects, forms):
self.play(
FadeIn(mr),
Write(me[:2], run_time=1),
)
self.play(
TransformFromCopy(mr, me[2], run_time=0.7)
)
self.wait()
self.play(
LaggedStart(*(
FadeIn(VGroup(form[1], form[3], form[4]))
for form in forms
), lag_ratio=0.2),
LaggedStart(*(
TransformFromCopy(me.get_parts_by_tex("0"), form.get_parts_by_tex("0"))
for me, form in zip(mean_eqs, forms)
), lag_ratio=0.2),
)
self.wait()
self.play(LaggedStart(*(
TransformMatchingShapes(form, simple_form)
for form, simple_form in zip(forms, simple_forms)
)))
self.wait()
# Products
prod_eqs = VGroup(*(
OldTex("p = -1", tex_to_color_map={"p": PROD_COLOR, "-1": WHITE}).next_to(
matrix, RIGHT, buff=MED_LARGE_BUFF
).shift(0.5 * DOWN)
for matrix in matrices
))
self.play(
mean_eqs.animate.shift(0.5 * UP),
LaggedStart(*(FadeIn(pe[:2]) for pe in prod_eqs)),
FadeOut(means_rects)
)
self.play(LaggedStart(*(det_path_anim(matrix) for matrix in matrices), lag_ratio=0.2))
for matrix, pe in zip(matrices, prod_eqs):
r1 = get_diag_rects(matrix)
r2 = get_diag_rects(matrix, off_diagonal=True)
VGroup(r1, r2).set_color(YELLOW)
self.play(FadeIn(r1))
self.play(FadeOut(r1), FadeIn(r2), FadeIn(pe[2]))
self.play(FadeOut(r2))
self.wait()
final_forms = VGroup(*(
OldTex("= \\pm 1").move_to(sf.get_center(), LEFT)
for sf in simple_forms
))
self.play(
LaggedStart(*(
FadeTransform(pe[2].copy(), ff)
for pe, ff in zip(prod_eqs, final_forms)
), lag_ratio=0.3),
LaggedStart(*(
sf.animate.next_to(ff, LEFT)
for sf, ff in zip(simple_forms, final_forms)
))
)
self.wait()
# Bring back axes
axes_copies.shift(2 * RIGHT)
self.play(
FadeOut(simple_forms), FadeOut(final_forms),
FadeIn(acx),
)
self.play(FadeIn(acy))
self.play(FadeIn(acz))
self.wait()
def get_lhss_and_matrices(self, colors):
lhss = VGroup(*(OldTex(f"\\sigma_{c} = ") for c in "xyz"))
kw = {"h_buff": 0.7, "v_buff": 0.7}
matrices = VGroup(
Matrix([["0", "1"], ["1", "0"]], **kw),
Matrix([["0", "-i"], ["i", "0"]], **kw),
Matrix([["1", "0"], ["0", "-1"]], **kw),
)
lhss.set_submobject_colors_by_gradient(*colors)
lhss.arrange(DOWN, buff=2.5)
for lhs, matrix in zip(lhss, matrices):
matrix.set_height(1.5)
matrix.next_to(lhs, RIGHT)
group = VGroup(lhss, matrices)
group.move_to(2 * LEFT)
return group
class SpinMeasurements(ThreeDScene):
def construct(self):
# Reorient
frame = self.camera.frame
frame.reorient(-70, 70)
frame.add_updater(lambda m, dt: m.increment_theta(0.01 * dt))
self.add(frame)
# Create machine
north_verts = [UL + 0.5 * DOWN, UR + 0.5 * DOWN, DR, 2 * DOWN, DL]
south_verts = [DOWN + 2 * LEFT, UP + 2 * LEFT, UL, LEFT, RIGHT, UR, UP + 2 * RIGHT, DOWN + 2 * RIGHT]
north_bar = get_prism(north_verts, depth=2)
south_bar = get_prism(south_verts, depth=2)
N_text = Text("N").move_to(north_bar, OUT).shift(0.025 * OUT)
S_text = Text("S").move_to(south_bar, OUT).shift(0.025 * OUT)
S_text.set_y(-0.5)
north_bar.add(N_text)
south_bar.add(S_text)
south_bar.move_to(ORIGIN, UP).shift(0.5 * UP)
north_bar.move_to(south_bar.get_top(), DOWN)
machine = VGroup(north_bar, south_bar)
machine.set_stroke(width=0)
# for bar in machine:
# bar.space_out_submobjects(1.1)
# Screen
screen = FullScreenRectangle()
axes = Axes((-3, 3), (-3, 3))
for sm in axes.get_family():
sm.flat_stroke = False
sm.insert_n_curves(10)
axes.add(OldTex("+z").next_to(axes.c2p(0, 2), RIGHT))
axes.add(OldTex("-z").next_to(axes.c2p(0, -2), RIGHT))
axes.shift(0.1 * OUT)
screen.set_height(5)
screen.set_fill(GREY_D, 1)
screen.set_stroke(WHITE, 2)
screen.shift(5 * IN)
axes.shift(5 * IN)
# Rotate all
everything = VGroup(screen, axes, machine)
everything.apply_depth_test()
self.add(everything)
self.update_frame() # Why?
everything.rotate(89.5 * DEGREES, RIGHT, about_point=ORIGIN)
self.play(
FadeIn(screen),
FadeIn(axes),
FadeIn(machine, lag_ratio=0.05),
)
self.wait()
# Paths
def path(t):
if t < -1:
z = 0
elif t < 1:
z = (1 / 4) * (t + 1)**2
else:
z = t
return [0, t, z]
high_beam = ParametricCurve(path, (-5, 5))
high_beam.set_stroke(BLUE, 10)
high_beam.set_depth(1, stretch=True, about_point=ORIGIN)
high_beam.apply_depth_test()
low_beam = high_beam.copy()
low_beam.stretch(-1, 2, about_point=ORIGIN)
def hit_anim(spin=1):
circ = Circle(radius=0.5)
circ.set_stroke(BLUE, 0)
circ.rotate(90 * DEGREES, RIGHT)
circ.move_to(5 * UP + spin * OUT)
tex = OldTex("+1" if spin == 1 else "-1")
tex.shift(20 * OUT)
self.add(tex)
self.update_frame()
tex.rotate(90 * DEGREES, RIGHT)
tex.move_to(circ.get_left())
pre_circ = circ.copy()
pre_circ.scale(0)
pre_circ.set_stroke(BLUE, 10)
return AnimationGroup(
Transform(pre_circ, circ, remover=True),
VFadeInThenOut(tex),
)
for x in range(20):
if random.random() < 0.5:
beam = high_beam
spin = +1
else:
beam = low_beam
spin = -1
self.play(LaggedStart(
VShowPassingFlash(beam, time_width=0.5),
hit_anim(spin),
lag_ratio=0.6
))
class HeresTheThing(TeacherStudentsScene):
def construct(self):
self.student_says(
"Wait, why?",
target_mode="raise_right_hand",
look_at=self.screen,
index=2,
)
self.play(
self.change_students("maybe", "confused", "raise_right_hand", look_at=self.screen),
self.teacher.change("happy"),
)
self.wait(3)
self.teacher_says(
OldTexText("Here's the thing..."),
added_anims=[self.change_students("sassy", "plain", "hesitant")],
target_mode="hesitant",
)
self.wait(2)
self.play(RemovePiCreatureBubble(self.teacher, target_mode="raise_right_hand"))
for pi in self.pi_creatures:
for eye in pi.eyes:
eye.refresh_bounding_box()
self.look_at(self.screen)
self.play_student_changes("tease", "thinking", "happy", look_at=self.screen)
self.wait(3)
words = OldTexText("Somewhat\\\\self-defeating")
words.next_to(self.screen, RIGHT)
words.shift(RIGHT)
self.play(
self.teacher.change("guilty"),
self.change_students("sassy", "hesitant", "sassy"),
Write(words),
)
self.wait(4)
class NotAllHopeIsLost(TeacherStudentsScene):
def construct(self):
self.teacher_says(
OldTexText("There's still a\\\\good example here"),
target_mode="speaking",
bubble_config={"height": 3, "width": 4},
added_anims=[self.change_students("erm", "sassy", "hesitant")],
)
self.wait(2)
class PauliMatricesWithCharacteristicPolynomial(PauliMatrices):
def construct(self):
# Set up determinants
colors = [RED, GREEN, BLUE]
lhss, matrices = group = self.get_lhss_and_matrices(colors)
group.to_edge(LEFT)
kw = {
"element_to_mobject_config": {
"tex_to_color_map": {
"-\\lambda": EIGEN_COLORS[1],
}
},
"v_buff": 0.7,
"h_buff": 1.3,
}
new_matrices = VGroup(
Matrix([["-\\lambda", "1"], ["1", "-\\lambda"]], **kw),
Matrix([["-\\lambda", "-i"], ["i", "-\\lambda"]], **kw),
Matrix([["1 -\\lambda", "0"], ["0", "-1 -\\lambda"]], **kw),
)
arrows = VGroup()
det_terms = VGroup()
for mat, new_mat in zip(matrices, new_matrices):
mat.set_height(1.0, about_edge=LEFT)
new_mat.replace(mat, dim_to_match=1)
new_mat.shift(3.75 * RIGHT)
parens = OldTex("()", font_size=60)[0]
parens.match_height(new_mat, stretch=True)
parens[0].next_to(new_mat, LEFT, buff=0.1)
parens[1].next_to(new_mat, RIGHT, buff=0.1)
det = Text("det", font_size=30)
det.next_to(parens, LEFT, SMALL_BUFF)
det_terms.add(VGroup(det, parens, new_mat))
arrows.add(Arrow(mat, det, buff=0.2))
self.add(lhss, matrices)
self.wait()
self.play(
LaggedStart(*(
TransformMatchingShapes(mat.copy(), det_term)
for mat, det_term in zip(matrices, det_terms)
), lag_ratio=0.8),
LaggedStartMap(GrowArrow, arrows, lag_ratio=0.8),
)
self.wait()
# Polynomials
kw = {"tex_to_color_map": {
"\\lambda": EIGEN_COLORS[1],
"=": WHITE,
}}
rhss = VGroup(
OldTex("= \\lambda^2 - 1 = 0", **kw),
OldTex("= \\lambda^2 - 1 = 0", **kw),
OldTex("= (1 - \\lambda)(-1 - \\lambda) = 0", **kw),
)
lpm1s = VGroup()
for rhs, det_term in zip(rhss, det_terms):
rhs.next_to(det_term, RIGHT, SMALL_BUFF, submobject_to_align=rhs[0])
self.play(LaggedStart(
det_path_anim(det_term[-1]),
TransformMatchingShapes(det_term[-1].get_entries().copy(), rhs),
lag_ratio=0.5
))
if rhs is rhss[-1]:
continue
self.play(FlashUnder(rhs))
self.wait()
lpm1 = OldTex("\\lambda = \\pm 1", **kw)
lpm1.next_to(rhs, DOWN, buff=0.75)
lpm1s.add(lpm1)
self.play(TransformMatchingShapes(rhs[1:4].copy(), lpm1))
self.wait()
# Comment on last
rect = SurroundingRectangle(matrices[2])
words = Text("Already diagonal!", font_size=24)
words.next_to(rect, UP)
words.set_color(YELLOW)
diag_rects = get_diag_rects(matrices[2], color=EIGEN_COLORS[1])
self.play(FadeInFromLarge(rect))
self.play(Write(words, run_time=1))
self.wait()
self.play(ShowCreation(diag_rects))
self.wait()
self.play(LaggedStartMap(FadeOut, VGroup(
*arrows, *det_terms, *rhss, *lpm1s,
rect, words, diag_rects,
)))
# Show combinations
colors = [YELLOW_B, YELLOW_C, YELLOW_D]
def get_combo(triplet):
group = VGroup(
OldTex("a"), triplet[0].copy(), OldTex("+"),
OldTex("b"), triplet[1].copy(), OldTex("+"),
OldTex("c"), triplet[2].copy()
)
group.arrange(RIGHT, buff=0.15)
group[3].align_to(group[0], DOWN)
group[6].align_to(group[0], DOWN)
group.set_color(WHITE)
group[0::3].set_submobject_colors_by_gradient(*colors)
return group
true_lhss = VGroup(*(lhs[0][:2] for lhs in lhss))
row1 = get_combo(true_lhss)
row1[1::3].shift(0.06 * DOWN)
row2 = get_combo(matrices)
rows = VGroup(row1, row2)
rows.arrange(DOWN, buff=LARGE_BUFF)
rows.to_corner(UR, buff=LARGE_BUFF)
kw = {"tex_to_color_map": {
"a": colors[0],
"b": colors[1],
"c": colors[2],
"\\lambda": EIGEN_COLORS[1],
}}
normalized_eq = OldTex("\\left(a^2 + b^2 + c^2 = 1\\right)", **kw)
normalized_eq.next_to(row2, DOWN, LARGE_BUFF)
normalized_eq.to_edge(DOWN)
full_mat = Matrix(
[
["c", "a - bi"],
["a + bi", "-c"],
],
element_to_mobject_config=kw,
h_buff=1.5
)
full_mat.next_to(row2, DOWN, LARGE_BUFF)
self.play(TransformMatchingShapes(true_lhss.copy(), row1))
self.wait()
self.play(FadeTransformPieces(row1.copy(), row2))
self.wait()
self.play(TransformMatchingShapes(row2.copy(), full_mat))
self.wait() # Let some physics happen
self.play(TransformMatchingShapes(row2[0::3].copy(), normalized_eq))
self.wait()
# Talk about eigen computations
self.play(FadeOut(lhss), FadeOut(matrices))
randy = Randolph()
randy.set_height(2)
randy.next_to(full_mat, LEFT).to_edge(DOWN)
randy.set_opacity(0)
diag_rects = get_diag_rects(full_mat)
bubble = ThoughtBubble(height=2, width=2)
bubble.pin_to(randy)
m_eq = OldTex("m = 0", tex_to_color_map={"m": MEAN_COLOR})
p_eq = OldTex("p = ??", tex_to_color_map={"p": PROD_COLOR})
bubble.position_mobject_inside(m_eq)
bubble.position_mobject_inside(p_eq)
p_eq2 = OldTex("p = -1", tex_to_color_map={"p": PROD_COLOR})
p_eq2.next_to(full_mat, RIGHT, aligned_edge=DOWN)
self.play(
VFadeIn(randy),
randy.animate.set_opacity(1).change("hesitant", full_mat)
)
self.play(ShowCreation(diag_rects))
self.play(
Write(bubble),
Write(m_eq),
randy.change("pondering", bubble)
)
self.play(randy.change("thinking", bubble))
self.play(Blink(randy))
self.wait()
self.play(
FadeIn(p_eq, 0.25 * UP),
m_eq.animate.next_to(full_mat, RIGHT, aligned_edge=UP),
randy.change("hesitant", full_mat),
FadeOut(diag_rects),
)
self.play(det_path_anim(full_mat))
self.play(Blink(randy))
self.wait()
self.play(
FadeOut(bubble),
randy.change("tease", p_eq2),
Transform(p_eq, p_eq2),
)
self.play(Blink(randy))
self.wait()
# Characteristic polynomial
mod_mat = Matrix(
[
["c - \\lambda", "a - bi"],
["a + bi", "-c - \\lambda"],
],
element_to_mobject_config=kw,
h_buff=2.0
)
parens = OldTex("(", ")")
parens.stretch(2, 1)
parens.match_height(mod_mat)
parens[0].next_to(mod_mat, LEFT, SMALL_BUFF)
parens[1].next_to(mod_mat, RIGHT, SMALL_BUFF)
det = Text("det")
det.next_to(parens, LEFT, SMALL_BUFF)
char_poly = VGroup(det, parens, mod_mat)
char_poly.next_to(randy, UL)
self.play(
TransformMatchingShapes(full_mat.copy(), char_poly, run_time=2),
randy.change("raise_left_hand", char_poly),
)
self.play(randy.change("horrified", char_poly))
self.play(Blink(randy))
self.wait()
self.play(randy.change("tired"))
self.wait()
class ThreeSpinExamples(Scene):
def construct(self):
frame = self.camera.frame
frame.focal_distance = 20
frame.reorient(-20, 70)
axes = ThreeDAxes(
(-1, 1), (-1, 1), (-1, 1),
axis_config={"tick_size": 0, "include_tip": False}
)
axes.insert_n_curves(10)
axes.flat_stroke = False
axes.set_stroke(WHITE, 3)
axes.apply_depth_test()
all_axes = axes.get_grid(3, 1)
all_axes.arrange(IN, buff=0.8)
self.add(all_axes)
for axes, vect, color in zip(all_axes, [RIGHT, UP, OUT], [RED, GREEN, BLUE]):
sphere = Sphere()
sphere.scale(0.9)
mesh = SurfaceMesh(sphere, resolution=(21, 11))
mesh.set_stroke(color, width=1, opacity=0.8)
mesh.apply_matrix(z_to_vector(vect))
sphere = Group(sphere, mesh)
sphere.scale(0.5)
sphere.move_to(axes)
axes.sphere = sphere
sphere.vect = vect
sphere.add_updater(lambda m, dt: m.rotate(dt, m.vect))
self.add(sphere)
self.wait(2 * TAU)
class GeneralDirection(Scene):
def construct(self):
frame = self.camera.frame
frame.reorient(-30, 70)
frame.add_updater(lambda m, dt: m.increment_theta(0.01 * dt))
self.add(frame)
axes = ThreeDAxes(
(-2, 2), (-2, 2), (-4, 4),
axis_config={'include_tip': False},
depth=6,
)
axes.set_stroke(width=1)
self.add(axes)
vect = axes.c2p(2, 2, 2)
vector = Vector(vect)
vector.set_fill(YELLOW)
label = Matrix([["a"], ["b"], ["c"]], v_buff=0.6)
# label.get_entries().set_submobject_colors_by_gradient(
# YELLOW_B, YELLOW_C, YELLOW_D
# )
label.get_entries().set_color(YELLOW)
label.rotate(89 * DEGREES, RIGHT)
label.next_to(vector.get_end(), RIGHT)
sphere = Sphere()
sphere.scale(0.5)
mesh = SurfaceMesh(sphere, resolution=(21, 11))
mesh.set_stroke(BLUE, width=0.5, opacity=0.5)
for mob in sphere, mesh:
mob.apply_matrix(z_to_vector(vect))
mob.add_updater(lambda m, dt: m.rotate(0.75 * dt, axis=vect))
axes.apply_depth_test()
for sm in axes.get_family():
sm.insert_n_curves(10)
self.add(vector, sphere, mesh)
self.play(
GrowArrow(vector),
UpdateFromAlphaFunc(sphere, lambda m, a: m.set_opacity(a)),
Write(mesh),
)
self.play(Write(label))
self.wait(6)
self.play(
vector.animate.scale(0.5, about_point=ORIGIN),
label.animate.shift(-0.5 * vect)
)
self.wait(24)
class TwoValuesEvenlySpaceAroundZero(Scene):
def construct(self):
nl = NumberLine((-2, 2, 0.25), width=6)
nl.add_numbers(np.arange(-2, 3, 1.0), num_decimal_places=1, font_size=24)
d_tracker = ValueTracker(2)
get_d = d_tracker.get_value
dots = VGroup(*(Dot() for x in range(2)))
labels = VGroup(OldTex("\\lambda_1"), OldTex("\\lambda_2"))
for group in dots, labels:
group.set_submobject_colors_by_gradient(*EIGEN_COLORS)
def update_dots(dots):
dots[0].move_to(nl.n2p(-get_d()))
dots[1].move_to(nl.n2p(get_d()))
def update_labels(labels):
for label, dot in zip(labels, dots):
label.match_color(dot)
label.next_to(dot, UP, SMALL_BUFF)
dots.add_updater(update_dots)
labels.add_updater(update_labels)
prod_label = VGroup(
labels.copy().clear_updaters().arrange(RIGHT, buff=SMALL_BUFF),
OldTex("="),
DecimalNumber(-4),
)
prod_label[-1].match_height(prod_label[0])
prod_label.arrange(RIGHT)
prod_label.next_to(labels, UP, LARGE_BUFF)
prod_label[-1].add_updater(lambda m: m.set_value(-get_d()**2))
self.add(nl, dots, labels, prod_label)
self.play(d_tracker.animate.set_value(0.5), run_time=3)
self.play(d_tracker.animate.set_value(1.5), run_time=3)
self.play(d_tracker.animate.set_value(1.0), run_time=2)
self.wait()
class MPIsSolvingCharPoly(TeacherStudentsScene):
def construct(self):
formula = OldTex(
"{m} \\pm \\sqrt{\\,{m}^2 - {p}}",
tex_to_color_map={"{m}": MEAN_COLOR, "{p}": PROD_COLOR},
font_size=72
)
char_poly = get_det_mod_mat(get_mod_mat([["a", "b"], ["c", "d"]]))
eq0 = OldTex("=0")
eq0.next_to(char_poly, RIGHT, SMALL_BUFF)
char_poly.add(eq0)
char_poly.move_to(self.hold_up_spot, DOWN)
arrow = Arrow(RIGHT, LEFT)
arrow.next_to(char_poly, LEFT)
self.teacher_holds_up(formula)
self.play_student_changes(
"happy", "thinking", "tease",
look_at=formula
)
self.wait()
self.play(
self.teacher.change("hooray", char_poly),
formula.animate.next_to(arrow, LEFT),
FadeIn(char_poly, 0.5 * UP),
)
self.play_student_changes(
"erm", "pondering", "pondering",
look_at=char_poly,
added_anims=[GrowArrow(arrow)]
)
self.wait(3)
class QuadraticPolynomials(Scene):
def construct(self):
# Set up equation
kw = {
"tex_to_color_map": {
"\\lambda_1": EIGEN_COLORS[0],
"\\lambda_2": EIGEN_COLORS[1],
"=": WHITE,
}
}
equation = VGroup(
OldTex("x^2 - 10x + 9", **kw)[0],
OldTex("=").rotate(PI / 2),
OldTex("x^2 - (\\lambda_1 + \\lambda_2)x + \\lambda_1 \\lambda_2", **kw),
OldTex("=").rotate(PI / 2),
OldTex("(x - \\lambda_1)(x - \\lambda_2)", **kw),
)
equation.arrange(DOWN)
equation.to_edge(LEFT)
line1, eq1, line2, eq2, line3 = equation
for line in line2, line3:
line.set_submobjects(line.family_members_with_points())
line3.save_state()
line3.move_to(line2)
# Graph
axes = Axes(
(-5, 10),
(-20, 20),
height=7,
width=FRAME_WIDTH / 2,
)
axes.y_axis.ticks.stretch(0.75, 0)
axes.to_edge(RIGHT)
graph = axes.get_graph(lambda x: x**2 - 10 * x + 9)
graph.set_color(BLUE)
graph_label = line1.copy()
graph_label.set_color(BLUE)
graph_label.next_to(graph.get_end(), UP)
graph_label.to_edge(RIGHT)
root_dots = VGroup()
root_labels = VGroup()
for i, n, vect in zip((0, 1), (1, 9), (RIGHT, LEFT)):
dot = Dot(axes.c2p(n, 0), color=EIGEN_COLORS[i])
label = OldTex(f"\\lambda_{i + 1}")
label.match_color(dot)
label.next_to(dot, UP + vect, buff=0.05)
root_dots.add(dot)
root_labels.add(label)
root_dots.set_stroke(BLACK, 5, background=True)
self.play(Write(axes))
self.play(
ShowCreation(graph, run_time=3),
FadeIn(graph_label, UP)
)
self.play(
LaggedStartMap(GrowFromCenter, root_dots),
LaggedStart(
GrowFromPoint(root_labels[0], root_dots[0].get_center()),
GrowFromPoint(root_labels[1], root_dots[1].get_center()),
)
)
self.wait()
# Animate to equation
self.play(TransformFromCopy(graph_label, line1))
self.play(
Write(eq1),
TransformMatchingShapes(root_labels.copy(), line3)
)
self.wait()
self.play(Restore(line3))
xs = VGroup(line3[1], line3[7])
self.play(TransformFromCopy(xs, line2[:2])) # x^2
self.play(LaggedStart( # x
AnimationGroup(
TransformFromCopy(line3[1], line2[10].copy()),
TransformFromCopy(line3[9:11], line2[7:9]),
FadeIn(line2[2])
),
AnimationGroup(
TransformFromCopy(line3[7], line2[10].copy(), remover=True),
TransformFromCopy(line3[3:5], line2[4:6]),
FadeIn(VGroup(line2[3], line2[6], line2[9]))
),
))
self.play(
TransformFromCopy(
VGroup(*line3[3:5], *line3[9:11]),
VGroup(*line2[12:16]),
),
FadeIn(line2[11]),
FadeIn(eq2),
)
self.wait()
# Highlight sum and product
for i, j, k, l in [(3, 5, 3, 10), (7, 8, 12, 16)]:
self.play(
FadeIn(SurroundingRectangle(line1[i:j])),
FadeIn(SurroundingRectangle(line2[k:l])),
run_time=2,
rate_func=there_and_back_with_pause,
remover=True
)
# Show mean and product
line2 = VGroup(*line2.family_members_with_points())
line3 = VGroup(*line3.family_members_with_points())
quad_terms = VGroup(line1[0:2], line2[0:2])
lin_terms = VGroup(line1[2:5], line2[2:10])
const_terms = VGroup(line1[6:], line2[11:])
mean_arrow = Vector(UP)
mean_arrow.next_to(lin_terms[0], UP, MED_SMALL_BUFF)
times_half = OldTex("\\times -\\frac{1}{2}", font_size=24)
times_half.next_to(mean_arrow, RIGHT, buff=0)
m_eq = OldTex(
"{\\lambda_1 + \\lambda_2 \\over 2}", "=", "5",
tex_to_color_map={"\\lambda_1": EIGEN_COLORS[0], "\\lambda_2": EIGEN_COLORS[1]}
)
m_eq.next_to(mean_arrow, UP, SMALL_BUFF, submobject_to_align=m_eq[-1])
m_eq[:-2].scale(0.7, about_edge=RIGHT)
m_dot = Dot(axes.c2p(5, 0))
m_dot.scale(0.5)
m_label = Integer(5, font_size=30)
m_label.next_to(m_dot, UP, SMALL_BUFF)
p_rects = VGroup(*map(SurroundingRectangle, const_terms))
p_rects.set_stroke(PROD_COLOR)
fade_rect = FullScreenFadeRectangle()
fade_rect.set_fill(BLACK, 0.75)
fade_rect.replace(equation, stretch=True)
self.add(fade_rect, *quad_terms)
self.play(FadeIn(fade_rect))
self.wait()
self.add(fade_rect, *lin_terms)
self.wait()
self.play(
GrowArrow(mean_arrow),
FadeIn(times_half, shift=0.25 * UP, scale=2),
FadeIn(m_eq[:-1])
)
self.play(Write(m_eq[-1]))
self.play(
FadeTransform(m_eq[-1].copy(), m_dot),
FadeTransform(m_eq[-1].copy(), m_label),
)
self.wait()
self.add(fade_rect, *const_terms)
VGroup(mean_arrow, times_half).set_opacity(0.5)
self.play(ShowCreation(p_rects))
self.wait()
# Show final roots
d_terms = VGroup(*(
OldTex(u, "\\sqrt{25 - 9}", font_size=24)
for u in ["+", "-"]
))
my = m_label.get_y()
arrows = VGroup(
Arrow(m_label.get_left(), [root_dots[0].get_left()[0], my, 0], buff=0.1),
Arrow(m_label.get_right(), [root_dots[1].get_right()[0], my, 0], buff=0.1),
)
for d_term, arrow in zip(d_terms, arrows):
d_term.next_to(arrow, UP, buff=0)
self.play(FadeOut(p_rects))
self.play(
TransformMatchingShapes(equation[0][-2:].copy(), d_terms),
FadeOut(root_labels),
*map(GrowArrow, arrows)
)
self.wait()
self.play(
FadeOut(fade_rect),
)
self.wait()
class SimplerQuadraticFormula(Scene):
def construct(self):
# Show comparison
form1 = OldTex(
"{m} \\pm \\sqrt{\\,{m}^2 - {p}}",
tex_to_color_map={"{m}": MEAN_COLOR, "{p}": PROD_COLOR},
font_size=72
)
words = OldTexText("takes less to\\\\remember than")
form2 = OldTex(
"{-b \\pm \\sqrt{\\,b^2 - 4ac} \\over 2a}",
tex_to_color_map={
"a": MAROON_A,
"b": MAROON_B,
"c": MAROON_C,
},
font_size=72
)
group = VGroup(form1, words, form2)
group.arrange(DOWN, buff=1.5)
self.add(form1)
self.play(Write(words, run_time=1))
self.play(FadeIn(form2, DOWN))
self.wait()
class SkipTheMiddleStep(Scene):
def construct(self):
matrix = IntegerMatrix([[3, 1], [4, 1]])
matrix.set_column_colors(*COL_COLORS)
char_poly = OldTex(
"\\lambda^2 - 4\\lambda - 1",
tex_to_color_map={"\\lambda": EIGEN_COLORS[0]}
)
mp_formula = OldTex(
"{2} \\pm \\sqrt{\\,{2}^2 - (-1)}",
tex_to_color_map={
"{2}": MEAN_COLOR,
"(-1)": PROD_COLOR,
}
)
group = VGroup(matrix, char_poly, mp_formula)
group.arrange(RIGHT, buff=2)
mat_label, char_poly_label, eigen_label = labels = VGroup(
OldTexText("Matrix"),
OldTexText("Characteristic\\\\polynomial"),
OldTexText("Eigenvalues"),
)
for label, mob, color in zip(labels, group, [COL_COLORS[0], EIGEN_COLORS[0], MEAN_COLOR]):
label.set_color(color)
label.next_to(mob, DOWN, MED_LARGE_BUFF)
label.align_to(labels[0], UP)
arc = -90 * DEGREES
arrows = VGroup(
Arrow(matrix.get_corner(UR), char_poly.get_top(), path_arc=arc),
Arrow(char_poly.get_top(), mp_formula.get_top(), path_arc=arc),
Arrow(matrix.get_corner(UR), mp_formula.get_top(), path_arc=0.8 * arc),
)
arrows.shift(0.5 * UP)
self.add(matrix, mat_label)
self.play(
DrawBorderThenFill(arrows[0]),
GrowFromPoint(char_poly, matrix.get_top(), path_arc=arc),
FadeTransform(mat_label.copy(), char_poly_label),
)
self.wait()
self.play(
DrawBorderThenFill(arrows[1]),
TransformFromCopy(char_poly, mp_formula),
FadeTransform(char_poly_label.copy(), eigen_label),
)
self.wait()
self.play(
VFadeInThenOut(get_diag_rects(matrix), run_time=2),
LaggedStart(*(
ShowCreationThenFadeOut(SurroundingRectangle(sm, buff=0.1, color=WHITE), run_time=2)
for sm in mp_formula.get_parts_by_tex("{2}")
))
)
self.play(
det_path_anim(matrix),
ShowCreationThenFadeOut(SurroundingRectangle(
mp_formula.get_parts_by_tex("(-1)"), buff=0.1, color=WHITE,
), run_time=2)
)
self.wait()
self.play(
Transform(arrows[0], arrows[2]),
Transform(arrows[1], arrows[2]),
VGroup(char_poly, char_poly_label).animate.set_opacity(0.15),
)
self.wait()
#
frame = self.camera.frame
quad_form = OldTex(
"{-b \\pm \\sqrt{\\,b^2 - 4ac} \\over 2a}",
tex_to_color_map={
"a": BLUE_B,
"b": BLUE_C,
"c": BLUE_D,
}
)
words = OldTexText("Never could\\\\have worked!")
group = VGroup(quad_form, words)
group.arrange(RIGHT, buff=LARGE_BUFF)
group.next_to(arrows, UP, LARGE_BUFF)
cross = Cross(quad_form)
cross.set_stroke(width=(1, 3, 3, 1))
self.play(
frame.animate.set_y(1),
FadeIn(quad_form, DOWN),
)
self.wait()
self.play(
Write(words),
ShowCreation(cross)
)
self.wait()
class GeneralCharPoly(Scene):
def construct(self):
t2c = {
"a": COL_COLORS[0],
"b": COL_COLORS[1],
"c": COL_COLORS[0],
"d": COL_COLORS[1],
"\\lambda": EIGEN_COLORS[1],
"-": WHITE,
"+": WHITE,
"=": WHITE,
}
det = get_det_mod_mat(get_mod_mat([["a", "b"], ["c", "d"]], t2c=t2c))
rhs1 = OldTex("= (a - \\lambda)(d - \\lambda) - bc", tex_to_color_map=t2c)
rhs2 = OldTex("= \\lambda^2 - (a + d)\\lambda + (ad - bc)", tex_to_color_map=t2c)
rhs1.next_to(det, RIGHT)
rhs2.next_to(rhs1, DOWN, buff=LARGE_BUFF, aligned_edge=LEFT)
VGroup(det, rhs1, rhs2).center()
self.add(det)
self.wait()
self.play(TransformMatchingShapes(
det[-1].get_entries()[0::3].copy(),
rhs1[:10],
))
self.play(TransformMatchingShapes(
det[-1].get_entries()[1:3].copy(),
rhs1[10:],
path_arc=45 * DEGREES,
))
self.wait()
self.play(TransformMatchingShapes(rhs1.copy(), rhs2, run_time=1.5))
self.wait()
self.play(ShowCreation(SurroundingRectangle(rhs2[3:9], buff=0.05, color=MEAN_COLOR)))
self.play(ShowCreation(SurroundingRectangle(rhs2[11:], buff=0.05, color=PROD_COLOR)))
self.wait()
self.embed()
class EndScreen(PatreonEndScreen):
pass