Files
2018-05-10 19:55:06 -07:00

2161 lines
74 KiB
Python

from big_ol_pile_of_manim_imports import *
X_COLOR = GREEN
Y_COLOR = RED
Z_COLOR = BLUE
OUTPUT_COLOR = YELLOW
INPUT_COLOR = MAROON_B
def get_cramer_matrix(matrix, output_vect, index=0):
"""
The inputs matrix and output_vect should be Matrix mobjects
"""
new_matrix = np.array(matrix.mob_matrix)
new_matrix[:, index] = output_vect.mob_matrix[:, 0]
# Create a new Matrix mobject with copies of these entries
result = Matrix(new_matrix, element_to_mobject=lambda m: m.copy())
result.match_height(matrix)
return result
class LinearSystem(VGroup):
CONFIG = {
"matrix_config": {
"element_to_mobject": Integer,
},
"dimensions": 3,
"min_int": -9,
"max_int": 10,
"height": 4,
}
def __init__(self, matrix=None, output_vect=None, **kwargs):
VGroup.__init__(self, **kwargs)
if matrix is None:
dim = self.dimensions
matrix = np.random.randint(
self.min_int,
self.max_int,
size=(dim, dim)
)
else:
dim = len(matrix)
self.matrix_mobject = Matrix(matrix, **self.matrix_config)
self.equals = TexMobject("=")
self.equals.scale(1.5)
colors = [X_COLOR, Y_COLOR, Z_COLOR][:dim]
chars = ["x", "y", "z"][:dim]
self.input_vect_mob = Matrix(np.array(chars))
self.input_vect_mob.elements.set_color_by_gradient(*colors)
if output_vect is None:
output_vect = np.random.randint(
self.min_int, self.max_int, size=(dim, 1))
self.output_vect_mob = IntegerMatrix(output_vect)
self.output_vect_mob.elements.set_color(OUTPUT_COLOR)
for mob in self.matrix_mobject, self.input_vect_mob, self.output_vect_mob:
mob.scale_to_fit_height(self.height)
self.add(
self.matrix_mobject,
self.input_vect_mob,
self.equals,
self.output_vect_mob,
)
self.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
# Scenes
class CramerOpeningQuote(OpeningQuote):
CONFIG = {
"quote": ["Computers are useless. They \\\\ can only give you answers."],
"author": "Pablo Picasso",
}
class LeaveItToComputers(TeacherStudentsScene):
CONFIG = {
"random_seed": 1,
}
def construct(self):
system = LinearSystem(height=3)
system.next_to(self.pi_creatures, UP)
system.generate_target()
system.target.scale(0.5)
system.target.to_corner(UL)
colors = [X_COLOR, Y_COLOR, Z_COLOR]
cramer_groups = VGroup()
for i in range(3):
numer_matrix = get_cramer_matrix(
system.matrix_mobject, system.output_vect_mob,
index=i
)
VGroup(*numer_matrix.mob_matrix[:, i]).set_color(colors[i])
VGroup(*numer_matrix.mob_matrix[:, i]).set_stroke(colors[i], 1)
numer = VGroup(
get_det_text(numer_matrix, initial_scale_factor=3),
numer_matrix
)
numer.to_corner(UP)
denom_matrix_mobject = system.matrix_mobject.deepcopy()
denom = VGroup(
get_det_text(denom_matrix_mobject, initial_scale_factor=3),
denom_matrix_mobject,
)
rhs = VGroup(numer, Line(LEFT, RIGHT).match_width(numer), denom)
rhs.arrange_submobjects(DOWN)
rhs.scale_to_fit_height(2.25)
rhs.move_to(self.hold_up_spot, DOWN)
rhs.to_edge(RIGHT, buff=LARGE_BUFF)
equals = TexMobject("=").next_to(rhs, LEFT)
variable = system.input_vect_mob.elements[i].copy()
variable.next_to(equals, LEFT)
cramer_group = VGroup(variable, equals, rhs)
cramer_group.variable = variable
cramer_group.equals = equals
cramer_group.rhs = rhs
cramer_groups.add(cramer_group)
self.play(
Write(system),
self.teacher.change, "raise_right_hand",
self.get_student_changes("pondering", "thinking", "hooray")
)
self.wait(2)
self.play(
PiCreatureSays(
self.teacher, "Let the computer \\\\ handle it",
target_mode="shruggie",
),
MoveToTarget(system, path_arc=90 * DEGREES),
self.get_student_changes(*["confused"] * 3)
)
self.wait(3)
cg = cramer_groups[0]
scale_factor = 1.5
cg.scale(scale_factor, about_edge=DOWN)
numer, line, denom = cg.rhs
x_copy = system.input_vect_mob.elements[0].copy()
self.play(
RemovePiCreatureBubble(
self.teacher, target_mode="raise_right_hand"),
ShowCreation(line),
ReplacementTransform(
system.matrix_mobject.copy(),
denom[1],
),
Write(denom[0]),
FadeIn(cg.equals),
ReplacementTransform(x_copy, cg.variable),
)
denom_mover = denom.deepcopy()
denom_mover.target = numer.deepcopy()
column1 = VGroup(*denom_mover.target[1].mob_matrix[:, 0])
column1.set_fill(opacity=0)
column1.set_stroke(width=0)
self.play(MoveToTarget(denom_mover))
self.look_at(system)
self.play(
ReplacementTransform(
system.output_vect_mob.elements.copy(),
VGroup(*numer[1].mob_matrix[:, 0]),
path_arc=180 * DEGREES
),
self.teacher.change, "happy",
)
self.remove(denom_mover)
self.add(cg)
self.change_all_student_modes("sassy")
self.wait(2)
self.play(
cramer_groups[0].scale, 1 / scale_factor,
cramer_groups[0].next_to, cramer_groups[1], LEFT, MED_LARGE_BUFF,
FadeIn(cramer_groups[1]),
FadeOut(system),
self.get_student_changes(*3 * ["horrified"], look_at_arg=UP),
)
self.wait()
self.play(
FadeIn(cramer_groups[2]),
cramer_groups[:2].next_to, cramer_groups[2], LEFT, MED_LARGE_BUFF,
self.get_student_changes(*3 * ["horrified"], look_at_arg=UP),
)
self.wait()
brace = Brace(cramer_groups, UP)
rule_text = brace.get_text("``Cramer's rule''")
self.play(
GrowFromCenter(brace),
Write(rule_text),
self.get_student_changes(
"pondering", "erm", "maybe",
look_at_arg=brace,
)
)
self.wait(3)
class PrerequisiteKnowledge(TeacherStudentsScene):
CONFIG = {
"camera_config": {"background_opacity": 1}
}
def construct(self):
self.remove(*self.pi_creatures)
randy = self.students[1]
self.add(randy)
title = TextMobject("Prerequisites")
title.to_edge(UP)
h_line = Line(LEFT, RIGHT).scale(5)
h_line.next_to(title, DOWN)
images = Group(*[
ImageMobject("eola%d_thumbnail" % d)
for d in [5, 7, 6]
])
images.arrange_submobjects(RIGHT, buff=LARGE_BUFF)
images.next_to(h_line, DOWN, MED_LARGE_BUFF)
for image in images:
rect = SurroundingRectangle(image, color=BLUE)
image.rect = rect
self.add(title)
self.play(
ShowCreation(h_line),
randy.change, "erm"
)
self.wait()
for image in images:
self.play(
FadeInFromDown(image),
FadeInFromDown(image.rect),
randy.change, "pondering"
)
self.wait()
class NotTheMostComputationallyEfficient(Scene):
CONFIG = {
"words": "Not the most \\\\ computationally efficient",
"word_height": 4,
"opacity": 0.7,
}
def construct(self):
big_rect = FullScreenFadeRectangle(opacity=self.opacity)
self.add(big_rect)
words = TextMobject(self.words)
words.set_color(RED)
words.set_stroke(WHITE, 1)
words.scale_to_fit_width(FRAME_WIDTH - 2 * MED_LARGE_BUFF)
self.play(Write(words))
self.wait()
class WhyLearnIt(TeacherStudentsScene):
def construct(self):
self.student_says(
"What?!? Then why \\\\ learn it?",
bubble_kwargs={"direction": LEFT},
student_index=2,
target_mode="angry",
)
self.change_all_student_modes("angry")
self.wait()
self.play(
self.teacher.change, "raise_right_hand",
self.get_student_changes("erm", "happy" "pondering"),
RemovePiCreatureBubble(self.students[2], target_mode="pondering"),
)
self.look_at(self.screen)
self.wait(10)
class SetupSimpleSystemOfEquations(LinearTransformationScene):
CONFIG = {
"matrix": [[3, 2], [-1, 2]],
"output_vect": [-4, -2],
"quit_before_final_transformation": False,
"array_scale_factor": 0.75,
"compare_to_big_system": True,
"transition_to_geometric_view": True,
}
def construct(self):
self.remove_grid()
self.introduce_system()
self.from_system_to_matrix()
self.show_geometry()
def remove_grid(self):
self.clear()
def introduce_system(self):
system = self.system = self.get_system(self.matrix, self.output_vect)
dim = len(self.matrix)
big_dim = 7 # Big system size
big_matrix = np.random.randint(-9, 10, size=(big_dim, big_dim))
big_output_vect = np.random.randint(-9, 10, size=big_dim)
big_matrix[:dim, :dim] = self.matrix
big_output_vect[:dim] = self.output_vect
big_system = self.get_system(big_matrix, big_output_vect)
unknown_circles = VGroup(*[
Circle(color=YELLOW).replace(term).scale(1.5)
for term in system.unknowns
])
unknown_circles.set_stroke(YELLOW, 2)
for circle in unknown_circles:
circle.save_state()
circle.scale(5)
circle.fade(1)
row_rects = VGroup(*map(SurroundingRectangle, system))
row_rects.set_stroke(BLUE, 2)
self.add(system)
self.wait()
self.play(LaggedStart(
ApplyMethod, unknown_circles,
lambda m: (m.restore,),
lag_ratio=0.7
))
self.play(FadeOut(unknown_circles))
self.play(LaggedStart(ShowCreation, row_rects,
run_time=1, lag_ratio=0.8))
self.play(FadeOut(row_rects))
self.wait()
if self.compare_to_big_system:
self.remove(system)
self.play(ReplacementTransform(system.copy(), big_system))
self.wait(2)
# Oh yeah, super readable line...
cutoff = 3 * dim - 1
self.play(*[
ReplacementTransform(big_system[i][:cutoff], system[i][:cutoff])
for i in range(dim)
] + [
ReplacementTransform(big_system[i][-2:], system[i][-2:])
for i in range(dim)
] + [
FadeOut(big_system[i][start:end])
for i in range(big_dim)
for start in [cutoff if i < dim else 0]
for end in [-2 if i < dim else len(big_system[i])]
])
self.remove(big_system, system)
self.add(system)
def from_system_to_matrix(self):
# dim = len(self.matrix)
system_in_lines = self.system
matrix_system = self.matrix_system = LinearSystem(
self.matrix, self.output_vect, height=2
)
matrix_system.center()
corner_rect = self.corner_rect = SurroundingRectangle(
matrix_system, buff=MED_SMALL_BUFF
)
corner_rect.set_stroke(width=0)
corner_rect.set_fill(BLACK, opacity=0.8)
corner_rect.scale_to_fit_height(2)
corner_rect.to_corner(UL, buff=0)
self.play(system_in_lines.to_edge, UP)
system_in_lines_copy = system_in_lines.deepcopy()
self.play(
ReplacementTransform(
VGroup(*map(VGroup, system_in_lines_copy.matrix_elements)),
matrix_system.matrix_mobject.elements,
),
Write(matrix_system.matrix_mobject.brackets),
Write(matrix_system.output_vect_mob.brackets),
Write(matrix_system.input_vect_mob.brackets),
Write(matrix_system.equals)
)
self.play(ReplacementTransform(
VGroup(*map(VGroup, system_in_lines_copy.output_vect_elements)),
matrix_system.output_vect_mob.elements,
))
self.play(*[
ReplacementTransform(uk, elem)
for uk, elem in zip(
system_in_lines_copy.unknowns,
it.cycle(matrix_system.input_vect_mob.elements)
)
])
self.wait()
if self.transition_to_geometric_view:
self.play(
Write(self.background_plane),
Write(self.plane),
FadeOut(system_in_lines),
FadeIn(corner_rect),
matrix_system.scale_to_fit_height, corner_rect.get_height() - MED_LARGE_BUFF,
matrix_system.move_to, corner_rect,
)
self.play(*map(GrowArrow, self.basis_vectors))
self.add_foreground_mobject(corner_rect)
self.add_foreground_mobject(matrix_system)
def show_geometry(self):
system = self.matrix_system
matrix_mobject = system.matrix_mobject
columns = VGroup(*[
VGroup(*matrix_mobject.mob_matrix[:, i])
for i in 0, 1
])
matrix = np.array(self.matrix)
first_half_matrix = np.identity(matrix.shape[0])
first_half_matrix[:, 0] = matrix[:, 0]
second_half_matrix = np.dot(
matrix,
np.linalg.inv(first_half_matrix),
)
scale_factor = self.array_scale_factor
column_mobs = VGroup()
for i in 0, 1:
column_mob = IntegerMatrix(matrix[:, i])
column_mob.elements.set_color([X_COLOR, Y_COLOR][i])
column_mob.scale(scale_factor)
column_mob.next_to(
self.plane.coords_to_point(*matrix[:, i]), RIGHT)
column_mob.add_to_back(BackgroundRectangle(column_mob))
column_mobs.add(column_mob)
output_vect_mob = self.get_vector(self.output_vect, color=OUTPUT_COLOR)
output_vect_label = system.output_vect_mob.deepcopy()
output_vect_label.add_to_back(BackgroundRectangle(output_vect_label))
output_vect_label.generate_target()
output_vect_label.target.scale(scale_factor)
output_vect_label.target.next_to(
output_vect_mob.get_end(), LEFT, SMALL_BUFF)
input_vect = np.dot(np.linalg.inv(self.matrix), self.output_vect)
input_vect_mob = self.get_vector(input_vect, color=INPUT_COLOR)
q_marks = TexMobject("????")
q_marks.set_color_by_gradient(INPUT_COLOR, OUTPUT_COLOR)
q_marks.next_to(input_vect_mob.get_end(), DOWN, SMALL_BUFF)
q_marks_rect = SurroundingRectangle(q_marks, color=WHITE)
# Show output vector
self.play(
GrowArrow(output_vect_mob),
MoveToTarget(output_vect_label),
)
self.add_foreground_mobjects(output_vect_mob, output_vect_label)
self.wait()
# Show columns
for column, color in zip(columns, [X_COLOR, Y_COLOR]):
rect = SurroundingRectangle(column, color=WHITE)
self.play(
column.set_color, color,
system.input_vect_mob.elements.set_color, WHITE,
ShowPassingFlash(rect),
)
matrices = [first_half_matrix, second_half_matrix]
for column, column_mob, m in zip(columns, column_mobs, matrices):
column_mob.save_state()
column_mob[0].scale(0).move_to(matrix_mobject)
Transform(column_mob.elements, column).update(1)
Transform(column_mob.brackets, matrix_mobject.brackets).update(1)
self.add_foreground_mobject(column_mob)
self.apply_matrix(m, added_anims=[
ApplyMethod(column_mob.restore, path_arc=90 * DEGREES)
])
self.wait()
# Do inverse transformation to reveal input
self.remove_foreground_mobjects(column_mobs)
self.apply_inverse(self.matrix, run_time=1, added_anims=[
ReplacementTransform(output_vect_mob.copy(), input_vect_mob),
ReplacementTransform(output_vect_label.elements.copy(), q_marks),
FadeOut(column_mobs)
])
self.play(ShowPassingFlash(q_marks_rect))
self.wait(2)
if not self.quit_before_final_transformation:
self.apply_matrix(self.matrix, added_anims=[
FadeOut(q_marks),
ReplacementTransform(input_vect_mob, output_vect_mob),
FadeIn(column_mobs),
])
self.wait()
self.q_marks = q_marks
self.input_vect_mob = input_vect_mob
self.output_vect_mob = output_vect_mob
self.output_vect_label = output_vect_label
self.column_mobs = column_mobs
# Helpers
def get_system(self, matrix, output_vect):
if len(matrix) <= 3:
chars = "xyzwv"
else:
chars = ["x_%d" % d for d in range(len(matrix))]
colors = [
color
for i, color in zip(
range(len(matrix)),
it.cycle([X_COLOR, Y_COLOR, Z_COLOR, YELLOW, MAROON_B, TEAL])
)
]
system = VGroup()
system.matrix_elements = VGroup()
system.output_vect_elements = VGroup()
system.unknowns = VGroup()
for row, num in zip(matrix, output_vect):
args = []
for i in range(len(row)):
if i + 1 == len(row):
sign = "="
elif row[i + 1] < 0:
sign = "-"
else:
sign = "+"
args += [str(abs(row[i])), chars[i], sign]
args.append(str(num))
line = TexMobject(*args)
line.set_color_by_tex_to_color_map(dict([
(char, color)
for char, color in zip(chars, colors)
]))
system.add(line)
system.matrix_elements.add(*line[0:-1:3])
system.unknowns.add(*line[1:-1:3])
system.output_vect_elements.add(line[-1])
system.output_vect_elements.set_color(OUTPUT_COLOR)
system.arrange_submobjects(
DOWN,
buff=0.75,
index_of_submobject_to_align=-2
)
return system
class ShowZeroDeterminantCase(LinearTransformationScene):
CONFIG = {
"show_basis_vectors": True,
"matrix": [[3, -2.0], [1, -2.0 / 3]],
"tex_scale_factor": 1.25,
"det_eq_symbol": "=",
}
def construct(self):
self.add_equation()
self.show_det_zero()
def add_equation(self):
equation = self.equation = TexMobject(
"A", "\\vec{\\textbf{x}}", "=", "\\vec{\\textbf{v}}"
)
equation.scale(self.tex_scale_factor)
equation.set_color_by_tex("{x}", INPUT_COLOR)
equation.set_color_by_tex("{v}", OUTPUT_COLOR)
equation.add_background_rectangle()
equation.to_corner(UL)
self.add(equation)
self.add_foreground_mobject(equation)
def show_det_zero(self):
matrix = self.matrix
# vect_in_span = [2, 2.0 / 3]
vect_in_span = [2, 2.0 / 3]
vect_off_span = [1, 2]
vect_in_span_mob = self.get_vector(vect_in_span, color=OUTPUT_COLOR)
vect_off_span_mob = self.get_vector(vect_off_span, color=OUTPUT_COLOR)
for vect_mob in vect_in_span_mob, vect_off_span_mob:
circle = Circle(color=WHITE, radius=0.15, stroke_width=2)
circle.move_to(vect_mob.get_end())
vect_mob.circle = circle
vect_off_span_words = TextMobject("No input lands here")
vect_off_span_words.next_to(vect_off_span_mob.circle, UP)
vect_off_span_words.add_background_rectangle()
vect_in_span_words = TextMobject("Many inputs lands here")
vect_in_span_words.next_to(vect_in_span_mob.circle, DR)
vect_in_span_words.shift_onto_screen()
vect_in_span_words.add_background_rectangle()
moving_group = VGroup(self.plane, self.basis_vectors)
moving_group.save_state()
solution = np.dot(np.linalg.pinv(matrix), vect_in_span)
import sympy
null_space_basis = np.array(sympy.Matrix(matrix).nullspace())
null_space_basis = null_space_basis.flatten().astype(float)
solution_vectors = VGroup(*[
self.get_vector(
solution + x * null_space_basis,
rectangular_stem_width=0.025,
tip_length=0.2,
)
for x in np.linspace(-4, 4, 20)
])
solution_vectors.set_color_by_gradient(YELLOW, MAROON_B)
self.apply_matrix(matrix, path_arc=0)
self.wait()
self.show_det_equation()
self.wait()
# Mention zero determinants
self.play(GrowArrow(vect_off_span_mob))
self.play(
ShowCreation(vect_off_span_mob.circle),
Write(vect_off_span_words),
)
self.wait()
self.play(
FadeOut(vect_off_span_mob.circle),
ReplacementTransform(vect_off_span_mob, vect_in_span_mob),
ReplacementTransform(vect_off_span_words, vect_in_span_words),
ReplacementTransform(vect_off_span_mob.circle,
vect_in_span_mob.circle),
)
self.wait(2)
self.play(
FadeOut(vect_in_span_words),
FadeOut(vect_in_span_mob.circle),
ApplyMethod(moving_group.restore, run_time=2),
ReplacementTransform(
VGroup(vect_in_span_mob),
solution_vectors,
run_time=2,
),
)
self.wait()
# Helpers
def show_det_equation(self):
equation = self.equation
det_equation = TexMobject(
"\\det(", "A", ")", self.det_eq_symbol, "0"
)
det_equation.scale(self.tex_scale_factor)
det_equation.next_to(
equation, DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT)
det_rect = BackgroundRectangle(det_equation)
self.play(
FadeIn(det_rect),
Write(det_equation[0]),
ReplacementTransform(
equation.get_part_by_tex("A").copy(),
det_equation.get_part_by_tex("A").copy(),
),
Write(det_equation[2:]),
)
self.add_foreground_mobject(det_rect, det_equation)
class NonZeroDeterminantCase(ShowZeroDeterminantCase, SetupSimpleSystemOfEquations):
CONFIG = {
"det_eq_symbol": "\\neq"
}
def construct(self):
self.add_equation()
self.show_det_equation()
output_vect = self.output_vect
matrix = self.matrix
input_vect = np.dot(np.linalg.inv(matrix), output_vect)
input_vect_mob = self.get_vector(input_vect, color=INPUT_COLOR)
output_vect_mob = self.get_vector(output_vect, color=OUTPUT_COLOR)
input_vect_label = TextMobject("Input")
input_vect_label.next_to(input_vect_mob.get_end(), DOWN, SMALL_BUFF)
input_vect_label.match_color(input_vect_mob)
output_vect_label = TextMobject("Output")
output_vect_label.next_to(output_vect_mob.get_end(), DOWN, SMALL_BUFF)
output_vect_label.match_color(output_vect_mob)
for label in input_vect_label, output_vect_label:
label.scale(1.25, about_edge=UP)
label.add_background_rectangle()
self.apply_matrix(matrix)
self.wait()
self.apply_inverse(matrix)
self.wait()
self.play(
GrowArrow(input_vect_mob),
Write(input_vect_label),
run_time=1
)
self.wait()
self.remove(input_vect_mob, input_vect_label)
self.apply_matrix(matrix, added_anims=[
ReplacementTransform(input_vect_mob.copy(), output_vect_mob),
ReplacementTransform(input_vect_label.copy(), output_vect_label),
], run_time=2)
self.wait()
self.remove(output_vect_mob, output_vect_label)
self.apply_inverse(matrix, added_anims=[
ReplacementTransform(output_vect_mob.copy(), input_vect_mob),
ReplacementTransform(output_vect_label.copy(), input_vect_label),
], run_time=2)
self.wait()
class ThinkOfPuzzleAsLinearCombination(SetupSimpleSystemOfEquations):
CONFIG = {
"output_vect": [-4, -2],
}
def construct(self):
self.force_skipping()
super(ThinkOfPuzzleAsLinearCombination, self).construct()
self.revert_to_original_skipping_status()
self.rearrange_equation_as_linear_combination()
self.show_linear_combination_of_vectors()
def rearrange_equation_as_linear_combination(self):
system = self.matrix_system
corner_rect = self.corner_rect
matrix, input_vect, equals, output_vect = system
columns = VGroup(*[
VGroup(*matrix.mob_matrix[:, i].flatten())
for i in 0, 1
])
column_arrays = VGroup(*[
MobjectMatrix(matrix.deepcopy().mob_matrix[:, i])
for i in 0, 1
])
for column_array in column_arrays:
column_array.match_height(output_vect)
x, y = input_vect.elements
movers = VGroup(x, y, equals, output_vect)
for mover in movers:
mover.generate_target()
plus = TexMobject("+")
new_system = VGroup(
x.target, column_arrays[0], plus,
y.target, column_arrays[1],
equals.target,
output_vect.target
)
new_system.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
new_system.move_to(matrix, LEFT)
corner_rect.generate_target()
corner_rect.target.stretch_to_fit_width(
new_system.get_width() + MED_LARGE_BUFF,
about_edge=LEFT
)
self.remove_foreground_mobjects(corner_rect, system)
self.play(
MoveToTarget(corner_rect),
FadeOut(input_vect.brackets),
ReplacementTransform(matrix.brackets, column_arrays[0].brackets),
ReplacementTransform(matrix.brackets.copy(),
column_arrays[1].brackets),
ReplacementTransform(columns[0], column_arrays[0].elements),
ReplacementTransform(columns[1], column_arrays[1].elements),
Write(plus, rate_func=squish_rate_func(smooth, 0.5, 1)),
*[
MoveToTarget(mover, replace_mobject_with_target_in_scene=True)
for mover in movers
],
path_arc=90 * DEGREES,
run_time=2
)
self.add_foreground_mobject(corner_rect, new_system)
self.wait()
def show_linear_combination_of_vectors(self):
basis_vectors = self.basis_vectors
input_vect = np.dot(np.linalg.inv(self.matrix), self.output_vect)
origin = self.plane.coords_to_point(0, 0)
for basis, scalar in zip(basis_vectors, input_vect):
basis.ghost = basis.copy()
basis.ghost.set_color(average_color(basis.get_color(), BLACK))
self.add_foreground_mobjects(basis.ghost, basis)
basis.generate_target()
basis_coords = np.array(
self.plane.point_to_coords(basis.get_end()))
new_coords = scalar * basis_coords
basis.target.put_start_and_end_on(
origin, self.plane.coords_to_point(*new_coords),
)
dashed_lines = VGroup(*[DashedLine(LEFT, RIGHT) for x in range(2)])
def update_dashed_lines(lines):
for i in 0, 1:
lines[i].put_start_and_end_on(
basis_vectors[i].get_start(),
basis_vectors[i].get_end(),
)
lines[i].shift(basis_vectors[1 - i].get_end() - origin)
return lines
update_dashed_lines(dashed_lines)
self.play(LaggedStart(ShowCreation, dashed_lines, lag_ratio=0.7))
for basis in basis_vectors:
self.play(
MoveToTarget(basis, run_time=2),
UpdateFromFunc(dashed_lines, update_dashed_lines)
)
self.wait()
class WrongButHelpful(TeacherStudentsScene):
def construct(self):
self.teacher_says("What's next is wrong, \\\\ but helpful")
self.change_student_modes("sassy", "sad", "angry")
self.wait(3)
class LookAtDotProducts(SetupSimpleSystemOfEquations):
CONFIG = {
"quit_before_final_transformation": True,
"equation_scale_factor": 0.7,
}
def construct(self):
self.force_skipping()
super(LookAtDotProducts, self).construct()
self.revert_to_original_skipping_status()
self.remove_corner_system()
self.show_dot_products()
def remove_corner_system(self):
to_remove = [self.corner_rect, self.matrix_system]
self.remove_foreground_mobjects(*to_remove)
self.remove(*to_remove)
q_marks = self.q_marks
input_vect_mob = self.input_vect_mob
equations = self.equations = VGroup()
for i in 0, 1:
basis = [0, 0]
basis[i] = 1
equation = VGroup(
Matrix(["x", "y"]),
TexMobject("\\cdot"),
IntegerMatrix(basis),
TexMobject("="),
TexMobject(["x", "y"][i]),
)
for part in equation:
if isinstance(part, Matrix):
part.scale(self.array_scale_factor)
equation[2].elements.set_color([X_COLOR, Y_COLOR][i])
equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
equation.scale(self.equation_scale_factor)
equations.add(equation)
equations.arrange_submobjects(DOWN, buff=MED_LARGE_BUFF)
equations.to_corner(UL)
corner_rect = self.corner_rect = BackgroundRectangle(
equations, opacity=0.8)
self.resize_corner_rect_to_mobjet(corner_rect, equations)
corner_rect.save_state()
self.resize_corner_rect_to_mobjet(corner_rect, equations[0])
xy_vect_mob = Matrix(["x", "y"], include_background_rectangle=True)
xy_vect_mob.scale(self.array_scale_factor)
xy_vect_mob.next_to(input_vect_mob.get_end(), DOWN, SMALL_BUFF)
q_marks.add_background_rectangle()
q_marks.next_to(xy_vect_mob, RIGHT)
origin = self.plane.coords_to_point(0, 0)
input_vect_end = input_vect_mob.get_end()
x_point = (input_vect_end - origin)[0] * RIGHT + origin
y_point = (input_vect_end - origin)[1] * UP + origin
v_dashed_line = DashedLine(input_vect_end, x_point)
h_dashed_line = DashedLine(input_vect_end, y_point)
h_brace = Brace(Line(x_point, origin), UP, buff=SMALL_BUFF)
v_brace = Brace(Line(y_point, origin), RIGHT, buff=SMALL_BUFF)
self.add(xy_vect_mob)
self.wait()
self.play(
FadeIn(corner_rect),
FadeIn(equations[0][:-1]),
ShowCreation(v_dashed_line),
GrowFromCenter(h_brace),
)
self.play(
ReplacementTransform(h_brace.copy(), equations[0][-1])
)
self.wait()
self.play(
corner_rect.restore,
Animation(equations[0]),
FadeIn(equations[1]),
)
self.wait()
self.play(
ReplacementTransform(equations[1][-1].copy(), v_brace),
ShowCreation(h_dashed_line),
GrowFromCenter(v_brace)
)
self.wait()
self.to_fade = VGroup(
h_dashed_line, v_dashed_line,
h_brace, v_brace,
xy_vect_mob, q_marks,
)
def show_dot_products(self):
moving_equations = self.equations.copy()
transformed_equations = VGroup()
implications = VGroup()
transformed_input_rects = VGroup()
transformed_basis_rects = VGroup()
for equation in moving_equations:
equation.generate_target()
xy_vect, dot, basis, equals, coord = equation.target
T1, lp1, rp1 = TexMobject("T", "(", ")")
lp1.scale(1, about_edge=LEFT)
rp1.scale(1, about_edge=LEFT)
for paren in lp1, rp1:
paren.stretch_to_fit_height(equation.get_height())
T2, lp2, rp2 = T1.copy(), lp1.copy(), rp1.copy()
transformed_equation = VGroup(
T1, lp1, xy_vect, rp1, dot,
T2, lp2, basis, rp2, equals,
coord
)
transformed_equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
# transformed_equation.scale(self.equation_scale_factor)
implies = TexMobject("\\Rightarrow").scale(1.2)
implies.next_to(equation, RIGHT)
implies.set_color(BLUE)
transformed_equation.next_to(implies, RIGHT)
implications.add(implies)
transformed_input_rects.add(SurroundingRectangle(
transformed_equation[:4],
color=OUTPUT_COLOR
))
transformed_basis_rects.add(SurroundingRectangle(
transformed_equation[5:5 + 4],
color=basis.elements.get_color()
))
for mob in [implies]:
mob.add(TexMobject("?").next_to(mob, UP, SMALL_BUFF))
transformed_equation.parts_to_write = VGroup(
T1, lp1, rp1, T2, lp2, rp2
)
transformed_equations.add(transformed_equation)
corner_rect = self.corner_rect
corner_rect.generate_target()
group = VGroup(self.equations, transformed_equations)
self.resize_corner_rect_to_mobjet(corner_rect.target, group)
for array in [self.output_vect_label] + list(self.column_mobs):
array.rect = SurroundingRectangle(array)
array.rect.match_color(array.elements)
self.play(
MoveToTarget(corner_rect),
Animation(self.equations),
FadeOut(self.to_fade),
LaggedStart(Write, implications),
)
self.remove(self.input_vect_mob)
self.apply_matrix(self.matrix, added_anims=[
Animation(VGroup(corner_rect, self.equations, implications)),
MoveToTarget(moving_equations[0]),
LaggedStart(FadeIn, transformed_equations[0].parts_to_write),
FadeIn(self.column_mobs),
ReplacementTransform(
self.input_vect_mob.copy(), self.output_vect_mob)
])
self.play(
MoveToTarget(moving_equations[1]),
LaggedStart(FadeIn, transformed_equations[1].parts_to_write),
path_arc=-30 * DEGREES,
run_time=2
)
self.wait()
# Show rectangles
self.play(
LaggedStart(ShowCreation, transformed_input_rects, lag_ratio=0.8),
ShowCreation(self.output_vect_label.rect),
)
for tbr, column_mob in zip(transformed_basis_rects, self.column_mobs):
self.play(
ShowCreation(tbr),
ShowCreation(column_mob.rect),
)
self.wait()
self.play(FadeOut(VGroup(
transformed_input_rects,
transformed_basis_rects,
self.output_vect_label.rect,
*[cm.rect for cm in self.column_mobs]
)))
# These computations assume plane is centered at ORIGIN
output_vect = self.output_vect_mob.get_end()
c1 = self.basis_vectors[0].get_end()
c2 = self.basis_vectors[1].get_end()
x_point = c1 * np.dot(output_vect, c1) / (np.linalg.norm(c1)**2)
y_point = c2 * np.dot(output_vect, c2) / (np.linalg.norm(c2)**2)
dashed_line_to_x = DashedLine(self.output_vect_mob.get_end(), x_point)
dashed_line_to_y = DashedLine(self.output_vect_mob.get_end(), y_point)
self.play(ShowCreation(dashed_line_to_x))
self.play(ShowCreation(dashed_line_to_y))
self.wait()
# Helpers
def resize_corner_rect_to_mobjet(self, rect, mobject):
rect.stretch_to_fit_width(
mobject.get_width() + MED_LARGE_BUFF + SMALL_BUFF)
rect.stretch_to_fit_height(
mobject.get_height() + MED_LARGE_BUFF + SMALL_BUFF)
rect.to_corner(UL, buff=0)
return rect
class NotAtAllTrue(NotTheMostComputationallyEfficient):
CONFIG = {
"words": "Not at all \\\\ True",
"word_height": 4,
}
class ShowDotProductChanging(LinearTransformationScene):
CONFIG = {
"matrix": [[2, -5.0 / 3], [-5.0 / 3, 2]],
"v": [1, 2],
"w": [2, 1],
"v_label": "v",
"w_label": "w",
"v_color": YELLOW,
"w_color": MAROON_B,
"rhs1": "> 0",
"rhs2": "< 0",
"foreground_plane_kwargs": {
"x_radius": 2 * FRAME_WIDTH,
"y_radius": 2 * FRAME_HEIGHT,
},
"equation_scale_factor": 1.5,
}
def construct(self):
v_mob = self.add_vector(self.v, self.v_color, animate=False)
w_mob = self.add_vector(self.w, self.w_color, animate=False)
kwargs = {
"transformation_name": "T",
"at_tip": True,
"animate": False,
}
v_label = self.add_transformable_label(v_mob, self.v_label, **kwargs)
w_label = self.add_transformable_label(w_mob, self.w_label, **kwargs)
start_equation = self.get_equation(v_label, w_label, self.rhs1)
start_equation.to_corner(UR)
self.play(
Write(start_equation[0::2]),
ReplacementTransform(v_label.copy(), start_equation[1]),
ReplacementTransform(w_label.copy(), start_equation[3]),
)
self.wait()
self.add_foreground_mobject(start_equation)
self.apply_matrix(self.matrix)
self.wait()
end_equation = self.get_equation(v_label, w_label, self.rhs2)
end_equation.next_to(start_equation, DOWN, aligned_edge=RIGHT)
self.play(
FadeIn(end_equation[0]),
ReplacementTransform(
start_equation[2::2].copy(),
end_equation[2::2],
),
ReplacementTransform(v_label.copy(), end_equation[1]),
ReplacementTransform(w_label.copy(), end_equation[3]),
)
self.wait(2)
def get_equation(self, v_label, w_label, rhs):
equation = VGroup(
v_label.copy(),
TexMobject("\\cdot"),
w_label.copy(),
TexMobject(rhs),
)
equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
equation.add_to_back(BackgroundRectangle(equation))
equation.scale(self.equation_scale_factor)
return equation
class ShowDotProductChangingAwayFromZero(ShowDotProductChanging):
CONFIG = {
"matrix": [[2, 2], [0, -1]],
"v": [1, 0],
"w": [0, 1],
"v_label": "x",
"w_label": "y",
"v_color": X_COLOR,
"w_color": Y_COLOR,
"rhs1": "= 0",
"rhs2": "\\ne 0",
}
class OrthonormalWords(Scene):
def construct(self):
v_tex = "\\vec{\\textbf{v}}"
w_tex = "\\vec{\\textbf{w}}"
top_words = TexMobject(
"\\text{If }",
"T(", v_tex, ")", "\\cdot",
"T(", w_tex, ")", "=",
v_tex, "\\cdot", w_tex,
"\\text{ for all }", v_tex, "\\text{ and }", w_tex,
)
top_words.set_color_by_tex_to_color_map({
v_tex: YELLOW,
w_tex: MAROON_B,
})
bottom_words = TextMobject(
"$T$", "is", "``Orthonormal''"
)
bottom_words.set_color_by_tex("Orthonormal", BLUE)
words = VGroup(top_words, bottom_words)
words.arrange_submobjects(DOWN, buff=MED_LARGE_BUFF)
for word in words:
word.add_background_rectangle()
words.to_edge(UP)
self.play(Write(words))
self.wait()
class ShowSomeOrthonormalTransformations(LinearTransformationScene):
CONFIG = {
"random_seed": 1,
"n_angles": 5
}
def construct(self):
for x in range(self.n_angles):
angle = TAU * np.random.random() - TAU / 2
matrix = rotation_matrix(angle, OUT)[:2, :2]
self.apply_matrix(matrix)
class SolvingASystemWithOrthonormalMatrix(LinearTransformationScene):
CONFIG = {
"array_scale_factor": 0.6,
}
def construct(self):
# Setup system
angle = TAU / 12
matrix = np.array([
[np.cos(angle), -np.sin(angle)],
[np.sin(angle), np.cos(angle)]
])
output_vect = [1, 2]
symbolic_matrix = [
["\\cos(30^\\circ)", "-\\sin(30^\\circ)"],
["\\sin(30^\\circ)", "\\cos(30^\\circ)"],
]
system = LinearSystem(
matrix=symbolic_matrix,
output_vect=output_vect,
matrix_config={
"h_buff": 2.5,
"element_to_mobject": TexMobject,
},
height=1.25,
)
system.to_corner(UL)
system.matrix_mobject.set_color_columns(X_COLOR, Y_COLOR)
system.input_vect_mob.elements.set_color(WHITE)
system_rect = BackgroundRectangle(system, buff=MED_SMALL_BUFF)
matrix_brace = Brace(system.matrix_mobject, DOWN, buff=SMALL_BUFF)
orthonomal_label = TextMobject("Orthonormal")
orthonomal_label.set_color(WHITE)
orthonomal_label.next_to(matrix_brace, DOWN, SMALL_BUFF)
orthonomal_label.add_background_rectangle()
# Input and output vectors
output_vect_mob = self.get_vector(output_vect, color=OUTPUT_COLOR)
output_vect_label = system.output_vect_mob.copy()
output_vect_label.add_background_rectangle()
output_vect_label.scale(self.array_scale_factor)
output_vect_label.next_to(
output_vect_mob.get_end(), RIGHT, buff=SMALL_BUFF)
input_vect = np.dot(np.linalg.inv(matrix), output_vect)
input_vect_mob = self.get_vector(input_vect, color=INPUT_COLOR)
input_vect_label = TextMobject("Mystery input vector")
input_vect_label.add_background_rectangle()
input_vect_label.next_to(input_vect_mob.get_end(), RIGHT, SMALL_BUFF)
input_vect_label.match_color(input_vect_mob)
# Column arrays
column_mobs = VGroup()
for i, vect in zip(range(2), [DR, DL]):
elements = system.matrix_mobject.deepcopy().mob_matrix[:, i]
column_mob = MobjectMatrix(elements)
column_mob.add_background_rectangle()
column_mob.scale(self.array_scale_factor)
column_mob.next_to(
self.get_vector(matrix[:, i]).get_end(), vect,
buff=SMALL_BUFF
)
column_mobs.add(column_mob)
column_mobs[1].shift(SMALL_BUFF * UP)
# Dot product lines
x_point = self.plane.coords_to_point(input_vect[0], 0)
y_point = self.plane.coords_to_point(0, input_vect[1])
input_dashed_lines = VGroup(
DashedLine(input_vect_mob.get_end(), x_point),
DashedLine(input_vect_mob.get_end(), y_point),
)
output_dashed_lines = input_dashed_lines.copy()
output_dashed_lines.apply_matrix(matrix)
self.add_foreground_mobjects(system_rect, system)
self.add_foreground_mobjects(matrix_brace, orthonomal_label)
self.add_foreground_mobjects(output_vect_mob, output_vect_label)
self.plane.set_stroke(width=2)
self.apply_matrix(matrix)
self.wait()
self.play(LaggedStart(ShowCreation, output_dashed_lines))
self.play(*self.get_column_animations(system.matrix_mobject, column_mobs))
self.wait()
self.remove(*output_dashed_lines)
self.apply_inverse(matrix, added_anims=[
FadeOut(column_mobs),
ReplacementTransform(output_vect_mob.copy(), input_vect_mob),
ReplacementTransform(
output_dashed_lines.copy(), input_dashed_lines),
FadeIn(input_vect_label),
])
self.wait()
self.remove(input_dashed_lines, input_vect_mob)
self.apply_matrix(matrix, added_anims=[
FadeOut(input_vect_label),
ReplacementTransform(input_vect_mob.copy(), output_vect_mob),
ReplacementTransform(input_dashed_lines.copy(),
output_dashed_lines),
FadeIn(column_mobs),
])
# Write dot product equations
equations = VGroup()
for i in 0, 1:
moving_output_vect_label = output_vect_label.copy()
moving_column_mob = column_mobs[i].copy()
moving_var = system.input_vect_mob.elements[i].copy()
equation = VGroup(
moving_var.generate_target(),
TexMobject("="),
moving_output_vect_label.generate_target(),
TexMobject("\\cdot"),
moving_column_mob.generate_target(use_deepcopy=True)
)
equation.movers = VGroup(
moving_var, moving_output_vect_label, moving_column_mob
)
for element in moving_column_mob.target.submobject_family():
if not isinstance(element, TexMobject):
continue
tex_string = element.get_tex_string()
if "sin" in tex_string or "cos" in tex_string:
element.set_stroke(width=1)
element.scale(1.25)
equation.to_write = equation[1::2]
equation[2].match_height(equation[4])
equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
equation.background_rectangle = BackgroundRectangle(equation)
equation.add_to_back(equation.background_rectangle)
equations.add(equation)
equations.arrange_submobjects(DOWN, buff=MED_LARGE_BUFF)
equations.scale(1.25)
equations.to_corner(UR, buff=MED_SMALL_BUFF)
equations_rect = BackgroundRectangle(equations, buff=MED_LARGE_BUFF)
equations_rect.set_fill(opacity=0.9)
for i, equation in enumerate(equations):
anims = [
FadeIn(equation.background_rectangle),
Write(equation.to_write),
LaggedStart(
MoveToTarget, equation.movers,
path_arc=60 * DEGREES
)
]
if i == 0:
anims.insert(0, FadeIn(equations_rect))
self.play(*anims)
self.wait()
def get_column_animations(self, matrix_mobject, column_mobs):
def get_kwargs(i):
return {
"rate_func": squish_rate_func(smooth, 0.4 * i, 0.6 + 0.4 * i),
"run_time": 2,
}
return list(it.chain(*[
[
FadeIn(cm[0], **get_kwargs(i)),
ReplacementTransform(
matrix_mobject.brackets.copy(),
cm.brackets,
**get_kwargs(i)
),
ReplacementTransform(
VGroup(*matrix_mobject.mob_matrix[:, i]).copy(),
cm.elements,
**get_kwargs(i)
),
]
for i, cm in enumerate(column_mobs)
]))
class TransitionToParallelogramIdea(TeacherStudentsScene):
def construct(self):
teacher_words = TextMobject(
"Now ask about \\\\ other geometric \\\\ views of",
"$x$", "and", "$y$"
)
teacher_words.set_color_by_tex_to_color_map({
"$x$": X_COLOR,
"$y$": Y_COLOR,
})
self.student_says(
"But that's a super \\\\ specific case",
target_mode="sassy",
added_anims=[self.teacher.change, "guilty"]
)
self.change_student_modes("confused", "sassy", "angry")
self.wait()
self.teacher_says(
teacher_words,
added_anims=[self.get_student_changes(*["pondering"] * 3)]
)
self.wait()
class TransformingAreasYCoord(LinearTransformationScene):
CONFIG = {
# Determines whether x-coordinate or y-coordinate is computed
"index": 1,
"matrix": [[2, -1], [0, 1]],
"input_vect": [3, 2],
"array_scale_factor": 0.7,
}
def construct(self):
self.init_matrix()
self.init_plane()
self.show_coord_parallelogram()
self.transform_space()
self.solve_for_coord()
def init_matrix(self):
self.matrix = np.array(self.matrix)
def init_plane(self):
self.plane.set_stroke(width=2)
def show_coord_parallelogram(self):
index = self.index
non_index = (index + 1) % 2
input_vect = self.input_vect
input_vect_mob = self.get_vector(input_vect, color=INPUT_COLOR)
input_vect_label = Matrix(["x", "y"])
input_vect_label.add_background_rectangle()
input_vect_label.scale(self.array_scale_factor)
self.set_input_vect_label_position(input_vect_mob, input_vect_label)
mystery_words = TextMobject("Mystery input vector")
mystery_words.next_to(input_vect_label, RIGHT)
mystery_words.add_background_rectangle()
# Add basis vector labels
basis_labels = self.basis_labels = VGroup()
basis_vectors = self.basis_vectors
chars = ["\\imath", "\\jmath"]
directions = ["right", "left"]
for basis, char, direction in zip(basis_vectors, chars, directions):
label = self.get_vector_label(
basis, "\\mathbf{\\hat{%s}}" % char,
direction=direction
)
self.basis_labels.add(label)
ip = self.get_input_parallelogram(input_vect_mob)
area_arrow_direction = 1.5 * DOWN + RIGHT if self.index == 0 else DR
area_arrow = Vector(
area_arrow_direction, color=WHITE,
rectangular_stem_width=0.025,
tip_length=0.2,
)
area_arrow.shift(ip.get_center() - area_arrow.get_end() + SMALL_BUFF * DL)
area_words = TexMobject(
"\\text{Area}", "=", "1", "\\times",
["x", "y"][index]
)
area_words.next_to(
area_arrow.get_start(), UL, SMALL_BUFF,
submobject_to_align=area_words[0]
)
area_words.set_color_by_tex_to_color_map({
"Area": YELLOW,
"x": X_COLOR,
"y": Y_COLOR,
})
area_words.rect = BackgroundRectangle(area_words)
origin = self.plane.coords_to_point(0, 0)
unit_brace = Brace(
Line(origin, basis_vectors[non_index].get_end()),
[DOWN, LEFT][non_index],
buff=SMALL_BUFF
)
one = unit_brace.get_tex("1")
one.add_background_rectangle()
coord_brace = self.get_parallelogram_braces(ip)[index]
self.add(input_vect_mob, input_vect_label)
self.add(basis_labels)
self.play(
FadeIn(ip),
Animation(VGroup(basis_vectors, input_vect_mob))
)
self.play(
ShowCreationThenDestruction(SurroundingRectangle(basis_labels[non_index])),
GrowArrow(self.basis_vectors[non_index].copy(), remover=True)
)
self.play(
ShowCreationThenDestruction(SurroundingRectangle(input_vect_label)),
GrowArrow(input_vect_mob.copy(), remover=True),
)
self.wait()
self.play(
FadeIn(area_words.rect),
Write(area_words[:2]),
GrowArrow(area_arrow),
)
self.wait()
self.play(
FadeOut(basis_labels),
GrowFromCenter(unit_brace),
FadeIn(one),
FadeIn(area_words[2])
)
self.wait()
self.play(
GrowFromCenter(coord_brace),
FadeIn(coord_brace.label),
FadeIn(area_words[3:]),
)
self.wait()
self.play(
area_words.rect.stretch_to_fit_width,
area_words[:3].get_width() + SMALL_BUFF, {"about_edge": LEFT},
FadeOut(area_words[2:4]),
area_words[4].shift,
(area_words[2].get_left()[0] - area_words[4].get_left()[0]) * RIGHT,
Animation(area_words[:2]),
)
area_words.remove(*area_words[2:4])
self.wait()
# Run with me
morty = Mortimer(height=2).flip()
morty.to_corner(DL)
randy = Randolph(height=2, color=BLUE_C).flip()
randy.move_to(4 * RIGHT)
randy.to_edge(DOWN)
self.play(FadeIn(randy))
self.play(randy.change, "confused", ip)
self.play(Blink(randy), FadeIn(morty))
self.play(
PiCreatureSays(morty, "Run with \\\\ me here...", look_at_arg=randy.eyes),
randy.look_at, morty.eyes,
)
self.play(Blink(morty))
self.play(FadeOut(VGroup(morty, morty.bubble, morty.bubble.content, randy)))
# Signed area
signed = TextMobject("Signed")
signed.match_color(area_words[0])
signed.next_to(area_words, LEFT)
brace_group = VGroup(coord_brace, coord_brace.label)
def update_brace_group(brace_group):
new_brace = self.get_parallelogram_braces(ip)[index]
new_group = VGroup(new_brace, new_brace.label)
Transform(brace_group, new_group).update(1)
area_words.add_to_back(signed)
self.play(
area_words.rect.stretch_to_fit_width, area_words.get_width(),
{"about_edge": RIGHT},
Write(signed),
Animation(area_words),
)
self.play(
UpdateFromFunc(
ip, lambda m: Transform(m, self.get_input_parallelogram(input_vect_mob)).update(1)
),
input_vect_mob.rotate, np.pi, {"about_point": origin},
Animation(self.basis_vectors),
UpdateFromFunc(brace_group, update_brace_group),
UpdateFromFunc(
input_vect_label,
lambda ivl: self.set_input_vect_label_position(input_vect_mob, ivl)
),
MaintainPositionRelativeTo(area_arrow, ip),
MaintainPositionRelativeTo(area_words.rect, area_arrow),
MaintainPositionRelativeTo(area_words, area_arrow),
run_time=9,
rate_func=there_and_back_with_pause,
)
# Fade out unneeded bits
self.play(LaggedStart(FadeOut, VGroup(
unit_brace, one, coord_brace, coord_brace.label,
)))
self.input_parallelogram = ip
self.area_words = area_words
self.area_arrow = area_arrow
self.input_vect_mob = input_vect_mob
self.input_vect_label = input_vect_label
def transform_space(self):
matrix = self.matrix
ip = self.input_parallelogram
area_words = self.area_words
area_arrow = self.area_arrow
input_vect_mob = self.input_vect_mob
input_vect_label = self.input_vect_label
basis_vectors = self.basis_vectors
index = self.index
non_index = (index + 1) % 2
apply_words = TextMobject("Apply")
apply_words.add_background_rectangle()
matrix_mobject = IntegerMatrix(self.matrix)
matrix_mobject.set_color_columns(X_COLOR, Y_COLOR)
matrix_mobject.add_background_rectangle()
matrix_mobject.next_to(apply_words, RIGHT)
matrix_brace = Brace(matrix_mobject, DOWN, buff=SMALL_BUFF)
matrix_label = matrix_brace.get_tex("A")
matrix_label.add_background_rectangle()
apply_group = VGroup(apply_words, matrix_mobject, matrix_brace, matrix_label)
apply_group.to_corner(UL, buff=MED_SMALL_BUFF)
area_scale_words = TextMobject("All areas get scaled by", "$\\det(A)$")
area_scale_words.scale(1.5)
area_scale_words.move_to(2 * DOWN)
area_scale_words.add_background_rectangle()
blobs = VGroup(
Circle(radius=0.5).move_to(2 * LEFT + UP),
Square(side_length=1).rotate(TAU / 12).move_to(2 * UP + 0.5 * RIGHT),
TexMobject("\\pi").scale(3).move_to(3 * RIGHT)
)
blobs.set_stroke(YELLOW, 3)
blobs.set_fill(YELLOW, 0.3)
# Initial transform
self.add_transformable_mobject(ip)
self.add(self.basis_vectors)
self.add_vector(input_vect_mob, animate=False)
self.play(
Write(apply_words), FadeIn(matrix_mobject),
GrowFromCenter(matrix_brace), Write(matrix_label),
)
self.add_foreground_mobjects(apply_group)
self.play(*map(FadeOut, [
area_words.rect, area_words, area_arrow, input_vect_label,
]))
self.apply_matrix(matrix)
self.wait(2)
self.apply_inverse(matrix, run_time=0)
# Show many areas
self.play(
LaggedStart(DrawBorderThenFill, blobs),
Write(area_scale_words)
)
self.add_transformable_mobject(blobs)
self.add_foreground_mobject(area_scale_words)
self.apply_matrix(matrix)
# Ask about parallelogram
ip_copy = ip.copy()
ip_copy.set_stroke(BLACK, 4)
ip_copy.set_fill(BLACK, 0)
q_marks = TexMobject("???")
q_marks.scale(1.5)
q_marks.rect = BackgroundRectangle(q_marks)
q_marks_group = VGroup(q_marks, q_marks.rect)
q_marks_group.rotate(input_vect_mob.get_angle())
q_marks_group.move_to(ip)
column_mobs = VGroup()
for i, vect in zip(range(2), [DOWN, LEFT]):
column = matrix_mobject.deepcopy().mob_matrix[:, i]
column_mob = MobjectMatrix(column)
column_mob.scale(self.array_scale_factor)
column_mob.next_to(basis_vectors[i].get_end(), vect)
column_mob.add_background_rectangle()
column_mobs.add(column_mob)
column_mob = column_mobs[non_index]
transformed_input_vect_label = VGroup(input_vect_label.copy())
transformed_input_vect_label.add_to_back(matrix_label.copy())
transformed_input_vect_label.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
transformed_input_vect_label.next_to(input_vect_mob.get_end(), UP)
self.play(
ShowPassingFlash(ip_copy),
FadeIn(q_marks.rect),
Animation(ip),
Animation(basis_vectors),
Animation(input_vect_mob),
Write(q_marks),
LaggedStart(FadeOut, blobs),
)
self.transformable_mobjects.remove(blobs)
self.play(
FadeIn(column_mob.background_rectangle),
ReplacementTransform(
matrix_mobject.brackets.copy(), column_mob.brackets
),
ReplacementTransform(
VGroup(*matrix_mobject.mob_matrix[:, non_index]).copy(),
column_mob.elements
),
)
self.wait()
self.play(
ReplacementTransform(matrix_label.copy(), transformed_input_vect_label[0]),
FadeIn(transformed_input_vect_label[1])
)
self.wait(2)
# Back to input state
self.remove(q_marks.rect)
self.apply_inverse(matrix, added_anims=[
FadeOut(q_marks),
FadeOut(transformed_input_vect_label),
FadeOut(column_mob),
FadeIn(area_words.rect),
FadeIn(area_words),
FadeIn(area_arrow),
])
self.wait(2)
# Show how parallelogram scales by det(A)
self.apply_matrix(matrix, added_anims=[
UpdateFromFunc(
area_arrow,
lambda a: a.put_start_and_end_on(
area_arrow.get_start(), ip.get_center() + SMALL_BUFF * DL
)
),
Animation(area_words.rect),
Animation(area_words),
])
det_A = area_scale_words.get_part_by_tex("det").copy()
det_A.generate_target()
det_A.target.scale(1.0 / 1.5)
det_A.target.next_to(
area_words[1:3], RIGHT, SMALL_BUFF,
aligned_edge=DOWN,
submobject_to_align=det_A.target[0]
)
coord = area_words[-1]
coord.generate_target()
coord.target.next_to(det_A.target, RIGHT, SMALL_BUFF)
self.play(
area_words.rect.match_width, VGroup(area_words, coord.target),
{"stretch": True, "about_edge": LEFT},
Animation(area_words),
MoveToTarget(det_A),
MoveToTarget(coord),
)
self.wait(2)
area_words.submobjects.insert(-1, det_A)
self.area_scale_words = area_scale_words
self.apply_group = apply_group
def solve_for_coord(self):
apply_group = self.apply_group
area_words = self.area_words.copy()
index = self.index
non_index = (index + 1) % 2
# Setup rearrangement
signed, area, equals, det, coord = area_words
for part in area_words:
part.add_background_rectangle()
part.generate_target()
apply_word, matrix_mobject, matrix_brace, matrix_label = apply_group
h_line = Line(LEFT, RIGHT).match_width(det)
frac = VGroup(area.target, h_line, det.target)
frac.arrange_submobjects(DOWN)
coord_equation = VGroup(coord.target, equals.target, frac)
equals.target.next_to(coord.target, RIGHT)
frac.next_to(equals.target, RIGHT, submobject_to_align=h_line)
coord_equation.next_to(ORIGIN, DOWN, buff=1.2)
coord_equation.to_edge(LEFT)
output_vect = np.dot(self.matrix, self.input_vect)
new_matrix = np.array(self.matrix)
new_matrix[:, self.index] = output_vect
# Setup rhs
frac_matrix_height = 1.5
matrix_mobject_copy = matrix_mobject.copy()
matrix_mobject_copy.scale_to_fit_height(frac_matrix_height)
denom_det_text = get_det_text(matrix_mobject_copy)
top_matrix_mobject = IntegerMatrix(new_matrix)
top_matrix_mobject.scale_to_fit_height(frac_matrix_height)
top_matrix_mobject.set_color_columns(X_COLOR, Y_COLOR)
VGroup(*top_matrix_mobject.mob_matrix[:, self.index]).set_color(MAROON_B)
top_matrix_mobject.add_background_rectangle()
num_det_text = get_det_text(top_matrix_mobject)
rhs_h_line = Line(LEFT, RIGHT)
rhs_h_line.match_width(num_det_text)
rhs = VGroup(
VGroup(num_det_text, top_matrix_mobject),
rhs_h_line,
VGroup(matrix_mobject_copy, denom_det_text)
)
rhs.arrange_submobjects(DOWN, buff=SMALL_BUFF)
rhs_equals = TexMobject("=")
rhs_equals.next_to(h_line, RIGHT)
rhs.next_to(rhs_equals, submobject_to_align=rhs_h_line)
# Setup linear system
output_vect_label = IntegerMatrix(output_vect)
output_vect_label.elements.match_color(self.input_vect_mob)
output_vect_label.scale(self.array_scale_factor)
output_vect_label.add_background_rectangle()
self.set_input_vect_label_position(self.input_vect_mob, output_vect_label)
matrix_mobject.generate_target()
system_input = Matrix(["x", "y"])
system_input.add_background_rectangle()
system_output = output_vect_label.copy()
system_output.generate_target()
system_eq = TexMobject("=")
for array in system_input, system_output.target:
array.match_height(matrix_mobject.target)
system = VGroup(
matrix_mobject.target,
system_input, system_eq, system_output.target
)
system.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
system.to_corner(UL)
# Rearrange
self.play(
FadeOut(self.area_scale_words),
ShowCreation(h_line),
*map(MoveToTarget, area_words[1:]),
run_time=3
)
self.wait()
self.play(
ReplacementTransform(matrix_mobject.copy(), matrix_mobject_copy),
Write(rhs_equals),
Write(denom_det_text),
ShowCreation(rhs_h_line)
)
self.wait(2)
# Show output coord
self.play(
FadeIn(output_vect_label.background_rectangle),
ReplacementTransform(
self.input_vect_mob.copy(),
output_vect_label.elements,
),
Write(output_vect_label.brackets),
)
self.wait()
self.play(
FadeOut(apply_word),
MoveToTarget(matrix_mobject),
MaintainPositionRelativeTo(matrix_brace, matrix_mobject),
MaintainPositionRelativeTo(matrix_label, matrix_mobject),
)
self.play(
FadeIn(system_input),
FadeIn(system_eq),
MoveToTarget(system_output),
area_words.rect.stretch_to_fit_width, self.area_words[1:].get_width(),
{"about_edge": RIGHT},
Animation(self.area_words[1:]),
FadeOut(self.area_words[0]),
)
self.wait()
replaced_column = VGroup(*top_matrix_mobject.mob_matrix[:, self.index])
replaced_column.set_fill(opacity=0)
self.play(
ReplacementTransform(matrix_mobject_copy.copy(), top_matrix_mobject),
ReplacementTransform(denom_det_text.copy(), num_det_text),
)
self.wiggle_vector(self.basis_vectors[non_index])
self.wait()
self.wiggle_vector(self.input_vect_mob)
self.remove(replaced_column)
self.play(
Transform(
output_vect_label.elements.copy(),
replaced_column.copy().set_fill(opacity=1),
remover=True
)
)
replaced_column.set_fill(opacity=1)
self.wait()
# Circle equation
equation = VGroup(
area_words[1:], h_line,
rhs_equals, rhs,
)
equation_rect = SurroundingRectangle(equation)
equation_rect.set_stroke(YELLOW, 3)
equation_rect.set_fill(BLACK, 0.9)
self.play(
DrawBorderThenFill(equation_rect),
Animation(equation)
)
self.wait()
# Helpers
def get_input_parallelogram(self, input_vect_mob):
input_vect = np.array(self.plane.point_to_coords(input_vect_mob.get_end()))
matrix = self.matrix
dim = matrix.shape[0]
# Transofmration from unit square to input parallelogram
square_to_ip = self.square_to_ip = np.identity(dim)
square_to_ip[:, self.index] = np.array(input_vect)
ip = self.get_unit_square()
ip.apply_matrix(self.square_to_ip)
if input_vect[self.index] < 0:
ip.set_color(GOLD)
return ip
def get_parallelogram_braces(self, input_parallelogram):
braces = VGroup(*[
Brace(
input_parallelogram, vect,
buff=SMALL_BUFF,
min_num_quads=3,
max_num_quads=3,
)
for vect in DOWN, RIGHT
])
for brace, tex, color in zip(braces, "xy", [X_COLOR, Y_COLOR]):
brace.label = brace.get_tex(tex, buff=SMALL_BUFF)
brace.label.add_background_rectangle()
brace.label.set_color(color)
return braces
def set_input_vect_label_position(self, input_vect_mob, input_vect_label):
direction = np.sign(input_vect_mob.get_vector())
input_vect_label.next_to(input_vect_mob.get_end(), direction, buff=SMALL_BUFF)
return input_vect_label
def wiggle_vector(self, vector_mob):
self.play(
Rotate(
vector_mob, 15 * DEGREES,
about_point=ORIGIN,
rate_func=wiggle,
)
)
class TransformingAreasXCoord(TransformingAreasYCoord):
CONFIG = {
"index": 0,
}
class ThreeDDotVectorWithKHat(ExternallyAnimatedScene):
pass
class ZEqualsVDotK(Scene):
def construct(self):
equation = VGroup(
TexMobject("z"),
TexMobject("="),
Matrix(["x", "y", "z"]),
TexMobject("\\cdot"),
IntegerMatrix([0, 0, 1]),
)
equation[2].elements.set_color_by_gradient(X_COLOR, Y_COLOR, Z_COLOR)
equation[4].elements.set_color(BLUE)
equation.arrange_submobjects(RIGHT, buff=MED_SMALL_BUFF)
equation.to_edge(LEFT)
self.play(Write(equation))
self.wait()
class InputParallelepipedAngledView(ExternallyAnimatedScene):
pass
class InputParallelepipedTopViewToSideView(ExternallyAnimatedScene):
pass
class ParallelepipedForXCoordinate(ExternallyAnimatedScene):
pass
class ParallelepipedForYCoordinate(ExternallyAnimatedScene):
pass
class ThreeDCoordinatesAsVolumes(Scene):
def construct(self):
colors = [X_COLOR, Y_COLOR, Z_COLOR]
x, y, z = coords = VGroup(*map(TexMobject, "xyz"))
coords.set_color_by_gradient(*colors)
matrix = IntegerMatrix(np.identity(3))
matrix.set_color_columns(*colors)
det_text = get_det_text(matrix)
equals = TexMobject("=")
equals.next_to(det_text[0], LEFT)
equals.shift(SMALL_BUFF * DOWN)
coords.next_to(equals, LEFT)
columns = VGroup(*[
VGroup(*matrix.mob_matrix[:, i])
for i in range(3)
])
coord_column = coords.copy()
for coord, m_elem in zip(coord_column, columns[2]):
coord.move_to(m_elem)
coord_column.set_color(WHITE)
coord_column[2].set_color(BLUE)
coord_column.generate_target()
self.play(LaggedStart(FadeIn, VGroup(
z, equals, det_text, matrix.brackets,
VGroup(*matrix.mob_matrix[:, :2].flatten()),
coord_column
)))
self.wait(2)
coord_column.target.move_to(columns[1])
coord_column.target.set_color_by_gradient(WHITE, Y_COLOR, WHITE)
self.play(
MoveToTarget(coord_column, path_arc=np.pi),
FadeOut(columns[1]),
FadeIn(columns[2]),
FadeInFromDown(y),
z.shift, UP,
z.fade, 1,
)
self.wait(2)
coord_column.target.move_to(columns[0])
coord_column.target.set_color_by_gradient(X_COLOR, WHITE, WHITE)
self.play(
MoveToTarget(coord_column, path_arc=np.pi),
FadeOut(columns[0]),
FadeIn(columns[1]),
FadeInFromDown(x),
y.shift, UP,
y.fade, 1,
)
self.wait(2)
class WriteCramersRule(Scene):
def construct(self):
words = TextMobject("``Cramer's Rule''")
words.scale_to_fit_width(FRAME_WIDTH - LARGE_BUFF)
words.add_background_rectangle()
self.play(Write(words))
self.wait()
class CramersYEvaluation(Scene):
def construct(self):
frac = TexMobject("{(2)(2) - (4)(0) \\over (2)(1) - (-1)(0)}")
VGroup(frac[1], frac[11], frac[15], frac[26]).set_color(GREEN)
VGroup(frac[4], frac[8]).set_color(MAROON_B)
VGroup(frac[18], frac[22], frac[23]).set_color(RED)
group = VGroup(
TexMobject("="), frac,
TexMobject("= \\frac{4}{2}"), TexMobject("=2")
)
group.arrange_submobjects(RIGHT)
group.add_to_back(BackgroundRectangle(group))
self.add(group)
self.wait(1)
# Largely copy-pasted. Not great, but what are you gonna do.
class CramersXEvaluation(Scene):
def construct(self):
frac = TexMobject("{(4)(1) - (-1)(2) \\over (2)(1) - (-1)(0)}")
VGroup(frac[1], frac[12]).set_color(MAROON_B)
VGroup(frac[4], frac[8], frac[9]).set_color(RED)
VGroup(frac[16], frac[27]).set_color(GREEN)
VGroup(frac[19], frac[23], frac[24]).set_color(RED)
group = VGroup(
TexMobject("="), frac,
TexMobject("= \\frac{6}{2}"), TexMobject("=3")
)
group.arrange_submobjects(RIGHT)
group.add_to_back(BackgroundRectangle(group))
self.add(group)
self.wait(1)
class Equals2(Scene):
def construct(self):
self.add(TexMobject("=2").add_background_rectangle())
self.wait()
class Equals3(Scene):
def construct(self):
self.add(TexMobject("=3").add_background_rectangle())
self.wait()
class Introduce3DSystem(SetupSimpleSystemOfEquations):
CONFIG = {
"matrix": [[3, 2, 7], [-1, 2, -4], [4, 0, 1]],
"output_vect": [-4, -2, 5],
"compare_to_big_system": False,
"transition_to_geometric_view": False,
}
def construct(self):
self.remove_grid()
self.introduce_system()
self.from_system_to_matrix()
class ThinkItThroughYourself(TeacherStudentsScene):
def construct(self):
self.teacher_says(
"Try thinking \\\\ it through!",
target_mode="hooray"
)
self.change_all_student_modes("pondering")
self.wait(4)
class TransformParallelepipedWithoutGrid(ExternallyAnimatedScene):
pass
class TransformParallelepipedWithGrid(ExternallyAnimatedScene):
pass
class AreYouPausingAndPondering(TeacherStudentsScene):
def construct(self):
self.teacher_says(
"Are you pausing \\\\ and pondering?",
target_mode="sassy",
added_anims=[self.get_student_changes(*3 * ["guilty"])]
)
self.wait()
self.change_all_student_modes(
"thinking",
added_anims=[
RemovePiCreatureBubble(self.teacher, target_mode="raise_right_hand")
],
look_at_arg=self.screen
)
self.wait(6)
class Thumbnail(TransformingAreasYCoord):
def construct(self):
self.matrix = np.array(self.matrix)
vect = self.add_vector([1, 1.5], color=MAROON_B)
ip = self.get_input_parallelogram(vect)
self.add_transformable_mobject(ip)
self.apply_transposed_matrix([[2, -0.5], [1, 2]])
words = TextMobject("Cramer's", "rule")
words.scale_to_fit_width(7)
# words.add_background_rectangle_to_submobjects()
words.add_background_rectangle()
words.to_edge(UP)
self.add(words)