mirror of
https://github.com/3b1b/videos.git
synced 2025-08-31 21:58:59 +00:00
1589 lines
51 KiB
Python
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()
|