from helpers import * import scipy import math from mobject.tex_mobject import TexMobject from mobject import Mobject from mobject.image_mobject import ImageMobject from mobject.vectorized_mobject import * from animation.animation import Animation from animation.transform import * from animation.simple_animations import * from animation.playground import * from topics.geometry import * from topics.characters import * from topics.functions import * from topics.fractals import * from topics.number_line import * from topics.combinatorics import * from topics.numerals import * from topics.three_dimensions import * from topics.objects import * from scene import Scene from scene.zoomed_scene import ZoomedScene from scene.reconfigurable_scene import ReconfigurableScene from camera import Camera from mobject.svg_mobject import * from mobject.tex_mobject import * from eoc.graph_scene import GraphScene from eoc.chapter8 import AreaIsDerivative from topics.common_scenes import OpeningQuote, PatreonThanks 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.highlight_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.dither() self.dither(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, } 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 = None, 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.dither() 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.dither(2) self.play( Write(to_write), ReplacementTransform( radius_brace[-1].copy(), height_tex_R ), ReplacementTransform( theta.copy(), height_tex_theta ), run_time = 2 ) self.dither(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) self.play(FadeIn(morty)) self.play( MoveToTarget(cosine), morty.change, "angry", cosine.target, ) self.dither() self.play(Blink(morty)) self.dither() 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, submobject_mode = "lagged_start" ), MoveToTarget(cosine), morty.change, "pondering", cosine_approx ) self.dither() 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.dither() self.play(randy.look_at, self.cosine) self.play(Blink(randy)) self.dither() ####### 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(SPACE_WIDTH) 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.highlight(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.highlight_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.highlight(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( "\\approx 1 - ", "{\\theta", "^2", "\\over", "2}" ) approx.highlight_by_tex("theta", YELLOW) approx.theta_squared_over_two = VGroup(*approx[-4:]) 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 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(*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.dither(2) self.play(Blink(randy)) self.dither() 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.highlight(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, submobject_mode = "lagged_start" )) self.play( FadeIn(free_to_change), *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.dither() 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.dither() 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.highlight_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").highlight_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.dither(2) self.play(FadeIn(equals_c0)) self.dither(2) self.play(Transform( poly_group, poly_group_target, run_time = 2, submobject_mode = "lagged_start" )) self.dither(2) self.play(*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.dither() 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.dither() self.change_quadratic_graph( self.quadratic_graph, 1, 1, -0.1 ) self.dither(2) self.change_quadratic_graph( self.quadratic_graph, 1, 0, -0.1 ) self.dither(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, submobject_mode = "lagged_start" )) self.dither(2) self.play(Write(VGroup(*rhs[2:])), run_time = 2) self.dither() self.play(Rotate( self.tangent_line, np.pi/12, in_place = True, run_time = 3, rate_func = wiggle )) self.dither() 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.highlight_by_tex("c_1", self.colors[1]) poly_group = VGroup( equals_c1, derivative, self.quadratic_tex ) poly_group_target = VGroup( TexMobject("=", "0", "+0").highlight_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, submobject_mode = "lagged_start" )) self.dither() self.play(Transform( derivative, derivative_at_zero, run_time = 2, submobject_mode = "lagged_start" )) self.dither(2) self.play(Write(equals_c1)) self.dither(2) self.play(Transform( poly_group, poly_group_target, run_time = 3, submobject_mode = "lagged_start" )) self.dither(2) self.play(*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.dither() 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.dither() 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.dither() self.play(*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, submobject_mode = "lagged_start" )) self.dither(3) self.play(Write(VGroup(*rhs[1][2:]), run_time = 2)) self.dither() 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.dither() 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(*map(ShowCreation, tangent_lines)) self.play(*tangent_change_anims) self.play(*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].highlight(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.dither(2) self.play(Write(second_derivs[0])) self.dither(2) self.play(Transform( poly_group, poly_group_target, run_time = 3, submobject_mode = "lagged_start" )) self.dither(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.dither(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.highlight_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].highlight(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.highlight_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.highlight_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.highlight_by_tex("cos", self.colors[0]) second_deriv.highlight_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.highlight_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"%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.highlight_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.dither(2) self.play(ReplacementTransform( approx_at_x, approx_at_point, )) self.dither() self.play(Write(approx_rhs)) self.dither(2) self.play( real_result.shift, 1.5*DOWN, real_result.set_fill, None, 1, ) self.change_student_modes(*["hooray"]*3) self.dither(2) self.change_student_modes( *["plain"]*3, added_anims = 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.dither(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.dither() 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.dither() 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.dither() self.play(Transform(self.polynomial, new_polynomial)) self.dither() 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.dither(2) self.play(Transform(self.polynomial, new_polynomial)) self.dither(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.highlight_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.dither() for c0 in 0, 2, 1: self.change_quadratic_graph( quadratic_graph, c0 = c0 ) self.dither(2) for c1 in 1, -1, 0: self.change_quadratic_graph( quadratic_graph, c1 = c1 ) self.dither(2) for c2 in -0.1, -1, -0.5: self.change_quadratic_graph( quadratic_graph, c2 = c2 ) self.dither(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(*map(FadeOut, [tangent_lines, dots])) self.dither() class MoreTerms(TeacherStudentsScene): def construct(self): self.teacher_says( "More terms!", target_mode = "surprised", ) self.change_student_modes(*["hooray"]*3) self.dither(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 = 2*SPACE_HEIGHT, width = 2*SPACE_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.highlight_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.highlight_by_tex("1", self.colors[0]) self.polynomial.highlight_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.highlight_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.dither() 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.dither(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.dither(2) dot.set_fill(opacity = 0.5) self.play( dot.move_to, self.polynomial_third_derivative.get_right(), dot.set_fill, None, 0, ) self.dither() 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.highlight(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, submobject_mode = "lagged_start" )) self.dither(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.dither() 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.highlight_by_tex("c_4", self.colors[4]) self.play(*map(FadeOut, [ self.plus_cubic_term, self.polynomial_third_derivative, self.polynomial_third_derivative_brace, ])) self.play(Write(plus_quartic_term)) self.dither() 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.dither(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.highlight_by_tex("c_4", self.colors[4]) self.play(Write(fourth_deriv_lhs)) self.play( quartic_term.next_to, fourth_deriv_lhs, RIGHT ) self.dither() fourth_deriv_rhs = self.take_derivatives_of_monomial(quartic_term) self.dither() self.play(Write(alt_rhs)) self.dither() 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.highlight(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, submobject_mode = "lagged_start", ), MoveToTarget(x_to_4, run_time = 2) ) self.dither(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.dither(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(added_anims_iter.next()) 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.dither() 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.highlight_by_tex("cos", self.colors[0]) third_deriv.highlight_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.highlight_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.highlight_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.highlight_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.dither(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.highlight_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.highlight_by_tex("c_8", YELLOW) self.add(term) self.dither() 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.highlight_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.dither(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.highlight(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.dither() self.play(Write(VGroup(*polynomial[-2:]))) self.play( Write(no_affect_words), ShowCreation(arrows), run_time = 3 ) self.dither(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.highlight_by_tex(self.c2_tex, self.colors[1]) second_deriv.highlight_by_tex(self.c4_tex, self.colors[2]) second_deriv.highlight_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.dither() self.play(GrowFromCenter(brace)) self.dither() self.play(Write(equals_zero)) self.dither(3) class ApproximateNearNewPoint(CubicAndQuarticApproximations): CONFIG = { "target_approx_centers" : [-np.pi/2, 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.dither() 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, submobject_mode = "lagged_start" )) self.dither(2) self.play(FadeIn(randy)) self.play(randy.change, "confused", poly_around_pi) self.play(Blink(randy)) self.dither(2) self.play(randy.change_mode, "happy") self.dither(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.highlight_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 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 piont" ) 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.highlight(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.dither() 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.dither() self.play(*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(*zip( derivs_at_x, arrows ))) group.add(TexMobject("\\vdots")) group.arrange_submobjects(DOWN, buff = SMALL_BUFF) group.scale_to_fit_height(2*SPACE_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.highlight(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.highlight(color) zeros = VGroup(*[ deriv.get_part_by_tex("0") for deriv in derivs_at_zero ]) self.play(FadeIn(derivs_at_x[0])) self.dither() 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.dither() self.dither() self.play(ReplacementTransform( derivs_at_x, derivs_at_zero )) self.dither() self.play(*map(Write, rhs_group)) self.dither() for rhs in rhs_group: self.play(Indicate(rhs[1]), color = WHITE) self.dither() 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.dither() 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.dither(2) to_fade = self.take_derivatives_of_monomial(monomial) self.play(FadeOut(to_fade)) self.dither() 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.highlight(self.colors[4]) self.play(GrowFromCenter(brace)) self.play(Write(name)) self.dither() self.play( Transform( self.quadratic_graph, quartic_graph, run_time = 2 ), Animation(self.dot) ) self.dither(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(*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_submobjects(DOWN, buff = MED_LARGE_BUFF) derivs_at_x.scale_to_fit_height(2*SPACE_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.highlight(color) d.add_background_rectangle() d0.replace(dx) da.replace(dx) self.play(FadeIn(derivs_at_x[0])) self.dither() for start, target in zip(derivs_at_x, derivs_at_x[1:]): self.play(ReplacementTransform( start.copy(), target )) self.dither() self.dither() self.play(ReplacementTransform( derivs_at_x, derivs_at_zero, )) self.play(ReplacementTransform( zeros.copy(), self.dot, run_time = 2, submobject_mode = "lagged_start" )) self.play(ShowCreation(self.v_line)) self.dither() 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, submobject_mode = "lagged_start" ), FadeOut(polynomial_fourth_term), FadeOut(self.taylor_name_group), ) self.polynomial = new_polynomial self.dither(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.highlight(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.dither() 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.dither() 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.dither() self.play(Transform( self.derivs_at_zero, self.derivs_at_a )) self.play( Transform(self.polynomial, new_polynomial), FadeOut(polynomial_third_term) ) self.dither() for x in -1, np.pi/6: self.play( UpdateFromAlphaFunc( group, get_update_function(x), ), Animation(self.polynomial), run_time = 4, ) self.dither() ##### 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.highlight(color) result.coefficients = coefs result.factorials = VGroup(*result[7::6]) return result 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.dither() 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).highlight(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(*zip( derivs_at_x, arrows ))) group.add(TexMobject("\\vdots")) group.arrange_submobjects(DOWN, buff = 2*SMALL_BUFF) group.scale_to_fit_height(2*SPACE_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.highlight(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.dither() self.dither() self.play(ReplacementTransform( derivs_at_x, derivs_at_zero )) self.dither() self.play(*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.highlight_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.play(Write(polynomial)) self.play( ShowCreation(box), FadeIn(words), self.teacher.change_mode, "raise_right_hand" ) self.dither(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.func_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.dither() 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), *map(Animation, self.foreground_mobjects) ) self.play( FadeOut(self.T_label_group), FadeIn(dx_label_group), FadeIn(rect), FadeIn(height_brace) ) self.dither() 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.scale_to_fit_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.dither() self.dither() self.play(ReplacementTransform( dx_label, x_minus_a )) self.dither() 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.highlight(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.dither(2) self.play( GrowFromCenter(height_brace), Write(height_labels[0]) ) self.dither(2) self.play(ReplacementTransform(*height_labels)) self.dither(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_submobjects(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.dither(2) self.play(Transform( group[-1], exp_2 )) self.dither(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.scale_to_fit_height(1) mini_rect.scale_to_fit_height(1) mini_triangle.scale_to_fit_height(0.5) geometric_taylor = VGroup( TexMobject("f(x) \\approx "), mini_area, TexMobject("+"), mini_rect, TexMobject("+"), mini_triangle, ) geometric_taylor.arrange_submobjects( 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.highlight_by_tex("f(a)", BLUE) analytic_taylor.highlight_by_tex("df", YELLOW) analytic_taylor.highlight_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.func_name) self.play(FadeIn( geometric_taylor, run_time = 2, submobject_mode = "lagged_start" )) self.dither() self.play( FadeIn(VGroup(*analytic_taylor[:3])), self.dark_area.set_fill, BLUE_E, 1, Transform(self.func_name, new_func_name) ) self.dither() self.play( self.rect.scale_in_place, 0.5, rate_func = there_and_back ) self.play(FadeIn(VGroup(*analytic_taylor[3:5]))) self.dither(2) self.play( self.triangle.scale_in_place, 0.5, rate_func = there_and_back ) self.play(FadeIn(VGroup(*analytic_taylor[5:]))) self.dither(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 = 2*SPACE_WIDTH, height = 2*SPACE_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.dither(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.dither(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.dither() self.play( GrowFromCenter(brace), Write(taylor_polynomial) ) self.dither(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.dither() 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.dither(2) self.play(self.randy.change, "confused", self.series) self.dither(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.highlight(YELLOW) rhs = TexMobject("= \\frac{1}{2}") rhs.next_to(series, RIGHT) rhs.highlight(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.dither() self.play( FadeOut(brace), Write(rhs) ) self.dither(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.highlight(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.dither() self.play(ReplacementTransform(x_group.copy(), one_group)) self.play(FadeIn(brace)) self.dither() for new_brace in braces[2:]: self.play(Transform(brace, new_brace)) self.dither() self.dither() self.play(MoveToTarget(e_to_1)) self.dither(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.highlight(color) lhs = TexMobject("e^%s"%arg, "\\rightarrow") lhs.arrange_submobjects(RIGHT, buff = SMALL_BUFF) group = VGroup(lhs, series) group.arrange_submobjects(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.dither() 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.dither() self.play( GrowFromCenter(brace), ShowCreation(approx_graph) ) self.dither() 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.dither() self.play(FocusOn(equals)) self.play(Transform(self.arrow, equals)) self.dither(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.highlight(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.dither() for new_graph in approx_graphs[2:]: self.play( Transform(approx_graph, new_graph), Animation(dot) ) self.dither() self.dither() 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].highlight(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, submobject_mode = "lagged_start" )) self.dither() 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.dither() 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.highlight(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( map(FadeIn, [approx_graph, brace]), map(Animation, self.foreground_mobjects) )) self.dither() 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), *map(Animation, self.foreground_mobjects) ) self.dither() approx_graph.remove(approx_dot) self.play( approx_dot.move_to, self.coords_to_point(self.divergent_example, 0), *it.chain( map(FadeOut, [approx_graph, brace]), map(Animation, self.foreground_mobjects) ) ) self.dither() 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), *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), *map(Animation, self.foreground_mobjects) ) self.dither() 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.dither() new_graphs = self.approx_graphs[self.initial_n_iterations:] for new_graph in new_graphs: self.play( Transform(self.approx_graph, new_graph), *map(Animation, self.foreground_mobjects) ) self.dither() def write_radius_of_convergence(self): line = Line(*[ self.coords_to_point(x, 0) for x in 1, 2 ]) line.highlight(YELLOW) brace = Brace(line, DOWN) words = brace.get_text("``Radius of convergence''") words.add_background_rectangle() self.play( GrowFromCenter(brace), ShowCreation(line) ) self.dither() self.play(Write(words)) self.dither(3) class MoreToBeSaid(TeacherStudentsScene): CONFIG = { "seconds_to_blink" : 4, } def construct(self): words = TextMobject( "Lagrange error bounds, ", "convergence tests, ", "$\\dots$" ) words[0].highlight(BLUE) words[1].highlight(GREEN) words.to_edge(UP) fade_rect = FullScreenFadeRectangle() rect = Rectangle(height = 9, width = 16) rect.scale_to_fit_height(SPACE_HEIGHT) 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.dither() self.teacher_says( "About everything", ) self.change_student_modes(*["pondering"]*3) self.dither() 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.dither(4)