3b1b-manim/old_projects/eoc/chapter7.py
2019-02-08 15:53:27 -08:00

2946 lines
92 KiB
Python

# -*- coding: utf-8 -*-
from big_ol_pile_of_manim_imports import *
class Chapter7OpeningQuote(OpeningQuote):
CONFIG = {
"quote" : [
" Calculus required ",
"continuity",
", and ",
"continuity ",
"was supposed to require the ",
"infinitely little",
"; but nobody could discover what the ",
"infinitely little",
" might be. ",
],
"quote_arg_separator" : "",
"highlighted_quote_terms" : {
"continuity" : BLUE,
"infinitely" : GREEN,
},
"author" : "Bertrand Russell",
}
class ThisVideo(TeacherStudentsScene):
def construct(self):
series = VideoSeries()
series.to_edge(UP)
deriv_videos = VGroup(*series[1:6])
this_video = series[6]
integral_videos = VGroup(*series[7:9])
video_groups = [deriv_videos, this_video, integral_videos]
braces = list(map(Brace, video_groups))
deriv_brace, this_brace, integral_brace = braces
tex_mobs = [
TexMobject(*args)
for args in [
("{df ", " \\over \\, ", " dx}"),
("\\lim_{h \\to 0}",),
("\\int ", "f(x)", "\\,dx"),
]
]
deriv_tex, this_tex, integral_tex = tex_mobs
for tex_mob, brace in zip(tex_mobs, braces):
tex_mob.set_color_by_tex("f", GREEN)
tex_mob.set_color_by_tex("dx", YELLOW)
tex_mob.next_to(brace, DOWN)
integral_tex.shift(LARGE_BUFF*RIGHT)
lim_to_deriv_arrow = Arrow(this_tex, deriv_tex, color = WHITE)
self.add(series)
for index in 0, 2:
videos = video_groups[index]
brace = braces[index]
tex_mob = tex_mobs[index]
self.play(ApplyWave(
videos,
direction = DOWN,
))
self.play(
GrowFromCenter(brace),
Write(tex_mob, run_time = 2)
)
self.play(
this_video.set_color, YELLOW,
GrowFromCenter(this_brace),
self.get_teacher().change_mode, "raise_right_hand",
self.get_teacher().look_at, this_video
)
self.play(Write(this_tex))
self.wait(2)
self.play(self.get_teacher().change_mode, "sassy")
self.wait(2)
class LimitJustMeansApproach(PiCreatureScene):
CONFIG = {
"dx_color" : GREEN,
"max_num_zeros" : 7,
}
def construct(self):
limit_expression = self.get_limit_expression()
limit_expression.shift(2*LEFT)
limit_expression.to_edge(UP)
evaluated_expressions = self.get_evaluated_expressions()
evaluated_expressions.next_to(limit_expression, DOWN, buff = LARGE_BUFF)
brace = Brace(evaluated_expressions[0][-1], DOWN)
question = TextMobject("What does this ``approach''?")
question.next_to(brace, DOWN)
point = VectorizedPoint(limit_expression.get_right())
expression = VGroup(
limit_expression[1].copy(),
point, point.copy()
)
self.add(limit_expression)
self.change_mode("raise_right_hand")
for next_expression in evaluated_expressions:
next_expression.move_to(evaluated_expressions[0], RIGHT)
self.play(
Transform(
expression, next_expression,
lag_ratio = 0.5,
lag_factor = 1.2,
),
self.pi_creature.look_at, next_expression[-1]
)
if brace not in self.get_mobjects():
self.play(
GrowFromCenter(brace),
Write(question)
)
self.wait(0.5)
self.wait(2)
def create_pi_creature(self):
self.pi_creature = Mortimer().flip()
self.pi_creature.to_corner(DOWN+LEFT)
return self.pi_creature
def get_limit_expression(self):
lim = TexMobject("\\lim_", "{dx", " \\to 0}")
lim.set_color_by_tex("dx", self.dx_color)
ratio = self.get_expression("dx")
ratio.next_to(lim, RIGHT)
limit_expression = VGroup(lim, ratio)
return limit_expression
def get_evaluated_expressions(self):
result = VGroup()
for num_zeros in range(1, self.max_num_zeros+1):
dx_str = "0." + "0"*num_zeros + "1"
expression = self.get_expression(dx_str)
dx = float(dx_str)
ratio = ((2+dx)**3-2**3)/dx
ratio_mob = TexMobject("%.6f\\dots"%ratio)
group = VGroup(expression, TexMobject("="), ratio_mob)
group.arrange(RIGHT)
result.add(group)
return result
def get_expression(self, dx):
result = TexMobject(
"{(2 + ", str(dx), ")^3 - 2^3 \\over", str(dx)
)
result.set_color_by_tex(dx, self.dx_color)
return result
class Goals(Scene):
def construct(self):
goals = [
TextMobject("Goal %d:"%d, s)
for d, s in [
(1, "Formal definition of derivatives"),
(2, "$(\\epsilon, \\delta)$ definition of a limit"),
(3, "L'Hôpital's rule"),
]
]
for goal in goals:
goal.scale(1.3)
goal.shift(3*DOWN).to_edge(LEFT)
curr_goal = goals[0]
self.play(FadeIn(curr_goal))
self.wait(2)
for goal in goals[1:]:
self.play(Transform(curr_goal, goal))
self.wait(2)
class RefreshOnDerivativeDefinition(GraphScene):
CONFIG = {
"start_x" : 2,
"start_dx" : 0.7,
"df_color" : YELLOW,
"dx_color" : GREEN,
"secant_line_color" : MAROON_B,
}
def construct(self):
self.setup_axes()
def func(x):
u = 0.3*x - 1.5
return -u**3 + 5*u + 7
graph = self.get_graph(func)
graph_label = self.get_graph_label(graph)
start_x_v_line, nudged_x_v_line = [
self.get_vertical_line_to_graph(
self.start_x + nudge, graph,
line_class = DashedLine,
color = RED
)
for nudge in (0, self.start_dx)
]
nudged_x_v_line.save_state()
ss_group = self.get_secant_slope_group(
self.start_x, graph,
dx = self.start_dx,
dx_label = "dx",
df_label = "df",
df_line_color = self.df_color,
dx_line_color = self.dx_color,
secant_line_color = self.secant_line_color,
)
derivative = TexMobject(
"{df", "\\over \\,", "dx}", "(", str(self.start_x), ")"
)
derivative.set_color_by_tex("df", self.df_color)
derivative.set_color_by_tex("dx", self.dx_color)
derivative.set_color_by_tex(str(self.start_x), RED)
df = derivative.get_part_by_tex("df")
dx = derivative.get_part_by_tex("dx")
input_x = derivative.get_part_by_tex(str(self.start_x))
derivative.move_to(self.coords_to_point(7, 4))
derivative.save_state()
deriv_brace = Brace(derivative)
dx_to_0 = TexMobject("dx", "\\to 0")
dx_to_0.set_color_by_tex("dx", self.dx_color)
dx_to_0.next_to(deriv_brace, DOWN)
#Introduce graph
self.play(ShowCreation(graph))
self.play(Write(graph_label, run_time = 1))
self.play(Write(derivative))
self.wait()
input_copy = input_x.copy()
self.play(
input_copy.next_to,
self.coords_to_point(self.start_x, 0),
DOWN
)
self.play(ShowCreation(start_x_v_line))
self.wait()
#ss_group_development
self.play(
ShowCreation(ss_group.dx_line),
ShowCreation(ss_group.dx_label),
)
self.wait()
self.play(ShowCreation(ss_group.df_line))
self.play(Write(ss_group.df_label))
self.wait(2)
self.play(
ReplacementTransform(ss_group.dx_label.copy(), dx),
ReplacementTransform(ss_group.df_label.copy(), df),
run_time = 2
)
self.play(ShowCreation(ss_group.secant_line))
self.wait()
#Let dx approach 0
self.play(
GrowFromCenter(deriv_brace),
Write(dx_to_0),
)
self.animate_secant_slope_group_change(
ss_group,
target_dx = 0.01,
run_time = 5,
)
self.wait()
#Write out fuller limit
new_deriv = TexMobject(
"{f", "(", str(self.start_x), "+", "dx", ")",
"-", "f", "(", str(self.start_x), ")",
"\\over \\,", "dx"
)
new_deriv.set_color_by_tex("dx", self.dx_color)
new_deriv.set_color_by_tex("f", self.df_color)
new_deriv.set_color_by_tex(str(self.start_x), RED)
deriv_to_new_deriv = dict([
(
VGroup(derivative.get_part_by_tex(s)),
VGroup(*new_deriv.get_parts_by_tex(s))
)
for s in ["f", "over", "dx", "(", str(self.start_x), ")"]
])
covered_new_deriv_parts = list(it.chain(*list(deriv_to_new_deriv.values())))
uncovered_new_deriv_parts = [part for part in new_deriv if part not in covered_new_deriv_parts]
new_deriv.move_to(derivative)
new_brace = Brace(new_deriv, DOWN)
self.animate_secant_slope_group_change(
ss_group,
target_dx = self.start_dx,
run_time = 2
)
self.play(ShowCreation(nudged_x_v_line))
self.wait()
self.play(*[
ReplacementTransform(*pair, run_time = 2)
for pair in list(deriv_to_new_deriv.items())
]+[
Transform(deriv_brace, new_brace),
dx_to_0.next_to, new_brace, DOWN
])
self.play(Write(VGroup(*uncovered_new_deriv_parts), run_time = 2))
self.wait()
#Introduce limit notation
lim = TexMobject("\\lim").scale(1.3)
dx_to_0.generate_target()
dx_to_0.target.scale(0.7)
dx_to_0.target.next_to(lim, DOWN, buff = SMALL_BUFF)
lim_group = VGroup(lim, dx_to_0.target)
lim_group.move_to(new_deriv, LEFT)
self.play(
ReplacementTransform(deriv_brace, lim),
MoveToTarget(dx_to_0),
new_deriv.next_to, lim_group, RIGHT,
run_time = 2
)
for sf, color in (1.2, YELLOW), (1/1.2, WHITE):
self.play(
lim.scale_in_place, sf,
lim.set_color, color,
lag_ratio = 0.5
)
self.wait(2)
self.animate_secant_slope_group_change(
ss_group, target_dx = 0.01,
run_time = 5,
added_anims = [
Transform(nudged_x_v_line, start_x_v_line, run_time = 5)
]
)
self.wait(2)
#Record attributes for DiscussLowercaseDs below
digest_locals(self)
class RantOpenAndClose(Scene):
def construct(self):
opening, closing = [
TextMobject(
start, "Rant on infinitesimals", "$>$",
arg_separator = ""
)
for start in ("$<$", "$<$/")
]
self.play(FadeIn(opening))
self.wait(2)
self.play(Transform(opening, closing))
self.wait(2)
class DiscussLowercaseDs(RefreshOnDerivativeDefinition, PiCreatureScene, ZoomedScene):
CONFIG = {
"zoomed_canvas_corner" : UP+LEFT
}
def construct(self):
self.skip_superclass_anims()
self.replace_dx_terms()
self.compare_rhs_and_lhs()
self.h_is_finite()
def skip_superclass_anims(self):
self.remove(self.pi_creature)
self.force_skipping()
RefreshOnDerivativeDefinition.construct(self)
self.revert_to_original_skipping_status()
self.animate_secant_slope_group_change(
self.ss_group, target_dx = self.start_dx,
added_anims = [
self.nudged_x_v_line.restore,
Animation(self.ss_group.df_line)
],
run_time = 1
)
everything = self.get_top_level_mobjects()
everything.remove(self.derivative)
self.play(*[
ApplyMethod(mob.shift, 2.5*LEFT)
for mob in everything
] + [
FadeIn(self.pi_creature)
])
def replace_dx_terms(self):
dx_list = [self.dx_to_0[0]]
dx_list += self.new_deriv.get_parts_by_tex("dx")
mover = dx_list[0]
mover_scale_val = 1.5
mover.initial_right = mover.get_right()
self.play(
mover.scale, mover_scale_val,
mover.next_to, self.pi_creature.get_corner(UP+LEFT),
UP, MED_SMALL_BUFF,
self.pi_creature.change_mode, "sassy",
path_arc = np.pi/2,
)
self.blink()
self.wait()
for tex in "\\Delta x", "h":
dx_list_replacement = [
TexMobject(
tex
).set_color(self.dx_color).move_to(dx, DOWN)
for dx in dx_list
]
self.play(
Transform(
VGroup(*dx_list),
VGroup(*dx_list_replacement),
),
self.pi_creature.change_mode, "raise_right_hand"
)
self.wait()
self.play(
mover.scale, 0.9,
mover.move_to, mover.initial_right, RIGHT,
self.pi_creature.change_mode, "happy",
)
self.play(
self.dx_to_0.next_to, self.lim, DOWN, SMALL_BUFF,
)
self.wait()
def compare_rhs_and_lhs(self):
self.derivative.restore()
lhs = self.derivative
equals = TexMobject("=")
rhs = VGroup(self.lim, self.dx_to_0, self.new_deriv)
rhs.generate_target()
rhs.target.next_to(self.pi_creature, UP, MED_LARGE_BUFF)
rhs.target.to_edge(RIGHT)
equals.next_to(rhs.target, LEFT)
lhs.next_to(equals, LEFT)
d_circles = VGroup(*[
Circle(color = BLUE_B).replace(
lhs.get_part_by_tex(tex)[0],
stretch = True,
).scale_in_place(1.5).rotate_in_place(-np.pi/12)
for tex in ("df", "dx")
])
d_words = TextMobject("""
Limit idea is
built in
""")
d_words.next_to(d_circles, DOWN)
d_words.set_color(d_circles[0].get_color())
lhs_rect, rhs_rect = rects = [
Rectangle(color = GREEN_B).replace(
mob, stretch = True
)
for mob in (lhs, rhs.target)
]
for rect in rects:
rect.stretch_to_fit_width(rect.get_width()+2*MED_SMALL_BUFF)
rect.stretch_to_fit_height(rect.get_height()+2*MED_SMALL_BUFF)
formal_definition_words = TextMobject("""
Formal derivative definition
""")
formal_definition_words.set_width(rhs_rect.get_width())
formal_definition_words.next_to(rhs_rect, UP)
formal_definition_words.set_color(rhs_rect.get_color())
formal_definition_words.add_background_rectangle()
df = VGroup(lhs.get_part_by_tex("df"))
df_target = VGroup(*self.new_deriv.get_parts_by_tex("f"))
self.play(
MoveToTarget(rhs),
Write(lhs),
Write(equals),
)
self.play(
ShowCreation(d_circles, run_time = 2),
self.pi_creature.change_mode, "pondering"
)
self.play(Write(d_words))
self.animate_secant_slope_group_change(
self.ss_group, target_dx = 0.01,
added_anims = [
Transform(
self.nudged_x_v_line, self.start_x_v_line,
run_time = 3
)
]
)
self.change_mode("thinking")
self.wait(2)
self.play(
ShowCreation(lhs_rect),
FadeOut(d_circles),
FadeOut(d_words),
)
self.wait(2)
self.play(
ReplacementTransform(lhs_rect, rhs_rect),
self.pi_creature.change_mode, "raise_right_hand"
)
self.wait(2)
self.play(ReplacementTransform(
df.copy(), df_target,
path_arc = -np.pi/2,
run_time = 2
))
self.wait(2)
self.play(Indicate(
VGroup(*rhs[:2]),
run_time = 2
))
self.wait()
self.play(Write(formal_definition_words))
self.play(
self.pi_creature.change_mode, "happy",
self.pi_creature.look_at, formal_definition_words
)
self.wait(2)
lhs.add_background_rectangle()
self.add(rhs_rect, rhs)
self.definition_group = VGroup(
lhs, equals, rhs_rect, rhs, formal_definition_words
)
self.lhs, self.rhs, self.rhs_rect = lhs, rhs, rhs_rect
def h_is_finite(self):
self.play(
FadeOut(self.graph_label),
self.definition_group.center,
self.definition_group.to_corner, UP+RIGHT,
self.pi_creature.change_mode, "sassy",
self.pi_creature.look_at, 4*UP
)
self.wait()
words = TextMobject("No ``infinitely small''")
words.next_to(
self.definition_group, DOWN,
buff = LARGE_BUFF,
)
arrow = Arrow(words.get_top(), self.rhs_rect.get_bottom())
arrow.set_color(WHITE)
h_group = VGroup(
self.rhs[1].get_part_by_tex("dx"),
*self.rhs[2].get_parts_by_tex("dx")
)
moving_h = h_group[0]
moving_h.original_center = moving_h.get_center()
dx_group = VGroup()
for h in h_group:
dx = TexMobject("dx")
dx.set_color(h.get_color())
dx.replace(h, dim_to_match = 1)
dx_group.add(dx)
moving_dx = dx_group[0]
self.play(Write(words), ShowCreation(arrow))
self.wait(2)
self.play(
moving_h.next_to, self.pi_creature.get_corner(UP+RIGHT), UP,
self.pi_creature.change_mode, "raise_left_hand",
)
self.wait()
moving_dx.move_to(moving_h)
h_group.save_state()
self.play(Transform(
h_group, dx_group,
path_arc = np.pi,
))
self.wait(2)
self.play(h_group.restore, path_arc = np.pi)
self.play(
moving_h.move_to, moving_h.original_center,
self.pi_creature.change_mode, "plain"
)
self.wait()
#Zoom in
self.activate_zooming()
lil_rect = self.little_rectangle
lil_rect.move_to(self.ss_group)
lil_rect.scale_in_place(3)
lil_rect.save_state()
self.wait()
self.add(self.rhs)
self.play(
lil_rect.set_width,
self.ss_group.dx_line.get_width()*4,
run_time = 4
)
self.wait()
dx = self.ss_group.dx_label
dx.save_state()
h = TexMobject("h")
h.set_color(dx.get_color())
h.replace(dx, dim_to_match = 1)
self.play(Transform(dx, h, path_arc = np.pi))
self.play(Indicate(dx))
self.wait()
self.play(dx.restore, path_arc = np.pi)
self.play(lil_rect.restore, run_time = 4)
self.wait()
self.disactivate_zooming()
self.wait()
#Last approaching reference
for target_dx in 3, 0.01, -2, 0.01:
self.animate_secant_slope_group_change(
self.ss_group, target_dx = target_dx,
run_time = 4,
)
self.wait()
class OtherViewsOfDx(TeacherStudentsScene):
def construct(self):
definition = TexMobject(
"{df", "\\over \\,", "dx}", "(", "2", ")", "=",
"\\lim", "_{h", "\\to", "0}",
"{f", "(", "2", "+", "h", ")", "-", "f", "(", "2", ")",
"\\over \\,", "h}"
)
tex_to_color = {
"df" : YELLOW,
"f" : YELLOW,
"dx" : GREEN,
"h" : GREEN,
"2" : RED
}
for tex, color in list(tex_to_color.items()):
definition.set_color_by_tex(tex, color)
definition.scale(0.8)
definition.to_corner(UP+LEFT)
dx_group = VGroup(*definition.get_parts_by_tex("dx"))
h_group = VGroup(*definition.get_parts_by_tex("h"))
self.add(definition)
statements = [
TextMobject(*args)
for args in [
("Why the new \\\\ variable", "$h$", "?"),
("$dx$", "is more $\\dots$ contentious."),
("$dx$", "is infinitely small"),
("$dx$", "is nothing more \\\\ than a symbol"),
]
]
for statement in statements:
statement.h, statement.dx = [
VGroup(*statement.get_parts_by_tex(
tex, substring = False
)).set_color(GREEN)
for tex in ("$h$", "$dx$")
]
#Question
self.student_says(
statements[0],
student_index = 1,
target_mode = "confused"
)
self.play(ReplacementTransform(
statements[0].h.copy(), h_group,
run_time = 2,
lag_ratio = 0.5,
lag_factor = 1.5,
))
self.wait()
#Teacher answer
self.teacher_says(
statements[1],
target_mode = "hesitant",
bubble_creation_class = FadeIn,
)
self.play(ReplacementTransform(
statements[1].dx.copy(), dx_group,
run_time = 2,
))
self.wait()
#First alternate view
moving_dx = dx_group.copy()
bubble_intro = PiCreatureBubbleIntroduction(
self.get_students()[2],
statements[2],
target_mode = "hooray",
bubble_creation_class = FadeIn,
)
bubble_intro.update(1)
dx_movement = Transform(
moving_dx, statements[2].dx,
run_time = 2
)
bubble_intro.update(0)
self.play(
bubble_intro, dx_movement,
RemovePiCreatureBubble(self.get_teacher()),
)
self.play(self.get_teacher().change_mode, "erm")
self.wait()
#Next alternate view
bubble_intro = PiCreatureBubbleIntroduction(
self.get_students()[0],
statements[3],
target_mode = "maybe",
look_at_arg = 3*UP,
bubble_creation_class = FadeIn,
)
bubble_intro.update(1)
dx_movement = Transform(
moving_dx, statements[3].dx,
run_time = 2
)
bubble_intro.update(0)
last_bubble = self.get_students()[2].bubble
self.play(
bubble_intro, dx_movement,
FadeOut(last_bubble),
FadeOut(last_bubble.content),
*it.chain(*[
[
pi.change_mode, "pondering",
pi.look_at, bubble_intro.mobject
]
for pi in self.get_students()[1:]
])
)
self.wait(3)
class GoalsListed(Scene):
def construct(self):
goals = VGroup(*[
TextMobject("Goal %d: %s"%(d, s))
for d, s in zip(it.count(1), [
"Formal definition of a derivative",
"$(\\epsilon, \\delta)$ definition of limits",
"L'Hôpital's rule",
])
])
goals.arrange(
DOWN, buff = LARGE_BUFF, aligned_edge = LEFT
)
for goal in goals:
self.play(FadeIn(goal))
self.wait()
for i, goal in enumerate(goals):
anims = [goal.set_color, YELLOW]
if i > 0:
anims += [goals[i-1].set_color, WHITE]
self.play(*anims)
self.wait()
class GraphLimitExpression(GraphScene):
CONFIG = {
"start_x" : 2,
"h_color" : GREEN,
"f_color" : YELLOW,
"two_color" : RED,
"graph_origin" : 3*DOWN+LEFT,
"x_min" : -8,
"x_max" : 5,
"x_axis_label" : "$h$",
"x_labeled_nums" : list(range(-8, 6, 2)),
"y_min" : 0,
"y_max" : 20,
"y_tick_frequency" : 1,
"y_labeled_nums" : list(range(5, 25, 5)),
"y_axis_label" : "",
"big_delta" : 0.7,
"small_delta" : 0.01,
}
def construct(self):
self.func = lambda h : 3*(2**2) + 3*2*h + h**2
self.setup_axes()
self.introduce_function()
self.emphasize_non_definedness_at_0()
self.draw_limit_point_hole()
self.show_limit()
self.skeptic_asks()
self.show_epsilon_delta_intuition()
def introduce_function(self):
expression = TexMobject(
"{(", "2", "+", "h", ")", "^3",
"-", "(", "2", ")", "^3",
"\\over \\,", "h}",
arg_separator = "",
)
limit = TexMobject("\\lim", "_{h", "\\to 0}")
derivative = TexMobject(
"{d(x^3)", "\\over \\,", "dx}", "(", "2", ")"
)
tex_to_color = {
"h" : self.h_color,
"dx" : self.h_color,
"2" : self.two_color
}
for tex_mob in expression, limit, derivative:
for tex, color in list(tex_to_color.items()):
tex_mob.set_color_by_tex(tex, color)
tex_mob.next_to(ORIGIN, RIGHT, LARGE_BUFF)
tex_mob.to_edge(UP)
expression.save_state()
expression.generate_target()
expression.target.next_to(limit, RIGHT)
brace = Brace(VGroup(limit, expression.target))
derivative.next_to(brace, DOWN)
indices = [0, 6, 11, 13]
funcs = [
lambda h : (2+h)**3,
lambda h : (2+h)**3 - 2**3,
self.func
]
graph = None
for i, j, func in zip(indices, indices[1:], funcs):
anims = [FadeIn(
VGroup(*expression[i:j]),
lag_ratio = 0.5,
lag_factor = 1.5
)]
new_graph = self.get_graph(func, color = BLUE)
if graph is None:
graph = new_graph
anims.append(FadeIn(graph))
else:
anims.append(Transform(graph, new_graph))
self.play(*anims)
self.wait()
self.wait()
self.play(
MoveToTarget(expression),
FadeIn(limit, lag_ratio = 0.5),
GrowFromCenter(brace)
)
self.play(Write(derivative))
self.wait(2)
self.play(
expression.restore,
*list(map(FadeOut, [derivative, brace, limit]))
)
self.wait()
colored_graph = graph.copy().set_color(YELLOW)
self.play(ShowCreation(colored_graph))
self.wait()
self.play(ShowCreation(graph))
self.remove(colored_graph)
self.wait()
self.expression = expression
self.limit = limit
self.graph = graph
def emphasize_non_definedness_at_0(self):
expression = self.expression
dot = Dot(self.graph_origin, color = GREEN)
h_equals_0 = TexMobject("h", "=", "0", "?")
h_equals_0.next_to(self.graph_origin, UP+RIGHT, LARGE_BUFF)
for tex in "h", "0":
h_equals_0.set_color_by_tex(tex, GREEN)
arrow = Arrow(h_equals_0.get_left(), self.graph_origin)
arrow.set_color(WHITE)
new_expression = expression.deepcopy()
h_group = VGroup(*new_expression.get_parts_by_tex("h"))
for h in h_group:
zero = TexMobject("0")
zero.set_color(h.get_color())
zero.replace(h, dim_to_match = 1)
Transform(h, zero).update(1)
rhs = TexMobject("=", "{\\, 0\\,", "\\over \\,", "0\\,}")
rhs.set_color_by_tex("0", GREEN)
rhs.next_to(new_expression, RIGHT)
equation = VGroup(new_expression, rhs)
equation.next_to(expression, DOWN, buff = LARGE_BUFF)
ud_brace = Brace(VGroup(*rhs[1:]), DOWN)
undefined = TextMobject("Undefined")
undefined.next_to(ud_brace, DOWN)
undefined.to_edge(RIGHT)
self.play(Write(h_equals_0, run_time = 2))
self.play(*list(map(ShowCreation, [arrow, dot])))
self.wait()
self.play(ReplacementTransform(
expression.copy(), new_expression
))
self.wait()
self.play(Write(rhs))
self.wait()
self.play(
GrowFromCenter(ud_brace),
Write(undefined)
)
self.wait(2)
self.point_to_zero_group = VGroup(
h_equals_0, arrow, dot
)
self.plug_in_zero_group = VGroup(
new_expression, rhs, ud_brace, undefined
)
def draw_limit_point_hole(self):
dx = 0.07
color = self.graph.get_color()
circle = Circle(
radius = dx,
stroke_color = color,
fill_color = BLACK,
fill_opacity = 1,
)
circle.move_to(self.coords_to_point(0, 12))
colored_circle = circle.copy()
colored_circle.set_stroke(YELLOW)
colored_circle.set_fill(opacity = 0)
self.play(GrowFromCenter(circle))
self.wait()
self.play(ShowCreation(colored_circle))
self.play(ShowCreation(
circle.copy().set_fill(opacity = 0),
remover = True
))
self.remove(colored_circle)
self.play(
circle.scale_in_place, 0.3,
run_time = 2,
rate_func = wiggle
)
self.wait()
self.limit_point_hole = circle
def show_limit(self):
dot = self.point_to_zero_group[-1]
ed_group = self.get_epsilon_delta_group(self.big_delta)
left_v_line, right_v_line = ed_group.delta_lines
bottom_h_line, top_h_line = ed_group.epsilon_lines
ed_group.delta_lines.save_state()
ed_group.epsilon_lines.save_state()
brace = Brace(ed_group.input_range, UP)
brace_text = brace.get_text("Inputs around 0", buff = SMALL_BUFF)
brace_text.add_background_rectangle()
brace_text.shift(RIGHT)
limit_point_hole_copy = self.limit_point_hole.copy()
limit_point_hole_copy.set_stroke(YELLOW)
h_zero_hole = limit_point_hole_copy.copy()
h_zero_hole.move_to(self.graph_origin)
ed_group.input_range.add(h_zero_hole)
ed_group.output_range.add(limit_point_hole_copy)
#Show range around 0
self.play(
FadeOut(self.plug_in_zero_group),
FadeOut(VGroup(*self.point_to_zero_group[:-1])),
)
self.play(
GrowFromCenter(brace),
Write(brace_text),
ReplacementTransform(dot, ed_group.input_range),
)
self.add(h_zero_hole)
self.wait()
self.play(
ReplacementTransform(
ed_group.input_range.copy(),
ed_group.output_range,
run_time = 2
),
)
self.remove(self.limit_point_hole)
#Show approaching
self.play(*list(map(FadeOut, [brace, brace_text])))
for v_line, h_line in (right_v_line, top_h_line), (left_v_line, bottom_h_line):
self.play(
ShowCreation(v_line),
ShowCreation(h_line)
)
self.wait()
self.play(
v_line.move_to, self.coords_to_point(0, 0), DOWN,
h_line.move_to, self.coords_to_point(0, self.func(0)),
run_time = 3
)
self.play(
VGroup(h_line, v_line).set_stroke, GREY, 2,
)
self.wait()
#Write limit
limit = self.limit
limit.next_to(self.expression, LEFT)
equals, twelve = rhs = TexMobject("=", "12")
rhs.next_to(self.expression, RIGHT)
twelve_copy = twelve.copy()
limit_group = VGroup(limit, rhs)
self.play(Write(limit_group))
self.wait()
self.play(twelve_copy.next_to, top_h_line, RIGHT)
self.wait()
self.twelve_copy = twelve_copy
self.rhs = rhs
self.ed_group = ed_group
self.input_range_brace_group = VGroup(brace, brace_text)
def skeptic_asks(self):
randy = Randolph()
randy.scale(0.9)
randy.to_edge(DOWN)
self.play(FadeIn(randy))
self.play(PiCreatureSays(
randy, """
What \\emph{exactly} do you
mean by ``approach''
""",
bubble_kwargs = {
"height" : 3,
"width" : 5,
"fill_opacity" : 1,
"direction" : LEFT,
},
target_mode = "sassy"
))
self.remove(self.twelve_copy)
self.play(randy.look, OUT)
self.play(Blink(randy))
self.wait()
self.play(RemovePiCreatureBubble(
randy, target_mode = "pondering",
look_at_arg = self.limit_point_hole
))
self.play(
self.ed_group.delta_lines.restore,
self.ed_group.epsilon_lines.restore,
Animation(randy),
rate_func = there_and_back,
run_time = 5,
)
self.play(Blink(randy))
self.play(FadeOut(randy))
def show_epsilon_delta_intuition(self):
self.play(
FadeOut(self.ed_group.epsilon_lines),
FadeIn(self.input_range_brace_group)
)
self.ed_group.epsilon_lines.restore()
self.wait()
self.play(
self.ed_group.delta_lines.restore,
Animation(self.input_range_brace_group),
run_time = 2
)
self.play(FadeOut(self.input_range_brace_group))
self.play(
ReplacementTransform(
self.ed_group.input_range.copy(),
self.ed_group.output_range,
run_time = 2
)
)
self.wait()
self.play(*list(map(GrowFromCenter, self.ed_group.epsilon_lines)))
self.play(*[
ApplyMethod(
line.copy().set_stroke(GREY, 2).move_to,
self.coords_to_point(0, self.func(0)),
run_time = 3,
rate_func = there_and_back,
remover = True,
)
for line in self.ed_group.epsilon_lines
])
self.wait()
holes = VGroup(
self.ed_group.input_range.submobjects.pop(),
self.ed_group.output_range.submobjects.pop(),
)
holes.save_state()
self.animate_epsilon_delta_group_change(
self.ed_group,
target_delta = self.small_delta,
run_time = 8,
rate_func = lambda t : smooth(t, 2),
added_anims = [
ApplyMethod(
hole.scale_in_place, 0.5,
run_time = 8
)
for hole in holes
]
)
self.wait()
self.holes = holes
#########
def get_epsilon_delta_group(
self,
delta,
limit_x = 0,
dashed_line_stroke_width = 3,
dashed_line_length = FRAME_HEIGHT,
input_range_color = YELLOW,
input_range_stroke_width = 6,
):
kwargs = dict(locals())
result = VGroup()
kwargs.pop("self")
result.delta = kwargs.pop("delta")
result.limit_x = kwargs.pop("limit_x")
result.kwargs = kwargs
dashed_line = DashedLine(
ORIGIN, dashed_line_length*RIGHT,
stroke_width = dashed_line_stroke_width
)
x_values = [limit_x-delta, limit_x+delta]
x_axis_points = [self.coords_to_point(x, 0) for x in x_values]
result.delta_lines = VGroup(*[
dashed_line.copy().rotate(np.pi/2).move_to(
point, DOWN
)
for point in x_axis_points
])
if self.func(limit_x) < 0:
result.delta_lines.rotate(
np.pi, RIGHT,
about_point = result.delta_lines.get_bottom()
)
basically_zero = 0.00001
result.input_range, result.output_range = [
VGroup(*[
self.get_graph(
func,
color = input_range_color,
x_min = x_min,
x_max = x_max,
)
for x_min, x_max in [
(limit_x-delta, limit_x-basically_zero),
(limit_x+basically_zero, limit_x+delta),
]
]).set_stroke(width = input_range_stroke_width)
for func in ((lambda h : 0), self.func)
]
result.epsilon_lines = VGroup(*[
dashed_line.copy().move_to(
self.coords_to_point(limit_x, 0)[0]*RIGHT+\
result.output_range.get_edge_center(vect)[1]*UP
)
for vect in (DOWN, UP)
])
result.digest_mobject_attrs()
return result
def animate_epsilon_delta_group_change(
self, epsilon_delta_group, target_delta,
**kwargs
):
added_anims = kwargs.get("added_anims", [])
limit_x = epsilon_delta_group.limit_x
start_delta = epsilon_delta_group.delta
ed_group_kwargs = epsilon_delta_group.kwargs
def update_ed_group(ed_group, alpha):
delta = interpolate(start_delta, target_delta, alpha)
new_group = self.get_epsilon_delta_group(
delta, limit_x = limit_x,
**ed_group_kwargs
)
Transform(ed_group, new_group).update(1)
return ed_group
self.play(
UpdateFromAlphaFunc(
epsilon_delta_group, update_ed_group,
**kwargs
),
*added_anims
)
class LimitCounterExample(GraphLimitExpression):
CONFIG = {
"x_min" : -8,
"x_max" : 8,
"x_labeled_nums" : list(range(-8, 10, 2)),
"x_axis_width" : FRAME_WIDTH - LARGE_BUFF,
"y_min" : -4,
"y_max" : 4,
"y_labeled_nums" : list(range(-2, 4, 1)),
"y_axis_height" : FRAME_HEIGHT+2*LARGE_BUFF,
"graph_origin" : DOWN,
"graph_color" : BLUE,
"hole_radius" : 0.075,
"smaller_hole_radius" : 0.04,
"big_delta" : 1.5,
"small_delta" : 0.05,
}
def construct(self):
self.add_func()
self.setup_axes()
self.draw_graph()
self.approach_zero()
self.write_limit_not_defined()
self.show_epsilon_delta_intuition()
def add_func(self):
def func(h):
square = 0.25*h**2
if h < 0:
return -square + 1
else:
return square + 2
self.func = func
def draw_graph(self):
epsilon = 0.1
left_graph, right_graph = [
self.get_graph(
self.func,
color = self.graph_color,
x_min = x_min,
x_max = x_max,
)
for x_min, x_max in [
(self.x_min, -epsilon),
(epsilon, self.x_max),
]
]
left_hole = self.get_hole(0, 1, color = self.graph_color)
right_hole = self.get_hole(0, 2, color = self.graph_color)
graph = VGroup(
left_graph, left_hole,
right_hole, right_graph
)
self.play(ShowCreation(graph, run_time = 5))
self.wait()
self.play(ReplacementTransform(
left_hole.copy().set_stroke(YELLOW), right_hole
))
self.wait()
self.graph = graph
self.graph_holes = VGroup(left_hole, right_hole)
def approach_zero(self):
ed_group = self.get_epsilon_delta_group(self.big_delta)
left_v_line, right_v_line = ed_group.delta_lines
bottom_h_line, top_h_line = ed_group.epsilon_lines
ed_group.delta_lines.save_state()
ed_group.epsilon_lines.save_state()
right_lines = VGroup(right_v_line, top_h_line)
left_lines = VGroup(left_v_line, bottom_h_line)
basically_zero = 0.00001
def update_lines(lines, alpha):
v_line, h_line = lines
sign = 1 if v_line is right_v_line else -1
x_val = interpolate(sign*self.big_delta, sign*basically_zero, alpha)
v_line.move_to(self.coords_to_point(x_val, 0), DOWN)
h_line.move_to(self.coords_to_point(0, self.func(x_val)))
return lines
for lines in right_lines, left_lines:
self.play(*list(map(ShowCreation, lines)))
self.play(UpdateFromAlphaFunc(
lines, update_lines,
run_time = 3
))
self.play(lines.set_stroke, GREY, 3)
self.wait()
self.ed_group = ed_group
def write_limit_not_defined(self):
limit = TexMobject(
"\\lim", "_{h", "\\to 0}", "f(", "h", ")"
)
limit.set_color_by_tex("h", GREEN)
limit.move_to(self.coords_to_point(2, 1.5))
words = TextMobject("is not defined")
words.set_color(RED)
words.next_to(limit, RIGHT, align_using_submobjects = True)
limit_group = VGroup(limit, words)
self.play(Write(limit))
self.wait()
self.play(Write(words))
self.wait()
self.play(limit_group.to_corner, UP+LEFT)
self.wait()
def show_epsilon_delta_intuition(self):
ed_group = self.ed_group
self.play(
ed_group.delta_lines.restore,
ed_group.epsilon_lines.restore,
)
self.play(ShowCreation(ed_group.input_range))
self.wait()
self.play(ReplacementTransform(
ed_group.input_range.copy(),
ed_group.output_range,
run_time = 2
))
self.graph.remove(*self.graph_holes)
self.remove(*self.graph_holes)
self.wait()
self.animate_epsilon_delta_group_change(
ed_group, target_delta = self.small_delta,
run_time = 6
)
self.hole_radius = self.smaller_hole_radius
brace = Brace(self.ed_group.epsilon_lines, RIGHT, buff = SMALL_BUFF)
brace_text = brace.get_text("Can't get \\\\ smaller", buff = SMALL_BUFF)
self.play(
GrowFromCenter(brace),
Write(brace_text)
)
self.wait()
run_time_rate_func_pairs = [
(3, lambda t : 1 - there_and_back(t)),
(1, lambda t : 1 - 0.2*there_and_back(3*t % 1)),
(1, lambda t : 1 - 0.2*there_and_back(5*t % 1)),
]
for run_time, rate_func in run_time_rate_func_pairs:
self.animate_epsilon_delta_group_change(
ed_group, target_delta = self.small_delta,
run_time = run_time,
rate_func = rate_func,
)
self.wait()
#####
def get_epsilon_delta_group(self, delta, **kwargs):
ed_group = GraphLimitExpression.get_epsilon_delta_group(self, delta, **kwargs)
color = ed_group.kwargs["input_range_color"]
radius = min(delta/2, self.hole_radius)
pairs = [
(ed_group.input_range[0], (0, 0)),
(ed_group.input_range[1], (0, 0)),
(ed_group.output_range[0], (0, 1)),
(ed_group.output_range[1], (0, 2)),
]
for mob, coords in pairs:
mob.add(self.get_hole(
*coords,
color = color,
radius = radius
))
return ed_group
def get_hole(self, *coords, **kwargs):
color = kwargs.get("color", BLUE)
radius = kwargs.get("radius", self.hole_radius)
return Circle(
radius = radius,
stroke_color = color,
fill_color = BLACK,
fill_opacity = 1,
).move_to(self.coords_to_point(*coords))
class PrefaceToEpsilonDeltaDefinition(TeacherStudentsScene):
def construct(self):
title = TexMobject("(\\epsilon, \\delta) \\text{ definition}")
title.next_to(self.get_teacher().get_corner(UP+LEFT), UP)
title.save_state()
title.shift(DOWN)
title.set_fill(opacity = 0)
self.play(
title.restore,
self.get_teacher().change_mode, "raise_right_hand",
)
self.change_student_modes(*["confused"]*3)
self.wait()
self.student_says(
"Isn't that pretty \\\\ technical?",
target_mode = "guilty",
added_anims = [
title.to_edge, UP,
self.get_teacher().change_mode, "plain",
self.get_teacher().look_at, self.get_students()[1].eyes
]
)
self.look_at(self.get_teacher().eyes, self.get_students())
self.wait()
self.teacher_says("", bubble_kwargs = {"stroke_width" : 0})
self.change_student_modes(
*["pondering"]*3,
look_at_arg = UP+LEFT,
added_anims = [self.get_teacher().look_at, UP+LEFT]
)
self.wait(3)
words = TextMobject(
"It's a glimpse of\\\\",
"real analysis"
)
words.set_color_by_tex("real", YELLOW)
self.teacher_says(
words,
bubble_kwargs = {"height" : 3, "width" : 6}
)
self.change_student_modes(*["happy"]*3)
self.wait(6)
class EpsilonDeltaExample(GraphLimitExpression, ZoomedScene):
CONFIG = {
"epsilon_list" : [2, 1, 0.5],
"zoomed_canvas_corner" : DOWN+RIGHT,
}
def construct(self):
self.delta_list = [
epsilon/6.0 for epsilon in self.epsilon_list
]
self.skip_superclass_anims()
self.introduce_epsilon()
self.match_epsilon()
self.zoom_in()
self.introduce_delta()
self.smaller_epsilon()
def skip_superclass_anims(self):
self.force_skipping()
GraphLimitExpression.construct(self)
self.animate_epsilon_delta_group_change(
self.ed_group,
target_delta = self.big_delta,
)
self.holes.restore()
self.add(self.holes)
self.revert_to_original_skipping_status()
def introduce_epsilon(self):
epsilon_group, small_epsilon_group = list(map(
self.get_epsilon_group,
self.epsilon_list[:2]
))
twelve_line = epsilon_group.limit_line
twelve = self.rhs[-1]
twelve_copy = twelve.copy()
twelve_copy.next_to(twelve_line)
distance = TextMobject("Distance")
distance.next_to(epsilon_group.labels, DOWN, LARGE_BUFF)
distance.to_edge(RIGHT)
arrows = VGroup(*[
Arrow(distance.get_top(), label.get_right())
for label in epsilon_group.labels
])
self.play(ShowCreation(twelve_line))
self.play(Write(twelve_copy))
self.play(ReplacementTransform(twelve_copy, twelve))
self.wait()
self.play(*it.chain(
[
ReplacementTransform(twelve_line.copy(), line)
for line in epsilon_group.epsilon_lines
],
list(map(GrowFromCenter, epsilon_group.braces)),
))
self.play(*list(map(Write, epsilon_group.labels)))
self.play(
Write(distance),
ShowCreation(arrows)
)
self.wait()
self.play(*list(map(FadeOut, [distance, arrows])))
self.play(Transform(
epsilon_group, small_epsilon_group,
run_time = 2
))
self.wait()
self.epsilon_group = epsilon_group
def match_epsilon(self):
self.animate_epsilon_delta_group_change(
self.ed_group, target_delta = self.delta_list[1],
run_time = 2,
added_anims = [
ApplyMethod(
hole.scale_in_place, 0.25,
run_time = 2
)
for hole in self.holes
]
)
self.ed_group.delta = self.delta_list[1]
self.ed_group.input_range.make_jagged()
self.wait()
def zoom_in(self):
self.ed_group.input_range.make_jagged()
self.activate_zooming()
lil_rect = self.little_rectangle
lil_rect.move_to(self.graph_origin)
lil_rect.scale_in_place(self.zoom_factor)
self.add(self.holes)
self.wait()
self.play(lil_rect.scale_in_place, 1./self.zoom_factor)
self.wait()
def introduce_delta(self):
delta_group = self.get_delta_group(self.delta_list[1])
self.play(*list(map(GrowFromCenter, delta_group.braces)))
self.play(*list(map(Write, delta_group.labels)))
self.wait()
self.play(
ReplacementTransform(
self.ed_group.input_range.copy(),
self.ed_group.output_range,
run_time = 2
),
Animation(self.holes),
)
self.play(ApplyWave(
VGroup(self.ed_group.output_range, self.holes[1]),
direction = RIGHT
))
self.wait(2)
self.delta_group = delta_group
def smaller_epsilon(self):
new_epsilon = self.epsilon_list[-1]
new_delta = self.delta_list[-1]
self.play(Transform(
self.epsilon_group,
self.get_epsilon_group(new_epsilon)
))
self.wait()
self.animate_epsilon_delta_group_change(
self.ed_group, target_delta = new_delta,
added_anims = [
Transform(
self.delta_group,
self.get_delta_group(new_delta)
)
] + [
ApplyMethod(hole.scale_in_place, 0.5)
for hole in self.holes
]
)
self.ed_group.input_range.make_jagged()
self.wait(2)
##
def get_epsilon_group(self, epsilon, limit_value = 12):
result = VGroup()
line_length = FRAME_HEIGHT
lines = [
Line(
ORIGIN, line_length*RIGHT,
).move_to(self.coords_to_point(0, limit_value+nudge))
for nudge in (0, -epsilon, epsilon)
]
result.limit_line = lines[0]
result.limit_line.set_stroke(RED, width = 3)
result.epsilon_lines = VGroup(*lines[1:])
result.epsilon_lines.set_stroke(MAROON_B, width = 2)
brace = Brace(Line(ORIGIN, 0.5*UP), RIGHT)
result.braces = VGroup(*[
brace.copy().set_height(
group.get_height()
).next_to(group, RIGHT, SMALL_BUFF)
for i in (1, 2)
for group in [VGroup(lines[0], lines[i])]
])
result.labels = VGroup(*[
brace.get_text("\\Big $\\epsilon$", buff = SMALL_BUFF)
for brace in result.braces
])
for label, brace in zip(result.labels, result.braces):
label.set_height(min(
label.get_height(),
0.8*brace.get_height()
))
result.digest_mobject_attrs()
return result
def get_delta_group(self, delta):
result = VGroup()
brace = Brace(Line(ORIGIN, RIGHT), DOWN)
brace.set_width(
(self.coords_to_point(delta, 0)-self.graph_origin)[0]
)
result.braces = VGroup(*[
brace.copy().move_to(self.coords_to_point(x, 0))
for x in (-delta/2, delta/2)
])
result.braces.shift(self.holes[0].get_height()*DOWN)
result.labels = VGroup(*[
TexMobject("\\delta").scale(
1./self.zoom_factor
)
for brace in result.braces
])
for label, brace in zip(result.labels, result.braces):
label.next_to(
brace, DOWN,
buff = SMALL_BUFF/self.zoom_factor
)
result.digest_mobject_attrs()
return result
class EpsilonDeltaCounterExample(LimitCounterExample, EpsilonDeltaExample):
def construct(self):
self.hole_radius = 0.04
self.add_func()
self.setup_axes()
self.draw_graph()
self.introduce_epsilon()
self.introduce_epsilon_delta_group()
self.move_epsilon_group_up_and_down()
def introduce_epsilon(self):
epsilon_group = self.get_epsilon_group(0.4, 1.5)
rhs = TexMobject("=0.4")
label = epsilon_group.labels[1]
rhs.next_to(label, RIGHT)
epsilon_group.add(rhs)
self.play(ShowCreation(epsilon_group.limit_line))
self.play(*it.chain(
[
ReplacementTransform(
epsilon_group.limit_line.copy(),
line
)
for line in epsilon_group.epsilon_lines
],
list(map(GrowFromCenter, epsilon_group.braces))
))
self.play(*list(map(Write, epsilon_group.labels)))
self.play(Write(rhs))
self.wait()
self.epsilon_group = epsilon_group
def introduce_epsilon_delta_group(self):
ed_group = self.get_epsilon_delta_group(self.big_delta)
self.play(*list(map(ShowCreation, ed_group.delta_lines)))
self.play(ShowCreation(ed_group.input_range))
self.play(ReplacementTransform(
ed_group.input_range.copy(),
ed_group.output_range,
run_time = 2
))
self.remove(self.graph_holes)
self.play(*list(map(GrowFromCenter, ed_group.epsilon_lines)))
self.wait(2)
self.animate_epsilon_delta_group_change(
ed_group, target_delta = self.small_delta,
run_time = 3
)
ed_group.delta = self.small_delta
self.wait()
self.ed_group = ed_group
def move_epsilon_group_up_and_down(self):
vects = [
self.coords_to_point(0, 2) - self.coords_to_point(0, 1.5),
self.coords_to_point(0, 1) - self.coords_to_point(0, 2),
self.coords_to_point(0, 1.5) - self.coords_to_point(0, 1),
]
for vect in vects:
self.play(self.epsilon_group.shift, vect)
self.wait()
self.shake_ed_group()
self.wait()
##
def shake_ed_group(self):
self.animate_epsilon_delta_group_change(
self.ed_group, target_delta = self.big_delta,
rate_func = lambda t : 0.2*there_and_back(2*t%1)
)
class TheoryHeavy(TeacherStudentsScene):
def construct(self):
lhs = TexMobject(
"{df", "\\over \\,", "dx}", "(", "x", ")"
)
equals = TexMobject("=")
rhs = TexMobject(
"\\lim", "_{h", "\\to 0}",
"{f", "(", "x", "+", "h", ")", "-", "f", "(", "x", ")",
"\\over \\,", "h}"
)
derivative = VGroup(lhs, equals, rhs)
derivative.arrange(RIGHT)
for tex_mob in derivative:
tex_mob.set_color_by_tex("x", RED)
tex_mob.set_color_by_tex("h", GREEN)
tex_mob.set_color_by_tex("dx", GREEN)
tex_mob.set_color_by_tex("f", YELLOW)
derivative.next_to(self.get_pi_creatures(), UP, buff = MED_LARGE_BUFF)
lim = rhs.get_part_by_tex("lim")
epsilon_delta = TexMobject("(\\epsilon, \\delta)")
epsilon_delta.next_to(lim, UP, buff = 1.5*LARGE_BUFF)
arrow = Arrow(epsilon_delta, lim, color = WHITE)
self.student_says(
"Too much theory!",
target_mode = "angry",
content_introduction_kwargs = {"run_time" : 2},
)
self.wait()
student = self.get_students()[1]
Scene.play(self,
Write(lhs),
FadeOut(student.bubble),
FadeOut(student.bubble.content),
*[
ApplyFunction(
lambda pi : pi.change_mode("pondering").look_at(epsilon_delta),
pi
)
for pi in self.get_pi_creatures()
]
)
student.bubble = None
part_tex_pairs = [
("df", "f"),
("over", "+"),
("over", "-"),
("over", "to"),
("over", "over"),
("dx", "h"),
("(", "("),
("x", "x"),
(")", ")"),
]
self.play(Write(equals), Write(lim), *[
ReplacementTransform(
VGroup(*lhs.get_parts_by_tex(t1)).copy(),
VGroup(*rhs.get_parts_by_tex(t2)),
run_time = 2,
rate_func = squish_rate_func(smooth, alpha, alpha+0.5)
)
for (t1, t2), alpha in zip(
part_tex_pairs,
np.linspace(0, 0.5, len(part_tex_pairs))
)
])
self.wait(2)
self.play(
Write(epsilon_delta),
ShowCreation(arrow)
)
self.wait(3)
derivative.add(epsilon_delta, arrow)
self.student_says(
"How do you \\\\ compute limits?",
student_index = 2,
added_anims = [
derivative.scale, 0.8,
derivative.to_corner, UP+LEFT
]
)
self.play(self.get_teacher().change_mode, "happy")
self.wait(2)
class LHopitalExample(LimitCounterExample, PiCreatureScene, ZoomedScene, ReconfigurableScene):
CONFIG = {
"graph_origin" : ORIGIN,
"x_axis_width" : FRAME_WIDTH,
"x_min" : -5,
"x_max" : 5,
"x_labeled_nums" : list(range(-6, 8, 2)),
"x_axis_label" : "$x$",
"y_axis_height" : FRAME_HEIGHT,
"y_min" : -3.1,
"y_max" : 3.1,
"y_bottom_tick" : -4,
"y_labeled_nums" : list(range(-2, 4, 2)),
"y_axis_label" : "",
"x_color" : RED,
"hole_radius" : 0.07,
"big_delta" : 0.5,
"small_delta" : 0.01,
"dx" : 0.06,
"dx_color" : WHITE,
"tex_scale_value" : 0.9,
"sin_color" : GREEN,
"parabola_color" : YELLOW,
"zoomed_canvas_corner" : DOWN+LEFT,
"zoom_factor" : 10,
"zoomed_canvas_frame_shape" : (5, 5),
"zoomed_canvas_corner_buff" : MED_SMALL_BUFF,
"zoomed_rect_center_coords" : (1 + 0.1, -0.03),
}
def construct(self):
self.setup_axes()
self.introduce_function()
self.show_non_definedness_at_one()
self.plug_in_value_close_to_one()
self.ask_about_systematic_process()
self.show_graph_of_numerator_and_denominator()
self.zoom_in_to_trouble_point()
self.talk_through_sizes_of_nudges()
self.show_final_ratio()
self.show_final_height()
def setup(self):
PiCreatureScene.setup(self)
ZoomedScene.setup(self)
ReconfigurableScene.setup(self)
self.remove(*self.get_pi_creatures())
def setup_axes(self):
GraphScene.setup_axes(self)
self.x_axis_label_mob.set_color(self.x_color)
def introduce_function(self):
graph = self.get_graph(self.func)
colored_graph = graph.copy().set_color(YELLOW)
func_label = self.get_func_label()
func_label.next_to(ORIGIN, RIGHT, buff = LARGE_BUFF)
func_label.to_edge(UP)
x_copy = self.x_axis_label_mob.copy()
self.play(
Write(func_label),
Transform(
x_copy, VGroup(*func_label.get_parts_by_tex("x")),
remover = True
)
)
self.play(ShowCreation(
graph,
run_time = 3,
rate_func=linear
))
self.wait(4) ## Overly oscillation
self.play(ShowCreation(colored_graph, run_time = 2))
self.wait()
self.play(ShowCreation(graph, run_time = 2))
self.remove(colored_graph)
self.wait()
self.graph = graph
self.func_label = func_label
def show_non_definedness_at_one(self):
morty = self.get_primary_pi_creature()
words = TexMobject("\\text{Try }", "x", "=1")
words.set_color_by_tex("x", self.x_color, substring = False)
v_line, alt_v_line = [
self.get_vertical_line_to_graph(
x, self.graph,
line_class = DashedLine,
color = self.x_color
)
for x in (1, -1)
]
hole, alt_hole = [
self.get_hole(x, self.func(x))
for x in (1, -1)
]
ed_group = self.get_epsilon_delta_group(
self.big_delta, limit_x = 1,
)
func_1 = self.get_func_label("1")
func_1.next_to(self.func_label, DOWN, buff = MED_LARGE_BUFF)
rhs = TexMobject("\\Rightarrow \\frac{0}{0}")
rhs.next_to(func_1, RIGHT)
func_1_group = VGroup(func_1, *rhs)
func_1_group.add_to_back(BackgroundRectangle(func_1_group))
lim = TexMobject("\\lim", "_{x", "\\to 1}")
lim.set_color_by_tex("x", self.x_color)
lim.move_to(self.func_label, LEFT)
self.func_label.generate_target()
self.func_label.target.next_to(lim, RIGHT)
equals_q = TexMobject("=", "???")
equals_q.next_to(self.func_label.target, RIGHT)
self.play(FadeIn(morty))
self.play(PiCreatureSays(morty, words))
self.play(
Blink(morty),
ShowCreation(v_line)
)
self.play(
RemovePiCreatureBubble(
morty, target_mode = "pondering",
look_at_arg = func_1
),
ReplacementTransform(
self.func_label.copy(),
func_1
)
)
self.wait(2)
self.play(Write(VGroup(*rhs[:-1])))
self.wait()
self.play(Write(rhs[-1]))
self.wait()
self.play(GrowFromCenter(hole))
self.wait()
self.play(ShowCreation(alt_v_line))
self.play(GrowFromCenter(alt_hole))
self.wait()
alt_group = VGroup(alt_v_line, alt_hole)
self.play(alt_group.set_stroke, GREY, 2)
self.play(FocusOn(hole))
self.wait()
self.play(GrowFromCenter(ed_group.input_range))
self.play(
ReplacementTransform(
ed_group.input_range.copy(),
ed_group.output_range
),
*list(map(ShowCreation, ed_group.delta_lines))
)
self.play(*list(map(GrowFromCenter, ed_group.epsilon_lines)))
self.play(morty.change_mode, "thinking")
self.animate_epsilon_delta_group_change(
ed_group, target_delta = self.small_delta,
run_time = 4
)
self.wait()
self.play(
Write(lim),
MoveToTarget(self.func_label),
Write(equals_q),
morty.change_mode, "confused",
morty.look_at, lim
)
self.wait(2)
self.play(
func_1_group.to_corner, UP+LEFT,
*list(map(FadeOut, [morty, ed_group]))
)
self.wait()
self.lim_group = VGroup(lim, self.func_label, equals_q)
for part in self.lim_group:
part.add_background_rectangle()
self.func_1_group = func_1_group
self.v_line = v_line
def plug_in_value_close_to_one(self):
num = 1.00001
result = self.func(num)
label = self.get_func_label(num)
label.add_background_rectangle()
rhs = TexMobject("= %.4f\\dots"%result)
rhs.next_to(label, RIGHT)
approx_group = VGroup(label, rhs)
approx_group.set_width(FRAME_X_RADIUS-2*MED_LARGE_BUFF)
approx_group.next_to(ORIGIN, UP, buff = MED_LARGE_BUFF)
approx_group.to_edge(RIGHT)
self.play(ReplacementTransform(
self.func_label.copy(),
label
))
self.wait()
self.play(Write(rhs))
self.wait()
self.approx_group = approx_group
def ask_about_systematic_process(self):
morty = self.pi_creature
morty.change_mode("plain")
self.func_1_group.save_state()
to_fade = VGroup(
*self.x_axis.numbers[:len(self.x_axis.numbers)/2]
)
self.play(
FadeIn(morty),
FadeOut(to_fade)
)
self.x_axis.remove(*to_fade)
self.pi_creature_says(
morty, "Is there a \\\\ better way?",
bubble_kwargs = {
"height" : 3,
"width" : 4,
},
)
self.wait(2)
self.play(
RemovePiCreatureBubble(
morty, target_mode = "raise_left_hand",
look_at_arg = self.func_1_group
),
self.func_1_group.scale, self.tex_scale_value,
self.func_1_group.move_to,
morty.get_corner(UP+LEFT), DOWN+LEFT,
self.func_1_group.shift, MED_LARGE_BUFF*UP,
)
self.wait(2)
self.play(
morty.change_mode, "raise_right_hand",
morty.look, UP+RIGHT,
FadeOut(self.approx_group),
self.func_1_group.restore,
self.lim_group.next_to,
morty.get_corner(UP+RIGHT), RIGHT,
)
self.wait(2)
self.play(
FadeOut(self.func_1_group),
self.lim_group.scale, self.tex_scale_value,
self.lim_group.to_corner, UP+LEFT,
# self.lim_group.next_to, ORIGIN, UP, MED_LARGE_BUFF,
# self.lim_group.to_edge, LEFT,
morty.change_mode, "plain"
)
self.wait()
self.play(FadeOut(morty))
def show_graph_of_numerator_and_denominator(self):
sine_graph = self.get_graph(
lambda x : np.sin(np.pi*x),
color = self.sin_color
)
sine_label = self.get_graph_label(
sine_graph, "\\sin(\\pi x)",
x_val = 4.5,
direction = UP
)
parabola = self.get_graph(
lambda x : x**2 - 1,
color = self.parabola_color
)
parabola_label = self.get_graph_label(
parabola, "x^2 - 1"
)
fader = VGroup(*[
Rectangle(
width = FRAME_WIDTH,
height = FRAME_HEIGHT,
stroke_width = 0,
fill_opacity = 0.75,
fill_color = BLACK,
).next_to(self.coords_to_point(1, 0), vect, MED_LARGE_BUFF)
for vect in (LEFT, RIGHT)
])
self.play(
ShowCreation(sine_graph, run_time = 2),
Animation(self.lim_group)
)
self.play(FadeIn(sine_label))
self.wait()
self.play(ShowCreation(parabola, run_time = 2))
self.play(FadeIn(parabola_label))
self.wait()
self.play(FadeIn(fader, run_time = 2))
self.wait()
self.play(FadeOut(fader))
self.sine_graph = sine_graph
self.sine_label = sine_label
self.parabola = parabola
self.parabola_label = parabola_label
def zoom_in_to_trouble_point(self):
self.activate_zooming()
lil_rect = self.little_rectangle
lil_rect.scale(self.zoom_factor)
lil_rect.move_to(self.coords_to_point(
*self.zoomed_rect_center_coords
))
self.wait()
self.play(lil_rect.scale_in_place, 1./self.zoom_factor)
self.wait()
def talk_through_sizes_of_nudges(self):
arrow_tip_length = 0.15/self.zoom_factor
zoom_tex_scale_factor = min(
0.75/self.zoom_factor,
1.5*self.dx
)
dx_arrow = Arrow(
self.coords_to_point(1, 0),
self.coords_to_point(1+self.dx, 0),
tip_length = arrow_tip_length,
color = WHITE,
)
dx_label = TexMobject("dx")
dx_label.scale(zoom_tex_scale_factor)
dx_label.next_to(dx_arrow, UP, buff = SMALL_BUFF/self.zoom_factor)
d_sine_arrow, d_parabola_arrow = [
Arrow(
self.coords_to_point(1+self.dx, 0),
self.coords_to_point(
1+self.dx,
graph.underlying_function(1+self.dx)
),
tip_length = arrow_tip_length,
color = graph.get_color()
)
for graph in (self.sine_graph, self.parabola)
]
tex_arrow_pairs = [
[("d\\big(", "\\sin(", "\\pi", "x", ")", "\\big)"), d_sine_arrow],
[("d\\big(", "x", "^2", "-1", "\\big)"), d_parabola_arrow],
[("\\cos(", "\\pi", "x", ")", "\\pi ", "\\, dx"), d_sine_arrow],
[("2", "x", "\\, dx"), d_parabola_arrow],
]
d_labels = []
for tex_args, arrow in tex_arrow_pairs:
label = TexMobject(*tex_args)
label.set_color_by_tex("x", self.x_color)
label.set_color_by_tex("dx", self.dx_color)
label.scale(zoom_tex_scale_factor)
label.next_to(arrow, RIGHT, buff = SMALL_BUFF/self.zoom_factor)
d_labels.append(label)
d_sine_label, d_parabola_label, cos_dx, two_x_dx = d_labels
#Show dx
self.play(ShowCreation(dx_arrow))
self.play(Write(dx_label))
self.wait()
#Show d_sine bump
point = VectorizedPoint(self.coords_to_point(1, 0))
self.play(ReplacementTransform(point, d_sine_arrow))
self.wait()
self.play(ReplacementTransform(
VGroup(dx_label[1].copy()),
d_sine_label,
run_time = 2
))
self.wait(2)
self.play(
d_sine_label.shift, d_sine_label.get_height()*UP
)
tex_pair_lists = [
[
("sin", "cos"),
("pi", "pi"),
("x", "x"),
(")", ")"),
],
[
("pi", "\\pi "), #Space there is important, though hacky
],
[
("d\\big(", "dx"),
("\\big)", "dx"),
]
]
for tex_pairs in tex_pair_lists:
self.play(*[
ReplacementTransform(
d_sine_label.get_part_by_tex(t1).copy(),
cos_dx.get_part_by_tex(t2)
)
for t1, t2 in tex_pairs
])
self.wait()
self.play(FadeOut(d_sine_label))
self.wait()
#Substitute x = 1
self.substitute_x_equals_1(cos_dx, zoom_tex_scale_factor)
#Proportionality constant
cos_pi = VGroup(*cos_dx[:-1])
cos = VGroup(*cos_dx[:-2])
brace = Brace(Line(LEFT, RIGHT), UP)
brace.set_width(cos_pi.get_width())
brace.move_to(cos_pi.get_top(), DOWN)
brace_text = TextMobject(
"""
\\begin{flushleft}
Proportionality
constant
\\end{flushleft}
"""
)
brace_text.scale(0.9*zoom_tex_scale_factor)
brace_text.add_background_rectangle()
brace_text.next_to(brace, UP, SMALL_BUFF/self.zoom_factor, LEFT)
neg_one = TexMobject("-", "1")
neg_one.add_background_rectangle()
neg_one.scale(zoom_tex_scale_factor)
self.play(GrowFromCenter(brace))
self.play(Write(brace_text))
self.wait(2)
self.play(
brace.set_width, cos.get_width(),
brace.next_to, cos, UP, SMALL_BUFF/self.zoom_factor,
FadeOut(brace_text)
)
neg_one.next_to(brace, UP, SMALL_BUFF/self.zoom_factor)
self.play(Write(neg_one))
self.wait()
self.play(FadeOut(cos))
neg = neg_one.get_part_by_tex("-").copy()
self.play(neg.next_to, cos_dx[-2], LEFT, SMALL_BUFF/self.zoom_factor)
self.play(*list(map(FadeOut, [neg_one, brace])))
neg_pi_dx = VGroup(neg, *cos_dx[-2:])
self.play(
neg_pi_dx.next_to, d_sine_arrow,
RIGHT, SMALL_BUFF/self.zoom_factor
)
self.wait()
#Show d_parabola bump
point = VectorizedPoint(self.coords_to_point(1, 0))
self.play(ReplacementTransform(point, d_parabola_arrow))
self.play(ReplacementTransform(
VGroup(dx_label[1].copy()),
d_parabola_label,
run_time = 2
))
self.wait(2)
self.play(
d_parabola_label.shift, d_parabola_label.get_height()*UP
)
tex_pair_lists = [
[
("2", "2"),
("x", "x"),
],
[
("d\\big(", "dx"),
("\\big)", "dx"),
]
]
for tex_pairs in tex_pair_lists:
self.play(*[
ReplacementTransform(
d_parabola_label.get_part_by_tex(t1).copy(),
two_x_dx.get_part_by_tex(t2)
)
for t1, t2 in tex_pairs
])
self.wait()
self.play(FadeOut(d_parabola_label))
self.wait()
#Substitute x = 1
self.substitute_x_equals_1(two_x_dx, zoom_tex_scale_factor)
def substitute_x_equals_1(self, tex_mob, zoom_tex_scale_factor):
x = tex_mob.get_part_by_tex("x")
equation = TexMobject("x", "=", "1")
eq_x, equals, one = equation
equation.scale(zoom_tex_scale_factor)
equation.next_to(
x, UP,
buff = MED_SMALL_BUFF/self.zoom_factor,
aligned_edge = LEFT
)
equation.set_color_by_tex("x", self.x_color)
equation.set_color_by_tex("1", self.x_color)
dot_one = TexMobject("\\cdot", "1")
dot_one.scale(zoom_tex_scale_factor)
dot_one.set_color(self.x_color)
dot_one.move_to(x, DOWN+LEFT)
self.play(x.move_to, eq_x)
self.wait()
self.play(
ReplacementTransform(x.copy(), eq_x),
Transform(x, one),
Write(equals)
)
self.wait()
self.play(Transform(x, dot_one))
self.wait()
self.play(*list(map(FadeOut, [eq_x, equals])))
self.wait()
def show_final_ratio(self):
lim, ratio, equals_q = self.lim_group
self.remove(self.lim_group)
self.add(*self.lim_group)
numerator = VGroup(*ratio[1][:3])
denominator = VGroup(*ratio[1][-2:])
rhs = TexMobject(
"\\approx",
"{-\\pi", "\\, dx", "\\over \\,", "2", "\\, dx}"
)
rhs.add_background_rectangle()
rhs.move_to(equals_q, LEFT)
equals = TexMobject("=")
approx = rhs.get_part_by_tex("approx")
equals.move_to(approx)
dxs = VGroup(*rhs.get_parts_by_tex("dx"))
circles = VGroup(*[
Circle(color = GREEN).replace(dx).scale_in_place(1.3)
for dx in dxs
])
#Show numerator and denominator
self.play(FocusOn(ratio))
for mob in numerator, denominator:
self.play(ApplyWave(
mob, direction = UP+RIGHT, amplitude = 0.1
))
self.wait()
self.play(ReplacementTransform(equals_q, rhs))
self.wait()
#Cancel dx's
self.play(*list(map(ShowCreation, circles)), run_time = 2)
self.wait()
self.play(dxs.fade, 0.75, FadeOut(circles))
self.wait()
#Shrink dx
self.transition_to_alt_config(
transformation_kwargs = {"run_time" : 2},
dx = self.dx/10
)
self.wait()
self.play(Transform(approx, equals))
self.play(Indicate(approx))
self.wait()
self.final_ratio = rhs
def show_final_height(self):
brace = Brace(self.v_line, LEFT)
height = brace.get_text("$\\dfrac{-\\pi}{2}$")
height.add_background_rectangle()
self.disactivate_zooming()
self.play(*list(map(FadeOut, [
self.sine_graph, self.sine_label,
self.parabola, self.parabola_label,
])) + [
Animation(self.final_ratio)
])
self.play(GrowFromCenter(brace))
self.play(Write(height))
self.wait(2)
##
def create_pi_creature(self):
self.pi_creature = Mortimer().flip().to_corner(DOWN+LEFT)
return self.pi_creature
def func(self, x):
if abs(x) != 1:
return np.sin(x*np.pi) / (x**2 - 1)
else:
return np.pi*np.cos(x*np.pi) / (2*x)
def get_func_label(self, argument = "x"):
in_tex = "{%s}"%str(argument)
result = TexMobject(
"{\\sin(\\pi ", in_tex, ")", " \\over \\,",
in_tex, "^2 - 1}"
)
result.set_color_by_tex(in_tex, self.x_color)
return result
def get_epsilon_delta_group(self, delta, **kwargs):
ed_group = GraphLimitExpression.get_epsilon_delta_group(self, delta, **kwargs)
color = ed_group.kwargs["input_range_color"]
radius = min(delta/2, self.hole_radius)
pairs = [
# (ed_group.input_range[0], (1, 0)),
(ed_group.input_range[1], (1, 0)),
# (ed_group.output_range[0], (1, self.func(1))),
(ed_group.output_range[1], (1, self.func(1))),
]
for mob, coords in pairs:
mob.add(self.get_hole(
*coords,
color = color,
radius = radius
))
return ed_group
class DerivativeLimitReciprocity(Scene):
def construct(self):
arrow = Arrow(LEFT, RIGHT, color = WHITE)
lim = TexMobject("\\lim", "_{h", "\\to 0}")
lim.set_color_by_tex("h", GREEN)
lim.next_to(arrow, LEFT)
deriv = TexMobject("{df", "\\over\\,", "dx}")
deriv.set_color_by_tex("dx", GREEN)
deriv.set_color_by_tex("df", YELLOW)
deriv.next_to(arrow, RIGHT)
self.play(FadeIn(lim, lag_ratio = 0.5))
self.play(ShowCreation(arrow))
self.play(FadeIn(deriv, lag_ratio = 0.5))
self.wait()
self.play(Rotate(arrow, np.pi, run_time = 2))
self.wait()
class GeneralLHoptial(LHopitalExample):
CONFIG = {
"f_color" : BLUE,
"g_color" : YELLOW,
"a_value" : 2.5,
"zoomed_rect_center_coords" : (2.55, 0),
"zoom_factor" : 15,
"image_height" : 3,
}
def construct(self):
self.setup_axes()
self.add_graphs()
self.zoom_in()
self.show_limit_in_symbols()
self.show_tiny_nudge()
self.show_derivative_ratio()
self.show_example()
self.show_bernoulli_and_lHopital()
def add_graphs(self):
f_graph = self.get_graph(self.f, self.f_color)
f_label = self.get_graph_label(
f_graph, "f(x)",
x_val = 3,
direction = RIGHT
)
g_graph = ParametricFunction(
lambda y : self.coords_to_point(np.exp(y)+self.a_value-1, y),
t_min = self.y_min,
t_max = self.y_max,
color = self.g_color
)
g_graph.underlying_function = self.g
g_label = self.get_graph_label(
g_graph, "g(x)", x_val = 4, direction = UP
)
a_dot = Dot(self.coords_to_point(self.a_value, 0))
a_label = TexMobject("x = a")
a_label.next_to(a_dot, UP, LARGE_BUFF)
a_arrow = Arrow(a_label.get_bottom(), a_dot, buff = SMALL_BUFF)
VGroup(a_dot, a_label, a_arrow).set_color(self.x_color)
self.play(ShowCreation(f_graph), Write(f_label))
self.play(ShowCreation(g_graph), Write(g_label))
self.wait()
self.play(
Write(a_label),
ShowCreation(a_arrow),
ShowCreation(a_dot),
)
self.wait()
self.play(*list(map(FadeOut, [a_label, a_arrow])))
self.a_dot = a_dot
self.f_graph = f_graph
self.f_label = f_label
self.g_graph = g_graph
self.g_label = g_label
def zoom_in(self):
self.activate_zooming()
lil_rect = self.little_rectangle
lil_rect.scale(self.zoom_factor)
lil_rect.move_to(self.coords_to_point(
*self.zoomed_rect_center_coords
))
self.wait()
self.play(
lil_rect.scale_in_place, 1./self.zoom_factor,
self.a_dot.scale_in_place, 1./self.zoom_factor,
run_time = 3,
)
self.wait()
def show_limit_in_symbols(self):
frac_a = self.get_frac("a", self.x_color)
frac_x = self.get_frac("x")
lim = TexMobject("\\lim", "_{x", "\\to", "a}")
lim.set_color_by_tex("a", self.x_color)
equals_zero_over_zero = TexMobject(
"=", "{\\, 0 \\,", "\\over \\,", "0 \\,}"
)
equals_q = TexMobject(*"=???")
frac_x.next_to(lim, RIGHT, SMALL_BUFF)
VGroup(lim, frac_x).to_corner(UP+LEFT)
frac_a.move_to(frac_x)
equals_zero_over_zero.next_to(frac_a, RIGHT)
equals_q.next_to(frac_a, RIGHT)
self.play(
ReplacementTransform(
VGroup(*self.f_label).copy(),
VGroup(frac_a.numerator)
),
ReplacementTransform(
VGroup(*self.g_label).copy(),
VGroup(frac_a.denominator)
),
Write(frac_a.over),
run_time = 2
)
self.wait()
self.play(Write(equals_zero_over_zero))
self.wait(2)
self.play(
ReplacementTransform(
VGroup(*frac_a.get_parts_by_tex("a")),
VGroup(lim.get_part_by_tex("a"))
)
)
self.play(Write(VGroup(*lim[:-1])))
self.play(ReplacementTransform(
VGroup(*lim.get_parts_by_tex("x")).copy(),
VGroup(*frac_x.get_parts_by_tex("x"))
))
self.play(ReplacementTransform(
equals_zero_over_zero, equals_q
))
self.wait()
self.remove(frac_a)
self.add(frac_x)
self.frac_x = frac_x
self.remove(equals_q)
self.add(*equals_q)
self.equals_q = equals_q
def show_tiny_nudge(self):
arrow_tip_length = 0.15/self.zoom_factor
zoom_tex_scale_factor = min(
0.75/self.zoom_factor,
1.5*self.dx
)
z_small_buff = SMALL_BUFF/self.zoom_factor
dx_arrow = Arrow(
self.coords_to_point(self.a_value, 0),
self.coords_to_point(self.a_value+self.dx, 0),
tip_length = arrow_tip_length,
color = WHITE,
)
dx_label = TexMobject("dx")
dx_label.scale(zoom_tex_scale_factor)
dx_label.next_to(dx_arrow, UP, buff = z_small_buff)
dx_label.shift(z_small_buff*RIGHT)
df_arrow, dg_arrow = [
Arrow(
self.coords_to_point(self.a_value+self.dx, 0),
self.coords_to_point(
self.a_value+self.dx,
graph.underlying_function(self.a_value+self.dx)
),
tip_length = arrow_tip_length,
color = graph.get_color()
)
for graph in (self.f_graph, self.g_graph)
]
v_labels = []
for char, arrow in ("f", df_arrow), ("g", dg_arrow):
label = TexMobject(
"\\frac{d%s}{dx}"%char, "(", "a", ")", "\\,dx"
)
label.scale(zoom_tex_scale_factor)
label.set_color_by_tex("a", self.x_color)
label.set_color_by_tex("frac", arrow.get_color())
label.next_to(arrow, RIGHT, z_small_buff)
v_labels.append(label)
df_label, dg_label = v_labels
self.play(ShowCreation(dx_arrow))
self.play(Write(dx_label))
self.play(Indicate(dx_label))
self.wait(2)
self.play(ShowCreation(df_arrow))
self.play(Write(df_label))
self.wait()
self.play(ShowCreation(dg_arrow))
self.play(Write(dg_label))
self.wait()
def show_derivative_ratio(self):
q_marks = VGroup(*self.equals_q[1:])
deriv_ratio = TexMobject(
"{ \\frac{df}{dx}", "(", "a", ")", "\\,dx",
"\\over \\,",
"\\frac{dg}{dx}", "(", "a", ")", "\\,dx}",
)
deriv_ratio.set_color_by_tex("a", self.x_color)
deriv_ratio.set_color_by_tex("df", self.f_color)
deriv_ratio.set_color_by_tex("dg", self.g_color)
deriv_ratio.move_to(q_marks, LEFT)
dxs = VGroup(*deriv_ratio.get_parts_by_tex("\\,dx"))
circles = VGroup(*[
Circle(color = GREEN).replace(dx).scale_in_place(1.3)
for dx in dxs
])
self.play(FadeOut(q_marks))
self.play(Write(deriv_ratio))
self.wait(2)
self.play(FadeIn(circles))
self.wait()
self.play(FadeOut(circles), dxs.fade, 0.75)
self.wait(2)
self.transition_to_alt_config(
transformation_kwargs = {"run_time" : 2},
dx = self.dx/10,
)
self.wait()
def show_example(self):
lhs = TexMobject(
"\\lim", "_{x \\to", "0}",
"{\\sin(", "x", ")", "\\over \\,", "x}",
)
rhs = TexMobject(
"=",
"{\\cos(", "0", ")", "\\over \\,", "1}",
"=", "1"
)
rhs.next_to(lhs, RIGHT)
equation = VGroup(lhs, rhs)
equation.to_corner(UP+RIGHT)
for part in equation:
part.set_color_by_tex("0", self.x_color)
brace = Brace(lhs, DOWN)
brace_text = brace.get_text("Looks like 0/0")
brace_text.add_background_rectangle()
name = TextMobject(
"``", "L'Hôpital's", " rule", "''",
arg_separator = ""
)
name.shift(FRAME_X_RADIUS*RIGHT/2)
name.to_edge(UP)
self.play(Write(lhs))
self.wait()
self.play(
GrowFromCenter(brace),
Write(brace_text)
)
self.wait()
self.play(Write(rhs[0]), ReplacementTransform(
VGroup(*lhs[3:6]).copy(),
VGroup(*rhs[1:4])
))
self.wait()
self.play(ReplacementTransform(
VGroup(*lhs[6:8]).copy(),
VGroup(*rhs[4:6]),
))
self.wait()
self.play(Write(VGroup(*rhs[6:])))
self.wait(2)
##Slide away
example = VGroup(lhs, rhs, brace, brace_text)
self.play(
example.scale, 0.7,
example.to_corner, DOWN+RIGHT, SMALL_BUFF,
path_arc = 7*np.pi/6,
)
self.play(Write(name))
self.wait(2)
self.rule_name = name
def show_bernoulli_and_lHopital(self):
lhopital_name = self.rule_name.get_part_by_tex("L'Hôpital's")
strike = Line(
lhopital_name.get_left(),
lhopital_name.get_right(),
color = RED
)
bernoulli_name = TextMobject("Bernoulli's")
bernoulli_name.next_to(lhopital_name, DOWN)
bernoulli_image = ImageMobject("Johann_Bernoulli2")
lhopital_image = ImageMobject("Guillaume_de_L'Hopital")
for image in bernoulli_image, lhopital_image:
image.set_height(self.image_height)
image.to_edge(UP)
arrow = Arrow(ORIGIN, DOWN, buff = 0, color = GREEN)
arrow.next_to(lhopital_image, DOWN, buff = SMALL_BUFF)
dollars = VGroup(*[TexMobject("\\$") for x in range(5)])
for dollar, alpha in zip(dollars, np.linspace(0, 1, len(dollars))):
angle = alpha*np.pi
dollar.move_to(np.sin(angle)*RIGHT + np.cos(angle)*UP)
dollars.set_color(GREEN)
dollars.next_to(arrow, RIGHT, MED_LARGE_BUFF)
dollars[0].set_fill(opacity = 0)
dollars.save_state()
self.play(ShowCreation(strike))
self.play(
Write(bernoulli_name),
FadeIn(bernoulli_image)
)
self.wait()
self.play(
FadeIn(lhopital_image),
bernoulli_image.next_to, arrow, DOWN, SMALL_BUFF,
ShowCreation(arrow),
FadeIn(dollars)
)
for x in range(10):
dollars.restore()
self.play(*[
Transform(*pair)
for pair in zip(dollars, dollars[1:])
] + [
FadeOut(dollars[-1])
])
####
def f(self, x):
return -0.1*(x-self.a_value)*x*(x+4.5)
def g(self, x):
return np.log(x-self.a_value+1)
def get_frac(self, input_tex, color = WHITE):
result = TexMobject(
"{f", "(", input_tex, ")", "\\over \\,",
"g", "(", input_tex, ")}"
)
result.set_color_by_tex("f", self.f_color)
result.set_color_by_tex("g", self.g_color)
result.set_color_by_tex(input_tex, color)
result.numerator = VGroup(*result[:4])
result.denominator = VGroup(*result[-4:])
result.over = result.get_part_by_tex("over")
return result
class CannotUseLHopital(TeacherStudentsScene):
def construct(self):
deriv = TexMobject(
"{d(e^x)", "\\over \\,", "dx}", "(", "x", ")", "=",
"\\lim", "_{h", "\\to 0}",
"{e^{", "x", "+", "h}",
"-", "e^", "x",
"\\over \\,", "h}"
)
deriv.to_edge(UP)
deriv.set_color_by_tex("x", RED)
deriv.set_color_by_tex("dx", GREEN)
deriv.set_color_by_tex("h", GREEN)
deriv.set_color_by_tex("e^", YELLOW)
self.play(
Write(deriv),
*it.chain(*[
[pi.change_mode, "pondering", pi.look_at, deriv]
for pi in self.get_pi_creatures()
])
)
self.wait()
self.student_says(
"Use L'Hôpital's rule!",
target_mode = "hooray"
)
self.wait()
answer = TexMobject(
"\\text{That requires knowing }",
"{d(e^x)", "\\over \\,", "dx}"
)
answer.set_color_by_tex("e^", YELLOW)
answer.set_color_by_tex("dx", GREEN)
self.teacher_says(
answer,
bubble_kwargs = {"height" : 2.5},
target_mode = "hesitant"
)
self.change_student_modes(*["confused"]*3)
self.wait(3)
class NextVideo(TeacherStudentsScene):
def construct(self):
series = VideoSeries()
series.to_edge(UP)
next_video = series[7]
brace = Brace(next_video, DOWN)
integral = TexMobject("\\int", "f(x)", "dx")
ftc = TexMobject(
"F(b)", "-", "F(a)", "=", "\\int_a^b",
"{dF", "\\over \\,", "dx}", "(x)", "dx"
)
for tex_mob in integral, ftc:
tex_mob.set_color_by_tex("dx", GREEN)
tex_mob.set_color_by_tex("f", YELLOW)
tex_mob.set_color_by_tex("F", YELLOW)
tex_mob.next_to(brace, DOWN)
self.add(series)
self.play(
GrowFromCenter(brace),
next_video.set_color, YELLOW,
self.get_teacher().change_mode, "raise_right_hand",
self.get_teacher().look_at, next_video
)
self.play(Write(integral))
self.wait(2)
self.play(*[
ReplacementTransform(
VGroup(*integral.get_parts_by_tex(p1)),
VGroup(*ftc.get_parts_by_tex(p2)),
run_time = 2,
path_arc = np.pi/2,
rate_func = squish_rate_func(smooth, alpha, alpha+0.5)
)
for alpha, (p1, p2) in zip(np.linspace(0, 0.5, 3), [
("int", "int"),
("f", "F"),
("dx", "dx"),
])
]+[
Write(VGroup(*ftc.get_parts_by_tex(part)))
for part in ("-", "=", "over", "(x)")
])
self.change_student_modes(*["pondering"]*3)
self.wait(3)
class Chapter7PatreonThanks(PatreonThanks):
CONFIG = {
"specific_patrons" : [
"Ali Yahya",
"Meshal Alshammari",
"CrypticSwarm ",
"Kaustuv DeBiswas",
"Kathryn Schmiedicke",
"Nathan Pellegrin",
"Karan Bhargava",
"Ankit Agarwal",
"Yu Jun",
"Dave Nicponski",
"Damion Kistler",
"Juan Benet",
"Othman Alikhan",
"Justin Helps",
"Markus Persson",
"Dan Buchoff",
"Derek Dai",
"Joseph John Cox",
"Luc Ritchie",
"Daan Smedinga",
"Jonathan Eppele",
"Albert Nguyen",
"Nils Schneider",
"Mustafa Mahdi",
"Mathew Bramson",
"Guido Gambardella",
"Jerry Ling",
"Mark Govea",
"Vecht",
"Shimin Kuang",
"Rish Kundalia",
"Achille Brighton",
"Ripta Pasay",
"Felipe Diniz",
]
}
class Thumbnail(Scene):
def construct(self):
lim = TexMobject("\\lim", "_{h", "\\to 0}")
lim.set_color_by_tex("h", GREEN)
lim.set_height(5)
self.add(lim)