from manimlib.imports import * def derivative(func, x, n = 1, dx = 0.01): samples = [func(x + (k - n/2)*dx) for k in range(n+1)] while len(samples) > 1: samples = [ (s_plus_dx - s)/dx for s, s_plus_dx in zip(samples, samples[1:]) ] return samples[0] def taylor_approximation(func, highest_term, center_point = 0): derivatives = [ derivative(func, center_point, n = n) for n in range(highest_term + 1) ] coefficients = [ d/math.factorial(n) for n, d in enumerate(derivatives) ] return lambda x : sum([ c*((x-center_point)**n) for n, c in enumerate(coefficients) ]) class Chapter10OpeningQuote(OpeningQuote): CONFIG = { "quote" : [ "For me, mathematics is a collection of ", "examples", "; a ", "theorem", " is a statement about a collection of ", "examples", " and the purpose of proving ", "theorems", " is to classify and explain the ", "examples", "." ], "quote_arg_separator" : "", "highlighted_quote_terms" : { "examples" : BLUE, }, "author" : "John B. Conway", "fade_in_kwargs" : { "run_time" : 7, } } class ExampleApproximation(GraphScene): CONFIG = { "function" : lambda x : np.exp(-x**2), "function_tex" : "e^{-x^2}", "function_color" : BLUE, "order_sequence" : [0, 2, 4], "center_point" : 0, "approximation_terms" : ["1 ", "-x^2", "+\\frac{1}{2}x^4"], "approximation_color" : GREEN, "x_min" : -3, "x_max" : 3, "y_min" : -1, "y_max" : 2, "graph_origin" : DOWN + 2*LEFT, } def construct(self): self.setup_axes() func_graph = self.get_graph( self.function, self.function_color, ) approx_graphs = [ self.get_graph( taylor_approximation(self.function, n), self.approximation_color ) for n in self.order_sequence ] near_text = TextMobject( "Near %s $= %d$"%( self.x_axis_label, self.center_point ) ) near_text.to_corner(UP + RIGHT) near_text.add_background_rectangle() equation = TexMobject( self.function_tex, "\\approx", *self.approximation_terms ) equation.next_to(near_text, DOWN, MED_LARGE_BUFF) equation.to_edge(RIGHT) near_text.next_to(equation, UP, MED_LARGE_BUFF) equation.set_color_by_tex( self.function_tex, self.function_color, substring = False ) approx_terms = VGroup(*[ equation.get_part_by_tex(tex, substring = False) for tex in self.approximation_terms ]) approx_terms.set_fill( self.approximation_color, opacity = 0, ) equation.add_background_rectangle() approx_graph = VectorizedPoint( self.input_to_graph_point(self.center_point, func_graph) ) self.play( ShowCreation(func_graph, run_time = 2), Animation(equation), Animation(near_text), ) for graph, term in zip(approx_graphs, approx_terms): self.play( Transform(approx_graph, graph, run_time = 2), Animation(equation), Animation(near_text), term.set_fill, None, 1, ) self.wait() self.wait(2) class ExampleApproximationWithSine(ExampleApproximation): CONFIG = { "function" : np.sin, "function_tex" : "\\sin(x)", "order_sequence" : [1, 3, 5], "center_point" : 0, "approximation_terms" : [ "x", "-\\frac{1}{6}x^3", "+\\frac{1}{120}x^5", ], "approximation_color" : GREEN, "x_min" : -2*np.pi, "x_max" : 2*np.pi, "x_tick_frequency" : np.pi/2, "y_min" : -2, "y_max" : 2, "graph_origin" : DOWN + 2*LEFT, } class ExampleApproximationWithExp(ExampleApproximation): CONFIG = { "function" : np.exp, "function_tex" : "e^x", "order_sequence" : [1, 2, 3, 4], "center_point" : 0, "approximation_terms" : [ "1 + x", "+\\frac{1}{2}x^2", "+\\frac{1}{6}x^3", "+\\frac{1}{24}x^4", ], "approximation_color" : GREEN, "x_min" : -3, "x_max" : 4, "y_min" : -1, "y_max" : 10, "graph_origin" : 2*DOWN + 3*LEFT, } class Pendulum(ReconfigurableScene): CONFIG = { "anchor_point" : 3*UP + 4*LEFT, "radius" : 4, "weight_radius" : 0.2, "angle" : np.pi/6, "approx_tex" : [ "\\approx 1 - ", "{\\theta", "^2", "\\over", "2}" ], "leave_original_cosine" : False, "perform_substitution" : True, } def construct(self): self.draw_pendulum() self.show_oscillation() self.show_height() self.get_angry_at_cosine() self.substitute_approximation() self.show_confusion() def draw_pendulum(self): pendulum = self.get_pendulum() ceiling = self.get_ceiling() self.add(ceiling) self.play(ShowCreation(pendulum.line)) self.play(DrawBorderThenFill(pendulum.weight, run_time = 1)) self.pendulum = pendulum def show_oscillation(self): trajectory_dots = self.get_trajectory_dots() kwargs = self.get_swing_kwargs() self.play( ShowCreation( trajectory_dots, rate_func=linear, run_time = kwargs["run_time"] ), Rotate(self.pendulum, -2*self.angle, **kwargs), ) for m in 2, -2, 2: self.play(Rotate(self.pendulum, m*self.angle, **kwargs)) self.wait() def show_height(self): v_line = self.get_v_line() h_line = self.get_h_line() radius_brace = self.get_radius_brace() height_brace = self.get_height_brace() height_tex = self.get_height_brace_tex(height_brace) arc, theta = self.get_arc_and_theta() height_tex_R = height_tex.get_part_by_tex("R") height_tex_theta = height_tex.get_part_by_tex("\\theta") to_write = VGroup(*[ part for part in height_tex if part not in [height_tex_R, height_tex_theta] ]) self.play( ShowCreation(h_line), GrowFromCenter(height_brace) ) self.play( ShowCreation(v_line), ShowCreation(arc), Write(theta), ) self.play( GrowFromCenter(radius_brace) ) self.wait(2) self.play( Write(to_write), ReplacementTransform( radius_brace[-1].copy(), height_tex_R ), ReplacementTransform( theta.copy(), height_tex_theta ), run_time = 2 ) self.wait(2) self.arc = arc self.theta = theta self.height_tex_R = height_tex_R self.cosine = VGroup(*[ height_tex.get_part_by_tex(tex) for tex in ("cos", "theta", ")") ]) self.one_minus = VGroup(*[ height_tex.get_part_by_tex(tex) for tex in ("\\big(1-", "\\big)") ]) def get_angry_at_cosine(self): cosine = self.cosine morty = Mortimer() morty.to_corner(DOWN+RIGHT) cosine.generate_target() cosine.save_state() cosine.target.next_to(morty, UP) if self.leave_original_cosine: cosine_copy = cosine.copy() self.add(cosine_copy) self.one_minus.add(cosine_copy) self.play(FadeIn(morty)) self.play( MoveToTarget(cosine), morty.change, "angry", cosine.target, ) self.wait() self.play(Blink(morty)) self.wait() self.morty = morty def substitute_approximation(self): morty = self.morty cosine = self.cosine cosine.generate_target() cosine_approx = self.get_cosine_approx() cosine_approx.next_to(cosine, UP+RIGHT) cosine_approx.to_edge(RIGHT) cosine.target.next_to( cosine_approx, LEFT, align_using_submobjects = True ) kwargs = self.get_swing_kwargs() self.play( FadeIn( cosine_approx, run_time = 2, lag_ratio = 0.5 ), MoveToTarget(cosine), morty.change, "pondering", cosine_approx ) self.wait() if not self.perform_substitution: return self.play( ApplyMethod( cosine_approx.theta_squared_over_two.copy().next_to, self.height_tex_R, run_time = 2, ), FadeOut(self.one_minus), morty.look_at, self.height_tex_R, ) self.play(morty.change, "thinking", self.height_tex_R) self.transition_to_alt_config( angle = np.pi/12, transformation_kwargs = {"run_time" : 2}, ) def show_confusion(self): randy = Randolph(color = BLUE_C) randy.scale(0.8) randy.next_to(self.cosine, DOWN+LEFT) randy.to_edge(DOWN) self.play(FadeIn(randy)) self.play( randy.change, "confused", self.cosine ) self.play(randy.look_at, self.height_tex_R) self.wait() self.play(randy.look_at, self.cosine) self.play(Blink(randy)) self.wait() ####### def get_pendulum(self): line = Line( self.anchor_point, self.anchor_point + self.radius*DOWN, color = WHITE ) weight = Circle( radius = self.weight_radius, fill_color = GREY, fill_opacity = 1, stroke_width = 0, ) weight.move_to(line.get_end()) result = VGroup(line, weight) result.rotate( self.angle, about_point = self.anchor_point ) result.line = line result.weight = weight return result def get_ceiling(self): line = Line(LEFT, RIGHT, color = GREY) line.scale(FRAME_X_RADIUS) line.move_to(self.anchor_point[1]*UP) return line def get_trajectory_dots(self, n_dots = 40, color = YELLOW): arc_angle = np.pi/6 proportions = self.swing_rate_func( np.linspace(0, 1, n_dots) ) angles = -2*arc_angle*proportions angle_to_point = lambda a : np.cos(a)*RIGHT + np.sin(a)*UP dots = VGroup(*[ # Line(*map(angle_to_point, pair)) Dot(angle_to_point(angle), radius = 0.005) for angle in angles ]) dots.set_color(color) dots.scale(self.radius) dots.rotate(-np.pi/2 + arc_angle) dots.shift(self.anchor_point) return dots def get_v_line(self): return DashedLine( self.anchor_point, self.anchor_point + self.radius*DOWN, color = WHITE ) def get_h_line(self, color = BLUE): start = self.anchor_point + self.radius*DOWN end = start + self.radius*np.sin(self.angle)*RIGHT return Line(start, end, color = color) def get_radius_brace(self): v_line = self.get_v_line() brace = Brace(v_line, RIGHT) brace.rotate(self.angle, about_point = self.anchor_point) brace.add(brace.get_text("$R$", buff = SMALL_BUFF)) return brace def get_height_brace(self): h_line = self.get_h_line() height = (1 - np.cos(self.angle))*self.radius line = Line( h_line.get_end(), h_line.get_end() + height*UP, ) brace = Brace(line, RIGHT) return brace def get_height_brace_tex(self, brace): tex_mob = TexMobject( "R", "\\big(1-", "\\cos(", "\\theta", ")", "\\big)" ) tex_mob.set_color_by_tex("theta", YELLOW) tex_mob.next_to(brace, RIGHT) return tex_mob def get_arc_and_theta(self): arc = Arc( start_angle = -np.pi/2, angle = self.angle, color = YELLOW ) theta = TexMobject("\\theta") theta.set_color(YELLOW) theta.next_to( arc.point_from_proportion(0.5), DOWN, SMALL_BUFF ) for mob in arc, theta: mob.shift(self.anchor_point) return arc, theta def get_cosine_approx(self): approx = TexMobject(*self.approx_tex) approx.set_color_by_tex("theta", YELLOW) approx.theta_squared_over_two = VGroup(*approx[1:5]) return approx def get_swing_kwargs(self): return { "about_point" : self.anchor_point, "run_time" : 1.7, "rate_func" : self.swing_rate_func, } def swing_rate_func(self, t): return (1-np.cos(np.pi*t))/2.0 class PendulumWithBetterApprox(Pendulum): CONFIG = { "approx_tex" : [ "\\approx 1 - ", "{\\theta", "^2", "\\over", "2}", "+", "{\\theta", "^4", "\\over", "24}" ], "leave_original_cosine" : True, "perform_substitution" : False, } def show_confusion(self): pass class ExampleApproximationWithCos(ExampleApproximationWithSine): CONFIG = { "function" : np.cos, "function_tex" : "\\cos(\\theta)", "order_sequence" : [0, 2], "approximation_terms" : [ "1", "-\\frac{1}{2} \\theta ^2", ], "x_axis_label" : "$\\theta$", "y_axis_label" : "", "x_axis_width" : 13, "graph_origin" : DOWN, } def construct(self): ExampleApproximationWithSine.construct(self) randy = Randolph(color = BLUE_C) randy.to_corner(DOWN+LEFT) high_graph = self.get_graph(lambda x : 4) v_lines, alt_v_lines = [ VGroup(*[ self.get_vertical_line_to_graph( u*dx, high_graph, line_class = DashedLine, color = YELLOW ) for u in (-1, 1) ]) for dx in (0.01, 0.7) ] self.play(*list(map(ShowCreation, v_lines)), run_time = 2) self.play(Transform( v_lines, alt_v_lines, run_time = 2, )) self.play(FadeIn(randy)) self.play(PiCreatureBubbleIntroduction( randy, "How...?", bubble_class = ThoughtBubble, look_at_arg = self.graph_origin, target_mode = "confused" )) self.wait(2) self.play(Blink(randy)) self.wait() def setup_axes(self): GraphScene.setup_axes(self) x_val_label_pairs = [ (-np.pi, "-\\pi"), (np.pi, "\\pi"), (2*np.pi, "2\\pi"), ] self.x_axis_labels = VGroup() for x_val, label in x_val_label_pairs: tex = TexMobject(label) tex.next_to(self.coords_to_point(x_val, 0), DOWN) self.add(tex) self.x_axis_labels.add(tex) class ConstructQuadraticApproximation(ExampleApproximationWithCos): CONFIG = { "x_axis_label" : "$x$", "colors" : [BLUE, YELLOW, GREEN], } def construct(self): self.setup_axes() self.add_cosine_graph() self.add_quadratic_graph() self.introduce_quadratic_constants() self.show_value_at_zero() self.set_c0_to_one() self.let_c1_and_c2_vary() self.show_tangent_slope() self.compute_cosine_derivative() self.compute_polynomial_derivative() self.let_c2_vary() self.point_out_negative_concavity() self.compute_cosine_second_derivative() self.show_matching_curvature() self.show_matching_tangent_lines() self.compute_polynomial_second_derivative() self.box_final_answer() def add_cosine_graph(self): cosine_label = TexMobject("\\cos(x)") cosine_label.to_corner(UP+LEFT) cosine_graph = self.get_graph(np.cos) dot = Dot(color = WHITE) dot.move_to(cosine_label) for mob in cosine_label, cosine_graph: mob.set_color(self.colors[0]) def update_dot(dot): dot.move_to(cosine_graph.points[-1]) return dot self.play(Write(cosine_label, run_time = 1)) self.play(dot.move_to, cosine_graph.points[0]) self.play( ShowCreation(cosine_graph), UpdateFromFunc(dot, update_dot), run_time = 4 ) self.play(FadeOut(dot)) self.cosine_label = cosine_label self.cosine_graph = cosine_graph def add_quadratic_graph(self): quadratic_graph = self.get_quadratic_graph() self.play(ReplacementTransform( self.cosine_graph.copy(), quadratic_graph, run_time = 3 )) self.quadratic_graph = quadratic_graph def introduce_quadratic_constants(self): quadratic_tex = self.get_quadratic_tex("c_0", "c_1", "c_2") const_terms = quadratic_tex.get_parts_by_tex("c") free_to_change = TextMobject("Free to change") free_to_change.next_to(const_terms, DOWN, LARGE_BUFF) arrows = VGroup(*[ Arrow( free_to_change.get_top(), const.get_bottom(), tip_length = 0.75*Arrow.CONFIG["tip_length"], color = const.get_color() ) for const in const_terms ]) alt_consts_list = [ (0, -1, -0.25), (1, -1, -0.25), (1, 0, -0.25), (), ] self.play(FadeIn( quadratic_tex, run_time = 3, lag_ratio = 0.5 )) self.play( FadeIn(free_to_change), *list(map(ShowCreation, arrows)) ) self.play(*[ ApplyMethod( const.scale_in_place, 0.8, run_time = 2, rate_func = squish_rate_func(there_and_back, a, a + 0.75) ) for const, a in zip(const_terms, np.linspace(0, 0.25, len(const_terms))) ]) for alt_consts in alt_consts_list: self.change_quadratic_graph( self.quadratic_graph, *alt_consts ) self.wait() self.quadratic_tex = quadratic_tex self.free_to_change_group = VGroup(free_to_change, *arrows) self.free_to_change_group.arrows = arrows def show_value_at_zero(self): arrow, x_equals_0 = ax0_group = self.get_arrow_x_equals_0_group() ax0_group.next_to( self.cosine_label, RIGHT, align_using_submobjects = True ) one = TexMobject("1") one.next_to(arrow, RIGHT) one.save_state() one.move_to(self.cosine_label) one.set_fill(opacity = 0) v_line = self.get_vertical_line_to_graph( 0, self.cosine_graph, line_class = DashedLine, color = YELLOW ) self.play(ShowCreation(v_line)) self.play( ShowCreation(arrow), Write(x_equals_0, run_time = 2) ) self.play(one.restore) self.wait() self.v_line = v_line self.equals_one_group = VGroup(arrow, x_equals_0, one) def set_c0_to_one(self): poly_at_zero = self.get_quadratic_tex( "c_0", "c_1", "c_2", arg = "0" ) poly_at_zero.next_to(self.quadratic_tex, DOWN) equals_c0 = TexMobject("=", "c_0", "+0") equals_c0.set_color_by_tex("c_0", self.colors[0]) equals_c0.next_to( poly_at_zero.get_part_by_tex("="), DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) poly_group = VGroup( equals_c0, poly_at_zero, self.quadratic_tex, ) poly_group_target = VGroup( TexMobject("=", "1", "+0").set_color_by_tex("1", self.colors[0]), self.get_quadratic_tex("1", "c_1", "c_2", arg = "0"), self.get_quadratic_tex("1", "c_1", "c_2"), ) for start, target in zip(poly_group, poly_group_target): target.move_to(start) self.play(FadeOut(self.free_to_change_group)) self.play(ReplacementTransform( self.quadratic_tex.copy(), poly_at_zero )) self.wait(2) self.play(FadeIn(equals_c0)) self.wait(2) self.play(Transform( poly_group, poly_group_target, run_time = 2, lag_ratio = 0.5 )) self.wait(2) self.play(*list(map(FadeOut, [poly_at_zero, equals_c0]))) self.free_to_change_group.remove( self.free_to_change_group.arrows[0] ) self.play(FadeIn(self.free_to_change_group)) def let_c1_and_c2_vary(self): alt_consts_list = [ (1, 1, -0.25), (1, -1, -0.25), (1, -1, 0.25), (1, 1, -0.1), ] for alt_consts in alt_consts_list: self.change_quadratic_graph( self.quadratic_graph, *alt_consts ) self.wait() def show_tangent_slope(self): graph_point_at_zero = self.input_to_graph_point( 0, self.cosine_graph ) tangent_line = self.get_tangent_line(0, self.cosine_graph) self.play(ShowCreation(tangent_line)) self.change_quadratic_graph( self.quadratic_graph, 1, 0, -0.1 ) self.wait() self.change_quadratic_graph( self.quadratic_graph, 1, 1, -0.1 ) self.wait(2) self.change_quadratic_graph( self.quadratic_graph, 1, 0, -0.1 ) self.wait(2) self.tangent_line = tangent_line def compute_cosine_derivative(self): derivative, rhs = self.get_cosine_derivative() self.play(FadeIn( VGroup(derivative, *rhs[:2]), run_time = 2, lag_ratio = 0.5 )) self.wait(2) self.play(Write(VGroup(*rhs[2:])), run_time = 2) self.wait() self.play(Rotate( self.tangent_line, np.pi/12, in_place = True, run_time = 3, rate_func = wiggle )) self.wait() def compute_polynomial_derivative(self): derivative = self.get_quadratic_derivative("c_1", "c_2") derivative_at_zero = self.get_quadratic_derivative( "c_1", "c_2", arg = "0" ) equals_c1 = TexMobject("=", "c_1", "+0") equals_c1.next_to( derivative_at_zero.get_part_by_tex("="), DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT, ) equals_c1.set_color_by_tex("c_1", self.colors[1]) poly_group = VGroup( equals_c1, derivative, self.quadratic_tex ) poly_group_target = VGroup( TexMobject("=", "0", "+0").set_color_by_tex( "0", self.colors[1], substring = False ), self.get_quadratic_derivative("0", "c_2", arg = "0"), self.get_quadratic_tex("1", "0", "c_2") ) for start, target in zip(poly_group, poly_group_target): target.move_to(start) self.play(FadeOut(self.free_to_change_group)) self.play(FadeIn( derivative, run_time = 3, lag_ratio = 0.5 )) self.wait() self.play(Transform( derivative, derivative_at_zero, run_time = 2, lag_ratio = 0.5 )) self.wait(2) self.play(Write(equals_c1)) self.wait(2) self.play(Transform( poly_group, poly_group_target, run_time = 3, lag_ratio = 0.5 )) self.wait(2) self.play(*list(map(FadeOut, poly_group[:-1]))) self.free_to_change_group.remove( self.free_to_change_group.arrows[1] ) self.play(FadeIn(self.free_to_change_group)) def let_c2_vary(self): alt_c2_values = [-1, -0.05, 1, -0.2] for alt_c2 in alt_c2_values: self.change_quadratic_graph( self.quadratic_graph, 1, 0, alt_c2 ) self.wait() def point_out_negative_concavity(self): partial_cosine_graph = self.get_graph( np.cos, x_min = -1, x_max = 1, color = PINK ) self.play(ShowCreation(partial_cosine_graph, run_time = 2)) self.wait() for x, run_time in (-1, 2), (1, 4): self.play(self.get_tangent_line_change_anim( self.tangent_line, x, self.cosine_graph, run_time = run_time )) self.wait() self.play(*list(map(FadeOut, [ partial_cosine_graph, self.tangent_line ]))) def compute_cosine_second_derivative(self): second_deriv, rhs = self.get_cosine_second_derivative() self.play(FadeIn( VGroup(second_deriv, *rhs[1][:2]), run_time = 2, lag_ratio = 0.5 )) self.wait(3) self.play(Write(VGroup(*rhs[1][2:]), run_time = 2)) self.wait() def show_matching_curvature(self): alt_consts_list = [ (1, 1, -0.2), (1, 0, -0.2), (1, 0, -0.5), ] for alt_consts in alt_consts_list: self.change_quadratic_graph( self.quadratic_graph, *alt_consts ) self.wait() def show_matching_tangent_lines(self): graphs = [self.quadratic_graph, self.cosine_graph] tangent_lines = [ self.get_tangent_line(0, graph, color = color) for graph, color in zip(graphs, [WHITE, YELLOW]) ] tangent_change_anims = [ self.get_tangent_line_change_anim( line, np.pi/2, graph, run_time = 6, rate_func = there_and_back, ) for line, graph in zip(tangent_lines, graphs) ] self.play(*list(map(ShowCreation, tangent_lines))) self.play(*tangent_change_anims) self.play(*list(map(FadeOut, tangent_lines))) def compute_polynomial_second_derivative(self): c2s = ["c_2", "\\text{\\tiny $\\left(-\\frac{1}{2}\\right)$}"] derivs = [ self.get_quadratic_derivative("0", c2) for c2 in c2s ] second_derivs = [ TexMobject( "{d^2 P \\over dx^2}", "(x)", "=", "2", c2 ) for c2 in c2s ] for deriv, second_deriv in zip(derivs, second_derivs): second_deriv[0].scale( 0.7, about_point = second_deriv[0].get_right() ) second_deriv[-1].set_color(self.colors[-1]) second_deriv.next_to( deriv, DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) poly_group = VGroup( second_derivs[0], derivs[0], self.quadratic_tex ) poly_group_target = VGroup( second_derivs[1], derivs[1], self.get_quadratic_tex("1", "0", c2s[1]) ) for tex_mob in poly_group_target: tex_mob.get_part_by_tex(c2s[1]).shift(SMALL_BUFF*UP) self.play(FadeOut(self.free_to_change_group)) self.play(FadeIn(derivs[0])) self.wait(2) self.play(Write(second_derivs[0])) self.wait(2) self.play(Transform( poly_group, poly_group_target, run_time = 3, lag_ratio = 0.5 )) self.wait(3) def box_final_answer(self): box = Rectangle(stroke_color = PINK) box.stretch_to_fit_width( self.quadratic_tex.get_width() + MED_LARGE_BUFF ) box.stretch_to_fit_height( self.quadratic_tex.get_height() + MED_LARGE_BUFF ) box.move_to(self.quadratic_tex) self.play(ShowCreation(box, run_time = 2)) self.wait(2) ###### def change_quadratic_graph(self, graph, *args, **kwargs): transformation_kwargs = {} transformation_kwargs["run_time"] = kwargs.pop("run_time", 2) transformation_kwargs["rate_func"] = kwargs.pop("rate_func", smooth) new_graph = self.get_quadratic_graph(*args, **kwargs) self.play(Transform(graph, new_graph, **transformation_kwargs)) graph.underlying_function = new_graph.underlying_function def get_quadratic_graph(self, c0 = 1, c1 = 0, c2 = -0.5): return self.get_graph( lambda x : c0 + c1*x + c2*x**2, color = self.colors[2] ) def get_quadratic_tex(self, c0, c1, c2, arg = "x"): tex_mob = TexMobject( "P(", arg, ")", "=", c0, "+", c1, arg, "+", c2, arg, "^2" ) for tex, color in zip([c0, c1, c2], self.colors): tex_mob.set_color_by_tex(tex, color) tex_mob.to_corner(UP+RIGHT) return tex_mob def get_quadratic_derivative(self, c1, c2, arg = "x"): result = TexMobject( "{dP \\over dx}", "(", arg, ")", "=", c1, "+", "2", c2, arg ) result[0].scale(0.7, about_point = result[0].get_right()) for index, color in zip([5, 8], self.colors[1:]): result[index].set_color(color) if hasattr(self, "quadratic_tex"): result.next_to( self.quadratic_tex, DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) return result def get_arrow_x_equals_0_group(self): arrow = Arrow(LEFT, RIGHT) x_equals_0 = TexMobject("x = 0") x_equals_0.scale(0.75) x_equals_0.next_to(arrow.get_center(), UP, 2*SMALL_BUFF) x_equals_0.shift(SMALL_BUFF*LEFT) return VGroup(arrow, x_equals_0) def get_tangent_line(self, x, graph, color = YELLOW): tangent_line = Line(LEFT, RIGHT, color = color) tangent_line.rotate(self.angle_of_tangent(x, graph)) tangent_line.scale(2) tangent_line.move_to(self.input_to_graph_point(x, graph)) return tangent_line def get_tangent_line_change_anim(self, tangent_line, new_x, graph, **kwargs): start_x = self.x_axis.point_to_number( tangent_line.get_center() ) def update(tangent_line, alpha): x = interpolate(start_x, new_x, alpha) new_line = self.get_tangent_line( x, graph, color = tangent_line.get_color() ) Transform(tangent_line, new_line).update(1) return tangent_line return UpdateFromAlphaFunc(tangent_line, update, **kwargs) def get_cosine_derivative(self): if not hasattr(self, "cosine_label"): self.cosine_label = TexMobject("\\cos(x)") self.cosine_label.to_corner(UP+LEFT) derivative = TexMobject( "{d(", "\\cos", ")", "\\over", "dx}", "(0)", ) derivative.set_color_by_tex("\\cos", self.colors[0]) derivative.scale(0.7) derivative.next_to( self.cosine_label, DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) rhs = TexMobject("=", "-\\sin(0)", "=", "0") rhs.set_color_by_tex("\\sin", self.colors[1]) rhs.scale(0.75) rhs.next_to( derivative, RIGHT, align_using_submobjects = True ) self.cosine_derivative = VGroup(derivative, rhs) return self.cosine_derivative def get_cosine_second_derivative(self): if not hasattr(self, "cosine_derivative"): self.get_cosine_derivative() second_deriv = TexMobject( "{d^2(", "\\cos", ")", "\\over", "dx^2}", "(", "0", ")", ) second_deriv.set_color_by_tex("cos", self.colors[0]) second_deriv.set_color_by_tex("-\\cos", self.colors[2]) second_deriv.scale(0.75) second_deriv.add_background_rectangle() second_deriv.next_to( self.cosine_derivative, DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) rhs = TexMobject("=", "-\\cos(0)", "=", "-1") rhs.set_color_by_tex("cos", self.colors[2]) rhs.scale(0.8) rhs.next_to( second_deriv, RIGHT, align_using_submobjects = True ) rhs.add_background_rectangle() self.cosine_second_derivative = VGroup(second_deriv, rhs) return self.cosine_second_derivative class ReflectOnQuadraticApproximation(TeacherStudentsScene): def construct(self): self.show_example_approximation() # self.add_polynomial() # self.show_c0() # self.show_c1() # self.show_c2() def show_example_approximation(self): approx_at_x, approx_at_point = [ TexMobject( "\\cos(", s, ")", "\\approx", "1 - \\frac{1}{2}", "(", s, ")", "^2" ).next_to(self.get_students(), UP, 2) for s in ("x", "0.1",) ] approx_rhs = TexMobject("=", "0.995") approx_rhs.next_to(approx_at_point, RIGHT) real_result = TexMobject( "\\cos(", "0.1", ")", "=", "%.7f\\dots"%np.cos(0.1) ) real_result.shift( approx_rhs.get_part_by_tex("=").get_center() -\ real_result.get_part_by_tex("=").get_center() ) for mob in approx_at_point, real_result: mob.set_color_by_tex("0.1", YELLOW) real_result.set_fill(opacity = 0) self.play( Write(approx_at_x, run_time = 2), self.teacher.change_mode, "raise_right_hand" ) self.wait(2) self.play(ReplacementTransform( approx_at_x, approx_at_point, )) self.wait() self.play(Write(approx_rhs)) self.wait(2) self.play( real_result.shift, 1.5*DOWN, real_result.set_fill, None, 1, ) self.change_student_modes(*["hooray"]*3) self.wait(2) self.change_student_modes( *["plain"]*3, added_anims = list(map(FadeOut, [ approx_at_point, approx_rhs, real_result ])), look_at_arg = approx_at_x ) def add_polynomial(self): polynomial = self.get_polynomial() const_terms = polynomial.get_parts_by_tex("c") self.play( Write(polynomial), self.teacher.change, "pondering" ) self.wait(2) self.play(*[ ApplyMethod( const.shift, MED_LARGE_BUFF*UP, run_time = 2, rate_func = squish_rate_func(there_and_back, a, a+0.7) ) for const, a in zip(const_terms, np.linspace(0, 0.3, len(const_terms))) ]) self.wait() self.const_terms = const_terms self.polynomial = polynomial def show_c0(self): c0 = self.polynomial.get_part_by_tex("c_0") c0.save_state() equation = TexMobject("P(0) = \\cos(0)") equation.to_corner(UP+RIGHT) new_polynomial = self.get_polynomial(c0 = "1") self.play(c0.shift, UP) self.play(Write(equation)) self.wait() self.play(Transform(self.polynomial, new_polynomial)) self.play(FadeOut(equation)) def show_c1(self): c1 = self.polynomial.get_part_by_tex("c_1") c1.save_state() equation = TexMobject( "\\frac{dP}{dx}(0) = \\frac{d(\\cos)}{dx}(0)" ) equation.to_corner(UP+RIGHT) new_polynomial = self.get_polynomial(c0 = "1", c1 = "0") self.play(c1.shift, UP) self.play(Write(equation)) self.wait() self.play(Transform(self.polynomial, new_polynomial)) self.wait() self.play(FadeOut(equation)) def show_c2(self): c2 = self.polynomial.get_part_by_tex("c_2") c2.save_state() equation = TexMobject( "\\frac{d^2 P}{dx^2}(0) = \\frac{d^2(\\cos)}{dx^2}(0)" ) equation.to_corner(UP+RIGHT) alt_c2_tex = "\\text{\\tiny $\\left(-\\frac{1}{2}\\right)$}" new_polynomial = self.get_polynomial( c0 = "1", c1 = "0", c2 = alt_c2_tex ) new_polynomial.get_part_by_tex(alt_c2_tex).shift(SMALL_BUFF*UP) self.play(c2.shift, UP) self.play(FadeIn(equation)) self.wait(2) self.play(Transform(self.polynomial, new_polynomial)) self.wait(2) self.play(FadeOut(equation)) ##### def get_polynomial(self, c0 = "c_0", c1 = "c_1", c2 = "c_2"): polynomial = TexMobject( "P(x) = ", c0, "+", c1, "x", "+", c2, "x^2" ) colors = ConstructQuadraticApproximation.CONFIG["colors"] for tex, color in zip([c0, c1, c2], colors): polynomial.set_color_by_tex(tex, color, substring = False) polynomial.next_to(self.teacher, UP, LARGE_BUFF) polynomial.to_edge(RIGHT) return polynomial class ReflectionOnQuadraticSupplement(ConstructQuadraticApproximation): def construct(self): self.setup_axes() self.add(self.get_graph(np.cos, color = self.colors[0])) quadratic_graph = self.get_quadratic_graph() self.add(quadratic_graph) self.wait() for c0 in 0, 2, 1: self.change_quadratic_graph( quadratic_graph, c0 = c0 ) self.wait(2) for c1 in 1, -1, 0: self.change_quadratic_graph( quadratic_graph, c1 = c1 ) self.wait(2) for c2 in -0.1, -1, -0.5: self.change_quadratic_graph( quadratic_graph, c2 = c2 ) self.wait(2) class SimilarityOfChangeBehavior(ConstructQuadraticApproximation): def construct(self): colors = [YELLOW, WHITE] max_x = np.pi/2 self.setup_axes() cosine_graph = self.get_graph(np.cos, color = self.colors[0]) quadratic_graph = self.get_quadratic_graph() graphs = VGroup(cosine_graph, quadratic_graph) dots = VGroup() for graph, color in zip(graphs, colors): dot = Dot(color = color) dot.move_to(self.input_to_graph_point(0, graph)) dot.graph = graph dots.add(dot) def update_dot(dot, alpha): x = interpolate(0, max_x, alpha) dot.move_to(self.input_to_graph_point(x, dot.graph)) dot_anims = [ UpdateFromAlphaFunc(dot, update_dot, run_time = 3) for dot in dots ] tangent_lines = VGroup(*[ self.get_tangent_line(0, graph, color) for graph, color in zip(graphs, colors) ]) tangent_line_movements = [ self.get_tangent_line_change_anim( line, max_x, graph, run_time = 5, ) for line, graph in zip(tangent_lines, graphs) ] self.add(cosine_graph, quadratic_graph) self.play(FadeIn(dots)) self.play(*dot_anims) self.play( FadeIn(tangent_lines), FadeOut(dots) ) self.play(*tangent_line_movements + dot_anims, run_time = 6) self.play(*list(map(FadeOut, [tangent_lines, dots]))) self.wait() class MoreTerms(TeacherStudentsScene): def construct(self): self.teacher_says( "More terms!", target_mode = "surprised", ) self.change_student_modes(*["hooray"]*3) self.wait(3) class CubicAndQuarticApproximations(ConstructQuadraticApproximation): CONFIG = { "colors": [BLUE, YELLOW, GREEN, RED, MAROON_B], } def construct(self): self.add_background() self.take_third_derivative_of_cubic() self.show_third_derivative_of_cosine() self.set_c3_to_zero() self.show_cubic_curves() self.add_quartic_term() self.show_fourth_derivative_of_cosine() self.take_fourth_derivative_of_quartic() self.solve_for_c4() self.show_quartic_approximation() def add_background(self): self.setup_axes() self.cosine_graph = self.get_graph( np.cos, color = self.colors[0] ) self.quadratic_graph = self.get_quadratic_graph() self.big_rect = Rectangle( height = FRAME_HEIGHT, width = FRAME_WIDTH, stroke_width = 0, fill_color = BLACK, fill_opacity = 0.5, ) self.add( self.cosine_graph, self.quadratic_graph, self.big_rect ) self.cosine_label = TexMobject("\\cos", "(0)", "=1") self.cosine_label.set_color_by_tex("cos", self.colors[0]) self.cosine_label.scale(0.75) self.cosine_label.to_corner(UP+LEFT) self.add(self.cosine_label) self.add(self.get_cosine_derivative()) self.add(self.get_cosine_second_derivative()) self.polynomial = TexMobject( "P(x)=", "1", "-\\frac{1}{2}", "x^2" ) self.polynomial.set_color_by_tex("1", self.colors[0]) self.polynomial.set_color_by_tex("-\\frac{1}{2}", self.colors[2]) self.polynomial.to_corner(UP+RIGHT) self.polynomial.quadratic_part = VGroup( *self.polynomial[1:] ) self.add(self.polynomial) def take_third_derivative_of_cubic(self): polynomial = self.polynomial plus_cubic_term = TexMobject("+\\,", "c_3", "x^3") plus_cubic_term.next_to(polynomial, RIGHT) plus_cubic_term.to_edge(RIGHT, buff = LARGE_BUFF) plus_cubic_term.set_color_by_tex("c_3", self.colors[3]) plus_cubic_copy = plus_cubic_term.copy() polynomial.generate_target() polynomial.target.next_to(plus_cubic_term, LEFT) self.play(FocusOn(polynomial)) self.play( MoveToTarget(polynomial), GrowFromCenter(plus_cubic_term) ) self.wait() brace = Brace(polynomial.quadratic_part, DOWN) third_derivative = TexMobject( "\\frac{d^3 P}{dx^3}(x) = ", "0" ) third_derivative.shift( brace.get_bottom() + MED_SMALL_BUFF*DOWN -\ third_derivative.get_part_by_tex("0").get_top() ) self.play(Write(third_derivative[0])) self.play(GrowFromCenter(brace)) self.play(ReplacementTransform( polynomial.quadratic_part.copy(), VGroup(third_derivative[1]) )) self.wait(2) self.play(plus_cubic_copy.next_to, third_derivative, RIGHT) derivative_term = self.take_derivatives_of_monomial( VGroup(*plus_cubic_copy[1:]) ) third_derivative.add(plus_cubic_copy[0], derivative_term) self.plus_cubic_term = plus_cubic_term self.polynomial_third_derivative = third_derivative self.polynomial_third_derivative_brace = brace def show_third_derivative_of_cosine(self): cosine_third_derivative = self.get_cosine_third_derivative() dot = Dot(fill_opacity = 0.5) dot.move_to(self.polynomial_third_derivative) self.play( dot.move_to, cosine_third_derivative, dot.set_fill, None, 0 ) self.play(ReplacementTransform( self.cosine_second_derivative.copy(), cosine_third_derivative )) self.wait(2) dot.set_fill(opacity = 0.5) self.play( dot.move_to, self.polynomial_third_derivative.get_right(), dot.set_fill, None, 0, ) self.wait() def set_c3_to_zero(self): c3s = VGroup( self.polynomial_third_derivative[-1][-1], self.plus_cubic_term.get_part_by_tex("c_3") ) zeros = VGroup(*[ TexMobject("0").move_to(c3) for c3 in c3s ]) zeros.set_color(self.colors[3]) zeros.shift(SMALL_BUFF*UP) zeros[0].shift(0.25*SMALL_BUFF*(UP+LEFT)) self.play(Transform( c3s, zeros, run_time = 2, lag_ratio = 0.5 )) self.wait(2) def show_cubic_curves(self): real_graph = self.quadratic_graph real_graph.save_state() graph = real_graph.copy() graph.save_state() alt_graphs = [ self.get_graph(func, color = real_graph.get_color()) for func in [ lambda x : x*(x-1)*(x+1), lambda x : 1 - 0.5*(x**2) + 0.2*(x**3) ] ] self.play(FadeIn(graph)) real_graph.set_stroke(width = 0) for alt_graph in alt_graphs: self.play(Transform(graph, alt_graph, run_time = 2)) self.wait() self.play(graph.restore, run_time = 2) real_graph.restore() self.play(FadeOut(graph)) def add_quartic_term(self): polynomial = self.polynomial plus_quartic_term = TexMobject("+\\,", "c_4", "x^4") plus_quartic_term.next_to(polynomial, RIGHT) plus_quartic_term.set_color_by_tex("c_4", self.colors[4]) self.play(*list(map(FadeOut, [ self.plus_cubic_term, self.polynomial_third_derivative, self.polynomial_third_derivative_brace, ]))) self.play(Write(plus_quartic_term)) self.wait() self.plus_quartic_term = plus_quartic_term def show_fourth_derivative_of_cosine(self): cosine_fourth_derivative = self.get_cosine_fourth_derivative() self.play(FocusOn(self.cosine_third_derivative)) self.play(ReplacementTransform( self.cosine_third_derivative.copy(), cosine_fourth_derivative )) self.wait(3) def take_fourth_derivative_of_quartic(self): quartic_term = VGroup(*self.plus_quartic_term.copy()[1:]) fourth_deriv_lhs = TexMobject("{d^4 P \\over dx^4}(x)", "=") fourth_deriv_lhs.next_to( self.polynomial, DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) alt_rhs = TexMobject("=", "24 \\cdot", "c_4") alt_rhs.next_to( fourth_deriv_lhs.get_part_by_tex("="), DOWN, buff = LARGE_BUFF, aligned_edge = LEFT ) alt_rhs.set_color_by_tex("c_4", self.colors[4]) self.play(Write(fourth_deriv_lhs)) self.play( quartic_term.next_to, fourth_deriv_lhs, RIGHT ) self.wait() fourth_deriv_rhs = self.take_derivatives_of_monomial(quartic_term) self.wait() self.play(Write(alt_rhs)) self.wait() self.fourth_deriv_lhs = fourth_deriv_lhs self.fourth_deriv_rhs = fourth_deriv_rhs self.fourth_deriv_alt_rhs = alt_rhs def solve_for_c4(self): c4s = VGroup( self.fourth_deriv_alt_rhs.get_part_by_tex("c_4"), self.fourth_deriv_rhs[-1], self.plus_quartic_term.get_part_by_tex("c_4") ) fraction = TexMobject("\\text{\\small $\\frac{1}{24}$}") fraction.set_color(self.colors[4]) fractions = VGroup(*[ fraction.copy().move_to(c4, LEFT) for c4 in c4s ]) fractions.shift(SMALL_BUFF*UP) x_to_4 = self.plus_quartic_term.get_part_by_tex("x^4") x_to_4.generate_target() x_to_4.target.shift(MED_SMALL_BUFF*RIGHT) self.play( Transform( c4s, fractions, run_time = 3, lag_ratio = 0.5, ), MoveToTarget(x_to_4, run_time = 2) ) self.wait(3) def show_quartic_approximation(self): real_graph = self.quadratic_graph graph = real_graph.copy() quartic_graph = self.get_graph( lambda x : 1 - (x**2)/2.0 + (x**4)/24.0, color = graph.get_color(), ) tex_mobs = VGroup(*[ self.polynomial, self.fourth_deriv_rhs, self.fourth_deriv_alt_rhs, self.cosine_label, self.cosine_derivative, self.cosine_second_derivative, self.cosine_third_derivative[1], ]) for tex_mob in tex_mobs: tex_mob.add_to_back(BackgroundRectangle(tex_mob)) self.play(FadeIn(graph)) real_graph.set_stroke(width = 0) self.play( Transform( graph, quartic_graph, run_time = 3, ), Animation(tex_mobs) ) self.wait(3) #### def take_derivatives_of_monomial(self, term, *added_anims): """ Must be a group of pure TexMobjects, last part must be of the form x^n """ n = int(term[-1].get_tex_string()[-1]) curr_term = term added_anims_iter = iter(added_anims) for k in range(n, 0, -1): exponent = curr_term[-1][-1] exponent_copy = exponent.copy() front_num = TexMobject("%d \\cdot"%k) front_num.move_to(curr_term[0][0], LEFT) new_monomial = TexMobject("x^%d"%(k-1)) new_monomial.replace(curr_term[-1]) Transform(curr_term[-1], new_monomial).update(1) curr_term.generate_target() curr_term.target.shift( (front_num.get_width()+SMALL_BUFF)*RIGHT ) curr_term[-1][-1].set_fill(opacity = 0) possibly_added_anims = [] try: possibly_added_anims.append(next(added_anims_iter)) except: pass self.play( ApplyMethod( exponent_copy.replace, front_num[0], path_arc = np.pi, ), Write( front_num[1], rate_func = squish_rate_func(smooth, 0.5, 1) ), MoveToTarget(curr_term), *possibly_added_anims, run_time = 2 ) self.remove(exponent_copy) self.add(front_num) curr_term = VGroup(front_num, *curr_term) self.wait() self.play(FadeOut(curr_term[-1])) return VGroup(*curr_term[:-1]) def get_cosine_third_derivative(self): if not hasattr(self, "cosine_second_derivative"): self.get_cosine_second_derivative() third_deriv = TexMobject( "{d^3(", "\\cos", ")", "\\over", "dx^3}", "(", "0", ")", ) third_deriv.set_color_by_tex("cos", self.colors[0]) third_deriv.set_color_by_tex("-\\cos", self.colors[3]) third_deriv.scale(0.75) third_deriv.add_background_rectangle() third_deriv.next_to( self.cosine_second_derivative, DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) rhs = TexMobject("=", "\\sin(0)", "=", "0") rhs.set_color_by_tex("sin", self.colors[3]) rhs.scale(0.8) rhs.next_to( third_deriv, RIGHT, align_using_submobjects = True ) rhs.add_background_rectangle() rhs.background_rectangle.scale_in_place(1.2) self.cosine_third_derivative = VGroup(third_deriv, rhs) return self.cosine_third_derivative def get_cosine_fourth_derivative(self): if not hasattr(self, "cosine_third_derivative"): self.get_cosine_third_derivative() fourth_deriv = TexMobject( "{d^4(", "\\cos", ")", "\\over", "dx^4}", "(", "0", ")", ) fourth_deriv.set_color_by_tex("cos", self.colors[0]) fourth_deriv.scale(0.75) fourth_deriv.add_background_rectangle() fourth_deriv.next_to( self.cosine_third_derivative, DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) rhs = TexMobject("=", "\\cos(0)", "=", "1") rhs.set_color_by_tex("cos", self.colors[4]) rhs.scale(0.8) rhs.next_to( fourth_deriv, RIGHT, align_using_submobjects = True ) rhs.add_background_rectangle() rhs.background_rectangle.scale_in_place(1.2) self.cosine_fourth_derivative = VGroup(fourth_deriv, rhs) return self.cosine_fourth_derivative class NoticeAFewThings(TeacherStudentsScene): def construct(self): self.teacher_says( "Notice a few things", target_mode = "hesitant" ) self.wait(3) class FactorialTerms(CubicAndQuarticApproximations): def construct(self): lhs_list = [ TexMobject( "{d%s"%s, "\\over", "dx%s}"%s, "(", "c_8", "x^8", ")=" ) for i in range(9) for s in ["^%d"%i if i > 1 else ""] ] for lhs in lhs_list: lhs.set_color_by_tex("c_8", YELLOW) lhs.next_to(ORIGIN, LEFT) lhs_list[0].set_fill(opacity = 0) added_anims = [ ReplacementTransform( start_lhs, target_lhs, rate_func = squish_rate_func(smooth, 0, 0.5) ) for start_lhs, target_lhs in zip(lhs_list, lhs_list[1:]) ] term = TexMobject("c_8", "x^8") term.next_to(lhs[-1], RIGHT) term.set_color_by_tex("c_8", YELLOW) self.add(term) self.wait() result = self.take_derivatives_of_monomial(term, *added_anims) factorial_term = VGroup(*result[:-1]) brace = Brace(factorial_term) eight_factorial = brace.get_text("$8!$") coefficient = result[-1] words = TextMobject( "Set", "$c_8$", "$ = \\frac{\\text{Desired derivative value}}{8!}" ) words.set_color_by_tex("c_8", YELLOW) words.shift(2*UP) self.play( GrowFromCenter(brace), Write(eight_factorial) ) self.play( ReplacementTransform( coefficient.copy(), words.get_part_by_tex("c_8") ), Write(words), ) self.wait(2) class HigherTermsDontMessUpLowerTerms(Scene): CONFIG = { "colors" : CubicAndQuarticApproximations.CONFIG["colors"][::2], } def construct(self): self.add_polynomial() self.show_second_derivative() def add_polynomial(self): c0_tex = "1" c2_tex = "\\text{\\small $\\left(-\\frac{1}{2}\\right)$}" c4_tex = "c_4" polynomial = TexMobject( "P(x) = ", c0_tex, "+", c2_tex, "x^2", "+", c4_tex, "x^4", ) polynomial.shift(2*LEFT + UP) c0, c2, c4 = [ polynomial.get_part_by_tex(tex) for tex in (c0_tex, c2_tex, c4_tex) ] for term, color in zip([c0, c2, c4], self.colors): term.set_color(color) arrows = VGroup(*[ Arrow( c4.get_top(), c.get_top(), path_arc = arc, color = c.get_color() ) for c, arc in [(c2, 0.9*np.pi), (c0, np.pi)] ]) no_affect_words = TextMobject( "Doesn't affect \\\\ previous terms" ) no_affect_words.next_to(arrows, RIGHT) no_affect_words.shift(MED_SMALL_BUFF*(UP+LEFT)) self.add(*polynomial[:-2]) self.wait() self.play(Write(VGroup(*polynomial[-2:]))) self.play( Write(no_affect_words), ShowCreation(arrows), run_time = 3 ) self.wait(2) self.polynomial = polynomial self.c0_tex = c0_tex self.c2_tex = c2_tex self.c4_tex = c4_tex def show_second_derivative(self): second_deriv = TexMobject( "{d^2 P \\over dx^2}(", "0", ")", "=", "2", self.c2_tex, "+", "3 \\cdot 4", self.c4_tex, "(", "0", ")", "^2" ) second_deriv.set_color_by_tex(self.c2_tex, self.colors[1]) second_deriv.set_color_by_tex(self.c4_tex, self.colors[2]) second_deriv.set_color_by_tex("0", YELLOW) second_deriv.next_to( self.polynomial, DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) higher_terms = VGroup(*second_deriv[-6:]) brace = Brace(higher_terms, DOWN) equals_zero = brace.get_text("=0") second_deriv.save_state() second_deriv.move_to(self.polynomial, LEFT) second_deriv.set_fill(opacity = 0) self.play(second_deriv.restore) self.wait() self.play(GrowFromCenter(brace)) self.wait() self.play(Write(equals_zero)) self.wait(3) class EachTermControlsOneDerivative(Scene): def construct(self): colors = CubicAndQuarticApproximations.CONFIG["colors"] polynomial = TexMobject( "P(x) = ", "c_0", "+", "c_1", "x", *it.chain(*[ ["+", "c_%d"%n, "x^%d"%n] for n in range(2, 5) ]) ) consts = polynomial.get_parts_by_tex("c") deriv_words = VGroup(*[ TextMobject("Controls \\\\ $%s(0)$"%tex) for tex in [ "P", "\\frac{dP}{dx}", ] + [ "\\frac{d^%d P}{dx^%d}"%(n, n) for n in range(2, 5) ] ]) deriv_words.arrange( RIGHT, buff = LARGE_BUFF, aligned_edge = UP ) deriv_words.set_width(FRAME_WIDTH - MED_LARGE_BUFF) deriv_words.to_edge(UP) for const, deriv, color in zip(consts, deriv_words, colors): for mob in const, deriv: mob.set_color(color) arrow = Arrow( const.get_top(), deriv.get_bottom(), # buff = SMALL_BUFF, color = color ) deriv.arrow = arrow self.add(polynomial) for deriv in deriv_words: self.play( ShowCreation(deriv.arrow), FadeIn(deriv) ) self.wait() class ApproximateNearNewPoint(CubicAndQuarticApproximations): CONFIG = { "target_approx_centers" : [np.pi/2, np.pi], } def construct(self): self.setup_axes() self.add_cosine_graph() self.shift_approximation_center() self.show_polynomials() def add_cosine_graph(self): self.cosine_graph = self.get_graph( np.cos, self.colors[0] ) self.add(self.cosine_graph) def shift_approximation_center(self): quartic_graph = self.get_quartic_approximation(0) dot = Dot(color = YELLOW) dot.move_to(self.coords_to_point(0, 1)) v_line = self.get_vertical_line_to_graph( self.target_approx_centers[-1], self.cosine_graph, line_class = DashedLine, color = YELLOW ) pi = self.x_axis_labels[1] pi.add_background_rectangle() self.play( ReplacementTransform( self.cosine_graph.copy(), quartic_graph, ), DrawBorderThenFill(dot, run_time = 1) ) for target, rt in zip(self.target_approx_centers, [3, 4, 4]): self.change_approximation_center( quartic_graph, dot, target, run_time = rt ) self.play( ShowCreation(v_line), Animation(pi) ) self.wait() def change_approximation_center(self, graph, dot, target, **kwargs): start = self.x_axis.point_to_number(dot.get_center()) def update_quartic(graph, alpha): new_a = interpolate(start, target, alpha) new_graph = self.get_quartic_approximation(new_a) Transform(graph, new_graph).update(1) return graph def update_dot(dot, alpha): new_x = interpolate(start, target, alpha) dot.move_to(self.input_to_graph_point(new_x, self.cosine_graph)) self.play( UpdateFromAlphaFunc(graph, update_quartic), UpdateFromAlphaFunc(dot, update_dot), **kwargs ) def show_polynomials(self): poly_around_pi = self.get_polynomial("(x-\\pi)", "\\pi") poly_around_pi.to_corner(UP+LEFT) randy = Randolph() randy.to_corner(DOWN+LEFT) self.play(FadeIn( poly_around_pi, run_time = 4, lag_ratio = 0.5 )) self.wait(2) self.play(FadeIn(randy)) self.play(randy.change, "confused", poly_around_pi) self.play(Blink(randy)) self.wait(2) self.play(randy.change_mode, "happy") self.wait(2) ### def get_polynomial(self, arg, center_tex): result = TexMobject( "P_{%s}(x)"%center_tex, "=", "c_0", *it.chain(*[ ["+", "c_%d"%d, "%s^%d"%(arg, d)] for d in range(1, 5) ]) ) for d, color in enumerate(self.colors): result.set_color_by_tex("c_%d"%d, color) result.scale(0.85) result.add_background_rectangle() return result def get_quartic_approximation(self, a): coefficients = [ derivative(np.cos, a, n) for n in range(5) ] func = lambda x : sum([ (c/math.factorial(n))*(x - a)**n for n, c in enumerate(coefficients) ]) return self.get_graph(func, color = GREEN) class OnAPhilosophicalLevel(TeacherStudentsScene): def construct(self): self.teacher_says( "And on a \\\\ philosophical level", run_time = 1 ) self.wait(3) class TranslationOfInformation(CubicAndQuarticApproximations): def construct(self): self.add_background() self.mention_information_exchange() self.show_derivative_pattern() self.show_polynomial() self.name_taylor_polynomial() self.draw_new_function_graph() self.write_general_function_derivative() self.replace_coefficients_in_generality() self.walk_through_terms() self.show_polynomial_around_a() def add_background(self): self.setup_axes() self.cosine_graph = self.get_graph( np.cos, color = self.colors[0] ) self.add(self.cosine_graph) def mention_information_exchange(self): deriv_info = TextMobject( "Derivative \\\\ information \\\\ at a point" ) deriv_info.next_to(ORIGIN, LEFT, LARGE_BUFF) deriv_info.to_edge(UP) output_info = TextMobject( "Output \\\\ information \\\\ near that point" ) output_info.next_to(ORIGIN, RIGHT, LARGE_BUFF) output_info.to_edge(UP) arrow = Arrow(deriv_info, output_info) center_v_line = self.get_vertical_line_to_graph( 0, self.cosine_graph, line_class = DashedLine, color = YELLOW ) outer_v_lines = VGroup(*[ center_v_line.copy().shift(vect) for vect in (LEFT, RIGHT) ]) outer_v_lines.set_color(GREEN) dot = Dot(color = YELLOW) dot.move_to(center_v_line.get_top()) dot.save_state() dot.move_to(deriv_info) dot.set_fill(opacity = 0) quadratic_graph = self.get_quadratic_graph() self.play(Write(deriv_info, run_time = 2)) self.play(dot.restore) self.play(ShowCreation(center_v_line)) self.wait() self.play(ShowCreation(arrow)) self.play(Write(output_info, run_time = 2)) self.play(ReplacementTransform( VGroup(center_v_line).copy(), outer_v_lines )) self.play(ReplacementTransform( self.cosine_graph.copy(), quadratic_graph ), Animation(dot)) for x in -1, 1, 0: start_x = self.x_axis.point_to_number(dot.get_center()) self.play(UpdateFromAlphaFunc( dot, lambda d, a : d.move_to(self.input_to_graph_point( interpolate(start_x, x, a), self.cosine_graph )), run_time = 2 )) self.wait() self.play(*list(map(FadeOut, [ deriv_info, arrow, output_info, outer_v_lines ]))) self.quadratic_graph = quadratic_graph self.v_line = center_v_line self.dot = dot def show_derivative_pattern(self): derivs_at_x, derivs_at_zero = [ VGroup(*[ TexMobject(tex, "(", arg, ")") for tex in [ "\\cos", "-\\sin", "-\\cos", "\\sin", "\\cos" ] ]) for arg in ("x", "0") ] arrows = VGroup(*[ Arrow( UP, ORIGIN, color = WHITE, buff = 0, tip_length = MED_SMALL_BUFF ) for d in derivs_at_x ]) group = VGroup(*it.chain(*list(zip( derivs_at_x, arrows )))) group.add(TexMobject("\\vdots")) group.arrange(DOWN, buff = SMALL_BUFF) group.set_height(FRAME_HEIGHT - MED_LARGE_BUFF) group.to_edge(LEFT) for dx, d0, color in zip(derivs_at_x, derivs_at_zero, self.colors): for d in dx, d0: d.set_color(color) d0.replace(dx) rhs_group = VGroup(*[ TexMobject("=", "%d"%d).scale(0.7).next_to(deriv, RIGHT) for deriv, d in zip(derivs_at_zero, [1, 0, -1, 0, 1]) ]) derivative_values = VGroup(*[ rhs[1] for rhs in rhs_group ]) for value, color in zip(derivative_values, self.colors): value.set_color(color) zeros = VGroup(*[ deriv.get_part_by_tex("0") for deriv in derivs_at_zero ]) self.play(FadeIn(derivs_at_x[0])) self.wait() for start_d, arrow, target_d in zip(group[::2], group[1::2], group[2::2]): self.play( ReplacementTransform( start_d.copy(), target_d ), ShowCreation(arrow) ) self.wait() self.wait() self.play(ReplacementTransform( derivs_at_x, derivs_at_zero )) self.wait() self.play(*list(map(Write, rhs_group))) self.wait() for rhs in rhs_group: self.play(Indicate(rhs[1]), color = WHITE) self.wait() self.play(*[ ReplacementTransform( zero.copy(), self.dot, run_time = 3, rate_func = squish_rate_func(smooth, a, a+0.4) ) for zero, a in zip(zeros, np.linspace(0, 0.6, len(zeros))) ]) self.wait() self.cosine_derivative_group = VGroup( derivs_at_zero, arrows, group[-1], rhs_group ) self.derivative_values = derivative_values def show_polynomial(self): derivative_values = self.derivative_values.copy() polynomial = self.get_polynomial("x", 1, 0, -1, 0, 1) polynomial.to_corner(UP+RIGHT) monomial = TexMobject("\\frac{1}{4!}", "x^4") monomial = VGroup(VGroup(monomial[0]), monomial[1]) monomial.next_to(polynomial, DOWN, LARGE_BUFF) self.play(*[ Transform( dv, pc, run_time = 2, path_arc = np.pi/2 ) for dv, pc, a in zip( derivative_values, polynomial.coefficients, np.linspace(0, 0.6, len(derivative_values)) ) ]) self.play( Write(polynomial, run_time = 5), Animation(derivative_values) ) self.remove(derivative_values) self.wait(2) to_fade = self.take_derivatives_of_monomial(monomial) self.play(FadeOut(to_fade)) self.wait() self.polynomial = polynomial def name_taylor_polynomial(self): brace = Brace( VGroup( self.polynomial.coefficients, self.polynomial.factorials ), DOWN ) name = brace.get_text("``Taylor polynomial''") name.shift(MED_SMALL_BUFF*RIGHT) quartic_graph = self.get_graph( lambda x : 1 - (x**2)/2.0 + (x**4)/24.0, color = GREEN, x_min = -3.2, x_max = 3.2, ) quartic_graph.set_color(self.colors[4]) self.play(GrowFromCenter(brace)) self.play(Write(name)) self.wait() self.play( Transform( self.quadratic_graph, quartic_graph, run_time = 2 ), Animation(self.dot) ) self.wait(2) self.taylor_name_group = VGroup(brace, name) def draw_new_function_graph(self): def func(x): return (np.sin(x**2 + x)+0.5)*np.exp(-x**2) graph = self.get_graph( func, color = self.colors[0] ) self.play(*list(map(FadeOut, [ self.cosine_derivative_group, self.cosine_graph, self.quadratic_graph, self.v_line, self.dot ]))) self.play(ShowCreation(graph)) self.graph = graph def write_general_function_derivative(self): derivs_at_x, derivs_at_zero, derivs_at_a = deriv_lists = [ VGroup(*[ TexMobject("\\text{$%s$}"%args[0], *args[1:]) for args in [ ("f", "(", arg, ")"), ("\\frac{df}{dx}", "(", arg, ")"), ("\\frac{d^2 f}{dx^2}", "(", arg, ")"), ("\\frac{d^3 f}{dx^3}", "(", arg, ")"), ("\\frac{d^4 f}{dx^4}", "(", arg, ")"), ] ]) for arg in ("x", "0", "a") ] derivs_at_x.arrange(DOWN, buff = MED_LARGE_BUFF) derivs_at_x.set_height(FRAME_HEIGHT - MED_LARGE_BUFF) derivs_at_x.to_edge(LEFT) zeros = VGroup(*[ deriv.get_part_by_tex("0") for deriv in derivs_at_zero ]) self.dot.move_to(self.input_to_graph_point(0, self.graph)) self.v_line.put_start_and_end_on( self.graph_origin, self.dot.get_center() ) for color, dx, d0, da in zip(self.colors, *deriv_lists): for d in dx, d0, da: d.set_color(color) d.add_background_rectangle() d0.replace(dx) da.replace(dx) self.play(FadeIn(derivs_at_x[0])) self.wait() for start, target in zip(derivs_at_x, derivs_at_x[1:]): self.play(ReplacementTransform( start.copy(), target )) self.wait() self.wait() self.play(ReplacementTransform( derivs_at_x, derivs_at_zero, )) self.play(ReplacementTransform( zeros.copy(), self.dot, run_time = 2, lag_ratio = 0.5 )) self.play(ShowCreation(self.v_line)) self.wait() self.derivs_at_zero = derivs_at_zero self.derivs_at_a = derivs_at_a def replace_coefficients_in_generality(self): new_polynomial = self.get_polynomial("x", *[ tex_mob.get_tex_string() for tex_mob in self.derivs_at_zero[:-1] ]) new_polynomial.to_corner(UP+RIGHT) polynomial_fourth_term = VGroup( *self.polynomial[-7:-1] ) self.polynomial.remove(*polynomial_fourth_term) self.play( ReplacementTransform( self.polynomial, new_polynomial, run_time = 2, lag_ratio = 0.5 ), FadeOut(polynomial_fourth_term), FadeOut(self.taylor_name_group), ) self.polynomial = new_polynomial self.wait(3) def walk_through_terms(self): func = self.graph.underlying_function approx_graphs = [ self.get_graph( taylor_approximation(func, n), color = WHITE ) for n in range(7) ] for graph, color in zip(approx_graphs, self.colors): graph.set_color(color) left_mob = self.polynomial.coefficients[0] right_mobs = list(self.polynomial.factorials) right_mobs.append(self.polynomial[-1]) braces = [ Brace( VGroup(left_mob, *right_mobs[:n]), DOWN ) for n in range(len(approx_graphs)) ] brace = braces[0] brace.stretch_to_fit_width(MED_LARGE_BUFF) approx_graph = approx_graphs[0] self.polynomial.add_background_rectangle() self.play(GrowFromCenter(brace)) self.play(ShowCreation(approx_graph)) self.wait() for new_brace, new_graph in zip(braces[1:], approx_graphs[1:]): self.play(Transform(brace, new_brace)) self.play( Transform(approx_graph, new_graph, run_time = 2), Animation(self.polynomial), Animation(self.dot), ) self.wait() self.play(FadeOut(brace)) self.approx_graph = approx_graph self.approx_order = len(approx_graphs) - 1 def show_polynomial_around_a(self): new_polynomial = self.get_polynomial("(x-a)", *[ tex_mob.get_tex_string() for tex_mob in self.derivs_at_a[:-2] ]) new_polynomial.to_corner(UP+RIGHT) new_polynomial.add_background_rectangle() polynomial_third_term = VGroup( *self.polynomial[1][-7:-1] ) self.polynomial[1].remove(*polynomial_third_term) group = VGroup(self.approx_graph, self.dot, self.v_line) def get_update_function(target_x): def update(group, alpha): graph, dot, line = group start_x = self.x_axis.point_to_number(dot.get_center()) x = interpolate(start_x, target_x, alpha) graph_point = self.input_to_graph_point(x, self.graph) dot.move_to(graph_point) line.put_start_and_end_on( self.coords_to_point(x, 0), graph_point, ) new_approx_graph = self.get_graph( taylor_approximation( self.graph.underlying_function, self.approx_order, center_point = x ), color = graph.get_color() ) Transform(graph, new_approx_graph).update(1) return VGroup(graph, dot, line) return update self.play( UpdateFromAlphaFunc( group, get_update_function(1), run_time = 2 ), Animation(self.polynomial), Animation(polynomial_third_term) ) self.wait() self.play(Transform( self.derivs_at_zero, self.derivs_at_a )) self.play( Transform(self.polynomial, new_polynomial), FadeOut(polynomial_third_term) ) self.wait() for x in -1, np.pi/6: self.play( UpdateFromAlphaFunc( group, get_update_function(x), ), Animation(self.polynomial), run_time = 4, ) self.wait() ##### def get_polynomial(self, arg, *coefficients): result = TexMobject( "P(x) = ", str(coefficients[0]), *list(it.chain(*[ ["+", str(c), "{%s"%arg, "^%d"%n, "\\over", "%d!}"%n] for n, c in zip(it.count(1), coefficients[1:]) ])) + [ "+ \\cdots" ] ) result.scale(0.8) coefs = VGroup(result[1], *result[3:-1:6]) for coef, color in zip(coefs, self.colors): coef.set_color(color) result.coefficients = coefs result.factorials = VGroup(*result[7::6]) return result class ThisIsAStandardFormula(TeacherStudentsScene): def construct(self): self.teacher_says( "You will see this \\\\ in your texts", run_time = 1 ) self.change_student_modes( *["sad"]*3, look_at_arg = FRAME_Y_RADIUS*UP ) self.wait(2) class ExpPolynomial(TranslationOfInformation, ExampleApproximationWithExp): CONFIG = { "x_tick_frequency" : 1, "x_leftmost_tick" : -3, "graph_origin" : 2*(DOWN+LEFT), "y_axis_label" : "", } def construct(self): self.setup_axes() self.add_graph() self.show_derivatives() self.show_polynomial() self.walk_through_terms() def add_graph(self): graph = self.get_graph(np.exp) e_to_x = self.get_graph_label(graph, "e^x") self.play( ShowCreation(graph), Write( e_to_x, rate_func = squish_rate_func(smooth, 0.5, 1) ), run_time = 2 ) self.wait() self.graph = graph self.e_to_x = e_to_x def show_derivatives(self): self.e_to_x.generate_target() derivs_at_x, derivs_at_zero = [ VGroup(*[ TexMobject("e^%s"%s).set_color(c) for c in self.colors ]) for s in ("x", "0") ] derivs_at_x.submobjects[0] = self.e_to_x.target arrows = VGroup(*[ Arrow( UP, ORIGIN, color = WHITE, buff = SMALL_BUFF, tip_length = 0.2, ) for d in derivs_at_x ]) group = VGroup(*it.chain(*list(zip( derivs_at_x, arrows )))) group.add(TexMobject("\\vdots")) group.arrange(DOWN, buff = 2*SMALL_BUFF) group.set_height(FRAME_HEIGHT - MED_LARGE_BUFF) group.to_edge(LEFT) for dx, d0 in zip(derivs_at_x, derivs_at_zero): for d in dx, d0: d.add_background_rectangle() d0.replace(dx) rhs_group = VGroup(*[ TexMobject("=", "1").scale(0.7).next_to(deriv, RIGHT) for deriv in derivs_at_zero ]) derivative_values = VGroup(*[ rhs[1] for rhs in rhs_group ]) for value, color in zip(derivative_values, self.colors): value.set_color(color) for arrow in arrows: d_dx = TexMobject("d/dx") d_dx.scale(0.5) d_dx.next_to(arrow, RIGHT, SMALL_BUFF) d_dx.shift(SMALL_BUFF*UP) arrow.add(d_dx) self.play(MoveToTarget(self.e_to_x)) derivs_at_x.submobjects[0] = self.e_to_x for start_d, arrow, target_d in zip(group[::2], group[1::2], group[2::2]): self.play( ReplacementTransform( start_d.copy(), target_d ), Write(arrow, run_time = 1) ) self.wait() self.wait() self.play(ReplacementTransform( derivs_at_x, derivs_at_zero )) self.wait() self.play(*list(map(Write, rhs_group))) self.derivative_values = derivative_values def show_polynomial(self): derivative_values = self.derivative_values.copy() polynomial = self.get_polynomial("x", 1, 1, 1, 1, 1) polynomial.to_corner(UP+RIGHT) ##These are to make the walk_through_terms method work self.polynomial = polynomial.copy() self.dot = Dot(fill_opacity = 0) ### polynomial.add_background_rectangle() self.play(*[ Transform( dv, pc, run_time = 2, path_arc = np.pi/2 ) for dv, pc in zip( derivative_values, polynomial.coefficients, ) ]) self.play( Write(polynomial, run_time = 4), Animation(derivative_values) ) #### def setup_axes(self): GraphScene.setup_axes(self) class ShowSecondTerm(TeacherStudentsScene): def construct(self): colors = CubicAndQuarticApproximations.CONFIG["colors"] polynomial = TexMobject( "f(a)", "+", "\\frac{df}{dx}(a)", "(x - a)", "+", "\\frac{d^2 f}{dx^2}(a)", "(x - a)^2" ) for tex, color in zip(["f(a)", "df", "d^2 f"], colors): polynomial.set_color_by_tex(tex, color) polynomial.next_to(self.teacher, UP+LEFT) polynomial.shift(MED_LARGE_BUFF*UP) second_term = VGroup(*polynomial[-2:]) box = Rectangle(color = colors[2]) box.replace(second_term, stretch = True) box.stretch_in_place(1.1, 0) box.stretch_in_place(1.2, 1) words = TextMobject("Geometric view") words.next_to(box, UP) self.teacher_says( "Now for \\\\ something fun!", target_mode = "hooray" ) self.wait(2) self.play( RemovePiCreatureBubble( self.teacher, target_mode = "raise_right_hand" ), Write(polynomial) ) self.play( ShowCreation(box), FadeIn(words), ) self.change_student_modes(*["pondering"]*3) self.wait(3) class SecondTermIntuition(AreaIsDerivative): CONFIG = { "func" : lambda x : x*(x-1)*(x-2) + 2, "num_rects" : 300, "t_max" : 2.3, "x_max" : 4, "x_labeled_nums" : None, "x_axis_label" : "", "y_labeled_nums" : None, "y_axis_label" : "", "y_min" : -1, "y_max" : 5, "y_tick_frequency" : 1, "variable_point_label" : "x", "area_opacity" : 1, "default_riemann_start_color" : BLUE_E, "dx" : 0.15, "skip_reconfiguration" : False, } def setup(self): GraphScene.setup(self) ReconfigurableScene.setup(self) self.foreground_mobjects = [] def construct(self): self.setup_axes() self.introduce_area() self.write_derivative() self.nudge_end_point() self.point_out_triangle() self.relabel_start_and_end() self.compute_triangle_area() self.walk_through_taylor_terms() def introduce_area(self): graph = self.v_graph = self.get_graph( self.func, color = WHITE, ) self.foreground_mobjects.append(graph) area = self.area = self.get_area(0, self.t_max) func_name = TexMobject("f_{\\text{area}}(x)") func_name.move_to(self.coords_to_point(0.6, 1)) self.foreground_mobjects.append(func_name) self.add(graph, area, func_name) self.add_T_label(self.t_max) if not self.skip_animations: for target in 1.6, 2.7, self.t_max: self.change_area_bounds( new_t_max = target, run_time = 3, ) self.__name__ = func_name def write_derivative(self): deriv = TexMobject("\\frac{df_{\\text{area}}}{dx}(x)") deriv.next_to( self.input_to_graph_point(2.7, self.v_graph), RIGHT ) deriv.shift_onto_screen() self.play(ApplyWave(self.v_graph, direction = UP)) self.play(Write(deriv, run_time = 2)) self.wait() self.deriv_label = deriv def nudge_end_point(self): dark_area = self.area.copy() dark_area.set_fill(BLACK, opacity = 0.5) curr_x = self.x_axis.point_to_number(self.area.get_right()) new_x = curr_x + self.dx rect = Rectangle( stroke_width = 0, fill_color = YELLOW, fill_opacity = 0.75 ) rect.replace( VGroup( VectorizedPoint(self.coords_to_point(new_x, 0)), self.right_v_line, ), stretch = True ) dx_brace = Brace(rect, DOWN, buff = 0) dx_label = dx_brace.get_text("$dx$", buff = SMALL_BUFF) dx_label_group = VGroup(dx_label, dx_brace) height_brace = Brace(rect, LEFT, buff = SMALL_BUFF) self.change_area_bounds(new_t_max = new_x) self.play( FadeIn(dark_area), *list(map(Animation, self.foreground_mobjects)) ) self.play( FadeOut(self.T_label_group), FadeIn(dx_label_group), FadeIn(rect), FadeIn(height_brace) ) self.wait() if not self.skip_reconfiguration: self.transition_to_alt_config( dx = self.dx/10.0, run_time = 3, ) self.play(FadeOut(height_brace)) self.dx_label_group = dx_label_group self.rect = rect self.dark_area = dark_area def point_out_triangle(self): triangle = Polygon(LEFT, ORIGIN, UP) triangle.set_stroke(width = 0) triangle.set_fill(MAROON_B, opacity = 1) triangle.replace( Line( self.rect.get_corner(UP+LEFT), self.right_v_line.get_top() ), stretch = True ) circle = Circle(color = RED) circle.set_height(triangle.get_height()) circle.replace(triangle, dim_to_match = 1) circle.scale_in_place(1.3) self.play(DrawBorderThenFill(triangle)) self.play(ShowCreation(circle)) self.play(FadeOut(circle)) self.triangle = triangle def relabel_start_and_end(self): dx_label, dx_brace = self.dx_label_group x_minus_a = TexMobject("(x-a)") x_minus_a.scale(0.7) x_minus_a.move_to(dx_label) labels = VGroup() arrows = VGroup() for vect, tex in (LEFT, "a"), (RIGHT, "x"): point = self.rect.get_corner(DOWN+vect) label = TexMobject(tex) label.next_to(point, DOWN+vect) label.shift(LARGE_BUFF*vect) arrow = Arrow( label.get_corner(UP-vect), point, buff = SMALL_BUFF, tip_length = 0.2, color = WHITE ) labels.add(label) arrows.add(arrow) for label, arrow in zip(labels, arrows): self.play( Write(label), ShowCreation(arrow) ) self.wait() self.wait() self.play(ReplacementTransform( dx_label, x_minus_a )) self.wait() self.x_minus_a = x_minus_a def compute_triangle_area(self): triangle = self.triangle.copy() tex_scale_factor = 0.7 base_line = Line(*[ triangle.get_corner(DOWN+vect) for vect in (LEFT, RIGHT) ]) base_line.set_color(RED) base_label = TextMobject("Base = ", "$(x-a)$") base_label.scale(tex_scale_factor) base_label.next_to(base_line, DOWN+RIGHT, MED_LARGE_BUFF) base_label.shift(SMALL_BUFF*UP) base_term = base_label[1].copy() base_arrow = Arrow( base_label.get_left(), base_line.get_center(), buff = SMALL_BUFF, color = base_line.get_color(), tip_length = 0.2 ) height_brace = Brace(triangle, RIGHT, buff = SMALL_BUFF) height_labels = [ TexMobject("\\text{Height} = ", s, "(x-a)") for s in [ "(\\text{Slope})", "\\frac{d^2 f_{\\text{area}}}{dx^2}(a)" ] ] for label in height_labels: label.scale(tex_scale_factor) label.next_to(height_brace, RIGHT) height_term = VGroup(*height_labels[1][1:]).copy() self.play( FadeIn(base_label), ShowCreation(base_arrow), ShowCreation(base_line) ) self.wait(2) self.play( GrowFromCenter(height_brace), Write(height_labels[0]) ) self.wait(2) self.play(ReplacementTransform(*height_labels)) self.wait(2) #Show area formula equals_half = TexMobject("=\\frac{1}{2}") equals_half.scale(tex_scale_factor) group = VGroup(triangle, equals_half, height_term, base_term) group.generate_target() group.target.arrange(RIGHT, buff = SMALL_BUFF) group.target[-1].next_to( group.target[-2], RIGHT, buff = SMALL_BUFF, align_using_submobjects = True ) group.target[1].shift(0.02*DOWN) group.target.to_corner(UP+RIGHT) exp_2 = TexMobject("2").scale(0.8*tex_scale_factor) exp_2.next_to( group.target[-2], UP+RIGHT, buff = 0, align_using_submobjects = True ) equals_half.scale(0.1) equals_half.move_to(triangle) equals_half.set_fill(opacity = 0) self.play( FadeOut(self.deriv_label), MoveToTarget(group, run_time = 2) ) self.wait(2) self.play(Transform( group[-1], exp_2 )) self.wait(2) def walk_through_taylor_terms(self): mini_area, mini_rect, mini_triangle = [ mob.copy() for mob in (self.dark_area, self.rect, self.triangle) ] mini_area.set_fill(BLUE_E, opacity = 1) mini_area.set_height(1) mini_rect.set_height(1) mini_triangle.set_height(0.5) geometric_taylor = VGroup( TexMobject("f(x) \\approx "), mini_area, TexMobject("+"), mini_rect, TexMobject("+"), mini_triangle, ) geometric_taylor.arrange( RIGHT, buff = MED_SMALL_BUFF ) geometric_taylor.to_corner(UP+LEFT) analytic_taylor = TexMobject( "f(x) \\approx", "f(a)", "+", "\\frac{df}{dx}(a)(x-a)", "+", "\\frac{1}{2}\\frac{d^2 f}{dx^2}(a)(x-a)^2" ) analytic_taylor.set_color_by_tex("f(a)", BLUE) analytic_taylor.set_color_by_tex("df", YELLOW) analytic_taylor.set_color_by_tex("d^2 f", MAROON_B) analytic_taylor.scale(0.7) analytic_taylor.next_to( geometric_taylor, DOWN, aligned_edge = LEFT ) for part in analytic_taylor: part.add_to_back(BackgroundRectangle(part)) new_func_name = TexMobject("f_{\\text{area}}(a)") new_func_name.replace(self.__name__) self.play(FadeIn( geometric_taylor, run_time = 2, lag_ratio = 0.5 )) self.wait() self.play( FadeIn(VGroup(*analytic_taylor[:3])), self.dark_area.set_fill, BLUE_E, 1, Transform(self.__name__, new_func_name) ) self.wait() self.play( self.rect.scale_in_place, 0.5, rate_func = there_and_back ) self.play(FadeIn(VGroup(*analytic_taylor[3:5]))) self.wait(2) self.play( self.triangle.scale_in_place, 0.5, rate_func = there_and_back ) self.play(FadeIn(VGroup(*analytic_taylor[5:]))) self.wait(3) class EachTermHasMeaning(TeacherStudentsScene): def construct(self): self.get_pi_creatures().scale_in_place(0.8).shift(UP) self.teacher_says( "Each term \\\\ has meaning!", target_mode = "hooray", bubble_kwargs = {"height" : 3, "width" : 4} ) self.change_student_modes( *["thinking"]*3, look_at_arg = 4*UP ) self.wait(3) class AskAboutInfiniteSum(TeacherStudentsScene): def construct(self): self.ask_question() self.name_taylor_series() self.be_careful() def ask_question(self): big_rect = Rectangle( width = FRAME_WIDTH, height = FRAME_HEIGHT, stroke_width = 0, fill_color = BLACK, fill_opacity = 0.7, ) randy = self.get_students()[1] series = TexMobject( "\\cos(x)", "\\approx", "1 - \\frac{x^2}{2!} + \\frac{x^4}{4!}", " - \\frac{x^6}{6!}", "+\\cdots" ) series.next_to(randy, UP, 2) series.shift_onto_screen() rhs = VGroup(*series[2:]) arrow = Arrow(rhs.get_left(), rhs.get_right()) arrow.next_to(rhs, UP) words = TextMobject("Add infinitely many") words.next_to(arrow, UP) self.teacher_says( "We could call \\\\ it an end here" ) self.change_student_modes(*["erm"]*3) self.wait(3) self.play( RemovePiCreatureBubble(self.teacher), self.get_students()[0].change_mode, "plain", self.get_students()[2].change_mode, "plain", FadeIn(big_rect), randy.change_mode, "pondering" ) crowd = VGroup(*self.get_pi_creatures()) crowd.remove(randy) self.crowd_copy = crowd.copy() self.remove(crowd) self.add(self.crowd_copy, big_rect, randy) self.play(Write(series)) self.play( ShowCreation(arrow), Write(words) ) self.wait(3) self.arrow = arrow self.words = words self.series = series self.randy = randy def name_taylor_series(self): series_def = TextMobject( "Infinite sum $\\Leftrightarrow$ series" ) taylor_series = TextMobject("Taylor series") for mob in series_def, taylor_series: mob.move_to(self.words) brace = Brace(self.series.get_part_by_tex("4!"), DOWN) taylor_polynomial = brace.get_text("Taylor polynomial") self.play(ReplacementTransform( self.words, series_def )) self.wait() self.play( GrowFromCenter(brace), Write(taylor_polynomial) ) self.wait(2) self.play( series_def.scale, 0.7, series_def.to_corner, UP+RIGHT, FadeIn(taylor_series) ) self.play(self.randy.change, "thinking", taylor_series) self.wait() def be_careful(self): self.play(FadeIn(self.teacher)) self.remove(self.crowd_copy[0]) self.teacher_says( "Be careful", bubble_kwargs = { "width" : 3, "height" : 2 }, added_anims = [self.randy.change, "hesitant"] ) self.wait(2) self.play(self.randy.change, "confused", self.series) self.wait(3) class ConvergenceExample(Scene): def construct(self): max_shown_power = 6 max_computed_power = 13 series = TexMobject(*list(it.chain(*[ ["\\frac{1}{%d}"%(3**n), "+"] for n in range(1, max_shown_power) ])) + ["\\cdots"]) series_nums = [3**(-n) for n in range(1, max_computed_power)] partial_sums = np.cumsum(series_nums) braces = self.get_partial_sum_braces(series, partial_sums) convergence_words = TextMobject("``Converges'' to $\\frac{1}{2}$") convergence_words.next_to(series, UP, LARGE_BUFF) convergence_words.set_color(YELLOW) rhs = TexMobject("= \\frac{1}{2}") rhs.next_to(series, RIGHT) rhs.set_color(BLUE) brace = braces[0] self.add(series, brace) for i, new_brace in enumerate(braces[1:]): self.play(Transform(brace, new_brace)) if i == 4: self.play(FadeIn(convergence_words)) else: self.wait() self.play( FadeOut(brace), Write(rhs) ) self.wait(2) def get_partial_sum_braces(self, series, partial_sums): braces = [ Brace(VGroup(*series[:n])) for n in range(1, len(series)-1, 2) ] last_brace = braces[-1] braces += [ braces[-1].copy().stretch_in_place( 1 + 0.1 + 0.02*(n+1), dim = 0, ).move_to(last_brace, LEFT) for n in range(len(partial_sums) - len(braces)) ] for brace, partial_sum in zip(braces, partial_sums): number = brace.get_text("%.7f"%partial_sum) number.set_color(YELLOW) brace.add(number) return braces class ExpConvergenceExample(ConvergenceExample): def construct(self): e_to_x, series_with_x = x_group = self.get_series("x") x_group.to_edge(UP) e_to_1, series_with_1 = one_group = self.get_series("1") terms = [1./math.factorial(n) for n in range(11)] partial_sums = np.cumsum(terms) braces = self.get_partial_sum_braces(series_with_1, partial_sums) brace = braces[1] for lhs, s in (e_to_x, "x"), (e_to_1, "1"): new_lhs = TexMobject("e^%s"%s, "=") new_lhs.move_to(lhs, RIGHT) lhs.target = new_lhs self.add(x_group) self.wait() self.play(ReplacementTransform(x_group.copy(), one_group)) self.play(FadeIn(brace)) self.wait() for new_brace in braces[2:]: self.play(Transform(brace, new_brace)) self.wait() self.wait() self.play(MoveToTarget(e_to_1)) self.wait(2) def get_series(self, arg, n_terms = 5): series = TexMobject("1", "+", *list(it.chain(*[ ["\\frac{%s^%d}{%d!}"%(arg, n, n), "+"] for n in range(1, n_terms+1) ])) + ["\\cdots"]) colors = list(CubicAndQuarticApproximations.CONFIG["colors"]) colors += [BLUE_C] for term, color in zip(series[::2], colors): term.set_color(color) lhs = TexMobject("e^%s"%arg, "\\rightarrow") lhs.arrange(RIGHT, buff = SMALL_BUFF) group = VGroup(lhs, series) group.arrange(RIGHT, buff = SMALL_BUFF) return group class ExpGraphConvergence(ExpPolynomial, ExpConvergenceExample): CONFIG = { "example_input" : 2, "graph_origin" : 3*DOWN + LEFT, "n_iterations" : 8, "y_max" : 20, "num_graph_anchor_points" : 50, "func" : np.exp, } def construct(self): self.setup_axes() self.add_series() approx_graphs = self.get_approx_graphs() graph = self.get_graph(self.func, color = self.colors[0]) v_line = self.get_vertical_line_to_graph( self.example_input, graph, line_class = DashedLine, color = YELLOW ) dot = Dot(color = YELLOW) dot.to_corner(UP+LEFT) equals = TexMobject("=") equals.replace(self.arrow) equals.scale_in_place(0.8) brace = self.braces[1] approx_graph = approx_graphs[1] x = self.example_input self.add(graph, self.series) self.wait() self.play(dot.move_to, self.coords_to_point(x, 0)) self.play( dot.move_to, self.input_to_graph_point(x, graph), ShowCreation(v_line) ) self.wait() self.play( GrowFromCenter(brace), ShowCreation(approx_graph) ) self.wait() for new_brace, new_graph in zip(self.braces[2:], approx_graphs[2:]): self.play( Transform(brace, new_brace), Transform(approx_graph, new_graph), Animation(self.series), ) self.wait() self.play(FocusOn(equals)) self.play(Transform(self.arrow, equals)) self.wait(2) def add_series(self): series_group = self.get_series("x") e_to_x, series = series_group series_group.scale(0.8) series_group.to_corner(UP+LEFT) braces = self.get_partial_sum_braces( series, np.zeros(self.n_iterations) ) for brace in braces: brace.remove(brace[-1]) series.add_background_rectangle() self.add(series_group) self.braces = braces self.series = series self.arrow = e_to_x[1] def get_approx_graphs(self): def get_nth_approximation(n): return lambda x : sum([ float(x**k)/math.factorial(k) for k in range(n+1) ]) approx_graphs = [ self.get_graph(get_nth_approximation(n)) for n in range(self.n_iterations) ] colors = it.chain(self.colors, it.repeat(WHITE)) for approx_graph, color in zip(approx_graphs, colors): approx_graph.set_color(color) dot = Dot(color = WHITE) dot.move_to(self.input_to_graph_point( self.example_input, approx_graph )) approx_graph.add(dot) return approx_graphs class SecondExpGraphConvergence(ExpGraphConvergence): CONFIG = { "example_input" : 3, "n_iterations" : 12, } class BoundedRadiusOfConvergence(CubicAndQuarticApproximations): CONFIG = { "num_graph_anchor_points" : 100, } def construct(self): self.setup_axes() func = lambda x : (np.sin(x**2 + x)+0.5)*(np.log(x+1.01)+1)*np.exp(-x) graph = self.get_graph( func, color = self.colors[0], x_min = -0.99, x_max = self.x_max, ) v_line = self.get_vertical_line_to_graph( 0, graph, line_class = DashedLine, color = YELLOW ) dot = Dot(color = YELLOW).move_to(v_line.get_top()) two_graph = self.get_graph(lambda x : 2) outer_v_lines = VGroup(*[ DashedLine( self.coords_to_point(x, -2), self.coords_to_point(x, 2), color = WHITE ) for x in (-1, 1) ]) colors = list(self.colors) + [GREEN, MAROON_B, PINK] approx_graphs = [ self.get_graph( taylor_approximation(func, n), color = color ) for n, color in enumerate(colors) ] approx_graph = approx_graphs[1] self.add(graph, v_line, dot) self.play(ReplacementTransform( VGroup(v_line.copy()), outer_v_lines )) self.play( ShowCreation(approx_graph), Animation(dot) ) self.wait() for new_graph in approx_graphs[2:]: self.play( Transform(approx_graph, new_graph), Animation(dot) ) self.wait() self.wait() class RadiusOfConvergenceForLnX(ExpGraphConvergence): CONFIG = { "x_min" : -1, "x_leftmost_tick" : None, "x_max" : 5, "y_min" : -2, "y_max" : 3, "graph_origin" : DOWN+2*LEFT, "func" : np.log, "num_graph_anchor_points" : 100, "initial_n_iterations" : 7, "n_iterations" : 11, "convergent_example" : 1.5, "divergent_example" : 2.5, } def construct(self): self.add_graph() self.add_series() self.show_bounds() self.show_converging_point() self.show_diverging_point() self.write_divergence() self.write_radius_of_convergence() def add_graph(self): self.setup_axes() self.graph = self.get_graph( self.func, x_min = 0.01 ) self.add(self.graph) def add_series(self): series = TexMobject( "\\ln(x) \\rightarrow", "(x-1)", "-", "\\frac{(x-1)^2}{2}", "+", "\\frac{(x-1)^3}{3}", "-", "\\frac{(x-1)^4}{4}", "+", "\\cdots" ) lhs = VGroup(*series[1:]) series.add_background_rectangle() series.scale(0.8) series.to_corner(UP+LEFT) for n in range(4): lhs[2*n].set_color(self.colors[n+1]) self.braces = self.get_partial_sum_braces( lhs, np.zeros(self.n_iterations) ) for brace in self.braces: brace.remove(brace[-1]) self.play(FadeIn( series, run_time = 3, lag_ratio = 0.5 )) self.wait() self.series = series self.foreground_mobjects = [series] def show_bounds(self): dot = Dot(fill_opacity = 0) dot.move_to(self.series) v_lines = [ DashedLine(*[ self.coords_to_point(x, y) for y in (-2, 2) ]) for x in (0, 1, 2) ] outer_v_lines = VGroup(*v_lines[::2]) center_v_line = VGroup(v_lines[1]) input_v_line = Line(*[ self.coords_to_point(self.convergent_example, y) for y in (-4, 3) ]) input_v_line.set_stroke(WHITE, width = 2) self.play( dot.move_to, self.coords_to_point(1, 0), dot.set_fill, YELLOW, 1, ) self.wait() self.play( GrowFromCenter(center_v_line), Animation(dot) ) self.play(Transform(center_v_line, outer_v_lines)) self.foreground_mobjects.append(dot) def show_converging_point(self): approx_graphs = [ self.get_graph( taylor_approximation(self.func, n, 1), color = WHITE ) for n in range(1, self.n_iterations+1) ] colors = it.chain( self.colors[1:], [GREEN, MAROON_B], it.repeat(PINK) ) for graph, color in zip(approx_graphs, colors): graph.set_color(color) for graph in approx_graphs: dot = Dot(color = WHITE) dot.move_to(self.input_to_graph_point( self.convergent_example, graph )) graph.dot = dot graph.add(dot) approx_graph = approx_graphs[0].deepcopy() approx_dot = approx_graph.dot brace = self.braces[0].copy() self.play(*it.chain( list(map(FadeIn, [approx_graph, brace])), list(map(Animation, self.foreground_mobjects)) )) self.wait() new_graphs = approx_graphs[1:self.initial_n_iterations] for new_graph, new_brace in zip(new_graphs, self.braces[1:]): self.play( Transform(approx_graph, new_graph), Transform(brace, new_brace), *list(map(Animation, self.foreground_mobjects)) ) self.wait() approx_graph.remove(approx_dot) self.play( approx_dot.move_to, self.coords_to_point(self.divergent_example, 0), *it.chain( list(map(FadeOut, [approx_graph, brace])), list(map(Animation, self.foreground_mobjects)) ) ) self.wait() self.approx_graphs = approx_graphs self.approx_dot = approx_dot def show_diverging_point(self): for graph in self.approx_graphs: graph.dot.move_to(self.input_to_graph_point( self.divergent_example, graph )) approx_graph = self.approx_graphs[0].deepcopy() brace = self.braces[0].copy() self.play( ReplacementTransform( self.approx_dot, approx_graph.dot ), FadeIn(approx_graph[0]), FadeIn(brace), *list(map(Animation, self.foreground_mobjects)) ) new_graphs = self.approx_graphs[1:self.initial_n_iterations] for new_graph, new_brace in zip(self.approx_graphs[1:], self.braces[1:]): self.play( Transform(approx_graph, new_graph), Transform(brace, new_brace), *list(map(Animation, self.foreground_mobjects)) ) self.wait() self.approx_dot = approx_graph.dot self.approx_graph = approx_graph def write_divergence(self): word = TextMobject("``Diverges''") word.next_to(self.approx_dot, RIGHT, LARGE_BUFF) word.shift(MED_SMALL_BUFF*DOWN) word.add_background_rectangle() arrow = Arrow( word.get_left(), self.approx_dot, buff = SMALL_BUFF, color = WHITE ) self.play( Write(word), ShowCreation(arrow) ) self.wait() new_graphs = self.approx_graphs[self.initial_n_iterations:] for new_graph in new_graphs: self.play( Transform(self.approx_graph, new_graph), *list(map(Animation, self.foreground_mobjects)) ) self.wait() def write_radius_of_convergence(self): line = Line(*[ self.coords_to_point(x, 0) for x in (1, 2) ]) line.set_color(YELLOW) brace = Brace(line, DOWN) words = brace.get_text("``Radius of convergence''") words.add_background_rectangle() self.play( GrowFromCenter(brace), ShowCreation(line) ) self.wait() self.play(Write(words)) self.wait(3) class MoreToBeSaid(TeacherStudentsScene): CONFIG = { "seconds_to_blink" : 4, } def construct(self): words = TextMobject( "Lagrange error bounds, ", "convergence tests, ", "$\\dots$" ) words[0].set_color(BLUE) words[1].set_color(GREEN) words.to_edge(UP) fade_rect = FullScreenFadeRectangle() rect = Rectangle(height = 9, width = 16) rect.set_height(FRAME_Y_RADIUS) rect.to_corner(UP+RIGHT) randy = self.get_students()[1] self.teacher_says( "There's still \\\\ more to learn!", target_mode = "surprised", bubble_kwargs = {"height" : 3, "width" : 4} ) for word in words: self.play(FadeIn(word)) self.wait() self.teacher_says( "About everything", ) self.change_student_modes(*["pondering"]*3) self.wait() self.remove() self.pi_creatures = []##Hack self.play( RemovePiCreatureBubble(self.teacher), FadeOut(words), FadeIn(fade_rect), randy.change, "happy", rect ) self.pi_creatures = [randy] self.play(ShowCreation(rect)) self.wait(4) class Chapter10Thanks(PatreonThanks): CONFIG = { "specific_patrons" : [ "Ali Yahya", "CrypticSwarm", "Kaustuv DeBiswas", "Kathryn Schmiedicke", "Karan Bhargava", "Ankit Agarwal", "Yu Jun", "Dave Nicponski", "Damion Kistler", "Juan Benet", "Othman Alikhan", "Markus Persson", "Joseph John Cox", "Dan Buchoff", "Derek Dai", "Luc Ritchie", "Ahmad Bamieh", "Mark Govea", "Zac Wentzell", "Robert Teed", "Jason Hise", "Meshal Alshammari", "Bernd Sing", "Nils Schneider", "James Thornton", "Mustafa Mahdi", "Jonathan Eppele", "Mathew Bramson", "Jerry Ling", "Vecht", "Shimin Kuang", "Rish Kundalia", "Achille Brighton", "Ripta Pasay", ], } class Thumbnail(ExampleApproximationWithSine): CONFIG = { "graph_origin" : DOWN, "x_axis_label" : "", "y_axis_label" : "", "x_axis_width" : 14, "graph_stroke_width" : 8, } def construct(self): self.setup_axes() cos_graph = self.get_graph(np.cos) cos_graph.set_stroke(BLUE, self.graph_stroke_width) quad_graph = self.get_graph(taylor_approximation(np.cos, 2)) quad_graph.set_stroke(GREEN, self.graph_stroke_width) quartic = self.get_graph(taylor_approximation(np.cos, 4)) quartic.set_stroke(PINK, self.graph_stroke_width) self.add(cos_graph, quad_graph, quartic) title = TextMobject("Taylor Series") title.set_width(1.5*FRAME_X_RADIUS) title.add_background_rectangle() title.to_edge(UP) self.add(title)