3b1b-manim/old_projects/eoc/chapter7.py

2980 lines
93 KiB
Python
Raw Normal View History

2017-03-24 13:59:14 -07:00
# -*- coding: utf-8 -*-
2017-03-22 15:06:17 -07:00
from helpers import *
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 topics.common_scenes import OpeningQuote, PatreonThanks
from topics.graph_scene import *
2017-03-22 15:06:17 -07:00
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 = 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.highlight_by_tex("f", GREEN)
tex_mob.highlight_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,
2017-03-29 15:41:09 -07:00
direction = DOWN,
2017-03-22 15:06:17 -07:00
))
self.play(
GrowFromCenter(brace),
Write(tex_mob, run_time = 2)
)
self.play(
this_video.highlight, YELLOW,
2017-03-29 15:41:09 -07:00
GrowFromCenter(this_brace),
self.get_teacher().change_mode, "raise_right_hand",
self.get_teacher().look_at, this_video
2017-03-22 15:06:17 -07:00
)
self.play(Write(this_tex))
2017-03-29 15:41:09 -07:00
self.dither(2)
self.play(self.get_teacher().change_mode, "sassy")
2017-03-22 15:06:17 -07:00
self.dither(2)
class LimitJustMeansApproach(PiCreatureScene):
2017-03-22 15:06:17 -07:00
CONFIG = {
"dx_color" : GREEN,
"max_num_zeros" : 7,
2017-03-22 15:06:17 -07:00
}
def construct(self):
limit_expression = self.get_limit_expression()
limit_expression.shift(2*LEFT)
limit_expression.to_edge(UP)
2017-03-22 15:06:17 -07:00
evaluated_expressions = self.get_evaluated_expressions()
evaluated_expressions.next_to(limit_expression, DOWN, buff = LARGE_BUFF)
2017-03-22 15:06:17 -07:00
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)
2017-03-22 15:06:17 -07:00
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,
submobject_mode = "lagged_start",
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)
)
2017-03-22 15:06:17 -07:00
self.dither(0.5)
self.dither(2)
def create_pi_creature(self):
self.pi_creature = Mortimer().flip()
self.pi_creature.to_corner(DOWN+LEFT)
return self.pi_creature
2017-03-22 15:06:17 -07:00
def get_limit_expression(self):
lim = TexMobject("\\lim_", "{dx", " \\to 0}")
lim.highlight_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):
2017-03-22 15:06:17 -07:00
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_submobjects(RIGHT)
result.add(group)
return result
def get_expression(self, dx):
result = TexMobject(
"{(2 + ", str(dx), ")^3 - 2^3 \\over", str(dx)
)
result.highlight_by_tex(dx, self.dx_color)
return result
2017-03-24 13:59:14 -07:00
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.dither(2)
for goal in goals[1:]:
self.play(Transform(curr_goal, goal))
self.dither(2)
2017-03-22 15:06:17 -07:00
class RefreshOnDerivativeDefinition(GraphScene):
CONFIG = {
"start_x" : 2,
"start_dx" : 0.7,
2017-03-22 15:06:17 -07:00
"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
2017-03-22 15:06:17 -07:00
]
nudged_x_v_line.save_state()
2017-03-22 15:06:17 -07:00
ss_group = self.get_secant_slope_group(
self.start_x, graph,
dx = self.start_dx,
2017-03-22 15:06:17 -07:00
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.highlight_by_tex("df", self.df_color)
derivative.highlight_by_tex("dx", self.dx_color)
derivative.highlight_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()
2017-03-22 15:06:17 -07:00
deriv_brace = Brace(derivative)
dx_to_0 = TexMobject("dx", "\\to 0")
dx_to_0.highlight_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.dither()
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.dither()
#ss_group_development
self.play(
ShowCreation(ss_group.dx_line),
ShowCreation(ss_group.dx_label),
)
self.dither()
self.play(ShowCreation(ss_group.df_line))
self.play(Write(ss_group.df_label))
self.dither(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.dither()
#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.dither()
#Write out fuller limit
new_deriv = TexMobject(
"{f", "(", str(self.start_x), "+", "dx", ")",
"-", "f", "(", str(self.start_x), ")",
"\\over \\,", "dx"
)
new_deriv.highlight_by_tex("dx", self.dx_color)
new_deriv.highlight_by_tex("f", self.df_color)
new_deriv.highlight_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(*deriv_to_new_deriv.values()))
uncovered_new_deriv_parts = filter(
lambda part : part not in covered_new_deriv_parts,
new_deriv
)
new_deriv.move_to(derivative)
new_brace = Brace(new_deriv, DOWN)
self.animate_secant_slope_group_change(
ss_group,
target_dx = self.start_dx,
2017-03-22 15:06:17 -07:00
run_time = 2
)
self.play(ShowCreation(nudged_x_v_line))
self.dither()
self.play(*[
ReplacementTransform(*pair, run_time = 2)
for pair in 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.dither()
#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.highlight, color,
submobject_mode = "lagged_start"
)
self.dither(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.dither(2)
#Record attributes for DiscussLowercaseDs below
digest_locals(self)
class RantOpenAndClose(Scene):
def construct(self):
2017-03-24 13:59:14 -07:00
opening, closing = [
TextMobject(
start, "Rant on infinitesimals", "$>$",
arg_separator = ""
)
for start in "$<$", "$<$/"
]
self.play(FadeIn(opening))
self.dither(2)
self.play(Transform(opening, closing))
self.dither(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.dither()
for tex in "\\Delta x", "h":
dx_list_replacement = [
TexMobject(
tex
).highlight(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.dither()
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.dither()
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.highlight(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.scale_to_fit_width(rhs_rect.get_width())
formal_definition_words.next_to(rhs_rect, UP)
formal_definition_words.highlight(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.dither(2)
self.play(
ShowCreation(lhs_rect),
FadeOut(d_circles),
FadeOut(d_words),
)
self.dither(2)
self.play(
ReplacementTransform(lhs_rect, rhs_rect),
self.pi_creature.change_mode, "raise_right_hand"
)
self.dither(2)
self.play(ReplacementTransform(
df.copy(), df_target,
path_arc = -np.pi/2,
run_time = 2
))
self.dither(2)
self.play(Indicate(
VGroup(*rhs[:2]),
run_time = 2
))
self.dither()
self.play(Write(formal_definition_words))
self.play(
self.pi_creature.change_mode, "happy",
self.pi_creature.look_at, formal_definition_words
)
self.dither(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.dither()
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.highlight(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.highlight(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.dither(2)
self.play(
moving_h.next_to, self.pi_creature.get_corner(UP+RIGHT), UP,
self.pi_creature.change_mode, "raise_left_hand",
)
self.dither()
moving_dx.move_to(moving_h)
h_group.save_state()
self.play(Transform(
h_group, dx_group,
path_arc = np.pi,
))
self.dither(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.dither()
2017-03-24 13:59:14 -07:00
#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.dither()
self.add(self.rhs)
self.play(
lil_rect.scale_to_fit_width,
self.ss_group.dx_line.get_width()*4,
run_time = 4
)
self.dither()
dx = self.ss_group.dx_label
dx.save_state()
h = TexMobject("h")
h.highlight(dx.get_color())
h.replace(dx, dim_to_match = 1)
self.play(Transform(dx, h, path_arc = np.pi))
self.play(Indicate(dx))
self.dither()
self.play(dx.restore, path_arc = np.pi)
self.play(lil_rect.restore, run_time = 4)
self.dither()
self.disactivate_zooming()
2017-03-24 13:59:14 -07:00
self.dither()
#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.dither()
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 tex_to_color.items():
definition.highlight_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$", "?"),
2017-03-24 13:59:14 -07:00
("$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
)).highlight(GREEN)
for tex in "$h$", "$dx$"
]
2017-03-24 13:59:14 -07:00
#Question
self.student_says(
statements[0],
student_index = 1,
target_mode = "confused"
)
self.play(ReplacementTransform(
statements[0].h.copy(), h_group,
run_time = 2,
submobject_mode = "lagged_start",
lag_factor = 1.5,
))
self.dither()
2017-03-24 13:59:14 -07:00
#Teacher answer
self.teacher_says(
statements[1],
target_mode = "hesitant",
2017-03-24 13:59:14 -07:00
bubble_creation_class = FadeIn,
)
self.play(ReplacementTransform(
statements[1].dx.copy(), dx_group,
2017-03-24 13:59:14 -07:00
run_time = 2,
))
self.dither()
2017-03-24 13:59:14 -07:00
#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.dither()
2017-03-24 13:59:14 -07:00
#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.dither(3)
2017-03-29 15:41:09 -07:00
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_submobjects(
DOWN, buff = LARGE_BUFF, aligned_edge = LEFT
)
for goal in goals:
self.play(FadeIn(goal))
self.dither()
for i, goal in enumerate(goals):
anims = [goal.highlight, YELLOW]
if i > 0:
anims += [goals[i-1].highlight, WHITE]
self.play(*anims)
self.dither()
class GraphLimitExpression(GraphScene):
CONFIG = {
"start_x" : 2,
"h_color" : GREEN,
"f_color" : YELLOW,
2017-03-24 13:59:14 -07:00
"two_color" : RED,
"graph_origin" : 3*DOWN+LEFT,
"x_min" : -8,
"x_max" : 5,
"x_axis_label" : "$h$",
2017-03-24 13:59:14 -07:00
"x_labeled_nums" : range(-8, 6, 2),
"y_min" : 0,
"y_max" : 20,
"y_tick_frequency" : 1,
"y_labeled_nums" : range(5, 25, 5),
"y_axis_label" : "",
"big_delta" : 0.7,
"small_delta" : 0.01,
}
def construct(self):
2017-03-24 13:59:14 -07:00
self.func = lambda h : 3*(2**2) + 3*2*h + h**2
self.setup_axes()
2017-03-24 13:59:14 -07:00
self.introduce_function()
self.emphasize_non_definedness_at_0()
2017-03-24 13:59:14 -07:00
self.draw_limit_point_hole()
self.show_limit()
self.skeptic_asks()
self.show_epsilon_delta_intuition()
2017-03-24 13:59:14 -07:00
def introduce_function(self):
expression = TexMobject(
"{(", "2", "+", "h", ")", "^3",
"-", "(", "2", ")", "^3",
"\\over \\,", "h}",
arg_separator = "",
)
2017-03-24 13:59:14 -07:00
limit = TexMobject("\\lim", "_{h", "\\to 0}")
derivative = TexMobject(
"{d(x^3)", "\\over \\,", "dx}", "(", "2", ")"
)
tex_to_color = {
"h" : self.h_color,
2017-03-24 13:59:14 -07:00
"dx" : self.h_color,
"2" : self.two_color
}
2017-03-24 13:59:14 -07:00
for tex_mob in expression, limit, derivative:
for tex, color in tex_to_color.items():
2017-03-24 13:59:14 -07:00
tex_mob.highlight_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)
2017-03-29 15:41:09 -07:00
2017-03-24 13:59:14 -07:00
indices = [0, 6, 11, 13]
2017-03-29 15:41:09 -07:00
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]),
2017-03-24 13:59:14 -07:00
submobject_mode = "lagged_start",
lag_factor = 1.5
2017-03-29 15:41:09 -07:00
)]
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)
2017-03-24 13:59:14 -07:00
self.dither()
self.dither()
self.play(
MoveToTarget(expression),
FadeIn(limit, submobject_mode = "lagged_start"),
GrowFromCenter(brace)
)
self.play(Write(derivative))
self.dither(2)
self.play(
expression.restore,
*map(FadeOut, [derivative, brace, limit])
)
self.dither()
2017-03-24 13:59:14 -07:00
colored_graph = graph.copy().highlight(YELLOW)
self.play(ShowCreation(colored_graph))
self.dither()
self.play(ShowCreation(graph))
self.remove(colored_graph)
self.dither()
2017-03-24 13:59:14 -07:00
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.highlight_by_tex(tex, GREEN)
arrow = Arrow(h_equals_0.get_left(), self.graph_origin)
arrow.highlight(WHITE)
2017-03-25 12:18:53 -07:00
new_expression = expression.deepcopy()
2017-03-24 13:59:14 -07:00
h_group = VGroup(*new_expression.get_parts_by_tex("h"))
for h in h_group:
zero = TexMobject("0")
zero.highlight(h.get_color())
zero.replace(h, dim_to_match = 1)
Transform(h, zero).update(1)
rhs = TexMobject("=", "{\\, 0\\,", "\\over \\,", "0\\,}")
rhs.highlight_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(*map(ShowCreation, [arrow, dot]))
self.dither()
self.play(ReplacementTransform(
expression.copy(), new_expression
))
self.dither()
self.play(Write(rhs))
self.dither()
self.play(
GrowFromCenter(ud_brace),
Write(undefined)
)
self.dither(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.dither()
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.dither()
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)
2017-03-24 13:59:14 -07:00
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),
2017-03-24 13:59:14 -07:00
Write(brace_text),
ReplacementTransform(dot, ed_group.input_range),
)
2017-03-24 13:59:14 -07:00
self.add(h_zero_hole)
self.dither()
2017-03-24 13:59:14 -07:00
self.play(
ReplacementTransform(
2017-03-24 13:59:14 -07:00
ed_group.input_range.copy(),
ed_group.output_range,
run_time = 2
),
)
self.remove(self.limit_point_hole)
#Show approaching
self.play(*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.dither()
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.dither()
2017-03-24 13:59:14 -07:00
#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)
2017-03-24 13:59:14 -07:00
self.play(Write(limit_group))
self.dither()
self.play(twelve_copy.next_to, top_h_line, RIGHT)
self.dither()
2017-03-24 13:59:14 -07:00
self.twelve_copy = twelve_copy
2017-03-25 12:18:53 -07:00
self.rhs = rhs
2017-03-24 13:59:14 -07:00
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.dither()
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):
2017-03-24 13:59:14 -07:00
self.play(
FadeOut(self.ed_group.epsilon_lines),
FadeIn(self.input_range_brace_group)
)
self.ed_group.epsilon_lines.restore()
self.dither()
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.dither()
self.play(*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.dither()
holes = VGroup(
self.ed_group.input_range.submobjects.pop(),
self.ed_group.output_range.submobjects.pop(),
)
holes.save_state()
2017-03-24 13:59:14 -07:00
self.animate_epsilon_delta_group_change(
self.ed_group,
target_delta = self.small_delta,
2017-03-24 13:59:14 -07:00
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.dither()
self.holes = holes
2017-03-24 13:59:14 -07:00
#########
def get_epsilon_delta_group(
self,
delta,
2017-03-27 13:37:19 -07:00
limit_x = 0,
2017-03-24 13:59:14 -07:00
dashed_line_stroke_width = 3,
dashed_line_length = 2*SPACE_HEIGHT,
input_range_color = YELLOW,
input_range_stroke_width = 6,
):
kwargs = dict(locals())
result = VGroup()
kwargs.pop("self")
result.delta = kwargs.pop("delta")
2017-03-27 13:37:19 -07:00
result.limit_x = kwargs.pop("limit_x")
2017-03-24 13:59:14 -07:00
result.kwargs = kwargs
dashed_line = DashedLine(
ORIGIN, dashed_line_length*RIGHT,
stroke_width = dashed_line_stroke_width
)
2017-03-27 13:37:19 -07:00
x_values = [limit_x-delta, limit_x+delta]
2017-03-24 13:59:14 -07:00
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
])
2017-03-27 13:37:19 -07:00
if self.func(limit_x) < 0:
result.delta_lines.rotate(
np.pi, RIGHT,
about_point = result.delta_lines.get_bottom()
2017-03-24 13:59:14 -07:00
)
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 [
2017-03-27 13:37:19 -07:00
(limit_x-delta, limit_x-basically_zero),
(limit_x+basically_zero, limit_x+delta),
]
]).set_stroke(width = input_range_stroke_width)
2017-03-27 13:37:19 -07:00
for func in (lambda h : 0), self.func
]
2017-03-27 13:37:19 -07:00
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
])
2017-03-24 13:59:14 -07:00
result.digest_mobject_attrs()
return result
2017-03-24 13:59:14 -07:00
def animate_epsilon_delta_group_change(
self, epsilon_delta_group, target_delta,
2017-03-24 13:59:14 -07:00
**kwargs
):
added_anims = kwargs.get("added_anims", [])
2017-03-27 13:37:19 -07:00
limit_x = epsilon_delta_group.limit_x
start_delta = epsilon_delta_group.delta
2017-03-24 13:59:14 -07:00
ed_group_kwargs = epsilon_delta_group.kwargs
def update_ed_group(ed_group, alpha):
delta = interpolate(start_delta, target_delta, alpha)
2017-03-24 13:59:14 -07:00
new_group = self.get_epsilon_delta_group(
2017-03-27 13:37:19 -07:00
delta, limit_x = limit_x,
**ed_group_kwargs
2017-03-24 13:59:14 -07:00
)
Transform(ed_group, new_group).update(1)
return ed_group
2017-03-24 13:59:14 -07:00
self.play(
UpdateFromAlphaFunc(
epsilon_delta_group, update_ed_group,
**kwargs
),
*added_anims
)
2017-03-22 15:06:17 -07:00
class LimitCounterExample(GraphLimitExpression):
CONFIG = {
"x_min" : -8,
"x_max" : 8,
"x_labeled_nums" : range(-8, 10, 2),
"x_axis_width" : 2*SPACE_WIDTH - LARGE_BUFF,
"y_min" : -4,
"y_max" : 4,
"y_labeled_nums" : range(-2, 4, 1),
"y_axis_height" : 2*SPACE_HEIGHT+2*LARGE_BUFF,
"graph_origin" : DOWN,
"graph_color" : BLUE,
"hole_radius" : 0.075,
2017-03-27 13:37:19 -07:00
"smaller_hole_radius" : 0.04,
"big_delta" : 1.5,
"small_delta" : 0.05,
}
def construct(self):
2017-03-25 15:27:54 -07:00
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.dither()
self.play(ReplacementTransform(
left_hole.copy().set_stroke(YELLOW), right_hole
))
self.dither()
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()
2017-03-22 15:06:17 -07:00
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(*map(ShowCreation, lines))
self.play(UpdateFromAlphaFunc(
lines, update_lines,
run_time = 3
))
self.play(lines.set_stroke, GREY, 3)
self.dither()
2017-03-22 15:06:17 -07:00
self.ed_group = ed_group
2017-03-22 15:06:17 -07:00
def write_limit_not_defined(self):
limit = TexMobject(
"\\lim", "_{h", "\\to 0}", "f(", "h", ")"
)
limit.highlight_by_tex("h", GREEN)
limit.move_to(self.coords_to_point(2, 1.5))
2017-03-22 15:06:17 -07:00
words = TextMobject("is not defined")
words.highlight(RED)
words.next_to(limit, RIGHT, align_using_submobjects = True)
2017-03-22 15:06:17 -07:00
limit_group = VGroup(limit, words)
2017-03-22 15:06:17 -07:00
self.play(Write(limit))
self.dither()
self.play(Write(words))
self.dither()
self.play(limit_group.to_corner, UP+LEFT)
self.dither()
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.dither()
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.dither()
self.animate_epsilon_delta_group_change(
ed_group, target_delta = self.small_delta,
run_time = 6
)
2017-03-27 13:37:19 -07:00
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.dither()
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.dither()
#####
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.dither()
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.dither()
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.dither(3)
words = TextMobject(
"It's a glimpse of\\\\",
"real analysis"
)
words.highlight_by_tex("real", YELLOW)
self.teacher_says(
words,
bubble_kwargs = {"height" : 3, "width" : 6}
)
self.change_student_modes(*["happy"]*3)
self.dither(6)
2017-03-25 12:18:53 -07:00
class EpsilonDeltaExample(GraphLimitExpression, ZoomedScene):
CONFIG = {
"epsilon_list" : [2, 1, 0.5],
"zoomed_canvas_corner" : DOWN+RIGHT,
}
def construct(self):
2017-03-25 12:18:53 -07:00
self.delta_list = [
epsilon/6.0 for epsilon in self.epsilon_list
]
self.skip_superclass_anims()
2017-03-25 12:18:53 -07:00
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()
2017-03-25 12:18:53 -07:00
def introduce_epsilon(self):
epsilon_group, small_epsilon_group = 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.dither()
self.play(*it.chain(
[
ReplacementTransform(twelve_line.copy(), line)
for line in epsilon_group.epsilon_lines
],
map(GrowFromCenter, epsilon_group.braces),
))
self.play(*map(Write, epsilon_group.labels))
self.play(
Write(distance),
ShowCreation(arrows)
)
self.dither()
self.play(*map(FadeOut, [distance, arrows]))
self.play(Transform(
epsilon_group, small_epsilon_group,
run_time = 2
))
self.dither()
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.dither()
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.dither()
self.play(lil_rect.scale_in_place, 1./self.zoom_factor)
self.dither()
def introduce_delta(self):
delta_group = self.get_delta_group(self.delta_list[1])
self.play(*map(GrowFromCenter, delta_group.braces))
self.play(*map(Write, delta_group.labels))
self.dither()
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.dither(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.dither()
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.dither(2)
##
def get_epsilon_group(self, epsilon, limit_value = 12):
result = VGroup()
line_length = 2*SPACE_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().scale_to_fit_height(
group.get_height()
2017-03-27 13:37:19 -07:00
).next_to(group, RIGHT, SMALL_BUFF)
2017-03-25 12:18:53 -07:00
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.scale_to_fit_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.scale_to_fit_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
2017-03-25 15:27:54 -07:00
class EpsilonDeltaCounterExample(LimitCounterExample, EpsilonDeltaExample):
def construct(self):
2017-03-27 13:37:19 -07:00
self.hole_radius = 0.04
2017-03-25 15:27:54 -07:00
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
],
map(GrowFromCenter, epsilon_group.braces)
))
self.play(*map(Write, epsilon_group.labels))
self.play(Write(rhs))
self.dither()
self.epsilon_group = epsilon_group
def introduce_epsilon_delta_group(self):
ed_group = self.get_epsilon_delta_group(self.big_delta)
self.play(*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
))
2017-03-27 13:37:19 -07:00
self.remove(self.graph_holes)
2017-03-25 15:27:54 -07:00
self.play(*map(GrowFromCenter, ed_group.epsilon_lines))
self.dither(2)
self.animate_epsilon_delta_group_change(
ed_group, target_delta = self.small_delta,
run_time = 3
)
ed_group.delta = self.small_delta
self.dither()
self.ed_group = ed_group
2017-03-25 12:18:53 -07:00
2017-03-25 15:27:54 -07:00
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),
2017-03-27 13:37:19 -07:00
self.coords_to_point(0, 1.5) - self.coords_to_point(0, 1),
2017-03-25 15:27:54 -07:00
]
for vect in vects:
self.play(self.epsilon_group.shift, vect)
self.dither()
self.shake_ed_group()
self.dither()
2017-03-25 12:18:53 -07:00
2017-03-25 15:27:54 -07:00
##
2017-03-25 12:18:53 -07:00
2017-03-25 15:27:54 -07:00
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)
)
2017-03-25 12:18:53 -07:00
2017-03-27 13:37:19 -07:00
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_submobjects(RIGHT)
for tex_mob in derivative:
tex_mob.highlight_by_tex("x", RED)
tex_mob.highlight_by_tex("h", GREEN)
tex_mob.highlight_by_tex("dx", GREEN)
tex_mob.highlight_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},
2017-03-27 13:37:19 -07:00
)
self.dither()
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()
]
)
2017-03-29 15:41:09 -07:00
student.bubble = None
2017-03-27 13:37:19 -07:00
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.dither(2)
self.play(
Write(epsilon_delta),
ShowCreation(arrow)
)
self.dither(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.dither(2)
class LHopitalExample(LimitCounterExample, PiCreatureScene, ZoomedScene, ReconfigurableScene):
2017-03-27 13:37:19 -07:00
CONFIG = {
"graph_origin" : ORIGIN,
"x_axis_width" : 2*SPACE_WIDTH,
"x_min" : -5,
"x_max" : 5,
"x_labeled_nums" : range(-6, 8, 2),
"x_axis_label" : "$x$",
"y_axis_height" : 2*SPACE_HEIGHT,
"y_min" : -3.1,
"y_max" : 3.1,
"y_bottom_tick" : -4,
2017-03-27 13:37:19 -07:00
"y_labeled_nums" : 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_space_shape" : (5, 5),
"zoomed_canvas_corner_buff" : MED_SMALL_BUFF,
"zoomed_rect_center_coords" : (1 + 0.1, -0.03),
2017-03-27 13:37:19 -07:00
}
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())
2017-03-27 13:37:19 -07:00
def setup_axes(self):
GraphScene.setup_axes(self)
self.x_axis_label_mob.highlight(self.x_color)
def introduce_function(self):
graph = self.get_graph(self.func)
colored_graph = graph.copy().highlight(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 = None
))
self.dither(4) ## Overly oscillation
self.play(ShowCreation(colored_graph, run_time = 2))
self.dither()
self.play(ShowCreation(graph, run_time = 2))
self.remove(colored_graph)
self.dither()
self.graph = graph
self.func_label = func_label
def show_non_definedness_at_one(self):
morty = self.get_primary_pi_creature()
2017-03-27 13:37:19 -07:00
words = TexMobject("\\text{Try }", "x", "=1")
words.highlight_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,
2017-03-27 13:37:19 -07:00
)
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}")
2017-03-27 13:37:19 -07:00
rhs.next_to(func_1, RIGHT)
func_1_group = VGroup(func_1, *rhs)
func_1_group.add_to_back(BackgroundRectangle(func_1_group))
2017-03-27 13:37:19 -07:00
lim = TexMobject("\\lim", "_{x", "\\to 1}")
lim.highlight_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("=", "???")
2017-03-27 13:37:19 -07:00
equals_q.next_to(self.func_label.target, RIGHT)
self.play(FadeIn(morty))
2017-03-27 13:37:19 -07:00
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.dither(2)
self.play(Write(VGroup(*rhs[:-1])))
self.dither()
self.play(Write(rhs[-1]))
self.dither()
self.play(GrowFromCenter(hole))
self.dither()
self.play(ShowCreation(alt_v_line))
self.play(GrowFromCenter(alt_hole))
self.dither()
alt_group = VGroup(alt_v_line, alt_hole)
self.play(alt_group.set_stroke, GREY, 2)
self.play(FocusOn(hole))
2017-03-27 13:37:19 -07:00
self.dither()
self.play(GrowFromCenter(ed_group.input_range))
self.play(
ReplacementTransform(
ed_group.input_range.copy(),
ed_group.output_range
),
*map(ShowCreation, ed_group.delta_lines)
)
self.play(*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.dither()
2017-03-27 13:37:19 -07:00
self.play(
Write(lim),
MoveToTarget(self.func_label),
Write(equals_q),
morty.change_mode, "confused",
morty.look_at, lim
)
self.dither(2)
self.play(
func_1_group.to_corner, UP+LEFT,
2017-03-27 13:37:19 -07:00
*map(FadeOut, [morty, ed_group])
)
self.dither()
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
2017-03-27 13:37:19 -07:00
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.scale_to_fit_width(SPACE_WIDTH-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.dither()
self.play(Write(rhs))
self.dither()
self.approx_group = approx_group
2017-03-27 13:37:19 -07:00
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]
)
2017-03-25 12:18:53 -07:00
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.dither(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.dither(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.dither(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.dither()
self.play(FadeOut(morty))
2017-03-25 12:18:53 -07:00
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 = 2*SPACE_WIDTH,
height = 2*SPACE_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.dither()
self.play(ShowCreation(parabola, run_time = 2))
self.play(FadeIn(parabola_label))
self.dither()
self.play(FadeIn(fader, run_time = 2))
self.dither()
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.dither()
self.play(lil_rect.scale_in_place, 1./self.zoom_factor)
self.dither()
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.highlight_by_tex("x", self.x_color)
label.highlight_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.dither()
#Show d_sine bump
point = VectorizedPoint(self.coords_to_point(1, 0))
self.play(ReplacementTransform(point, d_sine_arrow))
self.dither()
self.play(ReplacementTransform(
VGroup(dx_label[1].copy()),
d_sine_label,
run_time = 2
))
self.dither(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.dither()
self.play(FadeOut(d_sine_label))
self.dither()
#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.scale_to_fit_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.dither(2)
self.play(
brace.scale_to_fit_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.dither()
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(*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.dither()
#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.dither(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.dither()
self.play(FadeOut(d_parabola_label))
self.dither()
#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.highlight_by_tex("x", self.x_color)
equation.highlight_by_tex("1", self.x_color)
dot_one = TexMobject("\\cdot", "1")
dot_one.scale(zoom_tex_scale_factor)
dot_one.highlight(self.x_color)
dot_one.move_to(x, DOWN+LEFT)
self.play(x.move_to, eq_x)
self.dither()
self.play(
ReplacementTransform(x.copy(), eq_x),
Transform(x, one),
Write(equals)
)
self.dither()
self.play(Transform(x, dot_one))
self.dither()
self.play(*map(FadeOut, [eq_x, equals]))
self.dither()
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.dither()
self.play(ReplacementTransform(equals_q, rhs))
self.dither()
#Cancel dx's
self.play(*map(ShowCreation, circles), run_time = 2)
self.dither()
self.play(dxs.fade, 0.75, FadeOut(circles))
self.dither()
#Shrink dx
self.transition_to_alt_config(
transformation_kwargs = {"run_time" : 2},
dx = self.dx/10
)
self.dither()
self.play(Transform(approx, equals))
self.play(Indicate(approx))
self.dither()
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(*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.dither(2)
2017-03-25 12:18:53 -07:00
2017-03-27 13:37:19 -07:00
##
2017-03-25 12:18:53 -07:00
def create_pi_creature(self):
self.pi_creature = Mortimer().flip().to_corner(DOWN+LEFT)
return self.pi_creature
2017-03-27 13:37:19 -07:00
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)
2017-03-25 12:18:53 -07:00
2017-03-27 13:37:19 -07:00
def get_func_label(self, argument = "x"):
in_tex = "{%s}"%str(argument)
result = TexMobject(
"{\\sin(\\pi ", in_tex, ")", " \\over \\,",
2017-03-27 13:37:19 -07:00
in_tex, "^2 - 1}"
)
result.highlight_by_tex(in_tex, self.x_color)
return result
2017-03-25 12:18:53 -07:00
2017-03-27 13:37:19 -07:00
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
2017-03-25 12:18:53 -07:00
class DerivativeLimitReciprocity(Scene):
def construct(self):
arrow = Arrow(LEFT, RIGHT, color = WHITE)
lim = TexMobject("\\lim", "_{h", "\\to 0}")
lim.highlight_by_tex("h", GREEN)
lim.next_to(arrow, LEFT)
deriv = TexMobject("{df", "\\over\\,", "dx}")
deriv.highlight_by_tex("dx", GREEN)
deriv.highlight_by_tex("df", YELLOW)
deriv.next_to(arrow, RIGHT)
2017-03-25 12:18:53 -07:00
self.play(FadeIn(lim, submobject_mode = "lagged_start"))
self.play(ShowCreation(arrow))
self.play(FadeIn(deriv, submobject_mode = "lagged_start"))
self.dither()
self.play(Rotate(arrow, np.pi, run_time = 2))
self.dither()
2017-03-25 12:18:53 -07:00
class GeneralLHoptial(LHopitalExample):
CONFIG = {
2017-03-29 15:41:09 -07:00
"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).highlight(self.x_color)
self.play(ShowCreation(f_graph), Write(f_label))
self.play(ShowCreation(g_graph), Write(g_label))
self.dither()
self.play(
Write(a_label),
ShowCreation(a_arrow),
ShowCreation(a_dot),
)
self.dither()
self.play(*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.dither()
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.dither()
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.highlight_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.dither()
self.play(Write(equals_zero_over_zero))
self.dither(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.dither()
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.highlight_by_tex("a", self.x_color)
label.highlight_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.dither(2)
self.play(ShowCreation(df_arrow))
self.play(Write(df_label))
self.dither()
self.play(ShowCreation(dg_arrow))
self.play(Write(dg_label))
self.dither()
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.highlight_by_tex("a", self.x_color)
deriv_ratio.highlight_by_tex("df", self.f_color)
deriv_ratio.highlight_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.dither(2)
self.play(FadeIn(circles))
self.dither()
self.play(FadeOut(circles), dxs.fade, 0.75)
self.dither(2)
self.transition_to_alt_config(
transformation_kwargs = {"run_time" : 2},
dx = self.dx/10,
)
self.dither()
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.highlight_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(SPACE_WIDTH*RIGHT/2)
name.to_edge(UP)
self.play(Write(lhs))
self.dither()
self.play(
GrowFromCenter(brace),
Write(brace_text)
)
self.dither()
self.play(Write(rhs[0]), ReplacementTransform(
VGroup(*lhs[3:6]).copy(),
VGroup(*rhs[1:4])
))
self.dither()
self.play(ReplacementTransform(
VGroup(*lhs[6:8]).copy(),
VGroup(*rhs[4:6]),
))
self.dither()
self.play(Write(VGroup(*rhs[6:])))
self.dither(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.dither(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.scale_to_fit_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.highlight(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.dither()
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.highlight_by_tex("f", self.f_color)
result.highlight_by_tex("g", self.g_color)
result.highlight_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
2017-03-29 15:41:09 -07:00
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.highlight_by_tex("x", RED)
deriv.highlight_by_tex("dx", GREEN)
deriv.highlight_by_tex("h", GREEN)
deriv.highlight_by_tex("e^", YELLOW)
2017-03-25 12:18:53 -07:00
2017-03-29 15:41:09 -07:00
self.play(
Write(deriv),
*it.chain(*[
[pi.change_mode, "pondering", pi.look_at, deriv]
for pi in self.get_pi_creatures()
])
)
self.dither()
self.student_says(
"Use L'Hôpital's rule!",
target_mode = "hooray"
)
self.dither()
answer = TexMobject(
"\\text{That requires knowing }",
"{d(e^x)", "\\over \\,", "dx}"
)
answer.highlight_by_tex("e^", YELLOW)
answer.highlight_by_tex("dx", GREEN)
self.teacher_says(
answer,
bubble_kwargs = {"height" : 2.5},
target_mode = "hesitant"
)
self.change_student_modes(*["confused"]*3)
self.dither(3)
2017-03-25 12:18:53 -07:00
2017-03-29 15:41:09 -07:00
class NextVideo(TeacherStudentsScene):
def construct(self):
series = VideoSeries()
series.to_edge(UP)
next_video = series[7]
brace = Brace(next_video, DOWN)
2017-03-25 12:18:53 -07:00
2017-03-29 15:41:09 -07:00
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.highlight_by_tex("dx", GREEN)
tex_mob.highlight_by_tex("f", YELLOW)
tex_mob.highlight_by_tex("F", YELLOW)
tex_mob.next_to(brace, DOWN)
2017-03-25 12:18:53 -07:00
2017-03-29 15:41:09 -07:00
self.add(series)
self.play(
GrowFromCenter(brace),
next_video.highlight, YELLOW,
self.get_teacher().change_mode, "raise_right_hand",
self.get_teacher().look_at, next_video
)
self.play(Write(integral))
self.dither(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.dither(3)
2017-03-22 15:06:17 -07:00
2017-03-29 15:41:09 -07:00
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",
]
}
2017-03-22 15:06:17 -07:00
2017-03-29 15:41:09 -07:00
class Thumbnail(Scene):
def construct(self):
lim = TexMobject("\\lim", "_{h", "\\to 0}")
lim.highlight_by_tex("h", GREEN)
lim.scale_to_fit_height(5)
self.add(lim)
2017-03-22 15:06:17 -07:00