3b1b-manim/old_projects/eola/chapter7.py
2019-02-08 15:53:27 -08:00

2311 lines
75 KiB
Python

from big_ol_pile_of_manim_imports import *
from old_projects.eola.footnote2 import TwoDTo1DTransformWithDots
V_COLOR = YELLOW
W_COLOR = MAROON_B
SUM_COLOR = PINK
def get_projection(vector_to_project, stable_vector):
v1, v2 = stable_vector, vector_to_project
return v1*np.dot(v1, v2)/(get_norm(v1)**2)
def get_vect_mob_projection(vector_to_project, stable_vector):
return Vector(
get_projection(
vector_to_project.get_end(),
stable_vector.get_end()
),
color = vector_to_project.get_color()
).fade()
class OpeningQuote(Scene):
def construct(self):
words = TextMobject(
"\\small Calvin:",
"You know, I don't think math is a science, I think it's a religion.",
"\\\\Hobbes:",
"A religion?",
"\\\\Calvin:" ,
"Yeah. All these equations are like miracles."
"You take two numbers and when you add them, "
"they magically become one NEW number! "
"No one can say how it happens. "
"You either believe it or you don't.",
)
words.set_width(FRAME_WIDTH - 1)
words.to_edge(UP)
words[0].set_color(YELLOW)
words[2].set_color("#fd9c2b")
words[4].set_color(YELLOW)
for i in range(3):
speaker, quote = words[2*i:2*i+2]
self.play(FadeIn(speaker, run_time = 0.5))
rt = max(1, len(quote.split())/18)
self.play(Write(
quote,
run_time = rt,
lag_factor = 5 if rt > 3 else 2,
))
self.wait(2)
class TraditionalOrdering(RandolphScene):
def construct(self):
title = TextMobject("Traditional ordering:")
title.set_color(YELLOW)
title.scale(1.2)
title.to_corner(UP+LEFT)
topics = VMobject(*list(map(TextMobject, [
"Topic 1: Vectors",
"Topic 2: Dot products",
"\\vdots",
"(everything else)",
"\\vdots",
])))
topics.arrange(DOWN, aligned_edge = LEFT, buff = SMALL_BUFF)
# topics.next_to(title, DOWN+RIGHT)
self.play(
Write(title, run_time = 1),
FadeIn(
topics,
run_time = 3,
lag_ratio = 0.5
),
)
self.play(topics[1].set_color, PINK)
self.wait()
class ThisSeriesOrdering(RandolphScene):
def construct(self):
title = TextMobject("Essence of linear algebra")
self.randy.rotate(np.pi, UP)
title.scale(1.2).set_color(BLUE)
title.to_corner(UP+LEFT)
line = Line(FRAME_X_RADIUS*LEFT, FRAME_X_RADIUS*RIGHT, color = WHITE)
line.next_to(title, DOWN, buff = SMALL_BUFF)
line.to_edge(LEFT, buff = 0)
chapters = VMobject(*[
TextMobject("\\small " + text)
for text in [
"Chapter 1: Vectors, what even are they?",
"Chapter 2: Linear combinations, span and bases",
"Chapter 3: Matrices as linear transformations",
"Chapter 4: Matrix multiplication as composition",
"Chapter 5: The determinant",
"Chapter 6: Inverse matrices, column space and null space",
"Chapter 7: Dot products and duality",
"Chapter 8: Cross products via transformations",
"Chapter 9: Change of basis",
"Chapter 10: Eigenvectors and eigenvalues",
"Chapter 11: Abstract vector spaces",
]
])
chapters.arrange(
DOWN, buff = SMALL_BUFF, aligned_edge = LEFT
)
chapters.set_height(1.5*FRAME_Y_RADIUS)
chapters.next_to(line, DOWN, buff = SMALL_BUFF)
chapters.to_edge(RIGHT)
self.add(title)
self.play(
ShowCreation(line),
)
self.play(
FadeIn(
chapters,
lag_ratio = 0.5,
run_time = 3
),
self.randy.change_mode, "sassy"
)
self.play(self.randy.look, UP+LEFT)
self.play(chapters[6].set_color, PINK)
self.wait(6)
class OneMustViewThroughTransformations(TeacherStudentsScene):
def construct(self):
words = TextMobject(
"Only with" , "transformations",
"\n can we truly understand",
)
words.set_color_by_tex("transformations", BLUE)
self.teacher_says(words)
self.change_student_modes(
"pondering",
"plain",
"raise_right_hand"
)
self.random_blink(2)
words = TextMobject(
"First, the ",
"standard view...",
arg_separator = "\n"
)
self.teacher_says(words)
self.random_blink(2)
class ShowNumericalDotProduct(Scene):
CONFIG = {
"v1" : [2, 7, 1],
"v2" : [8, 2, 8],
"write_dot_product_words" : True,
}
def construct(self):
v1 = Matrix(self.v1)
v2 = Matrix(self.v2)
inter_array_dot = TexMobject("\\cdot").scale(1.5)
dot_product = VGroup(v1, inter_array_dot, v2)
dot_product.arrange(RIGHT, buff = MED_SMALL_BUFF/2)
dot_product.to_edge(LEFT)
pairs = list(zip(v1.get_entries(), v2.get_entries()))
for pair, color in zip(pairs, [X_COLOR, Y_COLOR, Z_COLOR, PINK]):
VGroup(*pair).set_color(color)
dot = TexMobject("\\cdot")
products = VGroup(*[
VGroup(
p1.copy(), dot.copy(), p2.copy()
).arrange(RIGHT, buff = SMALL_BUFF)
for p1, p2 in pairs
])
products.arrange(DOWN, buff = LARGE_BUFF)
products.next_to(dot_product, RIGHT, buff = LARGE_BUFF)
products.target = products.copy()
plusses = ["+"]*(len(self.v1)-1)
symbols = VGroup(*list(map(TexMobject, ["="] + plusses)))
final_sum = VGroup(*it.chain(*list(zip(
symbols, products.target
))))
final_sum.arrange(RIGHT, buff = SMALL_BUFF)
final_sum.next_to(dot_product, RIGHT)
title = TextMobject("Two vectors of the same dimension")
title.to_edge(UP)
arrow = Arrow(DOWN, UP).next_to(inter_array_dot, DOWN)
dot_product_words = TextMobject("Dot product")
dot_product_words.set_color(YELLOW)
dot_product_words.next_to(arrow, DOWN)
dot_product_words.shift_onto_screen()
self.play(
Write(v1),
Write(v2),
FadeIn(inter_array_dot),
FadeIn(title)
)
self.wait()
if self.write_dot_product_words:
self.play(
inter_array_dot.set_color, YELLOW,
ShowCreation(arrow),
Write(dot_product_words, run_time = 2)
)
self.wait()
self.play(Transform(
VGroup(*it.starmap(Group, pairs)).copy(),
products,
path_arc = -np.pi/2,
run_time = 2
))
self.remove(*self.get_mobjects_from_last_animation())
self.add(products)
self.wait()
self.play(
Write(symbols),
Transform(products, products.target, path_arc = np.pi/2)
)
self.wait()
class TwoDDotProductExample(ShowNumericalDotProduct):
CONFIG = {
"v1" : [1, 2],
"v2" : [3, 4],
}
class FourDDotProductExample(ShowNumericalDotProduct):
CONFIG = {
"v1" : [6, 2, 8, 3],
"v2" : [1, 8, 5, 3],
"write_dot_product_words" : False,
}
class GeometricInterpretation(VectorScene):
CONFIG = {
"v_coords" : [4, 1],
"w_coords" : [2, -1],
"v_color" : V_COLOR,
"w_color" : W_COLOR,
"project_onto_v" : True,
}
def construct(self):
self.lock_in_faded_grid()
self.add_symbols()
self.add_vectors()
self.line()
self.project()
self.show_lengths()
self.handle_possible_negative()
def add_symbols(self):
v = matrix_to_mobject(self.v_coords).set_color(self.v_color)
w = matrix_to_mobject(self.w_coords).set_color(self.w_color)
v.add_background_rectangle()
w.add_background_rectangle()
dot = TexMobject("\\cdot")
eq = VMobject(v, dot, w)
eq.arrange(RIGHT, buff = SMALL_BUFF)
eq.to_corner(UP+LEFT)
self.play(Write(eq), run_time = 1)
for array, char in zip([v, w], ["v", "w"]):
brace = Brace(array, DOWN)
label = brace.get_text("$\\vec{\\textbf{%s}}$"%char)
label.set_color(array.get_color())
self.play(
GrowFromCenter(brace),
Write(label),
run_time = 1
)
self.dot_product = eq
def add_vectors(self):
self.v = Vector(self.v_coords, color = self.v_color)
self.w = Vector(self.w_coords, color = self.w_color)
self.play(ShowCreation(self.v))
self.play(ShowCreation(self.w))
for vect, char, direction in zip(
[self.v, self.w], ["v", "w"], [DOWN+RIGHT, DOWN]
):
label = TexMobject("\\vec{\\textbf{%s}}"%char)
label.next_to(vect.get_end(), direction)
label.set_color(vect.get_color())
self.play(Write(label, run_time = 1))
self.stable_vect = self.v if self.project_onto_v else self.w
self.proj_vect = self.w if self.project_onto_v else self.v
def line(self):
line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
line.rotate(self.stable_vect.get_angle())
self.play(ShowCreation(line), Animation(self.stable_vect))
self.wait()
def project(self):
dot_product = np.dot(self.v.get_end(), self.w.get_end())
v_norm, w_norm = [
get_norm(vect.get_end())
for vect in (self.v, self.w)
]
projected = Vector(
self.stable_vect.get_end()*dot_product/(
self.stable_vect.get_length()**2
),
color = self.proj_vect.get_color()
)
projection_line = Line(
self.proj_vect.get_end(), projected.get_end(),
color = GREY
)
self.play(ShowCreation(projection_line))
self.add(self.proj_vect.copy().fade())
self.play(Transform(self.proj_vect, projected))
self.wait()
def show_lengths(self):
stable_char = "v" if self.project_onto_v else "w"
proj_char = "w" if self.project_onto_v else "v"
product = TextMobject(
"=",
"(",
"Length of projected $\\vec{\\textbf{%s}}$"%proj_char,
")",
"(",
"Length of $\\vec{\\textbf{%s}}$"%stable_char,
")",
arg_separator = ""
)
product.scale(0.9)
product.next_to(self.dot_product, RIGHT)
proj_words = product[2]
proj_words.set_color(self.proj_vect.get_color())
stable_words = product[5]
stable_words.set_color(self.stable_vect.get_color())
product.remove(proj_words, stable_words)
for words in stable_words, proj_words:
words.add_to_back(BackgroundRectangle(words))
words.start = words.copy()
proj_brace, stable_brace = braces = [
Brace(Line(ORIGIN, vect.get_length()*RIGHT*sgn), UP)
for vect in (self.proj_vect, self.stable_vect)
for sgn in [np.sign(np.dot(vect.get_end(), self.stable_vect.get_end()))]
]
proj_brace.put_at_tip(proj_words.start)
proj_brace.words = proj_words.start
stable_brace.put_at_tip(stable_words.start)
stable_brace.words = stable_words.start
for brace in braces:
brace.rotate(self.stable_vect.get_angle())
brace.words.rotate(self.stable_vect.get_angle())
self.play(
GrowFromCenter(proj_brace),
Write(proj_words.start, run_time = 2)
)
self.wait()
self.play(
Transform(proj_words.start, proj_words),
FadeOut(proj_brace)
)
self.play(
GrowFromCenter(stable_brace),
Write(stable_words.start, run_time = 2),
Animation(self.stable_vect)
)
self.wait()
self.play(
Transform(stable_words.start, stable_words),
Write(product)
)
self.wait()
product.add(stable_words.start, proj_words.start)
self.product = product
def handle_possible_negative(self):
if np.dot(self.w.get_end(), self.v.get_end()) > 0:
return
neg = TexMobject("-").set_color(RED)
neg.next_to(self.product[0], RIGHT)
words = TextMobject("Should be negative")
words.set_color(RED)
words.next_to(
VMobject(*self.product[2:]),
DOWN,
buff = LARGE_BUFF,
aligned_edge = LEFT
)
words.add_background_rectangle()
arrow = Arrow(words.get_left(), neg, color = RED)
self.play(
Write(neg),
ShowCreation(arrow),
VMobject(*self.product[1:]).next_to, neg,
Write(words)
)
self.wait()
class GeometricInterpretationNegative(GeometricInterpretation):
CONFIG = {
"v_coords" : [3, 1],
"w_coords" : [-1, -2],
"v_color" : YELLOW,
"w_color" : MAROON_B,
}
class ShowQualitativeDotProductValues(VectorScene):
def construct(self):
self.lock_in_faded_grid()
v_sym, dot, w_sym, comp, zero = ineq = TexMobject(
"\\vec{\\textbf{v}}",
"\\cdot",
"\\vec{\\textbf{w}}",
">",
"0",
)
ineq.to_edge(UP)
ineq.add_background_rectangle()
comp.set_color(GREEN)
equals = TexMobject("=").set_color(PINK).move_to(comp)
less_than = TexMobject("<").set_color(RED).move_to(comp)
v_sym.set_color(V_COLOR)
w_sym.set_color(W_COLOR)
words = list(map(TextMobject, [
"Similar directions",
"Perpendicular",
"Opposing directions"
]))
for word, sym in zip(words, [comp, equals, less_than]):
word.add_background_rectangle()
word.next_to(sym, DOWN, aligned_edge = LEFT, buff = MED_SMALL_BUFF)
word.set_color(sym.get_color())
v = Vector([1.5, 1.5], color = V_COLOR)
w = Vector([2, 2], color = W_COLOR)
w.rotate(-np.pi/6)
shadow = Vector(
v.get_end()*np.dot(v.get_end(), w.get_end())/(v.get_length()**2),
color = MAROON_E,
preserve_tip_size_when_scaling = False
)
shadow_opposite = shadow.copy().scale(-1)
line = Line(LEFT, RIGHT, color = WHITE)
line.scale(FRAME_X_RADIUS)
line.rotate(v.get_angle())
proj_line = Line(w.get_end(), shadow.get_end(), color = GREY)
word = words[0]
self.add(ineq)
for mob in v, w, line, proj_line:
self.play(
ShowCreation(mob),
Animation(v)
)
self.play(Transform(w.copy(), shadow))
self.remove(*self.get_mobjects_from_last_animation())
self.add(shadow)
self.play(FadeOut(proj_line))
self.play(Write(word, run_time = 1))
self.wait()
self.play(
Rotate(w, -np.pi/3),
shadow.scale, 0
)
self.play(
Transform(comp, equals),
Transform(word, words[1])
)
self.wait()
self.play(
Rotate(w, -np.pi/3),
Transform(shadow, shadow_opposite)
)
self.play(
Transform(comp, less_than),
Transform(word, words[2])
)
self.wait()
class AskAboutSymmetry(TeacherStudentsScene):
def construct(self):
v, w = "\\vec{\\textbf{v}}", "\\vec{\\textbf{w}}",
question = TexMobject(
"\\text{Why does }",
v, "\\cdot", w, "=", w, "\\cdot", v,
"\\text{?}"
)
VMobject(question[1], question[7]).set_color(V_COLOR)
VMobject(question[3], question[5]).set_color(W_COLOR)
self.student_says(
question,
target_mode = "raise_left_hand"
)
self.change_student_modes("confused")
self.play(self.get_teacher().change_mode, "pondering")
self.play(self.get_teacher().look, RIGHT)
self.play(self.get_teacher().look, LEFT)
self.random_blink()
class GeometricInterpretationSwapVectors(GeometricInterpretation):
CONFIG = {
"project_onto_v" : False,
}
class SymmetricVAndW(VectorScene):
def construct(self):
self.lock_in_faded_grid()
v = Vector([3, 1], color = V_COLOR)
w = Vector([1, 3], color = W_COLOR)
for vect, char in zip([v, w], ["v", "w"]):
vect.label = TexMobject("\\vec{\\textbf{%s}}"%char)
vect.label.set_color(vect.get_color())
vect.label.next_to(vect.get_end(), DOWN+RIGHT)
for v1, v2 in (v, w), (w, v):
v1.proj = get_vect_mob_projection(v1, v2)
v1.proj_line = Line(
v1.get_end(), v1.proj.get_end(), color = GREY
)
line_of_symmetry = DashedLine(FRAME_X_RADIUS*LEFT, FRAME_X_RADIUS*RIGHT)
line_of_symmetry.rotate(np.mean([v.get_angle(), w.get_angle()]))
line_of_symmetry_words = TextMobject("Line of symmetry")
line_of_symmetry_words.add_background_rectangle()
line_of_symmetry_words.next_to(ORIGIN, UP+LEFT)
line_of_symmetry_words.rotate(line_of_symmetry.get_angle())
for vect in v, w:
self.play(ShowCreation(vect))
self.play(Write(vect.label, run_time = 1))
self.wait()
angle = (v.get_angle()-w.get_angle())/2
self.play(
Rotate(w, angle),
Rotate(v, -angle),
rate_func = there_and_back,
run_time = 2
)
self.wait()
self.play(
ShowCreation(line_of_symmetry),
Write(line_of_symmetry_words, run_time = 1)
)
self.wait(0.5)
self.remove(line_of_symmetry_words)
self.play(*list(map(Uncreate, line_of_symmetry_words)))
for vect in w, v:
self.play(ShowCreation(vect.proj_line))
vect_copy = vect.copy()
self.play(Transform(vect_copy, vect.proj))
self.remove(vect_copy)
self.add(vect.proj)
self.wait()
self.play(*list(map(FadeOut,[
v.proj, v.proj_line, w.proj, w.proj_line
])))
self.show_doubling(v, w)
def show_doubling(self, v, w):
scalar = 2
new_v = v.copy().scale(scalar)
new_v.label = VMobject(TexMobject("2"), v.label.copy())
new_v.label.arrange(aligned_edge = DOWN)
new_v.label.next_to(new_v.get_end(), DOWN+RIGHT)
new_v.proj = v.proj.copy().scale(scalar)
new_v.proj.fade()
new_v.proj_line = Line(
new_v.get_end(), new_v.proj.get_end(),
color = GREY
)
v_tex, w_tex = ["\\vec{\\textbf{%s}}"%c for c in ("v", "w")]
equation = TexMobject(
"(", "2", v_tex, ")", "\\cdot", w_tex,
"=",
"2(", v_tex, "\\cdot", w_tex, ")"
)
equation.set_color_by_tex(v_tex, V_COLOR)
equation.set_color_by_tex(w_tex, W_COLOR)
equation.next_to(ORIGIN, DOWN).to_edge(RIGHT)
words = TextMobject("Symmetry is broken")
words.next_to(ORIGIN, LEFT)
words.to_edge(UP)
v.save_state()
v.proj.save_state()
v.proj_line.save_state()
self.play(Transform(*[
VGroup(mob, mob.label)
for mob in (v, new_v)
]), run_time = 2)
last_mob = self.get_mobjects_from_last_animation()[0]
self.remove(last_mob)
self.add(*last_mob)
Transform(
VGroup(v.proj, v.proj_line),
VGroup(new_v.proj, new_v.proj_line)
).update(1)##Hacky
self.play(Write(words))
self.wait()
two_v_parts = equation[1:3]
equation.remove(*two_v_parts)
for full_v, projector, stable in zip([False, True], [w, v], [v, w]):
self.play(
Transform(projector.copy(), projector.proj),
ShowCreation(projector.proj_line)
)
self.remove(self.get_mobjects_from_last_animation()[0])
self.add(projector.proj)
self.wait()
if equation not in self.get_mobjects():
self.play(
Write(equation),
Transform(new_v.label.copy(), VMobject(*two_v_parts))
)
self.wait()
v_parts = [v]
if full_v:
v_parts += [v.proj, v.proj_line]
self.play(
*[v_part.restore for v_part in v_parts],
rate_func = there_and_back,
run_time = 2
)
self.wait()
self.play(*list(map(FadeOut, [
projector.proj, projector.proj_line
])))
class LurkingQuestion(TeacherStudentsScene):
def construct(self):
# self.teacher_says("That's the standard intro")
# self.wait()
# self.student_says(
# """
# Wait, why are the
# two views connected?
# """,
# student_index = 2,
# target_mode = "raise_left_hand",
# width = 6,
# )
self.change_student_modes(
"raise_right_hand", "confused", "raise_left_hand"
)
self.random_blink(5)
answer = TextMobject("""
The most satisfactory
answer comes from""",
"duality"
)
answer[-1].set_color_by_gradient(BLUE, YELLOW)
self.teacher_says(answer)
self.random_blink(2)
self.teacher_thinks("")
everything = VMobject(*self.get_mobjects())
self.play(ApplyPointwiseFunction(
lambda p : 10*(p+2*DOWN)/get_norm(p+2*DOWN),
everything
))
class TwoDToOneDScene(LinearTransformationScene):
CONFIG = {
"include_background_plane" : False,
"foreground_plane_kwargs" : {
"x_radius" : FRAME_X_RADIUS,
"y_radius" : FRAME_Y_RADIUS,
"secondary_line_ratio" : 1
},
"t_matrix" : [[2, 0], [1, 0]]
}
def setup(self):
self.number_line = NumberLine()
self.add(self.number_line)
LinearTransformationScene.setup(self)
class Introduce2Dto1DLinearTransformations(TwoDToOneDScene):
def construct(self):
number_line_words = TextMobject("Number line")
number_line_words.next_to(self.number_line, UP, buff = MED_SMALL_BUFF)
numbers = VMobject(*self.number_line.get_number_mobjects())
self.remove(self.number_line)
self.apply_transposed_matrix(self.t_matrix)
self.play(
ShowCreation(number_line),
*[Animation(v) for v in (self.i_hat, self.j_hat)]
)
self.play(*list(map(Write, [numbers, number_line_words])))
self.wait()
class Symbolic2To1DTransform(Scene):
def construct(self):
func = TexMobject("L(", "\\vec{\\textbf{v}}", ")")
input_array = Matrix([2, 7])
input_array.set_color(YELLOW)
in_arrow = Arrow(LEFT, RIGHT, color = input_array.get_color())
func[1].set_color(input_array.get_color())
output_array = Matrix([1.8])
output_array.set_color(PINK)
out_arrow = Arrow(LEFT, RIGHT, color = output_array.get_color())
VMobject(
input_array, in_arrow, func, out_arrow, output_array
).arrange(RIGHT, buff = SMALL_BUFF)
input_brace = Brace(input_array, DOWN)
input_words = input_brace.get_text("2d input")
output_brace = Brace(output_array, UP)
output_words = output_brace.get_text("1d output")
input_words.set_color(input_array.get_color())
output_words.set_color(output_array.get_color())
special_words = TextMobject("Linear", "functions are quite special")
special_words.set_color_by_tex("Linear", BLUE)
special_words.to_edge(UP)
self.add(func, input_array)
self.play(
GrowFromCenter(input_brace),
Write(input_words)
)
mover = input_array.copy()
self.play(
Transform(mover, Dot().move_to(func)),
ShowCreation(in_arrow),
rate_func = rush_into,
run_time = 0.5
)
self.play(
Transform(mover, output_array),
ShowCreation(out_arrow),
rate_func = rush_from,
run_time = 0.5
)
self.play(
GrowFromCenter(output_brace),
Write(output_words)
)
self.wait()
self.play(Write(special_words))
self.wait()
class RecommendChapter3(Scene):
def construct(self):
title = TextMobject("""
Definitely watch Chapter 3
if you haven't already
""")
title.to_edge(UP)
rect = Rectangle(width = 16, height = 9, color = BLUE)
rect.set_height(6)
rect.next_to(title, DOWN)
self.add(title)
self.play(ShowCreation(rect))
self.wait()
class OkayToIgnoreFormalProperties(Scene):
def construct(self):
title = TextMobject("Formal linearity properties")
title.to_edge(UP)
h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
h_line.next_to(title, DOWN)
v_tex, w_tex = ["\\vec{\\textbf{%s}}"%s for s in ("v", "w")]
additivity = TexMobject(
"L(", v_tex, "+", w_tex, ") = ",
"L(", v_tex, ") + L(", w_tex, ")",
)
scaling = TexMobject(
"L(", "c", v_tex, ") =", "c", "L(", v_tex, ")",
)
for tex_mob in additivity, scaling:
tex_mob.set_color_by_tex(v_tex, V_COLOR)
tex_mob.set_color_by_tex(w_tex, W_COLOR)
tex_mob.set_color_by_tex("c", GREEN)
additivity.next_to(h_line, DOWN, buff = MED_SMALL_BUFF)
scaling.next_to(additivity, DOWN, buff = MED_SMALL_BUFF)
words = TextMobject("We'll ignore these")
words.set_color(RED)
arrow = Arrow(DOWN, UP, color = RED)
arrow.next_to(scaling, DOWN)
words.next_to(arrow, DOWN)
randy = Randolph().to_corner(DOWN+LEFT)
morty = Mortimer().to_corner(DOWN+RIGHT)
self.add(randy, morty, title)
self.play(ShowCreation(h_line))
for tex_mob in additivity, scaling:
self.play(Write(tex_mob, run_time = 2))
self.play(
FadeIn(words),
ShowCreation(arrow),
)
self.play(
randy.look, LEFT,
morty.look, RIGHT,
)
self.wait()
class FormalVsVisual(Scene):
def construct(self):
title = TextMobject("Linearity")
title.set_color(BLUE)
title.to_edge(UP)
line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
line.next_to(title, DOWN)
v_line = Line(line.get_center(), FRAME_Y_RADIUS*DOWN)
formal = TextMobject("Formal definition")
visual = TextMobject("Visual intuition")
formal.next_to(line, DOWN).shift(FRAME_X_RADIUS*LEFT/2)
visual.next_to(line, DOWN).shift(FRAME_X_RADIUS*RIGHT/2)
v_tex, w_tex = ["\\vec{\\textbf{%s}}"%c for c in ("v", "w")]
additivity = TexMobject(
"L(", v_tex, "+", w_tex, ") = ",
"L(", v_tex, ")+", "L(", w_tex, ")"
)
additivity.set_color_by_tex(v_tex, V_COLOR)
additivity.set_color_by_tex(w_tex, W_COLOR)
scaling = TexMobject(
"L(", "c", v_tex, ")=", "c", "L(", v_tex, ")"
)
scaling.set_color_by_tex(v_tex, V_COLOR)
scaling.set_color_by_tex("c", GREEN)
visual_statement = TextMobject("""
Line of dots evenly spaced
dots remains evenly spaced
""")
visual_statement.set_submobject_colors_by_gradient(YELLOW, MAROON_B)
properties = VMobject(additivity, scaling)
properties.arrange(DOWN, buff = MED_SMALL_BUFF)
for text, mob in (formal, properties), (visual, visual_statement):
mob.scale(0.75)
mob.next_to(text, DOWN, buff = MED_SMALL_BUFF)
self.add(title)
self.play(*list(map(ShowCreation, [line, v_line])))
for mob in formal, visual, additivity, scaling, visual_statement:
self.play(Write(mob, run_time = 2))
self.wait()
class AdditivityProperty(TwoDToOneDScene):
CONFIG = {
"show_basis_vectors" : False,
"sum_before" : True
}
def construct(self):
v = Vector([2, 1], color = V_COLOR)
w = Vector([-1, 1], color = W_COLOR)
L, sum_tex, r_paren = symbols = self.get_symbols()
symbols.shift(4*RIGHT+2*UP)
self.add_foreground_mobject(sum_tex)
self.play(ShowCreation(v))
self.play(ShowCreation(w))
if self.sum_before:
sum_vect = self.play_sum(v, w)
else:
self.add_vector(v, animate = False)
self.add_vector(w, animate = False)
self.apply_transposed_matrix(self.t_matrix)
if not self.sum_before:
sum_vect = self.play_sum(v, w)
symbols.target = symbols.copy().next_to(sum_vect, UP)
VGroup(L, r_paren).set_color(BLACK)
self.play(Transform(symbols, symbols.target))
self.wait()
def play_sum(self, v, w):
sum_vect = Vector(v.get_end()+w.get_end(), color = SUM_COLOR)
self.play(w.shift, v.get_end(), path_arc = np.pi/4)
self.play(ShowCreation(sum_vect))
for vect in v, w:
vect.target = vect.copy()
vect.target.set_fill(opacity = 0)
vect.target.set_stroke(width = 0)
sum_vect.target = sum_vect
self.play(*[
Transform(mob, mob.target)
for mob in (v, w, sum_vect)
])
self.add_vector(sum_vect, animate = False)
return sum_vect
def get_symbols(self):
v_tex, w_tex = ["\\vec{\\textbf{%s}}"%c for c in ("v", "w")]
if self.sum_before:
tex_mob = TexMobject(
"L(", v_tex, "+", w_tex, ")"
)
result = VGroup(
tex_mob[0],
VGroup(*tex_mob[1:4]),
tex_mob[4]
)
else:
tex_mob = TexMobject(
"L(", v_tex, ")", "+", "L(", w_tex, ")"
)
result = VGroup(
VectorizedPoint(tex_mob.get_left()),
tex_mob,
VectorizedPoint(tex_mob.get_right()),
)
tex_mob.set_color_by_tex(v_tex, V_COLOR)
tex_mob.set_color_by_tex(w_tex, W_COLOR)
result[1].add_to_back(BackgroundRectangle(result[1]))
return result
class AdditivityPropertyPart2(AdditivityProperty):
CONFIG = {
"sum_before" : False
}
class ScalingProperty(TwoDToOneDScene):
CONFIG = {
"show_basis_vectors" : False,
"scale_before" : True,
"scalar" : 2,
}
def construct(self):
v = Vector([-1, 1], color = V_COLOR)
self.play(ShowCreation(v))
if self.scale_before:
scaled_vect = self.show_scaling(v)
self.add_vector(v, animate = False)
self.apply_transposed_matrix(self.t_matrix)
if not self.scale_before:
scaled_vect = self.show_scaling(v)
self.wait()
self.write_symbols(scaled_vect)
def show_scaling(self, v):
self.add_vector(v.copy().fade(), animate = False)
self.play(v.scale, self.scalar)
return v
def write_symbols(self, scaled_vect):
v_tex = "\\vec{\\textbf{v}}"
if self.scale_before:
tex_mob = TexMobject(
"L(", "c", v_tex, ")"
)
tex_mob.next_to(scaled_vect, UP)
else:
tex_mob = TexMobject(
"c", "L(", v_tex, ")",
)
tex_mob.next_to(scaled_vect, DOWN)
tex_mob.set_color_by_tex(v_tex, V_COLOR)
tex_mob.set_color_by_tex("c", GREEN)
self.play(Write(tex_mob))
self.wait()
class ScalingPropertyPart2(ScalingProperty):
CONFIG = {
"scale_before" : False
}
class ThisTwoDTo1DTransformWithDots(TwoDTo1DTransformWithDots):
pass
class NonLinearFailsDotTest(TwoDTo1DTransformWithDots):
def construct(self):
line = NumberLine()
self.add(line, *self.get_mobjects())
offset = LEFT+DOWN
vect = 2*RIGHT+UP
dots = VMobject(*[
Dot(offset + a*vect, radius = 0.075)
for a in np.linspace(-2, 3, 18)
])
dots.set_submobject_colors_by_gradient(YELLOW_B, YELLOW_C)
func = lambda p : (p[0]**2 - p[1]**2)*RIGHT
new_dots = VMobject(*[
Dot(
func(dot.get_center()),
color = dot.get_color(),
radius = dot.radius
)
for dot in dots
])
words = TextMobject(
"Line of dots", "do not", "remain evenly spaced"
)
words.set_color_by_tex("do not", RED)
words.next_to(line, UP, buff = MED_SMALL_BUFF)
array_tex = matrix_to_tex_string(["x", "y"])
equation = TexMobject(
"f", "\\left(%s \\right)"%array_tex, " = x^2 - y^2"
)
for part in equation:
part.add_to_back(BackgroundRectangle(part))
equation.to_corner(UP+LEFT)
self.add_foreground_mobject(equation)
self.play(Write(dots))
self.apply_nonlinear_transformation(
func,
added_anims = [Transform(dots, new_dots)]
)
self.play(Write(words))
self.wait()
class AlwaysfollowIHatJHat(TeacherStudentsScene):
def construct(self):
i_tex, j_tex = ["$\\hat{\\%smath}$"%c for c in ("i", "j")]
words = TextMobject(
"Always follow", i_tex, "and", j_tex
)
words.set_color_by_tex(i_tex, X_COLOR)
words.set_color_by_tex(j_tex, Y_COLOR)
self.teacher_says(words)
students = VMobject(*self.get_students())
ponderers = VMobject(*[
pi.copy().change_mode("pondering")
for pi in students
])
self.play(Transform(
students, ponderers,
lag_ratio = 0.5,
run_time = 2
))
self.random_blink(2)
class ShowMatrix(TwoDToOneDScene):
def construct(self):
self.apply_transposed_matrix(self.t_matrix)
self.play(Write(self.number_line.get_numbers()))
self.show_matrix()
def show_matrix(self):
for vect, char in zip([self.i_hat, self.j_hat], ["i", "j"]):
vect.words = TextMobject(
"$\\hat\\%smath$ lands on"%char,
str(int(vect.get_end()[0]))
)
direction = UP if vect is self.i_hat else DOWN
vect.words.next_to(vect.get_end(), direction, buff = LARGE_BUFF)
vect.words.set_color(vect.get_color())
matrix = Matrix([[1, 2]])
matrix_words = TextMobject("Transformation matrix: ")
matrix_group = VMobject(matrix_words, matrix)
matrix_group.arrange()
matrix_group.to_edge(UP)
entries = matrix.get_entries()
self.play(
Write(matrix_words),
Write(matrix.get_brackets()),
run_time = 1
)
for i, vect in enumerate([self.i_hat, self.j_hat]):
self.play(
Write(vect.words, run_time = 1),
ApplyMethod(vect.shift, 0.5*UP, rate_func = there_and_back)
)
self.wait()
self.play(vect.words[1].copy().move_to, entries[i])
self.wait()
class FollowVectorViaCoordinates(TwoDToOneDScene):
CONFIG = {
"t_matrix" : [[1, 0], [-2, 0]],
"v_coords" : [3, 3],
"written_v_coords" : ["x", "y"],
"concrete" : False,
}
def construct(self):
v = Vector(self.v_coords)
array = Matrix(self.v_coords if self.concrete else self.written_v_coords)
array.get_entries().set_color_by_gradient(X_COLOR, Y_COLOR)
array.add_to_back(BackgroundRectangle(array))
v_label = TexMobject("\\vec{\\textbf{v}}", "=")
v_label[0].set_color(YELLOW)
v_label.next_to(v.get_end(), RIGHT)
v_label.add_background_rectangle()
array.next_to(v_label, RIGHT)
bases = self.i_hat, self.j_hat
basis_labels = self.get_basis_vector_labels(direction = "right")
scaling_anim_tuples = self.get_scaling_anim_tuples(
basis_labels, array, [DOWN, RIGHT]
)
self.play(*list(map(Write, basis_labels)))
self.play(
ShowCreation(v),
Write(array),
Write(v_label)
)
self.add_foreground_mobject(v_label, array)
self.add_vector(v, animate = False)
self.wait()
to_fade = basis_labels
for i, anim_tuple in enumerate(scaling_anim_tuples):
self.play(*anim_tuple)
movers = self.get_mobjects_from_last_animation()
to_fade += movers[:-1]
if i == 1:
self.play(*[
ApplyMethod(m.shift, self.v_coords[0]*RIGHT)
for m in movers
])
self.wait()
self.play(
*list(map(FadeOut, to_fade)) + [
vect.restore
for vect in (self.i_hat, self.j_hat)
]
)
self.apply_transposed_matrix(self.t_matrix)
self.play(Write(self.number_line.get_numbers(), run_time = 1))
self.play(
self.i_hat.shift, 0.5*UP,
self.j_hat.shift, DOWN,
)
if self.concrete:
new_labels = [
TexMobject("(%d)"%num)
for num in self.t_matrix[:,0]
]
else:
new_labels = [
TexMobject("L(\\hat{\\%smath})"%char)
for char in ("i", "j")
]
new_labels[0].set_color(X_COLOR)
new_labels[1].set_color(Y_COLOR)
new_labels.append(
TexMobject("L(\\vec{\\textbf{v}})").set_color(YELLOW)
)
for label, vect, direction in zip(new_labels, list(bases) + [v], [UP, DOWN, UP]):
label.next_to(vect, direction)
self.play(*list(map(Write, new_labels)))
self.wait()
scaling_anim_tuples = self.get_scaling_anim_tuples(
new_labels, array, [UP, DOWN]
)
for i, anim_tuple in enumerate(scaling_anim_tuples):
self.play(*anim_tuple)
movers = VMobject(*self.get_mobjects_from_last_animation())
self.wait()
self.play(movers.shift, self.i_hat.get_end()[0]*RIGHT)
self.wait()
if self.concrete:
final_label = TexMobject(str(int(v.get_end()[0])))
final_label.move_to(new_labels[-1])
final_label.set_color(new_labels[-1].get_color())
self.play(Transform(new_labels[-1], final_label))
self.wait()
def get_scaling_anim_tuples(self, labels, array, directions):
scaling_anim_tuples = []
bases = self.i_hat, self.j_hat
quints = list(zip(
bases, self.v_coords, labels,
array.get_entries(), directions
))
for basis, scalar, label, entry, direction in quints:
basis.save_state()
basis.scaled = basis.copy().scale(scalar)
basis.scaled.shift(basis.get_start() - basis.scaled.get_start())
scaled_label = VMobject(entry.copy(), label.copy())
entry.target, label.target = scaled_label.split()
entry.target.next_to(label.target, LEFT)
scaled_label.next_to(
basis.scaled,
direction
)
scaling_anim_tuples.append((
ApplyMethod(label.move_to, label.target),
ApplyMethod(entry.copy().move_to, entry.target),
Transform(basis, basis.scaled),
))
return scaling_anim_tuples
class FollowVectorViaCoordinatesConcrete(FollowVectorViaCoordinates):
CONFIG = {
"v_coords" : [4, 3],
"concrete" : True
}
class TwoDOneDMatrixMultiplication(Scene):
CONFIG = {
"matrix" : [[1, -2]],
"vector" : [4, 3],
"order_left_to_right" : False,
}
def construct(self):
matrix = Matrix(self.matrix)
matrix.label = "Transform"
vector = Matrix(self.vector)
vector.label = "Vector"
matrix.next_to(vector, LEFT, buff = 0.2)
self.color_matrix_and_vector(matrix, vector)
for m, vect in zip([matrix, vector], [UP, DOWN]):
m.brace = Brace(m, vect)
m.label = m.brace.get_text(m.label)
matrix.label.set_color(BLUE)
vector.label.set_color(MAROON_B)
for m in vector, matrix:
self.play(Write(m))
self.play(
GrowFromCenter(m.brace),
Write(m.label),
run_time = 1
)
self.wait()
self.show_product(matrix, vector)
def show_product(self, matrix, vector):
starter_pairs = list(zip(vector.get_entries(), matrix.get_entries()))
pairs = [
VMobject(
e1.copy(), TexMobject("\\cdot"), e2.copy()
).arrange(
LEFT if self.order_left_to_right else RIGHT,
)
for e1, e2 in starter_pairs
]
symbols = list(map(TexMobject, ["=", "+"]))
equation = VMobject(*it.chain(*list(zip(symbols, pairs))))
equation.arrange(align_using_submobjects = True)
equation.next_to(vector, RIGHT)
self.play(Write(VMobject(*symbols)))
for starter_pair, pair in zip(starter_pairs, pairs):
self.play(Transform(
VMobject(*starter_pair).copy(),
pair,
path_arc = -np.pi/2
))
self.wait()
def color_matrix_and_vector(self, matrix, vector):
for m in matrix, vector:
x, y = m.get_entries()
x.set_color(X_COLOR)
y.set_color(Y_COLOR)
class AssociationBetweenMatricesAndVectors(Scene):
CONFIG = {
"matrices" : [
[[2, 7]],
[[1, -2]]
]
}
def construct(self):
matrices_words = TextMobject("$1\\times 2$ matrices")
matrices_words.set_color(BLUE)
vectors_words = TextMobject("2d vectors")
vectors_words.set_color(YELLOW)
arrow = DoubleArrow(LEFT, RIGHT, color = WHITE)
VGroup(
matrices_words, arrow, vectors_words
).arrange(buff = MED_SMALL_BUFF)
matrices = VGroup(*list(map(Matrix, self.matrices)))
vectors = VGroup(*list(map(Matrix, [m[0] for m in self.matrices])))
for m in list(matrices) + list(vectors):
x, y = m.get_entries()
x.set_color(X_COLOR)
y.set_color(Y_COLOR)
matrices.words = matrices_words
vectors.words = vectors_words
for group in matrices, vectors:
for m, direction in zip(group, [UP, DOWN]):
m.next_to(group.words, direction, buff = MED_SMALL_BUFF)
self.play(*list(map(Write, [matrices_words, vectors_words])))
self.play(ShowCreation(arrow))
self.wait()
self.play(FadeIn(vectors))
vectors.save_state()
self.wait()
self.play(Transform(
vectors, matrices,
path_arc = np.pi/2,
lag_ratio = 0.5,
run_time = 2,
))
self.wait()
self.play(
vectors.restore,
path_arc = -np.pi/2,
lag_ratio = 0.5,
run_time = 2
)
self.wait()
class WhatAboutTheGeometricView(TeacherStudentsScene):
def construct(self):
self.student_says("""
What does this association
mean geometrically?
""",
target_mode = "raise_right_hand"
)
self.change_student_modes("pondering", "raise_right_hand", "pondering")
self.random_blink(2)
class SomeKindOfConnection(Scene):
CONFIG = {
"v_coords" : [2, 3]
}
def construct(self):
width = FRAME_X_RADIUS-1
plane = NumberPlane(x_radius = 4, y_radius = 6)
squish_plane = plane.copy()
i_hat = Vector([1, 0], color = X_COLOR)
j_hat = Vector([0, 1], color = Y_COLOR)
vect = Vector(self.v_coords, color = YELLOW)
plane.add(vect, i_hat, j_hat)
plane.set_width(FRAME_X_RADIUS)
plane.to_edge(LEFT, buff = 0)
plane.remove(vect, i_hat, j_hat)
squish_plane.apply_function(
lambda p : np.dot(p, [4, 1, 0])*RIGHT
)
squish_plane.add(Vector(self.v_coords[1]*RIGHT, color = Y_COLOR))
squish_plane.add(Vector(self.v_coords[0]*RIGHT, color = X_COLOR))
squish_plane.scale(width/(FRAME_WIDTH))
plane.add(j_hat, i_hat)
number_line = NumberLine().stretch_to_fit_width(width)
number_line.to_edge(RIGHT)
squish_plane.move_to(number_line)
numbers = number_line.get_numbers(*list(range(-6, 8, 2)))
v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS)
v_line.set_color(GREY)
v_line.set_stroke(width = 10)
matrix = Matrix([self.v_coords])
matrix.set_column_colors(X_COLOR, Y_COLOR)
matrix.next_to(number_line, UP, buff = LARGE_BUFF)
v_coords = Matrix(self.v_coords)
v_coords.set_column_colors(YELLOW)
v_coords.scale(0.75)
v_coords.next_to(vect.get_end(), RIGHT)
for array in matrix, v_coords:
array.add_to_back(BackgroundRectangle(array))
self.play(*list(map(ShowCreation, [
plane, number_line, v_line
]))+[
Write(numbers, run_time = 2)
])
self.play(Write(matrix, run_time = 1))
mover = plane.copy()
interim = plane.copy().scale(0.8).move_to(number_line)
for target in interim, squish_plane:
self.play(
Transform(mover, target),
Animation(plane),
run_time = 1,
)
self.wait()
self.play(ShowCreation(vect))
self.play(Transform(matrix.copy(), v_coords))
self.wait()
class AnExampleWillClarify(TeacherStudentsScene):
def construct(self):
self.teacher_says("An example will clarify...")
self.change_student_modes(*["happy"]*3)
self.random_blink(3)
class ImagineYouDontKnowThis(Scene):
def construct(self):
words = TextMobject("Imagine you don't know this")
words.set_color(RED)
words.scale(1.5)
self.play(Write(words))
self.wait()
class ProjectOntoUnitVectorNumberline(VectorScene):
CONFIG = {
"randomize_dots" : True,
"animate_setup" : True,
"zoom_factor" : 1,
"u_hat_color" : YELLOW,
"tilt_angle" : np.pi/6,
}
def setup(self):
plane = self.add_plane()
plane.fade()
u_hat = Vector([1, 0], color = self.u_hat_color)
u_brace = Brace(u_hat, UP)
u_hat.rotate(self.tilt_angle)
u_hat.label = TexMobject("\\hat{\\textbf{u}}")
u_hat.label.set_color(u_hat.get_color())
u_hat.label.next_to(u_hat.get_end(), UP+LEFT)
one = TexMobject("1")
u_brace.put_at_tip(one)
u_brace.add(one)
u_brace.rotate(u_hat.get_angle())
one.rotate_in_place(-u_hat.get_angle())
number_line = NumberLine(x_min = -9, x_max = 9)
numbers = number_line.get_numbers()
VGroup(number_line, numbers).rotate(u_hat.get_angle())
if self.animate_setup:
self.play(
ShowCreation(number_line),
Write(numbers),
run_time = 3
)
self.wait()
self.play(ShowCreation(u_hat))
self.play(FadeIn(u_brace))
self.play(FadeOut(u_brace))
self.play(Write(u_hat.label))
self.wait()
else:
self.add(number_line, numbers, u_hat)
if self.zoom_factor != 1:
for mob in plane, u_hat:
mob.target = mob.copy().scale(self.zoom_factor)
number_line.target = number_line.copy()
number_line.target.rotate(-u_hat.get_angle())
number_line.target.stretch(self.zoom_factor, 0)
numbers.target = number_line.target.get_numbers()
number_line.target.rotate(u_hat.get_angle())
numbers.target.rotate(u_hat.get_angle())
self.play(*[
Transform(mob, mob.target)
for mob in self.get_mobjects()
])
self.wait()
self.number_line, self.numbers, self.u_hat = number_line, numbers, u_hat
def construct(self):
vectors = self.get_vectors(randomize = self.randomize_dots)
dots = self.get_dots(vectors)
proj_dots = self.get_proj_dots(dots)
proj_lines = self.get_proj_lines(dots, proj_dots)
self.wait()
self.play(FadeIn(vectors, lag_ratio = 0.5))
self.wait()
self.play(Transform(vectors, dots))
self.wait()
self.play(ShowCreation(proj_lines))
self.wait()
self.play(
self.number_line.set_stroke, None, 2,
Transform(vectors, proj_dots),
Transform(proj_lines, proj_dots),
Animation(self.u_hat),
lag_ratio = 0.5,
run_time = 2
)
self.wait()
def get_vectors(self, num_vectors = 10, randomize = True):
x_max = FRAME_X_RADIUS - 1
y_max = FRAME_Y_RADIUS - 1
x_vals = np.linspace(-x_max, x_max, num_vectors)
y_vals = np.linspace(y_max, -y_max, num_vectors)
if randomize:
random.shuffle(y_vals)
vectors = VGroup(*[
Vector(x*RIGHT + y*UP)
for x, y in zip(x_vals, y_vals)
])
vectors.set_color_by_gradient(PINK, MAROON_B)
return vectors
def get_dots(self, vectors):
return VGroup(*[
Dot(v.get_end(), color = v.get_color(), radius = 0.075)
for v in vectors
])
def get_proj_dots(self, dots):
return VGroup(*[
dot.copy().move_to(get_projection(
dot.get_center(), self.u_hat.get_end()
))
for dot in dots
])
def get_proj_lines(self, dots, proj_dots):
return VGroup(*[
DashedLine(
d1.get_center(), d2.get_center(),
buff = 0, color = d1.get_color(),
dash_length = 0.15
)
for d1, d2 in zip(dots, proj_dots)
])
class ProjectionFunctionSymbol(Scene):
def construct(self):
v_tex = "\\vec{\\textbf{v}}"
equation = TexMobject(
"P(", v_tex, ")=",
"\\text{number }", v_tex, "\\text{ lands on}"
)
equation.set_color_by_tex(v_tex, YELLOW)
equation.shift(2*UP)
words = TextMobject(
"This projection function is", "linear"
)
words.set_color_by_tex("linear", BLUE)
arrow = Arrow(
words.get_top(), equation[0].get_bottom(),
color = BLUE
)
self.add(VGroup(*equation[:3]))
self.play(Write(VGroup(*equation[3:])))
self.wait()
self.play(Write(words), ShowCreation(arrow))
self.wait()
class ProjectLineOfDots(ProjectOntoUnitVectorNumberline):
CONFIG = {
"randomize_dots" : False,
"animate_setup" : False,
}
class ProjectSingleVectorOnUHat(ProjectOntoUnitVectorNumberline):
CONFIG = {
"animate_setup" : False
}
def construct(self):
v = Vector([-3, 1], color = PINK)
v.proj = get_vect_mob_projection(v, self.u_hat)
v.proj_line = DashedLine(v.get_end(), v.proj.get_end())
v.proj_line.set_color(v.get_color())
v_tex = "\\vec{\\textbf{v}}"
u_tex = self.u_hat.label.get_tex_string()
v.label = TexMobject(v_tex)
v.label.set_color(v.get_color())
v.label.next_to(v.get_end(), LEFT)
dot_product = TexMobject(v_tex, "\\cdot", u_tex)
dot_product.set_color_by_tex(v_tex, v.get_color())
dot_product.set_color_by_tex(u_tex, self.u_hat.get_color())
dot_product.next_to(ORIGIN, UP, buff = MED_SMALL_BUFF)
dot_product.rotate(self.tilt_angle)
dot_product.shift(v.proj.get_end())
dot_product.add_background_rectangle()
v.label.add_background_rectangle()
self.play(
ShowCreation(v),
Write(v.label),
)
self.wait()
self.play(
ShowCreation(v.proj_line),
Transform(v.copy(), v.proj)
)
self.wait()
self.play(
FadeOut(v),
FadeOut(v.proj_line),
FadeOut(v.label),
Write(dot_product)
)
self.wait()
class AskAboutProjectionMatrix(Scene):
def construct(self):
matrix = Matrix([["?", "?"]])
matrix.set_column_colors(X_COLOR, Y_COLOR)
words = TextMobject("Projection matrix:")
VMobject(words, matrix).arrange(buff = MED_SMALL_BUFF).shift(UP)
basis_words = [
TextMobject("Where", "$\\hat{\\%smath}$"%char, "lands")
for char in ("i", "j")
]
for b_words, q_mark, direction in zip(basis_words, matrix.get_entries(), [UP, DOWN]):
b_words.next_to(q_mark, direction, buff = 1.5)
b_words.arrow = Arrow(b_words, q_mark, color = q_mark.get_color())
b_words.set_color(q_mark.get_color())
self.play(
Write(words),
Write(matrix)
)
self.wait()
for b_words in basis_words:
self.play(
Write(b_words),
ShowCreation(b_words.arrow)
)
self.wait()
class ProjectBasisVectors(ProjectOntoUnitVectorNumberline):
CONFIG = {
"animate_setup" : False,
"zoom_factor" : 3,
"u_hat_color" : YELLOW,
"tilt_angle" : np.pi/5,
}
def construct(self):
basis_vectors = self.get_basis_vectors()
i_hat, j_hat = basis_vectors
for vect in basis_vectors:
vect.scale(self.zoom_factor)
dots = self.get_dots(basis_vectors)
proj_dots = self.get_proj_dots(dots)
proj_lines = self.get_proj_lines(dots, proj_dots)
for dot in proj_dots:
dot.scale_in_place(0.1)
proj_basis = VGroup(*[
get_vect_mob_projection(vector, self.u_hat)
for vector in basis_vectors
])
i_tex, j_tex = ["$\\hat{\\%smath}$"%char for char in ("i", "j")]
question = TextMobject(
"Where do", i_tex, "and", j_tex, "land?"
)
question.set_color_by_tex(i_tex, X_COLOR)
question.set_color_by_tex(j_tex, Y_COLOR)
question.add_background_rectangle()
matrix = Matrix([["u_x", "u_y"]])
VGroup(question, matrix).arrange(DOWN).to_corner(
UP+LEFT, buff = MED_SMALL_BUFF/2
)
matrix_rect = BackgroundRectangle(matrix)
i_label = TexMobject(i_tex[1:-1])
j_label = TexMobject(j_tex[1:-1])
u_label = TexMobject("\\hat{\\textbf{u}}")
trips = list(zip(
(i_label, j_label, u_label),
(i_hat, j_hat, self.u_hat),
(DOWN+RIGHT, UP+LEFT, UP),
))
for label, vect, direction in trips:
label.set_color(vect.get_color())
label.scale(1.2)
label.next_to(vect.get_end(), direction, buff = MED_SMALL_BUFF/2)
self.play(Write(u_label, run_time = 1))
self.play(*list(map(ShowCreation, basis_vectors)))
self.play(*list(map(Write, [i_label, j_label])), run_time = 1)
self.play(ShowCreation(proj_lines))
self.play(
Write(question),
ShowCreation(matrix_rect),
Write(matrix.get_brackets()),
run_time = 2,
)
to_remove = [proj_lines]
quads = list(zip(basis_vectors, proj_basis, proj_lines, proj_dots))
for vect, proj_vect, proj_line, proj_dot in quads:
self.play(Transform(vect.copy(), proj_vect))
to_remove += self.get_mobjects_from_last_animation()
self.wait()
self.play(*list(map(FadeOut, to_remove)))
# self.show_u_coords(u_label)
u_x, u_y = [
TexMobject("u_%s"%c).set_color(self.u_hat.get_color())
for c in ("x", "y")
]
matrix_x, matrix_y = matrix.get_entries()
self.remove(j_hat, j_label)
self.show_symmetry(i_hat, u_x, matrix_x)
self.add(j_hat, j_label)
self.remove(i_hat, i_label)
self.show_symmetry(j_hat, u_y, matrix_y)
# def show_u_coords(self, u_label):
# coords = Matrix(["u_x", "u_y"])
# x, y = coords.get_entries()
# x.set_color(X_COLOR)
# y.set_color(Y_COLOR)
# coords.add_to_back(BackgroundRectangle(coords))
# eq = TexMobject("=")
# eq.next_to(u_label, RIGHT)
# coords.next_to(eq, RIGHT)
# self.play(*map(FadeIn, [eq, coords]))
# self.wait()
# self.u_coords = coords
def show_symmetry(self, vect, coord, coord_landing_spot):
starting_mobjects = list(self.get_mobjects())
line = DashedLine(FRAME_X_RADIUS*LEFT, FRAME_X_RADIUS*RIGHT)
words = TextMobject("Line of symmetry")
words.next_to(ORIGIN, UP+LEFT)
words.shift(LEFT)
words.add_background_rectangle()
angle = np.mean([vect.get_angle(), self.u_hat.get_angle()])
VGroup(line, words).rotate(angle)
self.play(ShowCreation(line))
if vect.get_end()[0] > 0.1:#is ihat
self.play(Write(words, run_time = 1))
vect.proj = get_vect_mob_projection(vect, self.u_hat)
self.u_hat.proj = get_vect_mob_projection(self.u_hat, vect)
for v in vect, self.u_hat:
v.proj_line = DashedLine(
v.get_end(), v.proj.get_end(),
color = v.get_color()
)
v.proj_line.fade()
v.tick = Line(0.1*DOWN, 0.1*UP, color = WHITE)
v.tick.rotate(v.proj.get_angle())
v.tick.shift(v.proj.get_end())
v.q_mark = TextMobject("?")
v.q_mark.next_to(v.tick, v.proj.get_end()-v.get_end())
self.play(ShowCreation(v.proj_line))
self.play(Transform(v.copy(), v.proj))
self.remove(*self.get_mobjects_from_last_animation())
self.add(v.proj)
self.wait()
for v in vect, self.u_hat:
self.play(
ShowCreation(v.tick),
Write(v.q_mark)
)
self.wait()
for v in self.u_hat, vect:
coord_copy = coord.copy().move_to(v.q_mark)
self.play(Transform(v.q_mark, coord_copy))
self.wait()
final_coord = coord_copy.copy()
self.play(final_coord.move_to, coord_landing_spot)
added_mobjects = [
mob
for mob in self.get_mobjects()
if mob not in starting_mobjects + [final_coord]
]
self.play(*list(map(FadeOut, added_mobjects)))
class ShowSingleProjection(ProjectBasisVectors):
CONFIG = {
"zoom_factor" : 1
}
def construct(self):
vector = Vector([5, - 1], color = MAROON_B)
proj = get_vect_mob_projection(vector, self.u_hat)
proj_line = DashedLine(
vector.get_end(), proj.get_end(),
color = vector.get_color()
)
coords = Matrix(["x", "y"])
coords.get_entries().set_color(vector.get_color())
coords.add_to_back(BackgroundRectangle(coords))
coords.next_to(vector.get_end(), RIGHT)
u_label = TexMobject("\\hat{\\textbf{u}}")
u_label.next_to(self.u_hat.get_end(), UP)
u_label.add_background_rectangle()
self.add(u_label)
self.play(ShowCreation(vector))
self.play(Write(coords))
self.wait()
self.play(ShowCreation(proj_line))
self.play(
Transform(vector.copy(), proj),
Animation(self.u_hat)
)
self.wait()
class GeneralTwoDOneDMatrixMultiplication(TwoDOneDMatrixMultiplication):
CONFIG = {
"matrix" : [["u_x", "u_y"]],
"vector" : ["x", "y"],
"order_left_to_right" : True,
}
def construct(self):
TwoDOneDMatrixMultiplication.construct(self)
everything = VGroup(*self.get_mobjects())
to_fade = [m for m in everything if isinstance(m, Brace) or isinstance(m, TextMobject)]
u = Matrix(self.matrix[0])
v = Matrix(self.vector)
self.color_matrix_and_vector(u, v)
dot_product = VGroup(u, TexMobject("\\cdot"), v)
dot_product.arrange()
dot_product.shift(2*RIGHT+DOWN)
words = VGroup(
TextMobject("Matrix-vector product"),
TexMobject("\\Updownarrow"),
TextMobject("Dot product")
)
words[0].set_color(BLUE)
words[2].set_color(GREEN)
words.arrange(DOWN)
words.to_edge(LEFT)
self.play(
everything.to_corner, UP+RIGHT,
*list(map(FadeOut, to_fade))
)
self.remove(everything)
self.add(*everything)
self.remove(*to_fade)
self.play(Write(words, run_time = 2))
self.play(ShowCreation(dot_product))
self.show_product(u, v)
def color_matrix_and_vector(self, matrix, vector):
colors = [X_COLOR, Y_COLOR]
for coord, color in zip(matrix.get_entries(), colors):
coord[0].set_color(YELLOW)
coord[1].set_color(color)
vector.get_entries().set_color(MAROON_B)
class UHatIsTransformInDisguise(Scene):
def construct(self):
u_tex = "$\\hat{\\textbf{u}}$"
words = TextMobject(
u_tex,
"is secretly a \\\\",
"transform",
"in disguise",
)
words.set_color_by_tex(u_tex, YELLOW)
words.set_color_by_tex("transform", BLUE)
words.scale(2)
self.play(Write(words))
self.wait()
class AskAboutNonUnitVectors(TeacherStudentsScene):
def construct(self):
self.student_says(
"What about \\\\ non-unit vectors",
target_mode = "raise_left_hand"
)
self.random_blink(2)
class ScaleUpUHat(ProjectOntoUnitVectorNumberline) :
CONFIG = {
"animate_setup" : False,
"u_hat_color" : YELLOW,
"tilt_angle" : np.pi/6,
"scalar" : 3,
}
def construct(self):
self.scale_u_hat()
self.show_matrix()
self.transform_basis_vectors()
self.transform_some_vector()
def scale_u_hat(self):
self.u_hat.coords = Matrix(["u_x", "u_y"])
new_u = self.u_hat.copy().scale(self.scalar)
new_u.coords = Matrix([
"%du_x"%self.scalar,
"%du_y"%self.scalar,
])
for v in self.u_hat, new_u:
v.coords.get_entries().set_color(YELLOW)
v.coords.add_to_back(BackgroundRectangle(v.coords))
v.coords.next_to(v.get_end(), UP+LEFT)
self.play(Write(self.u_hat.coords))
self.play(
Transform(self.u_hat, new_u),
Transform(self.u_hat.coords, new_u.coords)
)
self.wait()
def show_matrix(self):
matrix = Matrix([list(self.u_hat.coords.get_entries().copy())])
matrix.set_column_colors(X_COLOR, Y_COLOR)
matrix.add_to_back(BackgroundRectangle(matrix))
brace = Brace(matrix)
words = TextMobject(
"\\centering Associated\\\\",
"transformation"
)
words.set_color_by_tex("transformation", BLUE)
words.add_background_rectangle()
brace.put_at_tip(words)
VGroup(matrix, brace, words).to_corner(UP+LEFT)
self.play(Transform(
self.u_hat.coords, matrix
))
self.play(
GrowFromCenter(brace),
Write(words, run_time = 2)
)
self.wait()
self.matrix_words = words
def transform_basis_vectors(self):
start_state = list(self.get_mobjects())
bases = self.get_basis_vectors()
for b, char in zip(bases, ["x", "y"]):
b.proj = get_vect_mob_projection(b, self.u_hat)
b.proj_line = DashedLine(
b.get_end(), b.proj.get_end(),
dash_length = 0.05
)
b.proj.label = TexMobject("u_%s"%char)
b.proj.label.set_color(b.get_color())
b.scaled_proj = b.proj.copy().scale(self.scalar)
b.scaled_proj.label = TexMobject("3u_%s"%char)
b.scaled_proj.label.set_color(b.get_color())
for v, direction in zip([b.proj, b.scaled_proj], [UP, UP+LEFT]):
v.label.add_background_rectangle()
v.label.next_to(v.get_end(), direction)
self.play(*list(map(ShowCreation, bases)))
for b in bases:
mover = b.copy()
self.play(ShowCreation(b.proj_line))
self.play(Transform(mover, b.proj))
self.play(Write(b.proj.label))
self.wait()
self.play(
Transform(mover, b.scaled_proj),
Transform(b.proj.label, b.scaled_proj.label)
)
self.wait()
self.play(*list(map(FadeOut, [
mob
for mob in self.get_mobjects()
if mob not in start_state
])))
def transform_some_vector(self):
words = TextMobject(
"\\centering Project\\\\",
"then scale"
)
project, then_scale = words.split()
words.add_background_rectangle()
words.move_to(self.matrix_words, aligned_edge = UP)
v = Vector([3, -1], color = MAROON_B)
proj = get_vect_mob_projection(v, self.u_hat)
proj_line = DashedLine(
v.get_end(), proj.get_end(),
color = v.get_color()
)
mover = v.copy()
self.play(ShowCreation(v))
self.play(Transform(self.matrix_words, words))
self.play(ShowCreation(proj_line))
self.play(
Transform(mover, proj),
project.set_color, YELLOW
)
self.wait()
self.play(
mover.scale, self.scalar,
then_scale.set_color, YELLOW
)
self.wait()
class NoticeWhatHappenedHere(TeacherStudentsScene):
def construct(self):
self.teacher_says("""
Notice what
happened here
""")
self.change_student_modes(*["pondering"]*3)
self.random_blink()
class AbstractNumericAssociation(AssociationBetweenMatricesAndVectors):
CONFIG = {
"matrices" : [
[["u_x", "u_y"]]
]
}
class TwoDOneDTransformationSeparateSpace(Scene):
CONFIG = {
"v_coords" : [4, 1]
}
def construct(self):
width = FRAME_X_RADIUS-1
plane = NumberPlane(x_radius = 6, y_radius = 7)
squish_plane = plane.copy()
i_hat = Vector([1, 0], color = X_COLOR)
j_hat = Vector([0, 1], color = Y_COLOR)
vect = Vector(self.v_coords, color = YELLOW)
plane.add(vect, i_hat, j_hat)
plane.set_width(FRAME_X_RADIUS)
plane.to_edge(LEFT, buff = 0)
plane.remove(vect, i_hat, j_hat)
squish_plane.apply_function(
lambda p : np.dot(p, [4, 1, 0])*RIGHT
)
squish_plane.add(Vector(self.v_coords[0]*RIGHT, color = X_COLOR))
squish_plane.add(Vector(self.v_coords[1]*RIGHT, color = Y_COLOR))
squish_plane.scale(width/(FRAME_WIDTH))
plane.add(i_hat, j_hat)
number_line = NumberLine().stretch_to_fit_width(width)
number_line.to_edge(RIGHT)
squish_plane.move_to(number_line)
numbers = number_line.get_numbers(*list(range(-6, 8, 2)))
v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS)
v_line.set_color(GREY)
v_line.set_stroke(width = 10)
matrix = Matrix([self.v_coords])
matrix.set_column_colors(X_COLOR, Y_COLOR)
matrix.next_to(number_line, UP, buff = LARGE_BUFF)
v_coords = Matrix(self.v_coords)
v_coords.set_column_colors(YELLOW)
v_coords.scale(0.75)
v_coords.next_to(vect.get_end(), RIGHT)
for array in matrix, v_coords:
array.add_to_back(BackgroundRectangle(array))
start_words = TextMobject(
"\\centering Any time you have a \\\\",
"2d-to-1d linear transform..."
)
end_words = TextMobject(
"\\centering ...it's associated \\\\",
"with some vector",
)
for words in start_words, end_words:
words.add_background_rectangle()
words.scale(0.8)
start_words.next_to(ORIGIN, RIGHT, buff = MED_SMALL_BUFF).to_edge(UP)
end_words.next_to(ORIGIN, DOWN+LEFT, buff = MED_SMALL_BUFF/2)
self.play(*list(map(ShowCreation, [
plane, number_line, v_line
]))+[
Write(numbers, run_time = 2)
])
self.play(Write(start_words, run_time = 2))
self.play(Write(matrix, run_time = 1))
mover = plane.copy()
interim = plane.copy().scale(0.8).move_to(number_line)
for target in interim, squish_plane:
self.play(
Transform(mover, target),
Animation(plane),
Animation(start_words),
run_time = 1,
)
self.wait()
self.play(Transform(start_words.copy(), end_words))
self.play(ShowCreation(vect))
self.play(Transform(matrix.copy(), v_coords))
self.wait()
class IsntThisBeautiful(TeacherStudentsScene):
def construct(self):
self.teacher.look(DOWN+LEFT)
self.teacher_says(
"Isn't this", "beautiful",
target_mode = "surprised"
)
for student in self.get_students():
self.play(student.change_mode, "happy")
self.random_blink()
duality_words = TextMobject(
"It's called", "duality"
)
duality_words[1].set_color_by_gradient(BLUE, YELLOW)
self.teacher_says(duality_words)
self.random_blink()
class RememberGraphDuality(Scene):
def construct(self):
words = TextMobject("""
\\centering
Some of you may remember an
early video I did on graph duality
""")
words.to_edge(UP)
self.play(Write(words, lag_factor = 4))
self.wait()
class LooseDualityDescription(Scene):
def construct(self):
duality = TextMobject("Duality")
duality.set_color_by_gradient(BLUE, YELLOW)
arrow = TexMobject("\\Leftrightarrow")
words = TextMobject("Natural-but-surprising", "correspondence")
words[1].set_color_by_gradient(BLUE, YELLOW)
VGroup(duality, arrow, words).arrange(buff = MED_SMALL_BUFF)
self.add(duality)
self.play(Write(arrow))
self.play(Write(words))
self.wait()
class DualOfAVector(ScaleUpUHat):
pass #Exact copy
class DualOfATransform(TwoDOneDTransformationSeparateSpace):
pass #Exact copy
class UnderstandingProjection(ProjectOntoUnitVectorNumberline):
pass ##Copy
class ShowQualitativeDotProductValuesCopy(ShowQualitativeDotProductValues):
pass
class TranslateToTheWorldOfTransformations(TwoDOneDMatrixMultiplication):
CONFIG = {
"order_left_to_right" : True,
}
def construct(self):
v1, v2 = [
Matrix(["x_%d"%n, "y_%d"%n])
for n in (1, 2)
]
v1.set_column_colors(V_COLOR)
v2.set_column_colors(W_COLOR)
dot = TexMobject("\\cdot")
matrix = Matrix([["x_1", "y_1"]])
matrix.set_column_colors(X_COLOR, Y_COLOR)
dot_product = VGroup(v1, dot, v2)
dot_product.arrange(RIGHT)
matrix.next_to(v2, LEFT)
brace = Brace(matrix, UP)
word = TextMobject("Transform")
word.set_width(brace.get_width())
brace.put_at_tip(word)
word.set_color(BLUE)
self.play(Write(dot_product))
self.wait()
self.play(
dot.set_fill, BLACK, 0,
Transform(v1, matrix),
)
self.play(
GrowFromCenter(brace),
Write(word)
)
self.wait()
self.show_product(v1, v2)
self.wait()
class NumericalAssociationSilliness(GeneralTwoDOneDMatrixMultiplication):
pass #copy
class YouMustKnowPersonality(TeacherStudentsScene):
def construct(self):
self.teacher_says("""
You should learn a
vector's personality
""")
self.random_blink()
self.change_student_modes("pondering")
self.random_blink()
self.change_student_modes("pondering", "plain", "pondering")
self.random_blink()
class WhatTheVectorWantsToBe(Scene):
CONFIG = {
"v_coords" : [2, 4]
}
def construct(self):
width = FRAME_X_RADIUS-1
plane = NumberPlane(x_radius = 6, y_radius = 7)
squish_plane = plane.copy()
i_hat = Vector([1, 0], color = X_COLOR)
j_hat = Vector([0, 1], color = Y_COLOR)
vect = Vector(self.v_coords, color = YELLOW)
plane.add(vect, i_hat, j_hat)
plane.set_width(FRAME_X_RADIUS)
plane.to_edge(LEFT, buff = 0)
plane.remove(vect, i_hat, j_hat)
squish_plane.apply_function(
lambda p : np.dot(p, [4, 1, 0])*RIGHT
)
squish_plane.add(Vector(self.v_coords[1]*RIGHT, color = Y_COLOR))
squish_plane.add(Vector(self.v_coords[0]*RIGHT, color = X_COLOR))
squish_plane.scale(width/(FRAME_WIDTH))
plane.add(j_hat, i_hat)
number_line = NumberLine().stretch_to_fit_width(width)
number_line.to_edge(RIGHT)
squish_plane.move_to(number_line)
numbers = number_line.get_numbers(*list(range(-6, 8, 2)))
v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS)
v_line.set_color(GREY)
v_line.set_stroke(width = 10)
matrix = Matrix([self.v_coords])
matrix.set_column_colors(X_COLOR, Y_COLOR)
matrix.next_to(number_line, UP, buff = LARGE_BUFF)
v_coords = Matrix(self.v_coords)
v_coords.set_column_colors(YELLOW)
v_coords.scale(0.75)
v_coords.next_to(vect.get_end(), RIGHT)
for array in matrix, v_coords:
array.add_to_back(BackgroundRectangle(array))
words = TextMobject(
"What the vector",
"\\\\ wants",
"to be"
)
words[1].set_color(BLUE)
words.next_to(matrix, UP, buff = MED_SMALL_BUFF)
self.add(plane, v_line, number_line, numbers)
self.play(ShowCreation(vect))
self.play(Write(v_coords))
self.wait()
self.play(
Transform(v_coords.copy(), matrix),
Write(words)
)
self.play(
Transform(plane.copy(), squish_plane, run_time = 3),
*list(map(Animation, [
words,
matrix,
plane,
vect,
v_coords
]))
)
self.wait()
class NextVideo(Scene):
def construct(self):
title = TextMobject("""
Next video: Cross products in the
light of linear transformations
""")
title.set_height(1.2)
title.to_edge(UP, buff = MED_SMALL_BUFF/2)
rect = Rectangle(width = 16, height = 9, color = BLUE)
rect.set_height(6)
rect.next_to(title, DOWN)
VGroup(title, rect).show()
self.add(title)
self.play(ShowCreation(rect))
self.wait()