mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 21:44:19 +08:00
2161 lines
74 KiB
Python
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)
|