3b1b-manim/eoc/chapter7.py

2028 lines
63 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 eoc.graph_scene 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 = 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,
apply_function_kwargs = {"maintain_smoothness" : False}
))
self.play(
GrowFromCenter(brace),
Write(tex_mob, run_time = 2)
)
self.dither()
self.play(self.get_teacher().change_mode, "raise_right_hand")
self.play(
this_video.highlight, YELLOW,
GrowFromCenter(this_brace)
)
self.play(Write(this_tex))
self.dither()
self.play(ShowCreation(lim_to_deriv_arrow))
self.change_student_modes(*["happy"]*3)
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)
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)
graph = self.get_graph(self.func, color = BLUE)
indices = [0, 6, 11, 13]
for i, j in zip(indices, indices[1:]):
group = VGroup(*expression[i:j])
self.play(FadeIn(
group,
submobject_mode = "lagged_start",
lag_factor = 1.5
))
self.dither()
self.play(ShowCreation(graph))
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",
write_kwargs = {"run_time" : 2},
)
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()
]
)
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):
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,
"y_max" : 3,
"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,
}
def construct(self):
self.force_skipping()
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.mention_derivatives_as_helpful()
self.nudge_by_dx()
self.show_altered_numerator_and_denominator()
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 = Mortimer().flip().to_corner(DOWN+LEFT)
words = TexMobject("\\text{Try }", "x", "=1")
words.highlight_by_tex("x", self.x_color, substring = False)
v_line = self.get_vertical_line_to_graph(
1, self.graph,
line_class = DashedLine,
color = self.x_color
)
func_1 = self.get_func_label("1")
func_1.next_to(self.func_label, DOWN, buff = MED_LARGE_BUFF)
rhs = TexMobject("= \\frac{0}{0}")
rhs.next_to(func_1, RIGHT)
func_1_group = VGroup(func_1, rhs)
hole = self.get_hole(1, self.func(1))
ed_group = self.get_epsilon_delta_group(
self.big_delta, limit_x = 1,
)
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("= ???")
equals_q.next_to(self.func_label.target, RIGHT)
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),
morty.look_at, hole
)
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.play(Blink(morty))
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, DOWN+RIGHT,
*map(FadeOut, [morty, ed_group])
)
self.dither()
def plug_in_value_close_to_one(self):
self.revert_to_original_skipping_status()
pass
def ask_about_systematic_process(self):
pass
2017-03-25 12:18:53 -07:00
2017-03-27 13:37:19 -07:00
def mention_derivatives_as_helpful(self):
pass
2017-03-25 12:18:53 -07:00
2017-03-27 13:37:19 -07:00
def nudge_by_dx(self):
pass
2017-03-25 12:18:53 -07:00
2017-03-27 13:37:19 -07:00
def show_altered_numerator_and_denominator(self):
pass
2017-03-25 12:18:53 -07:00
2017-03-27 13:37:19 -07:00
##
2017-03-25 12:18:53 -07:00
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 ",
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
2017-03-22 15:06:17 -07:00