From 36e969b2a9edf14f231ca4bf052873c5edc1b023 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 13 Aug 2016 18:27:02 -0700 Subject: [PATCH] End of chapter 6 --- eola/chapter6.py | 858 +++++++++++++++++++++++++++++++++++++++++-- eola/footnote2.py | 42 +++ eola/thumbnails.py | 9 + mobject/mobject.py | 8 +- topics/characters.py | 27 +- 5 files changed, 903 insertions(+), 41 deletions(-) create mode 100644 eola/footnote2.py diff --git a/eola/chapter6.py b/eola/chapter6.py index 5bf6ab99..93e8415f 100644 --- a/eola/chapter6.py +++ b/eola/chapter6.py @@ -24,21 +24,15 @@ from ka_playgrounds.circuits import Resistor, Source, LongResistor class OpeningQuote(Scene): def construct(self): - words = TextMobject([ - "The question you raise, ", - "``how can such a formulation lead to computations?''", - "doesn't bother me in the least! Throughout my whole life " - "as a mathematician, the possibility of making explicit, " - "elegant computations has always come out by itself, as a " - "byproduct of a ", - "thorough conceptual understanding." - ], separate_list_arg_with_spaces = False) - words.scale_to_fit_width(2*SPACE_WIDTH - 2) + words = TextMobject( + "To ask the", + "right question\\\\", + "is harder than to answer it." + ) words.to_edge(UP) - words.split()[1].highlight(BLUE) - words.split()[3].highlight(GREEN) - author = TextMobject(["-Grothendieck", "(a hero of mine)"]) - author.split()[0].highlight(YELLOW) + words[1].highlight(BLUE) + author = TextMobject("-Georg Cantor") + author.highlight(YELLOW) author.next_to(words, DOWN, buff = 0.5) self.play(FadeIn(words)) @@ -112,6 +106,10 @@ class NoComputations(TeacherStudentsScene): ]) self.random_blink() +class PuntToSoftware(Scene): + def construct(self): + self.play(Write("Let the computers do the computing")) + class UsefulnessOfMatrices(Scene): def construct(self): title = TextMobject("Usefulness of matrices") @@ -598,6 +596,32 @@ class LookForX(MentionThatItsATransformation): self.apply_transposed_matrix(self.t_matrix) self.dither() +class ThinkAboutWhatsHappening(Scene): + def construct(self): + randy = Randolph() + randy.to_corner() + bubble = randy.get_bubble(height = 5) + bubble.add_content(TexMobject(""" + 3x + 1y + 4z &= 1 \\\\ + 5x + 9y + 2z &= 6 \\\\ + 5x + 3y + 5z &= 8 + """)) + + self.play(randy.change_mode, "pondering") + self.play(ShowCreation(bubble)) + self.play(Write(bubble.content, run_time = 2)) + self.play(Blink(randy)) + self.dither() + everything = VMobject(*self.get_mobjects()) + self.play( + ApplyFunction( + lambda m : m.shift(2*DOWN).scale(5), + everything + ), + bubble.content.highlight, BLACK, + run_time = 2 + ) + class SystemOfTwoEquationsTwoUnknowns(Scene): def construct(self): system = TexMobject(""" @@ -617,10 +641,25 @@ class SystemOfTwoEquationsTwoUnknowns(Scene): ) matrix_system.arrange_submobjects(RIGHT) matrix_system.next_to(system, DOWN, buff = 1) + matrix.label = "A" + matrix.label_color = WHITE + x.label = "\\vec{\\textbf{x}}" + x.label_color = PINK + v.label = "\\vec{\\textbf{v}}" + v.label_color = YELLOW + for mob in matrix, x, v: + brace = Brace(mob) + label = brace.get_text("$%s$"%mob.label) + label.highlight(mob.label_color) + brace.add(label) + mob.brace = brace self.add(system) self.play(Write(matrix_system)) self.dither() + for mob in matrix, v, x: + self.play(Write(mob.brace)) + self.dither() class ShowBijectivity(LinearTransformationScene): CONFIG = { @@ -678,15 +717,18 @@ class LabeledExample(LinearSystemTransformationScene): CONFIG = { "title" : "", "t_matrix" : [[0, 0], [0, 0]], + "show_square" : False, } def setup(self): LinearSystemTransformationScene.setup(self) title = TextMobject(self.title) - title.scale(1.5) - title.next_to(self.equation, DOWN) + title.next_to(self.equation, DOWN, buff = 1) title.add_background_rectangle() + title.shift_onto_screen() self.add_foreground_mobject(title) self.title = title + if self.show_square: + self.add_unit_square() def construct(self): self.dither() @@ -708,20 +750,48 @@ class FullRankExmapleWithWords(LabeledExample): class SquishExmapleDet(SquishExmapleWithWords): CONFIG = { "title" : "$\\det(A) = 0$", + "show_square" : True, } class FullRankExmapleDet(FullRankExmapleWithWords): CONFIG = { "title" : "$\\det(A) \\ne 0$", + "show_square" : True, } +class StartWithNonzeroDetCase(TeacherStudentsScene): + def construct(self): + words = TextMobject( + "Let's start with \\\\", + "the", "$\\det(A) \\ne 0$", "case" + ) + words[2].highlight(TEAL) + self.teacher_says(words) + self.random_blink() + self.play( + random.choice(self.get_students()).change_mode, + "happy" + ) + self.dither() + +class DeclareNewTransformation(TeacherStudentsScene): + def construct(self): + words = TextMobject( + "Playing a transformation in\\\\", + "reverse gives a", "new transformation" + ) + words[-1].highlight(GREEN) + self.teacher_says(words) + self.change_student_modes("pondering", "sassy") + self.random_blink() + class PlayInReverse(FullRankExmapleDet): CONFIG = { "show_basis_vectors" : False } def construct(self): FullRankExmapleDet.construct(self) - v = self.add_vector([-2, -2], color = YELLOW) + v = self.add_vector([-4, -1], color = YELLOW) v_label = self.label_vector(v, "v", color = YELLOW) self.add(v.copy()) self.apply_inverse_transpose(self.t_matrix) @@ -794,9 +864,9 @@ class ShearInverseShear(DescribeInverse): class MultiplyToIdentity(LinearTransformationScene): def construct(self): self.setup() - lhs = TexMobject("A", "A^{-1}", "=") + lhs = TexMobject("A^{-1}", "A", "=") lhs.scale(1.5) - A, A_inv, eq = lhs.split() + A_inv, A, eq = lhs.split() identity = Matrix([[1, 0], [0, 1]]) identity.highlight_columns(X_COLOR, Y_COLOR) identity.next_to(eq, RIGHT) @@ -816,6 +886,7 @@ class MultiplyToIdentity(LinearTransformationScene): for mob in A, A_inv, product, identity: mob.brace = Brace(mob) mob.text = mob.brace.get_text(mob.text) + mob.text.shift_onto_screen() mob.text.add_background_rectangle() self.add_foreground_mobject(A, A_inv) @@ -847,6 +918,15 @@ class MultiplyToIdentity(LinearTransformationScene): self.play(Write(col2)) self.dither() +class ThereAreComputationMethods(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + There are methods + to compute $A^{-1}$ + """) + self.random_blink() + self.dither() + class TwoDInverseFormula(Scene): def construct(self): title = TextMobject("If you're curious...") @@ -921,6 +1001,9 @@ class SymbolicInversion(Scene): self.dither() class PlayInReverseWithSolution(PlayInReverse): + CONFIG = { + "t_matrix" : [[2, 1], [2, 3]] + } def setup(self): LinearTransformationScene.setup(self) equation = TexMobject([ @@ -964,9 +1047,40 @@ class OneUniqueSolution(Scene): self.play(Write(words.split()[1], run_time = 1)) self.dither() +class ThreeDTransformXToV(Scene): + pass + class ThreeDTransformAndReverse(Scene): pass +class DetNEZeroRule(Scene): + def construct(self): + text = TexMobject("\\det(A) \\ne 0") + text.shift(2*UP) + A_inv = TextMobject("$A^{-1}$ exists") + A_inv.shift(DOWN) + arrow = Arrow(text, A_inv) + self.play(Write(text)) + self.dither() + self.play(ShowCreation(arrow)) + self.play(Write(A_inv, run_time = 1)) + self.dither() + + +class ThreeDInverseRule(Scene): + def construct(self): + form = TexMobject("A^{-1} A = ") + form.scale(2) + matrix = Matrix(np.identity(3, 'int')) + matrix.highlight_columns(X_COLOR, Y_COLOR, Z_COLOR) + matrix.next_to(form, RIGHT) + self.add(form) + self.play(Write(matrix)) + self.dither() + +class ThreeDApplyReverseToV(Scene): + pass + class InversesDontAlwaysExist(TeacherStudentsScene): def construct(self): self.teacher_says("$A^{-1}$ doesn't always exist") @@ -1080,20 +1194,38 @@ class TowDColumnsDontSpan(LinearTransformationScene): lambda m : m.scale(-1).shift(self.i_hat.get_end()), self.j_hat )) - for x in range(5): - i_target, j_target = [ - m.copy().scale(random.uniform(-1.2, 1.2)) - for m in self.i_hat, self.j_hat - ] - j_target.shift(-j_target.get_start()+i_target.get_end()) + bases = [self.i_hat, self.j_hat] + for mob in bases: + mob.original = mob.copy() + for x in range(12): + for mob in bases: + mob.target = mob.original.copy() + mob.target.set_stroke(width = 6) + target_len = random.uniform(0.5, 1.5) + target_len *= random.choice([-1, 1]) + mob.target.scale(target_len) + self.j_hat.target.shift( + -self.j_hat.target.get_start()+ \ + self.i_hat.target.get_end() + ) self.play(Transform( - VMobject(self.i_hat, j_hat, VectorizedPoint()), - VMobject(i_target, j_target, VectorizedPoint()), - submobject_mode = "lagged_start" + VMobject(*bases), + VMobject(*[m.target for m in bases]), + run_time = 2 )) - self.dither() - if x == 2: + if x == 5: self.play(ShowCreation(Vector([2, -1]))) + form = TexMobject( + "A", "\\vec{\\textbf{x}}", "=", "\\vec{\\textbf{v}}" + ) + form[1].highlight(PINK) + form[3].highlight(YELLOW) + words = TextMobject("has no solution") + words.next_to(form, RIGHT) + form.add(words) + form.to_corner(UP+RIGHT) + form.add_background_rectangle() + self.play(Write(form, run_time = 2)) class ThreeDColumnsDontSpan(Scene): def construct(self): @@ -1118,6 +1250,672 @@ class ThreeDColumnsDontSpan(Scene): ) self.dither() +class NameColumnSpace(Scene): + def construct(self): + matrix = Matrix(np.array([ + [1, 1, 0], + [0, 1, 1], + [-1, -2, -1], + ]).T) + matrix.highlight_columns(X_COLOR, Y_COLOR, Z_COLOR) + matrix.to_corner(UP+LEFT) + cols = list(matrix.copy().get_mob_matrix().T) + col_arrays = map(Matrix, cols) + + span_text = TexMobject( + "\\text{Span}", + "\\Big(", + matrix_to_tex_string([1, 2, 3]), + ",", + matrix_to_tex_string([1, 2, 3]), + ",", + matrix_to_tex_string([1, 2, 3]), + "\\big)" + ) + for i in 1, -1: + span_text[i].stretch(1.5, 1) + span_text[i].do_in_place( + span_text[i].scale_to_fit_height, + span_text.get_height() + ) + for col_array, index in zip(col_arrays, [2, 4, 6]): + col_array.replace(span_text[index], dim_to_match = 1) + span_text.submobjects[index] = col_array + span_text.arrange_submobjects(RIGHT, buff = 0.2) + + arrow = DoubleArrow(LEFT, RIGHT) + column_space = TextMobject("``Column space''") + for mob in column_space, arrow: + mob.highlight(TEAL) + text = VMobject(span_text, arrow, column_space) + text.arrange_submobjects(RIGHT) + text.next_to(matrix, DOWN, buff = 1, aligned_edge = LEFT) + + self.add(matrix) + self.dither() + self.play(*[ + Transform( + VMobject(*matrix.copy().get_mob_matrix()[:,i]), + col_arrays[i].get_entries() + ) + for i in range(3) + ]) + self.play( + Write(span_text), + *map(Animation, self.get_mobjects_from_last_animation()) + ) + self.play( + ShowCreation(arrow), + Write(column_space) + ) + self.dither() + self.play(FadeOut(matrix)) + self.clear() + self.add(text) + + words = TextMobject( + "To solve", + "$A\\vec{\\textbf{x}} = \\vec{\\textbf{v}}$,\\\\", + "$\\vec{\\textbf{v}}$", + "must be in \\\\ the", + "column space." + ) + VMobject(*words[1][1:3]).highlight(PINK) + VMobject(*words[1][4:6]).highlight(YELLOW) + words[2].highlight(YELLOW) + words[4].highlight(TEAL) + words.to_corner(UP+LEFT) + + self.play(Write(words)) + self.dither(2) + self.play(FadeOut(words)) + + brace = Brace(column_space, UP) + rank_words = brace.get_text( + "Number of dimensions \\\\ is called", + "``rank''" + ) + rank_words[1].highlight(MAROON) + self.play( + GrowFromCenter(brace), + Write(rank_words) + ) + self.dither() + self.cycle_through_span_possibilities(span_text) + + def cycle_through_span_possibilities(self, span_text): + span_text.save_state() + two_d_span = span_text.copy() + for index, arr, c in (2, [1, 1], X_COLOR), (4, [0, 1], Y_COLOR): + col = Matrix(arr) + col.replace(two_d_span[index]) + two_d_span.submobjects[index] = col + col.get_entries().highlight(c) + for index in 5, 6: + two_d_span[index].scale(0) + two_d_span.arrange_submobjects(RIGHT, buff = 0.2) + two_d_span[-1].next_to(two_d_span[4], RIGHT, buff = 0.2) + two_d_span.move_to(span_text, side_to_align = RIGHT) + mob_matrix = np.array([ + two_d_span[i].get_entries().split() + for i in 2, 4 + ]) + + self.play(Transform(span_text, two_d_span)) + #horrible hack + span_text.shift(10*DOWN) + span_text = span_text.copy().restore() + ### + self.add(two_d_span) + self.dither() + self.replace_number_matrix(mob_matrix, [[1, 1], [1, 1]]) + self.dither() + self.replace_number_matrix(mob_matrix, [[0, 0], [0, 0]]) + self.dither() + self.play(Transform(two_d_span, span_text)) + self.dither() + self.remove(two_d_span) + self.add(span_text) + mob_matrix = np.array([ + span_text[i].get_entries().split() + for i in 2, 4, 6 + ]) + self.replace_number_matrix(mob_matrix, [[1, 1, 0], [0, 1, 1], [1, 0, 1]]) + self.dither() + self.replace_number_matrix(mob_matrix, [[1, 1, 0], [0, 1, 1], [-1, -2, -1]]) + self.dither() + self.replace_number_matrix(mob_matrix, [[1, 1, 0], [2, 2, 0], [3, 3, 0]]) + self.dither() + self.replace_number_matrix(mob_matrix, np.zeros((3, 3)).astype('int')) + self.dither() + + + def replace_number_matrix(self, matrix, new_numbers): + starters = matrix.flatten() + targets = map(TexMobject, map(str, np.array(new_numbers).flatten())) + for start, target in zip(starters, targets): + target.move_to(start) + target.highlight(start.get_color()) + self.play(*[ + Transform(*pair, path_arc = np.pi) + for pair in zip(starters, targets) + ]) + +class IHatShear(LinearTransformationScene): + CONFIG = { + "foreground_plane_kwargs" : { + "x_radius" : 2*SPACE_WIDTH, + "y_radius" : 2*SPACE_WIDTH, + "secondary_line_ratio" : 0 + }, + } + def construct(self): + self.apply_transposed_matrix([[1, 1], [0, 1]]) + self.dither() + +class DiagonalDegenerate(LinearTransformationScene): + def construct(self): + self.apply_transposed_matrix([[1, 1], [1, 1]]) + +class ZeroMatirx(LinearTransformationScene): + def construct(self): + origin = Dot(ORIGIN) + self.play(Transform( + VMobject(self.plane, self.i_hat, self.j_hat), + origin, + run_time = 3 + )) + self.dither() + +class RankNumber(Scene): + CONFIG = { + "number" : 3, + "color" : BLUE + } + def construct(self): + words = TextMobject("Rank", "%d"%self.number) + words[1].highlight(self.color) + self.add(words) + +class RankNumber2(RankNumber): + CONFIG = { + "number" : 2, + "color" : RED, + } + +class RankNumber1(RankNumber): + CONFIG = { + "number" : 1, + "color" : GREEN + } + +class RankNumber0(RankNumber): + CONFIG = { + "number" : 0, + "color" : GREY + } + +class NameFullRank(Scene): + def construct(self): + matrix = Matrix([[2, 5, 1], [3, 1, 4], [-4, 0, 0]]) + matrix.highlight_columns(X_COLOR, Y_COLOR, Z_COLOR) + matrix.to_edge(UP) + brace = Brace(matrix) + top_words = brace.get_text( + "When", "rank", "$=$", "number of columns", + ) + top_words[1].highlight(MAROON) + low_words = TextMobject( + "matrix is", "``full rank''" + ) + low_words[1].highlight(MAROON) + low_words.next_to(top_words, DOWN) + VMobject(matrix, brace, top_words, low_words).to_corner(UP+LEFT) + self.add(matrix) + self.play( + GrowFromCenter(brace), + Write(top_words) + ) + self.dither() + self.play(Write(low_words)) + self.dither() + +class OriginIsAlwaysInColumnSpace(LinearTransformationScene): + def construct(self): + vector = Matrix([0, 0]).highlight(YELLOW) + words = TextMobject("is always in the", "column space") + words[1].highlight(TEAL) + words.next_to(vector, RIGHT) + vector.add_to_back(BackgroundRectangle(vector)) + words.add_background_rectangle() + VMobject(vector, words).center().to_edge(UP) + arrow = Arrow(vector.get_bottom(), ORIGIN) + dot = Dot(ORIGIN, color = YELLOW) + + self.play(Write(vector), Write(words)) + self.play(ShowCreation(arrow)) + self.play(ShowCreation(dot, run_time = 0.5)) + self.add_foreground_mobject(vector, words, arrow, dot) + self.dither() + self.apply_transposed_matrix(self.t_matrix) + self.dither() + self.apply_transposed_matrix([[1./3, -1./2], [-1./3, 1./2]]) + self.dither() + +class FullRankCase(LinearTransformationScene): + CONFIG = { + "foreground_plane_kwargs" : { + "x_radius" : 2*SPACE_WIDTH, + "y_radius" : 2*SPACE_WIDTH, + "secondary_line_ratio" : 0 + }, + } + def construct(self): + t_matrices = [ + [[2, 1], [-3, 2]], + [[1./2, 1], [1./3, -1./2]] + ] + vector = Matrix([0, 0]).highlight(YELLOW) + title = VMobject( + TextMobject("Only"), vector, + TextMobject("lands on"), vector.copy() + ) + title.arrange_submobjects(buff = 0.2) + title.to_edge(UP) + for mob in title: + mob.add_to_back(BackgroundRectangle(mob)) + arrow = Arrow(vector.get_bottom(), ORIGIN) + dot = Dot(ORIGIN, color = YELLOW) + + words_on = False + for t_matrix in t_matrices: + self.apply_transposed_matrix(t_matrix) + if not words_on: + self.play(Write(title)) + self.play(ShowCreation(arrow)) + self.play(ShowCreation(dot, run_time = 0.5)) + self.add_foreground_mobject(title, arrow, dot) + words_on = True + self.apply_inverse_transpose(t_matrix) + self.dither() + +class NameNullSpace(LinearTransformationScene): + CONFIG = { + "show_basis_vectors" : False, + "t_matrix" : [[1, -1], [-1, 1]] + } + def construct(self): + vectors = self.get_vectors() + dot = Dot(ORIGIN, color = YELLOW) + line = Line(vectors[0].get_end(), vectors[-1].get_end()) + line.highlight(YELLOW) + null_space_label = TextMobject("``Null space''") + kernel_label = TextMobject("``Kernel''") + null_space_label.move_to(vectors[13].get_end(), side_to_align = UP+LEFT) + kernel_label.next_to(null_space_label, DOWN) + for mob in null_space_label, kernel_label: + mob.highlight(YELLOW) + mob.add_background_rectangle() + + self.play(ShowCreation(vectors, run_time = 3)) + self.dither() + vectors.save_state() + self.plane.save_state() + self.apply_transposed_matrix( + self.t_matrix, + added_anims = [Transform(vectors, dot)], + path_arc = 0 + ) + self.dither() + self.play( + vectors.restore, + self.plane.restore, + *map(Animation, self.foreground_mobjects), + run_time = 2 + ) + self.play(Transform( + vectors, line, + run_time = 2, + submobject_mode = "lagged_start" + )) + self.dither() + for label in null_space_label, kernel_label: + self.play(Write(label)) + self.dither() + self.apply_transposed_matrix( + self.t_matrix, + added_anims = [ + Transform(vectors, dot), + ApplyMethod(null_space_label.scale, 0), + ApplyMethod(kernel_label.scale, 0), + ], + path_arc = 0 + ) + self.dither() + + def get_vectors(self, offset = 0): + vect = np.array(UP+RIGHT) + vectors = VMobject(*[ + Vector(a*vect + offset) + for a in np.linspace(-5, 5, 18) + ]) + vectors.submobject_gradient_highlight(PINK, YELLOW) + return vectors + +class ThreeDNullSpaceIsLine(Scene): + pass + +class ThreeDNullSpaceIsPlane(Scene): + pass + +class NullSpaceSolveForVEqualsZero(NameNullSpace): + def construct(self): + vec = lambda s : "\\vec{\\textbf{%s}}"%s + equation = TexMobject("A", vec("x"), "=", vec("v")) + A, x, eq, v = equation + x.highlight(PINK) + v.highlight(YELLOW) + zero_vector = Matrix([0, 0]) + zero_vector.highlight(YELLOW) + zero_vector.scale(0.7) + zero_vector.move_to(v, side_to_align = LEFT) + VMobject(equation, zero_vector).next_to(ORIGIN, LEFT).to_edge(UP) + zero_vector_rect = BackgroundRectangle(zero_vector) + equation.add_background_rectangle() + + self.play(Write(equation)) + self.dither() + self.play( + ShowCreation(zero_vector_rect), + Transform(v, zero_vector) + ) + self.dither() + self.add_foreground_mobject(zero_vector_rect, equation) + NameNullSpace.construct(self) + +class OffsetNullSpace(NameNullSpace): + def construct(self): + x = Vector([-2, 1], color = RED) + vectors = self.get_vectors() + offset_vectors = self.get_vectors(offset = x.get_end()) + dots = VMobject(*[ + Dot(v.get_end(), color = v.get_color()) + for v in offset_vectors + ]) + dot = Dot( + self.get_matrix_transformation(self.t_matrix)(x.get_end()), + color = RED + ) + circle = Circle(color = YELLOW).replace(dot) + circle.scale_in_place(5) + words = TextMobject(""" + All vectors still land + on the same spot + """) + words.highlight(YELLOW) + words.add_background_rectangle() + words.next_to(circle) + x_copies = VMobject(*[ + x.copy().shift(v.get_end()) + for v in vectors + ]) + + self.play(FadeIn(vectors)) + self.dither() + self.add_vector(x, animate = True) + self.dither() + x_copy = VMobject(x.copy()) + self.play(Transform(x_copy, x_copies)) + self.play( + Transform(vectors, offset_vectors), + *[ + Transform(v, VectorizedPoint(v.get_end())) + for v in x_copy + ] + ) + self.remove(x_copy) + self.dither() + self.play(Transform(vectors, dots)) + self.dither() + self.apply_transposed_matrix( + self.t_matrix, + added_anims = [Transform(vectors, dot)] + ) + self.dither() + self.play( + ShowCreation(circle), + Write(words) + ) + self.dither() + +class ShowAdditivityProperty(LinearTransformationScene): + CONFIG = { + "show_basis_vectors" : False, + "t_matrix" : [[1, 0.5], [-1, 1]], + "include_background_plane" : False, + } + def construct(self): + v = Vector([2, -1]) + w = Vector([1, 2]) + v.highlight(YELLOW) + w.highlight(MAROON_B) + sum_vect = Vector(v.get_end()+w.get_end(), color = PINK) + form = TexMobject( + "A(", + "\\vec{\\textbf{v}}", + "+", + "\\vec{\\textbf{w}}", + ")", + "=A", + "\\vec{\\textbf{v}}", + "+A", + "\\vec{\\textbf{w}}", + ) + form.to_corner(UP+RIGHT) + VMobject(form[1], form[6]).highlight(YELLOW) + VMobject(form[3], form[8]).highlight(MAROON_B) + initial_sum = VMobject(*form[1:4]) + transformer = VMobject(form[0], form[4]) + final_sum = VMobject(*form[5:]) + form_rect = BackgroundRectangle(form) + + self.add(form_rect) + self.add_vector(v, animate = True) + self.add_vector(w, animate = True) + w_copy = w.copy() + self.play(w_copy.shift, v.get_end()) + self.add_vector(sum_vect, animate = True) + self.play( + Write(initial_sum), + FadeOut(w_copy) + ) + self.add_foreground_mobject(form_rect, initial_sum) + self.apply_transposed_matrix( + self.t_matrix, + added_anims = [Write(transformer)] + ) + self.dither() + self.play(w.copy().shift, v.get_end()) + self.play(Write(final_sum)) + self.dither() + +class AddJustOneNullSpaceVector(NameNullSpace): + def construct(self): + vectors = self.get_vectors() + self.add(vectors) + null_vector = vectors[int(0.7*len(vectors.split()))] + vectors.remove(null_vector) + null_vector.label = "$\\vec{\\textbf{n}}$" + x = Vector([-1, 1], color = RED) + x.label = "$\\vec{\\textbf{x}}$" + sum_vect = Vector( + x.get_end() + null_vector.get_end(), + color = PINK + ) + for v in x, null_vector: + v.label = TextMobject(v.label) + v.label.highlight(v.get_color()) + v.label.next_to(v.get_end(), UP) + v.label.add_background_rectangle() + dot = Dot(ORIGIN, color = null_vector.get_color()) + + form = TexMobject( + "A(", + "\\vec{\\textbf{x}}", + "+", + "\\vec{\\textbf{n}}", + ")", + "=A", + "\\vec{\\textbf{x}}", + "+A", + "\\vec{\\textbf{n}}", + ) + form.to_corner(UP+RIGHT) + VMobject(form[1], form[6]).highlight(x.get_color()) + VMobject(form[3], form[8]).highlight(null_vector.get_color()) + initial_sum = VMobject(*form[1:4]) + transformer = VMobject(form[0], form[4]) + final_sum = VMobject(*form[5:]) + brace = Brace(VMobject(*form[-2:])) + brace.add(brace.get_text("+0").add_background_rectangle()) + form_rect = BackgroundRectangle(form) + sum_vect.label = initial_sum.copy() + sum_vect.label.next_to(sum_vect.get_end(), UP) + + self.add_vector(x, animate = True) + self.play(Write(x.label)) + self.dither() + self.play( + FadeOut(vectors), + Animation(null_vector) + ) + self.play(Write(null_vector.label)) + self.dither() + x_copy = x.copy() + self.play(x_copy.shift, null_vector.get_end()) + self.add_vector(sum_vect, animate = True) + self.play( + FadeOut(x_copy), + Write(sum_vect.label) + ) + self.dither() + self.play( + ShowCreation(form_rect), + sum_vect.label.replace, initial_sum + ) + self.add_foreground_mobject(form_rect, sum_vect.label) + self.remove(x.label, null_vector.label) + self.apply_transposed_matrix( + self.t_matrix, + added_anims = [ + Transform(null_vector, dot), + Write(transformer) + ] + ) + self.play(Write(final_sum)) + self.dither() + self.play(Write(brace)) + self.dither() + words = TextMobject( + "$\\vec{\\textbf{x}}$", + "and the", + "$\\vec{\\textbf{x}} + \\vec{\\textbf{n}}$\\\\", + "land on the same spot" + ) + words[0].highlight(x.get_color()) + VMobject(*words[2][:2]).highlight(x.get_color()) + VMobject(*words[2][3:]).highlight(null_vector.get_color()) + words.next_to(brace, DOWN) + words.to_edge(RIGHT) + self.play(Write(words)) + self.dither() + +class NullSpaceOffsetRule(Scene): + def construct(self): + vec = lambda s : "\\vec{\\textbf{%s}}"%s + equation = TexMobject("A", vec("x"), "=", vec("v")) + A, x, equals, v = equation + x.highlight(PINK) + v.highlight(YELLOW) + A_text = TextMobject( + "When $A$ is not", "full rank" + ) + A_text[1].highlight(MAROON_C) + A_text.next_to(A, UP+LEFT, buff = 1) + A_text.shift_onto_screen() + A_arrow = Arrow(A_text.get_bottom(), A, color = WHITE) + v_text = TextMobject( + "If", "$%s$"%vec("v"), "is in the", + "column space", "of $A$" + ) + v_text[1].highlight(YELLOW) + v_text[3].highlight(TEAL) + v_text.next_to(v, DOWN+RIGHT, buff = 1) + v_text.shift_onto_screen() + v_arrow = Arrow(v_text.get_top(), v, color = YELLOW) + + + self.add(equation) + self.play(Write(A_text, run_time = 2)) + self.play(ShowCreation(A_arrow)) + self.dither() + self.play(Write(v_text, run_time = 2)) + self.play(ShowCreation(v_arrow)) + self.dither() + +class MuchLeftToLearn(TeacherStudentsScene): + def construct(self): + self.teacher_says( + "That's the high \\\\", + "level overview" + ) + self.random_blink() + self.dither() + self.teacher_says( + "There is still \\\\", + "much to learn" + ) + for pi in self.get_students(): + target_mode = random.choice([ + "raise_right_hand", "raise_left_hand" + ]) + self.play(pi.change_mode, target_mode) + self.random_blink() + self.dither() + +class NotToLearnItAllNow(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + The goal is not to + learn it all now + """) + self.random_blink() + self.dither() + self.random_blink() + +class NextVideo(Scene): + def construct(self): + title = TextMobject(""" + Next video: Nonsquare matrices + """) + title.scale_to_fit_width(2*SPACE_WIDTH - 2) + title.to_edge(UP) + rect = Rectangle(width = 16, height = 9, color = BLUE) + rect.scale_to_fit_height(6) + rect.next_to(title, DOWN) + + self.add(title) + self.play(ShowCreation(rect)) + self.dither() + +class WhatAboutNonsquareMatrices(TeacherStudentsScene): + def construct(self): + self.student_says( + "What about \\\\ nonsquare matrices?", + pi_creature_target_mode = "raise_right_hand" + ) + self.play(self.get_students()[0].change_mode, "confused") + self.random_blink(6) diff --git a/eola/footnote2.py b/eola/footnote2.py new file mode 100644 index 00000000..8ae39a98 --- /dev/null +++ b/eola/footnote2.py @@ -0,0 +1,42 @@ +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import VMobject + +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from topics.geometry import * +from topics.characters import * +from topics.functions import * +from topics.number_line import * +from topics.numerals import * +from scene import Scene +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * +from mobject.vectorized_mobject import * + +from eola.matrix import * +from eola.two_d_space import * + +from ka_playgrounds.circuits import Resistor, Source, LongResistor + +class OpeningQuote(Scene): + def construct(self): + words = TextMobject( + "On this quiz, I asked you to find the determinant of a", + "2x3 matrix.", + "Some of you, to my great amusement, actually tried to do this." + ) + words.scale_to_fit_width(2*SPACE_WIDTH - 2) + words.to_edge(UP) + words[1].highlight(GREEN) + author = TextMobject("-(Linear algebra professor whose name I could not track down)") + author.highlight(YELLOW) + author.next_to(words, DOWN, buff = 0.5) + + self.play(FadeIn(words)) + self.dither(2) + self.play(Write(author, run_time = 3)) + self.dither() \ No newline at end of file diff --git a/eola/thumbnails.py b/eola/thumbnails.py index f9ed3d66..29081bb0 100644 --- a/eola/thumbnails.py +++ b/eola/thumbnails.py @@ -74,6 +74,15 @@ class Chapter4p2(Chapter0): "t_matrix" : [[1, 2], [-1, 1]] } +class Chapter5(LinearTransformationScene): + def construct(self): + self.plane.fade() + self.add_unit_square() + self.plane.set_stroke(width = 6) + VMobject(self.i_hat, self.j_hat).set_stroke(width = 10) + self.square.set_fill(YELLOW, opacity = 0.7) + self.square.set_stroke(width = 0) + self.apply_transposed_matrix(self.t_matrix, run_time = 0) diff --git a/mobject/mobject.py b/mobject/mobject.py index cb7d9a26..6749afe7 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -267,7 +267,7 @@ class Mobject(object): self.shift(target - point_to_align) return self - def replace(self, mobject, stretch = False): + def replace(self, mobject, dim_to_match = 0, stretch = False): if not mobject.get_num_points() and not mobject.submobjects: raise Warning("Attempting to replace mobject with no points") return self @@ -275,7 +275,11 @@ class Mobject(object): self.stretch_to_fit_width(mobject.get_width()) self.stretch_to_fit_height(mobject.get_height()) else: - self.scale_to_fit_width(mobject.get_width()) + self.stretch_to_fit( + mobject.length_over_dim(dim_to_match), + dim_to_match, + stretch = False + ) self.shift(mobject.get_center() - self.get_center()) return self diff --git a/topics/characters.py b/topics/characters.py index 45ba6cc2..d6ccf603 100644 --- a/topics/characters.py +++ b/topics/characters.py @@ -3,7 +3,7 @@ from helpers import * from mobject import Mobject from mobject.svg_mobject import SVGMobject from mobject.vectorized_mobject import VMobject -from mobject.tex_mobject import TextMobject +from mobject.tex_mobject import TextMobject, TexMobject from animation import Animation from animation.transform import Transform, ApplyMethod, FadeOut, FadeIn @@ -343,8 +343,12 @@ class TeacherStudentsScene(Scene): pi_creature_target_mode = None, added_anims = [], **bubble_kwargs): - if isinstance(content, str): - content = TextMobject(content) + if all(map(lambda s : isinstance(s, str), content)): + content = TextMobject(*content) + elif len(content) == 1 and isinstance(content[0], TexMobject): + content = content[0] + else: + raise Exception("Invalid content type") content_intro_anims = self.get_bubble_intro_animation( content, bubble_type, pi_creature, **bubble_kwargs ) @@ -373,22 +377,22 @@ class TeacherStudentsScene(Scene): self.play(*anims) return pi_creature.bubble - def teacher_says(self, content = "", **kwargs): + def teacher_says(self, *content, **kwargs): return self.introduce_bubble( content, "speech", self.get_teacher(), **kwargs ) - def student_says(self, content = "", student_index = 1, **kwargs): - student = self.get_students()[student_index] + def student_says(self, *content, **kwargs): + student = self.get_students()[kwargs.get("student_index", 1)] return self.introduce_bubble(content, "speech", student, **kwargs) - def teacher_thinks(self, content = "", **kwargs): + def teacher_thinks(self, *content, **kwargs): return self.introduce_bubble( content, "thought", self.get_teacher(), **kwargs ) - def student_thinks(self, content = "", student_index = 1, **kwargs): - student = self.get_students()[student_index] + def student_thinks(self, *content, **kwargs): + student = self.get_students()[kwargs.get("student_index", 1)] return self.introduce_bubble(content, "thought", student, **kwargs) def random_blink(self, num_times = 1): @@ -397,6 +401,11 @@ class TeacherStudentsScene(Scene): self.play(Blink(pi_creature)) self.dither() + def change_student_modes(self, *modes): + self.play(*[ + ApplyMethod(pi.change_mode, mode) + for pi, mode in zip(self.get_students(), modes) + ])