diff --git a/active_projects/eola2/determinant_puzzle.py b/active_projects/eola2/determinant_puzzle.py index 59a3fdf4..be24c390 100644 --- a/active_projects/eola2/determinant_puzzle.py +++ b/active_projects/eola2/determinant_puzzle.py @@ -1,12 +1,286 @@ from big_ol_pile_of_manim_imports import * + class WorkOutNumerically(Scene): + CONFIG = { + "M1_COLOR": TEAL, + "M2_COLOR": PINK, + } + def construct(self): - pass + self.add_question() + self.add_example() + self.compute_rhs() + self.compute_lhs() + + def add_question(self): + equation = self.original_equation = TexMobject( + "\\det(", "M_1", "M_2", ")", "=", + "\\det(", "M_1", ")", + "\\det(", "M_2", ")", + ) + equation.set_color_by_tex_to_color_map({ + "M_1": self.M1_COLOR, + "M_2": self.M2_COLOR, + }) + challenge = TextMobject("Explain in one sentence") + challenge.set_color(YELLOW) + group = VGroup(challenge, equation) + group.arrange_submobjects(DOWN) + group.to_edge(UP) + + self.add(equation) + self.play(Write(challenge)) + self.wait() + + def add_example(self): + M1 = self.M1 = Matrix([[2, -1], [1, 1]]) + M1.set_color(self.M1_COLOR) + self.M1_copy = M1.copy() + M2 = self.M2 = Matrix([[-1, 4], [1, 1]]) + M2.set_color(self.M2_COLOR) + self.M2_copy = M2.copy() + eq_parts = TexMobject( + "\\det", "\\big(", "\\big)", "=", + "\\det", "\\big(", "\\big)", + "\\det", "\\big(", "\\big)", + ) + for part in eq_parts.get_parts_by_tex("\\big"): + part.scale(2) + part.stretch(1.5, 1) + i1, i2, i3 = [ + eq_parts.index_of_part(part) + 1 + for part in eq_parts.get_parts_by_tex("\\big(") + ] + equation = self.equation_with_numbers = VGroup(*it.chain( + eq_parts[:i1], [M1, M2], + eq_parts[i1:i2], [self.M1_copy], + eq_parts[i2:i3], [self.M2_copy], + eq_parts[i3:], + )) + equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF) + eq_parts.get_part_by_tex("=").shift(0.2 * SMALL_BUFF * DOWN) + equation.scale_to_fit_width(FRAME_WIDTH - 2 * LARGE_BUFF) + equation.next_to(self.original_equation, DOWN, MED_LARGE_BUFF) + + self.play(LaggedStart(FadeIn, equation)) + + def compute_rhs(self): + M1, M2 = self.M1_copy, self.M2_copy + + line1 = VGroup( + TexMobject( + "\\big(", "2", "\\cdot", "2", "-", + "(-1)", "\\cdot", "1", "\\big)" + ), + TexMobject( + "\\big(", "-1", "\\cdot", "1", "-", + "4", "\\cdot", "1", "\\big)" + ), + ) + line1.arrange_submobjects(RIGHT, buff=SMALL_BUFF) + line1[0].set_color(self.M1_COLOR) + line1[1].set_color(self.M2_COLOR) + indices = [1, 3, 5, 7] + + line2 = TexMobject("(3)", "(-5)") + line2.match_style(line1) + line3 = TexMobject("-15") + arrows = [TexMobject("\\downarrow") for x in range(2)] + lines = VGroup(line1, arrows[0], line2, arrows[1], line3) + lines.arrange_submobjects(DOWN, buff=MED_SMALL_BUFF) + lines.next_to(self.equation_with_numbers, DOWN, buff=MED_LARGE_BUFF) + lines.to_edge(RIGHT) + + for matrix, det in zip([M1, M2], line1): + numbers = VGroup(*[det[i] for i in indices]) + numbers_iter = iter(numbers) + non_numbers = VGroup(*filter( + lambda m: m not in numbers, + det + )) + matrix_numbers = VGroup(*[ + matrix.mob_matrix[i][j].copy() + for i, j in (0, 0), (1, 1), (0, 1), (1, 0) + ]) + self.play( + LaggedStart(FadeIn, non_numbers, run_time=1), + LaggedStart( + ReplacementTransform, + matrix_numbers, + lambda m: (m, numbers_iter.next()), + path_arc=TAU / 6 + ), + ) + self.play(LaggedStart(FadeIn, lines[1:], run_time=3)) + + def compute_lhs(self): + matrix = Matrix([[-3, 7], [0, 5]]) + matrix.set_color(BLUE) + matrix.scale(0.8) + empty_det_tex = TexMobject("\\det", "\\big(", "\\big)") + empty_det_tex[1:].scale(1.5) + empty_det_tex[1:].match_height(matrix, stretch=True) + det_tex = VGroup(empty_det_tex[:2], matrix, *empty_det_tex[2:]) + det_tex.arrange_submobjects(RIGHT, buff=SMALL_BUFF) + + group = VGroup( + det_tex, + TexMobject("\\downarrow"), + TexMobject("(-3)(5) - (7)(0)").scale(0.8), + TexMobject("\\downarrow"), + TexMobject("-15"), + ) + group.arrange_submobjects(DOWN, buff=2 * SMALL_BUFF) + # group.scale_to_fit_height(0.4*FRAME_HEIGHT) + group.next_to(self.equation_with_numbers, DOWN) + group.shift(FRAME_WIDTH * LEFT / 4) + + self.play(FadeIn(empty_det_tex)) + self.play(*[ + ReplacementTransform(M.copy(), matrix) + for M in self.M1, self.M2 + ]) + self.play(LaggedStart(FadeIn, group[1:])) + self.wait() + + +class LetsGoInOneSentence(TeacherStudentsScene): + def construct(self): + self.teacher_says( + "Here we go, \\\\", "one sentence!" + ) + self.change_all_student_modes("hooray") + self.teacher_says( + "Or three...", "", + target_mode="guilty" + ) + self.change_all_student_modes("sassy") + self.wait(4) class SuccessiveLinearTransformations(LinearTransformationScene): + CONFIG = { + "matrix_2": [[3, -1], [0, 1]], + "matrix_1": [[2, 3], [-1. / 3, 2]], + } + def construct(self): - self.apply_transposed_matrix([[0, 1], [1, 0]]) - self.apply_transposed_matrix([[1, 1], [0, 1]]) + self.create_product_and_inverse() + self.scale_area_successively() + self.apply_transformations_successively() + self.scale_area_successively( + "\\det(M_2)", "\\det(M_1)", "\\det(M_1 M_2)", + reset=False + ) + # self.show_det_as_scaling_factor() + + def create_product_and_inverse(self): + self.matrix_product = np.dot(self.matrix_1, self.matrix_2) + self.matrix_product_inverse = np.linalg.inv(self.matrix_product) + + def scale_area_successively(self, tex2="3", tex1="5", tex_prod="15", reset=True): + self.add_unit_square() + t1 = "$%s \\, \\cdot $" % tex1 + t2 = "$%s \\, \\cdot $" % tex2 + t3 = "Area" + areas = VGroup( + TextMobject("", "", t3), + TextMobject("", t2, t3), + TextMobject(t1, t2, t3), + ) + areas.scale(0.8) + areas.move_to(self.square) + area = areas[0] + self.add_moving_mobject(area, areas[1]) + + self.play( + FadeIn(self.square), + Write(area), + Animation(self.basis_vectors) + ) + self.apply_matrix(self.matrix_2) self.wait() + self.add_moving_mobject(area, areas[2]) + self.apply_matrix(self.matrix_1) + self.wait() + + product = VGroup(area[:2]) + brace = Brace(product, DOWN, buff=SMALL_BUFF) + brace_tex = brace.get_tex(tex_prod, buff=SMALL_BUFF) + brace_tex.scale(0.8, about_edge=UP) + + self.play( + GrowFromCenter(brace), + Write(brace_tex) + ) + self.wait() + if reset: + self.play( + FadeOut(VGroup(self.square, area, brace, brace_tex)), + Animation(self.plane), + Animation(self.basis_vectors) + ) + self.transformable_mobjects.remove(self.square) + self.moving_mobjects = [] + self.reset_plane() + self.wait() + + def apply_transformations_successively(self): + M1, M2, all_space = expression = TexMobject( + "M_1", "M_2", "\\text{(All 2d space)}" + ) + expression.set_color_by_tex_to_color_map({ + "M_1": TEAL, + "M_2": PINK, + }) + expression.shift(FRAME_WIDTH * LEFT / 4) + expression.to_edge(UP) + for part in expression: + part.add_background_rectangle() + part.background_rectangle.stretch(1.05, 0) + M1.save_state() + M1.move_to(ORIGIN) + M1.fade(1) + + # Apply one after the other + self.play( + FocusOn(M2, run_time=1), + FadeIn(VGroup(M2, all_space)) + ) + self.add_foreground_mobjects(M2, all_space) + self.apply_matrix(self.matrix_2) + self.wait() + self.play(M1.restore) + self.add_foreground_mobjects(M1) + self.apply_matrix(self.matrix_1) + self.wait() + + # Show full composition + rp, lp = parens = TexMobject("()") + matrices = VGroup(M1, M2) + matrices.generate_target() + parens.match_height(matrices) + lp.move_to(matrices, RIGHT) + matrices.target.next_to(lp, LEFT, SMALL_BUFF) + rp.next_to(matrices.target, LEFT, SMALL_BUFF) + + self.reset_plane() + self.play( + MoveToTarget(matrices), + *map(GrowFromCenter, parens) + ) + self.apply_matrix(self.matrix_product) + self.wait() + self.reset_plane() + + def show_det_as_scaling_factor(self): + pass + + ### + + def reset_plane(self): + plane_and_bases = VGroup(self.plane, self.basis_vectors) + self.play(FadeOut(plane_and_bases)) + self.apply_matrix(self.matrix_product_inverse, run_time=0) + self.play(FadeIn(plane_and_bases))