3b1b-videos/_2023/clt/herschel.py
2023-04-03 08:46:40 -07:00

1589 lines
51 KiB
Python

from manim_imports_ext import *
from _2023.clt.main import *
class TwoDGaussianAsADistribution(InteractiveScene):
n_points = 2000
n_dots_per_moment = 10
def construct(self):
# Setup
frame = self.frame
plane = self.get_plane()
plane.set_flat_stroke(False)
self.add(plane)
dartboard = self.get_dartboard(plane)
dartboard.save_state()
dartboard.set_opacity(0)
self.add(dartboard)
self.add_random_points_anim(plane)
self.wait(10)
self.play(Restore(dartboard))
self.wait(10)
# Graph
def func(u, v, sigma=1):
return np.exp(-(u**2 + v**2) / sigma**2) / sigma
graphs = []
for sigma in [0.8, 1.0, 0.6]:
graph = ParametricSurface(lambda u, v: [u, v, func(u, v, sigma)], u_range=(-2, 2), v_range=(-2, 2))
graph.match_width(plane.axes)
graph.set_color(BLUE_E, 0.5)
graph.move_to(plane.axes, IN)
graph.always_sort_to_camera(self.camera)
mesh = VGroup(*plane.background_lines.copy(), plane.faded_lines.copy())
mesh.insert_n_curves(50)
mesh.start = mesh.copy().set_opacity(0)
mesh.save_state()
mesh.saved_state.set_opacity(0)
unit_size = plane.x_axis.get_unit_size()
for submob in mesh.family_members_with_points():
submob.set_points([
p + unit_size * func(*plane.p2c(p), sigma) * OUT
for p in submob.get_points()
])
submob.set_stroke(
WHITE,
width=0.5 * submob.get_stroke_width(),
opacity=0.5 * submob.get_stroke_opacity()
)
mesh.shift(0.01 * OUT)
graphs.append(Group(graph, mesh))
graph1, graph2, graph3 = graphs
# Test
self.add(graph1)
graph1[0].set_opacity(0)
self.play(
frame.animate.reorient(20, 70),
graph1[0].animate.set_opacity(0.5),
TransformFromCopy(graph1[1].start, graph1[1]),
run_time=3
)
self.play(
frame.animate.reorient(-20),
run_time=7,
)
graph1.save_state()
self.play(Transform(graph1, graph2), run_time=2)
self.play(Transform(graph1, graph3), run_time=2)
self.play(Restore(graph1), run_time=2)
self.wait(2)
self.play(
FadeOut(graph1[0]),
Restore(graph1[1]),
frame.animate.reorient(0, 0),
run_time=3,
)
# Radial symmetry
blob = Circle(radius=0.2)
blob.set_stroke(TEAL, 2)
blob.set_fill(TEAL, 0.5)
blob.move_to(plane.c2p(0.5, 0.5))
arrow = FillArrow(ORIGIN, DL)
arrow.next_to(blob, UR, buff=0)
arrow.set_fill(RED, 1)
arrow.set_backstroke(width=2)
radial_line = DashedLine(plane.c2p(0, 0), blob.get_center(), dash_length=0.025)
radial_line.set_stroke(TEAL, 2)
self.play(
dartboard.animate.set_opacity(0.2),
DrawBorderThenFill(blob),
GrowArrow(arrow)
)
self.wait()
self.play(ShowCreation(radial_line))
self.play(
Rotate(blob, TAU, about_point=plane.get_origin(), run_time=6),
Rotate(radial_line, TAU, about_point=plane.get_origin(), run_time=6),
MaintainPositionRelativeTo(arrow, blob),
)
self.wait()
self.play(
LaggedStartMap(FadeOut, VGroup(blob, arrow, radial_line)),
dartboard.animate.set_opacity(0.75)
)
self.play(
self.frame.animate.set_gamma(-0.75 * PI),
rate_func=there_and_back,
run_time=6,
)
self.wait()
# Ambient randomness
self.wait(0.1 * self.n_points)
def get_plane(self):
plane = NumberPlane(
(-2, 2), (-2, 2),
background_line_style=dict(stroke_color=GREY, stroke_width=1, stroke_opacity=1)
)
plane.set_height(7)
plane.to_edge(DOWN, buff=0.25)
plane.add(Tex("x").next_to(plane.x_axis.get_right(), RIGHT, SMALL_BUFF))
plane.add(Tex("y").next_to(plane.y_axis.get_top(), UP, SMALL_BUFF))
return plane
def get_dartboard(self, plane):
dartboard = Dartboard()
dartboard.match_height(plane.axes)
dartboard.move_to(plane.axes)
dartboard.set_opacity(0.75)
return dartboard
def add_random_points_anim(self, plane):
coords = np.random.normal(0, 0.5, (self.n_points, 2))
dots = Group(*(
GlowDot(plane.c2p(x, y), glow_factor=4.0, radius=0.3)
for x, y in coords
))
anim = LaggedStart(*(
FadeIn(dot, rate_func=there_and_back)
for dot in dots
), lag_ratio=1 / self.n_dots_per_moment, run_time=self.n_points / self.n_dots_per_moment)
anim_mob = turn_animation_into_updater(anim)
self.add(anim_mob)
class FaintDartboard(TwoDGaussianAsADistribution):
n_points = 1000
n_dots_per_moment = 5
def construct(self):
frame = self.frame
plane = self.get_plane()
plane.set_flat_stroke(False)
self.add(plane)
dartboard = self.get_dartboard(plane)
dartboard.set_opacity(0.15)
self.add(dartboard)
self.add_random_points_anim(plane)
self.wait(0.1 * self.n_points)
class ShowXYCoordinate(TwoDGaussianAsADistribution):
def construct(self):
plane = self.get_plane()
# self.add(plane) # Remove
# Test
x, y = (-1.5, 0.5)
dot = Dot(plane.c2p(x, y))
dot.set_color(TEAL)
r_line = Line(plane.get_origin(), dot.get_center())
r_line.set_stroke(RED, 3)
x_line = Line(plane.get_origin(), plane.c2p(x, 0))
x_line.set_stroke(BLUE, 5)
y_line = Line(plane.c2p(x, 0), plane.c2p(x, y))
y_line.set_stroke(YELLOW, 5)
x_label = Tex("x", color=BLUE).next_to(x_line, DOWN, SMALL_BUFF)
y_label = Tex("y", color=YELLOW).next_to(y_line, LEFT, SMALL_BUFF)
r_label = Tex("r", color=RED).next_to(r_line.get_center(), UR, SMALL_BUFF)
self.play(
FadeIn(dot),
ShowCreation(x_line),
FadeIn(x_label, 0.5 * LEFT),
)
self.add(y_line, dot)
self.play(
ShowCreation(y_line),
FadeIn(y_label, 0.5 * UP),
)
self.wait()
self.add(r_line, dot)
self.play(
ShowCreation(r_line),
FadeIn(r_label, shift=0.5 * normalize(r_line.get_vector()))
)
self.wait()
class IndependentCoordinates(TwoDGaussianAsADistribution):
n_iterations = 20
random_seed = 1
def construct(self):
frame = self.frame
plane = self.get_plane()
dartboard = self.get_dartboard(plane)
dartboard.set_opacity(0.15)
self.add(plane, dartboard)
x_tip = ArrowTip(angle=90 * DEGREES).scale(0.5).set_color(BLUE)
y_tip = ArrowTip(angle=0).scale(0.5).set_color(YELLOW)
x_tip.move_to(plane.get_origin(), UP)
y_tip.move_to(plane.get_origin(), RIGHT)
for tip in x_tip, y_tip:
tip.set_opacity(0)
tip.save_state()
for n in range(self.n_iterations):
# Test
x_tip.restore()
y_tip.restore()
# xs = np.random.normal(0, 1, 10)
# ys = np.random.normal(0, 1, 10)
# x = xs[-2]
# y = ys[-2]
x = np.random.normal(0, 0.5)
y = np.random.normal(0, 0.5)
if y < 0:
x_tip.flip(RIGHT, about_point=plane.get_origin())
if x < 0:
y_tip.flip(UP, about_point=plane.get_origin())
lines = VGroup(
DashedLine(plane.c2p(x, 0), plane.c2p(x, y), dash_length=0.05),
DashedLine(plane.c2p(0, y), plane.c2p(x, y), dash_length=0.05),
)
lines.set_stroke(WHITE, 2)
dot = GlowDot().move_to(plane.c2p(x, y))
self.play(LaggedStart(
x_tip.animate.match_x(dot).set_opacity(1),
y_tip.animate.match_y(dot).set_opacity(1),
lag_ratio=0.5,
))
self.play(
*map(ShowCreation, lines),
FadeIn(dot),
run_time=0.5
)
self.wait(0.5)
# self.play(UpdateFromAlphaFunc(x_tip, lambda m, a: m.match_x(
# plane.c2p(xs[integer_interpolate(0, len(xs) - 1, a)[0]], 0)
# )))
# self.play(UpdateFromAlphaFunc(y_tip, lambda m, a: m.match_y(
# plane.c2p(0, ys[integer_interpolate(0, len(ys) - 1, a)[0]])
# )))
# self.add(lines, dot)
# self.wait()
self.play(LaggedStartMap(FadeOut, Group(
x_tip, y_tip, lines, dot
)), run_time=1)
class ShowPointR0(TwoDGaussianAsADistribution):
def construct(self):
plane = self.get_plane()
plane.axes.set_stroke(width=1)
dartboard = self.get_dartboard(plane)
dartboard.set_opacity(0.15)
self.add(plane, dartboard)
# Show point
x, y = (0.7, 0.5)
r = get_norm([x, y])
dot = Dot(plane.c2p(x, y), radius=0.05)
r_line = Line(plane.get_origin(), dot.get_center())
r_line.set_stroke(RED, 3)
coord_label = Tex("(x, y)", t2c={"x": BLUE, "y": YELLOW})
coord_label.next_to(dot, UR, buff=SMALL_BUFF)
coord_label.set_backstroke()
new_coord_label = Tex("(r, 0)", t2c={"r": RED, "0": YELLOW})
new_coord_label.next_to(plane.c2p(r, 0), UR, SMALL_BUFF)
angle = math.atan2(y, x)
self.add(r_line, dot, coord_label)
self.wait()
self.play(
Rotate(r_line, -angle, about_point=plane.get_origin()),
Rotate(dot, -angle, about_point=plane.get_origin()),
ReplacementTransform(coord_label, new_coord_label, path_arc=-angle, time_span=(1, 2)),
run_time=3
)
self.wait()
class RescaleG(InteractiveScene):
def construct(self):
def g(x):
return 0.5 * math.exp(-x**4 + x**2)
# Setup
axes = Axes((-2, 2), (0, 1, 0.25), width=6, height=3)
axes.x_axis.add_numbers()
axes.y_axis.add_numbers(num_decimal_places=2, excluding=[0], font_size=16)
self.add(axes)
curve = axes.get_graph(g)
curve.make_smooth()
curve.set_stroke(BLUE, 3)
curve.save_state()
curve.generate_target()
curve.target.stretch(1 / g(0), 1, about_edge=DOWN)
label1 = TexText(R"$g(0) \ne 1$", font_size=36)
label2 = TexText(R"$g(0) = 1$", font_size=36)
label3 = TexText(R"Later we \\ re-scale anyway", font_size=36)
labels = [label1, label2, label3]
curves = [curve, curve.target, curve]
for label, crv in zip(labels, curves):
label.next_to(crv.get_top(), UR)
self.play(
ShowCreation(curve),
FadeIn(label1, lag_ratio=0.1),
)
self.wait()
self.play(
MoveToTarget(curve),
FadeTransform(label1, label2),
)
self.wait(2)
self.play(
Restore(curve),
FadeTransform(label2, label3),
)
self.play(curve.animate.set_fill(BLUE, 0.5))
self.wait()
class ManyDifferentFs(InteractiveScene):
def construct(self):
axes = Axes((0, 4), (0, 1, 0.25), width=6, height=3)
axes.x_axis.add_numbers()
axes.y_axis.add_numbers(num_decimal_places=2, excluding=[0], font_size=16)
self.add(axes)
# Many curves
curves = VGroup(
axes.get_graph(lambda x: math.exp(-x**2)),
axes.get_graph(lambda x: 1 / (1 + x**2)),
axes.get_graph(lambda x: math.exp(-x)),
axes.get_graph(lambda x: 0.5 * math.exp(-x**4 + x**2)),
axes.get_graph(lambda x: math.cos(x)**2 / (x + 1)),
axes.get_graph(lambda x: (1 / 2) * (x**2) * np.exp(-x)),
)
curves.set_stroke(RED, 3)
func_names = VGroup(
Tex("e^{-x^2}"),
Tex(R"1 \over (1 + x^2)"),
Tex("e^{-x}"),
Tex(R"\frac{1}{2} e^{-x^4 + x^2}"),
Tex(R"\cos^2(x) \over (x + 1)"),
Tex(R"\frac{1}{2} x^2 e^{-x}"),
)
func_names.move_to(axes.get_top())
curve = curves[0]
name = func_names[0]
self.play(
ShowCreation(curve),
FadeIn(name, 0.5 * UP)
)
self.wait()
for new_curve, new_name in zip(curves[1:], func_names[1:]):
self.play(
Transform(curve, new_curve),
TransformMatchingTex(name, new_name, run_time=1)
)
name = new_name
self.wait()
class VariableInputs(InteractiveScene):
def construct(self):
equation = Tex(R"f(\sqrt{(1.00)^2 + (0.00)^2}) = f(1.00)f(0.00)")
xs = equation.make_number_changable("1.00", replace_all=True)
ys = equation.make_number_changable("0.00", replace_all=True)
xs.set_color(BLUE)
ys.set_color(YELLOW)
x_tracker = ValueTracker(1.0)
y_tracker = ValueTracker(1.0)
for mob in xs:
mob.add_updater(lambda m: m.set_value(x_tracker.get_value()))
for mob in ys:
mob.add_updater(lambda m: m.set_value(y_tracker.get_value()))
self.add(equation)
for n in range(30):
self.play(x_tracker.animate.set_value(random.random() * 10))
self.play(y_tracker.animate.set_value(random.random() * 10))
self.wait(0.5)
class RationalNumbers(InteractiveScene):
def construct(self):
# Interval
interval = UnitInterval((0, 1, 1))
interval.center()
interval.add_numbers(font_size=36, num_decimal_places=0)
self.add(interval)
# Add rational points
pairs = []
max_n = 100
for n in range(2, max_n):
for k in range(1, n):
if math.gcd(n, k) == 1:
pairs.append((k, n))
lines = VGroup()
line_groups = VGroup(*(VGroup() for n in range(max_n - 2)))
labels = VGroup()
frac_template = Tex(R"1 \over 2")
frac_template.make_number_changable("1")
frac_template.make_number_changable("2")
for pair in pairs:
k, n = pair
line = Line(DOWN, UP)
line.set_height(2.0 / n)
line.set_stroke(TEAL, width=4.0 / math.sqrt(n))
line.move_to(interval.n2p(k / n))
lines.add(line)
line_groups[n - 2].add(line)
if n < 15:
frac = frac_template.copy()
frac[0].set_value(k)
frac[2].set_value(n)
frac[1].match_width(frac[2])
frac.set_height(1.5 / n)
frac.next_to(line, UP, SMALL_BUFF)
labels.add(frac)
line_groups.set_submobject_colors_by_gradient(BLUE, TEAL)
for i, j in [(0, 9), (9, 27), (27, len(labels))]:
self.play(
LaggedStartMap(FadeIn, lines[i:j], lag_ratio=0.75),
LaggedStartMap(FadeIn, labels[i:j], lag_ratio=0.75),
rate_func=linear,
run_time=3,
)
self.play(
LaggedStartMap(FadeIn, lines[27:], lag_ratio=0.75),
run_time=10,
rate_func=rush_into,
)
self.wait()
# Transition to real
real_line = Line(interval.n2p(0), interval.n2p(1))
real_line.insert_n_curves(50)
real_line.set_stroke([TEAL, BLUE, TEAL], width=[0, 3, 3, 0])
self.play(
LaggedStart(*(Rotate(line, -90 * DEGREES) for line in lines), lag_ratio=1 / len(lines), run_time=3),
FadeOut(labels, lag_ratio=0.1, run_time=1),
ShowCreation(real_line, time_span=(2, 3)),
)
self.wait()
class TwoProperties(InteractiveScene):
def construct(self):
# Name properties
properties = VGroup(
VGroup(
Text("Property 1"),
TexText(R"""
The probability (density) depends \\
only on the distance from the origin
""", alignment="", font_size=36, color=GREY_A),
),
VGroup(
Text("Property 2"),
TexText(R"""
The $x$ and $y$ coordinates are \\
independent from each other.
""", alignment="", font_size=36, color=GREY_A),
),
)
for prop in properties:
prop.arrange(DOWN, aligned_edge=LEFT)
properties.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT)
properties.to_corner(UL)
prop_boxes = VGroup(*(
SurroundingRectangle(prop[1]).set_fill(GREY_E, 1).set_stroke(RED, 1, 0.5)
for prop in properties
))
for prop, box in zip(properties, prop_boxes):
self.play(FadeIn(prop[0], lag_ratio=0.1), FadeIn(box))
# Formula
implies = Tex(R"\Downarrow", font_size=72)
implies.next_to(properties, DOWN, MED_LARGE_BUFF)
kw = dict(
t2c={"x": BLUE, "y": YELLOW, R"\sigma": RED, "{r}": RED}
)
form1, form2, form3 = forms = VGroup(
Tex(R"f_2(x, y) = e^{-(x^2 + y^2)}", **kw),
Tex(R"f_2(x, y) = e^{-(x^2 + y^2) / 2 \sigma^2}", **kw),
Tex(R"f_2(x, y) = {1 \over 4 \sigma^2 \pi} e^{-(x^2 + y^2) / 2 \sigma^2}", **kw),
)
forms.next_to(implies, DOWN, MED_SMALL_BUFF)
form1.save_state()
self.play(
Write(implies),
FadeIn(form1, DOWN)
)
self.wait()
self.play(TransformMatchingTex(form1, form2, run_time=1, lag_ratio=0.05))
self.wait(2)
self.play(TransformMatchingTex(form2, form3, run_time=1, lag_ratio=0.05))
self.wait(2)
form1.restore()
self.play(TransformMatchingTex(form3, form1, run_time=2, lag_ratio=0.05))
self.wait(2)
# Property 1
lhs = form1["f_2(x, y)"][0]
self.add(properties[0][1], prop_boxes[0])
self.play(
prop_boxes[0].animate.stretch(0, 0, about_edge=RIGHT).set_opacity(0),
FadeOut(implies),
FadeOut(form1["= e^{-(x^2 + y^2)}"]),
)
self.remove(prop_boxes[0])
self.add(lhs)
self.wait()
phrase = properties[0][1]["only on the distance"]
self.play(
FlashUnder(phrase, color=TEAL, buff=0),
phrase.animate.set_color(TEAL),
)
self.wait(2)
# Function of radius
lhs.generate_target()
lhs.target.to_edge(LEFT)
radial_rhs = Tex(R"= f({r})", **kw)
full_radial_rhs = Tex(R"= f(\sqrt{x^2 + y^2})", **kw)
radial_rhs.next_to(lhs.target, RIGHT, SMALL_BUFF)
full_radial_rhs.next_to(radial_rhs, RIGHT, MED_SMALL_BUFF)
full_radial_rhs.shift((radial_rhs["="].get_y() - full_radial_rhs["="].get_y()) * UP)
lhs_rect = SurroundingRectangle(lhs)
f_rect = SurroundingRectangle(radial_rhs["f"], buff=0.05)
f_rect.set_stroke(BLUE, 2)
f_words = Text("Some single-variable function", font_size=36)
f_words.next_to(f_rect, UP, SMALL_BUFF, aligned_edge=LEFT)
f_words.match_color(f_rect)
self.play(ShowCreation(lhs_rect))
self.wait()
self.play(lhs_rect.animate.replace(lhs[1], stretch=True).set_stroke(width=1).scale(1.1))
self.play(FadeOut(lhs_rect))
self.wait()
self.play(
MoveToTarget(lhs),
Write(radial_rhs),
)
self.play(
ShowCreation(f_rect),
FadeIn(f_words, lag_ratio=0.1)
)
self.wait(2)
self.play(FadeOut(f_words), FadeOut(f_rect))
self.play(TransformMatchingTex(radial_rhs.copy(), full_radial_rhs))
self.wait(2)
# Property 2
self.add(properties[1][1], prop_boxes[1])
self.play(
prop_boxes[1].animate.stretch(0, 0, about_edge=RIGHT).set_opacity(0),
)
self.remove(prop_boxes[0])
self.wait()
phrase = properties[1][1]["independent"]
self.play(
FlashUnder(phrase, color=TEAL, buff=0),
phrase.animate.set_color(TEAL)
)
self.wait()
# Factored expression
lhs.generate_target()
lhs.target.next_to(properties, DOWN, buff=0.7, aligned_edge=LEFT)
radial_rhss = VGroup(radial_rhs, full_radial_rhs)
factored_rhs = Tex(R"= g(x) h(y)", **kw)
simpler_rhs = Tex(R"= g(x) g(y)", **kw)
for rhs in factored_rhs, simpler_rhs:
rhs.next_to(lhs.target, RIGHT)
g_box = SurroundingRectangle(factored_rhs["g(x)"], buff=0.05).set_stroke(BLUE, 2)
h_box = SurroundingRectangle(factored_rhs["h(y)"], buff=0.05).set_stroke(YELLOW, 2)
g_words = TexText("Distribution of $x$", font_size=30).next_to(g_box, DOWN, 0.2)
h_words = TexText("Distribution of $y$", font_size=30).next_to(h_box, DOWN, 0.2)
self.play(
MoveToTarget(lhs),
radial_rhss.animate.to_edge(LEFT).shift(0.5 * DOWN).set_opacity(0.35),
)
self.play(
TransformMatchingShapes(lhs.copy(), factored_rhs)
)
self.wait()
self.play(
ShowCreation(g_box),
FadeIn(g_words)
)
self.wait()
self.play(
ReplacementTransform(g_box, h_box),
ReplacementTransform(g_words, h_words),
)
self.wait()
self.play(FadeOut(h_box), FadeOut(h_words))
self.wait()
self.play(
FadeOut(factored_rhs["h(y)"], 0.5 * UP),
FadeIn(simpler_rhs["g(y)"], 0.5 * UP),
)
self.wait()
self.remove(factored_rhs)
self.add(simpler_rhs)
# Show proportionality
arrow = Arrow(simpler_rhs, radial_rhs)
self.play(
GrowArrow(arrow),
radial_rhs.animate.set_opacity(1),
FadeOut(full_radial_rhs),
)
self.wait()
radial_rhs.generate_target()
radial_rhs.target.next_to(lhs, RIGHT),
self.play(
MoveToTarget(radial_rhs),
simpler_rhs.animate.next_to(radial_rhs.target, RIGHT),
Uncreate(arrow),
)
self.wait()
xs = VGroup(lhs[3], simpler_rhs["x"][0][0])
ys = VGroup(lhs[5], simpler_rhs["y"][0][0])
rs = Tex("{r}", **kw).replicate(2)
zeros = Tex("0", **kw).replicate(2)
zeros.set_color(YELLOW)
for x, r in zip(xs, rs):
r.move_to(x, DOWN)
for x, y, zero in zip(xs, ys, zeros):
zero.move_to(y)
zero.align_to(x, DOWN)
const_rect = SurroundingRectangle(simpler_rhs["g(y)"], buff=0.05)
const_rect.set_stroke(YELLOW, 1)
const_words = Text("Some constant", font_size=36)
const_words.match_color(const_rect)
const_words.next_to(const_rect, DOWN)
self.play(
LaggedStartMap(FadeOut, VGroup(*xs, *ys), shift=0.5 * UP),
LaggedStartMap(FadeIn, VGroup(*rs, *zeros), shift=0.5 * UP),
)
self.wait()
self.play(
ShowCreation(const_rect),
FadeIn(const_words)
)
self.wait()
# Assume this constant is 1
assumption = TexText("Assume this is 1", font_size=36)
assumption.move_to(const_words)
assumption.match_color(const_words)
f_eq_g = Tex("f = g", **kw)
f_eq_g.next_to(radial_rhs, DOWN, LARGE_BUFF)
f_rhs = Tex(R"= f(x)f(y)", **kw)
f_rhs.move_to(simpler_rhs, LEFT)
gs = simpler_rhs["g"]
fs = f_rhs["f"]
self.play(
FadeIn(assumption, 0.5 * DOWN),
FadeOut(const_words, 0.5 * DOWN),
)
self.wait()
self.play(
TransformFromCopy(
VGroup(radial_rhs[1], *simpler_rhs[:2]),
f_eq_g
)
)
self.wait()
self.play(
LaggedStartMap(FadeIn, VGroup(*xs, *ys), shift=0.5 * DOWN),
LaggedStartMap(FadeOut, VGroup(*rs, *zeros), shift=0.5 * DOWN),
FadeOut(const_rect),
FadeOut(assumption),
)
self.wait()
self.play(
TransformMatchingShapes(f_eq_g[0].copy(), fs),
ReplacementTransform(simpler_rhs, f_rhs),
)
self.wait()
# Highlight key equation
key_equation = VGroup(*radial_rhs[1:], *f_rhs)
self.play(
key_equation.animate.set_x(0.25 * FRAME_WIDTH).to_edge(UP),
FadeOut(f_eq_g),
FadeOut(lhs),
FadeOut(radial_rhs[0]),
)
self.play(FlashAround(key_equation, time_width=1, run_time=2))
full_radial_rhs.set_opacity(1)
full_radial_rhs.move_to(key_equation).shift(LEFT)
self.play(
GrowFromCenter(full_radial_rhs, lag_ratio=0.02),
radial_rhs[1:].animate.next_to(full_radial_rhs, LEFT, aligned_edge=DOWN),
key_equation[4:].animate.next_to(full_radial_rhs, RIGHT),
)
self.wait()
# Name as a functional equation
func_eq_name = Text("Functional\nequation")
func_eq_name.to_corner(UL)
func_eq_name.match_y(key_equation)
arrow = Arrow(func_eq_name, radial_rhs[1].get_left(), buff=0.25)
func_eq_name.to_edge(UP)
self.play(LaggedStartMap(FadeOut, properties, shift=LEFT, lag_ratio=0.2))
self.play(
Write(func_eq_name),
GrowArrow(arrow)
)
self.wait()
# Example
example_box = Rectangle(4, 3)
example_box.set_stroke(TEAL, 2)
example_box.set_fill(TEAL, 0.35)
example_box.to_corner(DL, buff=0)
example_word = Text("For example", font_size=30)
example_word.next_to(example_box.get_top(), DOWN, SMALL_BUFF)
example_f = Tex(R"f({r}) = e^{-{r}^2}", **kw)
example_f.scale(0.75)
example_f.next_to(example_word, DOWN, MED_LARGE_BUFF)
self.play(
FadeIn(example_box),
FadeIn(example_word, 0.5 * DOWN)
)
self.play(
TransformFromCopy(key_equation[:4], example_f[:4]),
GrowFromPoint(example_f[4:], key_equation.get_left()),
)
self.wait()
# Define h
let = Text("Let")
h_def = Tex(R"h({x}) = f(\sqrt{{x}})", **kw)
h_def.next_to(key_equation, DOWN, LARGE_BUFF)
let.next_to(h_def, LEFT, MED_LARGE_BUFF)
h_def2 = Tex(R"h({x}^2) = f({x})", **kw)
h_def2.next_to(h_def["h"], DOWN, MED_LARGE_BUFF, aligned_edge=LEFT)
h_eq = Tex(R"h(x^2 + y^2) = h(x^2) h(y^2)", **kw)
h_eq.to_corner(UR)
h_eq.to_edge(RIGHT, buff=1.25)
example_h = Tex(R"h({r}) = e^{-{r}}", **kw)
example_h.scale(0.75)
example_h.next_to(example_f, DOWN, MED_LARGE_BUFF)
self.play(FadeIn(h_def, DOWN), Write(let))
self.wait()
self.play(TransformMatchingTex(
h_def.copy(), h_def2,
key_map={R"\sqrt": "^2"},
run_time=1
))
self.wait()
self.play(
TransformFromCopy(h_def, example_h)
)
self.wait(2)
self.play(
VGroup(key_equation, full_radial_rhs).animate.scale(0.8).to_edge(LEFT),
VGroup(let, h_def, h_def2).animate.scale(0.8).to_edge(LEFT),
FadeOut(func_eq_name, LEFT),
Uncreate(arrow),
)
self.play(
TransformMatchingShapes(
VGroup(*full_radial_rhs[1:], *f_rhs).copy(),
h_eq
)
)
self.wait()
# Exponential property
sum_box = SurroundingRectangle(h_eq["x^2 + y^2"])
prod_box = SurroundingRectangle(h_eq["h(x^2) h(y^2)"])
sum_words = Text("Adding inputs", font_size=30)
sum_words.next_to(sum_box, DOWN)
prod_words = Text("Multiplying outputs", font_size=30)
prod_words.next_to(prod_box, DOWN)
VGroup(sum_box, prod_box).set_stroke(TEAL, 2)
VGroup(sum_words, prod_words).set_color(TEAL)
self.play(
ShowCreation(sum_box),
FadeIn(sum_words, lag_ratio=0.1),
)
self.wait()
self.play(
ReplacementTransform(sum_box, prod_box),
FadeTransform(sum_words, prod_words),
)
self.wait()
self.play(FadeOut(prod_box), FadeOut(prod_words))
self.wait()
# Multi-input property
implies = Tex(R"\Downarrow", font_size=72)
implies.next_to(h_eq, DOWN)
full_h_eq = Tex(R"h(x_1 + x_2 + \cdots + x_n) = h(x_1)h(x_2) \cdots h(x_n)")
for s, color in zip(["1", "2", "n"], color_gradient([BLUE, YELLOW], 3)):
full_h_eq[f"x_{s}"].set_color(color)
full_h_eq.scale(0.75)
full_h_eq.next_to(implies, DOWN)
self.play(
Write(implies),
FadeIn(full_h_eq, DOWN),
)
self.wait()
# Whole numbers
implies2 = implies.copy()
implies2.next_to(full_h_eq, DOWN, buff=MED_LARGE_BUFF)
five_eq = Tex(R"h(5) &= h(1 + 1 + 1 + 1 + 1) \\ &= h(1)h(1)h(1)h(1)h(1) = h(1)^5")
five_eq.next_to(implies2, DOWN)
five_eq.to_edge(RIGHT)
n_eq = Tex(R"h(n) = h(1 + \cdots + 1) = h(1) \cdots h(1) = h(1)^n")
n_eq.scale(0.75)
n_eq.next_to(implies2, DOWN, MED_LARGE_BUFF)
sum_brace = Brace(n_eq[R"1 + \cdots + 1"], UP, SMALL_BUFF)
sum_tex = sum_brace.get_tex(R"n \text{ times}", buff=SMALL_BUFF).scale(0.5, about_edge=DOWN)
prod_brace = Brace(n_eq[R"h(1) \cdots h(1)"], UP, SMALL_BUFF)
prod_tex = prod_brace.get_tex(R"n \text{ times}", buff=SMALL_BUFF).scale(0.5, about_edge=DOWN)
for tex in n_eq, sum_tex, prod_tex:
tex["n"].set_color(BLUE)
self.play(Write(five_eq["h(5)"]))
self.wait()
self.play(
TransformFromCopy(five_eq["h("][0], five_eq["h("][1]),
TransformFromCopy(five_eq[")"][0], five_eq[")"][1]),
Write(five_eq["="][0]),
)
self.play(ShowIncreasingSubsets(five_eq["1 + 1 + 1 + 1 + 1"][0]))
self.wait()
self.play(
FadeTransform(
five_eq["= h(1 + 1 + 1 + 1 + 1)"].copy(),
five_eq["= h(1)h(1)h(1)h(1)h(1)"],
)
)
self.wait()
self.play(Write(five_eq["= h(1)^5"]))
self.wait()
self.play(FadeOut(five_eq), FadeIn(n_eq), FadeIn(implies2))
self.play(LaggedStart(
GrowFromCenter(sum_brace),
FadeIn(sum_tex, 0.25 * DOWN),
GrowFromCenter(prod_brace),
FadeIn(prod_tex, 0.25 * DOWN),
))
self.wait()
# Exponential equation
exp_eq1 = Tex(R"h(n) = h(1)^n")
exp_eq2 = Tex(R"h(n) = b^n")
for eq in exp_eq1, exp_eq2:
eq["n"].set_color(BLUE)
exp_eq1.next_to(n_eq, DOWN, MED_LARGE_BUFF)
exp_eq2.move_to(exp_eq1, LEFT)
h1_rect = SurroundingRectangle(exp_eq1["h(1)"], buff=0.05)
h1_rect.set_stroke(YELLOW, 1)
h1_words = Text("Some number", font_size=30)
h1_words.match_color(h1_rect)
h1_words.next_to(h1_rect, DOWN, SMALL_BUFF)
self.play(
TransformFromCopy(n_eq["h(n)"], exp_eq1["h(n)"]),
TransformFromCopy(n_eq["= h(1)^n"], exp_eq1["= h(1)^n"]),
)
self.wait()
self.play(ShowCreation(h1_rect), FadeIn(h1_words))
self.wait()
self.play(
TransformMatchingTex(exp_eq1, exp_eq2),
FadeOut(h1_rect, scale=0.5),
FadeOut(h1_words, scale=0.5),
)
self.wait()
# Show exercises
self.play(
exp_eq2.animate.next_to(implies2, DOWN),
FadeOut(VGroup(n_eq, sum_brace, sum_tex, prod_brace, prod_tex), UP),
)
rational_eq = Tex(R"h(p / q) = b^{\,p / q}")
rational_eq["p / q"].set_color(RED)
rational_eq.move_to(exp_eq2)
exercise = TexText(R"Exercise: Show this is also true for rational inputs, $p / q$")
exercise["p / q"].set_color(RED)
hint = TexText(R"Hint, think about $h\left(\frac{p}{q} + \cdots + \frac{p}{q} \right)$")
exercise.next_to(rational_eq, DOWN, LARGE_BUFF)
exercise.to_edge(RIGHT)
hint.scale(0.7)
hint.set_fill(GREY_A)
hint.next_to(exercise, DOWN)
self.play(
Write(exercise),
FadeOut(VGroup(example_box, example_word, example_f, example_h), shift=DL),
)
self.wait()
pq_target = rational_eq["p / q"].copy()
self.play(
TransformMatchingTex(exp_eq2, rational_eq),
TransformMatchingShapes(exercise["p / q"].copy(), pq_target),
)
self.remove(pq_target)
self.wait()
self.play(FadeIn(hint, DOWN))
self.wait(2)
self.play(LaggedStart(
FadeOut(exercise, 0.5 * DOWN),
FadeOut(hint, 0.5 * DOWN),
lag_ratio=0.25,
))
# Continuity
assumption = TexText(R"Assuming $f$ (and hence also $h$) \\ is continuous...", font_size=36)
assumption.next_to(rational_eq, LEFT, buff=2.0, aligned_edge=UP)
assumption.shift(0.5 * DOWN)
hx_eq = Tex("h(x) = b^x", **kw)
hx_eq.move_to(rational_eq)
range1 = TexText(R"For all $x \in \mathds{R}$", **kw)
range2 = TexText(R"For all $x \in \mathds{R}^+$", **kw)
for ran in range1, range2:
ran.scale(0.75)
ran.next_to(hx_eq, DOWN, MED_LARGE_BUFF)
arrow = Arrow(assumption, hx_eq)
self.play(FadeIn(assumption, lag_ratio=0.1))
self.wait()
self.play(
GrowArrow(arrow),
TransformMatchingTex(rational_eq, hx_eq)
)
self.play(FadeIn(range1, DOWN))
self.wait()
self.play(FadeTransform(range1, range2))
self.wait()
# Swap out for e
hx_eq2 = Tex(R"h(x) = e^{{c} x}", **kw)
hx_eq2.move_to(hx_eq)
hx_eq2["c"].set_color(RED)
b_rect = SurroundingRectangle(hx_eq["b"], buff=0.05)
b_rect.set_stroke(PINK, 2)
b_words = Text("Some constant", font_size=30)
b_words.next_to(b_rect, DOWN, SMALL_BUFF, LEFT)
b_words.match_color(b_rect)
self.play(
FadeOut(assumption, LEFT),
Uncreate(arrow),
FadeOut(range2, LEFT),
ShowCreation(b_rect),
Write(b_words, run_time=1)
)
self.wait()
c = hx_eq2["{c}"][0][0]
c_copy = c.copy()
c.set_opacity(0)
self.play(
ReplacementTransform(b_rect, c_copy),
TransformMatchingTex(hx_eq, hx_eq2, key_map={"b": "e"}),
FadeOut(b_words, 0.2 * DOWN),
)
self.remove(c_copy)
c.set_opacity(1)
self.add(hx_eq2)
self.wait()
# Write final form for f
implies3 = implies2.copy()
implies3.rotate(-90 * DEGREES)
implies3.next_to(hx_eq2, LEFT)
f_form = Tex(R"f(x) = e^{cx^2}", **kw)
f_form["c"].set_color(RED)
f_form.next_to(implies3, LEFT)
f_form.align_to(hx_eq2, DOWN)
self.play(
Write(implies3),
TransformMatchingTex(hx_eq2.copy(), f_form, run_time=1)
)
self.wait()
f_form.generate_target()
f_form.target.scale(1.5, about_edge=RIGHT)
rect = SurroundingRectangle(f_form.target)
rect.set_stroke(YELLOW, 2)
self.play(
MoveToTarget(f_form),
FlashAround(f_form.target, time_width=1, run_time=2, stroke_width=5),
ShowCreation(rect, run_time=2),
)
self.wait()
class VariableC(InteractiveScene):
c_values = [1.0, 0.5, -1.0, -0.7, -0.5, 0.25, -0.2, -0.4, -0.9, -0.1, 0.5, 0.3, 0.1]
def construct(self):
axes = self.get_axes()
self.add(axes)
curve = axes.get_graph(lambda x: self.func(x, 1))
curve.set_stroke(RED, 3)
self.add(curve)
label = self.get_label(axes)
self.add(label)
c_tracker, c_interval, c_tip, c_label = self.get_c_group()
get_c = c_tracker.get_value
c_interval.move_to(axes, UR)
c_interval.shift(0.5 * DOWN)
self.add(c_interval, c_tip, c_label)
axes.bind_graph_to_func(curve, lambda x: self.func(x, get_c()))
# Animate
for c in self.c_values:
self.play(c_tracker.animate.set_value(c), run_time=2)
self.wait()
def get_c_group(self):
c_tracker = ValueTracker(1)
get_c = c_tracker.get_value
c_interval = NumberLine(
(-1, 1, 0.25), width=3, tick_size=0.05, numbers_with_elongated_ticks=[-1, 0, 1],
)
c_interval.set_stroke(WHITE, 1)
c_interval.add_numbers([-1, 0, 1], num_decimal_places=1, font_size=16)
c_tip = ArrowTip(angle=-90 * DEGREES)
c_tip.scale(0.5)
c_tip.set_fill(RED)
c_tip.add_updater(lambda m: m.move_to(c_interval.n2p(get_c()), DOWN))
c_label = Tex("c = 1.00", t2c={"c": RED}, font_size=36)
c_label.make_number_changable("1.00")
c_label[-1].scale(0.8, about_edge=LEFT)
c_label.add_updater(lambda m: m[-1].set_value(get_c()))
c_label.add_updater(lambda m: m.next_to(c_tip, UP, aligned_edge=LEFT))
return [c_tracker, c_interval, c_tip, c_label]
def get_axes(self):
axes = Axes(
(-1, 5), (0, 4),
width=6, height=4,
)
return axes
def func(self, x, c):
return np.exp(c * x)
def get_label(self, axes):
label = Tex("e^{cx}", t2c={"c": RED})
label.next_to(axes.c2p(0, 2.7), RIGHT)
return label
class VariableCWithF(VariableC):
def get_axes(self):
axes = Axes(
(-4, 4), (0, 2),
width=8, height=3,
)
axes.add(VectorizedPoint(axes.c2p(0, 3)))
axes.center()
return axes
def func(self, x, c):
return np.exp(c * x * x)
def get_label(self, axes):
label = Tex("e^{cx^2}", t2c={"c": RED})
label.next_to(axes.c2p(0, 2), LEFT)
return label
class TalkAboutSignOfConstant3D(VariableCWithF):
def construct(self):
# Setup
frame = self.frame
frame.add_updater(lambda m: m.reorient(20 * math.cos(0.1 * self.time), 75))
axes = ThreeDAxes((-4, 4), (-4, 4), (0, 1), depth=2)
axes.set_width(10)
axes.set_depth(2, stretch=True)
axes.center()
self.add(axes)
label = Tex("f(r) = e^{cr^2}", t2c={"c": RED}, font_size=72)
label.next_to(ORIGIN, LEFT)
label.to_edge(UP)
label.fix_in_frame()
c_tracker, c_interval, c_tip, c_label = self.get_c_group()
get_c = c_tracker.get_value
c_interval.next_to(label, RIGHT, LARGE_BUFF)
c_interval.shift(0.5 * DOWN)
c_tracker.set_value(-1)
c_group = VGroup(c_interval, c_tip, c_label)
c_group.fix_in_frame()
# Graph
def get_graph(c):
surface = axes.get_graph(lambda x, y: np.exp(c * (x**2 + y**2)))
surface.always_sort_to_camera(self.camera)
surface.set_color(BLUE_E, 0.5)
mesh = SurfaceMesh(surface, (31, 31))
mesh.set_stroke(WHITE, 0.5, 0.5)
mesh.shift(0.001 * OUT)
x_slice = ParametricCurve(
lambda t: axes.c2p(t, 0, np.exp(c * t**2)),
t_range=(-4, 4, 0.1)
)
x_slice.set_stroke(RED, 2)
x_slice.set_flat_stroke(False)
return Group(mesh, surface, x_slice)
graph = get_graph(-1)
self.add(graph)
self.add(c_group)
self.add(label)
# Animations
for value in [-0.5, -0.8, -0.3, -1.0, -0.3, 0.05, 0.1, -0.3, -1.0, -0.7, -1.0]:
new_graph = get_graph(value)
self.play(
c_tracker.animate.set_value(value),
Transform(graph, new_graph),
run_time=5
)
self.wait()
class OldTalkAboutSignOfConstantScraps(InteractiveScene):
def construct(self):
plane.bind_graph_to_func(graph, lambda x: self.func(x, get_c()))
# Area
area = VMobject()
area.set_fill(RED, 0.5)
area.set_stroke(width=0)
def update_area(area):
area.set_points_as_corners([
plane.c2p(-4, 0),
*graph.get_anchors(),
plane.c2p(4, 0)
])
area.add_updater(update_area)
class OldTwoKeyProperties(InteractiveScene):
def construct(self):
# Setup equations
kw = dict(
t2c={"x": BLUE, "y": YELLOW, "{r}": RED, "{c}": PINK}
)
one_var = Tex("f_1(x) = e^{-x^2}", **kw)
two_var = Tex("f_2(x, y) = e^{-(x^2 + y^2)}", **kw)
factored = Tex("= f_1(x)f_1(y)", **kw)
factored_exp = Tex("= e^{-x^2} e^{-y^2}", **kw)
radial_exp = Tex(R"= e^{-{r}^2}", **kw)
radial = Tex(R"= f_1({r})", **kw)
radial_full = Tex(R"= f_1(\sqrt{x^2 + y^2})", **kw)
expressions = VGroup(
one_var,
two_var,
factored_exp,
factored,
radial_exp,
radial,
radial_full,
)
expressions.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT)
expressions.to_corner(UL)
expressions.set_backstroke()
rhs1 = VGroup(factored_exp, factored)
rhs2 = VGroup(radial_exp, radial, radial_full)
for rhs in [rhs1, rhs2]:
rhs.arrange(RIGHT)
rhs.next_to(two_var, RIGHT)
for mob in expressions[2:]:
mob.shift((two_var["="].get_y() - mob["="].get_y()) * UP)
# From one to two
self.add(one_var, two_var)
self.wait()
rects = VGroup(SurroundingRectangle(one_var), SurroundingRectangle(two_var))
rects.set_stroke(TEAL, 2)
rect_words = VGroup(Text("1 variable"), Text("2 variable"))
for rect, words in zip(rects, rect_words):
words.next_to(rect, RIGHT)
arrow = Arrow(
one_var.get_right(),
two_var.get_corner(UR) + 0.5 * LEFT,
path_arc=-PI,
)
words = Text("Two interpretations")
words.next_to(arrow, RIGHT)
self.play(
FadeIn(rects[0]),
FadeIn(rect_words[0]),
)
self.wait()
self.play(
ReplacementTransform(*rects),
FadeTransform(*rect_words),
)
self.wait()
two_var_copy = two_var.copy()
self.play(
FadeOut(rects[1]),
FadeOut(rect_words[1]),
ShowCreation(arrow),
TransformMatchingTex(one_var.copy(), two_var_copy, run_time=1),
Write(words, run_time=1)
)
self.remove(two_var_copy)
self.wait()
# Factored
self.play(LaggedStart(
Write(factored_exp["="]),
TransformFromCopy(one_var["e^{-x^2}"], factored_exp["e^{-x^2}"]),
TransformFromCopy(one_var["e^{-x^2}"], factored_exp["e^{-y^2}"]),
run_time=2,
lag_ratio=0.4,
))
self.wait()
self.play(LaggedStart(
Write(factored["="]),
TransformFromCopy(one_var["f_1(x)"], factored["f_1(x)"], path_arc=PI / 4),
TransformFromCopy(one_var["f_1(x)"], factored["f_1(y)"], path_arc=PI / 4),
run_time=3,
lag_ratio=0.5,
))
self.wait()
# Rearrange
rhs1.generate_target()
rhs1.target.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT)
rhs1.target.next_to(two_var["="], DOWN, MED_LARGE_BUFF, aligned_edge=LEFT)
rhs1.target.set_fill(opacity=0.5)
self.play(MoveToTarget(rhs1, path_arc=-PI / 2))
# Radial
self.play(
Write(radial_exp["="]),
TransformFromCopy(one_var["e^{-x^2}"], radial_exp["e^{-{r}^2}"]),
)
self.wait()
self.play(
Write(radial["="]),
TransformFromCopy(one_var["f_1(x)"], radial["f_1({r})"]),
)
self.wait()
self.play(FadeIn(radial_full, lag_ratio=0.1))
self.wait()
# Consolidate
prop = VGroup(factored, radial)
prop.generate_target()
prop.target.arrange(RIGHT)
prop.target.center().move_to(UP)
prop.target.set_opacity(1)
prop.target.scale(1.5)
prop.target[0]["="].set_opacity(0).scale(0, about_edge=RIGHT)
prop.target.shift(0.5 * LEFT)
f2_eq = VGroup(two_var, radial_exp, factored_exp)
f2_eq.generate_target()
f2_eq.target.set_fill(opacity=0.5)
f2_eq.target[-1].next_to(f2_eq.target[-2], RIGHT)
f2_eq.target.to_corner(UR)
one_var.generate_target()
one_var.target.scale(1.5, about_edge=UL)
self.play(LaggedStart(
FadeOut(arrow),
FadeOut(words),
MoveToTarget(prop),
MoveToTarget(f2_eq),
FadeOut(radial_full),
lag_ratio=0.15,
run_time=3,
))
self.wait()
self.play(
FlashAround(one_var.target, time_width=1.0, run_time=2),
MoveToTarget(one_var),
)
self.wait()
self.play(FlashUnder(factored[1:]))
self.wait()
self.play(FlashUnder(radial))
self.wait()
radial_full.set_opacity(1)
radial_full.scale(1.5)
radial_full.move_to(radial, LEFT)
radial_full.shift(0.1 * UP)
left_shift = 2.0 * LEFT
radial_full.shift(left_shift)
self.play(
FadeIn(radial_full),
radial.animate.next_to(radial_full, RIGHT),
factored.animate.shift(left_shift)
)
self.wait()
# Flip the question
prop = VGroup(factored, radial_full, radial)
prop.generate_target()
prop.target.scale(1 / 1.5).to_edge(UP).shift(1.5 * RIGHT)
question = Text("What are all the functions \n with this property?")
question.next_to(prop.target, DOWN, buff=1.5)
question.to_edge(LEFT)
arrow = Arrow(question, prop.target[0], buff=0.5)
self.play(
one_var.animate.scale(1 / 1.5).to_corner(DL).set_opacity(0.5),
FadeOut(f2_eq, UP),
MoveToTarget(prop),
)
self.play(
FadeIn(question, lag_ratio=0.1),
GrowArrow(arrow),
)
self.wait()
# Substitute h
h_eq = Tex(R"h(x^2) h(y^2) = h(x^2 + y^2)", **kw)
h_eq.next_to(prop, DOWN, buff=MED_LARGE_BUFF)
h_eq.shift((prop[1]["="].get_x() - h_eq["="].get_x()) * RIGHT)
h_def = Tex(R"h(x) = f_1(\sqrt{x})", **kw)
h_def.to_edge(LEFT).match_y(h_eq)
h_def2 = Tex(R"h(x^2) = f_1(x)", **kw)
h_def2.next_to(h_def, DOWN, MED_LARGE_BUFF, aligned_edge=LEFT)
example_box = Rectangle(4, 4)
example_box.set_stroke(TEAL, 2)
example_box.set_fill(TEAL, 0.2)
example_box.to_corner(DL, buff=0)
example_word = Text("For example", font_size=30)
example_word.next_to(example_box.get_top(), DOWN, SMALL_BUFF)
one_var.generate_target()
one_var.target.set_opacity(1)
one_var.target.next_to(example_word, DOWN, MED_LARGE_BUFF)
one_var.target.to_edge(LEFT)
h_example = Tex(R"h(x) = e^{-x}", **kw)
h_example.next_to(one_var.target, DOWN, MED_LARGE_BUFF)
h_example.to_edge(LEFT)
self.play(
FadeOut(question, DOWN),
FadeOut(arrow, DOWN),
Write(h_def)
)
self.wait()
self.play(TransformMatchingTex(h_def.copy(), h_def2))
self.wait()
self.add(example_box, one_var)
self.play(
FadeIn(example_box),
FadeIn(example_word),
MoveToTarget(one_var),
)
self.wait()
self.play(FadeIn(h_example, DOWN))
self.wait()
self.play(FlashAround(prop[:2], time_width=1, run_time=2))
self.play(
TransformFromCopy(factored, h_eq["h(x^2) h(y^2)"][0]),
TransformFromCopy(radial_full, h_eq["= h(x^2 + y^2)"][0]),
)
self.wait()
# Exponential property
h_box = SurroundingRectangle(h_eq)
exp_words = Text("Exponential property!")
exp_words.next_to(h_box, DOWN)
sum_box = SurroundingRectangle(h_eq["x^2 + y^2"])
prod_box = SurroundingRectangle(h_eq["h(x^2) h(y^2)"])
sum_words = Text("Adding inputs", font_size=30)
sum_words.next_to(sum_box, DOWN)
prod_words = Text("Multiplying outputs", font_size=30)
prod_words.next_to(prod_box, DOWN)
VGroup(h_box, sum_box, prod_box).set_stroke(TEAL, 2)
VGroup(exp_words, sum_words, prod_words).set_color(TEAL)
self.play(
ShowCreation(sum_box),
FadeIn(sum_words, lag_ratio=0.1),
)
self.wait()
self.play(
ReplacementTransform(sum_box, prod_box),
FadeTransform(sum_words, prod_words),
)
self.wait()
self.play(
ReplacementTransform(prod_box, h_box),
FadeTransform(prod_words, exp_words),
)
self.wait()
# Exponent
implies = Tex(R"\Downarrow", font_size=72)
implies.next_to(h_eq, DOWN)
implies.rotate(PI)
h_exp = Tex(R"h(x) = a \cdot b^x", **kw)
h_exp.next_to(implies, DOWN)
h_exp2 = Tex(R"h(x) = a \cdot e^{{c}x}", **kw)
h_exp2.move_to(h_exp)
h_exp0 = Tex(R"h(x) = b^x", **kw)
h_exp0.move_to(h_exp)
assumption = TexText("Assuming $h$ is continuous", font_size=24)
assumption.next_to(implies, RIGHT)
b_rect = SurroundingRectangle(h_exp["b"], buff=SMALL_BUFF)
b_rect.set_stroke(PINK, 2)
b_words = Text("Some constant", font_size=30)
b_words.next_to(b_rect, DOWN, SMALL_BUFF, LEFT)
b_words.match_color(b_rect)
self.play(
FadeTransform(h_box, implies),
FadeTransform(exp_words, h_exp0),
)
self.wait()
self.play(TransformMatchingTex(h_exp0, h_exp, run_time=1))
self.wait()
self.play(
Rotate(implies, PI),
FadeIn(assumption, lag_ratio=0.1)
)
self.wait()
self.play(
ShowCreation(b_rect),
Write(b_words, run_time=1)
)
self.wait()
c = h_exp2["{c}"][0][0]
c_copy = c.copy()
c.set_opacity(0)
self.play(
ReplacementTransform(b_rect, c_copy),
TransformMatchingTex(h_exp, h_exp2),
FadeOut(b_words, 0.2 * DOWN),
)
self.remove(c_copy)
c.set_opacity(1)
self.add(h_exp2)
self.wait()
# Final form
implies2 = implies.copy()
implies2.next_to(h_exp2, DOWN, MED_LARGE_BUFF)
f_eq = Tex(R"f_1(x) = a \cdot e^{{c}x^2}", **kw)
f_eq.next_to(implies2, DOWN)
rect = SurroundingRectangle(f_eq)
rect.set_stroke(YELLOW, 1)
self.play(
TransformMatchingTex(h_exp2.copy(), f_eq),
Write(implies2, run_time=1)
)
self.wait()
self.play(
ShowCreation(rect, run_time=2),
FlashAround(f_eq, time_width=1, run_time=2, stroke_width=5),
)
self.wait()