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

3343 lines
106 KiB
Python

from big_ol_pile_of_manim_imports import *
import mpmath
mpmath.mp.dps = 7
def zeta(z):
max_norm = FRAME_X_RADIUS
try:
return np.complex(mpmath.zeta(z))
except:
return np.complex(max_norm, 0)
def d_zeta(z):
epsilon = 0.01
return (zeta(z + epsilon) - zeta(z))/epsilon
class ZetaTransformationScene(ComplexTransformationScene):
CONFIG = {
"anchor_density" : 35,
"min_added_anchors" : 10,
"max_added_anchors" : 300,
"num_anchors_to_add_per_line" : 75,
"post_transformation_stroke_width" : 2,
"default_apply_complex_function_kwargs" : {
"run_time" : 5,
},
"x_min" : 1,
"x_max" : int(FRAME_X_RADIUS+2),
"extra_lines_x_min" : -2,
"extra_lines_x_max" : 4,
"extra_lines_y_min" : -2,
"extra_lines_y_max" : 2,
}
def prepare_for_transformation(self, mob):
for line in mob.family_members_with_points():
#Find point of line cloest to 1 on C
if not isinstance(line, Line):
line.insert_n_curves(self.min_added_anchors)
continue
p1 = line.get_start()+LEFT
p2 = line.get_end()+LEFT
t = (-np.dot(p1, p2-p1))/(get_norm(p2-p1)**2)
closest_to_one = interpolate(
line.get_start(), line.get_end(), t
)
#See how big this line will become
diameter = abs(zeta(complex(*closest_to_one[:2])))
target_num_curves = np.clip(
int(self.anchor_density*np.pi*diameter),
self.min_added_anchors,
self.max_added_anchors,
)
num_curves = line.get_num_curves()
if num_curves < target_num_curves:
line.insert_n_curves(target_num_curves-num_curves)
line.make_smooth()
def add_extra_plane_lines_for_zeta(self, animate = False, **kwargs):
dense_grid = self.get_dense_grid(**kwargs)
if animate:
self.play(ShowCreation(dense_grid))
self.plane.add(dense_grid)
self.add(self.plane)
def get_dense_grid(self, step_size = 1./16):
epsilon = 0.1
x_range = np.arange(
max(self.x_min, self.extra_lines_x_min),
min(self.x_max, self.extra_lines_x_max),
step_size
)
y_range = np.arange(
max(self.y_min, self.extra_lines_y_min),
min(self.y_max, self.extra_lines_y_max),
step_size
)
vert_lines = VGroup(*[
Line(
self.y_min*UP,
self.y_max*UP,
).shift(x*RIGHT)
for x in x_range
if abs(x-1) > epsilon
])
vert_lines.set_color_by_gradient(
self.vert_start_color, self.vert_end_color
)
horiz_lines = VGroup(*[
Line(
self.x_min*RIGHT,
self.x_max*RIGHT,
).shift(y*UP)
for y in y_range
if abs(y) > epsilon
])
horiz_lines.set_color_by_gradient(
self.horiz_start_color, self.horiz_end_color
)
dense_grid = VGroup(horiz_lines, vert_lines)
dense_grid.set_stroke(width = 1)
return dense_grid
def add_reflected_plane(self, animate = False):
reflected_plane = self.get_reflected_plane()
if animate:
self.play(ShowCreation(reflected_plane, run_time = 5))
self.plane.add(reflected_plane)
self.add(self.plane)
def get_reflected_plane(self):
reflected_plane = self.plane.copy()
reflected_plane.rotate(np.pi, UP, about_point = RIGHT)
for mob in reflected_plane.family_members_with_points():
mob.set_color(
Color(rgb = 1-0.5*color_to_rgb(mob.get_color()))
)
self.prepare_for_transformation(reflected_plane)
reflected_plane.submobjects = list(reversed(
reflected_plane.family_members_with_points()
))
return reflected_plane
def apply_zeta_function(self, **kwargs):
transform_kwargs = dict(self.default_apply_complex_function_kwargs)
transform_kwargs.update(kwargs)
self.apply_complex_function(zeta, **kwargs)
class TestZetaOnHalfPlane(ZetaTransformationScene):
CONFIG = {
"anchor_density" : 15,
}
def construct(self):
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
self.prepare_for_transformation(self.plane)
print(sum([
mob.get_num_points()
for mob in self.plane.family_members_with_points()
]))
print(len(self.plane.family_members_with_points()))
self.apply_zeta_function()
self.wait()
class TestZetaOnFullPlane(ZetaTransformationScene):
def construct(self):
self.add_transformable_plane(animate = True)
self.add_extra_plane_lines_for_zeta(animate = True)
self.add_reflected_plane(animate = True)
self.apply_zeta_function()
class TestZetaOnLine(ZetaTransformationScene):
def construct(self):
line = Line(UP+20*LEFT, UP+20*RIGHT)
self.add_transformable_plane()
self.plane.submobjects = [line]
self.apply_zeta_function()
self.wait(2)
self.play(ShowCreation(line, run_time = 10))
self.wait(3)
######################
class IntroduceZeta(ZetaTransformationScene):
CONFIG = {
"default_apply_complex_function_kwargs" : {
"run_time" : 8,
}
}
def construct(self):
title = TextMobject("Riemann zeta function")
title.add_background_rectangle()
title.to_corner(UP+LEFT)
func_mob = VGroup(
TexMobject("\\zeta(s) = "),
TexMobject("\\sum_{n=1}^\\infty \\frac{1}{n^s}")
)
func_mob.arrange(RIGHT, buff = 0)
for submob in func_mob:
submob.add_background_rectangle()
func_mob.next_to(title, DOWN)
randy = Randolph().flip()
randy.to_corner(DOWN+RIGHT)
self.add_foreground_mobjects(title, func_mob)
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
self.play(ShowCreation(self.plane, run_time = 2))
reflected_plane = self.get_reflected_plane()
self.play(ShowCreation(reflected_plane, run_time = 2))
self.plane.add(reflected_plane)
self.wait()
self.apply_zeta_function()
self.wait(2)
self.play(FadeIn(randy))
self.play(
randy.change_mode, "confused",
randy.look_at, func_mob,
)
self.play(Blink(randy))
self.wait()
class WhyPeopleMayKnowIt(TeacherStudentsScene):
def construct(self):
title = TextMobject("Riemann zeta function")
title.to_corner(UP+LEFT)
func_mob = TexMobject(
"\\zeta(s) = \\sum_{n=1}^\\infty \\frac{1}{n^s}"
)
func_mob.next_to(title, DOWN, aligned_edge = LEFT)
self.add(title, func_mob)
mercenary_thought = VGroup(
TexMobject("\\$1{,}000{,}000").set_color_by_gradient(GREEN_B, GREEN_D),
TexMobject("\\zeta(s) = 0")
)
mercenary_thought.arrange(DOWN)
divergent_sum = VGroup(
TexMobject("1+2+3+4+\\cdots = -\\frac{1}{12}"),
TexMobject("\\zeta(-1) = -\\frac{1}{12}")
)
divergent_sum.arrange(DOWN)
divergent_sum[0].set_color_by_gradient(YELLOW, MAROON_B)
divergent_sum[1].set_color(BLACK)
#Thoughts
self.play(*it.chain(*[
[pi.change_mode, "pondering", pi.look_at, func_mob]
for pi in self.get_pi_creatures()
]))
self.random_blink()
self.student_thinks(
mercenary_thought, student_index = 2,
target_mode = "surprised",
)
student = self.get_students()[2]
self.random_blink()
self.wait(2)
self.student_thinks(
divergent_sum, student_index = 1,
added_anims = [student.change_mode, "plain"]
)
student = self.get_students()[1]
self.play(
student.change_mode, "confused",
student.look_at, divergent_sum,
)
self.random_blink()
self.play(*it.chain(*[
[pi.change_mode, "confused", pi.look_at, divergent_sum]
for pi in self.get_pi_creatures()
]))
self.wait()
self.random_blink()
divergent_sum[1].set_color(WHITE)
self.play(Write(divergent_sum[1]))
self.random_blink()
self.wait()
#Ask about continuation
self.student_says(
TextMobject("Can you explain \\\\" , "``analytic continuation''?"),
student_index = 1,
target_mode = "raise_right_hand"
)
self.change_student_modes(
"raise_left_hand",
"raise_right_hand",
"raise_left_hand",
)
self.play(
self.get_teacher().change_mode, "happy",
self.get_teacher().look_at, student.eyes,
)
self.random_blink()
self.wait(2)
self.random_blink()
self.wait()
class ComplexValuedFunctions(ComplexTransformationScene):
def construct(self):
title = TextMobject("Complex-valued function")
title.scale(1.5)
title.add_background_rectangle()
title.to_edge(UP)
self.add(title)
z_in = Dot(UP+RIGHT, color = YELLOW)
z_out = Dot(4*RIGHT + 2*UP, color = MAROON_B)
arrow = Arrow(z_in, z_out, buff = 0.1)
arrow.set_color(WHITE)
z = TexMobject("z").next_to(z_in, DOWN+LEFT, buff = SMALL_BUFF)
z.set_color(z_in.get_color())
f_z = TexMobject("f(z)").next_to(z_out, UP+RIGHT, buff = SMALL_BUFF)
f_z.set_color(z_out.get_color())
self.add(z_in, z)
self.wait()
self.play(ShowCreation(arrow))
self.play(
ShowCreation(z_out),
Write(f_z)
)
self.wait(2)
class PreviewZetaAndContinuation(ZetaTransformationScene):
CONFIG = {
"default_apply_complex_function_kwargs" : {
"run_time" : 4,
}
}
def construct(self):
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
reflected_plane = self.get_reflected_plane()
titles = [
TextMobject(
"What does", "%s"%s,
"look like?",
alignment = "",
)
for s in [
"$\\displaystyle \\sum_{n=1}^\\infty \\frac{1}{n^s}$",
"analytic continuation"
]
]
for mob in titles:
mob[1].set_color(YELLOW)
mob.to_corner(UP+LEFT, buff = 0.7)
mob.add_background_rectangle()
self.remove(self.plane)
self.play(Write(titles[0], run_time = 2))
self.add_foreground_mobjects(titles[0])
self.play(FadeIn(self.plane))
self.apply_zeta_function()
reflected_plane.apply_complex_function(zeta)
reflected_plane.make_smooth()
reflected_plane.set_stroke(width = 2)
self.wait()
self.play(Transform(*titles))
self.wait()
self.play(ShowCreation(
reflected_plane,
lag_ratio = 0,
run_time = 2
))
self.wait()
class AssumeKnowledgeOfComplexNumbers(ComplexTransformationScene):
def construct(self):
z = complex(5, 2)
dot = Dot(z.real*RIGHT + z.imag*UP, color = YELLOW)
line = Line(ORIGIN, dot.get_center(), color = dot.get_color())
x_line = Line(ORIGIN, z.real*RIGHT, color = GREEN_B)
y_line = Line(ORIGIN, z.imag*UP, color = RED)
y_line.shift(z.real*RIGHT)
complex_number_label = TexMobject(
"%d+%di"%(int(z.real), int(z.imag))
)
complex_number_label[0].set_color(x_line.get_color())
complex_number_label[2].set_color(y_line.get_color())
complex_number_label.next_to(dot, UP)
text = VGroup(
TextMobject("Assumed knowledge:"),
TextMobject("1) What complex numbers are."),
TextMobject("2) How to work with them."),
TextMobject("3) Maybe derivatives?"),
)
text.arrange(DOWN, aligned_edge = LEFT)
for words in text:
words.add_background_rectangle()
text[0].shift(LEFT)
text[-1].set_color(PINK)
text.to_corner(UP+LEFT)
self.play(Write(text[0]))
self.wait()
self.play(FadeIn(text[1]))
self.play(
ShowCreation(x_line),
ShowCreation(y_line),
ShowCreation(VGroup(line, dot)),
Write(complex_number_label),
)
self.play(Write(text[2]))
self.wait(2)
self.play(Write(text[3]))
self.wait()
self.play(text[3].fade)
class DefineForRealS(PiCreatureScene):
def construct(self):
zeta_def, s_group = self.get_definition("s")
self.initial_definition(zeta_def)
self.plug_in_two(zeta_def)
self.plug_in_three_and_four(zeta_def)
self.plug_in_negative_values(zeta_def)
def initial_definition(self, zeta_def):
zeta_s, sum_terms, brace, sigma = zeta_def
self.say("Let's define $\\zeta(s)$")
self.blink()
pre_zeta_s = VGroup(
*self.pi_creature.bubble.content.copy()[-4:]
)
pre_zeta_s.add(VectorizedPoint(pre_zeta_s.get_right()))
self.play(
Transform(pre_zeta_s, zeta_s),
*self.get_bubble_fade_anims()
)
self.remove(pre_zeta_s)
self.add(zeta_s)
self.wait()
for count, term in enumerate(sum_terms):
self.play(FadeIn(term), run_time = 0.5)
if count%2 == 0:
self.wait()
self.play(
GrowFromCenter(brace),
Write(sigma),
self.pi_creature.change_mode, "pondering"
)
self.wait()
def plug_in_two(self, zeta_def):
two_def = self.get_definition("2")[0]
number_line = NumberLine(
x_min = 0,
x_max = 3,
tick_frequency = 0.25,
numbers_with_elongated_ticks = list(range(4)),
unit_size = 3,
)
number_line.add_numbers()
number_line.next_to(self.pi_creature, LEFT)
number_line.to_edge(LEFT)
self.number_line = number_line
lines, braces, dots, pi_dot = self.get_sum_lines(2)
fracs = VGroup(*[
TexMobject("\\frac{1}{%d}"%((d+1)**2)).scale(0.7)
for d, brace in enumerate(braces)
])
for frac, brace, line in zip(fracs, braces, lines):
frac.set_color(line.get_color())
frac.next_to(brace, UP, buff = SMALL_BUFF)
if frac is fracs[-1]:
frac.shift(0.5*RIGHT + 0.2*UP)
arrow = Arrow(
frac.get_bottom(), brace.get_top(),
tip_length = 0.1,
buff = 0.1
)
arrow.set_color(line.get_color())
frac.add(arrow)
pi_term = TexMobject("= \\frac{\\pi^2}{6}")
pi_term.next_to(zeta_def[1], RIGHT)
pi_arrow = Arrow(
pi_term[-1].get_bottom(), pi_dot,
color = pi_dot.get_color()
)
approx = TexMobject("\\approx 1.645")
approx.next_to(pi_term)
self.play(Transform(zeta_def, two_def))
self.wait()
self.play(ShowCreation(number_line))
for frac, brace, line in zip(fracs, braces, lines):
self.play(
Write(frac),
GrowFromCenter(brace),
ShowCreation(line),
run_time = 0.7
)
self.wait(0.7)
self.wait()
self.play(
ShowCreation(VGroup(*lines[4:])),
Write(dots)
)
self.wait()
self.play(
Write(pi_term),
ShowCreation(VGroup(pi_arrow, pi_dot)),
self.pi_creature.change_mode, "hooray"
)
self.wait()
self.play(
Write(approx),
self.pi_creature.change_mode, "happy"
)
self.wait(3)
self.play(*list(map(FadeOut, [
fracs, pi_arrow, pi_dot, approx,
])))
self.lines = lines
self.braces = braces
self.dots = dots
self.final_dot = pi_dot
self.final_sum = pi_term
def plug_in_three_and_four(self, zeta_def):
final_sums = ["1.202\\dots", "\\frac{\\pi^4}{90}"]
sum_terms, brace, sigma = zeta_def[1:]
for exponent, final_sum in zip([3, 4], final_sums):
self.transition_to_new_input(zeta_def, exponent, final_sum)
self.wait()
arrow = Arrow(sum_terms.get_left(), sum_terms.get_right())
arrow.next_to(sum_terms, DOWN)
smaller_words = TextMobject("Getting smaller")
smaller_words.next_to(arrow, DOWN)
self.arrow, self.smaller_words = arrow, smaller_words
self.wait()
self.play(
ShowCreation(arrow),
Write(smaller_words)
)
self.change_mode("happy")
self.wait(2)
def plug_in_negative_values(self, zeta_def):
zeta_s, sum_terms, brace, sigma = zeta_def
arrow = self.arrow
smaller_words = self.smaller_words
bigger_words = TextMobject("Getting \\emph{bigger}?")
bigger_words.move_to(self.smaller_words)
#plug in -1
self.transition_to_new_input(zeta_def, -1, "-\\frac{1}{12}")
self.play(
Transform(self.smaller_words, bigger_words),
self.pi_creature.change_mode, "confused"
)
new_sum_terms = TexMobject(
list("1+2+3+4+") + ["\\cdots"]
)
new_sum_terms.move_to(sum_terms, LEFT)
arrow.target = arrow.copy().next_to(new_sum_terms, DOWN)
arrow.target.stretch_to_fit_width(new_sum_terms.get_width())
bigger_words.next_to(arrow.target, DOWN)
new_brace = Brace(new_sum_terms, UP)
self.play(
Transform(sum_terms, new_sum_terms),
Transform(brace, new_brace),
sigma.next_to, new_brace, UP,
MoveToTarget(arrow),
Transform(smaller_words, bigger_words),
self.final_sum.next_to, new_sum_terms, RIGHT
)
self.wait(3)
#plug in -2
new_sum_terms = TexMobject(
list("1+4+9+16+") + ["\\cdots"]
)
new_sum_terms.move_to(sum_terms, LEFT)
new_zeta_def, ignore = self.get_definition("-2")
zeta_minus_two, ignore, ignore, new_sigma = new_zeta_def
new_sigma.next_to(brace, UP)
new_final_sum = TexMobject("=0")
new_final_sum.next_to(new_sum_terms)
lines, braces, dots, final_dot = self.get_sum_lines(-2)
self.play(
Transform(zeta_s, zeta_minus_two),
Transform(sum_terms, new_sum_terms),
Transform(sigma, new_sigma),
Transform(self.final_sum, new_final_sum),
Transform(self.lines, lines),
Transform(self.braces, braces),
)
self.wait()
self.change_mode("pleading")
self.wait(2)
def get_definition(self, input_string, input_color = YELLOW):
inputs = VGroup()
num_shown_terms = 4
n_input_chars = len(input_string)
zeta_s_eq = TexMobject("\\zeta(%s) = "%input_string)
zeta_s_eq.to_edge(LEFT, buff = LARGE_BUFF)
zeta_s_eq.shift(0.5*UP)
inputs.add(*zeta_s_eq[2:2+n_input_chars])
sum_terms = TexMobject(*it.chain(*list(zip(
[
"\\frac{1}{%d^{%s}}"%(d, input_string)
for d in range(1, 1+num_shown_terms)
],
it.cycle(["+"])
))))
sum_terms.add(TexMobject("\\cdots").next_to(sum_terms))
sum_terms.next_to(zeta_s_eq, RIGHT)
for x in range(num_shown_terms):
inputs.add(*sum_terms[2*x][-n_input_chars:])
brace = Brace(sum_terms, UP)
sigma = TexMobject(
"\\sum_{n=1}^\\infty \\frac{1}{n^{%s}}"%input_string
)
sigma.next_to(brace, UP)
inputs.add(*sigma[-n_input_chars:])
inputs.set_color(input_color)
group = VGroup(zeta_s_eq, sum_terms, brace, sigma)
return group, inputs
def get_sum_lines(self, exponent, line_thickness = 6):
num_lines = 100 if exponent > 0 else 6
powers = [0] + [x**(-exponent) for x in range(1, num_lines)]
power_sums = np.cumsum(powers)
lines = VGroup(*[
Line(
self.number_line.number_to_point(s1),
self.number_line.number_to_point(s2),
)
for s1, s2 in zip(power_sums, power_sums[1:])
])
lines.set_stroke(width = line_thickness)
# VGroup(*lines[:4]).set_color_by_gradient(RED, GREEN_B)
# VGroup(*lines[4:]).set_color_by_gradient(GREEN_B, MAROON_B)
VGroup(*lines[::2]).set_color(MAROON_B)
VGroup(*lines[1::2]).set_color(RED)
braces = VGroup(*[
Brace(line, UP)
for line in lines[:4]
])
dots = TexMobject("...")
dots.stretch_to_fit_width(
0.8 * VGroup(*lines[4:]).get_width()
)
dots.next_to(braces, RIGHT, buff = SMALL_BUFF)
final_dot = Dot(
self.number_line.number_to_point(power_sums[-1]),
color = GREEN_B
)
return lines, braces, dots, final_dot
def transition_to_new_input(self, zeta_def, exponent, final_sum):
new_zeta_def = self.get_definition(str(exponent))[0]
lines, braces, dots, final_dot = self.get_sum_lines(exponent)
final_sum = TexMobject("=" + final_sum)
final_sum.next_to(new_zeta_def[1][-1])
final_sum.shift(SMALL_BUFF*UP)
self.play(
Transform(zeta_def, new_zeta_def),
Transform(self.lines, lines),
Transform(self.braces, braces),
Transform(self.dots, dots),
Transform(self.final_dot, final_dot),
Transform(self.final_sum, final_sum),
self.pi_creature.change_mode, "pondering"
)
class ReadIntoZetaFunction(Scene):
CONFIG = {
"statement" : "$\\zeta(-1) = -\\frac{1}{12}$",
"target_mode" : "frustrated",
}
def construct(self):
randy = Randolph(mode = "pondering")
randy.shift(3*LEFT+DOWN)
paper = Rectangle(width = 4, height = 5)
paper.next_to(randy, RIGHT, aligned_edge = DOWN)
paper.set_color(WHITE)
max_width = 0.8*paper.get_width()
title = TextMobject("$\\zeta(s)$ manual")
title.next_to(paper.get_top(), DOWN)
title.set_color(YELLOW)
paper.add(title)
paragraph_lines = VGroup(
Line(LEFT, RIGHT),
Line(LEFT, RIGHT).shift(0.2*DOWN),
Line(LEFT, ORIGIN).shift(0.4*DOWN)
)
paragraph_lines.set_width(max_width)
paragraph_lines.next_to(title, DOWN, MED_LARGE_BUFF)
paper.add(paragraph_lines)
max_height = 1.5*paragraph_lines.get_height()
statement = TextMobject(self.statement)
if statement.get_width() > max_width:
statement.set_width(max_width)
if statement.get_height() > max_height:
statement.set_height(max_height)
statement.next_to(paragraph_lines, DOWN)
statement.set_color(GREEN_B)
paper.add(paragraph_lines.copy().next_to(statement, DOWN, MED_LARGE_BUFF))
randy.look_at(statement)
self.add(randy, paper)
self.play(Write(statement))
self.play(
randy.change_mode, self.target_mode,
randy.look_at, title
)
self.play(Blink(randy))
self.play(randy.look_at, statement)
self.wait()
class ReadIntoZetaFunctionTrivialZero(ReadIntoZetaFunction):
CONFIG = {
"statement" : "$\\zeta(-2n) = 0$"
}
class ReadIntoZetaFunctionAnalyticContinuation(ReadIntoZetaFunction):
CONFIG = {
"statement" : "...analytic \\\\ continuation...",
"target_mode" : "confused",
}
class IgnoreNegatives(TeacherStudentsScene):
def construct(self):
definition = TexMobject("""
\\zeta(s) = \\sum_{n=1}^{\\infty} \\frac{1}{n^s}
""")
VGroup(definition[2], definition[-1]).set_color(YELLOW)
definition.to_corner(UP+LEFT)
self.add(definition)
brace = Brace(definition, DOWN)
only_s_gt_1 = brace.get_text("""
Only defined
for $s > 1$
""")
only_s_gt_1[-3].set_color(YELLOW)
self.change_student_modes(*["confused"]*3)
words = TextMobject(
"Ignore $s \\le 1$ \\dots \\\\",
"For now."
)
words[0][6].set_color(YELLOW)
words[1].set_color(BLACK)
self.teacher_says(words)
self.play(words[1].set_color, WHITE)
self.change_student_modes(*["happy"]*3)
self.play(
GrowFromCenter(brace),
Write(only_s_gt_1),
*it.chain(*[
[pi.look_at, definition]
for pi in self.get_pi_creatures()
])
)
self.random_blink(3)
class RiemannFatherOfComplex(ComplexTransformationScene):
def construct(self):
name = TextMobject(
"Bernhard Riemann $\\rightarrow$ Complex analysis"
)
name.to_corner(UP+LEFT)
name.shift(0.25*DOWN)
name.add_background_rectangle()
# photo = Square()
photo = ImageMobject("Riemann", invert = False)
photo.set_width(5)
photo.next_to(name, DOWN, aligned_edge = LEFT)
self.add(photo)
self.play(Write(name))
self.wait()
input_dot = Dot(2*RIGHT+UP, color = YELLOW)
arc = Arc(-2*np.pi/3)
arc.rotate(-np.pi)
arc.add_tip()
arc.shift(input_dot.get_top()-arc.points[0]+SMALL_BUFF*UP)
output_dot = Dot(
arc.points[-1] + SMALL_BUFF*(2*RIGHT+DOWN),
color = MAROON_B
)
for dot, tex in (input_dot, "z"), (output_dot, "f(z)"):
dot.label = TexMobject(tex)
dot.label.add_background_rectangle()
dot.label.next_to(dot, DOWN+RIGHT, buff = SMALL_BUFF)
dot.label.set_color(dot.get_color())
self.play(
ShowCreation(input_dot),
Write(input_dot.label)
)
self.play(ShowCreation(arc))
self.play(
ShowCreation(output_dot),
Write(output_dot.label)
)
self.wait()
class FromRealToComplex(ComplexTransformationScene):
CONFIG = {
"plane_config" : {
"space_unit_to_x_unit" : 2,
"space_unit_to_y_unit" : 2,
},
"background_label_scale_val" : 0.7,
"output_color" : GREEN_B,
"num_lines_in_spiril_sum" : 1000,
}
def construct(self):
self.handle_background()
self.show_real_to_real()
self.transition_to_complex()
self.single_out_complex_exponent()
##Fade to several scenes defined below
self.show_s_equals_two_lines()
self.transition_to_spiril_sum()
self.vary_complex_input()
self.show_domain_of_convergence()
self.ask_about_visualizing_all()
def handle_background(self):
self.remove(self.background)
#Oh yeah, this is great practice...
self.background[-1].remove(*self.background[-1][-3:])
def show_real_to_real(self):
zeta = self.get_zeta_definition("2", "\\frac{\\pi^2}{6}")
number_line = NumberLine(
unit_size = 2,
tick_frequency = 0.5,
numbers_with_elongated_ticks = list(range(-2, 3))
)
number_line.add_numbers()
input_dot = Dot(number_line.number_to_point(2))
input_dot.set_color(YELLOW)
output_dot = Dot(number_line.number_to_point(np.pi**2/6))
output_dot.set_color(self.output_color)
arc = Arc(
2*np.pi/3, start_angle = np.pi/6,
)
arc.stretch_to_fit_width(
(input_dot.get_center()-output_dot.get_center())[0]
)
arc.stretch_to_fit_height(0.5)
arc.next_to(input_dot.get_center(), UP, aligned_edge = RIGHT)
arc.add_tip()
two = zeta[1][2].copy()
sum_term = zeta[-1]
self.add(number_line, *zeta[:-1])
self.wait()
self.play(Transform(two, input_dot))
self.remove(two)
self.add(input_dot)
self.play(ShowCreation(arc))
self.play(ShowCreation(output_dot))
self.play(Transform(output_dot.copy(), sum_term))
self.remove(*self.get_mobjects_from_last_animation())
self.add(sum_term)
self.wait(2)
self.play(
ShowCreation(
self.background,
run_time = 2
),
FadeOut(VGroup(arc, output_dot, number_line)),
Animation(zeta),
Animation(input_dot)
)
self.wait(2)
self.zeta = zeta
self.input_dot = input_dot
def transition_to_complex(self):
complex_zeta = self.get_zeta_definition("2+i", "???")
input_dot = self.input_dot
input_dot.generate_target()
input_dot.target.move_to(
self.background.num_pair_to_point((2, 1))
)
input_label = TexMobject("2+i")
input_label.set_color(YELLOW)
input_label.next_to(input_dot.target, DOWN+RIGHT, buff = SMALL_BUFF)
input_label.add_background_rectangle()
input_label.save_state()
input_label.replace(VGroup(*complex_zeta[1][2:5]))
input_label.background_rectangle.scale_in_place(0.01)
self.input_label = input_label
self.play(Transform(self.zeta, complex_zeta))
self.wait()
self.play(
input_label.restore,
MoveToTarget(input_dot)
)
self.wait(2)
def single_out_complex_exponent(self):
frac_scale_factor = 1.2
randy = Randolph()
randy.to_corner()
bubble = randy.get_bubble(height = 4)
bubble.set_fill(BLACK, opacity = 1)
frac = VGroup(
VectorizedPoint(self.zeta[2][3].get_left()),
self.zeta[2][3],
VectorizedPoint(self.zeta[2][3].get_right()),
self.zeta[2][4],
).copy()
frac.generate_target()
frac.target.scale(frac_scale_factor)
bubble.add_content(frac.target)
new_frac = TexMobject(
"\\Big(", "\\frac{1}{2}", "\\Big)", "^{2+i}"
)
new_frac[-1].set_color(YELLOW)
new_frac.scale(frac_scale_factor)
new_frac.move_to(frac.target)
new_frac.shift(LEFT+0.2*UP)
words = TextMobject("Not repeated \\\\", " multiplication")
words.scale(0.8)
words.set_color(RED)
words.next_to(new_frac, RIGHT)
new_words = TextMobject("Not \\emph{super} \\\\", "crucial to know...")
new_words.replace(words)
new_words.scale_in_place(1.3)
self.play(FadeIn(randy))
self.play(
randy.change_mode, "confused",
randy.look_at, bubble,
ShowCreation(bubble),
MoveToTarget(frac)
)
self.play(Blink(randy))
self.play(Transform(frac, new_frac))
self.play(Write(words))
for x in range(2):
self.wait(2)
self.play(Blink(randy))
self.play(
Transform(words, new_words),
randy.change_mode, "maybe"
)
self.wait()
self.play(Blink(randy))
self.play(randy.change_mode, "happy")
self.wait()
self.play(*list(map(FadeOut, [randy, bubble, frac, words])))
def show_s_equals_two_lines(self):
self.input_label.save_state()
zeta = self.get_zeta_definition("2", "\\frac{\\pi^2}{6}")
lines, output_dot = self.get_sum_lines(2)
sum_terms = self.zeta[2][:-1:3]
dots_copy = zeta[2][-1].copy()
pi_copy = zeta[3].copy()
def transform_and_replace(m1, m2):
self.play(Transform(m1, m2))
self.remove(m1)
self.add(m2)
self.play(
self.input_dot.shift, 2*DOWN,
self.input_label.fade, 0.7,
)
self.play(Transform(self.zeta, zeta))
for term, line in zip(sum_terms, lines):
line.save_state()
line.next_to(term, DOWN)
term_copy = term.copy()
transform_and_replace(term_copy, line)
self.play(line.restore)
later_lines = VGroup(*lines[4:])
transform_and_replace(dots_copy, later_lines)
self.wait()
transform_and_replace(pi_copy, output_dot)
self.wait()
self.lines = lines
self.output_dot = output_dot
def transition_to_spiril_sum(self):
zeta = self.get_zeta_definition("2+i", "1.15 - 0.44i")
zeta.set_width(FRAME_WIDTH-1)
zeta.to_corner(UP+LEFT)
lines, output_dot = self.get_sum_lines(complex(2, 1))
self.play(
self.input_dot.shift, 2*UP,
self.input_label.restore,
)
self.wait()
self.play(Transform(self.zeta, zeta))
self.wait()
self.play(
Transform(self.lines, lines),
Transform(self.output_dot, output_dot),
run_time = 2,
path_arc = -np.pi/6,
)
self.wait()
def vary_complex_input(self):
zeta = self.get_zeta_definition("s", "")
zeta[3].set_color(BLACK)
self.play(Transform(self.zeta, zeta))
self.play(FadeOut(self.input_label))
self.wait(2)
inputs = [
complex(1.5, 1.8),
complex(1.5, -1),
complex(3, -1),
complex(1.5, 1.8),
complex(1.5, -1.8),
complex(1.4, -1.8),
complex(1.5, 0),
complex(2, 1),
]
for s in inputs:
input_point = self.z_to_point(s)
lines, output_dot = self.get_sum_lines(s)
self.play(
self.input_dot.move_to, input_point,
Transform(self.lines, lines),
Transform(self.output_dot, output_dot),
run_time = 2
)
self.wait()
self.wait()
def show_domain_of_convergence(self, opacity = 0.2):
domain = Rectangle(
width = FRAME_X_RADIUS-2,
height = FRAME_HEIGHT,
stroke_width = 0,
fill_color = YELLOW,
fill_opacity = opacity,
)
domain.to_edge(RIGHT, buff = 0)
anti_domain = Rectangle(
width = FRAME_X_RADIUS+2,
height = FRAME_HEIGHT,
stroke_width = 0,
fill_color = RED,
fill_opacity = opacity,
)
anti_domain.to_edge(LEFT, buff = 0)
domain_words = TextMobject("""
$\\zeta(s)$ happily
converges and
makes sense
""")
domain_words.to_corner(UP+RIGHT, buff = MED_LARGE_BUFF)
anti_domain_words = TextMobject("""
Not so much...
""")
anti_domain_words.next_to(ORIGIN, LEFT, buff = LARGE_BUFF)
anti_domain_words.shift(1.5*DOWN)
self.play(FadeIn(domain))
self.play(Write(domain_words))
self.wait()
self.play(FadeIn(anti_domain))
self.play(Write(anti_domain_words))
self.wait(2)
self.play(*list(map(FadeOut, [
anti_domain, anti_domain_words,
])))
self.domain_words = domain_words
def ask_about_visualizing_all(self):
morty = Mortimer().flip()
morty.scale(0.7)
morty.to_corner(DOWN+LEFT)
bubble = morty.get_bubble(SpeechBubble, height = 4)
bubble.set_fill(BLACK, opacity = 0.5)
bubble.write("""
How can we visualize
this for all inputs?
""")
self.play(FadeIn(morty))
self.play(
morty.change_mode, "speaking",
ShowCreation(bubble),
Write(bubble.content)
)
self.play(Blink(morty))
self.wait(3)
self.play(
morty.change_mode, "pondering",
morty.look_at, self.input_dot,
*list(map(FadeOut, [
bubble, bubble.content, self.domain_words
]))
)
arrow = Arrow(self.input_dot, self.output_dot, buff = SMALL_BUFF)
arrow.set_color(WHITE)
self.play(ShowCreation(arrow))
self.play(Blink(morty))
self.wait()
def get_zeta_definition(self, input_string, output_string, input_color = YELLOW):
inputs = VGroup()
num_shown_terms = 4
n_input_chars = len(input_string)
zeta_s_eq = TexMobject("\\zeta(%s) = "%input_string)
zeta_s_eq.to_edge(LEFT, buff = LARGE_BUFF)
zeta_s_eq.shift(0.5*UP)
inputs.add(*zeta_s_eq[2:2+n_input_chars])
raw_sum_terms = TexMobject(*[
"\\frac{1}{%d^{%s}} + "%(d, input_string)
for d in range(1, 1+num_shown_terms)
])
sum_terms = VGroup(*it.chain(*[
[
VGroup(*term[:3]),
VGroup(*term[3:-1]),
term[-1],
]
for term in raw_sum_terms
]))
sum_terms.add(TexMobject("\\cdots").next_to(sum_terms[-1]))
sum_terms.next_to(zeta_s_eq, RIGHT)
for x in range(num_shown_terms):
inputs.add(*sum_terms[3*x+1])
output = TexMobject("= \\," + output_string)
output.next_to(sum_terms, RIGHT)
output.set_color(self.output_color)
inputs.set_color(input_color)
group = VGroup(zeta_s_eq, sum_terms, output)
group.to_edge(UP)
group.add_to_back(BackgroundRectangle(group))
return group
def get_sum_lines(self, exponent, line_thickness = 6):
powers = [0] + [
x**(-exponent)
for x in range(1, self.num_lines_in_spiril_sum)
]
power_sums = np.cumsum(powers)
lines = VGroup(*[
Line(*list(map(self.z_to_point, z_pair)))
for z_pair in zip(power_sums, power_sums[1:])
])
widths = np.linspace(line_thickness, 0, len(list(lines)))
for line, width in zip(lines, widths):
line.set_stroke(width = width)
VGroup(*lines[::2]).set_color(MAROON_B)
VGroup(*lines[1::2]).set_color(RED)
final_dot = Dot(
# self.z_to_point(power_sums[-1]),
self.z_to_point(zeta(exponent)),
color = self.output_color
)
return lines, final_dot
class TerritoryOfExponents(ComplexTransformationScene):
def construct(self):
self.add_title()
familiar_territory = TextMobject("Familiar territory")
familiar_territory.set_color(YELLOW)
familiar_territory.next_to(ORIGIN, UP+RIGHT)
familiar_territory.shift(2*UP)
real_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
real_line.set_color(YELLOW)
arrow1 = Arrow(familiar_territory.get_bottom(), real_line.get_left())
arrow2 = Arrow(familiar_territory.get_bottom(), real_line.get_right())
VGroup(arrow1, arrow2).set_color(WHITE)
extended_realm = TextMobject("Extended realm")
extended_realm.move_to(familiar_territory)
full_plane = Rectangle(
width = FRAME_WIDTH,
height = FRAME_HEIGHT,
fill_color = YELLOW,
fill_opacity = 0.3
)
self.add(familiar_territory)
self.play(ShowCreation(arrow1))
self.play(
Transform(arrow1, arrow2),
ShowCreation(real_line)
)
self.play(FadeOut(arrow1))
self.play(
FadeIn(full_plane),
Transform(familiar_territory, extended_realm),
Animation(real_line)
)
def add_title(self):
exponent = TexMobject(
"\\left(\\frac{1}{2}\\right)^s"
)
exponent[-1].set_color(YELLOW)
exponent.next_to(ORIGIN, LEFT, MED_LARGE_BUFF).to_edge(UP)
self.add_foreground_mobjects(exponent)
class ComplexExponentiation(Scene):
def construct(self):
self.extract_pure_imaginary_part()
self.add_on_planes()
self.show_imaginary_powers()
def extract_pure_imaginary_part(self):
original = TexMobject(
"\\left(\\frac{1}{2}\\right)", "^{2+i}"
)
split = TexMobject(
"\\left(\\frac{1}{2}\\right)", "^{2}",
"\\left(\\frac{1}{2}\\right)", "^{i}",
)
VGroup(original[-1], split[1], split[3]).set_color(YELLOW)
VGroup(original, split).shift(UP)
real_part = VGroup(*split[:2])
imag_part = VGroup(*split[2:])
brace = Brace(real_part)
we_understand = brace.get_text(
"We understand this"
)
VGroup(brace, we_understand).set_color(GREEN_B)
self.add(original)
self.wait()
self.play(*[
Transform(*pair)
for pair in [
(original[0], split[0]),
(original[1][0], split[1]),
(original[0].copy(), split[2]),
(VGroup(*original[1][1:]), split[3]),
]
])
self.remove(*self.get_mobjects_from_last_animation())
self.add(real_part, imag_part)
self.wait()
self.play(
GrowFromCenter(brace),
FadeIn(we_understand),
real_part.set_color, GREEN_B
)
self.wait()
self.play(
imag_part.move_to, imag_part.get_left(),
*list(map(FadeOut, [brace, we_understand, real_part]))
)
self.wait()
self.imag_exponent = imag_part
def add_on_planes(self):
left_plane = NumberPlane(x_radius = (FRAME_X_RADIUS-1)/2)
left_plane.to_edge(LEFT, buff = 0)
imag_line = Line(DOWN, UP).scale(FRAME_Y_RADIUS)
imag_line.set_color(YELLOW).fade(0.3)
imag_line.move_to(left_plane.get_center())
left_plane.add(imag_line)
left_title = TextMobject("Input space")
left_title.add_background_rectangle()
left_title.set_color(YELLOW)
left_title.next_to(left_plane.get_top(), DOWN)
right_plane = NumberPlane(x_radius = (FRAME_X_RADIUS-1)/2)
right_plane.to_edge(RIGHT, buff = 0)
unit_circle = Circle()
unit_circle.set_color(MAROON_B).fade(0.3)
unit_circle.shift(right_plane.get_center())
right_plane.add(unit_circle)
right_title = TextMobject("Output space")
right_title.add_background_rectangle()
right_title.set_color(MAROON_B)
right_title.next_to(right_plane.get_top(), DOWN)
for plane in left_plane, right_plane:
labels = VGroup()
for x in range(-2, 3):
label = TexMobject(str(x))
label.move_to(plane.num_pair_to_point((x, 0)))
labels.add(label)
for y in range(-3, 4):
if y == 0:
continue
label = TexMobject(str(y) + "i")
label.move_to(plane.num_pair_to_point((0, y)))
labels.add(label)
for label in labels:
label.scale_in_place(0.5)
label.next_to(
label.get_center(), DOWN+RIGHT,
buff = SMALL_BUFF
)
plane.add(labels)
arrow = Arrow(LEFT, RIGHT)
self.play(
ShowCreation(left_plane),
Write(left_title),
run_time = 3
)
self.play(
ShowCreation(right_plane),
Write(right_title),
run_time = 3
)
self.play(ShowCreation(arrow))
self.wait()
self.left_plane = left_plane
self.right_plane = right_plane
def show_imaginary_powers(self):
i = complex(0, 1)
input_dot = Dot(self.z_to_point(i))
input_dot.set_color(YELLOW)
output_dot = Dot(self.z_to_point(0.5**(i), is_input = False))
output_dot.set_color(MAROON_B)
output_dot.save_state()
output_dot.move_to(input_dot)
output_dot.set_color(input_dot.get_color())
curr_base = 0.5
def output_dot_update(ouput_dot):
y = input_dot.get_center()[1]
output_dot.move_to(self.z_to_point(
curr_base**complex(0, y), is_input = False
))
return output_dot
def walk_up_and_down():
for vect in 3*DOWN, 5*UP, 5*DOWN, 2*UP:
self.play(
input_dot.shift, vect,
UpdateFromFunc(output_dot, output_dot_update),
run_time = 3
)
exp = self.imag_exponent[-1]
new_exp = TexMobject("ti")
new_exp.set_color(exp.get_color())
new_exp.set_height(exp.get_height())
new_exp.move_to(exp, LEFT)
nine = TexMobject("9")
nine.set_color(BLUE)
denom = self.imag_exponent[0][3]
denom.save_state()
nine.replace(denom)
self.play(Transform(exp, new_exp))
self.play(input_dot.shift, 2*UP)
self.play(input_dot.shift, 2*DOWN)
self.wait()
self.play(output_dot.restore)
self.wait()
walk_up_and_down()
self.wait()
curr_base = 1./9
self.play(Transform(denom, nine))
walk_up_and_down()
self.wait()
def z_to_point(self, z, is_input = True):
if is_input:
plane = self.left_plane
else:
plane = self.right_plane
return plane.num_pair_to_point((z.real, z.imag))
class SizeAndRotationBreakdown(Scene):
def construct(self):
original = TexMobject(
"\\left(\\frac{1}{2}\\right)", "^{2+i}"
)
split = TexMobject(
"\\left(\\frac{1}{2}\\right)", "^{2}",
"\\left(\\frac{1}{2}\\right)", "^{i}",
)
VGroup(original[-1], split[1], split[3]).set_color(YELLOW)
VGroup(original, split).shift(UP)
real_part = VGroup(*split[:2])
imag_part = VGroup(*split[2:])
size_brace = Brace(real_part)
size = size_brace.get_text("Size")
rotation_brace = Brace(imag_part, UP)
rotation = rotation_brace.get_text("Rotation")
self.add(original)
self.wait()
self.play(*[
Transform(*pair)
for pair in [
(original[0], split[0]),
(original[1][0], split[1]),
(original[0].copy(), split[2]),
(VGroup(*original[1][1:]), split[3]),
]
])
self.play(
GrowFromCenter(size_brace),
Write(size)
)
self.play(
GrowFromCenter(rotation_brace),
Write(rotation)
)
self.wait()
class SeeLinksInDescription(TeacherStudentsScene):
def construct(self):
self.teacher_says("""
See links in the
description for more.
""")
self.play(*it.chain(*[
[pi.change_mode, "hooray", pi.look, DOWN]
for pi in self.get_students()
]))
self.random_blink(3)
class ShowMultiplicationOfRealAndImaginaryExponentialParts(FromRealToComplex):
def construct(self):
self.break_up_exponent()
self.show_multiplication()
def break_up_exponent(self):
original = TexMobject(
"\\left(\\frac{1}{2}\\right)", "^{2+i}"
)
split = TexMobject(
"\\left(\\frac{1}{2}\\right)", "^{2}",
"\\left(\\frac{1}{2}\\right)", "^{i}",
)
VGroup(original[-1], split[1], split[3]).set_color(YELLOW)
VGroup(original, split).to_corner(UP+LEFT)
rect = BackgroundRectangle(split)
real_part = VGroup(*split[:2])
imag_part = VGroup(*split[2:])
self.add(rect, original)
self.wait()
self.play(*[
Transform(*pair)
for pair in [
(original[0], split[0]),
(original[1][0], split[1]),
(original[0].copy(), split[2]),
(VGroup(*original[1][1:]), split[3]),
]
])
self.remove(*self.get_mobjects_from_last_animation())
self.add(real_part, imag_part)
self.wait()
self.real_part = real_part
self.imag_part = imag_part
def show_multiplication(self):
real_part = self.real_part.copy()
imag_part = self.imag_part.copy()
for part in real_part, imag_part:
part.add_to_back(BackgroundRectangle(part))
fourth_point = self.z_to_point(0.25)
fourth_line = Line(ORIGIN, fourth_point)
brace = Brace(fourth_line, UP, buff = SMALL_BUFF)
fourth_dot = Dot(fourth_point)
fourth_group = VGroup(fourth_line, brace, fourth_dot)
fourth_group.set_color(RED)
circle = Circle(radius = 2, color = MAROON_B)
circle.fade(0.3)
imag_power_point = self.z_to_point(0.5**complex(0, 1))
imag_power_dot = Dot(imag_power_point)
imag_power_line = Line(ORIGIN, imag_power_point)
VGroup(imag_power_dot, imag_power_line).set_color(MAROON_B)
full_power_tex = TexMobject(
"\\left(\\frac{1}{2}\\right)", "^{2+i}"
)
full_power_tex[-1].set_color(YELLOW)
full_power_tex.add_background_rectangle()
full_power_tex.scale(0.7)
full_power_tex.next_to(
0.5*self.z_to_point(0.5**complex(2, 1)),
UP+RIGHT
)
self.play(
real_part.scale, 0.7,
real_part.next_to, brace, UP, SMALL_BUFF, LEFT,
ShowCreation(fourth_dot)
)
self.play(
GrowFromCenter(brace),
ShowCreation(fourth_line),
)
self.wait()
self.play(
imag_part.scale, 0.7,
imag_part.next_to, imag_power_dot, DOWN+RIGHT, SMALL_BUFF,
ShowCreation(imag_power_dot)
)
self.play(ShowCreation(circle), Animation(imag_power_dot))
self.play(ShowCreation(imag_power_line))
self.wait(2)
self.play(
fourth_group.rotate, imag_power_line.get_angle()
)
real_part.generate_target()
imag_part.generate_target()
real_part.target.next_to(brace, UP+RIGHT, buff = 0)
imag_part.target.next_to(real_part.target, buff = 0)
self.play(*list(map(MoveToTarget, [real_part, imag_part])))
self.wait()
class ComplexFunctionsAsTransformations(ComplexTransformationScene):
def construct(self):
self.add_title()
input_dots, output_dots, arrows = self.get_dots()
self.play(FadeIn(
input_dots,
run_time = 2,
lag_ratio = 0.5
))
for in_dot, out_dot, arrow in zip(input_dots, output_dots, arrows):
self.play(
Transform(in_dot.copy(), out_dot),
ShowCreation(arrow)
)
self.wait()
self.wait()
def add_title(self):
title = TextMobject("Complex functions as transformations")
title.add_background_rectangle()
title.to_edge(UP)
self.add(title)
def get_dots(self):
input_points = [
RIGHT+2*UP,
4*RIGHT+DOWN,
2*LEFT+2*UP,
LEFT+DOWN,
6*LEFT+DOWN,
]
output_nudges = [
DOWN+RIGHT,
2*UP+RIGHT,
2*RIGHT+2*DOWN,
2*RIGHT+DOWN,
RIGHT+2*UP,
]
input_dots = VGroup(*list(map(Dot, input_points)))
input_dots.set_color(YELLOW)
output_dots = VGroup(*[
Dot(ip + on)
for ip, on in zip(input_points, output_nudges)
])
output_dots.set_color(MAROON_B)
arrows = VGroup(*[
Arrow(in_dot, out_dot, buff = 0.1, color = WHITE)
for in_dot, out_dot, in zip(input_dots, output_dots)
])
for i, dot in enumerate(input_dots):
label = TexMobject("s_%d"%i)
label.set_color(dot.get_color())
label.next_to(dot, DOWN+LEFT, buff = SMALL_BUFF)
dot.add(label)
for i, dot in enumerate(output_dots):
label = TexMobject("f(s_%d)"%i)
label.set_color(dot.get_color())
label.next_to(dot, UP+RIGHT, buff = SMALL_BUFF)
dot.add(label)
return input_dots, output_dots, arrows
class VisualizingSSquared(ComplexTransformationScene):
CONFIG = {
"num_anchors_to_add_per_line" : 100,
"horiz_end_color" : GOLD,
"y_min" : 0,
}
def construct(self):
self.add_title()
self.plug_in_specific_values()
self.show_transformation()
self.comment_on_two_dimensions()
def add_title(self):
title = TexMobject("f(", "s", ") = ", "s", "^2")
title.set_color_by_tex("s", YELLOW)
title.add_background_rectangle()
title.scale(1.5)
title.to_corner(UP+LEFT)
self.play(Write(title))
self.add_foreground_mobject(title)
self.wait()
self.title = title
def plug_in_specific_values(self):
inputs = list(map(complex, [2, -1, complex(0, 1)]))
input_dots = VGroup(*[
Dot(self.z_to_point(z), color = YELLOW)
for z in inputs
])
output_dots = VGroup(*[
Dot(self.z_to_point(z**2), color = BLUE)
for z in inputs
])
arrows = VGroup()
VGroup(*[
ParametricFunction(
lambda t : self.z_to_point(z**(1.1+0.8*t))
)
for z in inputs
])
for z, dot in zip(inputs, input_dots):
path = ParametricFunction(
lambda t : self.z_to_point(z**(1+t))
)
dot.path = path
arrow = ParametricFunction(
lambda t : self.z_to_point(z**(1.1+0.8*t))
)
stand_in_arrow = Arrow(
arrow.points[-2], arrow.points[-1],
tip_length = 0.2
)
arrow.add(stand_in_arrow.tip)
arrows.add(arrow)
arrows.set_color(WHITE)
for input_dot, output_dot, arrow in zip(input_dots, output_dots, arrows):
input_dot.save_state()
input_dot.move_to(self.title[1][1])
input_dot.set_fill(opacity = 0)
self.play(input_dot.restore)
self.wait()
self.play(ShowCreation(arrow))
self.play(ShowCreation(output_dot))
self.wait()
self.add_foreground_mobjects(arrows, output_dots, input_dots)
self.input_dots = input_dots
self.output_dots = output_dots
def add_transformable_plane(self, **kwargs):
ComplexTransformationScene.add_transformable_plane(self, **kwargs)
self.plane.next_to(ORIGIN, UP, buff = 0.01)
self.plane.add(self.plane.copy().rotate(np.pi, RIGHT))
self.plane.add(
Line(ORIGIN, FRAME_X_RADIUS*RIGHT, color = self.horiz_end_color),
Line(ORIGIN, FRAME_X_RADIUS*LEFT, color = self.horiz_end_color),
)
self.add(self.plane)
def show_transformation(self):
self.add_transformable_plane()
self.play(ShowCreation(self.plane, run_time = 3))
self.wait()
self.apply_complex_homotopy(
lambda z, t : z**(1+t),
added_anims = [
MoveAlongPath(dot, dot.path, run_time = 5)
for dot in self.input_dots
],
run_time = 5
)
self.wait(2)
def comment_on_two_dimensions(self):
morty = Mortimer().flip()
morty.scale(0.7)
morty.to_corner(DOWN+LEFT)
bubble = morty.get_bubble(SpeechBubble, height = 2, width = 4)
bubble.set_fill(BLACK, opacity = 0.9)
bubble.write("""
It all happens
in two dimensions!
""")
self.foreground_mobjects = []
self.play(FadeIn(morty))
self.play(
morty.change_mode, "hooray",
ShowCreation(bubble),
Write(bubble.content),
)
self.play(Blink(morty))
self.wait(2)
class ShowZetaOnHalfPlane(ZetaTransformationScene):
CONFIG = {
"x_min" : 1,
"x_max" : int(FRAME_X_RADIUS+2),
}
def construct(self):
self.add_title()
self.initial_transformation()
self.react_to_transformation()
self.show_cutoff()
self.set_color_i_line()
self.show_continuation()
self.emphsize_sum_doesnt_make_sense()
def add_title(self):
zeta = TexMobject(
"\\zeta(", "s", ")=",
*[
"\\frac{1}{%d^s} + "%d
for d in range(1, 5)
] + ["\\cdots"]
)
zeta[1].set_color(YELLOW)
for mob in zeta[3:3+4]:
mob[-2].set_color(YELLOW)
zeta.add_background_rectangle()
zeta.scale(0.8)
zeta.to_corner(UP+LEFT)
self.add_foreground_mobjects(zeta)
self.zeta = zeta
def initial_transformation(self):
self.add_transformable_plane()
self.wait()
self.add_extra_plane_lines_for_zeta(animate = True)
self.wait(2)
self.plane.save_state()
self.apply_zeta_function()
self.wait(2)
def react_to_transformation(self):
morty = Mortimer().flip()
morty.to_corner(DOWN+LEFT)
bubble = morty.get_bubble(SpeechBubble)
bubble.set_fill(BLACK, 0.5)
bubble.write("\\emph{Damn}!")
bubble.resize_to_content()
bubble.pin_to(morty)
self.play(FadeIn(morty))
self.play(
morty.change_mode, "surprised",
ShowCreation(bubble),
Write(bubble.content)
)
self.play(Blink(morty))
self.play(morty.look_at, self.plane.get_top())
self.wait()
self.play(
morty.look_at, self.plane.get_bottom(),
*list(map(FadeOut, [bubble, bubble.content]))
)
self.play(Blink(morty))
self.play(FadeOut(morty))
def show_cutoff(self):
words = TextMobject("Such an abrupt stop...")
words.add_background_rectangle()
words.next_to(ORIGIN, UP+LEFT)
words.shift(LEFT+UP)
line = Line(*list(map(self.z_to_point, [
complex(np.euler_gamma, u*FRAME_Y_RADIUS)
for u in (1, -1)
])))
line.set_color(YELLOW)
arrows = [
Arrow(words.get_right(), point)
for point in line.get_start_and_end()
]
self.play(Write(words, run_time = 2))
self.play(ShowCreation(arrows[0]))
self.play(
Transform(*arrows),
ShowCreation(line),
run_time = 2
)
self.play(FadeOut(arrows[0]))
self.wait(2)
self.play(*list(map(FadeOut, [words, line])))
def set_color_i_line(self):
right_i_lines, left_i_lines = [
VGroup(*[
Line(
vert_vect+RIGHT,
vert_vect+(FRAME_X_RADIUS+1)*horiz_vect
)
for vert_vect in (UP, DOWN)
])
for horiz_vect in (RIGHT, LEFT)
]
right_i_lines.set_color(YELLOW)
left_i_lines.set_color(BLUE)
for lines in right_i_lines, left_i_lines:
self.prepare_for_transformation(lines)
self.restore_mobjects(self.plane)
self.plane.add(*right_i_lines)
colored_plane = self.plane.copy()
right_i_lines.set_stroke(width = 0)
self.play(
self.plane.set_stroke, GREY, 1,
)
right_i_lines.set_stroke(YELLOW, width = 3)
self.play(ShowCreation(right_i_lines))
self.plane.save_state()
self.wait(2)
self.apply_zeta_function()
self.wait(2)
left_i_lines.save_state()
left_i_lines.apply_complex_function(zeta)
self.play(ShowCreation(left_i_lines, run_time = 5))
self.wait()
self.restore_mobjects(self.plane, left_i_lines)
self.play(Transform(self.plane, colored_plane))
self.wait()
self.left_i_lines = left_i_lines
def show_continuation(self):
reflected_plane = self.get_reflected_plane()
self.play(ShowCreation(reflected_plane, run_time = 2))
self.plane.add(reflected_plane)
self.remove(self.left_i_lines)
self.wait()
self.apply_zeta_function()
self.wait(2)
self.play(ShowCreation(
reflected_plane,
run_time = 6,
rate_func = lambda t : 1-there_and_back(t)
))
self.wait(2)
def emphsize_sum_doesnt_make_sense(self):
brace = Brace(VGroup(*self.zeta[1][3:]))
words = brace.get_text("""
Still fails to converge
when Re$(s) < 1$
""", buff = SMALL_BUFF)
words.add_background_rectangle()
words.scale_in_place(0.8)
divergent_sum = TexMobject("1+2+3+4+\\cdots")
divergent_sum.next_to(ORIGIN, UP)
divergent_sum.to_edge(LEFT)
divergent_sum.add_background_rectangle()
self.play(
GrowFromCenter(brace),
Write(words)
)
self.wait(2)
self.play(Write(divergent_sum))
self.wait(2)
def restore_mobjects(self, *mobjects):
self.play(*it.chain(*[
[m.restore, m.make_smooth]
for m in mobjects
]), run_time = 2)
for m in mobjects:
self.remove(m)
m.restore()
self.add(m)
class ShowConditionalDefinition(Scene):
def construct(self):
zeta = TexMobject("\\zeta(s)=")
zeta[2].set_color(YELLOW)
sigma = TexMobject("\\sum_{n=1}^\\infty \\frac{1}{n^s}")
sigma[-1].set_color(YELLOW)
something_else = TextMobject("Something else...")
conditions = VGroup(*[
TextMobject("if Re$(s) %s 1$"%s)
for s in (">", "\\le")
])
definitions = VGroup(sigma, something_else)
definitions.arrange(DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT)
conditions.arrange(DOWN, buff = LARGE_BUFF)
definitions.shift(2*LEFT+2*UP)
conditions.next_to(definitions, RIGHT, buff = LARGE_BUFF, aligned_edge = DOWN)
brace = Brace(definitions, LEFT)
zeta.next_to(brace, LEFT)
sigma.save_state()
sigma.next_to(zeta)
self.add(zeta, sigma)
self.wait()
self.play(
sigma.restore,
GrowFromCenter(brace),
FadeIn(something_else)
)
self.play(Write(conditions))
self.wait()
underbrace = Brace(something_else)
question = underbrace.get_text("""
What to put here?
""")
VGroup(underbrace, question).set_color(GREEN_B)
self.play(
GrowFromCenter(underbrace),
Write(question),
something_else.set_color, GREEN_B
)
self.wait(2)
class SquiggleOnExtensions(ZetaTransformationScene):
CONFIG = {
"x_min" : 1,
"x_max" : int(FRAME_X_RADIUS+2),
}
def construct(self):
self.show_negative_one()
self.cycle_through_options()
self.lock_into_place()
def show_negative_one(self):
self.add_transformable_plane()
thin_plane = self.plane.copy()
thin_plane.add(self.get_reflected_plane())
self.remove(self.plane)
self.add_extra_plane_lines_for_zeta()
reflected_plane = self.get_reflected_plane()
self.plane.add(reflected_plane)
self.remove(self.plane)
self.add(thin_plane)
dot = self.note_point(-1, "-1")
self.play(
ShowCreation(self.plane, run_time = 2),
Animation(dot),
run_time = 2
)
self.remove(thin_plane)
self.apply_zeta_function(added_anims = [
ApplyMethod(
dot.move_to, self.z_to_point(-1./12),
run_time = 5
)
])
dot_to_remove = self.note_point(-1./12, "-\\frac{1}{12}")
self.remove(dot_to_remove)
self.left_plane = reflected_plane
self.dot = dot
def note_point(self, z, label_tex):
dot = Dot(self.z_to_point(z))
dot.set_color(YELLOW)
label = TexMobject(label_tex)
label.add_background_rectangle()
label.next_to(dot, UP+LEFT, buff = SMALL_BUFF)
label.shift(LEFT)
arrow = Arrow(label.get_right(), dot, buff = SMALL_BUFF)
self.play(Write(label, run_time = 1))
self.play(*list(map(ShowCreation, [arrow, dot])))
self.wait()
self.play(*list(map(FadeOut, [arrow, label])))
return dot
def cycle_through_options(self):
gamma = np.euler_gamma
def shear(point):
x, y, z = point
return np.array([
x,
y+0.25*(1-x)**2,
0
])
def mixed_scalar_func(point):
x, y, z = point
scalar = 1 + (gamma-x)/(gamma+FRAME_X_RADIUS)
return np.array([
(scalar**2)*x,
(scalar**3)*y,
0
])
def alt_mixed_scalar_func(point):
x, y, z = point
scalar = 1 + (gamma-x)/(gamma+FRAME_X_RADIUS)
return np.array([
(scalar**5)*x,
(scalar**2)*y,
0
])
def sinusoidal_func(point):
x, y, z = point
freq = np.pi/gamma
return np.array([
x-0.2*np.sin(x*freq)*np.sin(y),
y-0.2*np.sin(x*freq)*np.sin(y),
0
])
funcs = [
shear,
mixed_scalar_func,
alt_mixed_scalar_func,
sinusoidal_func,
]
for mob in self.left_plane.family_members_with_points():
if np.all(np.abs(mob.points[:,1]) < 0.1):
self.left_plane.remove(mob)
new_left_planes = [
self.left_plane.copy().apply_function(func)
for func in funcs
]
new_dots = [
self.dot.copy().move_to(func(self.dot.get_center()))
for func in funcs
]
self.left_plane.save_state()
for plane, dot in zip(new_left_planes, new_dots):
self.play(
Transform(self.left_plane, plane),
Transform(self.dot, dot),
run_time = 3
)
self.wait()
self.play(FadeOut(self.dot))
#Squiggle on example
self.wait()
self.play(FadeOut(self.left_plane))
self.play(ShowCreation(
self.left_plane,
run_time = 5,
rate_func=linear
))
self.wait()
def lock_into_place(self):
words = TextMobject(
"""Only one extension
has a """,
"\\emph{derivative}",
"everywhere",
alignment = ""
)
words.to_corner(UP+LEFT)
words.set_color_by_tex("\\emph{derivative}", YELLOW)
words.add_background_rectangle()
self.play(Write(words))
self.add_foreground_mobjects(words)
self.play(self.left_plane.restore)
self.wait()
class DontKnowDerivatives(TeacherStudentsScene):
def construct(self):
self.student_says(
"""
You said we don't
need derivatives!
""",
target_mode = "pleading"
)
self.random_blink(2)
self.student_says(
"""
I get $\\frac{df}{dx}$, just not
for complex functions
""",
target_mode = "confused",
student_index = 2
)
self.random_blink(2)
self.teacher_says(
"""
Luckily, there's a purely
geometric intuition here.
""",
target_mode = "hooray"
)
self.change_student_modes(*["happy"]*3)
self.random_blink(3)
class IntroduceAnglePreservation(VisualizingSSquared):
CONFIG = {
"num_anchors_to_add_per_line" : 50,
"use_homotopy" : True,
}
def construct(self):
self.add_title()
self.show_initial_transformation()
self.talk_about_derivative()
self.cycle_through_line_pairs()
self.note_grid_lines()
self.name_analytic()
def add_title(self):
title = TexMobject("f(", "s", ")=", "s", "^2")
title.set_color_by_tex("s", YELLOW)
title.scale(1.5)
title.to_corner(UP+LEFT)
title.add_background_rectangle()
self.title = title
self.add_transformable_plane()
self.play(Write(title))
self.add_foreground_mobjects(title)
self.wait()
def show_initial_transformation(self):
self.apply_function()
self.wait(2)
self.reset()
def talk_about_derivative(self):
randy = Randolph().scale(0.8)
randy.to_corner(DOWN+LEFT)
morty = Mortimer()
morty.to_corner(DOWN+RIGHT)
randy.make_eye_contact(morty)
for pi, words in (randy, "$f'(s) = 2s$"), (morty, "Here's some \\\\ related geometry..."):
pi.bubble = pi.get_bubble(SpeechBubble)
pi.bubble.set_fill(BLACK, opacity = 0.7)
pi.bubble.write(words)
pi.bubble.resize_to_content()
pi.bubble.pin_to(pi)
for index in 3, 7:
randy.bubble.content[index].set_color(YELLOW)
self.play(*list(map(FadeIn, [randy, morty])))
self.play(
randy.change_mode, "speaking",
ShowCreation(randy.bubble),
Write(randy.bubble.content)
)
self.play(Blink(morty))
self.wait()
self.play(
morty.change_mode, "speaking",
randy.change_mode, "pondering",
ShowCreation(morty.bubble),
Write(morty.bubble.content),
)
self.play(Blink(randy))
self.wait()
self.play(*list(map(FadeOut, [
randy, morty,
randy.bubble, randy.bubble.content,
morty.bubble, morty.bubble.content,
])))
def cycle_through_line_pairs(self):
line_pairs = [
(
Line(3*DOWN+3*RIGHT, 2*UP),
Line(DOWN+RIGHT, 3*UP+4*RIGHT)
),
(
Line(RIGHT+3.5*DOWN, RIGHT+2.5*UP),
Line(3*LEFT+0.5*UP, 3*RIGHT+0.5*UP),
),
(
Line(4*RIGHT+4*DOWN, RIGHT+2*UP),
Line(4*DOWN+RIGHT, 2*UP+2*RIGHT)
),
]
for lines in line_pairs:
self.show_angle_preservation_between_lines(*lines)
self.reset()
def note_grid_lines(self):
intersection_inputs = [
complex(x, y)
for x in np.arange(-5, 5, 0.5)
for y in np.arange(0, 3, 0.5)
if not (x <= 0 and y == 0)
]
brackets = VGroup(*list(map(
self.get_right_angle_bracket,
intersection_inputs
)))
self.apply_function()
self.wait()
self.play(
ShowCreation(brackets, run_time = 5),
Animation(self.plane)
)
self.wait()
def name_analytic(self):
equiv = TextMobject("``Analytic'' $\\Leftrightarrow$ Angle-preserving")
kind_of = TextMobject("...kind of")
for text in equiv, kind_of:
text.scale(1.2)
text.add_background_rectangle()
equiv.set_color(YELLOW)
kind_of.set_color(RED)
kind_of.next_to(equiv, RIGHT)
VGroup(equiv, kind_of).next_to(ORIGIN, UP, buff = 1)
self.play(Write(equiv))
self.wait(2)
self.play(Write(kind_of, run_time = 1))
self.wait(2)
def reset(self, faded = True):
self.play(FadeOut(self.plane))
self.add_transformable_plane()
if faded:
self.plane.fade()
self.play(FadeIn(self.plane))
def apply_function(self, **kwargs):
if self.use_homotopy:
self.apply_complex_homotopy(
lambda z, t : z**(1+t),
run_time = 5,
**kwargs
)
else:
self.apply_complex_function(
lambda z : z**2,
**kwargs
)
def show_angle_preservation_between_lines(self, *lines):
R2_endpoints = [
[l.get_start()[:2], l.get_end()[:2]]
for l in lines
]
R2_intersection_point = intersection(*R2_endpoints)
intersection_point = np.array(list(R2_intersection_point) + [0])
angle1, angle2 = [l.get_angle() for l in lines]
arc = Arc(
start_angle = angle1,
angle = angle2-angle1,
radius = 0.4,
color = YELLOW
)
arc.shift(intersection_point)
arc.insert_n_curves(10)
arc.generate_target()
input_z = complex(*arc.get_center()[:2])
scale_factor = abs(2*input_z)
arc.target.scale_about_point(1./scale_factor, intersection_point)
arc.target.apply_complex_function(lambda z : z**2)
angle_tex = TexMobject(
"%d^\\circ"%abs(int((angle2-angle1)*180/np.pi))
)
angle_tex.set_color(arc.get_color())
angle_tex.add_background_rectangle()
self.put_angle_tex_next_to_arc(angle_tex, arc)
angle_arrow = Arrow(
angle_tex, arc,
color = arc.get_color(),
buff = 0.1,
)
angle_group = VGroup(angle_tex, angle_arrow)
self.play(*list(map(ShowCreation, lines)))
self.play(
Write(angle_tex),
ShowCreation(angle_arrow),
ShowCreation(arc)
)
self.wait()
self.play(FadeOut(angle_group))
self.plane.add(*lines)
self.apply_function(added_anims = [
MoveToTarget(arc, run_time = 5)
])
self.put_angle_tex_next_to_arc(angle_tex, arc)
arrow = Arrow(angle_tex, arc, buff = 0.1)
arrow.set_color(arc.get_color())
self.play(
Write(angle_tex),
ShowCreation(arrow)
)
self.wait(2)
self.play(*list(map(FadeOut, [arc, angle_tex, arrow])))
def put_angle_tex_next_to_arc(self, angle_tex, arc):
vect = arc.point_from_proportion(0.5)-interpolate(
arc.points[0], arc.points[-1], 0.5
)
unit_vect = vect/get_norm(vect)
angle_tex.move_to(arc.get_center() + 1.7*unit_vect)
def get_right_angle_bracket(self, input_z):
output_z = input_z**2
derivative = 2*input_z
rotation = np.log(derivative).imag
brackets = VGroup(
Line(RIGHT, RIGHT+UP),
Line(RIGHT+UP, UP)
)
brackets.scale(0.15)
brackets.set_stroke(width = 2)
brackets.set_color(YELLOW)
brackets.shift(0.02*UP) ##Why???
brackets.rotate(rotation, about_point = ORIGIN)
brackets.shift(self.z_to_point(output_z))
return brackets
class AngleAtZeroDerivativePoints(IntroduceAnglePreservation):
CONFIG = {
"use_homotopy" : True
}
def construct(self):
self.add_title()
self.is_before_transformation = True
self.add_transformable_plane()
self.plane.fade()
line = Line(3*LEFT+0.5*UP, 3*RIGHT+0.5*DOWN)
self.show_angle_preservation_between_lines(
line, line.copy().rotate(np.pi/5)
)
self.wait()
def add_title(self):
title = TexMobject("f(", "s", ")=", "s", "^2")
title.set_color_by_tex("s", YELLOW)
title.scale(1.5)
title.to_corner(UP+LEFT)
title.add_background_rectangle()
derivative = TexMobject("f'(0) = 0")
derivative.set_color(RED)
derivative.scale(1.2)
derivative.add_background_rectangle()
derivative.next_to(title, DOWN)
self.add_foreground_mobjects(title, derivative)
def put_angle_tex_next_to_arc(self, angle_tex, arc):
IntroduceAnglePreservation.put_angle_tex_next_to_arc(
self, angle_tex, arc
)
if not self.is_before_transformation:
two_dot = TexMobject("2 \\times ")
two_dot.set_color(angle_tex.get_color())
two_dot.next_to(angle_tex, LEFT, buff = SMALL_BUFF)
two_dot.add_background_rectangle()
center = angle_tex.get_center()
angle_tex.add_to_back(two_dot)
angle_tex.move_to(center)
else:
self.is_before_transformation = False
class AnglePreservationAtAnyPairOfPoints(IntroduceAnglePreservation):
def construct(self):
self.add_transformable_plane()
self.plane.fade()
line_pairs = self.get_line_pairs()
line_pair = line_pairs[0]
for target_pair in line_pairs[1:]:
self.play(Transform(
line_pair, target_pair,
run_time = 2,
path_arc = np.pi
))
self.wait()
self.show_angle_preservation_between_lines(*line_pair)
self.show_example_analytic_functions()
def get_line_pairs(self):
return list(it.starmap(VGroup, [
(
Line(3*DOWN, 3*LEFT+2*UP),
Line(2*LEFT+DOWN, 3*UP+RIGHT)
),
(
Line(2*RIGHT+DOWN, 3*LEFT+2*UP),
Line(LEFT+3*DOWN, 4*RIGHT+3*UP),
),
(
Line(LEFT+3*DOWN, LEFT+3*UP),
Line(5*LEFT+UP, 3*RIGHT+UP)
),
(
Line(4*RIGHT+3*DOWN, RIGHT+2*UP),
Line(3*DOWN+RIGHT, 2*UP+2*RIGHT)
),
]))
def show_example_analytic_functions(self):
words = TextMobject("Examples of analytic functions:")
words.shift(2*UP)
words.set_color(YELLOW)
words.add_background_rectangle()
words.next_to(UP, UP).to_edge(LEFT)
functions = TextMobject(
"$e^x$, ",
"$\\sin(x)$, ",
"any polynomial, "
"$\\log(x)$, ",
"\\dots",
)
functions.next_to(ORIGIN, UP).to_edge(LEFT)
for function in functions:
function.add_to_back(BackgroundRectangle(function))
self.play(Write(words))
for function in functions:
self.play(FadeIn(function))
self.wait()
class NoteZetaFunctionAnalyticOnRightHalf(ZetaTransformationScene):
CONFIG = {
"anchor_density" : 35,
}
def construct(self):
self.add_title()
self.add_transformable_plane(animate = False)
self.add_extra_plane_lines_for_zeta(animate = True)
self.apply_zeta_function()
self.note_right_angles()
def add_title(self):
title = TexMobject(
"\\zeta(s) = \\sum_{n=1}^\\infty \\frac{1}{n^s}"
)
title[2].set_color(YELLOW)
title[-1].set_color(YELLOW)
title.add_background_rectangle()
title.to_corner(UP+LEFT)
self.add_foreground_mobjects(title)
def note_right_angles(self):
intersection_inputs = [
complex(x, y)
for x in np.arange(1+2./16, 1.4, 1./16)
for y in np.arange(-0.5, 0.5, 1./16)
if abs(y) > 1./16
]
brackets = VGroup(*list(map(
self.get_right_angle_bracket,
intersection_inputs
)))
self.play(ShowCreation(brackets, run_time = 3))
self.wait()
def get_right_angle_bracket(self, input_z):
output_z = zeta(input_z)
derivative = d_zeta(input_z)
rotation = np.log(derivative).imag
brackets = VGroup(
Line(RIGHT, RIGHT+UP),
Line(RIGHT+UP, UP)
)
brackets.scale(0.1)
brackets.set_stroke(width = 2)
brackets.set_color(YELLOW)
brackets.rotate(rotation, about_point = ORIGIN)
brackets.shift(self.z_to_point(output_z))
return brackets
class InfiniteContinuousJigsawPuzzle(ZetaTransformationScene):
CONFIG = {
"anchor_density" : 35,
}
def construct(self):
self.set_stage()
self.add_title()
self.show_jigsaw()
self.name_analytic_continuation()
def set_stage(self):
self.plane = self.get_dense_grid()
left_plane = self.get_reflected_plane()
self.plane.add(left_plane)
self.apply_zeta_function(run_time = 0)
self.remove(left_plane)
lines_per_piece = 5
pieces = [
VGroup(*left_plane[lines_per_piece*i:lines_per_piece*(i+1)])
for i in range(len(list(left_plane))/lines_per_piece)
]
random.shuffle(pieces)
self.pieces = pieces
def add_title(self):
title = TextMobject("Infinite ", "continuous ", "jigsaw puzzle")
title.scale(1.5)
title.to_edge(UP)
for word in title:
word.add_to_back(BackgroundRectangle(word))
self.play(FadeIn(word))
self.wait()
self.add_foreground_mobjects(title)
self.title = title
def show_jigsaw(self):
for piece in self.pieces:
self.play(FadeIn(piece, run_time = 0.5))
self.wait()
def name_analytic_continuation(self):
words = TextMobject("``Analytic continuation''")
words.set_color(YELLOW)
words.scale(1.5)
words.next_to(self.title, DOWN, buff = LARGE_BUFF)
words.add_background_rectangle()
self.play(Write(words))
self.wait()
class ThatsHowZetaIsDefined(TeacherStudentsScene):
def construct(self):
self.add_zeta_definition()
self.teacher_says("""
So that's how
$\\zeta(s)$ is defined
""")
self.change_student_modes(*["hooray"]*3)
self.random_blink(2)
def add_zeta_definition(self):
zeta = TexMobject(
"\\zeta(s) = \\sum_{n=1}^\\infty \\frac{1}{n^s}"
)
VGroup(zeta[2], zeta[-1]).set_color(YELLOW)
zeta.to_corner(UP+LEFT)
self.add(zeta)
class ManyIntersectingLinesPreZeta(ZetaTransformationScene):
CONFIG = {
"apply_zeta" : False,
"lines_center" : RIGHT,
"nudge_size" : 0.9,
"function" : zeta,
"angle" : np.pi/5,
"arc_scale_factor" : 0.3,
"shift_directions" : [LEFT, RIGHT],
}
def construct(self):
self.establish_plane()
self.add_title()
line = Line(DOWN+2*LEFT, UP+2*RIGHT)
lines = VGroup(line, line.copy().rotate(self.angle))
arc = Arc(start_angle = line.get_angle(), angle = self.angle)
arc.scale(self.arc_scale_factor)
arc.set_color(YELLOW)
lines.add(arc)
# lines.set_stroke(WHITE, width = 5)
lines.shift(self.lines_center + self.nudge_size*RIGHT)
if self.apply_zeta:
self.apply_zeta_function(run_time = 0)
lines.set_stroke(width = 0)
added_anims = self.get_modified_line_anims(lines)
for vect in self.shift_directions:
self.play(
ApplyMethod(lines.shift, 2*self.nudge_size*vect, path_arc = np.pi),
*added_anims,
run_time = 3
)
def establish_plane(self):
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
self.add_reflected_plane()
self.plane.fade()
def add_title(self):
if self.apply_zeta:
title = TextMobject("After \\\\ transformation")
else:
title = TextMobject("Before \\\\ transformation")
title.add_background_rectangle()
title.to_edge(UP)
self.add_foreground_mobjects(title)
def get_modified_line_anims(self, lines):
return []
class ManyIntersectingLinesPostZeta(ManyIntersectingLinesPreZeta):
CONFIG = {
"apply_zeta" : True,
# "anchor_density" : 5
}
def get_modified_line_anims(self, lines):
n_inserted_points = 30
new_lines = lines.copy()
new_lines.set_stroke(width = 5)
def update_new_lines(lines_to_update):
transformed = lines.copy()
self.prepare_for_transformation(transformed)
transformed.apply_complex_function(self.function)
transformed.make_smooth()
transformed.set_stroke(width = 5)
for start, end in zip(lines_to_update, transformed):
if start.get_num_points() > 0:
start.points = np.array(end.points)
return [UpdateFromFunc(new_lines, update_new_lines)]
class ManyIntersectingLinesPreSSquared(ManyIntersectingLinesPreZeta):
CONFIG = {
"x_min" : -int(FRAME_X_RADIUS),
"apply_zeta" : False,
"lines_center" : ORIGIN,
"nudge_size" : 0.9,
"function" : lambda z : z**2,
"shift_directions" : [LEFT, RIGHT, UP, DOWN, DOWN+LEFT, UP+RIGHT],
}
def establish_plane(self):
self.add_transformable_plane()
self.plane.fade()
def apply_zeta_function(self, **kwargs):
self.apply_complex_function(self.function, **kwargs)
class ManyIntersectingLinesPostSSquared(ManyIntersectingLinesPreSSquared):
CONFIG = {
"apply_zeta" : True,
}
def get_modified_line_anims(self, lines):
n_inserted_points = 30
new_lines = lines.copy()
new_lines.set_stroke(width = 5)
def update_new_lines(lines_to_update):
transformed = lines.copy()
self.prepare_for_transformation(transformed)
transformed.apply_complex_function(self.function)
transformed.make_smooth()
transformed.set_stroke(width = 5)
for start, end in zip(lines_to_update, transformed):
if start.get_num_points() > 0:
start.points = np.array(end.points)
return [UpdateFromFunc(new_lines, update_new_lines)]
class ButWhatIsTheExensions(TeacherStudentsScene):
def construct(self):
self.student_says(
"""
But what exactly \\emph{is}
that continuation?
""",
target_mode = "sassy"
)
self.change_student_modes("confused", "sassy", "confused")
self.random_blink(2)
self.teacher_says("""
You're $\\$1{,}000{,}000$ richer
if you can answer
that fully
""", target_mode = "shruggie")
self.change_student_modes(*["pondering"]*3)
self.random_blink(3)
class MathematiciansLookingAtFunctionEquation(Scene):
def construct(self):
equation = TexMobject(
"\\zeta(s)",
"= 2^s \\pi ^{s-1}",
"\\sin\\left(\\frac{\\pi s}{2}\\right)",
"\\Gamma(1-s)",
"\\zeta(1-s)",
)
equation.shift(UP)
mathy = Mathematician().to_corner(DOWN+LEFT)
mathys = VGroup(mathy)
for x in range(2):
mathys.add(Mathematician().next_to(mathys))
for mathy in mathys:
mathy.change_mode("pondering")
mathy.look_at(equation)
self.add(mathys)
self.play(Write(VGroup(*equation[:-1])))
self.play(Transform(
equation[0].copy(),
equation[-1],
path_arc = -np.pi/3,
run_time = 2
))
for mathy in mathys:
self.play(Blink(mathy))
self.wait()
class DiscussZeros(ZetaTransformationScene):
def construct(self):
self.establish_plane()
self.ask_about_zeros()
self.show_trivial_zeros()
self.show_critical_strip()
self.transform_bit_of_critical_line()
self.extend_transformed_critical_line()
def establish_plane(self):
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
self.add_reflected_plane()
self.plane.fade()
def ask_about_zeros(self):
dots = VGroup(*[
Dot(
(2+np.sin(12*alpha))*\
rotate_vector(RIGHT, alpha+nudge)
)
for alpha in np.arange(3*np.pi/20, 2*np.pi, 2*np.pi/5)
for nudge in [random.random()*np.pi/6]
])
dots.set_color(YELLOW)
q_marks = VGroup(*[
TexMobject("?").next_to(dot, UP)
for dot in dots
])
arrows = VGroup(*[
Arrow(dot, ORIGIN, buff = 0.2, tip_length = 0.1)
for dot in dots
])
question = TextMobject("Which numbers go to $0$?")
question.add_background_rectangle()
question.to_edge(UP)
for mob in dots, arrows, q_marks:
self.play(ShowCreation(mob))
self.play(Write(question))
self.wait(2)
dots.generate_target()
for i, dot in enumerate(dots.target):
dot.move_to(2*(i+1)*LEFT)
self.play(
FadeOut(arrows),
FadeOut(q_marks),
FadeOut(question),
MoveToTarget(dots),
)
self.wait()
self.dots = dots
def show_trivial_zeros(self):
trivial_zero_words = TextMobject("``Trivial'' zeros")
trivial_zero_words.next_to(ORIGIN, UP)
trivial_zero_words.to_edge(LEFT)
randy = Randolph().flip()
randy.to_corner(DOWN+RIGHT)
bubble = randy.get_bubble()
bubble.set_fill(BLACK, opacity = 0.8)
bubble.write("$1^1 + 2^2 + 3^2 + \\cdots = 0$")
bubble.resize_to_content()
bubble.pin_to(randy)
self.plane.save_state()
self.dots.save_state()
for dot in self.dots.target:
dot.move_to(ORIGIN)
self.apply_zeta_function(
added_anims = [MoveToTarget(self.dots, run_time = 3)],
run_time = 3
)
self.wait(3)
self.play(
self.plane.restore,
self.plane.make_smooth,
self.dots.restore,
run_time = 2
)
self.remove(*self.get_mobjects_from_last_animation())
self.plane.restore()
self.dots.restore()
self.add(self.plane, self.dots)
self.play(Write(trivial_zero_words))
self.wait()
self.play(FadeIn(randy))
self.play(
randy.change_mode, "confused",
ShowCreation(bubble),
Write(bubble.content)
)
self.play(Blink(randy))
self.wait()
self.play(Blink(randy))
self.play(*list(map(FadeOut, [
randy, bubble, bubble.content, trivial_zero_words
])))
def show_critical_strip(self):
strip = Rectangle(
height = FRAME_HEIGHT,
width = 1
)
strip.next_to(ORIGIN, RIGHT, buff = 0)
strip.set_stroke(width = 0)
strip.set_fill(YELLOW, opacity = 0.3)
name = TextMobject("Critical strip")
name.add_background_rectangle()
name.next_to(ORIGIN, LEFT)
name.to_edge(UP)
arrow = Arrow(name.get_bottom(), 0.5*RIGHT+UP)
primes = TexMobject("2, 3, 5, 7, 11, 13, 17, \\dots")
primes.to_corner(UP+RIGHT)
# photo = Square()
photo = ImageMobject("Riemann", invert = False)
photo.set_width(5)
photo.to_corner(UP+LEFT)
new_dots = VGroup(*[
Dot(0.5*RIGHT + y*UP)
for y in np.linspace(-2.5, 3.2, 5)
])
new_dots.set_color(YELLOW)
critical_line = Line(
0.5*RIGHT+FRAME_Y_RADIUS*DOWN,
0.5*RIGHT+FRAME_Y_RADIUS*UP,
color = YELLOW
)
self.give_dots_wandering_anims()
self.play(FadeIn(strip), *self.get_dot_wandering_anims())
self.play(
Write(name, run_time = 1),
ShowCreation(arrow),
*self.get_dot_wandering_anims()
)
self.play(*self.get_dot_wandering_anims())
self.play(
FadeIn(primes),
*self.get_dot_wandering_anims()
)
for x in range(7):
self.play(*self.get_dot_wandering_anims())
self.play(
GrowFromCenter(photo),
FadeOut(name),
FadeOut(arrow),
*self.get_dot_wandering_anims()
)
self.play(Transform(self.dots, new_dots))
self.play(ShowCreation(critical_line))
self.wait(3)
self.play(
photo.shift, 7*LEFT,
*list(map(FadeOut, [
primes, self.dots, strip
]))
)
self.remove(photo)
self.critical_line = critical_line
def give_dots_wandering_anims(self):
def func(t):
result = (np.sin(6*2*np.pi*t) + 1)*RIGHT/2
result += 3*np.cos(2*2*np.pi*t)*UP
return result
self.wandering_path = ParametricFunction(func)
for i, dot in enumerate(self.dots):
dot.target = dot.copy()
q_mark = TexMobject("?")
q_mark.next_to(dot.target, UP)
dot.target.add(q_mark)
dot.target.move_to(self.wandering_path.point_from_proportion(
(float(2+2*i)/(4*len(list(self.dots))))%1
))
self.dot_anim_count = 0
def get_dot_wandering_anims(self):
self.dot_anim_count += 1
if self.dot_anim_count == 1:
return list(map(MoveToTarget, self.dots))
denom = 4*(len(list(self.dots)))
def get_rate_func(index):
return lambda t : (float(self.dot_anim_count + 2*index + t)/denom)%1
return [
MoveAlongPath(
dot, self.wandering_path,
rate_func = get_rate_func(i)
)
for i, dot in enumerate(self.dots)
]
def transform_bit_of_critical_line(self):
self.play(
self.plane.scale, 0.8,
self.critical_line.scale, 0.8,
rate_func = there_and_back,
run_time = 2
)
self.wait()
self.play(
self.plane.set_stroke, GREY, 1,
Animation(self.critical_line)
)
self.plane.add(self.critical_line)
self.apply_zeta_function()
self.wait(2)
self.play(
self.plane.fade,
Animation(self.critical_line)
)
def extend_transformed_critical_line(self):
def func(t):
z = zeta(complex(0.5, t))
return z.real*RIGHT + z.imag*UP
full_line = VGroup(*[
ParametricFunction(func, t_min = t0, t_max = t0+1)
for t0 in range(100)
])
full_line.set_color_by_gradient(
YELLOW, BLUE, GREEN, RED, YELLOW, BLUE, GREEN, RED,
)
self.play(ShowCreation(full_line, run_time = 20, rate_func=linear))
self.wait()
class AskAboutRelationToPrimes(TeacherStudentsScene):
def construct(self):
self.student_says("""
Whoa! Where the heck
do primes come in here?
""", target_mode = "confused")
self.random_blink(3)
self.teacher_says("""
Perhaps in a
different video.
""", target_mode = "hesitant")
self.random_blink(3)
class HighlightCriticalLineAgain(DiscussZeros):
def construct(self):
self.establish_plane()
title = TexMobject("\\zeta(", "s", ") = 0")
title.set_color_by_tex("s", YELLOW)
title.add_background_rectangle()
title.to_corner(UP+LEFT)
self.add(title)
strip = Rectangle(
height = FRAME_HEIGHT,
width = 1
)
strip.next_to(ORIGIN, RIGHT, buff = 0)
strip.set_stroke(width = 0)
strip.set_fill(YELLOW, opacity = 0.3)
line = Line(
0.5*RIGHT+FRAME_Y_RADIUS*UP,
0.5*RIGHT+FRAME_Y_RADIUS*DOWN,
color = YELLOW
)
randy = Randolph().to_corner(DOWN+LEFT)
million = TexMobject("\\$1{,}000{,}000")
million.set_color(GREEN_B)
million.next_to(ORIGIN, UP+LEFT)
million.shift(2*LEFT)
arrow1 = Arrow(million.get_right(), line.get_top())
arrow2 = Arrow(million.get_right(), line.get_bottom())
self.add(randy, strip)
self.play(Write(million))
self.play(
randy.change_mode, "pondering",
randy.look_at, line.get_top(),
ShowCreation(arrow1),
run_time = 3
)
self.play(
randy.look_at, line.get_bottom(),
ShowCreation(line),
Transform(arrow1, arrow2)
)
self.play(FadeOut(arrow1))
self.play(Blink(randy))
self.wait()
self.play(randy.look_at, line.get_center())
self.play(randy.change_mode, "confused")
self.play(Blink(randy))
self.wait()
self.play(randy.change_mode, "pondering")
self.wait()
class DiscussSumOfNaturals(Scene):
def construct(self):
title = TexMobject(
"\\zeta(s) = \\sum_{n=1}^\\infty \\frac{1}{n^s}"
)
VGroup(title[2], title[-1]).set_color(YELLOW)
title.to_corner(UP+LEFT)
neg_twelfth, eq, zeta_neg_1, sum_naturals = equation = TexMobject(
"-\\frac{1}{12}",
"=",
"\\zeta(-1)",
"= 1 + 2 + 3 + 4 + \\cdots"
)
neg_twelfth.set_color(GREEN_B)
VGroup(*zeta_neg_1[2:4]).set_color(YELLOW)
q_mark = TexMobject("?").next_to(sum_naturals[0], UP)
q_mark.set_color(RED)
randy = Randolph()
randy.to_corner(DOWN+LEFT)
analytic_continuation = TextMobject("Analytic continuation")
analytic_continuation.next_to(title, RIGHT, 3*LARGE_BUFF)
sum_to_zeta = Arrow(title.get_corner(DOWN+RIGHT), zeta_neg_1)
sum_to_ac = Arrow(title.get_right(), analytic_continuation)
ac_to_zeta = Arrow(analytic_continuation.get_bottom(), zeta_neg_1.get_top())
cross = TexMobject("\\times")
cross.scale(2)
cross.set_color(RED)
cross.rotate(np.pi/6)
cross.move_to(sum_to_zeta.get_center())
brace = Brace(VGroup(zeta_neg_1, sum_naturals))
words = TextMobject(
"If not equal, at least connected",
"\\\\(see links in description)"
)
words.next_to(brace, DOWN)
self.add(neg_twelfth, eq, zeta_neg_1, randy, title)
self.wait()
self.play(
Write(sum_naturals),
Write(q_mark),
randy.change_mode, "confused"
)
self.play(Blink(randy))
self.wait()
self.play(randy.change_mode, "angry")
self.play(
ShowCreation(sum_to_zeta),
Write(cross)
)
self.play(Blink(randy))
self.wait()
self.play(
Transform(sum_to_zeta, sum_to_ac),
FadeOut(cross),
Write(analytic_continuation),
randy.change_mode, "pondering",
randy.look_at, analytic_continuation,
)
self.play(ShowCreation(ac_to_zeta))
self.play(Blink(randy))
self.wait()
self.play(
GrowFromCenter(brace),
Write(words[0]),
randy.look_at, words[0],
)
self.wait()
self.play(FadeIn(words[1]))
self.play(Blink(randy))
self.wait()
class InventingMathPreview(Scene):
def construct(self):
rect = Rectangle(height = 9, width = 16)
rect.set_height(4)
title = TextMobject("What does it feel like to invent math?")
title.next_to(rect, UP)
sum_tex = TexMobject("1+2+4+8+\\cdots = -1")
sum_tex.set_width(rect.get_width()-1)
self.play(
ShowCreation(rect),
Write(title)
)
self.play(Write(sum_tex))
self.wait()
class FinalAnimationTease(Scene):
def construct(self):
morty = Mortimer().shift(2*(DOWN+RIGHT))
bubble = morty.get_bubble(SpeechBubble)
bubble.write("""
Want to know what
$\\zeta'(s)$ looks like?
""")
self.add(morty)
self.play(
morty.change_mode, "hooray",
morty.look_at, bubble.content,
ShowCreation(bubble),
Write(bubble.content)
)
self.play(Blink(morty))
self.wait()
class PatreonThanks(Scene):
CONFIG = {
"specific_patrons" : [
"CrypticSwarm",
"Ali Yahya",
"Damion Kistler",
"Juan Batiz-Benet",
"Yu Jun",
"Othman Alikhan",
"Markus Persson",
"Joseph John Cox",
"Luc Ritchie",
"Shimin Kuang",
"Einar Johansen",
"Rish Kundalia",
"Achille Brighton",
"Kirk Werklund",
"Ripta Pasay",
"Felipe Diniz",
]
}
def construct(self):
morty = Mortimer()
morty.next_to(ORIGIN, DOWN)
n_patrons = len(self.specific_patrons)
special_thanks = TextMobject("Special thanks to:")
special_thanks.set_color(YELLOW)
special_thanks.shift(3*UP)
patreon_logo = ImageMobject("patreon", invert = False)
patreon_logo.set_height(1.5)
patreon_logo.next_to(special_thanks, DOWN)
left_patrons = VGroup(*list(map(TextMobject,
self.specific_patrons[:n_patrons/2]
)))
right_patrons = VGroup(*list(map(TextMobject,
self.specific_patrons[n_patrons/2:]
)))
for patrons, vect in (left_patrons, LEFT), (right_patrons, RIGHT):
patrons.arrange(DOWN, aligned_edge = LEFT)
patrons.next_to(special_thanks, DOWN)
patrons.to_edge(vect, buff = LARGE_BUFF)
self.add(patreon_logo)
self.play(morty.change_mode, "gracious")
self.play(Write(special_thanks, run_time = 1))
self.play(
Write(left_patrons),
morty.look_at, left_patrons
)
self.play(
Write(right_patrons),
morty.look_at, right_patrons
)
self.play(Blink(morty))
for patrons in left_patrons, right_patrons:
for index in 0, -1:
self.play(morty.look_at, patrons[index])
self.wait()
class CreditTwo(Scene):
def construct(self):
morty = Mortimer()
morty.next_to(ORIGIN, DOWN)
morty.to_edge(RIGHT)
brother = PiCreature(color = GOLD_E)
brother.next_to(morty, LEFT)
brother.look_at(morty.eyes)
headphones = Headphones(height = 1)
headphones.move_to(morty.eyes, aligned_edge = DOWN)
headphones.shift(0.1*DOWN)
url = TextMobject("www.audible.com/3blue1brown")
url.to_corner(UP+RIGHT, buff = LARGE_BUFF)
self.add(morty)
self.play(Blink(morty))
self.play(
FadeIn(headphones),
Write(url),
Animation(morty)
)
self.play(morty.change_mode, "happy")
for x in range(4):
self.wait()
self.play(Blink(morty))
self.wait()
self.play(
FadeIn(brother),
morty.look_at, brother.eyes
)
self.play(brother.change_mode, "surprised")
self.play(Blink(brother))
self.wait()
self.play(
morty.look, LEFT,
brother.change_mode, "happy",
brother.look, LEFT
)
for x in range(10):
self.play(Blink(morty))
self.wait()
self.play(Blink(brother))
self.wait()
class FinalAnimation(ZetaTransformationScene):
CONFIG = {
"min_added_anchors" : 100,
}
def construct(self):
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
self.add_reflected_plane()
title = TexMobject("s", "\\to \\frac{d\\zeta}{ds}(", "s", ")")
title.set_color_by_tex("s", YELLOW)
title.add_background_rectangle()
title.scale(1.5)
title.to_corner(UP+LEFT)
self.play(Write(title))
self.add_foreground_mobjects(title)
self.wait()
self.apply_complex_function(d_zeta, run_time = 8)
self.wait()
class Thumbnail(ZetaTransformationScene):
CONFIG = {
"anchor_density" : 35
}
def construct(self):
self.y_min = -4
self.y_max = 4
self.x_min = 1
self.x_max = int(FRAME_X_RADIUS+2)
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
self.add_reflected_plane()
# self.apply_zeta_function()
self.plane.set_stroke(width = 4)
div_sum = TexMobject("-\\frac{1}{12} = ", "1+2+3+4+\\cdots")
div_sum.set_width(FRAME_WIDTH-1)
div_sum.to_edge(DOWN)
div_sum.set_color(YELLOW)
div_sum.set_background_stroke(width=8)
# for mob in div_sum.submobjects:
# mob.add_to_back(BackgroundRectangle(mob))
zeta = TexMobject("\\zeta(s)")
zeta.set_height(FRAME_Y_RADIUS-1)
zeta.to_corner(UP+LEFT)
million = TexMobject("\\$1{,}000{,}000")
million.set_width(FRAME_X_RADIUS+1)
million.to_edge(UP+RIGHT)
million.set_color(GREEN_B)
million.set_background_stroke(width=8)
self.add(div_sum, million, zeta)
class ZetaPartialSums(ZetaTransformationScene):
CONFIG = {
"anchor_density" : 35,
"num_partial_sums" : 12,
}
def construct(self):
self.add_transformable_plane()
self.add_extra_plane_lines_for_zeta()
self.prepare_for_transformation(self.plane)
N_list = [2**k for k in range(self.num_partial_sums)]
sigma = TexMobject(
"\\sum_{n = 1}^N \\frac{1}{n^s}"
)
sigmas = []
for N in N_list + ["\\infty"]:
tex = TexMobject(str(N))
tex.set_color(YELLOW)
new_sigma = sigma.copy()
top = new_sigma[0]
tex.move_to(top, DOWN)
new_sigma.remove(top)
new_sigma.add(tex)
new_sigma.to_corner(UP+LEFT)
sigmas.append(new_sigma)
def get_partial_sum_func(n_terms):
return lambda s : sum([1./(n**s) for n in range(1, n_terms+1)])
interim_planes = [
self.plane.copy().apply_complex_function(
get_partial_sum_func(N)
)
for N in N_list
]
interim_planes.append(self.plane.copy().apply_complex_function(zeta))
symbol = VGroup(TexMobject("s"))
symbol.scale(2)
symbol.set_color(YELLOW)
symbol.to_corner(UP+LEFT)
for plane, sigma in zip(interim_planes, sigmas):
self.play(
Transform(self.plane, plane),
Transform(symbol, sigma)
)
self.wait()