2021-02-25 08:53:12 -08:00
|
|
|
from manim_imports_ext import *
|
|
|
|
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
def get_matrix_exponential(matrix, height=1.5, scalar_tex="t", **matrix_config):
|
|
|
|
elem = matrix[0][0]
|
|
|
|
if isinstance(elem, str):
|
|
|
|
mat_class = Matrix
|
|
|
|
elif isinstance(elem, int) or isinstance(elem, np.int64):
|
|
|
|
mat_class = IntegerMatrix
|
|
|
|
else:
|
|
|
|
mat_class = DecimalMatrix
|
|
|
|
|
|
|
|
matrix = mat_class(matrix, **matrix_config)
|
|
|
|
base = Tex("e")
|
|
|
|
base.set_height(0.4 * height)
|
|
|
|
matrix.set_height(0.6 * height)
|
|
|
|
matrix.move_to(base.get_corner(UR), DL)
|
|
|
|
result = VGroup(base, matrix)
|
|
|
|
if scalar_tex:
|
|
|
|
scalar = Tex(scalar_tex)
|
|
|
|
scalar.set_height(0.7 * base.get_height())
|
|
|
|
scalar.next_to(matrix, RIGHT, buff=SMALL_BUFF, aligned_edge=DOWN)
|
|
|
|
result.add(scalar)
|
|
|
|
return result
|
2021-02-25 08:53:12 -08:00
|
|
|
|
|
|
|
|
|
|
|
def get_vector_field_and_stream_lines(func, coordinate_system,
|
|
|
|
magnitude_range=(0.5, 4),
|
|
|
|
vector_opacity=0.75,
|
|
|
|
vector_thickness=0.03,
|
2021-03-18 17:27:51 -07:00
|
|
|
color_by_magnitude=False,
|
2021-02-25 08:53:12 -08:00
|
|
|
line_color=GREY_A,
|
|
|
|
line_width=3,
|
2021-03-18 17:27:51 -07:00
|
|
|
line_opacity=0.75,
|
|
|
|
sample_freq=5,
|
|
|
|
n_samples_per_line=10,
|
2021-02-25 08:53:12 -08:00
|
|
|
arc_len=3,
|
2021-03-18 17:27:51 -07:00
|
|
|
time_width=0.3,
|
2021-02-25 08:53:12 -08:00
|
|
|
):
|
|
|
|
vector_field = VectorField(
|
|
|
|
func, coordinate_system,
|
|
|
|
magnitude_range=magnitude_range,
|
|
|
|
vector_config={
|
|
|
|
"fill_opacity": vector_opacity,
|
|
|
|
"thickness": vector_thickness,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
stream_lines = StreamLines(
|
|
|
|
func, coordinate_system,
|
|
|
|
step_multiple=1.0 / sample_freq,
|
|
|
|
n_samples_per_line=n_samples_per_line,
|
|
|
|
arc_len=arc_len,
|
|
|
|
magnitude_range=magnitude_range,
|
|
|
|
color_by_magnitude=color_by_magnitude,
|
|
|
|
stroke_color=line_color,
|
|
|
|
stroke_width=line_width,
|
2021-03-18 17:27:51 -07:00
|
|
|
stroke_opacity=line_opacity,
|
2021-02-25 08:53:12 -08:00
|
|
|
)
|
|
|
|
animated_lines = AnimatedStreamLines(
|
|
|
|
stream_lines,
|
|
|
|
line_anim_config={
|
|
|
|
"time_width": time_width,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
return vector_field, animated_lines
|
|
|
|
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
def mat_exp(matrix, N=100):
|
|
|
|
curr = np.identity(len(matrix))
|
|
|
|
curr_sum = curr
|
|
|
|
for n in range(1, N):
|
|
|
|
curr = np.dot(curr, matrix) / n
|
|
|
|
curr_sum += curr
|
|
|
|
return curr_sum
|
|
|
|
|
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
def get_1d_equation():
|
|
|
|
return Tex(
|
|
|
|
"{d \\over d{t}} x({t}) = {r} \\cdot x({t})",
|
|
|
|
tex_to_color_map={
|
|
|
|
"{t}": GREY_B,
|
|
|
|
"{r}": BLUE,
|
|
|
|
"=": WHITE,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def get_2d_equation():
|
|
|
|
deriv = Tex("d \\over dt", tex_to_color_map={"t": GREY_B})
|
|
|
|
vect = Matrix(
|
|
|
|
[["x(t)"], ["y(t)"]],
|
|
|
|
bracket_h_buff=SMALL_BUFF,
|
|
|
|
bracket_v_buff=SMALL_BUFF,
|
|
|
|
element_to_mobject_config={
|
|
|
|
"tex_to_color_map": {"t": GREY_B},
|
|
|
|
"isolate": ["(", ")"]
|
|
|
|
}
|
|
|
|
)
|
|
|
|
deriv.match_height(vect)
|
|
|
|
equals = Tex("=")
|
|
|
|
matrix_mob = Matrix([["a", "b"], ["c", "d"]], h_buff=0.8)
|
|
|
|
matrix_mob.set_color(TEAL)
|
|
|
|
matrix_mob.match_height(vect)
|
|
|
|
equation = VGroup(deriv, vect, equals, matrix_mob, vect.deepcopy())
|
|
|
|
equation.arrange(RIGHT)
|
|
|
|
return equation
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
# Generic fLow scenes
|
|
|
|
|
|
|
|
|
|
|
|
class ExponentialPhaseFlow(Scene):
|
|
|
|
CONFIG = {
|
|
|
|
"field_config": {
|
|
|
|
"color_by_magnitude": False,
|
|
|
|
"magnitude_range": (0.5, 5),
|
|
|
|
"arc_len": 5,
|
|
|
|
},
|
|
|
|
"plane_config": {
|
|
|
|
"x_range": [-4, 4],
|
|
|
|
"y_range": [-2, 2],
|
|
|
|
"height": 8,
|
|
|
|
"width": 16,
|
|
|
|
},
|
|
|
|
"matrix": [
|
|
|
|
[1, 0],
|
|
|
|
[0, 1],
|
|
|
|
],
|
|
|
|
"label_height": 3,
|
|
|
|
"run_time": 30,
|
|
|
|
"slow_factor": 0.25,
|
|
|
|
}
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
mr = np.array(self.field_config["magnitude_range"])
|
|
|
|
self.field_config["magnitude_range"] = self.slow_factor * mr
|
|
|
|
plane = NumberPlane(**self.plane_config)
|
|
|
|
plane.add_coordinate_labels()
|
|
|
|
|
|
|
|
vector_field, animated_lines = get_vector_field_and_stream_lines(
|
|
|
|
self.func, plane,
|
|
|
|
**self.field_config,
|
|
|
|
)
|
|
|
|
|
|
|
|
box = Square()
|
|
|
|
box.replace(Line(plane.c2p(-1, -1), plane.c2p(1, 1)), stretch=True)
|
|
|
|
box.set_stroke(GREY_A, 1)
|
|
|
|
box.set_fill(BLUE_E, 0.8)
|
|
|
|
move_points_along_vector_field(box, self.func, plane)
|
|
|
|
|
|
|
|
basis_vectors = VGroup(
|
|
|
|
Vector(RIGHT, fill_color=GREEN),
|
|
|
|
Vector(UP, fill_color=RED),
|
|
|
|
)
|
|
|
|
basis_vectors[0].add_updater(lambda m: m.put_start_and_end_on(
|
|
|
|
plane.get_origin(),
|
|
|
|
box.pfp(7 / 8)
|
|
|
|
))
|
|
|
|
basis_vectors[1].add_updater(lambda m: m.put_start_and_end_on(
|
|
|
|
plane.get_origin(),
|
|
|
|
box.pfp(1 / 8)
|
|
|
|
))
|
|
|
|
|
|
|
|
self.add(plane)
|
|
|
|
self.add(vector_field)
|
|
|
|
self.add(animated_lines)
|
|
|
|
self.add(box)
|
|
|
|
self.add(*basis_vectors)
|
|
|
|
self.wait(self.run_time)
|
|
|
|
|
|
|
|
def func(self, x, y):
|
|
|
|
return self.slow_factor * np.dot([x, y], np.transpose(self.matrix))
|
|
|
|
|
|
|
|
def get_label(self):
|
|
|
|
exponential = get_matrix_exponential(self.matrix)
|
|
|
|
changing_t = DecimalNumber(0, color=YELLOW)
|
|
|
|
changing_t.match_height(exponential[2])
|
|
|
|
changing_t.move_to(exponential[2], DL)
|
|
|
|
exponential.replace_submobject(2, changing_t)
|
|
|
|
|
|
|
|
equals = Tex("=")
|
|
|
|
rhs = DecimalMatrix(
|
|
|
|
np.zeros((2, 2)),
|
|
|
|
element_to_mobject_config={"num_decimal_places": 3},
|
|
|
|
h_buff=1.8,
|
|
|
|
)
|
|
|
|
rhs.match_height(exponential)
|
|
|
|
|
|
|
|
equation = VGroup(
|
|
|
|
exponential,
|
|
|
|
equals,
|
|
|
|
rhs,
|
|
|
|
)
|
|
|
|
equation.arrange(RIGHT)
|
|
|
|
equation.to_corner(UL)
|
|
|
|
|
|
|
|
|
|
|
|
class ExponentialEvaluationWithTime(Scene):
|
|
|
|
flow_scene_class = ExponentialPhaseFlow
|
2021-02-25 08:53:12 -08:00
|
|
|
|
|
|
|
def construct(self):
|
2021-03-18 17:27:51 -07:00
|
|
|
flow_scene_attrs = merge_dicts_recursively(
|
|
|
|
ExponentialPhaseFlow.CONFIG,
|
|
|
|
self.flow_scene_class.CONFIG,
|
|
|
|
)
|
|
|
|
matrix = np.array(flow_scene_attrs["matrix"])
|
|
|
|
slow_factor = flow_scene_attrs["slow_factor"]
|
|
|
|
|
|
|
|
def get_t():
|
|
|
|
return slow_factor * self.time
|
|
|
|
|
|
|
|
exponential = get_matrix_exponential(matrix)
|
|
|
|
dot = Tex("\\cdot")
|
|
|
|
dot.move_to(exponential[2], LEFT)
|
|
|
|
changing_t = DecimalNumber(0)
|
|
|
|
changing_t.match_height(exponential[2])
|
|
|
|
changing_t.next_to(dot, RIGHT, SMALL_BUFF)
|
|
|
|
changing_t.align_to(exponential[1], DOWN)
|
|
|
|
changing_t.add_updater(lambda m: m.set_value(get_t()).set_color(YELLOW))
|
|
|
|
lhs = VGroup(*exponential[:2], dot, changing_t)
|
|
|
|
|
|
|
|
equals = Tex("=")
|
|
|
|
rhs = DecimalMatrix(
|
|
|
|
np.zeros((2, 2)),
|
|
|
|
element_to_mobject_config={"num_decimal_places": 2},
|
|
|
|
element_alignment_corner=ORIGIN,
|
|
|
|
h_buff=2.0,
|
|
|
|
)
|
|
|
|
for mob in rhs.get_entries():
|
|
|
|
mob.edge_to_fix = ORIGIN
|
|
|
|
|
|
|
|
rhs.match_height(lhs)
|
|
|
|
|
|
|
|
def update_rhs(rhs):
|
|
|
|
result = mat_exp(matrix * get_t())
|
|
|
|
for mob, value in zip(rhs.get_entries(), result.flatten()):
|
|
|
|
mob.set_value(value)
|
|
|
|
return rhs
|
|
|
|
|
|
|
|
rhs.add_updater(update_rhs)
|
|
|
|
|
|
|
|
equation = VGroup(lhs, equals, rhs)
|
|
|
|
equation.arrange(RIGHT)
|
|
|
|
equation.center()
|
|
|
|
|
|
|
|
self.add(equation)
|
|
|
|
self.wait(flow_scene_attrs["run_time"])
|
|
|
|
self.embed()
|
|
|
|
|
|
|
|
|
|
|
|
class CircularPhaseFlow(ExponentialPhaseFlow):
|
|
|
|
CONFIG = {
|
|
|
|
"matrix": [
|
|
|
|
[0, -1],
|
|
|
|
[1, 0],
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class CircularFlowEvaluation(ExponentialEvaluationWithTime):
|
|
|
|
flow_scene_class = CircularPhaseFlow
|
|
|
|
|
|
|
|
|
|
|
|
class EllipticalPhaseFlow(ExponentialPhaseFlow):
|
|
|
|
CONFIG = {
|
|
|
|
"matrix": [
|
|
|
|
[0, -3],
|
|
|
|
[1, 0],
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class EllipticalFlowEvaluation(ExponentialEvaluationWithTime):
|
|
|
|
flow_scene_class = EllipticalPhaseFlow
|
|
|
|
|
|
|
|
|
|
|
|
class HyperbolicPhaseFlow(ExponentialPhaseFlow):
|
|
|
|
CONFIG = {
|
|
|
|
"field_config": {
|
|
|
|
"sample_freq": 8,
|
|
|
|
},
|
|
|
|
"matrix": [
|
|
|
|
[1, 0],
|
|
|
|
[0, -1],
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class HyperbolicFlowEvaluation(ExponentialEvaluationWithTime):
|
|
|
|
flow_scene_class = HyperbolicPhaseFlow
|
|
|
|
|
|
|
|
|
|
|
|
class ShearPhaseFlow(ExponentialPhaseFlow):
|
|
|
|
CONFIG = {
|
|
|
|
"field_config": {
|
|
|
|
"sample_freq": 2,
|
|
|
|
"magnitude_range": (0.5, 8),
|
|
|
|
},
|
|
|
|
"plane_config": {
|
|
|
|
"x_range": [-8, 8],
|
|
|
|
"y_range": [-4, 4],
|
|
|
|
},
|
|
|
|
"matrix": [
|
|
|
|
[1, 1],
|
|
|
|
[0, 1],
|
|
|
|
],
|
|
|
|
"slow_factor": 0.1,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class ShearFlowEvaluation(ExponentialEvaluationWithTime):
|
|
|
|
flow_scene_class = ShearPhaseFlow
|
|
|
|
|
|
|
|
|
|
|
|
class DampedRotationPhaseFlow(ExponentialPhaseFlow):
|
|
|
|
CONFIG = {
|
|
|
|
"matrix": [
|
|
|
|
[-1, -1],
|
|
|
|
[1, 0],
|
|
|
|
],
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class DampedRotationFlowEvaluation(ExponentialEvaluationWithTime):
|
|
|
|
flow_scene_class = DampedRotationPhaseFlow
|
|
|
|
|
|
|
|
|
|
|
|
class FrameForFlow(Scene):
|
|
|
|
def construct(self):
|
|
|
|
self.add(FullScreenRectangle(fill_color=GREY_D))
|
|
|
|
screen_rect = ScreenRectangle()
|
|
|
|
screen_rect.set_height(5.5)
|
|
|
|
screen_rect.set_stroke(WHITE, 3)
|
|
|
|
screen_rect.set_fill(BLACK, 1)
|
|
|
|
screen_rect.to_edge(DOWN)
|
|
|
|
self.add(screen_rect)
|
|
|
|
|
|
|
|
|
|
|
|
class PrerequisitesWrapper(Scene):
|
|
|
|
def construct(self):
|
|
|
|
self.add(FullScreenRectangle())
|
|
|
|
title = Text("Helpful background knowledge")
|
|
|
|
title.to_edge(UP)
|
|
|
|
self.add(title)
|
|
|
|
|
|
|
|
screens = VGroup(*(ScreenRectangle() for x in range(2)))
|
|
|
|
screens.arrange(RIGHT, buff=LARGE_BUFF)
|
|
|
|
screens.set_width(FRAME_WIDTH - 1)
|
|
|
|
screens.move_to(DOWN)
|
|
|
|
screens.set_fill(BLACK, 1)
|
|
|
|
screens.set_stroke(WHITE, 2)
|
|
|
|
|
|
|
|
topics = VGroup(
|
|
|
|
TexText("Basics of $e^x$"),
|
|
|
|
TexText("How matrices act\\\\as transformations"),
|
|
|
|
)
|
|
|
|
for topic, screen in zip(topics, screens):
|
|
|
|
topic.next_to(screen, UP)
|
|
|
|
topic.set_color(WHITE)
|
|
|
|
|
|
|
|
for topic, screen in zip(topics, screens):
|
|
|
|
sc = screen.copy()
|
|
|
|
sc.set_fill(opacity=0)
|
|
|
|
sc.set_stroke(width=3)
|
|
|
|
self.play(
|
|
|
|
FadeIn(topic, 0.5 * UP),
|
|
|
|
FadeIn(screen),
|
|
|
|
VShowPassingFlash(sc, time_width=1.0, run_time=1.5),
|
|
|
|
)
|
|
|
|
self.wait(2)
|
|
|
|
|
|
|
|
# Video scenes
|
|
|
|
|
|
|
|
|
|
|
|
class IntroduceTheComputation(Scene):
|
|
|
|
def construct(self):
|
|
|
|
# Matrix in exponent
|
2021-02-25 08:53:12 -08:00
|
|
|
base = Tex("e")
|
|
|
|
base.set_height(1.0)
|
|
|
|
matrix = IntegerMatrix(
|
|
|
|
[[3, 1, 4],
|
|
|
|
[1, 5, 9],
|
|
|
|
[2, 6, 5]],
|
|
|
|
)
|
|
|
|
matrix.move_to(base.get_corner(UR), DL)
|
|
|
|
matrix_exp = VGroup(base, matrix)
|
|
|
|
matrix_exp.set_height(2)
|
|
|
|
matrix_exp.to_corner(UL)
|
|
|
|
matrix_exp.shift(3 * RIGHT)
|
|
|
|
|
|
|
|
randy = Randolph()
|
|
|
|
randy.set_height(2)
|
|
|
|
randy.to_corner(DL)
|
|
|
|
|
|
|
|
matrix.save_state()
|
|
|
|
matrix.center()
|
|
|
|
matrix.set_height(2.5)
|
|
|
|
|
|
|
|
self.add(randy)
|
|
|
|
self.play(
|
|
|
|
randy.animate.change("pondering", matrix),
|
|
|
|
Write(matrix.get_brackets()),
|
|
|
|
ShowIncreasingSubsets(matrix.get_entries()),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
matrix.animate.restore(),
|
|
|
|
Write(base),
|
|
|
|
randy.animate.change("erm", base),
|
|
|
|
)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
# Question the repeated multiplication implication
|
2021-02-25 08:53:12 -08:00
|
|
|
rhs = Tex("= e \\cdot e \\dots e \\cdot e")
|
|
|
|
rhs.set_height(0.75 * base.get_height())
|
|
|
|
rhs.next_to(matrix_exp, RIGHT)
|
|
|
|
rhs.align_to(base, DOWN)
|
|
|
|
brace = Brace(rhs[0][1:], DOWN)
|
|
|
|
matrix_copy = matrix.copy()
|
|
|
|
matrix_copy.scale(0.5)
|
|
|
|
brace_label = VGroup(
|
|
|
|
matrix.copy().scale(0.5),
|
|
|
|
Text("times?")
|
|
|
|
)
|
|
|
|
brace_label.arrange(RIGHT)
|
|
|
|
brace_label.next_to(brace, DOWN, SMALL_BUFF)
|
|
|
|
|
|
|
|
bubble = randy.get_bubble(
|
|
|
|
TexText("I'm sorry,\\\\what?!").scale(0.75),
|
|
|
|
height=2,
|
|
|
|
width=3,
|
|
|
|
bubble_class=SpeechBubble,
|
|
|
|
)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
TransformMatchingParts(
|
|
|
|
base.copy(), rhs,
|
|
|
|
path_arc=10 * DEGREES,
|
|
|
|
lag_ratio=0.01,
|
|
|
|
),
|
|
|
|
GrowFromCenter(brace),
|
|
|
|
ReplacementTransform(
|
|
|
|
matrix.copy(), brace_label[0],
|
|
|
|
path_arc=30 * DEGREES,
|
|
|
|
run_time=2,
|
|
|
|
rate_func=squish_rate_func(smooth, 0.3, 1),
|
|
|
|
),
|
|
|
|
Write(
|
|
|
|
brace_label[1],
|
|
|
|
run_time=2,
|
|
|
|
rate_func=squish_rate_func(smooth, 0.5, 1),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
randy.animate.change("angry", rhs),
|
|
|
|
ShowCreation(bubble),
|
2021-03-18 17:27:51 -07:00
|
|
|
Write(bubble.content, run_time=1),
|
2021-02-25 08:53:12 -08:00
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
false_equation = VGroup(
|
|
|
|
matrix_exp, rhs, brace, brace_label
|
|
|
|
)
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
# This is nonsense.
|
2021-02-25 08:53:12 -08:00
|
|
|
morty = Mortimer()
|
2021-03-18 17:27:51 -07:00
|
|
|
morty.refresh_triangulation()
|
2021-02-25 08:53:12 -08:00
|
|
|
morty.match_height(randy)
|
|
|
|
morty.to_corner(DR)
|
2021-03-18 17:27:51 -07:00
|
|
|
morty.set_opacity(0)
|
2021-02-25 08:53:12 -08:00
|
|
|
false_equation.generate_target()
|
|
|
|
false_equation.target.scale(0.5)
|
|
|
|
false_equation.target.next_to(morty, UL)
|
|
|
|
fe_rect = SurroundingRectangle(false_equation.target)
|
|
|
|
fe_rect.set_color(GREY_BROWN)
|
|
|
|
cross = Cross(false_equation.target[1])
|
2021-03-18 17:27:51 -07:00
|
|
|
cross.insert_n_curves(1)
|
|
|
|
cross.set_stroke(RED, width=[1, 5, 1])
|
2021-02-25 08:53:12 -08:00
|
|
|
nonsense = Text("This would be nonsense")
|
|
|
|
nonsense.match_width(fe_rect)
|
|
|
|
nonsense.next_to(fe_rect, UP)
|
|
|
|
nonsense.set_color(RED)
|
|
|
|
|
|
|
|
randy.bubble = bubble
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(false_equation),
|
|
|
|
RemovePiCreatureBubble(randy, target_mode="hesitant"),
|
2021-03-18 17:27:51 -07:00
|
|
|
morty.animate.set_opacity(1).change("raise_right_hand"),
|
2021-02-25 08:53:12 -08:00
|
|
|
ShowCreation(fe_rect),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
ShowCreation(cross),
|
|
|
|
FadeIn(nonsense),
|
|
|
|
)
|
|
|
|
self.play(Blink(morty))
|
|
|
|
self.wait()
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
false_group = VGroup(false_equation, fe_rect, cross, nonsense)
|
|
|
|
|
|
|
|
# Show Taylor series
|
2021-02-25 08:53:12 -08:00
|
|
|
real_equation = Tex(
|
2021-03-18 17:27:51 -07:00
|
|
|
"e^x = x^0 + x^1 + \\frac{1}{2} x^2 + \\frac{1}{6} x^3 + \\cdots + \\frac{1}{n!} x^n + \\cdots",
|
|
|
|
isolate=["x"]
|
2021-02-25 08:53:12 -08:00
|
|
|
)
|
2021-03-18 17:27:51 -07:00
|
|
|
xs = real_equation.get_parts_by_tex("x")
|
|
|
|
xs.set_color(YELLOW)
|
2021-02-25 08:53:12 -08:00
|
|
|
real_equation.set_width(FRAME_WIDTH - 2.0)
|
|
|
|
real_equation.to_edge(UP)
|
2021-03-18 17:27:51 -07:00
|
|
|
real_rhs = real_equation[3:]
|
|
|
|
|
|
|
|
real_label = Text("Real number", color=YELLOW, font_size=24)
|
|
|
|
real_label.next_to(xs[0], DOWN, buff=0.8)
|
|
|
|
real_label.to_edge(LEFT, buff=MED_SMALL_BUFF)
|
|
|
|
real_arrow = Arrow(real_label, xs[0], buff=0.1, fill_color=GREY_B, thickness=0.025)
|
2021-02-25 08:53:12 -08:00
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
taylor_brace = Brace(real_rhs, DOWN)
|
|
|
|
taylor_label = taylor_brace.get_text("Taylor series")
|
2021-02-25 08:53:12 -08:00
|
|
|
|
|
|
|
self.play(
|
|
|
|
TransformFromCopy(base, real_equation[0]),
|
|
|
|
FadeTransform(matrix.copy(), real_equation[1]),
|
2021-03-18 17:27:51 -07:00
|
|
|
FadeIn(real_label, UR),
|
|
|
|
GrowArrow(real_arrow),
|
|
|
|
randy.animate.change("thinking", real_label),
|
|
|
|
morty.animate.look_at(real_label),
|
2021-02-25 08:53:12 -08:00
|
|
|
)
|
2021-03-18 17:27:51 -07:00
|
|
|
self.wait()
|
2021-02-25 08:53:12 -08:00
|
|
|
self.play(
|
2021-03-18 17:27:51 -07:00
|
|
|
Write(real_equation[2], lag_ratio=0.2),
|
2021-02-25 08:53:12 -08:00
|
|
|
FadeTransformPieces(xs[:1].copy(), xs[1:], path_arc=20 * DEGREES),
|
|
|
|
LaggedStart(*(
|
|
|
|
FadeIn(part)
|
|
|
|
for part in real_equation[4:]
|
|
|
|
if part not in xs
|
2021-03-18 17:27:51 -07:00
|
|
|
)),
|
|
|
|
randy.animate.change("pondering", real_equation),
|
|
|
|
morty.animate.change("pondering", real_equation),
|
2021-02-25 08:53:12 -08:00
|
|
|
)
|
|
|
|
self.add(real_equation)
|
2021-03-18 17:27:51 -07:00
|
|
|
self.play(Blink(morty))
|
2021-02-25 08:53:12 -08:00
|
|
|
self.play(
|
2021-03-18 17:27:51 -07:00
|
|
|
false_group.animate.scale(0.7).to_edge(DOWN),
|
|
|
|
GrowFromCenter(taylor_brace),
|
|
|
|
FadeIn(taylor_label, 0.5 * DOWN)
|
2021-02-25 08:53:12 -08:00
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
2021-03-23 08:45:23 -07:00
|
|
|
# Taylor series example
|
|
|
|
ex_rhs = Tex(
|
|
|
|
"""
|
|
|
|
{2}^0 +
|
|
|
|
{2}^1 +
|
|
|
|
{ {2}^2 \\over 2} +
|
|
|
|
{ {2}^3 \\over 6} +
|
|
|
|
{ {2}^4 \\over 24} +
|
|
|
|
{ {2}^5 \\over 120} +
|
|
|
|
{ {2}^6 \\over 720} +
|
|
|
|
{ {2}^7 \\over 5040} +
|
|
|
|
\\cdots
|
|
|
|
""",
|
|
|
|
tex_to_color_map={"{2}": YELLOW, "+": WHITE},
|
|
|
|
)
|
|
|
|
ex_rhs.next_to(real_equation[3:], DOWN, buff=0.75)
|
|
|
|
|
|
|
|
ex_parts = VGroup(*(
|
|
|
|
ex_rhs[i:j] for i, j in [
|
|
|
|
(0, 2),
|
|
|
|
(3, 5),
|
|
|
|
(6, 8),
|
|
|
|
(9, 11),
|
|
|
|
(12, 14),
|
|
|
|
(15, 17),
|
|
|
|
(18, 20),
|
|
|
|
(21, 23),
|
|
|
|
(24, 25),
|
|
|
|
]
|
|
|
|
))
|
|
|
|
term_brace = Brace(ex_parts[0], DOWN)
|
|
|
|
frac = Tex("1", font_size=36)
|
|
|
|
frac.next_to(term_brace, DOWN, SMALL_BUFF)
|
|
|
|
|
|
|
|
rects = VGroup(*(
|
|
|
|
Rectangle(height=2**n / math.factorial(n), width=1)
|
|
|
|
for n in range(11)
|
|
|
|
))
|
|
|
|
rects.arrange(RIGHT, buff=0, aligned_edge=DOWN)
|
|
|
|
rects.set_fill(opacity=1)
|
|
|
|
rects.set_submobject_colors_by_gradient(BLUE, GREEN)
|
|
|
|
rects.set_stroke(WHITE, 1)
|
|
|
|
rects.set_width(7)
|
|
|
|
rects.to_edge(DOWN)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ReplacementTransform(taylor_brace, term_brace),
|
|
|
|
FadeTransform(real_equation[3:].copy(), ex_rhs),
|
|
|
|
FadeOut(false_group, shift=DOWN),
|
|
|
|
FadeOut(taylor_label, shift=DOWN),
|
|
|
|
FadeIn(frac),
|
|
|
|
)
|
|
|
|
term_values = VGroup()
|
|
|
|
for n in range(11):
|
|
|
|
rect = rects[n]
|
|
|
|
fact = math.factorial(n)
|
|
|
|
ex_part = ex_parts[min(n, len(ex_parts) - 1)]
|
|
|
|
value = DecimalNumber(2**n / fact)
|
|
|
|
value.set_color(GREY_A)
|
|
|
|
max_width = 0.6 * rect.get_width()
|
|
|
|
if value.get_width() > max_width:
|
|
|
|
value.set_width(max_width)
|
|
|
|
value.next_to(rects[n], UP, SMALL_BUFF)
|
|
|
|
new_brace = Brace(ex_part, DOWN)
|
|
|
|
if fact == 1:
|
|
|
|
new_frac = Tex(f"{2**n}", font_size=36)
|
|
|
|
else:
|
|
|
|
new_frac = Tex(f"{2**n} / {fact}", font_size=36)
|
|
|
|
new_frac.next_to(new_brace, DOWN, SMALL_BUFF)
|
|
|
|
self.play(
|
|
|
|
term_brace.animate.become(new_brace),
|
|
|
|
FadeTransform(frac, new_frac),
|
|
|
|
)
|
|
|
|
frac = new_frac
|
|
|
|
rect.save_state()
|
|
|
|
rect.stretch(0, 1, about_edge=DOWN)
|
|
|
|
rect.set_opacity(0)
|
|
|
|
value.set_value(0)
|
|
|
|
self.play(
|
|
|
|
Restore(rect),
|
|
|
|
ChangeDecimalToValue(value, 2**n / math.factorial(n)),
|
|
|
|
UpdateFromAlphaFunc(value, lambda m, a: m.next_to(rect, UP, SMALL_BUFF).set_opacity(a)),
|
|
|
|
randy.animate.look_at(rect),
|
|
|
|
morty.animate.look_at(rect),
|
|
|
|
)
|
|
|
|
term_values.add(value)
|
|
|
|
self.play(FadeOut(frac))
|
|
|
|
|
|
|
|
new_brace = Brace(ex_rhs, DOWN)
|
|
|
|
sum_value = DecimalNumber(math.exp(2), num_decimal_places=4, font_size=36)
|
|
|
|
sum_value.next_to(new_brace, DOWN)
|
|
|
|
self.play(
|
|
|
|
term_brace.animate.become(new_brace),
|
|
|
|
randy.animate.change("thinking", sum_value),
|
|
|
|
morty.animate.change("tease", sum_value),
|
|
|
|
*(FadeTransform(dec.copy().set_opacity(0), sum_value) for dec in term_values)
|
|
|
|
)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
|
|
|
|
lhs = Tex("e \\cdot e =")
|
|
|
|
lhs.match_height(real_equation[0])
|
|
|
|
lhs.next_to(ex_rhs, LEFT)
|
|
|
|
self.play(Write(lhs))
|
|
|
|
self.play(Blink(morty))
|
|
|
|
self.play(Blink(randy))
|
|
|
|
|
|
|
|
# Increment input
|
|
|
|
twos = ex_rhs.get_parts_by_tex("{2}")
|
|
|
|
threes = VGroup(*(
|
|
|
|
Tex("3").set_color(YELLOW).replace(two)
|
|
|
|
for two in twos
|
|
|
|
))
|
|
|
|
new_lhs = Tex("e \\cdot e \\cdot e = ")
|
|
|
|
new_lhs.match_height(lhs)
|
|
|
|
new_lhs[0].space_out_submobjects(0.8)
|
|
|
|
new_lhs[0][-1].shift(SMALL_BUFF * RIGHT)
|
|
|
|
new_lhs.move_to(lhs, RIGHT)
|
|
|
|
|
|
|
|
anims = []
|
|
|
|
unit_height = 0.7 * rects[0].get_height()
|
|
|
|
for n, rect, value_mob in zip(it.count(0), rects, term_values):
|
|
|
|
rect.generate_target()
|
|
|
|
new_value = 3**n / math.factorial(n)
|
|
|
|
rect.target.set_height(unit_height * new_value, stretch=True, about_edge=DOWN)
|
|
|
|
value_mob.rect = rect
|
|
|
|
anims += [
|
|
|
|
MoveToTarget(rect),
|
|
|
|
ChangeDecimalToValue(value_mob, new_value),
|
|
|
|
UpdateFromFunc(value_mob, lambda m: m.next_to(m.rect, UP, SMALL_BUFF))
|
|
|
|
]
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(twos, 0.5 * UP),
|
|
|
|
FadeIn(threes, 0.5 * UP),
|
|
|
|
)
|
|
|
|
twos.set_opacity(0)
|
|
|
|
self.play(
|
|
|
|
ChangeDecimalToValue(sum_value, math.exp(3)),
|
|
|
|
*anims,
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FadeOut(lhs, 0.5 * UP),
|
|
|
|
FadeIn(new_lhs, 0.5 * UP),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
# Isolate polynomial
|
|
|
|
real_lhs = VGroup(real_equation[:3], real_label, real_arrow)
|
|
|
|
|
|
|
|
self.play(
|
2021-03-19 10:54:42 -07:00
|
|
|
# LaggedStartMap(FadeOut, VGroup(
|
|
|
|
# *new_lhs, *threes, *ex_rhs,
|
|
|
|
# term_brace, sum_value,
|
|
|
|
# *rects, *term_values,
|
|
|
|
# )),
|
|
|
|
LaggedStart(
|
|
|
|
FadeOut(taylor_brace),
|
|
|
|
FadeOut(taylor_label),
|
|
|
|
FadeOut(false_group, DOWN),
|
|
|
|
),
|
2021-03-18 17:27:51 -07:00
|
|
|
real_lhs.animate.set_opacity(0.2),
|
|
|
|
randy.animate.change("erm", real_equation),
|
|
|
|
morty.animate.change("thinking", real_equation),
|
|
|
|
)
|
|
|
|
self.play(Blink(morty))
|
|
|
|
|
|
|
|
# Alternate inputs
|
2021-02-25 08:53:12 -08:00
|
|
|
rhs_tex = "X^0 + X^1 + \\frac{1}{2} X^2 + \\frac{1}{6} X^3 + \\cdots + \\frac{1}{n!} X^n + \\cdots"
|
2021-03-18 17:27:51 -07:00
|
|
|
pii_rhs = Tex(
|
|
|
|
rhs_tex.replace("X", "(\\pi i)"),
|
|
|
|
tex_to_color_map={"(\\pi i)": BLUE},
|
|
|
|
)
|
|
|
|
pii_rhs.match_width(real_rhs)
|
|
|
|
|
2021-02-25 08:53:12 -08:00
|
|
|
mat_tex = "\\left[ \\begin{array}{ccc} 3 & 1 & 4 \\\\ 1 & 5 & 9 \\\\ 2 & 6 & 5 \\end{array} \\right]"
|
2021-03-18 17:27:51 -07:00
|
|
|
mat_rhs = Tex(
|
2021-02-25 08:53:12 -08:00
|
|
|
rhs_tex.replace("X", mat_tex),
|
|
|
|
tex_to_color_map={mat_tex: TEAL},
|
|
|
|
)
|
2021-03-18 17:27:51 -07:00
|
|
|
mat_rhs.scale(0.5)
|
2021-02-25 08:53:12 -08:00
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
pii_rhs.next_to(real_rhs, DOWN, buff=0.7)
|
|
|
|
mat_rhs.next_to(pii_rhs, DOWN, buff=0.7)
|
2021-02-25 08:53:12 -08:00
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
self.play(FlashAround(real_rhs))
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
morty.animate.change("raise_right_hand", pii_rhs),
|
|
|
|
FadeTransformPieces(real_rhs.copy(), pii_rhs),
|
|
|
|
)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
self.play(
|
|
|
|
FadeTransformPieces(real_rhs.copy(), mat_rhs),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
randy.animate.change("maybe", mat_rhs),
|
|
|
|
)
|
|
|
|
self.wait()
|
2021-02-25 08:53:12 -08:00
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
why = Text("Why?", font_size=36)
|
|
|
|
why.next_to(randy, UP, aligned_edge=LEFT)
|
2021-02-25 08:53:12 -08:00
|
|
|
self.play(
|
2021-03-18 17:27:51 -07:00
|
|
|
randy.animate.change("confused", mat_rhs.get_corner(UL)),
|
|
|
|
Write(why),
|
|
|
|
)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
|
|
|
|
reassurance = VGroup(
|
|
|
|
Text("I know it looks complicated,", font_size=24),
|
|
|
|
Text("don't panic, it'll be okay", font_size=24),
|
|
|
|
)
|
|
|
|
reassurance.arrange(DOWN)
|
|
|
|
reassurance.next_to(morty, LEFT, aligned_edge=UP)
|
|
|
|
reassurance.set_color(GREY_A)
|
|
|
|
|
|
|
|
for words in reassurance:
|
|
|
|
self.play(FadeIn(words))
|
|
|
|
self.play(Blink(morty))
|
|
|
|
|
|
|
|
# Matrix powers
|
|
|
|
top_parts = VGroup(real_lhs, real_rhs, pii_rhs)
|
|
|
|
top_parts.save_state()
|
|
|
|
original_mat_rhs = mat_rhs.copy()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
mat_rhs.animate.set_width(FRAME_WIDTH - 1).center().to_edge(UP),
|
|
|
|
FadeOut(top_parts, shift=2 * UP),
|
|
|
|
LaggedStartMap(FadeOut, VGroup(why, *reassurance), shift=0.1 * LEFT),
|
|
|
|
)
|
|
|
|
|
|
|
|
mat = mat_rhs[4]
|
|
|
|
mat_brace = Brace(VGroup(mat, mat_rhs[5][0]), DOWN, buff=SMALL_BUFF)
|
|
|
|
matrix = np.array([[3, 1, 4], [1, 5, 9], [2, 6, 5]])
|
|
|
|
matrix_square = np.dot(matrix, matrix)
|
|
|
|
result = IntegerMatrix(matrix_square, h_buff=1.3, v_buff=0.7)
|
|
|
|
result.match_height(mat)
|
|
|
|
square_eq = VGroup(mat.copy(), mat.copy(), Tex("="), result)
|
|
|
|
square_eq.arrange(RIGHT, buff=SMALL_BUFF)
|
|
|
|
square_eq.next_to(mat_brace, DOWN)
|
|
|
|
|
|
|
|
self.play(GrowFromCenter(mat_brace))
|
|
|
|
self.play(
|
|
|
|
LaggedStart(
|
|
|
|
TransformFromCopy(mat, square_eq[0], path_arc=45 * DEGREES),
|
|
|
|
TransformFromCopy(mat, square_eq[1]),
|
|
|
|
Write(square_eq[2]),
|
|
|
|
Write(result.brackets),
|
2021-02-25 08:53:12 -08:00
|
|
|
),
|
2021-03-18 17:27:51 -07:00
|
|
|
randy.animate.change("pondering", square_eq),
|
|
|
|
)
|
2021-03-19 10:54:42 -07:00
|
|
|
self.show_mat_mult(matrix, matrix, square_eq[0][2:11], square_eq[1][2:11], result.elements)
|
2021-03-18 17:27:51 -07:00
|
|
|
|
|
|
|
# Show matrix cubed
|
|
|
|
mat_brace.generate_target()
|
|
|
|
mat_brace.target.next_to(mat_rhs[6], DOWN, SMALL_BUFF)
|
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
mat_squared = result
|
2021-03-18 17:27:51 -07:00
|
|
|
mat_cubed = IntegerMatrix(
|
|
|
|
np.dot(matrix, matrix_square),
|
|
|
|
h_buff=1.8, v_buff=0.7,
|
|
|
|
element_alignment_corner=ORIGIN,
|
|
|
|
)
|
|
|
|
mat_cubed.match_height(mat)
|
2021-03-19 10:54:42 -07:00
|
|
|
cube_eq = VGroup(
|
|
|
|
VGroup(mat.copy(), mat.copy(), mat.copy()).arrange(RIGHT, buff=SMALL_BUFF),
|
|
|
|
Tex("=").rotate(90 * DEGREES),
|
|
|
|
VGroup(mat.copy(), mat_squared.deepcopy()).arrange(RIGHT, buff=SMALL_BUFF),
|
|
|
|
Tex("=").rotate(90 * DEGREES),
|
|
|
|
mat_cubed
|
|
|
|
)
|
|
|
|
cube_eq.arrange(DOWN)
|
2021-03-18 17:27:51 -07:00
|
|
|
cube_eq.next_to(mat_brace.target, DOWN)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(mat_brace),
|
2021-03-19 10:54:42 -07:00
|
|
|
ReplacementTransform(square_eq[0], cube_eq[0][1]),
|
|
|
|
ReplacementTransform(square_eq[1], cube_eq[0][2]),
|
|
|
|
ReplacementTransform(square_eq[2], cube_eq[1]),
|
|
|
|
ReplacementTransform(square_eq[3], cube_eq[2][1]),
|
|
|
|
randy.animate.change("happy", cube_eq),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
LaggedStart(
|
|
|
|
FadeIn(cube_eq[0][0]),
|
|
|
|
FadeIn(cube_eq[2][0]),
|
|
|
|
FadeIn(cube_eq[3]),
|
|
|
|
FadeIn(cube_eq[4].brackets),
|
|
|
|
),
|
|
|
|
randy.animate.change("tease", cube_eq),
|
|
|
|
)
|
|
|
|
self.show_mat_mult(
|
|
|
|
matrix, matrix_square,
|
|
|
|
cube_eq[2][0][2:11],
|
|
|
|
cube_eq[2][1].get_entries(),
|
|
|
|
cube_eq[4].get_entries(),
|
|
|
|
0.1, 0.1,
|
2021-03-18 17:27:51 -07:00
|
|
|
)
|
|
|
|
self.play(Blink(morty))
|
|
|
|
|
|
|
|
# Scaling
|
|
|
|
example_matrix = Matrix([
|
|
|
|
["a", "b", "c"],
|
|
|
|
["d", "e", "f"],
|
|
|
|
["g", "h", "i"],
|
|
|
|
])
|
|
|
|
example_scaled_matrix = Matrix([
|
|
|
|
["a / n!", "b / n!", "c / n!"],
|
|
|
|
["d / n!", "e / n!", "f / n!"],
|
|
|
|
["g / n!", "h / n!", "i / n!"],
|
|
|
|
])
|
|
|
|
factor = Tex("1 \\over n!")
|
|
|
|
factor.scale(1.5)
|
|
|
|
factor.next_to(example_matrix, LEFT, MED_SMALL_BUFF)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
LaggedStartMap(FadeOut, VGroup(mat_brace, *cube_eq[:-1])),
|
|
|
|
FadeIn(factor),
|
|
|
|
FadeTransformPieces(cube_eq[-1], example_matrix),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
TransformMatchingShapes(
|
|
|
|
VGroup(*factor, *example_matrix),
|
|
|
|
example_scaled_matrix,
|
|
|
|
),
|
2021-03-19 10:54:42 -07:00
|
|
|
randy.animate.change("pondering", example_scaled_matrix),
|
2021-03-18 17:27:51 -07:00
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Adding
|
2021-03-23 08:45:23 -07:00
|
|
|
mat1 = np.array([[2, 7, 1], [8, 2, 8], [1, 8, 2]])
|
|
|
|
mat2 = np.array([[8, 4, 5], [9, 0, 4], [5, 2, 3]])
|
2021-03-18 17:27:51 -07:00
|
|
|
|
|
|
|
sum_eq = VGroup(
|
|
|
|
IntegerMatrix(mat1),
|
|
|
|
Tex("+"),
|
|
|
|
IntegerMatrix(mat2),
|
|
|
|
Tex("="),
|
|
|
|
Matrix(
|
|
|
|
np.array([
|
|
|
|
f"{m1} + {m2}"
|
|
|
|
for m1, m2 in zip(mat1.flatten(), mat2.flatten())
|
|
|
|
]).reshape((3, 3)),
|
|
|
|
h_buff=1.8,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
sum_eq.set_height(1.5)
|
|
|
|
sum_eq.arrange(RIGHT)
|
|
|
|
sum_eq.center()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(example_scaled_matrix, UP),
|
|
|
|
FadeIn(sum_eq[:-1], UP),
|
|
|
|
FadeIn(sum_eq[-1].brackets, UP),
|
2021-03-19 10:54:42 -07:00
|
|
|
morty.animate.change("raise_right_hand", sum_eq),
|
|
|
|
randy.animate.change("thinking", sum_eq),
|
2021-02-25 08:53:12 -08:00
|
|
|
)
|
2021-03-18 17:27:51 -07:00
|
|
|
|
|
|
|
last_rects = VGroup()
|
|
|
|
for e1, e2, e3 in zip(sum_eq[0].elements, sum_eq[2].elements, sum_eq[4].elements):
|
|
|
|
rects = VGroup(SurroundingRectangle(e1), SurroundingRectangle(e2))
|
|
|
|
self.add(e3, rects)
|
|
|
|
self.play(FadeOut(last_rects), run_time=0.2)
|
|
|
|
self.wait(0.1)
|
|
|
|
last_rects = rects
|
|
|
|
self.play(FadeOut(last_rects))
|
|
|
|
|
|
|
|
# Ask about infinity
|
|
|
|
bubble = randy.get_bubble(TexText("But...going\\\\to $\\infty$?"))
|
|
|
|
bubble.shift(SMALL_BUFF * RIGHT)
|
|
|
|
self.play(
|
|
|
|
Write(bubble),
|
|
|
|
Write(bubble.content),
|
|
|
|
FadeOut(sum_eq, UP),
|
|
|
|
randy.animate.change("sassy", mat_rhs),
|
|
|
|
morty.animate.change("guilty", randy.eyes),
|
|
|
|
)
|
2021-02-25 08:53:12 -08:00
|
|
|
self.play(Blink(randy))
|
2021-03-18 17:27:51 -07:00
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FadeOut(bubble),
|
|
|
|
bubble.content.animate.next_to(randy, RIGHT, aligned_edge=UP),
|
|
|
|
randy.animate.change("pondering", mat_rhs),
|
|
|
|
morty.animate.change("pondering", mat_rhs),
|
|
|
|
)
|
|
|
|
|
|
|
|
# Replace matrix
|
|
|
|
pi_mat_tex = ""
|
|
|
|
pi_mat_tex = "\\left[ \\begin{array}{cc} 0 & -\\pi \\\\ \\pi & 0 \\end{array} \\right]"
|
|
|
|
pi_mat_rhs = Tex(
|
|
|
|
rhs_tex.replace("X", pi_mat_tex),
|
|
|
|
tex_to_color_map={pi_mat_tex: BLUE},
|
|
|
|
)
|
|
|
|
pi_mat_rhs.match_width(mat_rhs)
|
|
|
|
pi_mat_rhs.move_to(mat_rhs)
|
|
|
|
|
|
|
|
pi_mat = pi_mat_rhs.get_part_by_tex(pi_mat_tex).copy()
|
|
|
|
pi_mat.scale(1.5)
|
|
|
|
pi_mat.next_to(morty, UL)
|
|
|
|
|
2021-02-25 08:53:12 -08:00
|
|
|
self.play(
|
2021-03-18 17:27:51 -07:00
|
|
|
morty.animate.change("raise_right_hand"),
|
|
|
|
FadeIn(pi_mat, UP)
|
|
|
|
)
|
|
|
|
self.play(Blink(morty))
|
|
|
|
self.play(
|
|
|
|
FadeTransformPieces(mat_rhs, pi_mat_rhs),
|
|
|
|
Transform(
|
|
|
|
VGroup(pi_mat),
|
|
|
|
pi_mat_rhs.get_parts_by_tex(pi_mat_tex),
|
|
|
|
remover=True,
|
|
|
|
),
|
|
|
|
morty.animate.change("tease"),
|
2021-02-25 08:53:12 -08:00
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
# Show various partial sum values
|
|
|
|
matrix = np.array([[0, -np.pi], [np.pi, 0]])
|
|
|
|
curr_matrix = np.identity(2)
|
|
|
|
curr_sum = np.identity(2)
|
|
|
|
curr_sum_mob = IntegerMatrix(curr_matrix)
|
|
|
|
curr_sum_mob.set_height(1.5)
|
|
|
|
mat_parts = pi_mat_rhs.get_parts_by_tex(pi_mat_tex)
|
|
|
|
|
|
|
|
brace = Brace(mat_parts[0], DOWN)
|
|
|
|
brace.stretch(1.1, 0, about_edge=LEFT)
|
|
|
|
curr_sum_mob.next_to(brace, DOWN)
|
|
|
|
curr_sum_mob.shift_onto_screen()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
GrowFromCenter(brace),
|
|
|
|
FadeTransform(mat_parts[0].copy(), curr_sum_mob),
|
|
|
|
randy.animate.change("erm", curr_sum_mob),
|
|
|
|
)
|
|
|
|
|
|
|
|
last_n_label = VMobject()
|
|
|
|
partial_sum_mobs = [curr_sum_mob]
|
|
|
|
for n in range(1, 18):
|
|
|
|
if n < 5:
|
|
|
|
new_brace = Brace(mat_parts[:n + 1])
|
|
|
|
new_brace.set_width(new_brace.get_width() + 0.2, about_edge=LEFT)
|
|
|
|
brace.generate_target()
|
|
|
|
brace.target.become(new_brace)
|
|
|
|
anims = [
|
|
|
|
MoveToTarget(brace),
|
|
|
|
]
|
|
|
|
else:
|
|
|
|
n_label = Tex(f"n = {n}", font_size=24)
|
|
|
|
n_label.next_to(brace.get_corner(DR), DL, SMALL_BUFF)
|
|
|
|
anims = [
|
|
|
|
FadeIn(n_label),
|
|
|
|
FadeOut(last_n_label),
|
|
|
|
]
|
|
|
|
last_n_label = n_label
|
|
|
|
|
|
|
|
curr_matrix = np.dot(curr_matrix, matrix) / n
|
|
|
|
curr_sum += curr_matrix
|
|
|
|
nd = min(n + 1, 4)
|
|
|
|
if n < 2:
|
|
|
|
h_buff = 1.3
|
|
|
|
else:
|
|
|
|
sample = DecimalMatrix(curr_sum[0], num_decimal_places=nd)
|
|
|
|
sample.replace(curr_sum_mob.get_entries()[0], 1)
|
|
|
|
h_buff = 1.2 * sample.get_width()
|
|
|
|
new_sum_mob = DecimalMatrix(
|
|
|
|
curr_sum,
|
|
|
|
element_alignment_corner=RIGHT,
|
|
|
|
element_to_mobject_config={
|
|
|
|
"num_decimal_places": nd,
|
|
|
|
"font_size": 36,
|
|
|
|
},
|
|
|
|
h_buff=h_buff,
|
|
|
|
)
|
|
|
|
new_sum_mob.match_height(curr_sum_mob)
|
|
|
|
new_sum_mob.next_to(brace.target, DOWN)
|
2021-02-25 08:53:12 -08:00
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
self.play(
|
|
|
|
FadeOut(curr_sum_mob),
|
|
|
|
FadeIn(new_sum_mob),
|
|
|
|
randy.animate.look_at(new_sum_mob),
|
|
|
|
*anims,
|
|
|
|
run_time=(1 if n < 5 else 1 / 60)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
curr_sum_mob = new_sum_mob
|
|
|
|
partial_sum_mobs.append(new_sum_mob)
|
|
|
|
self.play(
|
|
|
|
FadeOut(last_n_label),
|
|
|
|
randy.animate.change("confused", curr_sum_mob),
|
|
|
|
)
|
|
|
|
|
|
|
|
# Ask why
|
|
|
|
why = Text("Why?")
|
|
|
|
why.move_to(bubble.content, UL)
|
|
|
|
epii = Tex("e^{\\pi i} = -1")
|
|
|
|
epii.next_to(morty, UL)
|
|
|
|
later_text = Text("...but that comes later", font_size=24)
|
|
|
|
later_text.set_color(GREY_A)
|
|
|
|
later_text.next_to(epii, DOWN, aligned_edge=RIGHT)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
randy.animate.change("maybe"),
|
|
|
|
FadeIn(why, UP),
|
|
|
|
FadeOut(bubble.content, UP),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
morty.animate.change("raise_right_hand"),
|
|
|
|
FadeIn(epii, UP),
|
|
|
|
)
|
|
|
|
self.play(Blink(morty))
|
|
|
|
self.play(
|
|
|
|
Write(later_text, run_time=1),
|
|
|
|
randy.animate.change("hesitant", morty.eyes)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Show partial sums
|
2021-03-19 10:54:42 -07:00
|
|
|
new_mat_rhs = Tex(
|
|
|
|
rhs_tex.replace("X", mat_tex),
|
|
|
|
tex_to_color_map={mat_tex: TEAL},
|
2021-03-18 17:27:51 -07:00
|
|
|
isolate=["+"]
|
|
|
|
)
|
2021-03-19 10:54:42 -07:00
|
|
|
new_mat_rhs.replace(mat_rhs)
|
2021-03-18 17:27:51 -07:00
|
|
|
self.play(
|
2021-03-19 10:54:42 -07:00
|
|
|
FadeOut(pi_mat_rhs),
|
|
|
|
FadeIn(new_mat_rhs),
|
|
|
|
FadeOut(new_sum_mob, DOWN),
|
|
|
|
brace.animate.become(Brace(new_mat_rhs, DOWN)),
|
2021-03-18 17:27:51 -07:00
|
|
|
LaggedStartMap(
|
|
|
|
FadeOut, VGroup(
|
|
|
|
why, epii, later_text,
|
|
|
|
),
|
|
|
|
shift=DOWN,
|
|
|
|
),
|
2021-03-19 10:54:42 -07:00
|
|
|
randy.animate.change("pondering", new_mat_rhs),
|
|
|
|
morty.animate.change("pondering", new_mat_rhs),
|
2021-03-18 17:27:51 -07:00
|
|
|
)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
|
|
|
matrix = np.array([[3, 1, 4], [1, 5, 9], [2, 6, 5]])
|
|
|
|
partial_sum_mobs = VGroup()
|
|
|
|
curr_matrix = np.identity(3)
|
|
|
|
partial_sum = np.array(curr_matrix)
|
|
|
|
for n in range(50):
|
|
|
|
psm = DecimalMatrix(
|
|
|
|
partial_sum,
|
|
|
|
element_to_mobject_config={"num_decimal_places": 2},
|
|
|
|
element_alignment_corner=ORIGIN,
|
|
|
|
h_buff=1.5 * DecimalNumber(partial_sum[0, 0]).get_width(),
|
|
|
|
)
|
|
|
|
psm.next_to(brace, DOWN, MED_LARGE_BUFF)
|
|
|
|
partial_sum_mobs.add(psm)
|
|
|
|
curr_matrix = np.dot(curr_matrix, matrix) / (n + 1)
|
|
|
|
partial_sum += curr_matrix
|
|
|
|
|
|
|
|
new_mat_rhs[2:].set_opacity(0.1)
|
|
|
|
self.add(partial_sum_mobs[0])
|
|
|
|
self.wait(0.5)
|
2021-03-18 17:27:51 -07:00
|
|
|
for n, k in zip(it.count(1), [5, 9, 13, 19, 21]):
|
|
|
|
self.remove(partial_sum_mobs[n - 1])
|
|
|
|
self.add(partial_sum_mobs[n])
|
2021-03-19 10:54:42 -07:00
|
|
|
new_mat_rhs[:k].set_opacity(1)
|
|
|
|
self.wait(0.5)
|
|
|
|
brace.become(Brace(new_mat_rhs, DOWN))
|
|
|
|
n_label = VGroup(Tex("n = "), Integer(n))
|
|
|
|
n_label[1].set_height(n_label[0].get_height() * 1.2)
|
|
|
|
n_label.arrange(RIGHT, SMALL_BUFF)
|
|
|
|
n_label.set_color(GREY_B)
|
|
|
|
n_label.next_to(brace.get_corner(DR), DL, SMALL_BUFF)
|
|
|
|
self.add(n_label)
|
|
|
|
for n in range(6, 50):
|
2021-03-18 17:27:51 -07:00
|
|
|
self.remove(partial_sum_mobs[n - 1])
|
|
|
|
self.add(partial_sum_mobs[n])
|
2021-03-19 10:54:42 -07:00
|
|
|
n_label[1].set_value(n)
|
|
|
|
n_label[1].set_color(GREY_B)
|
|
|
|
n_label[1].next_to(n_label[0], RIGHT, SMALL_BUFF)
|
|
|
|
self.wait(0.1)
|
|
|
|
self.play(
|
|
|
|
randy.animate.change("erm"),
|
|
|
|
morty.animate.change("tease"),
|
|
|
|
LaggedStartMap(
|
|
|
|
FadeOut, VGroup(brace, n_label, partial_sum_mobs[n]),
|
|
|
|
shift=DOWN,
|
|
|
|
)
|
|
|
|
)
|
2021-03-18 17:27:51 -07:00
|
|
|
|
|
|
|
# Describe exp
|
2021-03-19 10:54:42 -07:00
|
|
|
self.clear()
|
|
|
|
self.add(randy, morty, new_mat_rhs)
|
2021-03-18 17:27:51 -07:00
|
|
|
mat_rhs.become(original_mat_rhs)
|
|
|
|
real_lhs.set_opacity(1)
|
|
|
|
real_label.to_corner(UL, buff=MED_SMALL_BUFF)
|
|
|
|
real_arrow.become(Arrow(real_label, real_equation[1], buff=0.1))
|
|
|
|
|
|
|
|
VGroup(real_lhs, real_rhs, mat_rhs, pii_rhs).to_edge(RIGHT, buff=SMALL_BUFF)
|
|
|
|
mat_rhs.to_edge(RIGHT, buff=SMALL_BUFF)
|
|
|
|
|
|
|
|
pii_lhs = Tex("\\text{exp}\\left(\\pi i \\right) = ")[0]
|
|
|
|
pii_lhs.next_to(pii_rhs, LEFT)
|
|
|
|
mat_lhs = Tex("\\text{exp}\\left(" + mat_tex + "\\right) = ")[0]
|
|
|
|
mat_lhs.match_height(mat_rhs)
|
|
|
|
mat_lhs[:3].match_height(pii_lhs[:3])
|
|
|
|
mat_lhs[:3].next_to(mat_lhs[3:5], LEFT, SMALL_BUFF)
|
|
|
|
mat_lhs.next_to(mat_rhs, LEFT)
|
|
|
|
|
|
|
|
pii_lhs_pi_part = pii_lhs[4:6]
|
|
|
|
pii_lhs_pi_part.set_color(BLUE)
|
|
|
|
mat_lhs_mat_part = mat_lhs[5:18]
|
|
|
|
mat_lhs_mat_part.set_color(TEAL)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(real_equation),
|
|
|
|
FadeIn(real_arrow),
|
|
|
|
FadeIn(real_label),
|
|
|
|
FadeIn(pii_rhs),
|
|
|
|
FadeIn(mat_rhs),
|
2021-03-19 10:54:42 -07:00
|
|
|
FadeOut(new_mat_rhs),
|
2021-03-18 17:27:51 -07:00
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FadeIn(pii_lhs),
|
|
|
|
randy.animate.change("thinking", pii_lhs),
|
|
|
|
randy.animate.change("tease", pii_lhs),
|
|
|
|
)
|
|
|
|
self.play(FadeIn(mat_lhs))
|
|
|
|
self.play(Blink(randy))
|
|
|
|
self.play(
|
|
|
|
LaggedStart(
|
|
|
|
FlashAround(pii_lhs[:3]),
|
|
|
|
FlashAround(mat_lhs[:3]),
|
|
|
|
lag_ratio=0.3,
|
|
|
|
run_time=2
|
|
|
|
),
|
|
|
|
randy.animate.change("raise_left_hand", pii_lhs),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Transition to e^x notation
|
|
|
|
crosses = VGroup(*(
|
|
|
|
Cross(lhs[:3], stroke_width=[0, 3, 3, 3, 0]).scale(1.3)
|
|
|
|
for lhs in [pii_lhs, mat_lhs]
|
|
|
|
))
|
|
|
|
bases = VGroup()
|
|
|
|
powers = VGroup()
|
|
|
|
equals = VGroup()
|
|
|
|
for part, lhs in (pii_lhs_pi_part, pii_lhs), (mat_lhs_mat_part, mat_lhs):
|
|
|
|
power = part.copy()
|
|
|
|
part.set_opacity(0)
|
|
|
|
self.add(power)
|
|
|
|
base = Tex("e", font_size=60)
|
|
|
|
equal = Tex(":=")
|
|
|
|
power.generate_target()
|
|
|
|
if power.target.get_height() > 0.7:
|
|
|
|
power.target.set_height(0.7)
|
|
|
|
power.target.next_to(base, UR, buff=0.05)
|
|
|
|
group = VGroup(base, power.target, equal)
|
|
|
|
equal.next_to(group[:2], RIGHT, MED_SMALL_BUFF)
|
|
|
|
equal.match_y(base)
|
|
|
|
if lhs is mat_lhs:
|
|
|
|
equal.shift(0.1 * UP)
|
|
|
|
group.shift(lhs.get_right() - equal.get_right())
|
|
|
|
bases.add(base)
|
|
|
|
powers.add(power)
|
|
|
|
equals.add(equal)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(crosses),
|
|
|
|
randy.animate.change("hesitant", crosses),
|
|
|
|
)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(pii_lhs),
|
|
|
|
FadeOut(mat_lhs),
|
|
|
|
FadeOut(crosses),
|
|
|
|
*(MoveToTarget(power) for power in powers),
|
|
|
|
*(TransformFromCopy(real_equation[0], base) for base in bases),
|
|
|
|
Write(equals),
|
|
|
|
randy.animate.change("sassy", powers),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Theorem vs. definition
|
|
|
|
real_part = VGroup(real_lhs, real_rhs)
|
|
|
|
pii_part = VGroup(bases[0], powers[0], equals[0], pii_rhs)
|
|
|
|
mat_part = VGroup(bases[1], powers[1], equals[1], mat_rhs)
|
|
|
|
def_parts = VGroup(pii_part, mat_part)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(randy, DOWN),
|
|
|
|
FadeOut(morty, DOWN),
|
|
|
|
real_part.animate.set_x(0).shift(DOWN),
|
|
|
|
def_parts.animate.set_x(0).to_edge(DOWN),
|
|
|
|
)
|
|
|
|
|
|
|
|
real_rect = SurroundingRectangle(real_part)
|
|
|
|
real_rect.set_stroke(YELLOW, 2)
|
|
|
|
real_label = Text("Theorem")
|
|
|
|
real_label.next_to(real_rect, UP)
|
|
|
|
|
|
|
|
def_rect = SurroundingRectangle(def_parts)
|
|
|
|
def_rect.set_stroke(BLUE, 2)
|
|
|
|
def_label = Text("Definition")
|
|
|
|
def_label.next_to(def_rect, UP)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(real_rect),
|
|
|
|
FadeIn(real_label, 0.5 * UP),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
ShowCreation(def_rect),
|
|
|
|
FadeIn(def_label, 0.5 * UP),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
def show_mat_mult(self, m1, m2, m1_terms, m2_terms, rhs_terms, per_term=0.1, between_terms=0.35):
|
|
|
|
m1_color = m1_terms[0].get_fill_color()
|
|
|
|
m2_color = m2_terms[0].get_fill_color()
|
|
|
|
for n in range(9):
|
|
|
|
i = n // 3
|
|
|
|
j = n % 3
|
|
|
|
row = m1_terms[3 * i:3 * i + 3]
|
|
|
|
col = m2_terms[j::3]
|
|
|
|
row_rect = SurroundingRectangle(row, buff=0.05)
|
|
|
|
col_rect = SurroundingRectangle(col, buff=0.05)
|
|
|
|
row_rect.set_stroke(YELLOW, 2)
|
|
|
|
col_rect.set_stroke(YELLOW, 2)
|
|
|
|
right_elem = Integer(0)
|
|
|
|
right_elem.replace(rhs_terms[n], dim_to_match=1)
|
|
|
|
right_elem.set_value(0)
|
|
|
|
|
|
|
|
self.add(row_rect, col_rect, right_elem)
|
|
|
|
for k in range(3):
|
|
|
|
self.wait(per_term)
|
|
|
|
right_elem.increment_value(m1[i, k] * m2[k, j])
|
|
|
|
row[k].set_color(YELLOW)
|
|
|
|
col[k].set_color(YELLOW)
|
|
|
|
self.remove(right_elem)
|
|
|
|
self.add(rhs_terms[n])
|
|
|
|
self.wait(between_terms)
|
|
|
|
m1_terms.set_color(m1_color)
|
|
|
|
m2_terms.set_color(m2_color)
|
|
|
|
self.remove(row_rect, col_rect)
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
|
2021-03-23 08:45:23 -07:00
|
|
|
class WhyTortureMatrices(TeacherStudentsScene):
|
|
|
|
def construct(self):
|
|
|
|
self.student_says(
|
|
|
|
TexText("Why...would you\\\\ever want\\\\to do that?"),
|
|
|
|
student_index=2,
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
self.get_student_changes("confused", "pondering", "raise_left_hand", look_at_arg=self.screen),
|
|
|
|
self.teacher.animate.change("tease", self.screen)
|
|
|
|
)
|
|
|
|
self.wait(2)
|
|
|
|
self.play(self.students[0].animate.change("erm"))
|
|
|
|
self.wait(3)
|
|
|
|
|
|
|
|
|
|
|
|
class DefinitionFirstVsLast(Scene):
|
|
|
|
def construct(self):
|
2021-03-24 13:49:29 -07:00
|
|
|
# Setup objects
|
|
|
|
top_title = Text("Textbook progression")
|
|
|
|
low_title = Text("Discovery progression")
|
|
|
|
|
|
|
|
top_prog = VGroup(
|
|
|
|
TexText("Definition", color=BLUE),
|
|
|
|
TexText("Theorem"),
|
|
|
|
TexText("Proof"),
|
|
|
|
TexText("Examples"),
|
|
|
|
)
|
|
|
|
low_prog = VGroup(
|
|
|
|
TexText("Specific\n\nproblem"),
|
|
|
|
TexText("General\n\nproblems"),
|
|
|
|
TexText("Helpful\n\nconstructs"),
|
|
|
|
TexText("Definition", color=BLUE),
|
|
|
|
)
|
|
|
|
progs = VGroup(top_prog, low_prog)
|
|
|
|
for progression in progs:
|
2021-03-23 08:45:23 -07:00
|
|
|
progression.arrange(RIGHT, buff=1.2)
|
2021-03-24 13:49:29 -07:00
|
|
|
progs.arrange(DOWN, buff=3)
|
|
|
|
progs.set_width(FRAME_WIDTH - 2)
|
2021-03-23 08:45:23 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
for progression in progs:
|
2021-03-23 08:45:23 -07:00
|
|
|
arrows = VGroup()
|
|
|
|
for m1, m2 in zip(progression[:-1], progression[1:]):
|
2021-03-24 13:49:29 -07:00
|
|
|
arrows.add(Arrow(m1, m2))
|
2021-03-23 08:45:23 -07:00
|
|
|
progression.arrows = arrows
|
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
top_dots = Tex("\\dots", font_size=72)
|
|
|
|
top_dots.next_to(top_prog.arrows[0], RIGHT)
|
|
|
|
low_dots = top_dots.copy()
|
|
|
|
low_dots.next_to(low_prog.arrows[-1], LEFT)
|
|
|
|
|
|
|
|
top_rect = SurroundingRectangle(top_prog, buff=MED_SMALL_BUFF)
|
|
|
|
top_rect.set_stroke(TEAL, 2)
|
|
|
|
top_title.next_to(top_rect, UP)
|
|
|
|
top_title.match_color(top_rect)
|
|
|
|
low_rect = SurroundingRectangle(low_prog, buff=MED_SMALL_BUFF)
|
|
|
|
low_rect.set_stroke(YELLOW, 2)
|
|
|
|
low_title.next_to(low_rect, UP)
|
|
|
|
low_title.match_color(low_rect)
|
|
|
|
versus = Text("vs.")
|
|
|
|
|
|
|
|
# Show progressions
|
|
|
|
self.add(top_prog[0])
|
|
|
|
self.play(
|
|
|
|
GrowArrow(top_prog.arrows[0]),
|
|
|
|
FadeIn(top_dots, 0.2 * RIGHT, lag_ratio=0.1),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
kw = {"path_arc": -90 * DEGREES}
|
|
|
|
self.play(
|
|
|
|
LaggedStart(
|
|
|
|
TransformFromCopy(top_prog[0], low_prog[-1], **kw),
|
|
|
|
TransformFromCopy(top_prog.arrows[0], low_prog.arrows[-1], **kw),
|
|
|
|
TransformFromCopy(top_dots, low_dots, **kw),
|
|
|
|
),
|
|
|
|
Write(versus)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(top_rect),
|
|
|
|
FadeIn(top_title, 0.25 * UP)
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FadeOut(top_dots),
|
|
|
|
FadeIn(top_prog[1]),
|
|
|
|
)
|
|
|
|
for arrow, term in zip(top_prog.arrows[1:], top_prog[2:]):
|
|
|
|
self.play(
|
|
|
|
GrowArrow(arrow),
|
|
|
|
FadeIn(term, shift=0.25 * RIGHT),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(low_rect),
|
|
|
|
FadeIn(low_title, 0.25 * UP),
|
|
|
|
versus.animate.move_to(midpoint(low_title.get_top(), top_rect.get_bottom())),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(FadeIn(low_prog[0]))
|
|
|
|
self.play(
|
|
|
|
GrowArrow(low_prog.arrows[0]),
|
|
|
|
FadeIn(low_prog[1], shift=0.25 * RIGHT),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
GrowArrow(low_prog.arrows[1]),
|
|
|
|
FadeIn(low_prog[2], shift=0.25 * RIGHT),
|
|
|
|
FadeOut(low_dots),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Highligh specific example
|
|
|
|
full_rect = FullScreenRectangle()
|
|
|
|
full_rect.set_fill(BLACK, opacity=0.75)
|
|
|
|
sp = low_prog[0]
|
|
|
|
self.add(full_rect, sp)
|
|
|
|
self.play(FadeIn(full_rect))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Love and quantum
|
|
|
|
love = SVGMobject("hearts")
|
|
|
|
love.set_height(1)
|
|
|
|
love.set_fill(RED, 1)
|
|
|
|
love.set_stroke(MAROON_B, 1)
|
|
|
|
|
|
|
|
quantum = Tex("|\\psi\\rangle")
|
|
|
|
quantum.set_color(BLUE)
|
|
|
|
quantum.match_height(love)
|
|
|
|
group = VGroup(quantum, love)
|
|
|
|
group.arrange(RIGHT, buff=MED_LARGE_BUFF)
|
|
|
|
group.next_to(sp, UP, MED_LARGE_BUFF)
|
|
|
|
|
|
|
|
love.save_state()
|
|
|
|
love.match_x(sp)
|
|
|
|
self.play(Write(love))
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
Restore(love),
|
|
|
|
FadeIn(quantum, 0.5 * LEFT)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
love.animate.center().scale(1.5),
|
|
|
|
FadeOut(quantum),
|
|
|
|
FadeOut(sp),
|
|
|
|
full_rect.animate.set_fill(opacity=1)
|
|
|
|
)
|
|
|
|
self.wait()
|
2021-03-23 08:45:23 -07:00
|
|
|
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
class RomeoAndJuliet(Scene):
|
|
|
|
def construct(self):
|
|
|
|
# Add Romeo and Juliet
|
|
|
|
romeo, juliet = lovers = self.get_romeo_and_juliet()
|
|
|
|
lovers.set_height(2)
|
|
|
|
lovers.arrange(LEFT, buff=1)
|
|
|
|
lovers.move_to(0.5 * DOWN)
|
|
|
|
|
|
|
|
self.add(*lovers)
|
|
|
|
self.make_romeo_and_juliet_dynamic(romeo, juliet)
|
|
|
|
romeo.love_tracker.set_value(1.5)
|
|
|
|
juliet.love_tracker.set_value(1.5)
|
2021-03-23 08:45:23 -07:00
|
|
|
get_romeo_juilet_name_labels(lovers)
|
2021-03-18 17:27:51 -07:00
|
|
|
|
|
|
|
for creature in lovers:
|
|
|
|
self.play(
|
|
|
|
creature.love_tracker.animate.set_value(2.5),
|
|
|
|
Write(creature.name_label, run_time=1),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Add their scales
|
|
|
|
juliet_scale = self.get_love_scale(juliet, LEFT, "x", BLUE_B)
|
|
|
|
romeo_scale = self.get_love_scale(romeo, RIGHT, "y", BLUE)
|
|
|
|
scales = [juliet_scale, romeo_scale]
|
|
|
|
|
|
|
|
scale_labels = VGroup(
|
|
|
|
TexText("Juliet's love for Romeo", font_size=30),
|
|
|
|
TexText("Romeo's love for Juliet", font_size=30),
|
|
|
|
)
|
|
|
|
scale_arrows = VGroup()
|
|
|
|
for scale, label in zip(scales, scale_labels):
|
|
|
|
var = scale[2][0][0]
|
|
|
|
label.next_to(var, UP, buff=0.7)
|
|
|
|
arrow = Arrow(var, label, buff=0.1, thickness=0.025)
|
|
|
|
scale_arrows.add(arrow)
|
|
|
|
label.set_color(var.get_fill_color())
|
|
|
|
|
|
|
|
for lover, scale, arrow, label, final_love in zip(reversed(lovers), scales, scale_arrows, scale_labels, [1, -1]):
|
|
|
|
self.add(scale)
|
|
|
|
self.play(FlashAround(scale[2][0][0]))
|
|
|
|
self.play(
|
|
|
|
lover.love_tracker.animate.set_value(5),
|
|
|
|
GrowArrow(arrow),
|
|
|
|
FadeIn(label, 0.5 * UP),
|
|
|
|
)
|
|
|
|
self.play(lover.love_tracker.animate.set_value(final_love), run_time=2)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Juliet's rule
|
|
|
|
frame = self.camera.frame
|
|
|
|
equations = VGroup(
|
|
|
|
Tex("{dx \\over dt} {{=}} -{{y(t)}}"),
|
|
|
|
Tex("{dy \\over dt} {{=}} {{x(t)}}"),
|
|
|
|
)
|
|
|
|
juliet_eq, romeo_eq = equations
|
|
|
|
juliet_eq.next_to(scale_labels[0], UR)
|
|
|
|
juliet_eq.shift(0.5 * UP)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
frame.animate.move_to(0.7 * UP),
|
|
|
|
Write(equations[0]),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(FlashAround(juliet_eq[0]))
|
|
|
|
self.wait()
|
|
|
|
y_rect = SurroundingRectangle(juliet_eq.get_parts_by_tex("y(t)"), buff=0.05)
|
|
|
|
y_rect_copy = y_rect.copy()
|
2021-03-23 08:45:23 -07:00
|
|
|
y_rect_copy.replace(romeo.scale_mob.dot, stretch=True)
|
2021-03-18 17:27:51 -07:00
|
|
|
self.play(FadeIn(y_rect))
|
|
|
|
self.wait()
|
|
|
|
self.play(TransformFromCopy(y_rect, y_rect_copy))
|
2021-03-23 08:45:23 -07:00
|
|
|
y_rect_copy.add_updater(lambda m: m.move_to(romeo.scale_mob.dot))
|
2021-03-18 17:27:51 -07:00
|
|
|
self.wait()
|
|
|
|
self.play(romeo.love_tracker.animate.set_value(-3))
|
|
|
|
|
|
|
|
big_arrow = Arrow(
|
2021-03-23 08:45:23 -07:00
|
|
|
juliet.scale_mob.number_line.get_bottom(),
|
|
|
|
juliet.scale_mob.number_line.get_top(),
|
2021-03-18 17:27:51 -07:00
|
|
|
)
|
|
|
|
big_arrow.set_color(GREEN)
|
2021-03-23 08:45:23 -07:00
|
|
|
big_arrow.next_to(juliet.scale_mob.number_line, LEFT)
|
2021-03-18 17:27:51 -07:00
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(big_arrow),
|
|
|
|
ApplyMethod(juliet.love_tracker.set_value, 5, run_time=3, rate_func=linear),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(romeo.love_tracker.animate.set_value(5))
|
|
|
|
self.play(
|
|
|
|
big_arrow.animate.rotate(PI).set_color(RED),
|
|
|
|
path_arc=PI,
|
|
|
|
run_time=0.5,
|
|
|
|
)
|
|
|
|
self.play(juliet.love_tracker.animate.set_value(-5), rate_func=linear, run_time=5)
|
|
|
|
self.play(FadeOut(y_rect), FadeOut(y_rect_copy))
|
|
|
|
|
|
|
|
# Romeo's rule
|
|
|
|
romeo_eq.next_to(scale_labels[1], UL)
|
|
|
|
romeo_eq.shift(0.5 * UP)
|
|
|
|
self.play(
|
|
|
|
juliet_eq.animate.to_edge(LEFT),
|
|
|
|
FadeOut(big_arrow),
|
|
|
|
)
|
|
|
|
self.play(FadeIn(romeo_eq, UP))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
dy_rect = SurroundingRectangle(romeo_eq.get_part_by_tex("dy"))
|
|
|
|
x_rect = SurroundingRectangle(romeo_eq.get_part_by_tex("x(t)"), buff=0.05)
|
|
|
|
x_rect_copy = x_rect.copy()
|
2021-03-23 08:45:23 -07:00
|
|
|
x_rect_copy.replace(juliet.scale_mob.dot, stretch=True)
|
2021-03-18 17:27:51 -07:00
|
|
|
self.play(ShowCreation(dy_rect))
|
|
|
|
self.wait()
|
|
|
|
self.play(TransformFromCopy(dy_rect, x_rect))
|
|
|
|
self.play(TransformFromCopy(x_rect, x_rect_copy))
|
|
|
|
self.wait()
|
|
|
|
|
2021-03-23 08:45:23 -07:00
|
|
|
big_arrow.next_to(romeo.scale_mob.number_line, RIGHT)
|
2021-03-18 17:27:51 -07:00
|
|
|
self.play(FadeIn(big_arrow), LaggedStartMap(FadeOut, VGroup(dy_rect, x_rect)))
|
|
|
|
self.play(romeo.love_tracker.animate.set_value(-3), run_time=4, rate_func=linear)
|
2021-03-23 08:45:23 -07:00
|
|
|
x_rect_copy.add_updater(lambda m: m.move_to(juliet.scale_mob.dot))
|
2021-03-18 17:27:51 -07:00
|
|
|
juliet.love_tracker.set_value(5)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
big_arrow.animate.rotate(PI).set_color(GREEN),
|
|
|
|
path_arc=PI,
|
|
|
|
run_time=0.5,
|
|
|
|
)
|
|
|
|
self.play(romeo.love_tracker.animate.set_value(5), rate_func=linear, run_time=5)
|
|
|
|
self.play(FadeOut(x_rect_copy))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Show constant change
|
|
|
|
left_arrow = Arrow(UP, DOWN)
|
|
|
|
left_arrow.character = juliet
|
|
|
|
left_arrow.get_rate = lambda: -romeo.love_tracker.get_value()
|
|
|
|
|
|
|
|
right_arrow = Arrow(DOWN, UP)
|
|
|
|
right_arrow.character = romeo
|
|
|
|
right_arrow.get_rate = lambda: juliet.love_tracker.get_value()
|
|
|
|
|
|
|
|
def update_arrow(arrow):
|
|
|
|
nl = arrow.character.scale.number_line
|
|
|
|
rate = arrow.get_rate()
|
|
|
|
if rate == 0:
|
|
|
|
rate = 1e-6
|
|
|
|
arrow.put_start_and_end_on(nl.n2p(0), nl.n2p(rate))
|
|
|
|
arrow.next_to(nl, np.sign(nl.get_center()[0]) * RIGHT)
|
|
|
|
if rate > 0:
|
|
|
|
arrow.set_color(GREEN)
|
|
|
|
else:
|
|
|
|
arrow.set_color(RED)
|
|
|
|
|
|
|
|
left_arrow.add_updater(update_arrow)
|
|
|
|
right_arrow.add_updater(update_arrow)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
VFadeIn(left_arrow),
|
|
|
|
ApplyMethod(big_arrow.scale, 0, remover=True, run_time=3),
|
|
|
|
ApplyMethod(juliet.love_tracker.set_value, 0, run_time=3),
|
|
|
|
)
|
|
|
|
|
|
|
|
ps_point = Point(5 * UP)
|
|
|
|
curr_time = self.time
|
|
|
|
ps_point.add_updater(lambda m: m.move_to([
|
|
|
|
-5 * np.sin(0.5 * (self.time - curr_time)),
|
|
|
|
5 * np.cos(0.5 * (self.time - curr_time)),
|
|
|
|
0,
|
|
|
|
]))
|
|
|
|
juliet.love_tracker.add_updater(lambda m: m.set_value(ps_point.get_location()[0]))
|
|
|
|
romeo.love_tracker.add_updater(lambda m: m.set_value(ps_point.get_location()[1]))
|
|
|
|
self.add(ps_point)
|
|
|
|
self.add(right_arrow)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
equations.animate.arrange(RIGHT, buff=LARGE_BUFF).to_edge(UP, buff=0),
|
|
|
|
run_time=2,
|
|
|
|
)
|
|
|
|
# Just let this play out for a long time while other animations are played on top
|
|
|
|
self.wait(5 * TAU)
|
|
|
|
|
2021-03-23 08:45:23 -07:00
|
|
|
def get_romeo_and_juliet(self):
|
|
|
|
romeo = PiCreature(color=BLUE_E, flip_at_start=True)
|
|
|
|
juliet = PiCreature(color=BLUE_B)
|
|
|
|
return VGroup(romeo, juliet)
|
|
|
|
|
|
|
|
def make_romeo_and_juliet_dynamic(self, romeo, juliet):
|
|
|
|
cutoff_values = [-5, -3, -1, 0, 1, 3, 5]
|
|
|
|
modes = ["angry", "sassy", "hesitant", "plain", "happy", "hooray", "surprised"]
|
|
|
|
self.make_character_dynamic(romeo, juliet, cutoff_values, modes)
|
|
|
|
self.make_character_dynamic(juliet, romeo, cutoff_values, modes)
|
|
|
|
|
|
|
|
def get_romeo_juilet_name_labels(self, lovers, font_size=36, spacing=1.2):
|
|
|
|
name_labels = VGroup(*(
|
|
|
|
Text(name, font_size=font_size)
|
|
|
|
for name in ["Romeo", "Juliet"]
|
|
|
|
))
|
|
|
|
for label, creature in zip(name_labels, lovers):
|
|
|
|
label.next_to(creature, DOWN)
|
|
|
|
creature.name_label = label
|
|
|
|
name_labels.space_out_submobjects(spacing)
|
|
|
|
return name_labels
|
|
|
|
|
2021-03-18 17:27:51 -07:00
|
|
|
def make_character_dynamic(self, pi_creature, lover, cutoff_values, modes):
|
|
|
|
height = pi_creature.get_height()
|
|
|
|
bottom = pi_creature.get_bottom()
|
|
|
|
copies = [
|
|
|
|
pi_creature.deepcopy().change(mode).set_height(height).move_to(bottom, DOWN)
|
|
|
|
for mode in modes
|
|
|
|
]
|
|
|
|
pi_creature.love_tracker = ValueTracker()
|
|
|
|
|
|
|
|
def update_func(pi):
|
|
|
|
love = pi.love_tracker.get_value()
|
|
|
|
|
|
|
|
if love < cutoff_values[0]:
|
|
|
|
pi.become(copies[0])
|
|
|
|
elif love >= cutoff_values[-1]:
|
|
|
|
pi.become(copies[-1])
|
|
|
|
else:
|
|
|
|
i = 1
|
|
|
|
while cutoff_values[i] < love:
|
|
|
|
i += 1
|
|
|
|
copy1 = copies[i - 1]
|
|
|
|
copy2 = copies[i]
|
|
|
|
|
|
|
|
alpha = inverse_interpolate(cutoff_values[i - 1], cutoff_values[i], love)
|
|
|
|
s_alpha = squish_rate_func(smooth, 0.25, 0.75)(alpha)
|
|
|
|
|
|
|
|
# if s_alpha > 0:
|
|
|
|
copy1.align_data_and_family(copy2)
|
|
|
|
pi.align_data_and_family(copy1)
|
|
|
|
pi.align_data_and_family(copy2)
|
|
|
|
fam = pi.family_members_with_points()
|
|
|
|
f1 = copy1.family_members_with_points()
|
|
|
|
f2 = copy2.family_members_with_points()
|
|
|
|
for sm, sm1, sm2 in zip(fam, f1, f2):
|
|
|
|
sm.interpolate(sm1, sm2, s_alpha)
|
|
|
|
|
2021-03-23 08:45:23 -07:00
|
|
|
pi.look_at(lover.get_top())
|
2021-03-18 17:27:51 -07:00
|
|
|
if love < cutoff_values[1]:
|
|
|
|
# Look away from the lover
|
|
|
|
pi.look_at(2 * pi.eyes.get_center() - lover.eyes.get_center() + DOWN)
|
|
|
|
|
|
|
|
return pi
|
|
|
|
|
|
|
|
pi_creature.add_updater(update_func)
|
|
|
|
|
|
|
|
def update_eyes(heart_eyes):
|
|
|
|
love = pi_creature.love_tracker.get_value()
|
|
|
|
l_alpha = np.clip(
|
|
|
|
inverse_interpolate(cutoff_values[-1] - 0.5, cutoff_values[-1], love),
|
|
|
|
0, 1
|
|
|
|
)
|
|
|
|
pi_creature.eyes.set_opacity(1 - l_alpha)
|
|
|
|
heart_eyes.set_opacity(l_alpha)
|
|
|
|
# heart_eyes.move_to(pi_creature.eyes)
|
|
|
|
heart_eyes.match_x(pi_creature.mouth)
|
|
|
|
|
|
|
|
heart_eyes = self.get_heart_eyes(pi_creature)
|
|
|
|
heart_eyes.add_updater(update_eyes)
|
2021-03-23 08:45:23 -07:00
|
|
|
pi_creature.heart_eyes = heart_eyes
|
2021-03-18 17:27:51 -07:00
|
|
|
self.add(heart_eyes)
|
|
|
|
return pi_creature
|
|
|
|
|
|
|
|
def get_heart_eyes(self, creature):
|
|
|
|
hearts = VGroup()
|
|
|
|
for eye in creature.eyes:
|
|
|
|
heart = SVGMobject("hearts")
|
|
|
|
heart.set_fill(RED)
|
|
|
|
heart.match_width(eye)
|
|
|
|
heart.move_to(eye)
|
|
|
|
heart.scale(1.25)
|
|
|
|
heart.set_stroke(BLACK, 1)
|
|
|
|
hearts.add(heart)
|
|
|
|
hearts.set_opacity(0)
|
|
|
|
return hearts
|
|
|
|
|
|
|
|
def get_love_scale(self, creature, direction, var_name, color):
|
|
|
|
number_line = NumberLine((-5, 5))
|
|
|
|
number_line.rotate(90 * DEGREES)
|
|
|
|
number_line.set_height(1.5 * creature.get_height())
|
|
|
|
number_line.next_to(creature, direction, buff=MED_LARGE_BUFF)
|
|
|
|
number_line.add_numbers(
|
|
|
|
range(-4, 6, 2),
|
|
|
|
font_size=18,
|
|
|
|
color=GREY_B,
|
|
|
|
buff=0.1,
|
|
|
|
direction=LEFT,
|
|
|
|
)
|
|
|
|
|
|
|
|
dot = Dot(color=color)
|
|
|
|
dot.add_updater(lambda m: m.move_to(number_line.n2p(creature.love_tracker.get_value())))
|
|
|
|
|
|
|
|
label = VGroup(Tex(var_name, "=", font_size=36), DecimalNumber(font_size=24))
|
|
|
|
label.set_color(color)
|
|
|
|
label[0].shift(label[1].get_left() + SMALL_BUFF * LEFT - label[0][1].get_right())
|
|
|
|
label.next_to(number_line, UP)
|
|
|
|
label[1].add_updater(lambda m: m.set_value(creature.love_tracker.get_value()).set_color(color))
|
|
|
|
|
|
|
|
result = VGroup(number_line, dot, label)
|
|
|
|
result.set_stroke(background=True)
|
|
|
|
result.number_line = number_line
|
|
|
|
result.dot = dot
|
|
|
|
result.label = label
|
2021-03-23 08:45:23 -07:00
|
|
|
creature.scale_mob = result
|
2021-03-18 17:27:51 -07:00
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
class DiscussSystem(Scene):
|
|
|
|
def construct(self):
|
2021-03-19 10:54:42 -07:00
|
|
|
# Setup equations
|
2021-03-18 17:27:51 -07:00
|
|
|
equations = VGroup(
|
|
|
|
Tex("{dx \\over dt} {{=}} -{{y(t)}}"),
|
|
|
|
Tex("{dy \\over dt} {{=}} {{x(t)}}"),
|
|
|
|
)
|
|
|
|
equations.arrange(RIGHT, buff=LARGE_BUFF)
|
|
|
|
equations.to_edge(UP, buff=1.5)
|
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
eq_rect = SurroundingRectangle(equations, stroke_width=2, buff=0.25)
|
2021-03-18 17:27:51 -07:00
|
|
|
sys_label = Text("System of differential equations")
|
|
|
|
sys_label.next_to(eq_rect, UP)
|
|
|
|
|
|
|
|
self.add(equations)
|
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
self.play(
|
|
|
|
FadeIn(sys_label, 0.5 * UP),
|
|
|
|
ShowCreation(eq_rect),
|
|
|
|
)
|
|
|
|
style = {"color": BLUE, "time_width": 3, "run_time": 2}
|
|
|
|
self.play(LaggedStart(
|
|
|
|
FlashAround(sys_label.get_part_by_text("differential"), **style),
|
|
|
|
FlashAround(equations[0].get_part_by_tex("dx"), **style),
|
|
|
|
FlashAround(equations[1].get_part_by_tex("dy"), **style),
|
|
|
|
))
|
|
|
|
self.wait()
|
2021-03-18 17:27:51 -07:00
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
# Ask for explicit solutions
|
|
|
|
solutions = VGroup(
|
|
|
|
Tex("x(t) {{=}} (\\text{expression with } t)"),
|
|
|
|
Tex("y(t) {{=}} (\\text{expression with } t)"),
|
|
|
|
)
|
|
|
|
for solution in solutions:
|
|
|
|
solution.set_color_by_tex("expression", GREY_B)
|
|
|
|
solutions.arrange(DOWN, buff=0.5)
|
|
|
|
solutions.move_to(equations)
|
|
|
|
solutions.set_x(3)
|
2021-03-18 17:27:51 -07:00
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
self.play(
|
|
|
|
sys_label.animate.match_width(eq_rect).to_edge(LEFT),
|
|
|
|
VGroup(equations, eq_rect).animate.to_edge(LEFT),
|
|
|
|
LaggedStartMap(FadeIn, solutions, shift=0.5 * UP, lag_ratio=0.3),
|
|
|
|
)
|
|
|
|
self.wait()
|
2021-03-18 17:27:51 -07:00
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
# Show a guess
|
|
|
|
guess_rhss = VGroup(
|
|
|
|
Tex("\\cos(t)", color=GREY_B)[0],
|
|
|
|
Tex("\\sin(t)", color=GREY_B)[0],
|
|
|
|
)
|
|
|
|
temp_rhss = VGroup()
|
|
|
|
for rhs, solution in zip(guess_rhss, solutions):
|
|
|
|
temp_rhss.add(solution[2])
|
|
|
|
rhs.move_to(solution[2], LEFT)
|
2021-03-18 17:27:51 -07:00
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
bubble = ThoughtBubble(height=4, width=4)
|
|
|
|
bubble.flip()
|
|
|
|
bubble.set_fill(opacity=0)
|
|
|
|
bubble[:3].rotate(30 * DEGREES, about_point=bubble[3].get_center() + 0.2 * RIGHT)
|
|
|
|
bubble.shift(solutions.get_left() + 0.7 * LEFT - bubble[3].get_left())
|
|
|
|
|
|
|
|
self.remove(temp_rhss)
|
|
|
|
self.play(
|
|
|
|
ShowCreation(bubble),
|
|
|
|
*(
|
|
|
|
TransformMatchingShapes(temp_rhs.copy(), guess_rhs)
|
|
|
|
for temp_rhs, guess_rhs in zip(temp_rhss, guess_rhss)
|
2021-03-18 17:27:51 -07:00
|
|
|
),
|
|
|
|
)
|
2021-03-19 10:54:42 -07:00
|
|
|
self.wait()
|
2021-03-18 17:27:51 -07:00
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
# Not enough!
|
|
|
|
not_enough = Text("Not enough!", font_size=40)
|
|
|
|
not_enough.next_to(bubble[3].get_corner(UR), DR)
|
|
|
|
not_enough.set_color(RED)
|
|
|
|
|
|
|
|
self.play(LaggedStartMap(FadeIn, not_enough, run_time=1, lag_ratio=0.1))
|
|
|
|
self.wait()
|
|
|
|
self.remove(guess_rhss)
|
|
|
|
self.play(
|
|
|
|
LaggedStartMap(FadeOut, VGroup(*bubble, *not_enough)),
|
|
|
|
*(
|
|
|
|
TransformMatchingShapes(guess_rhs.copy(), temp_rhs)
|
|
|
|
for temp_rhs, guess_rhs in zip(temp_rhss, guess_rhss)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
# Initial condition
|
|
|
|
solutions.generate_target()
|
|
|
|
initial_conditions = VGroup(
|
|
|
|
Tex("x(0) = x_0"),
|
|
|
|
Tex("y(0) = y_0"),
|
|
|
|
)
|
|
|
|
full_requirement = VGroup(*solutions.target, *initial_conditions)
|
|
|
|
full_requirement.arrange(DOWN, buff=0.25, aligned_edge=LEFT)
|
|
|
|
full_requirement.scale(0.8)
|
|
|
|
full_requirement.move_to(solutions)
|
|
|
|
full_requirement.to_edge(UP)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(solutions),
|
|
|
|
LaggedStartMap(FadeIn, initial_conditions, shift=0.1 * UP, lag_ratio=0.3),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
ic_label = Text("Initial condition", font_size=30)
|
|
|
|
ic_label.set_color(BLUE)
|
|
|
|
ic_label.next_to(initial_conditions, RIGHT, buff=1.0)
|
|
|
|
ic_arrows = VGroup(*(
|
|
|
|
Arrow(ic_label.get_left(), eq.get_right(), buff=0.1, fill_color=BLUE, thickness=0.025)
|
|
|
|
for eq in initial_conditions
|
|
|
|
))
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(ic_label),
|
|
|
|
LaggedStartMap(GrowArrow, ic_arrows, run_time=1)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
2021-03-23 08:45:23 -07:00
|
|
|
class HowExampleLeadsToMatrixExponents(Scene):
|
2021-03-19 10:54:42 -07:00
|
|
|
def construct(self):
|
2021-03-23 08:45:23 -07:00
|
|
|
# Screen
|
|
|
|
self.add(FullScreenRectangle())
|
|
|
|
screen = ScreenRectangle()
|
|
|
|
screen.set_height(3)
|
|
|
|
screen.set_fill(BLACK, 1)
|
|
|
|
screen.set_stroke(BLUE_B, 2)
|
|
|
|
screen.to_edge(LEFT)
|
|
|
|
self.add(screen)
|
|
|
|
|
|
|
|
# Mat exp
|
|
|
|
mat_exp = get_matrix_exponential(
|
|
|
|
[["a", "b"], ["c", "d"]],
|
|
|
|
height=2,
|
|
|
|
h_buff=0.75, v_buff=0.5
|
|
|
|
)
|
|
|
|
mat_exp.set_x(FRAME_WIDTH / 4)
|
|
|
|
|
|
|
|
def get_arrow():
|
|
|
|
return Arrow(screen, mat_exp[0])
|
|
|
|
|
|
|
|
arrow = get_arrow()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
GrowArrow(arrow),
|
|
|
|
FadeIn(mat_exp, RIGHT)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# New screen
|
|
|
|
screen2 = screen.copy()
|
|
|
|
screen2.set_stroke(GREY_BROWN, 2)
|
|
|
|
screen2.to_corner(DR)
|
|
|
|
|
|
|
|
mat_exp.generate_target()
|
|
|
|
mat_exp.target.to_edge(UP)
|
|
|
|
mat_exp.target.match_x(screen2)
|
|
|
|
double_arrow = VGroup(
|
|
|
|
Arrow(mat_exp.target, screen2),
|
|
|
|
Arrow(screen2, mat_exp.target),
|
|
|
|
)
|
|
|
|
for mob in double_arrow:
|
|
|
|
mob.scale(0.9, about_point=mob.get_end())
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(mat_exp),
|
|
|
|
GrowFromCenter(double_arrow),
|
|
|
|
arrow.animate.become(Arrow(screen, screen2)),
|
|
|
|
FadeIn(screen2, DOWN),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class RomeoJulietVectorSpace(RomeoAndJuliet):
|
|
|
|
def construct(self):
|
|
|
|
# Set up Romeo and Juliet
|
|
|
|
romeo, juliet = lovers = self.get_romeo_and_juliet()
|
|
|
|
lovers.set_height(2.0)
|
|
|
|
lovers.arrange(LEFT, buff=3)
|
|
|
|
name_labels = self.get_romeo_juilet_name_labels(lovers, font_size=36, spacing=1.1)
|
|
|
|
self.make_romeo_and_juliet_dynamic(*lovers)
|
|
|
|
|
|
|
|
self.add(*lovers)
|
|
|
|
self.add(*name_labels)
|
|
|
|
|
|
|
|
# Scales
|
|
|
|
juliet_scale = self.get_love_scale(juliet, LEFT, "x", BLUE_B)
|
|
|
|
romeo_scale = self.get_love_scale(romeo, RIGHT, "y", BLUE)
|
|
|
|
scales = [juliet_scale, romeo_scale]
|
|
|
|
self.add(*scales)
|
|
|
|
|
|
|
|
# Animate in
|
|
|
|
psp_tracker = Point()
|
|
|
|
|
|
|
|
def get_psp():
|
|
|
|
# Get phase space point
|
|
|
|
return psp_tracker.get_location()
|
|
|
|
|
|
|
|
juliet.love_tracker.add_updater(lambda m: m.set_value(get_psp()[0]))
|
|
|
|
romeo.love_tracker.add_updater(lambda m: m.set_value(get_psp()[1]))
|
|
|
|
self.add(romeo.love_tracker, juliet.love_tracker)
|
|
|
|
|
|
|
|
psp_tracker.move_to([1, -3, 0])
|
|
|
|
self.play(
|
|
|
|
Rotate(psp_tracker, 90 * DEGREES, about_point=ORIGIN, run_time=3, rate_func=linear)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Transition to axes
|
|
|
|
axes = Axes(
|
|
|
|
x_range=(-5, 5),
|
|
|
|
y_range=(-5, 5),
|
|
|
|
height=7,
|
|
|
|
width=7,
|
|
|
|
axis_config={
|
|
|
|
"include_tip": False,
|
|
|
|
"numbers_to_exclude": [],
|
|
|
|
}
|
|
|
|
)
|
|
|
|
axes.set_x(-3)
|
|
|
|
|
|
|
|
for axis in axes:
|
|
|
|
axis.add_numbers(range(-4, 6, 2), color=GREY_B)
|
|
|
|
axis.numbers[2].set_opacity(0)
|
|
|
|
|
|
|
|
for pi in lovers:
|
|
|
|
pi.clear_updaters()
|
|
|
|
pi.generate_target()
|
|
|
|
pi.target.set_height(0.75)
|
|
|
|
pi.name_label.generate_target()
|
|
|
|
pi.name_label.target.scale(0.5)
|
|
|
|
group = VGroup(pi.target, pi.name_label.target)
|
|
|
|
group.arrange(DOWN, buff=SMALL_BUFF)
|
|
|
|
pi.target_group = group
|
|
|
|
pi.scale_mob[2].clear_updaters()
|
|
|
|
self.add(*pi.scale_mob)
|
|
|
|
juliet.target_group.next_to(axes.x_axis.get_end(), RIGHT)
|
|
|
|
romeo.target_group.next_to(axes.y_axis.get_corner(UR), RIGHT)
|
|
|
|
romeo.target_group.shift_onto_screen(buff=MED_SMALL_BUFF)
|
|
|
|
romeo.target.flip()
|
|
|
|
juliet.target.flip()
|
|
|
|
juliet.target.make_eye_contact(romeo.target)
|
|
|
|
|
|
|
|
self.play(LaggedStart(
|
|
|
|
juliet.scale_mob.number_line.animate.become(axes.x_axis),
|
|
|
|
FadeOut(juliet.scale_mob.label),
|
|
|
|
MoveToTarget(juliet),
|
|
|
|
MoveToTarget(juliet.name_label),
|
|
|
|
romeo.scale_mob.number_line.animate.become(axes.y_axis),
|
|
|
|
FadeOut(romeo.scale_mob.label),
|
|
|
|
MoveToTarget(romeo),
|
|
|
|
MoveToTarget(romeo.name_label),
|
|
|
|
run_time=3
|
|
|
|
))
|
|
|
|
self.add(*romeo.scale_mob[:2], *juliet.scale_mob[:2])
|
|
|
|
|
|
|
|
# Reset pi creatures
|
|
|
|
self.remove(lovers)
|
|
|
|
self.remove(romeo.heart_eyes)
|
|
|
|
self.remove(juliet.heart_eyes)
|
|
|
|
new_lovers = self.get_romeo_and_juliet()
|
|
|
|
for new_pi, pi in zip(new_lovers, lovers):
|
|
|
|
new_pi.flip()
|
|
|
|
new_pi.replace(pi)
|
|
|
|
new_pi.scale_mob = pi.scale_mob
|
|
|
|
lovers = new_lovers
|
|
|
|
romeo, juliet = new_lovers
|
|
|
|
self.add(romeo, juliet)
|
|
|
|
self.make_romeo_and_juliet_dynamic(romeo, juliet)
|
|
|
|
juliet.love_tracker.add_updater(lambda m: m.set_value(get_psp()[0]))
|
|
|
|
romeo.love_tracker.add_updater(lambda m: m.set_value(get_psp()[1]))
|
|
|
|
self.add(romeo.love_tracker, juliet.love_tracker)
|
|
|
|
|
|
|
|
# h_line and v_line
|
|
|
|
ps_dot = Dot(color=BLUE)
|
|
|
|
ps_dot.add_updater(lambda m: m.move_to(axes.c2p(*get_psp()[:2])))
|
|
|
|
v_line = Line().set_stroke(BLUE_D, 2)
|
|
|
|
h_line = Line().set_stroke(BLUE_B, 2)
|
|
|
|
v_line.add_updater(lambda m: m.put_start_and_end_on(
|
|
|
|
axes.x_axis.n2p(get_psp()[0]),
|
|
|
|
axes.c2p(*get_psp()[:2]),
|
|
|
|
))
|
|
|
|
h_line.add_updater(lambda m: m.put_start_and_end_on(
|
|
|
|
axes.y_axis.n2p(get_psp()[1]),
|
|
|
|
axes.c2p(*get_psp()[:2]),
|
|
|
|
))
|
|
|
|
x_dec = DecimalNumber(0, font_size=24)
|
|
|
|
x_dec.next_to(h_line, UP, SMALL_BUFF)
|
|
|
|
y_dec = DecimalNumber(0, font_size=24)
|
|
|
|
y_dec.next_to(v_line, RIGHT, SMALL_BUFF)
|
|
|
|
|
|
|
|
romeo.scale_mob.dot.clear_updaters()
|
|
|
|
juliet.scale_mob.dot.clear_updaters()
|
|
|
|
self.play(
|
|
|
|
ShowCreation(h_line.copy().clear_updaters(), remover=True),
|
|
|
|
ShowCreation(v_line.copy().clear_updaters(), remover=True),
|
|
|
|
ReplacementTransform(romeo.scale_mob.dot, ps_dot),
|
|
|
|
ReplacementTransform(juliet.scale_mob.dot, ps_dot),
|
|
|
|
ChangeDecimalToValue(x_dec, get_psp()[0]),
|
|
|
|
VFadeIn(x_dec),
|
|
|
|
ChangeDecimalToValue(y_dec, get_psp()[1]),
|
|
|
|
VFadeIn(y_dec),
|
|
|
|
)
|
|
|
|
self.add(h_line, v_line, ps_dot)
|
|
|
|
|
|
|
|
# Add coordinates
|
|
|
|
equation = VGroup(
|
|
|
|
Matrix([["x"], ["y"]], bracket_h_buff=SMALL_BUFF),
|
|
|
|
Tex("="),
|
|
|
|
DecimalMatrix(
|
|
|
|
np.reshape(get_psp()[:2], (2, 1)),
|
|
|
|
element_to_mobject_config={
|
|
|
|
"num_decimal_places": 2,
|
|
|
|
"font_size": 36,
|
|
|
|
"include_sign": True,
|
|
|
|
}
|
|
|
|
),
|
|
|
|
)
|
|
|
|
equation[0].match_height(equation[2])
|
|
|
|
equation.arrange(RIGHT)
|
|
|
|
equation.to_corner(UR)
|
|
|
|
equation.shift(MED_SMALL_BUFF * LEFT)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(equation[:2]),
|
|
|
|
FadeIn(equation[2].get_brackets()),
|
|
|
|
TransformFromCopy(x_dec, equation[2].get_entries()[0]),
|
|
|
|
TransformFromCopy(y_dec, equation[2].get_entries()[1]),
|
|
|
|
)
|
|
|
|
equation[2].get_entries()[0].add_updater(lambda m: m.set_value(get_psp()[0]))
|
|
|
|
equation[2].get_entries()[1].add_updater(lambda m: m.set_value(get_psp()[1]))
|
|
|
|
|
|
|
|
self.play(FadeOut(x_dec), FadeOut(y_dec))
|
|
|
|
|
|
|
|
# Play around in state space
|
|
|
|
self.play(psp_tracker.move_to, [3, -2, 0], path_arc=120 * DEGREES, run_time=3)
|
|
|
|
self.wait()
|
|
|
|
self.play(psp_tracker.move_to, [-5, -2, 0], path_arc=0 * DEGREES, run_time=3, rate_func=there_and_back)
|
|
|
|
self.wait()
|
|
|
|
self.play(psp_tracker.move_to, [3, 5, 0], path_arc=0 * DEGREES, run_time=3, rate_func=there_and_back)
|
|
|
|
self.wait()
|
|
|
|
self.play(psp_tracker.move_to, [5, 3, 0], path_arc=-120 * DEGREES, run_time=2)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Arrow vs. dot
|
|
|
|
arrow = Arrow(axes.get_origin(), ps_dot.get_center(), buff=0, fill_color=BLUE)
|
|
|
|
arrow.set_stroke(BLACK, 2, background=True)
|
|
|
|
arrow_outline = arrow.copy()
|
|
|
|
arrow_outline.set_fill(opacity=0)
|
|
|
|
arrow_outline.set_stroke(YELLOW, 1)
|
|
|
|
self.play(LaggedStart(
|
|
|
|
FadeIn(arrow),
|
|
|
|
FadeOut(ps_dot),
|
|
|
|
ShowPassingFlash(arrow_outline, run_time=1, time_width=0.5),
|
|
|
|
lag_ratio=0.5,
|
|
|
|
))
|
|
|
|
self.wait()
|
|
|
|
self.play(LaggedStart(
|
|
|
|
FadeIn(ps_dot),
|
|
|
|
FadeOut(arrow),
|
|
|
|
FlashAround(ps_dot, buff=0.05),
|
|
|
|
))
|
|
|
|
self.wait()
|
|
|
|
self.play(FlashAround(equation))
|
|
|
|
self.play(psp_tracker.move_to, [4, 3, 0], run_time=2)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Function of time
|
|
|
|
new_lhs = Matrix([["x(t)"], ["y(t)"]])
|
|
|
|
new_lhs.match_height(equation[0])
|
|
|
|
new_lhs.move_to(equation[0], RIGHT)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeTransformPieces(equation[0], new_lhs),
|
|
|
|
)
|
|
|
|
self.remove(equation[0])
|
|
|
|
self.add(new_lhs)
|
|
|
|
equation.replace_submobject(0, new_lhs)
|
|
|
|
|
|
|
|
# Initialize rotation
|
|
|
|
curr_time = self.time
|
|
|
|
curr_psp = get_psp()
|
|
|
|
psp_tracker.add_updater(lambda m: m.move_to(np.dot(
|
|
|
|
curr_psp,
|
|
|
|
np.transpose(rotation_about_z(0.25 * (self.time - curr_time))),
|
|
|
|
)))
|
|
|
|
self.wait(5)
|
|
|
|
|
|
|
|
# Rate of change
|
|
|
|
deriv_lhs = Matrix([["x'(t)"], ["y'(t)"]], bracket_h_buff=SMALL_BUFF)
|
|
|
|
deriv_lhs.match_height(equation[0])
|
|
|
|
deriv_lhs.move_to(equation[0])
|
|
|
|
deriv_lhs.set_color(RED_B)
|
|
|
|
deriv_label = Text("Rate of change", font_size=24)
|
|
|
|
deriv_label.match_width(deriv_lhs)
|
|
|
|
deriv_label.match_color(deriv_lhs)
|
|
|
|
deriv_label.next_to(deriv_lhs, DOWN, SMALL_BUFF)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(deriv_lhs),
|
|
|
|
Write(deriv_label, run_time=1),
|
|
|
|
equation.animate.shift(2.0 * deriv_lhs.get_height() * DOWN)
|
|
|
|
)
|
|
|
|
self.wait(5)
|
|
|
|
|
|
|
|
deriv_vect = Arrow(fill_color=RED_B)
|
|
|
|
deriv_vect.add_updater(
|
|
|
|
lambda m: m.put_start_and_end_on(
|
|
|
|
axes.get_origin(),
|
|
|
|
axes.c2p(-0.5 * get_psp()[1], 0.5 * get_psp()[0])
|
|
|
|
).shift(
|
|
|
|
ps_dot.get_center() - axes.get_origin()
|
|
|
|
)
|
|
|
|
)
|
|
|
|
pre_vect = Arrow(LEFT, RIGHT)
|
|
|
|
pre_vect.replace(deriv_label, dim_to_match=0)
|
|
|
|
pre_vect.set_fill(RED_B, 0)
|
|
|
|
moving_vect = pre_vect.copy()
|
|
|
|
deriv_vect.set_opacity(0)
|
|
|
|
self.add(deriv_vect)
|
|
|
|
self.play(
|
|
|
|
UpdateFromAlphaFunc(
|
|
|
|
moving_vect,
|
|
|
|
lambda m, a: m.interpolate(pre_vect, deriv_vect, a).set_fill(opacity=a),
|
|
|
|
remover=True
|
|
|
|
)
|
|
|
|
)
|
|
|
|
deriv_vect.set_fill(opacity=1)
|
|
|
|
self.add(deriv_vect, ps_dot)
|
|
|
|
self.wait(8)
|
|
|
|
|
|
|
|
# Show equation
|
|
|
|
rhs = VGroup(
|
|
|
|
Tex("="),
|
|
|
|
Matrix([["-y(t)"], ["x(t)"]], bracket_h_buff=SMALL_BUFF)
|
|
|
|
)
|
|
|
|
rhs.match_height(deriv_lhs)
|
|
|
|
rhs.arrange(RIGHT)
|
|
|
|
rhs.next_to(deriv_lhs, RIGHT)
|
|
|
|
|
|
|
|
self.play(FadeIn(rhs))
|
|
|
|
self.wait()
|
|
|
|
for i in range(2):
|
|
|
|
self.play(FlashAround(
|
|
|
|
VGroup(deriv_lhs.get_entries()[i], rhs[1].get_entries()[i]),
|
|
|
|
run_time=3,
|
|
|
|
time_width=4,
|
|
|
|
))
|
|
|
|
self.wait(2)
|
|
|
|
|
|
|
|
# Write with a matrix
|
|
|
|
deriv_lhs.generate_target()
|
|
|
|
new_eq = VGroup(
|
|
|
|
deriv_lhs.target,
|
|
|
|
Tex("="),
|
|
|
|
IntegerMatrix([[0, -1], [1, 0]], bracket_v_buff=MED_LARGE_BUFF),
|
|
|
|
Matrix([["x(t)"], ["y(t)"]], bracket_h_buff=SMALL_BUFF),
|
|
|
|
)
|
|
|
|
new_eq[2].match_height(new_eq[0])
|
|
|
|
new_eq[3].match_height(new_eq[0])
|
|
|
|
new_eq.arrange(RIGHT)
|
|
|
|
new_eq.to_corner(UR)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(deriv_lhs),
|
|
|
|
MaintainPositionRelativeTo(deriv_label, deriv_lhs),
|
|
|
|
ReplacementTransform(rhs[0], new_eq[1]),
|
|
|
|
ReplacementTransform(rhs[1].get_brackets(), new_eq[3].get_brackets()),
|
|
|
|
FadeIn(new_eq[2], scale=2),
|
|
|
|
FadeTransform(rhs[1].get_entries()[1], new_eq[3].get_entries()[0]),
|
|
|
|
FadeTransform(rhs[1].get_entries()[0], new_eq[3].get_entries()[1]),
|
|
|
|
)
|
|
|
|
self.wait(3)
|
|
|
|
|
|
|
|
row_rect = SurroundingRectangle(new_eq[2].get_entries()[:2], buff=SMALL_BUFF)
|
|
|
|
col_rect = SurroundingRectangle(new_eq[3].get_entries(), buff=SMALL_BUFF)
|
|
|
|
both_rects = VGroup(row_rect, col_rect)
|
|
|
|
both_rects.set_stroke(YELLOW, 2)
|
|
|
|
|
|
|
|
self.play(*map(ShowCreation, both_rects))
|
|
|
|
self.wait(3)
|
|
|
|
self.play(row_rect.animate.move_to(new_eq[2].get_entries()[2:4]))
|
|
|
|
self.wait(3)
|
|
|
|
self.play(FadeOut(both_rects))
|
|
|
|
|
|
|
|
# Write general form
|
|
|
|
general_form = Tex(
|
|
|
|
"{d \\over dt}",
|
|
|
|
"\\vec{\\textbf{v} }",
|
|
|
|
"(t)",
|
|
|
|
"=",
|
|
|
|
"\\textbf{M}",
|
|
|
|
"\\vec{\\textbf{v} }",
|
|
|
|
"(t)",
|
|
|
|
)
|
|
|
|
general_form.set_color_by_tex("d \\over dt", RED_B)
|
|
|
|
general_form.set_color_by_tex("\\textbf{v}", GREY_B)
|
|
|
|
general_form.scale(1.2)
|
|
|
|
general_form.next_to(new_eq, DOWN, LARGE_BUFF)
|
|
|
|
general_form.shift(0.5 * RIGHT)
|
|
|
|
gf_rect = SurroundingRectangle(general_form, buff=MED_SMALL_BUFF)
|
|
|
|
gf_rect.set_stroke(YELLOW, 2)
|
|
|
|
|
|
|
|
equation.clear_updaters()
|
|
|
|
self.play(
|
|
|
|
FadeIn(general_form),
|
|
|
|
FadeOut(equation),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(ShowCreation(gf_rect))
|
|
|
|
self.wait(4 * TAU)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
class From2DTo1D(Scene):
|
|
|
|
show_solution = False
|
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
def construct(self):
|
2021-03-24 13:49:29 -07:00
|
|
|
# (Setup vector equation)
|
|
|
|
equation = get_2d_equation()
|
|
|
|
equation.center()
|
|
|
|
equation.to_edge(UP, buff=1.0)
|
|
|
|
deriv, vect_sym, equals, matrix_mob, vect_sym2 = equation
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
vect_sym.save_state()
|
|
|
|
|
|
|
|
# (Setup plane)
|
|
|
|
plane = NumberPlane(
|
|
|
|
x_range=(-4, 4),
|
|
|
|
y_range=(-2, 2),
|
|
|
|
height=4,
|
|
|
|
width=8,
|
2021-03-19 10:54:42 -07:00
|
|
|
)
|
2021-03-24 13:49:29 -07:00
|
|
|
plane.to_edge(DOWN)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
point = Point(plane.c2p(2, 0.5))
|
|
|
|
vector = Arrow(plane.get_origin(), point.get_location(), buff=0)
|
|
|
|
vector.set_color(YELLOW)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
# Show vector
|
|
|
|
vect_sym.set_x(0)
|
|
|
|
static_vect_sym = vect_sym.deepcopy()
|
|
|
|
for entry in static_vect_sym.get_entries():
|
|
|
|
entry[1:].set_opacity(0)
|
|
|
|
entry[:1].move_to(entry)
|
|
|
|
static_vect_sym.get_brackets().space_out_submobjects(0.7)
|
|
|
|
vector.save_state()
|
|
|
|
vector.put_start_and_end_on(
|
|
|
|
static_vect_sym.get_corner(DL),
|
|
|
|
static_vect_sym.get_corner(UR),
|
|
|
|
)
|
|
|
|
vector.set_opacity(0)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
self.add(plane, static_vect_sym)
|
|
|
|
self.play(Restore(vector))
|
2021-03-19 10:54:42 -07:00
|
|
|
self.wait()
|
2021-03-24 13:49:29 -07:00
|
|
|
|
|
|
|
# Changing with time
|
|
|
|
def func(x, y):
|
|
|
|
return 0.2 * np.dot([x, y], matrix.T)
|
|
|
|
|
|
|
|
move_points_along_vector_field(point, func, plane)
|
|
|
|
globals().update(locals())
|
|
|
|
vector.add_updater(lambda m: m.put_start_and_end_on(
|
|
|
|
plane.get_origin(), point.get_location(),
|
|
|
|
))
|
|
|
|
deriv_vector = Vector(fill_color=RED, thickness=0.03)
|
|
|
|
deriv_vector.add_updater(
|
|
|
|
lambda m: m.put_start_and_end_on(
|
|
|
|
plane.get_origin(),
|
|
|
|
plane.c2p(*func(*plane.p2c(point.get_location()))),
|
|
|
|
).shift(vector.get_vector())
|
2021-03-19 10:54:42 -07:00
|
|
|
)
|
2021-03-24 13:49:29 -07:00
|
|
|
|
|
|
|
self.add(point)
|
|
|
|
self.play(ReplacementTransform(static_vect_sym, vect_sym))
|
|
|
|
self.wait(3)
|
|
|
|
|
|
|
|
# Show matrix equation
|
|
|
|
deriv_underline = Underline(VGroup(deriv, vect_sym.saved_state))
|
|
|
|
deriv_underline.set_stroke(RED, 3)
|
|
|
|
alt_line = deriv_underline.deepcopy()
|
|
|
|
|
2021-03-19 10:54:42 -07:00
|
|
|
self.play(
|
2021-03-24 13:49:29 -07:00
|
|
|
Restore(vect_sym),
|
|
|
|
FadeIn(deriv),
|
2021-03-19 10:54:42 -07:00
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
2021-03-24 13:49:29 -07:00
|
|
|
ShowCreation(deriv_underline),
|
2021-03-19 10:54:42 -07:00
|
|
|
)
|
|
|
|
self.play(
|
2021-03-24 13:49:29 -07:00
|
|
|
VFadeIn(deriv_vector, rate_func=squish_rate_func(smooth, 0.8, 1.0)),
|
|
|
|
UpdateFromAlphaFunc(
|
|
|
|
alt_line,
|
|
|
|
lambda m, a: m.put_start_and_end_on(
|
|
|
|
interpolate(deriv_underline.get_start(), deriv_vector.get_start(), a),
|
|
|
|
interpolate(deriv_underline.get_end(), deriv_vector.get_end(), a),
|
|
|
|
),
|
|
|
|
remover=True,
|
2021-03-19 10:54:42 -07:00
|
|
|
),
|
|
|
|
)
|
2021-03-24 13:49:29 -07:00
|
|
|
self.wait(4)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
|
|
|
self.play(
|
2021-03-24 13:49:29 -07:00
|
|
|
LaggedStartMap(FadeIn, equation[2:4], shift=RIGHT, lag_ratio=0.3),
|
|
|
|
TransformFromCopy(
|
|
|
|
equation[1], equation[4], path_arc=-45 * DEGREES,
|
|
|
|
run_time=2,
|
|
|
|
rate_func=squish_rate_func(smooth, 0.3, 1.0)
|
|
|
|
)
|
2021-03-19 10:54:42 -07:00
|
|
|
)
|
2021-03-24 13:49:29 -07:00
|
|
|
self.wait(8)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
# Highlight equation
|
|
|
|
deriv_rect = SurroundingRectangle(equation[:2])
|
|
|
|
deriv_rect.set_stroke(RED, 2)
|
|
|
|
rhs_rect = SurroundingRectangle(equation[-1])
|
|
|
|
rhs_rect.set_stroke(YELLOW, 2)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
self.play(ShowCreation(deriv_rect))
|
|
|
|
self.wait()
|
|
|
|
self.play(ReplacementTransform(deriv_rect, rhs_rect, path_arc=-45 * DEGREES))
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
# Draw vector field
|
|
|
|
vector_field = VectorField(
|
|
|
|
func, plane,
|
|
|
|
magnitude_range=(0, 1.2),
|
|
|
|
opacity=0.5,
|
|
|
|
vector_config={"thickness": 0.02}
|
|
|
|
)
|
|
|
|
vector_field.sort(lambda p: get_norm(p - plane.get_origin()))
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
self.add(vector_field, deriv_vector, vector)
|
|
|
|
VGroup(vector, deriv_vector).set_stroke(BLACK, 5, background=True)
|
|
|
|
self.play(
|
|
|
|
FadeOut(rhs_rect),
|
|
|
|
LaggedStartMap(GrowArrow, vector_field, lag_ratio=0)
|
2021-03-19 10:54:42 -07:00
|
|
|
)
|
2021-03-24 13:49:29 -07:00
|
|
|
self.wait(14)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
# flow_lines = AnimatedStreamLines(StreamLines(func, plane, step_multiple=0.25))
|
|
|
|
# self.add(flow_lines)
|
|
|
|
# self.wait(4)
|
|
|
|
# self.play(VFadeOut(flow_lines))
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
# self.wait(10)
|
2021-03-19 10:54:42 -07:00
|
|
|
|
2021-03-24 13:49:29 -07:00
|
|
|
# Show solution
|
|
|
|
equation.add(deriv_underline)
|
|
|
|
mat_exp = get_matrix_exponential(
|
|
|
|
[["a", "b"], ["c", "d"]],
|
|
|
|
h_buff=0.75,
|
|
|
|
v_buff=0.75,
|
|
|
|
)
|
|
|
|
mat_exp[1].set_color(TEAL)
|
|
|
|
if self.show_solution:
|
|
|
|
equation.generate_target()
|
|
|
|
equation.target.to_edge(LEFT)
|
|
|
|
implies = Tex("\\Rightarrow")
|
|
|
|
implies.next_to(equation.target, RIGHT)
|
|
|
|
solution = VGroup(
|
|
|
|
equation[1].copy(),
|
|
|
|
Tex("="),
|
|
|
|
mat_exp,
|
|
|
|
Matrix(
|
|
|
|
[["x(0)"], ["y(0)"]],
|
|
|
|
bracket_h_buff=SMALL_BUFF,
|
|
|
|
bracket_v_buff=SMALL_BUFF,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
solution[2].match_height(solution[0])
|
|
|
|
solution[3].match_height(solution[0])
|
|
|
|
solution.arrange(RIGHT, buff=MED_SMALL_BUFF)
|
|
|
|
solution.next_to(implies, RIGHT, MED_LARGE_BUFF)
|
|
|
|
solution.align_to(equation[1], DOWN)
|
|
|
|
solution_rect = SurroundingRectangle(solution, buff=MED_SMALL_BUFF)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(equation),
|
|
|
|
)
|
|
|
|
self.play(LaggedStart(
|
|
|
|
Write(implies),
|
|
|
|
ShowCreation(solution_rect),
|
|
|
|
TransformFromCopy(equation[4], solution[0], path_arc=30 * DEGREES),
|
|
|
|
))
|
|
|
|
self.wait()
|
|
|
|
self.play(LaggedStart(
|
|
|
|
TransformFromCopy(equation[3], solution[2][1]),
|
|
|
|
FadeIn(solution[2][0]),
|
|
|
|
FadeIn(solution[2][2]),
|
|
|
|
FadeIn(solution[1]),
|
|
|
|
FadeIn(solution[3]),
|
|
|
|
lag_ratio=0.1,
|
|
|
|
))
|
|
|
|
self.wait(10)
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
# Show relation with matrix exp
|
|
|
|
mat_exp.move_to(equation[0], DOWN)
|
|
|
|
mat_exp.to_edge(RIGHT, buff=LARGE_BUFF)
|
|
|
|
equation.generate_target()
|
|
|
|
equation.target.to_edge(LEFT, buff=LARGE_BUFF)
|
|
|
|
arrow1 = Arrow(equation.target.get_corner(UR), mat_exp.get_corner(UL), path_arc=-45 * DEGREES)
|
|
|
|
arrow2 = Arrow(mat_exp.get_corner(DL), equation.target.get_corner(DR), path_arc=-45 * DEGREES)
|
|
|
|
arrow1.shift(0.2 * RIGHT)
|
|
|
|
|
|
|
|
self.play(MoveToTarget(equation))
|
|
|
|
self.play(
|
|
|
|
FadeIn(mat_exp[0::2]),
|
|
|
|
TransformFromCopy(equation[3], mat_exp[1]),
|
|
|
|
Write(arrow1, run_time=1),
|
|
|
|
)
|
|
|
|
self.wait(4)
|
|
|
|
self.play(Write(arrow2, run_time=2))
|
|
|
|
self.wait(4)
|
|
|
|
|
|
|
|
normal_exp = Tex("e^{rt}")[0]
|
|
|
|
normal_exp.set_height(1.0)
|
|
|
|
normal_exp[1].set_color(BLUE)
|
|
|
|
normal_exp.move_to(mat_exp)
|
|
|
|
self.play(
|
|
|
|
FadeTransformPieces(mat_exp, normal_exp),
|
|
|
|
FadeOut(VGroup(arrow1, arrow2))
|
|
|
|
)
|
|
|
|
self.wait(2)
|
|
|
|
|
|
|
|
# Transition to 1D
|
|
|
|
max_x = 50
|
|
|
|
mult = 50
|
|
|
|
number_line = NumberLine((0, max_x), width=max_x)
|
|
|
|
number_line.add_numbers()
|
|
|
|
number_line.move_to(plane)
|
|
|
|
number_line.to_edge(LEFT)
|
|
|
|
nl = number_line
|
|
|
|
nl2 = NumberLine((0, mult * max_x, mult), width=max_x)
|
|
|
|
nl2.add_numbers()
|
|
|
|
nl2.set_width(nl.get_width() * mult)
|
|
|
|
nl2.shift(nl.n2p(0) - nl2.n2p(0))
|
|
|
|
nl2.set_opacity(0)
|
|
|
|
nl.add(nl2)
|
|
|
|
|
|
|
|
new_equation = Tex(
|
|
|
|
"{d \\over dt}", "x(t)", "=", "r \\cdot", "x(t)",
|
|
|
|
)
|
|
|
|
new_equation[0][3].set_color(GREY_B)
|
|
|
|
new_equation[1][2].set_color(GREY_B)
|
|
|
|
new_equation[4][2].set_color(GREY_B)
|
|
|
|
new_equation[3][0].set_color(BLUE)
|
|
|
|
new_equation.match_height(equation)
|
|
|
|
new_equation.move_to(equation)
|
|
|
|
|
|
|
|
self.remove(point)
|
|
|
|
vector.clear_updaters()
|
|
|
|
deriv_vector.clear_updaters()
|
|
|
|
|
|
|
|
self.remove(vector_field)
|
|
|
|
plane.add(vector_field)
|
|
|
|
self.add(number_line, deriv_vector, vector)
|
|
|
|
self.play(
|
|
|
|
normal_exp.animate.scale(0.5).to_corner(UR),
|
|
|
|
# Plane to number line
|
|
|
|
vector.animate.put_start_and_end_on(nl.n2p(0), nl.n2p(1)),
|
|
|
|
deriv_vector.animate.put_start_and_end_on(nl.n2p(1), nl.n2p(1.5)),
|
|
|
|
plane.animate.shift(nl.n2p(0) - plane.get_origin()).set_opacity(0),
|
|
|
|
FadeIn(number_line, rate_func=squish_rate_func(smooth, 0.5, 1)),
|
|
|
|
# Equation
|
|
|
|
TransformMatchingShapes(equation[0], new_equation[0]),
|
|
|
|
Transform(equation[1].get_entries()[0], new_equation[1]),
|
|
|
|
FadeTransform(equation[2], new_equation[2]),
|
|
|
|
FadeTransform(equation[3], new_equation[3]),
|
|
|
|
FadeTransform(equation[4].get_entries()[0], new_equation[4]),
|
|
|
|
FadeOut(equation[1].get_brackets()),
|
|
|
|
FadeOut(equation[1].get_entries()[1]),
|
|
|
|
FadeOut(equation[4].get_brackets()),
|
|
|
|
FadeOut(equation[4].get_entries()[1]),
|
|
|
|
FadeOut(deriv_underline),
|
|
|
|
run_time=2,
|
|
|
|
)
|
|
|
|
|
|
|
|
vt = ValueTracker(1)
|
|
|
|
vt.add_updater(lambda m, dt: m.increment_value(0.2 * dt * m.get_value()))
|
|
|
|
|
|
|
|
vector.add_updater(lambda m: m.put_start_and_end_on(nl.n2p(0), nl.n2p(vt.get_value())))
|
|
|
|
deriv_vector.add_updater(lambda m: m.set_width(0.5 * vector.get_width()).move_to(vector.get_right(), LEFT))
|
|
|
|
|
|
|
|
self.add(vt)
|
|
|
|
self.wait(11)
|
|
|
|
self.play(
|
|
|
|
number_line.animate.scale(0.3, about_point=nl.n2p(0)),
|
|
|
|
)
|
|
|
|
self.wait(4)
|
|
|
|
number_line.generate_target()
|
|
|
|
number_line.target.scale(0.1, about_point=nl.n2p(0)),
|
|
|
|
number_line.target[-1].set_opacity(1)
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(number_line)
|
|
|
|
)
|
|
|
|
self.wait(11)
|
|
|
|
self.play(number_line.animate.scale(0.2, about_point=nl.n2p(0)))
|
|
|
|
self.wait(10)
|
|
|
|
|
|
|
|
|
|
|
|
class SimpleDerivativeOfExp(TeacherStudentsScene):
|
|
|
|
def construct(self):
|
|
|
|
eq = Tex(
|
|
|
|
"{d \\over dt}", "e^{rt}", "=", "r", "e^{rt}",
|
|
|
|
)
|
|
|
|
eq.set_color_by_tex("r", TEAL, substring=False)
|
|
|
|
for part in eq.get_parts_by_tex("e^{rt}"):
|
|
|
|
part[1].set_color(TEAL)
|
|
|
|
|
|
|
|
s0, s1, s2 = self.students
|
|
|
|
morty = self.teacher
|
|
|
|
bubble = s2.get_bubble(eq)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
s2.animate.change("pondering", eq),
|
|
|
|
ShowCreation(bubble),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
LaggedStart(
|
|
|
|
s0.animate.change("hesitant", eq),
|
|
|
|
s1.animate.change("erm", eq),
|
|
|
|
morty.animate.change("tease"),
|
|
|
|
),
|
|
|
|
Write(eq[:2])
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
TransformFromCopy(*eq.get_parts_by_tex("e^{rt}"), path_arc=45 * DEGREES),
|
|
|
|
Write(eq.get_part_by_tex("=")),
|
|
|
|
*(
|
|
|
|
pi.animate.look_at(eq[4])
|
|
|
|
for pi in self.pi_creatures
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FadeTransform(eq[4][1].copy(), eq[3][0], path_arc=90 * DEGREES)
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
LaggedStart(
|
|
|
|
s0.animate.change("thinking"),
|
|
|
|
s1.animate.change("tease"),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.wait(2)
|
|
|
|
|
|
|
|
rect = ScreenRectangle(height=3.5)
|
|
|
|
rect.set_fill(BLACK, 1)
|
|
|
|
rect.set_stroke(BLUE_B, 2)
|
|
|
|
rect.to_corner(UR)
|
|
|
|
self.play(
|
|
|
|
morty.animate.change("raise_right_hand", rect),
|
|
|
|
self.get_student_changes("pondering", "pondering", "pondering", look_at_arg=rect),
|
|
|
|
FadeIn(rect, UP)
|
|
|
|
)
|
|
|
|
self.wait(6)
|
|
|
|
|
|
|
|
|
|
|
|
class GraphOfExponential(Scene):
|
|
|
|
def construct(self):
|
|
|
|
axes = Axes(
|
|
|
|
x_range=(0, 23, 1),
|
|
|
|
y_range=(0, 300, 10),
|
|
|
|
height=150,
|
|
|
|
width=13,
|
|
|
|
axis_config={"include_tip": False}
|
|
|
|
)
|
|
|
|
axes.y_axis.add(*(axes.y_axis.get_tick(x, size=0.05) for x in range(20)))
|
|
|
|
axes.to_corner(DL)
|
|
|
|
exp_graph = axes.get_graph(lambda t: np.exp(0.25 * t))
|
|
|
|
exp_graph.set_stroke([BLUE_E, BLUE, YELLOW])
|
|
|
|
graph_template = exp_graph.copy()
|
|
|
|
graph_template.set_stroke(width=0)
|
|
|
|
axes.add(graph_template)
|
|
|
|
|
|
|
|
equation = self.get_equation()
|
|
|
|
solution = Tex("x({t}) = e^{r{t} }", tex_to_color_map={"{t}": GREY_B, "r": BLUE})
|
|
|
|
solution.next_to(equation, DOWN, MED_LARGE_BUFF)
|
|
|
|
|
|
|
|
self.add(axes)
|
|
|
|
self.add(axes.get_x_axis_label("t"))
|
|
|
|
self.add(axes.get_y_axis_label("x"))
|
|
|
|
self.add(equation)
|
|
|
|
self.add(solution)
|
|
|
|
|
|
|
|
curr_time = self.time
|
|
|
|
exp_graph.add_updater(lambda m: m.pointwise_become_partial(
|
|
|
|
graph_template, 0, (self.time - curr_time) / 20,
|
|
|
|
))
|
|
|
|
dot = Dot(color=BLUE_B, radius=0.04)
|
|
|
|
dot.add_updater(lambda d: d.move_to(exp_graph.get_end()))
|
|
|
|
vect = Arrow(DOWN, UP, fill_color=YELLOW, thickness=0.025)
|
|
|
|
vect.add_updater(lambda v: v.put_start_and_end_on(
|
|
|
|
axes.get_origin(),
|
|
|
|
axes.y_axis.get_projection(exp_graph.get_end()),
|
|
|
|
))
|
|
|
|
h_line = always_redraw(lambda: DashedLine(
|
|
|
|
vect.get_end(), dot.get_left(),
|
|
|
|
stroke_width=1,
|
|
|
|
stroke_color=GREY_B,
|
|
|
|
))
|
|
|
|
v_line = always_redraw(lambda: DashedLine(
|
|
|
|
axes.x_axis.get_projection(dot.get_bottom()),
|
|
|
|
dot.get_bottom(),
|
|
|
|
stroke_width=1,
|
|
|
|
stroke_color=GREY_B,
|
|
|
|
))
|
|
|
|
|
|
|
|
def stretch_axes(factor):
|
|
|
|
axes.generate_target(use_deepcopy=True)
|
|
|
|
axes.target.stretch(factor, 1, about_point=axes.get_origin()),
|
|
|
|
axes.target.x_axis.stretch(1 / factor, 1, about_point=axes.get_origin()),
|
|
|
|
self.play(MoveToTarget(axes))
|
|
|
|
|
|
|
|
self.add(exp_graph, dot, vect, h_line, v_line)
|
|
|
|
self.wait(8)
|
|
|
|
stretch_axes(0.2)
|
|
|
|
self.wait(6)
|
|
|
|
stretch_axes(0.23)
|
|
|
|
self.wait(4)
|
|
|
|
|
|
|
|
exp_graph.clear_updaters()
|
|
|
|
exp_graph.become(graph_template)
|
|
|
|
exp_graph.set_stroke(width=3)
|
|
|
|
self.add(exp_graph)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Highlight equation parts
|
|
|
|
index = equation.index_of_part_by_tex("=")
|
|
|
|
lhs = equation[:index]
|
|
|
|
rhs = equation[index + 1:]
|
|
|
|
|
|
|
|
for part in (lhs, rhs):
|
|
|
|
self.play(FlashAround(part, time_width=2))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
def get_equation(self):
|
|
|
|
return get_1d_equation().to_edge(UP)
|
|
|
|
|
|
|
|
|
|
|
|
class CompoundInterestPopulationAndEpidemic(Scene):
|
|
|
|
def construct(self):
|
|
|
|
N = 16000
|
|
|
|
points = 2 * np.random.random((N, 3)) - 1
|
|
|
|
points[:, 2] = 0
|
|
|
|
points = points[[get_norm(p) < 1 for p in points]]
|
|
|
|
points *= 2 * FRAME_WIDTH
|
|
|
|
points = np.array(list(sorted(points, key=get_norm)))
|
|
|
|
|
|
|
|
dollar = Tex("\\$")
|
|
|
|
dollar.set_fill(GREEN)
|
|
|
|
person = SVGMobject("person")
|
|
|
|
person.set_fill(GREY_B, 1)
|
|
|
|
virus = SVGMobject("virus")
|
|
|
|
virus.set_fill([RED, RED_D])
|
|
|
|
virus.remove(virus[1:])
|
|
|
|
virus[0].set_points(virus[0].get_subpaths()[0])
|
|
|
|
templates = [dollar, person, virus]
|
|
|
|
mob_height = 0.25
|
|
|
|
|
|
|
|
for mob in templates:
|
|
|
|
mob.set_stroke(BLACK, 1, background=True)
|
|
|
|
mob.set_height(mob_height)
|
|
|
|
|
|
|
|
dollars, people, viruses = groups = [
|
|
|
|
VGroup(*(mob.copy().move_to(point) for point in points))
|
|
|
|
for mob in templates
|
|
|
|
]
|
|
|
|
|
|
|
|
dollars.set_submobjects(dollars[:20])
|
|
|
|
people.set_submobjects(people[:500])
|
|
|
|
|
|
|
|
start_time = self.time
|
|
|
|
|
|
|
|
def get_n():
|
|
|
|
time = self.time - start_time
|
|
|
|
return int(math.exp(0.75 * time))
|
|
|
|
|
|
|
|
def update_group(group):
|
|
|
|
group.set_opacity(0)
|
|
|
|
group[:get_n()].set_opacity(0.9)
|
|
|
|
|
|
|
|
def update_height(group, alpha):
|
|
|
|
for mob in group:
|
|
|
|
mob.set_height(max(alpha * mob_height, 1e-4))
|
|
|
|
|
|
|
|
for group in groups:
|
|
|
|
group.add_updater(update_group)
|
|
|
|
|
|
|
|
frame = self.camera.frame
|
|
|
|
frame.set_height(2)
|
|
|
|
frame.add_updater(lambda m, dt: m.set_height(m.get_height() * (1 + 0.2 * dt)))
|
|
|
|
self.add(frame)
|
|
|
|
|
|
|
|
self.add(dollars)
|
|
|
|
self.wait(3)
|
|
|
|
self.play(
|
|
|
|
UpdateFromAlphaFunc(people, update_height),
|
|
|
|
UpdateFromAlphaFunc(dollars, update_height, rate_func=lambda t: smooth(1 - t), remover=True),
|
|
|
|
)
|
|
|
|
self.wait(4)
|
|
|
|
self.play(
|
|
|
|
UpdateFromAlphaFunc(viruses, update_height),
|
|
|
|
UpdateFromAlphaFunc(people, update_height, rate_func=lambda t: smooth(1 - t), remover=True),
|
|
|
|
)
|
|
|
|
self.wait(4)
|
|
|
|
|
|
|
|
|
|
|
|
class CovidPlot(ExternallyAnimatedScene):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class Compare1DTo2DEquations(Scene):
|
|
|
|
def construct(self):
|
|
|
|
eq1d = get_1d_equation()
|
|
|
|
eq2d = get_2d_equation()
|
|
|
|
eq1d.match_height(eq2d)
|
|
|
|
|
|
|
|
equations = VGroup(eq1d, eq2d)
|
|
|
|
equations.arrange(DOWN, buff=2.0)
|
|
|
|
equations.to_edge(LEFT, buff=1.0)
|
|
|
|
|
|
|
|
solutions = VGroup(
|
|
|
|
Tex("e^{rt}", tex_to_color_map={"r": BLUE}),
|
|
|
|
get_matrix_exponential(
|
|
|
|
[["a", "b"], ["c", "d"]],
|
|
|
|
h_buff=0.75,
|
|
|
|
v_buff=0.5,
|
|
|
|
bracket_h_buff=0.25,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
solutions[1][1].set_color(TEAL)
|
|
|
|
solutions[0].scale(2)
|
|
|
|
arrows = VGroup()
|
|
|
|
for eq, sol in zip(equations, solutions):
|
|
|
|
sol.next_to(eq[-1], RIGHT, index_of_submobject_to_align=0)
|
|
|
|
sol.set_x(4)
|
|
|
|
arrows.add(Arrow(eq, sol[0], buff=0.5))
|
|
|
|
|
|
|
|
sol0, sol1 = solutions
|
|
|
|
sol0.save_state()
|
|
|
|
sol0.center()
|
|
|
|
self.add(sol0)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
Restore(sol0),
|
|
|
|
TransformFromCopy(Arrow(eq1d, sol0, fill_opacity=0), arrows[0]),
|
|
|
|
FadeIn(eq1d),
|
|
|
|
)
|
|
|
|
self.wait(2)
|
|
|
|
self.play(
|
|
|
|
TransformMatchingShapes(sol0.copy(), sol1, fade_transform_mismatches=True),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
TransformFromCopy(*arrows),
|
|
|
|
LaggedStart(*(
|
|
|
|
FadeTransform(m1.copy(), m2)
|
|
|
|
for m1, m2 in zip(
|
|
|
|
[eq1d[:2], eq1d[2:5], eq1d[5], eq1d[6], eq1d[7:]],
|
|
|
|
eq2d
|
|
|
|
)
|
|
|
|
), lag_ratio=0.05)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class EVideoWrapper(Scene):
|
|
|
|
def construct(self):
|
|
|
|
self.add(FullScreenRectangle())
|
|
|
|
screen_rect = ScreenRectangle(height=6)
|
|
|
|
screen_rect.set_stroke(BLUE_D, 1)
|
|
|
|
screen_rect.set_fill(BLACK, 1)
|
|
|
|
screen_rect.to_edge(DOWN)
|
|
|
|
self.add(screen_rect)
|
|
|
|
|
|
|
|
title = TexText("Video on $e^x$", font_size=90)
|
|
|
|
title.next_to(screen_rect, UP)
|
|
|
|
|
|
|
|
self.play(Write(title))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class ManyExponentialForms(ExternallyAnimatedScene):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class ManySolutionsDependingOnInitialCondition(GraphOfExponential):
|
|
|
|
def construct(self):
|
|
|
|
axes = Axes(
|
|
|
|
x_range=(0, 12),
|
|
|
|
y_range=(0, 10),
|
|
|
|
width=12,
|
|
|
|
height=6,
|
|
|
|
)
|
|
|
|
axes.add(axes.get_x_axis_label("t"))
|
|
|
|
axes.add(axes.get_y_axis_label("x"))
|
|
|
|
axes.x_axis.add_numbers()
|
|
|
|
equation = self.get_equation()
|
|
|
|
|
|
|
|
def get_graph(x0, r=0.25):
|
|
|
|
return axes.get_graph(lambda t: np.exp(r * t) * x0)
|
|
|
|
|
|
|
|
step = 0.05
|
|
|
|
graphs = VGroup(*(
|
|
|
|
get_graph(x0)
|
|
|
|
for x0 in np.arange(step, axes.y_range[1], step)
|
|
|
|
))
|
|
|
|
graphs.set_submobject_colors_by_gradient(BLUE_E, BLUE, TEAL, YELLOW)
|
|
|
|
graphs.set_stroke(width=1)
|
|
|
|
graph = get_graph(1)
|
|
|
|
graph.set_color(BLUE)
|
|
|
|
|
|
|
|
solution = Tex("x({t}) = e^{r{t} } \\cdot x_0", tex_to_color_map={"{t}": GREY_B, "r": BLUE})
|
|
|
|
solution.next_to(equation, DOWN, MED_LARGE_BUFF)
|
|
|
|
solution.shift(0.25 * RIGHT)
|
|
|
|
|
|
|
|
VGroup(solution, equation).set_stroke(BLACK, 5, background=True)
|
|
|
|
|
|
|
|
equation_label = Text("Differential equation")
|
|
|
|
solution_label = Text("Solution")
|
|
|
|
|
|
|
|
self.add(axes)
|
|
|
|
self.add(graphs)
|
|
|
|
self.add(equation)
|
|
|
|
|
|
|
|
self.add(graph)
|
|
|
|
self.add(solution)
|
|
|
|
|
|
|
|
###
|
|
|
|
self.embed()
|
|
|
|
|
|
|
|
|
|
|
|
class SolutionsToMatrixEquation(From2DTo1D):
|
|
|
|
show_solution = True
|
|
|
|
|
|
|
|
|
|
|
|
class OneDimensionalCase(Scene):
|
|
|
|
def construct(self):
|
|
|
|
# Setup 2D equation
|
|
|
|
eq2d = VGroup(
|
|
|
|
Tex("d \\over dt"),
|
|
|
|
Matrix([["x(t)"], ["y(t)"]], h_buff=SMALL_BUFF),
|
|
|
|
Tex("="),
|
|
|
|
Matrix([["a", "b"], ["c", "d"]], h_buff=SMALL_BUFF),
|
|
|
|
Matrix([["x(t)"], ["y(t)"]], h_buff=SMALL_BUFF),
|
|
|
|
)
|
|
|
|
eq2d.arrange(RIGHT, SMALL_BUFF)
|
|
|
|
eq2d.to_edge(UP)
|
|
|
|
|
|
|
|
|
|
|
|
# Setup 1D equation
|
|
|
|
self.embed()
|
|
|
|
|
|
|
|
|
|
|
|
# Older
|
|
|
|
|
|
|
|
|
|
|
|
class SchroedingersEquationIntro(Scene):
|
|
|
|
def construct(self):
|
|
|
|
title = Text("Schrödinger equation", font_size=72)
|
|
|
|
title.to_edge(UP)
|
|
|
|
self.add(title)
|
|
|
|
|
|
|
|
t2c = {
|
|
|
|
"|\\psi \\rangle": BLUE,
|
|
|
|
"{H}": GREY_A,
|
|
|
|
"=": WHITE,
|
|
|
|
"i\\hbar": WHITE,
|
|
|
|
}
|
|
|
|
original_equation = Tex(
|
|
|
|
"i\\hbar \\frac{\\partial}{\\partial t} |\\psi \\rangle = {H} |\\psi \\rangle",
|
|
|
|
tex_to_color_map=t2c
|
|
|
|
)
|
|
|
|
equation = Tex(
|
|
|
|
"\\frac{\\partial}{\\partial t} |\\psi \\rangle = \\frac{1}{i\\hbar} {H} |\\psi \\rangle",
|
|
|
|
tex_to_color_map=t2c
|
|
|
|
)
|
|
|
|
VGroup(original_equation, equation).scale(1.5)
|
|
|
|
|
|
|
|
psis = original_equation.get_parts_by_tex("\\psi")
|
|
|
|
state_label = TexText("State of a system \\\\ as a vector", font_size=36)
|
|
|
|
state_label.next_to(psis, DOWN, buff=1.5)
|
|
|
|
state_arrows = VGroup(*(Arrow(state_label, psi) for psi in psis))
|
|
|
|
state_label.match_color(psis[0])
|
|
|
|
state_arrows.match_color(psis[0])
|
|
|
|
psis.set_color(WHITE)
|
|
|
|
|
|
|
|
randy = Randolph(height=2.0, color=BLUE_C)
|
|
|
|
randy.to_corner(DL)
|
|
|
|
randy.set_opacity(0)
|
|
|
|
|
|
|
|
self.play(Write(original_equation, run_time=3))
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
randy.animate.set_opacity(1).change("horrified", original_equation)
|
|
|
|
)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
self.play(
|
|
|
|
randy.animate.change("pondering", state_label),
|
|
|
|
psis.animate.match_color(state_label),
|
|
|
|
FadeIn(state_label, 0.25 * DOWN),
|
|
|
|
*map(GrowArrow, state_arrows),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FlashUnder(title, time_width=1.5, run_time=2),
|
|
|
|
randy.animate.look_at(title),
|
|
|
|
)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ReplacementTransform(original_equation[1:4], equation[0:3]),
|
|
|
|
Write(equation[3]),
|
|
|
|
ReplacementTransform(original_equation[0], equation[4], path_arc=90 * DEGREES),
|
|
|
|
ReplacementTransform(original_equation[4:], equation[5:]),
|
|
|
|
state_arrows.animate.become(
|
|
|
|
VGroup(*(Arrow(state_label, psi) for psi in equation.get_parts_by_tex("\\psi")))
|
|
|
|
),
|
|
|
|
randy.animate.change("hesitant", equation)
|
|
|
|
)
|
|
|
|
self.play(FlashAround(equation[0], time_width=2, run_time=2))
|
|
|
|
self.play(Blink(randy))
|
|
|
|
|
|
|
|
mat_rect = SurroundingRectangle(equation[3:6], buff=0.05, color=TEAL)
|
|
|
|
mat_label = Text("A certain matrix", font_size=36)
|
|
|
|
mat_label.next_to(mat_rect, UP)
|
|
|
|
mat_label.match_color(mat_rect)
|
|
|
|
self.play(
|
|
|
|
ShowCreation(mat_rect),
|
|
|
|
FadeIn(mat_label, 0.25 * UP),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(randy.animate.change("confused", equation))
|
|
|
|
self.play(Blink(randy))
|
|
|
|
self.wait()
|
|
|
|
self.play(FadeOut(randy))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class SchroedingersComplicatingFactors(TeacherStudentsScene):
|
|
|
|
def construct(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class OldComputationCode(Scene):
|
|
|
|
def construct(self):
|
|
|
|
# Taylor series example
|
|
|
|
ex_rhs = Tex(
|
|
|
|
"""
|
|
|
|
{2}^0 +
|
|
|
|
{2}^1 +
|
|
|
|
{ {2}^2 \\over 2} +
|
|
|
|
{ {2}^3 \\over 6} +
|
|
|
|
{ {2}^4 \\over 24} +
|
|
|
|
{ {2}^5 \\over 120} +
|
|
|
|
{ {2}^6 \\over 720} +
|
|
|
|
{ {2}^7 \\over 5040} +
|
|
|
|
\\cdots
|
|
|
|
""",
|
|
|
|
tex_to_color_map={"{2}": YELLOW, "+": WHITE},
|
|
|
|
)
|
|
|
|
ex_rhs.next_to(real_equation[3:], DOWN, buff=0.75)
|
|
|
|
|
|
|
|
ex_parts = VGroup(*(
|
|
|
|
ex_rhs[i:j] for i, j in [
|
|
|
|
(0, 2),
|
|
|
|
(3, 5),
|
|
|
|
(6, 8),
|
|
|
|
(9, 11),
|
|
|
|
(12, 14),
|
|
|
|
(15, 17),
|
|
|
|
(18, 20),
|
|
|
|
(21, 23),
|
|
|
|
(24, 25),
|
|
|
|
]
|
|
|
|
))
|
|
|
|
term_brace = Brace(ex_parts[0], DOWN)
|
|
|
|
frac = Tex("1", font_size=36)
|
|
|
|
frac.next_to(term_brace, DOWN, SMALL_BUFF)
|
|
|
|
|
|
|
|
rects = VGroup(*(
|
|
|
|
Rectangle(height=2**n / math.factorial(n), width=1)
|
|
|
|
for n in range(11)
|
|
|
|
))
|
|
|
|
rects.arrange(RIGHT, buff=0, aligned_edge=DOWN)
|
|
|
|
rects.set_fill(opacity=1)
|
|
|
|
rects.set_submobject_colors_by_gradient(BLUE, GREEN)
|
|
|
|
rects.set_stroke(WHITE, 1)
|
|
|
|
rects.set_width(7)
|
|
|
|
rects.to_edge(DOWN)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ReplacementTransform(taylor_brace, term_brace),
|
|
|
|
FadeTransform(real_equation[3:].copy(), ex_rhs),
|
|
|
|
FadeOut(false_group, shift=DOWN),
|
|
|
|
FadeOut(taylor_label, shift=DOWN),
|
|
|
|
FadeIn(frac),
|
|
|
|
)
|
|
|
|
term_values = VGroup()
|
2021-03-19 10:54:42 -07:00
|
|
|
for n in range(11):
|
|
|
|
rect = rects[n]
|
|
|
|
fact = math.factorial(n)
|
|
|
|
ex_part = ex_parts[min(n, len(ex_parts) - 1)]
|
|
|
|
value = DecimalNumber(2**n / fact)
|
|
|
|
value.set_color(GREY_A)
|
|
|
|
max_width = 0.6 * rect.get_width()
|
|
|
|
if value.get_width() > max_width:
|
|
|
|
value.set_width(max_width)
|
|
|
|
value.next_to(rects[n], UP, SMALL_BUFF)
|
|
|
|
new_brace = Brace(ex_part, DOWN)
|
|
|
|
if fact == 1:
|
|
|
|
new_frac = Tex(f"{2**n}", font_size=36)
|
|
|
|
else:
|
|
|
|
new_frac = Tex(f"{2**n} / {fact}", font_size=36)
|
|
|
|
new_frac.next_to(new_brace, DOWN, SMALL_BUFF)
|
|
|
|
self.play(
|
|
|
|
term_brace.animate.become(new_brace),
|
|
|
|
FadeTransform(frac, new_frac),
|
|
|
|
)
|
|
|
|
frac = new_frac
|
|
|
|
rect.save_state()
|
|
|
|
rect.stretch(0, 1, about_edge=DOWN)
|
|
|
|
rect.set_opacity(0)
|
|
|
|
value.set_value(0)
|
|
|
|
self.play(
|
|
|
|
Restore(rect),
|
|
|
|
ChangeDecimalToValue(value, 2**n / math.factorial(n)),
|
|
|
|
UpdateFromAlphaFunc(value, lambda m, a: m.next_to(rect, UP, SMALL_BUFF).set_opacity(a)),
|
|
|
|
randy.animate.look_at(rect),
|
|
|
|
morty.animate.look_at(rect),
|
|
|
|
)
|
|
|
|
term_values.add(value)
|
|
|
|
self.play(FadeOut(frac))
|
|
|
|
|
|
|
|
new_brace = Brace(ex_rhs, DOWN)
|
|
|
|
sum_value = DecimalNumber(math.exp(2), num_decimal_places=4, font_size=36)
|
|
|
|
sum_value.next_to(new_brace, DOWN)
|
|
|
|
self.play(
|
|
|
|
term_brace.animate.become(new_brace),
|
|
|
|
randy.animate.change("thinking", sum_value),
|
|
|
|
morty.animate.change("tease", sum_value),
|
|
|
|
*(FadeTransform(dec.copy().set_opacity(0), sum_value) for dec in term_values)
|
|
|
|
)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
|
|
|
|
lhs = Tex("e \\cdot e =")
|
|
|
|
lhs.match_height(real_equation[0])
|
|
|
|
lhs.next_to(ex_rhs, LEFT)
|
|
|
|
self.play(Write(lhs))
|
|
|
|
self.play(Blink(morty))
|
|
|
|
self.play(Blink(randy))
|
|
|
|
|
|
|
|
# Increment input
|
|
|
|
twos = ex_rhs.get_parts_by_tex("{2}")
|
|
|
|
threes = VGroup(*(
|
|
|
|
Tex("3").set_color(YELLOW).replace(two)
|
|
|
|
for two in twos
|
|
|
|
))
|
|
|
|
new_lhs = Tex("e \\cdot e \\cdot e = ")
|
|
|
|
new_lhs.match_height(lhs)
|
|
|
|
new_lhs[0].space_out_submobjects(0.8)
|
|
|
|
new_lhs[0][-1].shift(SMALL_BUFF * RIGHT)
|
|
|
|
new_lhs.move_to(lhs, RIGHT)
|
|
|
|
|
|
|
|
anims = []
|
|
|
|
unit_height = 0.7 * rects[0].get_height()
|
|
|
|
for n, rect, value_mob in zip(it.count(0), rects, term_values):
|
|
|
|
rect.generate_target()
|
|
|
|
new_value = 3**n / math.factorial(n)
|
|
|
|
rect.target.set_height(unit_height * new_value, stretch=True, about_edge=DOWN)
|
|
|
|
value_mob.rect = rect
|
|
|
|
anims += [
|
|
|
|
MoveToTarget(rect),
|
|
|
|
ChangeDecimalToValue(value_mob, new_value),
|
|
|
|
UpdateFromFunc(value_mob, lambda m: m.next_to(m.rect, UP, SMALL_BUFF))
|
|
|
|
]
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(twos, 0.5 * UP),
|
|
|
|
FadeIn(threes, 0.5 * UP),
|
|
|
|
)
|
|
|
|
twos.set_opacity(0)
|
|
|
|
self.play(
|
|
|
|
ChangeDecimalToValue(sum_value, math.exp(3)),
|
|
|
|
*anims,
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FadeOut(lhs, 0.5 * UP),
|
|
|
|
FadeIn(new_lhs, 0.5 * UP),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class PreviewVisualizationWrapper(Scene):
|
|
|
|
def construct(self):
|
|
|
|
background = FullScreenFadeRectangle(fill_color=GREY_E, fill_opacity=1)
|
|
|
|
self.add(background)
|
|
|
|
|
|
|
|
screen = ScreenRectangle(height=6)
|
|
|
|
screen.set_fill(BLACK, 1)
|
|
|
|
screen.set_stroke(GREY_A, 3)
|
|
|
|
screen.to_edge(DOWN)
|
|
|
|
self.add(screen)
|
|
|
|
|
|
|
|
titles = VGroup(
|
|
|
|
Text("How to think about matrix exponentiation"),
|
|
|
|
Text(
|
|
|
|
"How to visualize matrix exponentiation",
|
|
|
|
t2s={"visualize": ITALIC},
|
|
|
|
),
|
|
|
|
Text("What problems matrix exponentiation solves?"),
|
|
|
|
)
|
|
|
|
for title in titles:
|
|
|
|
title.next_to(screen, UP)
|
|
|
|
title.get_parts_by_text("matrix exponentiation").set_color(TEAL)
|
|
|
|
|
|
|
|
self.play(FadeIn(titles[0], 0.5 * UP))
|
|
|
|
self.wait(2)
|
|
|
|
self.play(*(
|
|
|
|
FadeTransform(
|
|
|
|
titles[0].get_parts_by_text(w1),
|
|
|
|
titles[1].get_parts_by_text(w2),
|
|
|
|
)
|
|
|
|
for w1, w2 in [
|
|
|
|
("How to", "How to"),
|
|
|
|
("think about", "visualize"),
|
|
|
|
("matrix exponentiation", "matrix exponentiation"),
|
2021-03-18 17:27:51 -07:00
|
|
|
]
|
|
|
|
))
|
|
|
|
self.wait(2)
|
|
|
|
self.play(
|
|
|
|
*(
|
|
|
|
FadeTransform(
|
|
|
|
titles[1].get_parts_by_text(w1),
|
|
|
|
titles[2].get_parts_by_text(w2),
|
|
|
|
)
|
|
|
|
for w1, w2 in [
|
|
|
|
("How to visualize", "What problems"),
|
|
|
|
("matrix exponentiation", "matrix exponentiation"),
|
|
|
|
]
|
|
|
|
),
|
|
|
|
FadeIn(titles[2].get_parts_by_text("solves?"))
|
|
|
|
)
|
|
|
|
self.wait(2)
|
|
|
|
|