Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
"Guiding Future STEM Leaders through Innovative Research Training" ~ thinkingbeyond.education
Image: ubuntu2204
#Summary of code
This code creates a video explaining the epsilon-delta definition of continuity. It is divided into 2 parts.
#Setting up Manim
This section installs Manim and IPython.
!sudo apt update !sudo apt install libcairo2-dev ffmpeg \ texlive texlive-latex-extra texlive-fonts-extra \ texlive-latex-recommended texlive-science \ tipa libpango1.0-dev !pip install manim==0.18.1 !pip install IPython==8.21.0
from manim import *
#Setting up colours
This section uses specific hex codes to define colours to be used later.
GREENISHWHITE = ManimColor.from_hex("#e6ebd5") NEWGREEN = ManimColor.from_hex("#93c280") NEWDARKGREEN = ManimColor.from_hex("#17282a") NEWMEDIUMGREEN = ManimColor.from_hex("#0a5936")
#Part 1
The first part of the video introduces the concept of a continuous function as a 'function you can draw without lifting your pen off the page'. It uses an animation of a straight line bending into different continuous functions to illustrate this idea.
Then, it adds more formality to this concept by introducing the idea that as 2 inputs approach each other, so should the outputs at these 2 points.
class Continuity(Scene): def construct(self): #Set the background colour. self.camera.background_color = NEWDARKGREEN #Define the axes and set the colour of the axes. axes = Axes( x_range=[-5, 6, 1], y_range=[-3, 3, 1], axis_config={"color": GREENISHWHITE} ) #Add numbers to the axes. numbered_axes = axes.add_coordinates() #Define a straight line on the axes. line = axes.plot_line_graph( x_values=[-5, 5], y_values=[1, 1], add_vertex_dots=False, line_color=NEWGREEN, ) #Define a quadratic on the axes, with the same start and end points as the line for smooth transformation. quadratic = axes.plot( lambda x: (x**2 - 25)/12+1, x_range=[-5, 5], color=NEWGREEN ) #Define a sine curve on the axes, with the same start and end points as the line for smooth transformation. sin = axes.plot( lambda x: np.sin(5*x/6), x_range=[-5, 5], color=NEWGREEN ) #Define cubic on the axes, with the same start and end points as the line for smooth transformation. cubic = axes.plot( lambda x: x*(x-5)*(x+5)/35+1, x_range=[-5, 5], color=NEWGREEN ) #Define and position the point y. point_coords = (-1.5, 1.975) point_location = axes.coords_to_point(*point_coords) y = Dot(point_location, color=NEWMEDIUMGREEN) #Define and position text describing a continuous function. text = Tex("A continuous function is often described as 'a function that you can draw without lifting your pen off the page'.", font_size=20) text.move_to([0,3.5,0]) ##Sets up a variable tracking the position of x (dot) and a function used to update its position. #The starting position of dot is at x = -3. x_tracker = ValueTracker(-3) #Defines a function for updating the position of dot. Updates the position depending on the tracking variable x_tracker and the y value at this point. def update_dot(dot): x1 = x_tracker.get_value() y1 = x1*(x1-5)*(x1+5)/35+1 dot.move_to(axes.coords_to_point(x1, y1)) #Defines dot (x) and assignes it the previously defined update function update_dot. dot = Dot(color=GREENISHWHITE) dot.add_updater(update_dot) ##Sets up a variable tracking the position of x2 (dot2) and a function used to update its position. #The starting position of dot2 is at x = -0.9. x2_tracker = ValueTracker(-0.9) #Defines a function for updating the position of dot2. Updates the position depending on the tracking variable x2_tracker and the y value at this point. def update_dot2(dot2): x2 = x2_tracker.get_value() y2 = x2*(x2-5)*(x2+5)/35+1 dot2.move_to(axes.coords_to_point(x2, y2)) #Defines dot2 (x2) and assignes it the previously defined update function update_dot2. dot2 = Dot(color=GREENISHWHITE) dot2.add_updater(update_dot2) #Defines fx, a horizontal line through dot to illustrate the output at x approaching the output at y. This line is always drawn at the y value of dot. fx = always_redraw(lambda: DashedLine( start=axes.c2p(-5, dot.get_y()), end=axes.c2p(5, dot.get_y()), color=GREENISHWHITE, )) #Defines fx, a horizontal line through dot to illustrate the output at x2 approaching the output at y. This line is always drawn at the y value of dot2. fx2 = always_redraw(lambda: DashedLine( start=axes.c2p(-5, dot2.get_y()), end=axes.c2p(5, dot2.get_y()), color=GREENISHWHITE, )) #Defines fy, a horizontal line through dot to illustrate the output at y. fy = DashedLine(start=axes.c2p(-5, 1.975), end=axes.c2p(5, 1.975), color=GREEN) ## Start the animation #Writes 'A continuous function is often described as 'a function that you can draw without lifting your pen off the page'.', then waits 1 second. self.play(Write(text)) self.wait(1) #Adds the axes and line. Then transforms the line to the quadratic, then the sine curve, then the cubic, then fades out the text. self.play(Create(numbered_axes)) self.play(Create(line)) self.play(Transform(line, quadratic)) self.play(Transform(line, sin)) self.play(Transform(line, cubic)) self.play(FadeOut(text)) #Changes the text, then writes it and waits 1 second. text = Tex("But how can we define a continuous function more precisely?", font_size=20) text.move_to([0,3.5,0]) self.play(Write(text)) self.wait(1) #Fades out the text, changes it then writes it, adds y to the scene then waits 1 second. self.play(FadeOut(text)) text = Tex("Consider a fixed point on the function, $(y, f(y))$.",font_size=20) text.move_to([0,3.5,0]) self.play(Write(text)) self.wait(0.5) self.play(Create(y)) self.wait(1) #Fades out the text, changes it then writes it, adds x to the scene and waits 1 second. self.play(FadeOut(text)) text = Tex("Now consider another variable point on the function, $(x,f(x))$.",font_size=20) text.move_to([0,3.5,0]) self.play(Write(text)) self.wait(0.5) dot.move_to(axes.coords_to_point(-3, 2.37)) self.add(dot) self.wait(1) #Fades out the text, changes the text and rewrites it. #Adds fx and fy to the scene and animates x to approach y (and therefore fx to approach fy) using x_tracker, and waits 2 seconds. self.play(FadeOut(text)) text = Tex("The function is continuous at the point $y$, if as x approaches y, f(x) approaches $f(y)$.",font_size=20) text.move_to([0,3.5,0]) self.play(Write(text)) self.add(fy,fx) self.play(x_tracker.animate.set_value(-1.5), run_time=3, rate_func=linear) self.wait(2) #Fades out the text, dot (x) and fx, replaces the text and rewrites it. #Resets the value of x_tracker and adds dot and fx back. #Also adds dot2 (x2) and fx2. #Animates dot and dot2 to approach y. #Waits 2 seconds, then fades out text then removes fx, fx2, dot and dot2. self.play(FadeOut(text, dot, fx)) text = Tex("This has to work on both sides of $y$.",font_size=20) text.move_to([0,3.5,0]) self.play(Write(text)) x_tracker = ValueTracker(-3) self.add(dot, dot2, fx, fx2) self.play(x_tracker.animate.set_value(-1.5), x2_tracker.animate.set_value(-1.5), run_time=3, rate_func=linear) self.wait(2) self.play(FadeOut(text)) self.remove(fx, fx2, dot, dot2) self.wait()
Finally, the following command is used to render the scene and save it in the subfolder 'jupyter' in the folder 'media'.
%%manim -qm -v WARNING Continuity
#Part 2
The second and final part of the animation links what has been previously shown to the formal definition of continuity, and applies this definition to another example.
class Continuity2(Scene): def construct(self): #Set the background colour. self.camera.background_color = NEWDARKGREEN axes = Axes( x_range=[-5, 6, 1], y_range=[-3, 3, 1], axis_config={"color": GREENISHWHITE} ) #Add numbers to the axes. numbered_axes = axes.add_coordinates() #Define a cubic on the axes. cubic = axes.plot( lambda x: x*(x-5)*(x+5)/35+1, x_range=[-5, 5], color=NEWGREEN ) #Define a sine curve on the axes. sin = axes.plot( lambda x: np.sin(5*x/6), x_range=[-5, 5], color=NEWGREEN ) #Define and position text. text = Tex("We have now seen that if we want $|f(x)-f(y)|<\epsilon$ for at a point $y$, we can find a $\delta$ such that if $|x-y|<\delta$ then this is true.", font_size=20) text.move_to([0,3.5,0]) #Defines fy, a horizontal line through dot to illustrate the output at y. fy = DashedLine(start=axes.c2p(-5, 1.975), end=axes.c2p(5, 1.975), color=GREEN) #Defines fx1, a horizontal line. fx1 = DashedLine(start=axes.c2p(-5, 2.25), end=axes.c2p(5, 2.25), color=GREENISHWHITE) #Defines fx2, a horizontal line. fx2 and fx1 are equidistant from fy and are on the opposite sides of fy. fx2 = DashedLine(start=axes.c2p(-5, 1.75), end=axes.c2p(5, 1.75), color=GREENISHWHITE) #Defines y. point_coords = (-1.5, 1.975) point_location = axes.coords_to_point(*point_coords) y = Dot(point_location, color=NEWMEDIUMGREEN) ## Animation starts here #Axes, the cubic, y and dy are added to the scene. Creates fx1 and fx2 and then writes the text, then waits 2 seconds. self.add(axes, cubic, y, fy) self.play(Create(fx1)) self.play(Create(fx2)) self.play(Write(text)) self.wait(2) #Fades out the text then changes and rewrites it. Waits 0.5 seconds, then fades fx1, fx2, y and fy, waits 0.1 seconds then transforms the cubic into a sin curve. self.play(FadeOut(text)) text = Tex("This is the epsilon-delta definition of a continuous function, as used in our proof. Let's look at another function to see that this definition works.", font_size=20) text.move_to([0,3.5,0]) self.play(Write(text)) self.wait(0.5) self.play(FadeOut(fx1, fx2, y, fy)) self.wait(0.1) self.play(Transform(cubic, sin)) #Redefines fx1 and fx2 to change their positions. fx1 = DashedLine(start=axes.c2p(-5, 0.75), end=axes.c2p(5, 0.75), color=GREENISHWHITE) fx2 = DashedLine(start=axes.c2p(-5, 0.25), end=axes.c2p(5, 0.25), color=GREENISHWHITE) #Redefines y and fy to change their positions. point_coords = (0.65, np.sin(5*0.65/6)) point_location = axes.coords_to_point(*point_coords) y = Dot(point_location, color=NEWMEDIUMGREEN) fy = DashedLine(start=axes.c2p(-5, np.sin(5*0.65/6)), end=axes.c2p(5, np.sin(5*0.65/6)), color=GREEN) #Defines a brace to show where 'epsilon' is measured and defines a label for the brace. brace = BraceBetweenPoints(axes.coords_to_point(5, np.sin(5*0.65/6)), axes.coords_to_point(5, 0.75)) label = brace.get_text(r"$\epsilon$") #Waits 0.5 seconds, then creates y, fy, fx1, fx2, and the brace and its label. self.wait(0.5) self.play(Create(y)) self.play(Create(fy)) self.play(Create(fx1)) self.play(Create(fx2)) self.play(Create(brace), Write(label)) #Fades out the text then replaces it. self.play(FadeOut(text)) text = Tex("If we want $|f(x) - f(y)| < \epsilon$, where $y$ is the green dot, it is clear to see that we can choose a $\delta$ such that when $|x-y| < \delta$, this is true.", font_size=20) text.move_to([0,3.5,0]) #Defines fx3, fx4 and fy2, vertical lines illustrating the position of the x values at all 3 positions focused on in the animation. fx3 = DashedLine(start=axes.c2p(1, -3), end=axes.c2p(1,3), color=GREENISHWHITE) fx4 = DashedLine(start=axes.c2p(0.25, -3), end=axes.c2p(0.25, 3), color=GREENISHWHITE) fy2 = DashedLine(start=axes.c2p(0.65, -3), end=axes.c2p(0.65, 3), color=GREEN) #Defines a second brace, brace2 to show where 'delta' is measured and defines a label for the brace. brace2 = BraceBetweenPoints(axes.coords_to_point(0.65, -3), axes.coords_to_point(1, -3)) label2 = Tex("$\delta$", font_size=30) label2.next_to(brace2, direction=DOWN) #Writes the text and creates fx3, fy2, fx4 and the second brace and its label. self.play(Write(text)) self.play(Create(fx3)) self.play(Create(fy2)) self.play(Create(fx4)) self.play(Create(brace2), Write(label2)) self.wait()
The following command is used to render the scene and save it in the subfolder 'jupyter' in the folder 'media'.
%%manim -qm -v WARNING Continuity2