mirror of
https://github.com/3b1b/videos.git
synced 2025-08-05 16:48:47 +00:00
6270 lines
No EOL
193 KiB
Python
6270 lines
No EOL
193 KiB
Python
from manim_imports_ext import *
|
|
from _2020.chess import string_to_bools
|
|
|
|
|
|
def get_background(color=GREY_E):
|
|
background = FullScreenRectangle()
|
|
background.set_fill(color, 1)
|
|
background.set_stroke(width=0)
|
|
return background
|
|
|
|
|
|
def get_bit_grid(n_rows, n_cols, bits=None, buff=MED_SMALL_BUFF, height=4):
|
|
bit_pair = VGroup(Integer(0), Integer(1))
|
|
bit_mobs = VGroup(*[
|
|
bit_pair.copy()
|
|
for x in range(n_rows * n_cols)
|
|
])
|
|
bit_mobs.arrange_in_grid(n_rows, n_cols, buff=buff)
|
|
bit_mobs.set_height(height)
|
|
if bits is None:
|
|
bits = np.random.randint(0, 2, len(bit_mobs))
|
|
|
|
for bit_mob, bit in zip(bit_mobs, bits):
|
|
bit_mob[1 - bit].set_opacity(0)
|
|
|
|
bit_mobs.n_rows = n_rows
|
|
bit_mobs.n_cols = n_cols
|
|
return bit_mobs
|
|
|
|
|
|
def get_bit_mob_value(bit_mob):
|
|
return int(bit_mob[1].get_fill_opacity() > bit_mob[0].get_fill_opacity())
|
|
|
|
|
|
def bit_grid_to_bits(bit_grid):
|
|
return list(map(get_bit_mob_value, bit_grid))
|
|
|
|
|
|
def toggle_bit(bit):
|
|
for sm in bit:
|
|
sm.set_fill(opacity=1 - sm.get_fill_opacity())
|
|
return bit
|
|
|
|
|
|
def hamming_syndrome(bits):
|
|
return reduce(
|
|
lambda i1, i2: i1 ^ i2,
|
|
[i for i, b in enumerate(bits) if b],
|
|
0,
|
|
)
|
|
|
|
|
|
def string_to_bits(message):
|
|
return [int(b) for b in string_to_bools(message)]
|
|
|
|
|
|
def int_to_bit_string(number, n_bits=None):
|
|
result = "{:b}".format(number)
|
|
if n_bits is not None:
|
|
result = (n_bits - len(result)) * "0" + result
|
|
return result
|
|
|
|
|
|
def get_image_bits(image, bit_height=0.15, buff=MED_SMALL_BUFF):
|
|
bit = Integer(0)
|
|
small_buff = (buff / bit.get_height()) * bit_height
|
|
bit.set_height(bit_height)
|
|
bits = get_bit_grid(
|
|
n_rows=int(image.get_height() / (bit.get_height() + small_buff)),
|
|
n_cols=int(image.get_width() / (bit.get_width() + small_buff)),
|
|
buff=buff
|
|
)
|
|
bits.replace(image)
|
|
return bits
|
|
|
|
|
|
def get_sound_wave():
|
|
sound = VGroup(*[
|
|
Line(DOWN, UP).set_height(
|
|
(0.3 + 0.8 * random.random()) * abs(np.sin(x))
|
|
)
|
|
for x in np.linspace(0, 3 * PI, 100)
|
|
])
|
|
sound.arrange(RIGHT, buff=0.05)
|
|
return sound
|
|
|
|
|
|
def get_sender_and_receiver(height=2):
|
|
sender = Randolph(height=height)
|
|
receiver = Mortimer(height=height)
|
|
sender.name = TextMobject("Sender")
|
|
receiver.name = TextMobject("Receiver")
|
|
|
|
pis = VGroup(sender, receiver)
|
|
names = VGroup(sender.name, receiver.name)
|
|
|
|
sender.to_corner(DL)
|
|
receiver.to_corner(DR)
|
|
pis.shift(UP)
|
|
for name, pi in zip(names, pis):
|
|
name.next_to(pi, DOWN)
|
|
|
|
return pis, names
|
|
|
|
|
|
def get_ones(block):
|
|
result = VGroup()
|
|
for bit in block:
|
|
if isinstance(bit, Integer):
|
|
value = bit.get_value()
|
|
else:
|
|
value = get_bit_mob_value(bit)
|
|
if value == 1:
|
|
result.add(bit)
|
|
return result
|
|
|
|
|
|
def get_one_rects(block, buff=SMALL_BUFF):
|
|
rects = VGroup()
|
|
for bit in get_ones(block):
|
|
rect = SurroundingRectangle(bit, buff=buff)
|
|
rect.set_stroke(YELLOW, 3)
|
|
rect.set_fill(YELLOW, 0.2)
|
|
rects.add(rect)
|
|
return rects
|
|
|
|
|
|
def get_ones_counter(label, one_rects, buff=MED_LARGE_BUFF):
|
|
counter = Integer()
|
|
counter.match_color(one_rects[0])
|
|
counter.match_height(label[1])
|
|
counter.next_to(label[1], RIGHT, buff=buff, aligned_edge=DOWN)
|
|
f_always(counter.set_value, lambda: len(one_rects))
|
|
return counter
|
|
|
|
|
|
def get_bit_grid_boxes(bit_grid, color=GREY_B, stroke_width=2):
|
|
width = get_norm(bit_grid[1].get_center() - bit_grid[0].get_center())
|
|
height = get_norm(bit_grid[bit_grid.n_cols].get_center() - bit_grid[0].get_center())
|
|
return VGroup(*[
|
|
Rectangle(
|
|
height=height,
|
|
width=width,
|
|
stroke_color=color,
|
|
stroke_width=stroke_width,
|
|
).move_to(bit)
|
|
for bit in bit_grid
|
|
])
|
|
|
|
|
|
def get_grid_position_labels(boxes, height=0.25):
|
|
labels = VGroup()
|
|
for n, box in enumerate(boxes):
|
|
label = Integer(n)
|
|
label.set_height(height)
|
|
label.move_to(box, DR)
|
|
label.shift(SMALL_BUFF * UL)
|
|
labels.add(label)
|
|
return labels
|
|
|
|
|
|
def get_bit_n_sublist(input_list, n, bit_value=1):
|
|
return [
|
|
elem
|
|
for i, elem in enumerate(input_list)
|
|
if bool(i & (1 << n)) ^ bool(1 - bit_value)
|
|
]
|
|
|
|
|
|
def get_bit_n_subgroup(mob, n, bit_value=1):
|
|
"""
|
|
If we enumerate mob, this returns a subgroup of all elements
|
|
whose index has a binary representation with the n'th bit
|
|
equal to bit_value
|
|
"""
|
|
return VGroup(*get_bit_n_sublist(mob, n, bit_value))
|
|
|
|
|
|
# Special animations
|
|
def image_reveal_animation(image, bit_height=0.1):
|
|
box = SurroundingRectangle(image)
|
|
box.set_fill(BLACK, 1)
|
|
box.set_stroke(width=0)
|
|
bits = get_image_bits(image, bit_height=bit_height)
|
|
|
|
return AnimationGroup(
|
|
Animation(image),
|
|
ApplyMethod(
|
|
box.stretch, 0, 1, {"about_edge": DOWN}, remover=True,
|
|
rate_func=linear,
|
|
),
|
|
LaggedStartMap(
|
|
VFadeInThenOut, bits,
|
|
run_time=1,
|
|
lag_ratio=3 / len(bits)
|
|
)
|
|
)
|
|
|
|
|
|
def toggle_bit_anim(bit, target_color=None, **kwargs):
|
|
original = bit.copy()
|
|
original[1 - get_bit_mob_value(original)].rotate(PI)
|
|
toggle_bit(bit)
|
|
if target_color is not None:
|
|
bit.set_color(target_color)
|
|
|
|
if "path_arc" not in kwargs:
|
|
kwargs["path_arc"] = PI
|
|
|
|
return TransformFromCopy(original, bit, **kwargs)
|
|
|
|
|
|
def zap_anim(bit, bolt_height=0.75):
|
|
bolt = SVGMobject("lightning_bolt")
|
|
bolt[0].add_line_to(bolt[0].get_start())
|
|
bolt.set_fill(RED_B, 1)
|
|
bolt.set_stroke(width=0)
|
|
bolt.set_height(bolt_height)
|
|
|
|
bolt.move_to(bit.get_center(), DL)
|
|
|
|
outline = bolt.deepcopy()
|
|
outline.set_stroke(RED_D, 2)
|
|
outline.set_fill(opacity=0)
|
|
|
|
return AnimationGroup(
|
|
Succession(
|
|
GrowFromPoint(bolt, bolt.get_corner(UR), rate_func=rush_into),
|
|
FadeOut(bolt, run_time=0.5),
|
|
),
|
|
Succession(
|
|
ShowCreation(outline),
|
|
FadeOut(outline),
|
|
),
|
|
run_time=1,
|
|
)
|
|
|
|
|
|
def scan_anim(point, bits, final_stroke_width=0, run_time=3, lag_factor=3, show_robot=True, robot_height=0.5):
|
|
lines = VGroup(*[
|
|
Line(
|
|
point, bit.get_center(),
|
|
stroke_color=[GREEN, BLUE][get_bit_mob_value(bit)],
|
|
stroke_width=1,
|
|
)
|
|
for bit in bits
|
|
])
|
|
anims = [
|
|
LaggedStartMap(
|
|
lambda m, **kw: Succession(
|
|
ShowCreation(m),
|
|
ApplyMethod(m.set_stroke, None, final_stroke_width),
|
|
**kw
|
|
),
|
|
lines,
|
|
lag_ratio=lag_factor / len(bits),
|
|
run_time=run_time,
|
|
remover=bool(final_stroke_width)
|
|
)
|
|
]
|
|
|
|
if show_robot:
|
|
robot = SVGMobject("robot")
|
|
robot.set_stroke(width=0)
|
|
robot.set_color(GREY)
|
|
robot.set_gloss(1)
|
|
robot.set_height(robot_height)
|
|
robot.move_to(point, UP)
|
|
anims.append(FadeIn(robot))
|
|
|
|
return AnimationGroup(*anims)
|
|
|
|
|
|
def focus_scan_anim_lines(scanim, point, final_stroke_width=1):
|
|
lines = scanim.mobject[0]
|
|
for line in lines:
|
|
line.generate_target()
|
|
line.target.put_start_and_end_on(line.get_start(), point)
|
|
line.target.set_stroke(width=final_stroke_width)
|
|
|
|
return AnimationGroup(*[
|
|
MoveToTarget(line)
|
|
for line in lines
|
|
])
|
|
|
|
|
|
def get_xor(height=0.35, color=BLUE_B, stroke_width=4):
|
|
xor = VGroup(
|
|
Line(UP, DOWN),
|
|
Line(LEFT, RIGHT),
|
|
Circle(),
|
|
)
|
|
xor.set_stroke(color, stroke_width)
|
|
xor.set_height(height)
|
|
return xor
|
|
|
|
|
|
# Scenes
|
|
|
|
|
|
class Thumbnail(Scene):
|
|
def construct(self):
|
|
phrases = VGroup(
|
|
TextMobject("One Bit is Wrong"),
|
|
TextMobject("(according to an extended Hamming Code)"),
|
|
TextMobject("Can you tell which?"),
|
|
)
|
|
for phrase in phrases:
|
|
phrase.set_width(12)
|
|
|
|
phrases[0].to_edge(UP)
|
|
|
|
phrases[1].set_width(10)
|
|
phrases[1].set_color(GREY_C)
|
|
phrases[1].next_to(phrases[0], DOWN, SMALL_BUFF)
|
|
|
|
phrases[2].to_edge(DOWN, buff=MED_SMALL_BUFF)
|
|
|
|
phrases[0].set_color(BLUE_B)
|
|
phrases[2].set_color(BLUE_C)
|
|
|
|
bit_values = [
|
|
1, 1, 0, 1, 1, 0, 0, 1,
|
|
0, 1, 1, 0, 1, 1, 1, 1,
|
|
]
|
|
bits = get_bit_grid(2, 8, bits=bit_values, buff=MED_LARGE_BUFF)
|
|
boxes = get_bit_grid_boxes(bits)
|
|
pos_labels = get_grid_position_labels(boxes)
|
|
|
|
pos_labels.set_color(GREY_B)
|
|
for bit, box in zip(bits, boxes):
|
|
bit.set_height(0.7 * box.get_height())
|
|
|
|
group = VGroup(boxes, bits, pos_labels)
|
|
group.set_width(10)
|
|
group.next_to(phrases[1], DOWN)
|
|
|
|
self.add(phrases)
|
|
self.add(group)
|
|
|
|
|
|
class DiskOfBits(Scene):
|
|
def construct(self):
|
|
# Setup disc
|
|
bits = get_bit_grid(2**5, 2**6, height=6)
|
|
|
|
inner_r = 1
|
|
outer_r = 3
|
|
annulus = Annulus(
|
|
inner_radius=inner_r * 0.93,
|
|
outer_radius=outer_r * 1.02,
|
|
fill_color=GREY_D,
|
|
)
|
|
annulus.set_gloss(0.5)
|
|
|
|
for bit in bits:
|
|
point = bit.get_center()
|
|
norm = get_norm(point)
|
|
to_inner = inner_r - norm
|
|
to_outer = norm - outer_r
|
|
sdf = 10 * max(to_inner, to_outer)
|
|
if 0 < sdf < 0.5:
|
|
bit.scale(1 - sdf)
|
|
elif sdf > 0:
|
|
bits.remove(bit)
|
|
|
|
disc = VGroup(annulus, bits)
|
|
|
|
# Setup scratch
|
|
scratch_line = Line(disc.get_top(), disc.get_right())
|
|
scratch_line.set_stroke(RED, width=2, opacity=0.75)
|
|
|
|
flipped_bits = VGroup()
|
|
for bit in bits:
|
|
point = bit.get_center()
|
|
norm = abs(point[0] + point[1] - 3)
|
|
alpha = 1 - clip(norm / 0.2, 0, 1)
|
|
if alpha > 0.25:
|
|
bit.generate_target()
|
|
bit.target.set_stroke(width=1)
|
|
bit.target.set_color(RED)
|
|
flipped_bits.add(bit)
|
|
|
|
# Add disc
|
|
light = self.camera.light_source
|
|
light.move_to([0, -10, 10])
|
|
|
|
random_bits = bits.copy()
|
|
random_bits.sort(lambda p: -get_norm(p))
|
|
self.add(annulus)
|
|
self.play(
|
|
LaggedStartMap(FadeIn, random_bits, lag_ratio=10 / len(bits)),
|
|
light.move_to, [-10, 10, 10],
|
|
run_time=1,
|
|
)
|
|
self.clear()
|
|
self.add(disc)
|
|
|
|
# Show scratch
|
|
self.play(
|
|
LaggedStartMap(MoveToTarget, flipped_bits),
|
|
ShowCreationThenDestruction(scratch_line),
|
|
run_time=2,
|
|
)
|
|
|
|
# Flip 'em'
|
|
self.play(LaggedStartMap(toggle_bit_anim, flipped_bits))
|
|
self.wait()
|
|
|
|
# Show image in and image out
|
|
disc.generate_target()
|
|
disc.target.scale(0.5)
|
|
|
|
in_image = ImageMobject("Mona_Lisa")
|
|
in_image.to_edge(LEFT)
|
|
in_image.shift(UP)
|
|
in_words = TextMobject("What was\\\\encoded")
|
|
in_words.next_to(in_image, DOWN)
|
|
out_image = in_image.copy()
|
|
out_image.to_edge(RIGHT)
|
|
out_words = TextMobject("What is\\\\decoded")
|
|
out_words.next_to(out_image, DOWN)
|
|
|
|
in_arrow = Arrow(
|
|
in_image.get_right(),
|
|
disc.target.get_left() + 0.2 * UP,
|
|
buff=MED_SMALL_BUFF,
|
|
)
|
|
out_arrow = Arrow(
|
|
disc.target.get_right() + 0.2 * UP,
|
|
out_image.get_left(),
|
|
buff=MED_SMALL_BUFF,
|
|
)
|
|
for arrow in in_arrow, out_arrow:
|
|
arrow.set_fill(GREY_B)
|
|
|
|
self.play(
|
|
MoveToTarget(disc),
|
|
GrowArrow(out_arrow),
|
|
)
|
|
|
|
in_bits = get_bit_grid(40, 33)
|
|
in_bits.replace(in_image, stretch=True)
|
|
in_bits.set_fill(GREY_B)
|
|
out_bits = in_bits.copy()
|
|
out_bits.move_to(out_image)
|
|
|
|
out_image_blocker = SurroundingRectangle(out_image)
|
|
out_image_blocker.set_fill(BLACK, 1)
|
|
out_image_blocker.set_stroke(width=0)
|
|
|
|
self.add(out_image, out_image_blocker, out_bits, out_words)
|
|
self.play(
|
|
LaggedStart(
|
|
*[
|
|
Succession(
|
|
GrowFromPoint(
|
|
out_bit,
|
|
random.choice(bits).get_center(),
|
|
),
|
|
FadeOut(out_bit),
|
|
)
|
|
for out_bit in out_bits
|
|
],
|
|
lag_ratio=3 / len(out_bits),
|
|
run_time=12,
|
|
),
|
|
ApplyMethod(
|
|
out_image_blocker.stretch, 0, 1, {"about_edge": DOWN},
|
|
run_time=12,
|
|
rate_func=bezier([0, 0, 1, 1]),
|
|
),
|
|
FadeIn(out_words, UP),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
TransformFromCopy(out_image, in_image),
|
|
ReplacementTransform(
|
|
in_words.copy().replace(out_words).set_opacity(0),
|
|
in_words,
|
|
),
|
|
)
|
|
self.play(
|
|
LaggedStart(
|
|
*[
|
|
Succession(
|
|
FadeIn(in_bit),
|
|
Transform(in_bit, random.choice(bits)),
|
|
FadeOut(in_bit),
|
|
)
|
|
for in_bit in in_bits
|
|
],
|
|
lag_ratio=3 / len(in_bits),
|
|
run_time=12,
|
|
),
|
|
GrowArrow(in_arrow),
|
|
)
|
|
self.wait()
|
|
|
|
# Pi Creature
|
|
randy = Randolph()
|
|
randy.set_height(1.5)
|
|
randy.to_edge(DOWN)
|
|
randy.shift(2 * LEFT)
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(randy.change, "maybe")
|
|
self.play(Blink(randy))
|
|
self.play(randy.change, "confused")
|
|
self.wait(2)
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
|
|
|
|
class TripleRedundancy(Scene):
|
|
def construct(self):
|
|
# Show different file types
|
|
image = ImageMobject("Claude_Shannon")
|
|
image.set_height(6)
|
|
image.to_edge(DOWN)
|
|
video = ImageMobject("ZoeyInGrass")
|
|
video.set_opacity(0)
|
|
sound = get_sound_wave()
|
|
text = TextMobject(
|
|
"""
|
|
Fourscore and seven years ago\\\\
|
|
our fathers brought forth, on this\\\\
|
|
continent, a new nation, conceived\\\\
|
|
in liberty, and dedicated to the\\\\
|
|
proposition that all men are created\\\\
|
|
equal. Now we are engaged$\\dots$
|
|
""",
|
|
alignment=""
|
|
)
|
|
code = ImageMobject("Hamming_Code_Snippet")
|
|
files = Group(video, sound, text, code, image)
|
|
for file in files:
|
|
file.match_width(image)
|
|
file.move_to(image, UP)
|
|
|
|
brace = Brace(image, UP)
|
|
file_word = TextMobject("Original File")
|
|
file_word.set_height(0.5)
|
|
file_word.next_to(brace, UP)
|
|
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
FadeIn(file_word),
|
|
image_reveal_animation(video),
|
|
)
|
|
video.set_opacity(0)
|
|
self.wait(2)
|
|
for f1, f2 in zip(files, files[1:]):
|
|
self.play(FadeOut(f1), FadeIn(f2))
|
|
self.wait()
|
|
|
|
# Show bits
|
|
bits = get_image_bits(image)
|
|
for bit, value in zip(bits, string_to_bits(";)Hi")):
|
|
bit[1 - value].set_opacity(0)
|
|
bit[value].set_opacity(1)
|
|
bits.generate_target()
|
|
bits.target.arrange(RIGHT)
|
|
bits.target.set_height(0.5)
|
|
bits.target.arrange(RIGHT, buff=SMALL_BUFF)
|
|
bits.target.to_edge(LEFT)
|
|
bits.target.shift(UP)
|
|
last_shown_bit_index = 31
|
|
dots = TextMobject("\\dots")
|
|
dots.next_to(bits.target[last_shown_bit_index], RIGHT, aligned_edge=DOWN, buff=SMALL_BUFF)
|
|
new_brace = Brace(VGroup(bits.target[0], dots), UP)
|
|
file_rect = SurroundingRectangle(VGroup(bits.target[0], dots))
|
|
file_rect.set_stroke(BLUE, 4)
|
|
|
|
self.play(
|
|
FadeOut(image),
|
|
FadeIn(bits, lag_ratio=1 / len(bits))
|
|
)
|
|
self.play(
|
|
MoveToTarget(bits),
|
|
Transform(brace, new_brace),
|
|
file_word.next_to, new_brace, UP, SMALL_BUFF,
|
|
run_time=3,
|
|
)
|
|
self.play(
|
|
FadeOut(bits[last_shown_bit_index + 1:last_shown_bit_index + 5]),
|
|
FadeIn(dots),
|
|
FadeOut(brace),
|
|
FadeIn(file_rect),
|
|
file_word.next_to, file_rect, UP,
|
|
)
|
|
self.remove(*bits[last_shown_bit_index + 1:])
|
|
bits.remove(*bits[last_shown_bit_index + 1:])
|
|
self.add(bits)
|
|
self.wait()
|
|
|
|
# Show redundant copies
|
|
bits.add(dots)
|
|
copies = VGroup(bits.copy(), bits.copy())
|
|
copies.arrange(DOWN, buff=MED_SMALL_BUFF)
|
|
copies.next_to(bits, DOWN, buff=MED_SMALL_BUFF)
|
|
copies.set_color(BLUE)
|
|
copies_rect = SurroundingRectangle(copies, buff=SMALL_BUFF)
|
|
copies_rect.set_stroke(BLUE_E, 4)
|
|
copies_word = TextMobject("Redundant copies")
|
|
copies_word.scale(file_word[0][0].get_height() / copies_word[0][0].get_height())
|
|
copies_word.match_color(copies)
|
|
copies_word.next_to(copies_rect, DOWN)
|
|
|
|
self.play(LaggedStart(*[
|
|
TransformFromCopy(bits, bits_copy)
|
|
for bits_copy in copies
|
|
], lag_ratio=0.5))
|
|
self.play(
|
|
ShowCreation(copies_rect),
|
|
FadeIn(copies_word, UP),
|
|
copies.set_color, WHITE,
|
|
)
|
|
self.wait()
|
|
|
|
# Show a correction
|
|
flipper_index = 7
|
|
flipper = bits[flipper_index]
|
|
self.play(
|
|
zap_anim(flipper),
|
|
toggle_bit_anim(flipper),
|
|
)
|
|
self.play()
|
|
self.wait()
|
|
|
|
bit_groups = [bits, *copies]
|
|
scan_rect = SurroundingRectangle(
|
|
VGroup(*[group[0] for group in bit_groups])
|
|
)
|
|
scan_rect.set_stroke(GREEN, 5)
|
|
|
|
self.add(scan_rect)
|
|
for i in range(flipper_index + 1):
|
|
self.play(
|
|
scan_rect.match_x, bits[i],
|
|
run_time=0.25
|
|
)
|
|
self.wait(0.25)
|
|
|
|
bangs = TextMobject("!!!")[0]
|
|
bangs.scale(1.5)
|
|
bangs.set_color(RED)
|
|
bangs.next_to(scan_rect, UP)
|
|
|
|
self.play(
|
|
LaggedStartMap(
|
|
FadeIn, bangs,
|
|
lambda m: (m, DOWN),
|
|
run_time=1,
|
|
),
|
|
scan_rect.set_color, RED
|
|
)
|
|
self.wait()
|
|
|
|
flipper_rect = SurroundingRectangle(flipper, buff=SMALL_BUFF)
|
|
flipper_rect.set_stroke(GREEN, 4)
|
|
flipper_rect.set_fill(GREEN, 0.5)
|
|
bangs.unlock_triangulation()
|
|
self.play(
|
|
ReplacementTransform(bangs, flipper_rect)
|
|
)
|
|
self.play(
|
|
toggle_bit_anim(flipper),
|
|
FadeOut(flipper_rect),
|
|
scan_rect.set_color, GREEN,
|
|
)
|
|
|
|
for i in range(flipper_index + 1, flipper_index + 5):
|
|
self.play(
|
|
scan_rect.match_x, bits[i],
|
|
run_time=0.25
|
|
)
|
|
self.wait(0.25)
|
|
self.play(FadeOut(scan_rect, 0.2 * RIGHT, run_time=0.5))
|
|
|
|
# Show 2/3 of transmission block
|
|
rects = VGroup(file_rect, copies_rect)
|
|
rects.generate_target()
|
|
for rect, width in zip(rects.target, [1, 2]):
|
|
rect.set_width(width, stretch=True)
|
|
rect.set_height(0.75, stretch=True)
|
|
rect.set_fill(rect.get_stroke_color(), opacity=1)
|
|
rects.target[0].set_color(BLUE)
|
|
rects.target[1].set_color(BLUE_E)
|
|
rects.target.set_stroke(width=0)
|
|
rects.target.arrange(RIGHT, buff=0)
|
|
rects.target.set_width(12, stretch=True)
|
|
rects.target.move_to(UP)
|
|
|
|
braces = VGroup(*[Brace(rect, UP, buff=SMALL_BUFF) for rect in rects.target])
|
|
|
|
frac_labels = VGroup(*[
|
|
DecimalNumber(100 * frac, unit="\\%", num_decimal_places=1)
|
|
for frac in [1 / 3, 2 / 3]
|
|
])
|
|
for rect, label in zip(rects.target, frac_labels):
|
|
label.move_to(rect)
|
|
|
|
copies_word.unlock_triangulation()
|
|
redundancy_word = TextMobject("Redundancy")
|
|
redundancy_word.match_height(copies_word)
|
|
redundancy_word.match_color(copies_word)
|
|
redundancy_word.next_to(braces[1], UP, SMALL_BUFF)
|
|
|
|
self.play(
|
|
bits.replace, rects.target[0], {"stretch": True},
|
|
bits.set_opacity, 0,
|
|
copies.replace, rects.target[1], {"stretch": True},
|
|
copies.set_opacity, 0,
|
|
MoveToTarget(rects),
|
|
LaggedStartMap(GrowFromCenter, braces),
|
|
file_word.next_to, braces[0], UP, SMALL_BUFF,
|
|
ReplacementTransform(copies_word, redundancy_word),
|
|
run_time=2,
|
|
)
|
|
self.play(Write(frac_labels))
|
|
self.wait()
|
|
|
|
# Show failure for some two-bit errors
|
|
bits = get_bit_grid(3, 1, [0, 0, 0], height=3, buff=SMALL_BUFF)
|
|
bits.next_to(rects, DOWN, buff=MED_LARGE_BUFF)
|
|
|
|
self.play(FadeIn(bits))
|
|
self.play(
|
|
LaggedStart(zap_anim(bits[0]), zap_anim(bits[2]), lag_ratio=0.5),
|
|
LaggedStart(toggle_bit_anim(bits[0]), toggle_bit_anim(bits[2]), lag_ratio=0.5),
|
|
run_time=1.5,
|
|
)
|
|
self.wait()
|
|
self.play(FadeOut(bits))
|
|
|
|
# Shrink bars
|
|
rects.generate_target()
|
|
p = 9 / 256
|
|
rects.target[0].set_width(1 - p, stretch=True)
|
|
rects.target[1].set_width(p, stretch=True)
|
|
rects.target.arrange(RIGHT, buff=0)
|
|
rects.target.match_width(rects, stretch=True)
|
|
rects.target.move_to(rects)
|
|
|
|
braces.generate_target()
|
|
for rect, brace in zip(rects.target, braces.target):
|
|
brace.become(Brace(rect, UP, buff=SMALL_BUFF, min_num_quads=1))
|
|
|
|
f_always(frac_labels[0].move_to, rects[0].get_center)
|
|
f_always(frac_labels[1].move_to, rects[1].get_center)
|
|
rl_width = frac_labels[1].get_width()
|
|
f_always(frac_labels[1].set_width, lambda: min(0.95 * rects[1].get_width(), rl_width))
|
|
|
|
self.play(
|
|
MoveToTarget(rects),
|
|
MoveToTarget(braces),
|
|
ChangeDecimalToValue(frac_labels[0], 100 * (1 - p)),
|
|
ChangeDecimalToValue(frac_labels[1], 100 * p),
|
|
file_word.next_to, braces.target[0], UP, SMALL_BUFF,
|
|
redundancy_word.next_to, braces.target[1], UP, SMALL_BUFF,
|
|
redundancy_word.to_edge, RIGHT,
|
|
run_time=3,
|
|
)
|
|
self.wait()
|
|
|
|
ratio_group = VGroup(rects, braces, frac_labels, file_word, redundancy_word)
|
|
ratio_group.clear_updaters()
|
|
|
|
# Show space division for a (256, 247) Hamming code
|
|
bits = get_bit_grid(
|
|
16, 16,
|
|
bits=string_to_bits("There are 10 kinds of people...."),
|
|
buff=0.2, height=5
|
|
)
|
|
bits.to_edge(DOWN, buff=MED_SMALL_BUFF)
|
|
bits.shift(2 * LEFT)
|
|
ecc_bits = bits[-9:]
|
|
ecc_rect = SurroundingRectangle(ecc_bits, buff=0.05)
|
|
ecc_rect.set_stroke(BLUE_E, 2)
|
|
ecc_rect.set_fill(BLUE_E, 0.5)
|
|
|
|
block_label = TextMobject("256 bit block")
|
|
ecc_label = TextMobject("9 redundancy bits")
|
|
message_label = TextMobject("247 message bits")
|
|
|
|
block_label.next_to(bits, RIGHT, LARGE_BUFF)
|
|
block_label.shift(UP)
|
|
ecc_label.next_to(ecc_bits, RIGHT, LARGE_BUFF)
|
|
ecc_label.to_edge(DOWN, buff=MED_SMALL_BUFF)
|
|
ecc_label.set_color(BLUE)
|
|
message_label.move_to(VGroup(block_label, ecc_label))
|
|
message_label.align_to(block_label, LEFT)
|
|
message_label.set_color(YELLOW)
|
|
|
|
self.play(
|
|
ratio_group.to_edge, UP,
|
|
LaggedStartMap(FadeIn, bits, lag_ratio=1 / len(bits)),
|
|
Write(block_label),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
ReplacementTransform(
|
|
SurroundingRectangle(bits, color=BLUE, stroke_opacity=0),
|
|
ecc_rect
|
|
),
|
|
Write(ecc_label)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
LaggedStartMap(
|
|
VFadeInThenOut,
|
|
bits[:-9].copy().set_fill(color=YELLOW),
|
|
lag_ratio=0.1 / len(bits),
|
|
run_time=3,
|
|
),
|
|
Write(message_label),
|
|
)
|
|
self.wait()
|
|
|
|
# Show correction
|
|
flipper = random.choice(bits)
|
|
|
|
self.play(
|
|
zap_anim(flipper),
|
|
toggle_bit_anim(flipper),
|
|
)
|
|
self.play(flipper.set_color, RED)
|
|
|
|
point = bits.get_corner(DL) + 1.5 * LEFT + UP
|
|
scanim = scan_anim(point, bits, final_stroke_width=0.2)
|
|
lines, robot = scanim.mobject
|
|
|
|
self.play(scanim)
|
|
self.play(*[
|
|
Succession(
|
|
ApplyMethod(line.put_start_and_end_on, point, flipper.get_center()),
|
|
ApplyMethod(line.set_stroke, None, 1)
|
|
)
|
|
for line in lines
|
|
])
|
|
self.play(
|
|
toggle_bit_anim(flipper),
|
|
FadeOut(lines),
|
|
FadeOut(robot),
|
|
)
|
|
self.play(flipper.set_color, WHITE)
|
|
|
|
# Show two errors
|
|
two_error_label = TextMobject("Two errors")
|
|
two_error_label.next_to(bits, LEFT, buff=MED_LARGE_BUFF, aligned_edge=UP)
|
|
two_error_label.set_color(RED)
|
|
|
|
flippers = VGroup(*random.sample(list(bits), 2))
|
|
|
|
self.play(
|
|
FadeIn(two_error_label),
|
|
LaggedStart(*[zap_anim(f) for f in flippers], lag_ratio=0.5),
|
|
LaggedStartMap(toggle_bit_anim, flippers, lag_ratio=0.5),
|
|
)
|
|
self.play(flippers.set_color, RED)
|
|
self.wait()
|
|
|
|
scanim = scan_anim(point, bits, final_stroke_width=0.2)
|
|
bangs = TextMobject("!!!")[0]
|
|
bangs.set_color(RED)
|
|
bangs.next_to(point, UL, SMALL_BUFF)
|
|
q_marks = TextMobject("???")[0]
|
|
q_marks.replace(bangs, dim_to_match=1)
|
|
q_marks.match_style(bangs)
|
|
|
|
self.play(scanim)
|
|
self.play(
|
|
LaggedStartMap(
|
|
FadeIn, bangs,
|
|
lambda m: (m, 0.25 * DOWN),
|
|
lag_ratio=0.2,
|
|
run_time=1,
|
|
),
|
|
)
|
|
self.wait()
|
|
bangs.unlock_triangulation()
|
|
self.play(ReplacementTransform(bangs, q_marks, lag_ratio=0.2))
|
|
self.wait()
|
|
|
|
|
|
class TimeLine(Scene):
|
|
def construct(self):
|
|
# Time line
|
|
decades = list(range(1920, 2030, 10))
|
|
timeline = NumberLine(
|
|
(decades[0], decades[-1], 2),
|
|
numbers_with_elongated_ticks=decades,
|
|
width=13
|
|
)
|
|
timeline.add_numbers(
|
|
decades,
|
|
number_config={
|
|
"group_with_commas": False,
|
|
"height": 0.2,
|
|
}
|
|
)
|
|
timeline.numbers.set_stroke(BLACK, 4, background=True)
|
|
|
|
# Events
|
|
def get_event(timeline, date, words, direction=UP):
|
|
arrow = Vector(-direction)
|
|
arrow.set_fill(GREY_A, 0.75)
|
|
arrow.shift(timeline.n2p(date) - arrow.get_end())
|
|
arrow.shift(SMALL_BUFF * direction)
|
|
|
|
label = TextMobject(words)
|
|
label.scale(0.7)
|
|
label.next_to(arrow.get_start(), np.sign(direction[1]) * UP, SMALL_BUFF)
|
|
label.set_color(GREY_A)
|
|
label.set_stroke(BLACK, 4, background=True)
|
|
|
|
event = VGroup(label, arrow)
|
|
return event
|
|
|
|
events = VGroup(
|
|
get_event(timeline, 1947, "Hamming codes", 1.5 * UP),
|
|
get_event(timeline, 1948, "Shannon's paper\\\\on information theory", DOWN + 0.2 * LEFT),
|
|
# get_event(timeline, 1949, "Gorlay codes", 0.7 * UP),
|
|
get_event(timeline, 1960, "Reed-Solomon\\\\codes", 0.7 * UP),
|
|
get_event(timeline, 1993, "Turbo codes", UP),
|
|
get_event(timeline, 1995, "Shor codes\\\\(quantum)", DOWN),
|
|
)
|
|
|
|
# Title
|
|
title = TextMobject("Error correction codes")
|
|
title.set_color(YELLOW)
|
|
title.set_height(0.5)
|
|
title.to_edge(UP)
|
|
title_underline = Underline(title)
|
|
title_underline.match_color(title)
|
|
title.add(title_underline)
|
|
title.fix_in_frame()
|
|
|
|
# Introduce time line
|
|
frame = self.camera.frame
|
|
frame.save_state()
|
|
frame.scale(0.5, about_point=timeline.n2p(1945))
|
|
frame.shift(0.5 * UP)
|
|
|
|
self.play(
|
|
Write(timeline),
|
|
Write(timeline.numbers.copy(), remover=True),
|
|
LaggedStart(
|
|
*[Animation(Mobject()) for x in range(1)],
|
|
*[
|
|
AnimationGroup(
|
|
Write(event[0], run_time=1),
|
|
GrowArrow(event[1]),
|
|
)
|
|
for event in events
|
|
],
|
|
lag_ratio=0.75,
|
|
),
|
|
FadeIn(title, DOWN),
|
|
Restore(frame, run_time=6),
|
|
)
|
|
self.wait(2)
|
|
|
|
# Isolate Hamming
|
|
hamming_word = events[0][0]
|
|
rs_word = events[2][0]
|
|
self.play(
|
|
hamming_word.scale, 2, {"about_edge": DL},
|
|
hamming_word.set_fill, WHITE,
|
|
LaggedStart(*[
|
|
ApplyMethod(mob.set_opacity, 0.5)
|
|
for mob in events[1:]
|
|
]),
|
|
FadeOut(VGroup(title, title_underline))
|
|
)
|
|
|
|
invent_words = TextMobject("How to invent")
|
|
invent_words.match_height(hamming_word[0][0])
|
|
invent_words.next_to(events[0], UP, buff=0.3)
|
|
invent_words.set_color(BLUE)
|
|
|
|
self.play(Write(invent_words))
|
|
self.wait()
|
|
|
|
hamming_word.generate_target()
|
|
hamming_word.target.scale(0.5, about_edge=UL)
|
|
hamming_word.target.set_opacity(0.5)
|
|
hamming_arrow = events[0][1]
|
|
|
|
self.play(
|
|
MoveToTarget(hamming_word),
|
|
FadeOut(invent_words),
|
|
hamming_arrow.put_start_and_end_on, hamming_word.target.get_bottom(), hamming_arrow.get_end(),
|
|
hamming_arrow.set_opacity, 0.5,
|
|
rs_word.scale, 1.5, {"about_edge": DOWN},
|
|
rs_word.set_opacity, 1,
|
|
events[2][1].set_opacity, 1,
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class WhatCDsActuallyUse(Scene):
|
|
def construct(self):
|
|
arrow = Vector(2 * RIGHT + UP)
|
|
words = TextMobject("What CDs/DVDs\\\\actually use")
|
|
words.next_to(arrow.get_end(), RIGHT)
|
|
arrow.set_color(YELLOW)
|
|
words.set_color(YELLOW)
|
|
|
|
self.play(
|
|
GrowFromPoint(words, arrow.get_start()),
|
|
GrowArrow(arrow)
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class ListOfRelevantMathTopics(Scene):
|
|
def construct(self):
|
|
topics = VGroup(
|
|
TextMobject("$L^1$ norm"),
|
|
TextMobject("Sphere packing"),
|
|
TextMobject("Finite sporadic groups (see Golay codes)"),
|
|
TextMobject("Finite fields"),
|
|
TextMobject("Galois extensions"),
|
|
TextMobject("Lagrange interpolation (see Reed-Solomon)"),
|
|
TextMobject("Discrete Fourier Transform"),
|
|
TexMobject("\\dots")
|
|
)
|
|
topics.arrange(RIGHT, buff=LARGE_BUFF)
|
|
brown = interpolate_color(GREY_BROWN, WHITE, 0.25)
|
|
for topic, color in zip(topics, it.cycle([BLUE_C, BLUE_D, BLUE_B, brown])):
|
|
topic.set_color(color)
|
|
|
|
topics.move_to(ORIGIN, LEFT)
|
|
topics.to_edge(UP)
|
|
self.play(topics.shift, (topics.get_width() - 5) * LEFT, run_time=12)
|
|
self.wait()
|
|
|
|
|
|
class Reinvention(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.play(
|
|
self.teacher.change, "raise_right_hand",
|
|
self.get_student_changes(*3 * ["pondering"]),
|
|
)
|
|
self.wait(3)
|
|
self.student_says(
|
|
"I see where\\\\this is going",
|
|
student_index=0,
|
|
target_mode="tease",
|
|
)
|
|
self.look_at(self.students[0].bubble)
|
|
self.play(self.students[0].change, "thinking")
|
|
self.wait(6)
|
|
|
|
|
|
class EaterWrapper(Scene):
|
|
def construct(self):
|
|
bg_rect = FullScreenRectangle()
|
|
bg_rect.set_fill(GREY_E, 1)
|
|
bg_rect.set_stroke(BLACK, 0)
|
|
self.add(bg_rect)
|
|
|
|
title = TextMobject("Ben Eater implementing Hamming codes")
|
|
title.set_width(FRAME_WIDTH - 2)
|
|
title.to_edge(UP)
|
|
self.add(title)
|
|
|
|
screen_rect = ScreenRectangle()
|
|
screen_rect.set_fill(BLACK, 1)
|
|
screen_rect.set_height(6)
|
|
screen_rect.next_to(title, DOWN, MED_LARGE_BUFF)
|
|
self.add(screen_rect)
|
|
|
|
self.add(AnimatedBoundary(screen_rect))
|
|
self.wait(16)
|
|
|
|
|
|
class DataGettingZapped(Scene):
|
|
CONFIG = {
|
|
"random_seed": 1,
|
|
}
|
|
|
|
def construct(self):
|
|
# Setup bit array
|
|
# bits = get_bit_grid(50, 100)
|
|
bits = get_bit_grid(25, 50)
|
|
bits.set_color(GREY_B)
|
|
bits.set_height(FRAME_HEIGHT - 0.25)
|
|
|
|
image = ImageMobject("LowResMandelbrot")
|
|
image.replace(bits, dim_to_match=0)
|
|
|
|
threshold = 0.1
|
|
for bit in bits:
|
|
try:
|
|
rgb = image.point_to_rgb(bit.get_center())
|
|
if get_norm(rgb) > threshold:
|
|
value = 1
|
|
else:
|
|
value = 0
|
|
bit[value].set_opacity(1)
|
|
bit[1 - value].set_opacity(0)
|
|
except Exception:
|
|
pass
|
|
|
|
self.add(bits)
|
|
|
|
# Zippity zap
|
|
bolt = SVGMobject("lightning_bolt")
|
|
bolt[0].add_line_to(bolt[0].get_start())
|
|
bolt.set_fill(RED_B, 1)
|
|
bolt.set_stroke(width=0)
|
|
bolt.set_height(0.5)
|
|
|
|
def strike_anim(bit, bolt=bolt, **kwargs):
|
|
bolt = bolt.copy()
|
|
bolt.move_to(bit.get_center(), DL)
|
|
bits.remove(bit)
|
|
bit.generate_target()
|
|
bit.target.set_color(RED)
|
|
bit.target.set_stroke(RED, 1)
|
|
for sm in bit.target:
|
|
sm.set_opacity(1 - sm.get_fill_opacity())
|
|
|
|
outline = bolt.deepcopy()
|
|
outline.set_stroke(RED_D, 2)
|
|
outline.set_fill(opacity=0)
|
|
|
|
return AnimationGroup(
|
|
Succession(
|
|
GrowFromPoint(bolt, bolt.get_corner(UR), rate_func=rush_into),
|
|
FadeOut(bolt, run_time=0.5),
|
|
),
|
|
Succession(
|
|
ShowCreation(outline),
|
|
FadeOut(outline),
|
|
),
|
|
MoveToTarget(bit),
|
|
)
|
|
|
|
for count in range(20):
|
|
self.play(LaggedStart(*[
|
|
strike_anim(random.choice(bits))
|
|
for x in range(int(random.expovariate(0.5)) + 1)
|
|
], lag_ratio=0.25))
|
|
self.wait(random.expovariate(2))
|
|
|
|
|
|
class AmbientErrorCorrection(Scene):
|
|
CONFIG = {
|
|
"N": 8,
|
|
"bit_grid_height": 7,
|
|
}
|
|
|
|
def construct(self):
|
|
N = self.N
|
|
size = (2**(N // 2), 2**(N // 2))
|
|
bits = get_bit_grid(
|
|
*size,
|
|
bits=string_to_bits("Claude Shannon was a total boss!"),
|
|
height=self.bit_grid_height,
|
|
)
|
|
bits.move_to(2 * RIGHT)
|
|
point = 2.5 * LEFT + 2.5 * DOWN
|
|
last_block = VMobject()
|
|
|
|
for x in range(10):
|
|
block = get_bit_grid(*size, height=self.bit_grid_height)
|
|
block.move_to(2 * RIGHT)
|
|
syndrome = hamming_syndrome(bit_grid_to_bits(block))
|
|
|
|
if random.random() < 0.5:
|
|
syndrome = 0
|
|
toggle_bit(block[syndrome])
|
|
|
|
scanim = scan_anim(
|
|
point, bits,
|
|
final_stroke_width=0.2, run_time=3, lag_factor=1,
|
|
show_robot=(x == 0),
|
|
)
|
|
self.play(
|
|
FadeIn(block, 6 * RIGHT),
|
|
FadeOut(last_block, 6 * LEFT),
|
|
run_time=2
|
|
)
|
|
self.play(scanim, run_time=1)
|
|
if syndrome:
|
|
flipper = block[syndrome]
|
|
bangs = TexMobject("!!!")
|
|
bangs.scale(2)
|
|
bangs.next_to(point, UL)
|
|
bangs.set_color(RED)
|
|
self.play(
|
|
Write(bangs, run_time=0.5),
|
|
focus_scan_anim_lines(scanim, flipper.get_center()),
|
|
flipper.set_color, RED,
|
|
)
|
|
self.play(
|
|
toggle_bit_anim(flipper, target_color=WHITE),
|
|
ApplyMethod(scanim.mobject[0].set_stroke, None, 0, 0, remover=True),
|
|
FadeOut(bangs),
|
|
)
|
|
else:
|
|
check = Checkmark()
|
|
check.scale(2)
|
|
check.next_to(point, UL)
|
|
self.play(FadeIn(check, 0.5 * DOWN))
|
|
self.play(FadeOut(check), FadeOut(scanim.mobject[0]))
|
|
last_block = block
|
|
|
|
|
|
class AmbientErrorCorrection6(AmbientErrorCorrection):
|
|
CONFIG = {
|
|
"N": 6
|
|
}
|
|
|
|
|
|
class AmbientErrorCorrection4(AmbientErrorCorrection):
|
|
CONFIG = {
|
|
"N": 4,
|
|
"bit_grid_height": 5,
|
|
}
|
|
|
|
|
|
class ImpossibleToReasonable(Scene):
|
|
def construct(self):
|
|
group = VGroup(
|
|
TextMobject("Impossible"),
|
|
Vector(RIGHT, color=GREY_B),
|
|
TextMobject("Utterly reasonable"),
|
|
)
|
|
group.arrange(RIGHT)
|
|
group.scale(1.5)
|
|
group.to_edge(UP)
|
|
|
|
line = Line(LEFT, RIGHT)
|
|
line.set_width(FRAME_WIDTH)
|
|
line.set_stroke(GREY, 2)
|
|
line.next_to(group, DOWN, SMALL_BUFF)
|
|
self.add(line)
|
|
|
|
self.play(FadeIn(group[0], 0.5 * UP))
|
|
self.wait()
|
|
self.play(
|
|
GrowArrow(group[1]),
|
|
FadeIn(group[2], LEFT),
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class HammingAtBell(Scene):
|
|
def construct(self):
|
|
# Setup
|
|
hamming_image = ImageMobject("Richard_Hamming")
|
|
hamming_name = TextMobject("Richard Hamming")
|
|
hamming_name.match_width(hamming_image)
|
|
hamming_name.next_to(hamming_image, DOWN, MED_SMALL_BUFF)
|
|
hamming = Group(hamming_image, hamming_name)
|
|
hamming.to_corner(DR)
|
|
hamming.shift(2 * LEFT)
|
|
|
|
bell_logo = ImageMobject("BellSystemLogo")
|
|
bell_logo.set_height(3)
|
|
bell_logo.next_to(hamming, LEFT, buff=2)
|
|
bell_logo.to_edge(UP)
|
|
|
|
bell_logo_outline = SVGMobject("BellSystemLogo")
|
|
bell_logo_outline.match_height(bell_logo)
|
|
bell_logo_outline.set_stroke(GREY_B, 1)
|
|
bell_logo_outline.set_fill(BLACK, 0)
|
|
bell_logo_outline.move_to(bell_logo)
|
|
|
|
punchcard = SVGMobject("punchcard")
|
|
punchcard.set_stroke(width=0)
|
|
punchcard.set_fill(GREY_B, 1)
|
|
punchcard.next_to(bell_logo, DOWN, LARGE_BUFF)
|
|
punchcard.remove(*punchcard[23:])
|
|
|
|
years = TextMobject("1940s")
|
|
years.scale(2)
|
|
years.to_edge(UP)
|
|
|
|
# Introductions
|
|
self.play(Write(years))
|
|
self.play(FadeIn(hamming[0], RIGHT))
|
|
self.play(Write(hamming[1]))
|
|
self.play(
|
|
FadeOut(years),
|
|
ShowCreationThenFadeOut(bell_logo_outline, lag_ratio=0.1, run_time=4),
|
|
FadeIn(bell_logo, run_time=3),
|
|
)
|
|
|
|
self.play(
|
|
FadeIn(punchcard[0]),
|
|
Write(punchcard[1:], lag_ratio=0.5, run_time=4)
|
|
)
|
|
self.add(punchcard)
|
|
self.wait()
|
|
|
|
# Zap some bits
|
|
random.seed(3)
|
|
bits = random.sample(list(punchcard[1:]), 4)
|
|
for bit in bits:
|
|
bit.generate_target()
|
|
bit.target.set_color(RED)
|
|
if random.random() < 0.5:
|
|
bit.target.stretch(0.2, 1, about_edge=DOWN)
|
|
else:
|
|
bit.target.shift(1.2 * bit.get_width() * LEFT)
|
|
self.play(
|
|
MoveToTarget(bit),
|
|
zap_anim(bit)
|
|
)
|
|
self.wait()
|
|
|
|
# Frustration
|
|
curse = TextMobject("\\$*@\\#*!!?!")[0]
|
|
curse.set_color(RED)
|
|
curse.next_to(hamming, UP)
|
|
|
|
self.play(ShowIncreasingSubsets(curse))
|
|
self.wait()
|
|
|
|
|
|
class MultiplePerspectives(Scene):
|
|
def construct(self):
|
|
# Background
|
|
background = VGroup(*[
|
|
Rectangle().set_fill(color, 1)
|
|
for color in [GREY_E, BLACK, GREY_E]
|
|
])
|
|
background.set_stroke(width=0)
|
|
background.arrange(RIGHT, buff=0)
|
|
background.set_height(FRAME_HEIGHT)
|
|
background.set_width(FRAME_WIDTH, stretch=True)
|
|
self.add(background)
|
|
|
|
# Names
|
|
names = VGroup(
|
|
TextMobject("Parity checks"),
|
|
TextMobject("Xor of indices"),
|
|
TextMobject("Matrix"),
|
|
)
|
|
|
|
names.set_height(0.6)
|
|
for name, rect in zip(names, background):
|
|
name.match_x(rect)
|
|
names[0].shift(SMALL_BUFF * DOWN)
|
|
names.to_edge(DOWN, buff=1)
|
|
|
|
# Objects
|
|
parity_groups = VGroup()
|
|
for n in range(4):
|
|
pg = VGroup(*[Square() for x in range(16)])
|
|
pg.arrange_in_grid(4, 4, buff=0)
|
|
pg.set_height(0.7)
|
|
pg.set_stroke(GREY_A, 2)
|
|
get_bit_n_subgroup(pg, n).set_fill(BLUE, 0.8)
|
|
parity_groups.add(pg)
|
|
parity_groups.arrange_in_grid(2, 2)
|
|
|
|
code = ImageMobject("Hamming_Code_Snippet")
|
|
|
|
ints = list(random.sample(list(range(16)), 4))
|
|
ints.sort()
|
|
xor_sum = reduce(op.xor, ints)
|
|
bits = [int_to_bit_string(n, n_bits=4) for n in [*ints, xor_sum]]
|
|
column = Group(*map(TextMobject, bits))
|
|
column.arrange(DOWN, SMALL_BUFF)
|
|
column[-1].set_color(YELLOW)
|
|
column[-1].shift(MED_SMALL_BUFF * DOWN)
|
|
line = Line(LEFT, RIGHT)
|
|
line.set_stroke(GREY_B)
|
|
line.set_width(column.get_width() + 0.75)
|
|
line.move_to(column[-2:], RIGHT)
|
|
xor = get_xor()
|
|
xor.next_to(line, UP, SMALL_BUFF, LEFT)
|
|
column.add(line, xor)
|
|
code.set_width(2 * column.get_width())
|
|
code.next_to(column, UP)
|
|
column.add(code)
|
|
|
|
matrix = IntegerMatrix(
|
|
[
|
|
[1, 1, 0, 1],
|
|
[1, 0, 1, 1],
|
|
[1, 0, 0, 0],
|
|
[0, 1, 1, 1],
|
|
[0, 1, 0, 0],
|
|
[0, 0, 1, 0],
|
|
[0, 0, 0, 1],
|
|
],
|
|
v_buff=0.6,
|
|
h_buff=0.75,
|
|
)
|
|
|
|
objs = Group(parity_groups, column, matrix)
|
|
|
|
for name, obj in zip(names, objs):
|
|
obj.match_width(names[0])
|
|
obj.next_to(name, UP, LARGE_BUFF)
|
|
matrix.scale(0.7, about_edge=DOWN)
|
|
|
|
# Introduce
|
|
anims = []
|
|
for name, obj in zip(names, objs):
|
|
anims.append(AnimationGroup(
|
|
FadeIn(name, 0.25 * UP),
|
|
FadeIn(obj, lag_ratio=0, run_time=2),
|
|
))
|
|
self.play(LaggedStart(*anims, lag_ratio=0.4))
|
|
self.wait()
|
|
for name, obj in zip(names, objs):
|
|
obj.add(name)
|
|
self.add(background[0])
|
|
self.play(
|
|
FadeOut(background),
|
|
objs[0].set_x, 0,
|
|
FadeOut(objs[1], 5 * RIGHT),
|
|
FadeOut(objs[2], 2 * RIGHT),
|
|
run_time=2
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class SetupSixteenBitExample(Scene):
|
|
def construct(self):
|
|
# Title
|
|
title = TextMobject("Reinventing Hamming Codes")
|
|
title.scale(1.5)
|
|
title.to_edge(UP)
|
|
title.set_color(BLUE)
|
|
self.play(Write(title))
|
|
|
|
# Simple but not too simple
|
|
block = get_bit_grid(4, 4, bits=string_to_bits(":)"))
|
|
block.move_to(0.5 * DOWN)
|
|
top_row = block[:4]
|
|
top_row.save_state()
|
|
top_row.center()
|
|
|
|
simple_words = TextMobject("Simple\\\\", "but not too\\\\simple")
|
|
simple_words.scale(1.5)
|
|
simple_words.next_to(block[4:12], LEFT, buff=LARGE_BUFF)
|
|
top_simp = simple_words[0]
|
|
top_simp.save_state()
|
|
top_simp.set_y(0)
|
|
|
|
self.play(
|
|
FadeIn(top_simp),
|
|
ShowIncreasingSubsets(top_row),
|
|
)
|
|
self.play(
|
|
Restore(top_simp),
|
|
Restore(top_row),
|
|
FadeIn(simple_words[1:]),
|
|
LaggedStartMap(FadeIn, block[4:])
|
|
)
|
|
self.add(block)
|
|
self.add(simple_words)
|
|
|
|
boxes = get_bit_grid_boxes(block, color=GREEN)
|
|
bits_word = TextMobject("bits")
|
|
bits_word.set_height(0.7)
|
|
bits_word.next_to(boxes, LEFT, buff=LARGE_BUFF)
|
|
counter = Integer(0, edge_to_fix=RIGHT)
|
|
counter.match_height(bits_word)
|
|
counter.next_to(bits_word, LEFT, buff=0.35)
|
|
bit_count_group = VGroup(bits_word, counter)
|
|
bit_count_group.set_color(YELLOW)
|
|
|
|
self.play(
|
|
FadeOut(simple_words),
|
|
FadeIn(bits_word),
|
|
UpdateFromAlphaFunc(
|
|
counter,
|
|
lambda m, a: m.set_fill(opacity=a)
|
|
),
|
|
UpdateFromFunc(
|
|
counter,
|
|
lambda c: c.set_value(len(boxes))
|
|
),
|
|
ShowIncreasingSubsets(boxes, run_time=2)
|
|
)
|
|
|
|
# Enumerate bits
|
|
block.generate_target()
|
|
block.target.space_out_submobjects(1.5)
|
|
block.target.center()
|
|
|
|
new_boxes = get_bit_grid_boxes(block.target, color=GREY_B)
|
|
h_buff = get_norm(block.target[0].get_center() - block.target[4].get_center())
|
|
for box in new_boxes:
|
|
box.set_height(h_buff, stretch=True)
|
|
|
|
p_labels = VGroup()
|
|
for n, box in enumerate(new_boxes):
|
|
label = Integer(n)
|
|
label.set_height(0.25)
|
|
label.set_color(YELLOW)
|
|
label.move_to(box, DR)
|
|
label.shift(SMALL_BUFF * UL)
|
|
p_labels.add(label)
|
|
|
|
self.play(
|
|
MoveToTarget(block),
|
|
Transform(boxes, new_boxes),
|
|
FadeOut(title, UP),
|
|
FadeOut(VGroup(counter, bits_word), LEFT),
|
|
)
|
|
self.play(FadeIn(p_labels, lag_ratio=0.07, run_time=3))
|
|
self.wait()
|
|
|
|
# Data bits
|
|
r_boxes = VGroup(*[boxes[2**n] for n in range(4)]).copy()
|
|
r_bits = VGroup(*[block[2**n] for n in range(4)])
|
|
d_bits = VGroup(*[b for b in block if b not in r_bits])
|
|
|
|
d_label = TextMobject("12 bits\\\\of data")
|
|
d_label.set_height(1.5)
|
|
d_label.next_to(boxes, RIGHT, aligned_edge=UP, buff=LARGE_BUFF)
|
|
d_label.set_color(BLUE)
|
|
|
|
d_lines = VGroup(*[
|
|
Line(
|
|
d_label.get_left(), bit.get_center(),
|
|
color=(TEAL if get_bit_mob_value(bit) else YELLOW)
|
|
)
|
|
for bit in d_bits
|
|
])
|
|
d_lines.set_stroke(width=1)
|
|
|
|
self.play(
|
|
FadeOut(r_bits),
|
|
d_bits.set_color, BLUE,
|
|
FadeIn(d_label),
|
|
p_labels.set_color, WHITE,
|
|
)
|
|
self.play(LaggedStartMap(ShowCreationThenFadeOut, d_lines))
|
|
self.wait()
|
|
|
|
# Redundancy bits
|
|
r_label = TextMobject("4 bits for\\\\", "redundancy")
|
|
r_label.match_height(d_label)
|
|
r_label.next_to(boxes, LEFT, aligned_edge=UP, buff=MED_LARGE_BUFF)
|
|
r_label.set_color(GREEN)
|
|
|
|
r_boxes.set_fill(GREEN, 0.5)
|
|
self.add(r_boxes, p_labels)
|
|
self.play(
|
|
FadeIn(r_label, 0.2 * RIGHT),
|
|
LaggedStartMap(FadeIn, r_boxes, run_time=2)
|
|
)
|
|
self.wait()
|
|
|
|
# Can't cram in copies
|
|
d_bit_copies = d_bits.copy()
|
|
d_bit_copies.generate_target()
|
|
for n, box in enumerate(r_boxes):
|
|
group = d_bit_copies.target[3 * n:3 * (n + 1)]
|
|
group.arrange_in_grid(2, 2, buff=SMALL_BUFF)
|
|
group.set_width(0.4 * box.get_width())
|
|
group.move_to(box)
|
|
group.set_color(YELLOW)
|
|
|
|
self.play(ShowCreationThenDestruction(Underline(r_label[1])))
|
|
self.play(MoveToTarget(d_bit_copies, lag_ratio=0.1, run_time=4, rate_func=linear))
|
|
self.wait()
|
|
self.play(
|
|
UpdateFromAlphaFunc(
|
|
d_bit_copies, lambda m, a: m.shift(0.03 * np.sin(7 * TAU * a) * RIGHT).fade(a),
|
|
remover=True
|
|
)
|
|
)
|
|
|
|
# Set true redundancy bits
|
|
highlights = boxes.copy()
|
|
highlights.set_stroke(YELLOW, 6)
|
|
|
|
for n in range(4):
|
|
h_group = get_bit_n_subgroup(highlights, n)
|
|
bit = r_bits[n]
|
|
if get_bit_mob_value(bit) == 1:
|
|
toggle_bit(bit)
|
|
|
|
self.play(
|
|
FadeIn(h_group, lag_ratio=0.2),
|
|
FadeIn(bit)
|
|
)
|
|
anims = [FadeOut(h_group)]
|
|
if n in [2, 3]:
|
|
anims.append(toggle_bit_anim(bit))
|
|
self.play(*anims)
|
|
|
|
# Might expect them to come at the end
|
|
movers = [d_bits, r_bits]
|
|
for mover in movers:
|
|
mover.save_state()
|
|
mover.generate_target()
|
|
|
|
for b1, b2 in zip(it.chain(d_bits.target, r_bits.target), block):
|
|
b1.move_to(b2)
|
|
|
|
for box, bit in zip(r_boxes, r_bits):
|
|
box.bit = bit
|
|
box.add_updater(lambda m: m.move_to(m.bit))
|
|
|
|
self.add(*r_boxes)
|
|
self.play(
|
|
*[
|
|
MoveToTarget(mover, lag_ratio=0.1, run_time=3, path_arc=20 * DEGREES)
|
|
for mover in movers
|
|
],
|
|
)
|
|
self.wait()
|
|
|
|
self.play(*[
|
|
Restore(mover, lag_ratio=0.1, run_time=3, path_arc=20 * DEGREES)
|
|
for mover in movers
|
|
])
|
|
r_boxes.clear_updaters()
|
|
self.wait()
|
|
|
|
power_of_2_rects = VGroup(*[
|
|
SurroundingRectangle(p_labels[2**n])
|
|
for n in range(4)
|
|
])
|
|
self.play(LaggedStartMap(ShowCreationThenFadeOut, power_of_2_rects))
|
|
|
|
# Correct to 11 bits
|
|
cross = Cross(d_label[0][:2])
|
|
c_label = TextMobject("Er...11 bits")
|
|
c_label.match_width(d_label)
|
|
c_label.next_to(d_label, DOWN, buff=MED_LARGE_BUFF)
|
|
c_label.set_color(RED)
|
|
q_mark = TexMobject("?")
|
|
q_mark.replace(block[0])
|
|
q_mark.set_color(RED)
|
|
|
|
self.play(ShowCreation(cross))
|
|
self.play(Write(c_label))
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(block[0]),
|
|
Write(q_mark),
|
|
)
|
|
|
|
randy = Randolph()
|
|
randy.to_corner(DL)
|
|
|
|
self.play(
|
|
VFadeIn(randy),
|
|
randy.change, "confused", q_mark
|
|
)
|
|
self.play(Blink(randy))
|
|
self.wait(2)
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
|
|
def old_functions(self):
|
|
# Show redundancy and message
|
|
redundancy_words = TextMobject("Separate some for\\\\redundancy")
|
|
redundancy_words.next_to(block[-4:], LEFT, buff=LARGE_BUFF, aligned_edge=UP)
|
|
redundancy_words.set_color(BLUE)
|
|
|
|
vect = redundancy_words.get_center() - bit_count_group.get_center()
|
|
self.play(
|
|
FadeOut(bit_count_group, vect),
|
|
FadeIn(redundancy_words, -vect),
|
|
ApplyMethod(boxes[-4:].set_color, BLUE, lag_ratio=0.2),
|
|
ApplyMethod(block[-4:].set_color, BLUE, lag_ratio=0.2),
|
|
)
|
|
self.wait()
|
|
|
|
message_words = TextMobject("Leave the rest\\\\for a message")
|
|
message_words.next_to(block[:-4], LEFT)
|
|
message_words.match_x(redundancy_words)
|
|
message_words.set_color(YELLOW)
|
|
message_words.save_state()
|
|
message_words.replace(redundancy_words)
|
|
message_words.set_opacity(0)
|
|
|
|
self.play(
|
|
Restore(message_words),
|
|
boxes[:-4].set_color, YELLOW,
|
|
)
|
|
for x in range(10):
|
|
bits = list(block[:-4])
|
|
random.shuffle(bits)
|
|
for bit in bits:
|
|
if random.random() < 0.5:
|
|
toggle_bit(bit)
|
|
self.wait(0.2 / 12)
|
|
self.wait()
|
|
|
|
# Show correction
|
|
flipper = random.choice(block)
|
|
scanim = scan_anim(
|
|
block.get_corner(DR) + 2 * RIGHT, block,
|
|
final_stroke_width=0.5,
|
|
lag_factor=1,
|
|
)
|
|
|
|
self.play(
|
|
zap_anim(flipper),
|
|
toggle_bit_anim(flipper, target_color=RED)
|
|
)
|
|
self.wait()
|
|
self.play(scanim)
|
|
self.play(focus_scan_anim_lines(scanim, flipper.get_center()))
|
|
self.play(
|
|
toggle_bit_anim(flipper, target_color=WHITE),
|
|
FadeOut(scanim.mobject)
|
|
)
|
|
|
|
|
|
class SenderReceiverDynamic(PiCreatureScene):
|
|
def construct(self):
|
|
# Sender and receiver
|
|
sender_word = TextMobject("Sender")
|
|
receiver_word = TextMobject("Receiver")
|
|
words = VGroup(sender_word, receiver_word)
|
|
words.scale(1.5)
|
|
words.set_y(-2)
|
|
sender_word.to_edge(LEFT)
|
|
receiver_word.to_edge(RIGHT)
|
|
|
|
sender, receiver = pis = self.pi_creatures
|
|
for pi, word in zip(pis, words):
|
|
pi.set_height(2)
|
|
pi.next_to(word, UP)
|
|
|
|
self.clear()
|
|
for pi in pis:
|
|
self.play(
|
|
VFadeIn(pi),
|
|
pi.change, "pondering", ORIGIN,
|
|
)
|
|
for word in words:
|
|
self.play(Write(word, run_time=1))
|
|
self.wait()
|
|
|
|
# Message
|
|
block = get_bit_grid(4, 4)
|
|
block.set_height(1.5)
|
|
block.next_to(pis[0].get_corner(UR), UR)
|
|
|
|
self.play(
|
|
sender.change, "raise_right_hand",
|
|
FadeIn(block, DOWN, lag_ratio=0.01),
|
|
)
|
|
self.add(sender, block)
|
|
block.save_state()
|
|
self.play(
|
|
block.move_to, sender,
|
|
block.scale, 0.5,
|
|
sender.change, "gracious", sender,
|
|
)
|
|
self.play(LaggedStart(
|
|
ApplyMethod(block[1].set_color, GREEN),
|
|
toggle_bit_anim(block[2], target_color=GREEN),
|
|
ApplyMethod(block[4].set_color, GREEN),
|
|
toggle_bit_anim(block[8], target_color=GREEN),
|
|
))
|
|
self.wait()
|
|
block.generate_target()
|
|
block.target.scale(2)
|
|
block.target.next_to(receiver, UL)
|
|
self.play(
|
|
MoveToTarget(block, run_time=3, path_arc=-30 * DEGREES),
|
|
receiver.change, "tease", receiver.get_corner(UL) + UP,
|
|
sender.change, "happy", receiver.get_corner(UL) + UP,
|
|
)
|
|
self.play(scan_anim(receiver.get_corner(UL), block, lag_factor=1, show_robot=False))
|
|
self.wait()
|
|
|
|
# Replace with machines
|
|
underlines = VGroup(*[Underline(word) for word in words])
|
|
underlines.set_color(YELLOW)
|
|
servers = VGroup(*[SVGMobject("server_stack") for x in range(2)])
|
|
servers.set_color(GREY)
|
|
servers.set_stroke(BLACK, 2)
|
|
servers.set_gloss(0.5)
|
|
for server, pi in zip(servers, pis):
|
|
server.move_to(pi)
|
|
|
|
self.play(
|
|
LaggedStartMap(ShowCreationThenFadeOut, underlines, lag_ratio=0.7),
|
|
*[
|
|
ApplyMethod(pi.change, "thinking", pi)
|
|
for pi in pis
|
|
],
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
*it.chain(*[
|
|
[
|
|
pi.change, "horrified",
|
|
pi.shift, 2 * UP,
|
|
pi.set_opacity, 0
|
|
]
|
|
for pi in pis
|
|
]),
|
|
FadeIn(servers, 2 * DOWN)
|
|
)
|
|
self.remove(pis)
|
|
|
|
bits_copy = block.copy()
|
|
bits_copy.arrange(RIGHT, buff=SMALL_BUFF)
|
|
bits_copy.set_width(0.7 * servers[1].get_width())
|
|
bits_copy.move_to(servers[1], LEFT)
|
|
bits_copy.shift(SMALL_BUFF * RIGHT)
|
|
self.play(
|
|
LaggedStart(*[
|
|
TransformFromCopy(b1, b2)
|
|
for b1, b2 in zip(block, bits_copy)
|
|
], run_time=3),
|
|
)
|
|
|
|
# Sent and received
|
|
sent_block = block.copy()
|
|
sent_block.next_to(servers[0], UR)
|
|
sent_block.match_y(block)
|
|
wire = VGroup(
|
|
SurroundingRectangle(sent_block),
|
|
Line(sent_block.get_right(), block.get_left(), buff=SMALL_BUFF / 2),
|
|
SurroundingRectangle(block),
|
|
)
|
|
wire.set_stroke(GREY, 2)
|
|
noise_word = TextMobject("Potential noise")
|
|
noise_word.scale(0.75)
|
|
noise_word.set_color(RED)
|
|
noise_word.next_to(wire[1], UP, SMALL_BUFF)
|
|
|
|
small_words = VGroup(
|
|
TextMobject("sent"),
|
|
TextMobject("received"),
|
|
)
|
|
for word, mob in zip(small_words, [sent_block, block]):
|
|
word.next_to(mob, UP, buff=0.35)
|
|
|
|
self.play(
|
|
ShowCreation(wire[0]),
|
|
FadeIn(small_words[0], 0.5 * DOWN),
|
|
FadeIn(sent_block),
|
|
)
|
|
self.play(
|
|
Transform(sent_block.copy(), block, remover=True, lag_ratio=0.01),
|
|
ShowCreation(wire[1]),
|
|
FadeIn(noise_word, lag_ratio=0.1)
|
|
)
|
|
self.play(
|
|
ShowCreation(wire[2]),
|
|
FadeIn(small_words[1], 0.5 * DOWN)
|
|
)
|
|
|
|
# Past and future
|
|
cross_lines = VGroup(*[
|
|
Line(
|
|
word.get_left(), word.get_right(),
|
|
stroke_width=5,
|
|
stroke_color=RED,
|
|
)
|
|
for word in words
|
|
])
|
|
time_words = VGroup(
|
|
TextMobject("Past"),
|
|
TextMobject("Future"),
|
|
)
|
|
time_words.set_color(BLUE)
|
|
for w1, w2 in zip(words, time_words):
|
|
w2.match_height(w1)
|
|
w2.next_to(w1, DOWN)
|
|
|
|
self.play(
|
|
LaggedStartMap(ShowCreation, cross_lines, lag_ratio=0.7),
|
|
LaggedStartMap(ApplyMethod, words, lambda m: (m.set_opacity, 0.75), lag_ratio=0.7),
|
|
run_time=1,
|
|
)
|
|
self.play(
|
|
LaggedStartMap(FadeIn, time_words, lambda m: (m, UP), lag_ratio=0.3)
|
|
)
|
|
self.wait(2)
|
|
|
|
def create_pi_creatures(self):
|
|
return VGroup(Randolph(), Mortimer())
|
|
|
|
|
|
class ParityChecks(Scene):
|
|
def construct(self):
|
|
# Show detection
|
|
block = get_bit_grid(4, 4, bits=string_to_bits(":)"))
|
|
block.move_to(3 * LEFT)
|
|
point = block.get_corner(DR) + 2 * RIGHT + UP
|
|
scanim = scan_anim(point, block, final_stroke_width=0.5, run_time=2, lag_factor=2)
|
|
lines, robot = scanim.mobject
|
|
detection_words = TextMobject("Error detected!")
|
|
detection_words.set_color(RED)
|
|
detection_subwords = TextMobject("But I have no idea where!")
|
|
detection_words.next_to(robot.get_top(), UR, SMALL_BUFF)
|
|
detection_subwords.move_to(detection_words, DL)
|
|
|
|
self.add(block)
|
|
self.play(scanim)
|
|
self.play(
|
|
GrowFromPoint(detection_words, robot.get_top())
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
detection_words.shift, 0.75 * UP,
|
|
FadeIn(detection_subwords, 0.5 * DOWN),
|
|
)
|
|
self.wait()
|
|
|
|
title = TextMobject("Parity Check")[0]
|
|
title.set_color(YELLOW)
|
|
title.set_stroke(BLACK, 3, background=True)
|
|
title.add(Underline(title).shift(0.05 * UP))
|
|
title.scale(1.5)
|
|
title.to_edge(UP)
|
|
|
|
self.play(Write(title))
|
|
self.wait()
|
|
self.play(
|
|
LaggedStart(
|
|
FadeOut(scanim.mobject),
|
|
FadeOut(detection_words),
|
|
FadeOut(detection_subwords),
|
|
),
|
|
block.move_to, DOWN,
|
|
)
|
|
|
|
# Single bit vs. message
|
|
pb_rect = SurroundingRectangle(block[0])
|
|
pb_rect.set_stroke(GREEN, 3)
|
|
ms_rect = SurroundingRectangle(block)
|
|
ms_blob = Polygon(
|
|
pb_rect.get_corner(UR) + 0.05 * RIGHT,
|
|
ms_rect.get_corner(UR),
|
|
ms_rect.get_corner(DR),
|
|
ms_rect.get_corner(DL),
|
|
pb_rect.get_corner(DL) + 0.05 * DOWN,
|
|
pb_rect.get_corner(DR) + 0.05 * DR,
|
|
)
|
|
ms_blob.set_stroke(BLUE, 3)
|
|
|
|
pb_words = TextMobject("Reserve one special bit")
|
|
pb_words.next_to(pb_rect, LEFT)
|
|
pb_words.match_color(pb_rect)
|
|
ms_words = TextMobject("The rest carry\\\\a message", alignment="")
|
|
ms_words.next_to(ms_blob, RIGHT, aligned_edge=UP)
|
|
ms_words.align_to(pb_words, UP)
|
|
ms_words.match_color(ms_blob)
|
|
|
|
self.play(
|
|
FadeIn(pb_words),
|
|
ShowCreation(pb_rect),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(ms_words),
|
|
ShowCreation(ms_blob),
|
|
)
|
|
|
|
# Wave of flips
|
|
k = 3
|
|
for n in range(1, len(block) + k):
|
|
if n < len(block):
|
|
toggle_bit(block[n])
|
|
if 1 <= n - k < len(block):
|
|
toggle_bit(block[n - k])
|
|
self.wait(0.05)
|
|
|
|
# Count 1's
|
|
number_ones_label = TextMobject("\\# ", "of 1's: ")
|
|
number_ones_label.set_height(0.7)
|
|
number_ones_label.to_edge(UP)
|
|
number_ones_label.shift(LEFT)
|
|
|
|
one_rects = get_one_rects(block)
|
|
one_counter = get_ones_counter(number_ones_label, one_rects)
|
|
|
|
self.play(
|
|
FadeIn(number_ones_label, DOWN),
|
|
FadeOut(title, UP),
|
|
)
|
|
self.add(one_counter)
|
|
self.play(ShowIncreasingSubsets(one_rects, run_time=1, rate_func=bezier([0, 0, 1, 1])))
|
|
self.wait()
|
|
|
|
# Show need to flip
|
|
want_even_label = TextMobject("Want this\\\\to be even")
|
|
want_even_label.next_to(one_counter, RIGHT, buff=1.5)
|
|
want_even_arrow = Arrow(
|
|
want_even_label.get_left(), one_counter.get_right()
|
|
)
|
|
want_even_label.shift_onto_screen()
|
|
|
|
self.play(
|
|
GrowArrow(want_even_arrow),
|
|
FadeIn(want_even_label),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
LaggedStartMap(
|
|
Indicate, one_rects,
|
|
scale_factor=1.1,
|
|
color=RED,
|
|
lag_ratio=0.2,
|
|
run_time=2,
|
|
)
|
|
)
|
|
self.wait()
|
|
|
|
pb = block[0]
|
|
pb_center = pb.get_center()
|
|
|
|
self.play(pb.next_to, pb_words, DOWN)
|
|
self.play(toggle_bit_anim(pb))
|
|
self.play(pb.move_to, pb_center)
|
|
one_rects.become(get_one_rects(block))
|
|
self.play(DrawBorderThenFill(one_rects[0]))
|
|
|
|
# Show case with no need to flip
|
|
self.play(
|
|
FadeOut(block),
|
|
FadeOut(one_rects),
|
|
FadeOut(one_counter),
|
|
)
|
|
new_block = get_bit_grid(4, 4, bits=string_to_bits("<3"))
|
|
new_block.replace(block)
|
|
block = new_block
|
|
self.play(ShowIncreasingSubsets(block))
|
|
one_rects = get_one_rects(block)
|
|
one_counter = get_ones_counter(number_ones_label, one_rects)
|
|
self.add(one_counter)
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
check = Checkmark()
|
|
check.scale(2)
|
|
check.next_to(pb, UP)
|
|
self.play(Write(check, run_time=0.5))
|
|
self.play(FadeOut(check))
|
|
self.wait()
|
|
|
|
# Sender and receiver
|
|
self.play(LaggedStart(*map(FadeOut, [
|
|
number_ones_label, one_counter,
|
|
want_even_arrow, want_even_label,
|
|
pb_words, pb_rect,
|
|
ms_words, ms_blob,
|
|
one_rects
|
|
])))
|
|
|
|
pis, names = get_sender_and_receiver()
|
|
sender, receiver = pis
|
|
|
|
block.generate_target()
|
|
block.target.scale(0.5)
|
|
block.target.next_to(sender, UR)
|
|
r_block = block.target.copy()
|
|
r_block.next_to(receiver, UL)
|
|
n_block = r_block.copy()
|
|
n_block.move_to(VGroup(block.target, r_block))
|
|
arrows = VGroup(
|
|
Arrow(block.target.get_right(), n_block.get_left()),
|
|
Arrow(n_block.get_right(), r_block.get_left()),
|
|
)
|
|
noise_word = TextMobject("Noise")
|
|
noise_arrow = Vector(0.7 * DOWN)
|
|
noise_arrow.next_to(n_block, UP)
|
|
noise_word.next_to(noise_arrow, UP)
|
|
noise_label = VGroup(noise_word, noise_arrow)
|
|
noise_label.set_color(RED)
|
|
flipper_index = 7
|
|
|
|
self.play(
|
|
ApplyMethod(sender.change, "raise_right_hand", block.target),
|
|
VFadeIn(sender),
|
|
FadeIn(names),
|
|
FadeIn(receiver),
|
|
MoveToTarget(block),
|
|
)
|
|
self.play(
|
|
TransformFromCopy(block, n_block),
|
|
GrowArrow(arrows[0]),
|
|
FadeIn(noise_label),
|
|
)
|
|
self.play(
|
|
toggle_bit_anim(n_block[flipper_index], target_color=RED),
|
|
zap_anim(n_block[flipper_index]),
|
|
)
|
|
toggle_bit(r_block[flipper_index])
|
|
self.play(
|
|
TransformFromCopy(n_block, r_block),
|
|
GrowArrow(arrows[1]),
|
|
receiver.change, "pondering", r_block,
|
|
)
|
|
|
|
# Recount the 1's
|
|
blocks = (block, r_block)
|
|
one_rects_pair = VGroup(*[get_one_rects(b, buff=0.05) for b in blocks])
|
|
label_pair = VGroup(*[
|
|
TextMobject("\\#", " 1's:").next_to(b, UP, buff=MED_LARGE_BUFF)
|
|
for b in blocks
|
|
])
|
|
one_counter_pair = VGroup(*[
|
|
get_ones_counter(label, rect, buff=MED_SMALL_BUFF)
|
|
for label, rect in zip(label_pair, one_rects_pair)
|
|
])
|
|
|
|
self.add(label_pair, one_counter_pair)
|
|
self.play(*[
|
|
ShowIncreasingSubsets(group)
|
|
for group in one_rects_pair
|
|
])
|
|
self.wait()
|
|
|
|
bangs = TexMobject("!!!")
|
|
bangs.set_color(RED)
|
|
bangs.next_to(receiver, UP)
|
|
self.play(
|
|
FadeIn(bangs, DOWN),
|
|
receiver.change, "horrified", r_block,
|
|
)
|
|
self.play(Blink(receiver))
|
|
self.wait()
|
|
|
|
# Define parity and parity bit
|
|
parity_rects = VGroup(
|
|
SurroundingRectangle(VGroup(block, one_counter_pair[0]), buff=MED_SMALL_BUFF),
|
|
SurroundingRectangle(VGroup(r_block, one_counter_pair[1]), buff=MED_SMALL_BUFF),
|
|
)
|
|
parity_rects[0].set_stroke(BLUE, 2)
|
|
parity_rects[1].set_stroke(RED, 2)
|
|
|
|
parity_labels = VGroup(*[
|
|
TextMobject(
|
|
"Parity: ", word,
|
|
tex_to_color_map={word: color}
|
|
).next_to(rect, UP)
|
|
for word, color, rect in zip(
|
|
["Even", "Odd"],
|
|
[BLUE, RED],
|
|
parity_rects,
|
|
)
|
|
])
|
|
|
|
for label, rect in zip(parity_labels, parity_rects):
|
|
self.play(
|
|
FadeIn(label, DOWN),
|
|
ShowCreation(rect),
|
|
)
|
|
self.play(LaggedStart(*[
|
|
ShowCreationThenFadeOut(Underline(label[0], color=YELLOW))
|
|
for label in parity_labels
|
|
], lag_ratio=0.3))
|
|
self.wait()
|
|
self.play(Blink(sender))
|
|
self.play(Blink(receiver))
|
|
for bit, label, rect in zip([0, 1], parity_labels, parity_rects):
|
|
bit_mob = get_bit_grid(1, 1, bits=[bit])[0]
|
|
bit_mob.match_height(label[1])
|
|
bit_mob.match_color(label[1])
|
|
bit_mob.move_to(label[1], LEFT)
|
|
x_shift = (rect.get_center() - VGroup(label[0], bit_mob).get_center()) * RIGHT
|
|
bit_mob.shift(x_shift)
|
|
self.play(
|
|
label[1].replace, bit_mob, {"stretch": True},
|
|
label[1].set_opacity, 0,
|
|
FadeIn(bit_mob),
|
|
label[0].shift, x_shift,
|
|
)
|
|
label.replace_submobject(1, bit_mob)
|
|
|
|
pb_rect = SurroundingRectangle(block[0], buff=0.05)
|
|
pb_rect.set_stroke(GREEN, 3)
|
|
pb_label = TextMobject("Parity bit")
|
|
pb_label.move_to(pb_rect)
|
|
pb_label.to_edge(LEFT, buff=MED_SMALL_BUFF)
|
|
pb_label.shift(UP)
|
|
pb_label.match_color(pb_rect)
|
|
pb_arrow = Arrow(
|
|
pb_label.get_bottom() + SMALL_BUFF * DOWN,
|
|
pb_rect.get_left(),
|
|
buff=SMALL_BUFF,
|
|
fill_color=GREEN
|
|
)
|
|
|
|
self.play(
|
|
Write(pb_label, run_time=1),
|
|
GrowArrow(pb_arrow),
|
|
ShowCreation(pb_rect),
|
|
)
|
|
for color in [RED, BLUE]:
|
|
self.play(
|
|
toggle_bit_anim(block[0]),
|
|
toggle_bit_anim(parity_labels[0][1], target_color=color),
|
|
parity_rects[0].set_color, color,
|
|
)
|
|
self.wait()
|
|
|
|
parity_descriptors = VGroup(
|
|
parity_rects, parity_labels,
|
|
pb_rect, pb_label, pb_arrow,
|
|
)
|
|
self.play(FadeOut(parity_descriptors, lag_ratio=0.1))
|
|
|
|
# More than 1 error
|
|
new_filpper_indices = [8, 13]
|
|
n_flippers = VGroup(*[n_block[fi] for fi in new_filpper_indices])
|
|
r_flippers = VGroup(*[r_block[fi] for fi in new_filpper_indices])
|
|
for bit in r_flippers:
|
|
toggle_bit(bit)
|
|
new_one_rects_pair = VGroup(*[get_one_rects(b, buff=0.05) for b in blocks])
|
|
new_one_counter_pair = VGroup(*[
|
|
get_ones_counter(label, rect, buff=MED_SMALL_BUFF)
|
|
for label, rect in zip(label_pair, new_one_rects_pair)
|
|
])
|
|
for bit in r_flippers:
|
|
toggle_bit(bit)
|
|
|
|
self.play(
|
|
LaggedStartMap(
|
|
toggle_bit_anim, n_flippers,
|
|
target_color=RED,
|
|
),
|
|
LaggedStartMap(toggle_bit_anim, r_flippers),
|
|
LaggedStart(*map(zap_anim, n_flippers)),
|
|
receiver.change, "maybe",
|
|
FadeOut(one_rects_pair[1]),
|
|
FadeOut(one_counter_pair[1]),
|
|
)
|
|
self.remove(one_rects_pair, one_counter_pair)
|
|
one_rects_pair = new_one_rects_pair
|
|
one_counter_pair = new_one_counter_pair
|
|
self.add(one_rects_pair, one_counter_pair)
|
|
|
|
self.play(
|
|
ShowIncreasingSubsets(one_rects_pair[1])
|
|
)
|
|
|
|
for x in range(2):
|
|
self.play(Blink(receiver))
|
|
self.play(Blink(sender))
|
|
self.wait()
|
|
|
|
# Even number of errors
|
|
self.play(
|
|
FadeOut(one_rects_pair[1]),
|
|
FadeOut(one_counter_pair[1]),
|
|
FadeOut(bangs),
|
|
receiver.change, "pondering", r_block,
|
|
)
|
|
temp_rect = SurroundingRectangle(
|
|
n_flippers[1],
|
|
buff=0.05,
|
|
color=GREEN,
|
|
)
|
|
temp_rect.flip()
|
|
self.play(
|
|
ShowCreationThenFadeOut(temp_rect),
|
|
toggle_bit_anim(n_flippers[1], target_color=WHITE),
|
|
toggle_bit_anim(r_flippers[1]),
|
|
)
|
|
self.wait()
|
|
self.play(Blink(receiver))
|
|
|
|
one_counter_pair.set_opacity(1)
|
|
self.add(one_counter_pair)
|
|
one_rects_pair[1].set_submobjects(
|
|
get_one_rects(r_block, buff=0.05).submobjects
|
|
)
|
|
self.play(
|
|
ShowIncreasingSubsets(one_rects_pair[1])
|
|
)
|
|
self.play(receiver.change, "hooray", r_block)
|
|
self.wait()
|
|
self.play(receiver.change, "erm")
|
|
self.wait()
|
|
|
|
|
|
class ChangeAnywhereToOneBit(Scene):
|
|
CONFIG = {
|
|
"random_seed": 3,
|
|
}
|
|
|
|
def construct(self):
|
|
title = VGroup(
|
|
TextMobject("Change anywhere"),
|
|
Vector(RIGHT),
|
|
TextMobject("One bit of information"),
|
|
)
|
|
title.arrange(RIGHT)
|
|
title.set_width(FRAME_WIDTH - 1)
|
|
title.to_edge(UP)
|
|
self.add(title)
|
|
|
|
grid = get_bit_grid(4, 4)
|
|
grid.set_height(4)
|
|
grid.match_x(title[0])
|
|
grid.set_y(-1)
|
|
self.add(grid)
|
|
|
|
one_rects = get_one_rects(grid)
|
|
self.add(one_rects)
|
|
|
|
parity_words = VGroup(
|
|
TextMobject("Even \\# of 1s", color=BLUE_B),
|
|
TextMobject("Odd \\# of 1s", color=TEAL_D),
|
|
)
|
|
parity_words.scale(1.5)
|
|
parity_words.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=RIGHT)
|
|
parity_words.match_x(title[2])
|
|
parity_words.match_y(grid)
|
|
self.add(parity_words)
|
|
|
|
def get_parity_rect(n, words=parity_words):
|
|
return SurroundingRectangle(words[n % 2])
|
|
|
|
p_rect = get_parity_rect(len(one_rects))
|
|
self.add(p_rect)
|
|
|
|
# Random changes
|
|
for x in range(10):
|
|
bit = random.choice(grid)
|
|
self.play(toggle_bit_anim(bit))
|
|
one_rects.set_submobjects(get_one_rects(grid))
|
|
p_rect.become(get_parity_rect(len(one_rects)))
|
|
self.wait()
|
|
|
|
|
|
class OddNumberCountTo101(Scene):
|
|
def construct(self):
|
|
group = VGroup(
|
|
*[Integer(2 * n + 1) for n in range(1, 50)],
|
|
)
|
|
group.set_color(RED)
|
|
group.scale(2)
|
|
for mob in group[:2]:
|
|
self.add(mob)
|
|
self.wait()
|
|
self.remove(mob)
|
|
self.play(ShowSubmobjectsOneByOne(group[2:]), run_time=6, rate_func=bezier([0, 0, 1, 1]))
|
|
self.wait()
|
|
|
|
|
|
class ComplainAboutParityCheckWeakness(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.embed()
|
|
|
|
self.screen.set_fill(BLACK, 1)
|
|
self.add(self.screen)
|
|
|
|
self.play(
|
|
PiCreatureSays(
|
|
self.students[1],
|
|
"Wait, it fails\\\\for two flips?",
|
|
target_mode="sassy",
|
|
bubble_kwargs={
|
|
"height": 3,
|
|
"width": 3,
|
|
}
|
|
),
|
|
self.teacher.change, "guilty",
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
PiCreatureSays(
|
|
self.students[2], "Weak!",
|
|
target_mode="angry",
|
|
bubble_kwargs={"direction": LEFT}
|
|
),
|
|
self.students[0].change, "hesitant"
|
|
)
|
|
self.wait()
|
|
self.play(self.teacher.change, "tease")
|
|
self.wait()
|
|
self.change_student_modes(
|
|
"thinking", "pondering", "pondering",
|
|
look_at_arg=self.screen,
|
|
added_anims=[
|
|
FadeOut(self.students[1].bubble),
|
|
FadeOut(self.students[1].bubble.content),
|
|
FadeOut(self.students[2].bubble),
|
|
FadeOut(self.students[2].bubble.content),
|
|
]
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class ArrayOfValidMessages(Scene):
|
|
def construct(self):
|
|
# Messages
|
|
title = TextMobject("All possible messages")
|
|
title.to_edge(UP)
|
|
nr = 22
|
|
nc = 46
|
|
dots = VGroup(*[Dot() for x in range(nr * nc)])
|
|
dots.arrange_in_grid(nr, nc)
|
|
dots.set_color(GREY_C)
|
|
dots.set_height(6)
|
|
dots.to_edge(DOWN)
|
|
shuffled_dots = dots.copy()
|
|
shuffled_dots.shuffle()
|
|
|
|
self.add(title)
|
|
self.play(Write(shuffled_dots, remover=True, run_time=6, lag_ratio=5 / len(dots)))
|
|
self.add(dots)
|
|
self.wait()
|
|
|
|
# Valid messages
|
|
subset = TexMobject("\\subset")
|
|
subset.set_height(0.4)
|
|
subset.to_edge(UP)
|
|
valid_label = TextMobject("Valid messages")
|
|
valid_label.set_color(YELLOW)
|
|
valid_label.next_to(subset, LEFT)
|
|
|
|
valid_dots = VGroup()
|
|
for row in range(0, nr, 3):
|
|
for col in range(0, nc, 3):
|
|
valid_dot = dots[row * nc + col]
|
|
valid_dot.generate_target()
|
|
valid_dot.target.scale(2)
|
|
valid_dot.target.set_color(YELLOW)
|
|
valid_dots.add(valid_dot)
|
|
|
|
self.play(
|
|
LaggedStartMap(MoveToTarget, valid_dots, run_time=3),
|
|
Write(subset),
|
|
FadeIn(valid_label, LEFT),
|
|
title.next_to, subset, RIGHT,
|
|
)
|
|
self.wait()
|
|
|
|
# Words analogy
|
|
example_words = VGroup(
|
|
TextMobject("Hello world", color=YELLOW),
|
|
TextMobject("Helho world", color=GREY_B),
|
|
)
|
|
example_words.scale(1.25)
|
|
index = 12 * nc + 21
|
|
example_dots = VGroup(dots[index], dots[index + 1]).copy()
|
|
|
|
example_groups = VGroup()
|
|
for word, dot in zip(example_words, example_dots):
|
|
arrow = Vector(0.7 * DOWN)
|
|
arrow.next_to(dot, UP, SMALL_BUFF)
|
|
word.next_to(arrow, UP, SMALL_BUFF)
|
|
example_group = VGroup(word, arrow, dot)
|
|
example_group.unlock_triangulation()
|
|
example_groups.add(example_group)
|
|
|
|
fade_rect = SurroundingRectangle(dots)
|
|
fade_rect.set_stroke(BLACK, 0)
|
|
fade_rect.set_fill(BLACK, 0.7)
|
|
|
|
self.play(
|
|
FadeIn(fade_rect),
|
|
FadeIn(example_groups[0])
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
zap_anim(example_words[0][0][3:5]),
|
|
Transform(*example_groups),
|
|
)
|
|
self.wait()
|
|
self.play(FadeOut(example_groups[0]), FadeOut(fade_rect))
|
|
|
|
# Corrections
|
|
valid_centers = [vd.get_center() for vd in valid_dots]
|
|
lines = VGroup()
|
|
for dot in dots:
|
|
dc = dot.get_center()
|
|
norms = [get_norm(dc - vc) for vc in valid_centers]
|
|
line = Line(dc, valid_centers[np.argmin(norms)])
|
|
line.set_stroke(WHITE, 1)
|
|
lines.add(line)
|
|
|
|
shuffled_lines = VGroup(*lines)
|
|
shuffled_lines.shuffle()
|
|
|
|
self.play(ShowCreation(shuffled_lines, lag_ratio=10 / len(lines), run_time=5))
|
|
self.wait()
|
|
|
|
# Mandering path between valid messages
|
|
self.add(fade_rect, valid_dots)
|
|
self.play(FadeIn(fade_rect))
|
|
|
|
path = [RIGHT, UP, UP, RIGHT, RIGHT, RIGHT, UP, UP, RIGHT, RIGHT, DOWN]
|
|
dist = get_norm(dots[1].get_center() - dots[0].get_center())
|
|
curr = dots[index].get_center()
|
|
arrows = VGroup()
|
|
for vect in path:
|
|
new = curr + dist * vect
|
|
arrows.add(Arrow(curr, new, buff=0, fill_color=RED))
|
|
curr = new
|
|
|
|
for arrow in arrows:
|
|
self.play(GrowArrow(arrow), run_time=0.5)
|
|
self.wait()
|
|
|
|
|
|
class RobustForLessThanNErrors(Scene):
|
|
def construct(self):
|
|
words = TextMobject(
|
|
"Robust for ", "$\\le N$", " errors",
|
|
)
|
|
words.to_edge(UP)
|
|
words[1].set_color(YELLOW)
|
|
words[2].shift(0.15 * RIGHT)
|
|
N = words[1][-1]
|
|
|
|
num = Integer(1)
|
|
num.set_color(YELLOW)
|
|
num.move_to(N, LEFT)
|
|
num.set_value(1)
|
|
|
|
self.play(Write(words, run_time=2))
|
|
self.wait()
|
|
self.remove(N)
|
|
self.add(num)
|
|
self.play(ChangeDecimalToValue(num, 20, run_time=3))
|
|
self.remove(num)
|
|
self.add(N)
|
|
self.wait()
|
|
|
|
|
|
class TwentyQuestions(Scene):
|
|
def construct(self):
|
|
# Hamming's insight
|
|
bits = [
|
|
1, 0, 0, 1,
|
|
0, 1, 0, 0,
|
|
0, 1, 0, 1,
|
|
1, 0, 1, 1,
|
|
]
|
|
block = get_bit_grid(4, 4, bits=bits)
|
|
|
|
hamming = ImageMobject("Richard_Hamming")
|
|
hamming.set_height(3)
|
|
hamming.to_corner(DL)
|
|
|
|
bulb = Lightbulb()
|
|
bulb.next_to(hamming.get_corner(UR), UP)
|
|
|
|
self.add(block)
|
|
self.play(FadeIn(hamming, 0.5 * RIGHT))
|
|
self.play(Write(bulb, run_time=1, stroke_width=5))
|
|
self.wait()
|
|
|
|
# Preview parity checks
|
|
parity_word = TextMobject("Parity check")
|
|
parity_word.set_height(0.7)
|
|
parity_word.set_color(BLUE)
|
|
parity_word.move_to(block, UP)
|
|
parity_word.set_opacity(0)
|
|
|
|
back_rects = VGroup(*[
|
|
SurroundingRectangle(bit, buff=MED_SMALL_BUFF / 2).scale(0)
|
|
for bit in get_bit_n_subgroup(block, 0)
|
|
])
|
|
back_rects.set_stroke(width=0)
|
|
back_rects.set_fill(BLUE_E, 1)
|
|
|
|
last_one_rects = VMobject()
|
|
|
|
self.add(back_rects, block)
|
|
|
|
for n, vect in zip(it.count(), [UP, UP, RIGHT, RIGHT]):
|
|
block.generate_target()
|
|
block.target.set_color(WHITE)
|
|
color_group = get_bit_n_subgroup(block.target, n)
|
|
color_group.set_color(BLUE)
|
|
one_rects = get_one_rects(color_group)
|
|
|
|
rects = VGroup(*[
|
|
SurroundingRectangle(bit, buff=MED_SMALL_BUFF / 2)
|
|
for bit in color_group
|
|
])
|
|
rects.match_style(back_rects)
|
|
|
|
self.play(
|
|
FadeOut(last_one_rects),
|
|
MoveToTarget(block),
|
|
parity_word.next_to, color_group, vect, MED_LARGE_BUFF,
|
|
parity_word.set_opacity, 1,
|
|
Transform(back_rects, rects),
|
|
)
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
last_one_rects = one_rects
|
|
|
|
self.play(
|
|
FadeOut(back_rects),
|
|
FadeOut(last_one_rects),
|
|
FadeOut(parity_word),
|
|
block.set_color, WHITE,
|
|
)
|
|
|
|
# Expand to grid
|
|
block.generate_target()
|
|
block.target.space_out_submobjects(1.5)
|
|
block.target.to_edge(LEFT, buff=LARGE_BUFF)
|
|
|
|
boxes = get_bit_grid_boxes(block.target)
|
|
p_labels = get_grid_position_labels(boxes)
|
|
p_labels.set_color(GREY_C)
|
|
|
|
self.play(
|
|
FadeOut(hamming, LEFT),
|
|
FadeOut(bulb, 1.5 * LEFT),
|
|
MoveToTarget(block),
|
|
)
|
|
self.play(
|
|
ShowCreation(boxes, lag_ratio=0.2),
|
|
FadeIn(p_labels, lag_ratio=0.2),
|
|
)
|
|
|
|
# Set up questions
|
|
q_labels = VGroup(*[
|
|
TextMobject(f"Q{n}:")
|
|
for n in range(1, 5)
|
|
])
|
|
q_labels.set_height(0.5)
|
|
q_labels.arrange(DOWN, buff=1.25)
|
|
q_labels.next_to(boxes, RIGHT, buff=2)
|
|
|
|
questions = VGroup()
|
|
for n, q_label in enumerate(q_labels):
|
|
m_grids = VGroup(boxes.copy(), boxes.copy())
|
|
m_grids.set_height(1)
|
|
m_grids.set_width(1, stretch=True)
|
|
colors = [GREY_BROWN, BLUE]
|
|
for k, m_grid in enumerate(m_grids):
|
|
get_bit_n_subgroup(m_grid, n, k).set_fill(colors[k], 0.75)
|
|
vs = TextMobject("or")
|
|
question = VGroup(m_grids[0], vs, m_grids[1])
|
|
question.arrange(RIGHT)
|
|
question.next_to(q_label, RIGHT, buff=MED_SMALL_BUFF)
|
|
question.add_to_back(q_label)
|
|
questions.add(question)
|
|
|
|
questions.to_edge(RIGHT, buff=MED_LARGE_BUFF)
|
|
|
|
for question in questions:
|
|
question.save_state()
|
|
q_center = question.get_center()
|
|
for mob in question:
|
|
if mob is not question[0]:
|
|
mob.move_to(q_center)
|
|
mob.set_fill(opacity=0)
|
|
mob.set_stroke(width=0)
|
|
|
|
self.play(
|
|
LaggedStartMap(Restore, questions, lag_ratio=0.25),
|
|
run_time=3,
|
|
)
|
|
self.wait()
|
|
|
|
questions.save_state()
|
|
|
|
# Focus on question 1
|
|
h_rects = boxes.copy()
|
|
h_rects.set_fill(BLUE, 0.5)
|
|
h_groups = VGroup(*[
|
|
get_bit_n_subgroup(h_rects, n)
|
|
for n in range(4)
|
|
])
|
|
|
|
pc_words = TextMobject("Parity check\\\\these 8 bits")
|
|
pc_words.next_to(boxes, RIGHT, aligned_edge=UP)
|
|
pc_words.shift(DOWN)
|
|
pc_words.set_color(BLUE)
|
|
|
|
block.save_state()
|
|
self.add(h_groups[0], block, p_labels)
|
|
self.play(
|
|
questions[1:].fade, 0.9,
|
|
FadeIn(h_groups[0], lag_ratio=0.3, run_time=3),
|
|
get_bit_n_subgroup(block, 0, 0).fade, 0.9,
|
|
Write(pc_words, run_time=1)
|
|
)
|
|
self.wait()
|
|
|
|
# Scan first group
|
|
def get_sub_scanim(n, boxes=boxes, block=block, **kwargs):
|
|
return scan_anim(
|
|
boxes.get_corner(DR) + UR,
|
|
get_bit_n_subgroup(block, n),
|
|
lag_factor=0.5,
|
|
run_time=2,
|
|
**kwargs
|
|
)
|
|
|
|
scanim = get_sub_scanim(0)
|
|
robot = scanim.mobject[-1]
|
|
bangs = TexMobject("!!!")
|
|
bangs.set_color(RED)
|
|
bangs.next_to(robot, UR, buff=SMALL_BUFF)
|
|
check = Checkmark()
|
|
check.next_to(robot, UR, buff=SMALL_BUFF)
|
|
for mob in (bangs, check):
|
|
mob.scale(1.5, about_edge=DL)
|
|
|
|
q1_rect = SurroundingRectangle(questions[0][3])
|
|
|
|
self.play(scanim)
|
|
self.play(FadeIn(bangs, 0.1 * DOWN, lag_ratio=0.2))
|
|
self.wait()
|
|
self.play(ShowCreation(q1_rect))
|
|
self.wait()
|
|
self.play(
|
|
get_sub_scanim(0, show_robot=False),
|
|
FadeOut(bangs)
|
|
)
|
|
self.play(FadeIn(check, 0.2 * DOWN))
|
|
self.wait()
|
|
self.play(
|
|
q1_rect.move_to, questions[0][1],
|
|
)
|
|
self.play(
|
|
LaggedStartMap(
|
|
ShowCreationThenFadeOut,
|
|
get_bit_n_subgroup(h_rects, 0, 0).copy().set_fill(GREY_BROWN, 0.5)
|
|
)
|
|
)
|
|
self.wait()
|
|
self.play(LaggedStart(*map(FadeOut, [
|
|
robot, check, q1_rect,
|
|
])))
|
|
|
|
# Comment over in pi creature scene
|
|
pass
|
|
|
|
# Highlight parity bit
|
|
frame = self.camera.frame
|
|
frame.save_state()
|
|
|
|
ecc_rects = VGroup(*[
|
|
h_group[0]
|
|
for h_group in h_groups
|
|
])
|
|
pb_label = TextMobject("Parity bit")
|
|
pb_label.next_to(ecc_rects[0], UP, MED_LARGE_BUFF)
|
|
pb_arrow = Arrow(
|
|
pb_label.get_bottom() + MED_SMALL_BUFF * LEFT,
|
|
block[1].get_top(),
|
|
buff=0.1
|
|
)
|
|
pb_label.set_color(GREEN)
|
|
pb_arrow.set_color(GREEN)
|
|
pb_arrow.set_stroke(BLACK, 5, background=True)
|
|
|
|
h_groups[0].remove(ecc_rects[0])
|
|
self.add(ecc_rects[0], block, p_labels)
|
|
self.play(
|
|
frame.set_height, 9, {"about_edge": DOWN},
|
|
FadeIn(pb_label, 0.5 * DOWN),
|
|
GrowArrow(pb_arrow),
|
|
ecc_rects[0].set_color, GREEN,
|
|
)
|
|
self.wait()
|
|
self.play(ShowCreationThenFadeAround(p_labels[1]))
|
|
self.wait()
|
|
|
|
one_rects = get_one_rects(get_bit_n_subgroup(block, 0))
|
|
counter = get_ones_counter(boxes[-2:], one_rects)
|
|
counter.scale(0.5)
|
|
self.add(counter)
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
self.wait()
|
|
self.play(toggle_bit_anim(block[1]))
|
|
one_rects.set_submobjects(
|
|
get_one_rects(get_bit_n_subgroup(block, 0)).submobjects
|
|
)
|
|
self.wait()
|
|
|
|
# Back to all questions
|
|
self.play(
|
|
LaggedStart(*map(FadeOut, [
|
|
one_rects, counter,
|
|
pb_arrow, pb_label,
|
|
pc_words, h_groups[0]
|
|
])),
|
|
frame.restore,
|
|
)
|
|
toggle_bit(block.saved_state[1]) # Dumb hack
|
|
self.play(
|
|
questions.restore,
|
|
block.restore,
|
|
)
|
|
|
|
# Focus on question 2
|
|
self.play(
|
|
questions[0].fade, 0.9,
|
|
questions[2:].fade, 0.9,
|
|
get_bit_n_subgroup(block, 1, 0).fade, 0.9,
|
|
ecc_rects[0].fade, 0.5,
|
|
)
|
|
self.add(h_groups[1], block, p_labels)
|
|
self.play(FadeIn(h_groups[1], lag_ratio=0.2, run_time=2))
|
|
|
|
pb_label.next_to(boxes[2], UP, SMALL_BUFF)
|
|
h_groups[1].remove(ecc_rects[1])
|
|
self.add(ecc_rects[1], block, p_labels)
|
|
self.play(
|
|
FadeIn(pb_label, 0.25 * LEFT),
|
|
ecc_rects[1].set_color, GREEN,
|
|
)
|
|
self.wait()
|
|
|
|
# Apply second parity check
|
|
one_rects = get_one_rects(get_bit_n_subgroup(block, 1))
|
|
counter = get_ones_counter(boxes[-2:], one_rects)
|
|
counter.scale(0.5)
|
|
self.add(counter)
|
|
self.play(
|
|
ShowIncreasingSubsets(one_rects)
|
|
)
|
|
self.wait()
|
|
self.play(LaggedStart(*map(FadeOut, [
|
|
one_rects, counter, pb_label,
|
|
])))
|
|
|
|
# Find error in right half
|
|
scanim = get_sub_scanim(1)
|
|
robot = scanim.mobject[-1]
|
|
self.play(
|
|
zap_anim(block[6]),
|
|
toggle_bit_anim(block[6], target_color=RED)
|
|
)
|
|
self.play(scanim)
|
|
self.play(FadeIn(bangs, 0.1 * DOWN, lag_ratio=0.1))
|
|
self.wait()
|
|
|
|
q2_rect = q1_rect.copy()
|
|
q2_rect.move_to(questions[1][3])
|
|
self.play(ShowCreation(q2_rect))
|
|
self.wait()
|
|
|
|
self.play(
|
|
toggle_bit_anim(block[6], target_color=WHITE),
|
|
FadeOut(bangs),
|
|
)
|
|
self.play(get_sub_scanim(1, show_robot=False))
|
|
self.play(FadeIn(check, 0.2 * DOWN))
|
|
self.wait()
|
|
self.play(q2_rect.move_to, questions[1][1])
|
|
self.play(
|
|
ShowCreationThenFadeOut(
|
|
get_bit_n_subgroup(boxes.copy(), 1, 0).set_fill(GREY_BROWN, 0.5),
|
|
lag_ratio=0.5,
|
|
run_time=2,
|
|
)
|
|
)
|
|
self.wait()
|
|
|
|
self.play(
|
|
FadeOut(h_groups[1]),
|
|
FadeOut(robot),
|
|
FadeOut(check),
|
|
FadeOut(q2_rect),
|
|
block.restore,
|
|
Transform(questions[0], questions.saved_state[0]),
|
|
ecc_rects[0].set_fill, GREEN, 0.5,
|
|
)
|
|
|
|
# Mention two errors?
|
|
|
|
# How to use Q1 with Q2
|
|
q1_rect.move_to(questions[0][3])
|
|
q2_rect.move_to(questions[1][3])
|
|
q1_highlight, q2_highlight = [
|
|
get_bit_n_subgroup(h_rects, n).copy().set_fill(BLUE, 0.5)
|
|
for n in [0, 1]
|
|
]
|
|
|
|
q2_highlight.set_fill(opacity=0)
|
|
q2_highlight.set_stroke(YELLOW, 7)
|
|
|
|
self.add(q1_highlight, block, p_labels)
|
|
self.play(
|
|
FadeIn(q1_highlight, lag_ratio=0.2),
|
|
ShowCreation(q1_rect),
|
|
get_bit_n_subgroup(block, 0, 0).fade, 0.9,
|
|
*map(FadeOut, ecc_rects[:2]),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(q2_highlight, lag_ratio=0.2),
|
|
ShowCreation(q2_rect),
|
|
get_bit_n_subgroup(block, 1, 0)[1::2].fade, 0.9,
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
q1_rect.move_to, questions[0][1],
|
|
q1_highlight.move_to, boxes, LEFT,
|
|
q1_highlight.set_fill, GREY_BROWN,
|
|
block.restore,
|
|
FadeOut(q2_highlight),
|
|
)
|
|
self.play(get_bit_n_subgroup(block, 0).fade, 0.9)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(q2_highlight, lag_ratio=0.2),
|
|
block[::4].fade, 0.9,
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
q1_highlight.move_to, boxes, RIGHT,
|
|
q1_highlight.set_fill, BLUE,
|
|
q2_highlight.move_to, boxes, LEFT,
|
|
block.restore,
|
|
q1_rect.move_to, questions[0][3],
|
|
q2_rect.move_to, questions[1][1],
|
|
)
|
|
self.play(
|
|
block[::4].fade, 0.9,
|
|
get_bit_n_subgroup(block, 1).fade, 0.9,
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
q1_rect.move_to, questions[0][1],
|
|
q1_highlight.move_to, boxes, LEFT,
|
|
q1_highlight.set_fill, GREY_BROWN,
|
|
block[1::4].fade, 0.9,
|
|
Transform(block[0::4], block.saved_state[0::4]),
|
|
)
|
|
self.wait()
|
|
|
|
morty = Mortimer(height=2)
|
|
morty.flip()
|
|
morty.next_to(boxes, RIGHT, buff=0.5, aligned_edge=DOWN)
|
|
words = TextMobject("Or no error\\\\at all!")
|
|
words.next_to(morty, UP)
|
|
|
|
self.play(
|
|
VFadeIn(morty),
|
|
morty.change, "shruggie", questions[0],
|
|
FadeIn(words, DOWN)
|
|
)
|
|
self.play(Blink(morty))
|
|
self.wait(2)
|
|
self.play(
|
|
LaggedStart(*map(FadeOut, [
|
|
q1_highlight, q2_highlight[1::2],
|
|
words, morty,
|
|
])),
|
|
block.restore,
|
|
)
|
|
self.wait()
|
|
|
|
column_highlight = q2_highlight[0::2]
|
|
|
|
# Choosing a column
|
|
self.play(
|
|
q1_rect.move_to, questions[0][3],
|
|
column_highlight.move_to, boxes[1], UP,
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
q1_rect.move_to, questions[0][1],
|
|
q2_rect.move_to, questions[1][3],
|
|
column_highlight.move_to, boxes[2], UP,
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
q1_rect.move_to, questions[0][3],
|
|
column_highlight.move_to, boxes[3], UP,
|
|
)
|
|
self.wait()
|
|
|
|
# Bring back the parity highlights
|
|
self.add(*ecc_rects[:2], block, p_labels)
|
|
self.play(
|
|
LaggedStart(*map(FadeOut, [
|
|
column_highlight, q1_rect, q2_rect
|
|
])),
|
|
*map(FadeIn, ecc_rects[:2]),
|
|
)
|
|
self.wait()
|
|
for n in [2, 3]:
|
|
self.play(Transform(questions[n], questions.saved_state[n]))
|
|
self.wait()
|
|
|
|
# Question 3
|
|
self.add(h_groups[2], block, p_labels)
|
|
self.play(
|
|
get_bit_n_subgroup(block, 2, 0).fade, 0.9,
|
|
FadeIn(h_groups[2], lag_ratio=0.2, run_time=2),
|
|
questions[:2].fade, 0.9,
|
|
questions[3].fade, 0.9,
|
|
)
|
|
self.wait()
|
|
|
|
h_groups[2].remove(ecc_rects[2])
|
|
self.add(ecc_rects[2], block, p_labels)
|
|
self.play(
|
|
ecc_rects[2].set_color, GREEN,
|
|
ShowCreationThenFadeOut(SurroundingRectangle(boxes[4], buff=0))
|
|
)
|
|
self.wait()
|
|
|
|
one_rects = get_one_rects(get_bit_n_subgroup(block, 2))
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
self.wait()
|
|
temp_check = Checkmark()
|
|
temp_check.set_height(0.4)
|
|
temp_check.next_to(block[4], UL, buff=0)
|
|
self.play(Write(temp_check))
|
|
self.play(FadeOut(temp_check))
|
|
self.play(FadeOut(one_rects))
|
|
self.play(
|
|
block.restore,
|
|
FadeOut(h_groups[2]),
|
|
)
|
|
self.wait()
|
|
|
|
# Question 4
|
|
self.add(h_groups[3], block, p_labels)
|
|
self.play(
|
|
questions[2].fade, 0.9,
|
|
Transform(questions[3], questions.saved_state[3]),
|
|
get_bit_n_subgroup(block, 3, 0).fade, 0.9,
|
|
FadeIn(h_groups[3], lag_ratio=0.2, run_time=2),
|
|
)
|
|
self.wait()
|
|
h_groups[3].remove(ecc_rects[3])
|
|
self.add(ecc_rects[3], block, p_labels)
|
|
self.play(
|
|
ecc_rects[3].set_color, GREEN,
|
|
ShowCreationThenFadeOut(SurroundingRectangle(boxes[8], buff=0))
|
|
)
|
|
|
|
one_rects = get_one_rects(get_bit_n_subgroup(block, 3))
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
self.wait()
|
|
self.play(toggle_bit_anim(block[8]))
|
|
toggle_bit(block.saved_state[8])
|
|
one_rects.set_submobjects(get_one_rects(get_bit_n_subgroup(block, 3)))
|
|
self.wait()
|
|
self.play(FadeOut(one_rects, lag_ratio=0.2))
|
|
self.play(
|
|
block.restore,
|
|
questions.restore,
|
|
FadeOut(h_groups[3], lag_ratio=0.2),
|
|
)
|
|
self.wait()
|
|
|
|
# Point out rolls of questions
|
|
braces = VGroup(
|
|
Brace(questions[:2], LEFT),
|
|
Brace(questions[2:], LEFT),
|
|
)
|
|
for brace, text in zip(braces, ["column", "row"]):
|
|
brace.words = brace.get_text(f"Which\\\\{text}?")
|
|
|
|
for brace in braces:
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
FadeIn(brace.words, 0.25 * RIGHT)
|
|
)
|
|
self.wait()
|
|
|
|
# Example with error at 3
|
|
q_rects = VGroup()
|
|
for question, answer in zip(questions, [1, 1, 0, 0]):
|
|
q_rects.add(SurroundingRectangle(question[1 + 2 * answer]))
|
|
|
|
self.play(
|
|
zap_anim(block[3]),
|
|
toggle_bit_anim(block[3], target_color=RED),
|
|
)
|
|
self.wait()
|
|
|
|
target_square = SurroundingRectangle(boxes[3::4], buff=0, stroke_width=5)
|
|
for n in range(4):
|
|
highlight = get_bit_n_subgroup(h_rects.copy(), n)
|
|
one_rects = get_one_rects(get_bit_n_subgroup(block, n))
|
|
self.add(highlight, block, p_labels)
|
|
self.play(
|
|
FadeIn(highlight),
|
|
ShowCreation(q_rects[n]),
|
|
ShowIncreasingSubsets(one_rects),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(highlight),
|
|
FadeOut(one_rects),
|
|
)
|
|
if n == 1:
|
|
self.play(FadeIn(target_square))
|
|
elif n == 3:
|
|
self.play(target_square.replace, boxes[3], {"stretch": True})
|
|
self.wait()
|
|
self.play(
|
|
toggle_bit_anim(block[3], target_color=WHITE)
|
|
)
|
|
self.wait()
|
|
|
|
# Binary counting
|
|
for n in range(1, 16):
|
|
target_square.generate_target()
|
|
target_square.target.move_to(boxes[n])
|
|
for k, q_rect in enumerate(q_rects):
|
|
index = 3 if((1 << k) & n) else 1
|
|
q_rect.generate_target()
|
|
q_rect.target.move_to(questions[k][index])
|
|
self.play(*map(MoveToTarget, [target_square, *q_rects]), run_time=0.5)
|
|
self.wait()
|
|
|
|
# Bit 0
|
|
for q_rect, question in zip(q_rects, questions):
|
|
q_rect.generate_target()
|
|
q_rect.target.move_to(question[1])
|
|
|
|
self.play(
|
|
target_square.move_to, boxes[0],
|
|
LaggedStartMap(MoveToTarget, q_rects),
|
|
)
|
|
self.wait()
|
|
for n in range(4):
|
|
group = get_bit_n_subgroup(h_rects.copy(), n)
|
|
self.play(FadeIn(group, lag_ratio=0.2))
|
|
self.wait(0.5)
|
|
self.play(FadeOut(group))
|
|
self.wait()
|
|
|
|
randy = Randolph(height=2)
|
|
randy.next_to(boxes, RIGHT, buff=MED_LARGE_BUFF, aligned_edge=DOWN)
|
|
ne_word = TextMobject("No error?")
|
|
ne_word.next_to(randy, UP, buff=MED_LARGE_BUFF)
|
|
ne_word.shift(0.2 * RIGHT)
|
|
ez_word = TextMobject("or error\\\\at bit 0?")
|
|
ez_word.set_color(RED)
|
|
ez_word.move_to(ne_word, DOWN)
|
|
|
|
self.play(
|
|
VFadeIn(randy),
|
|
randy.change, 'pondering', questions,
|
|
FadeOut(braces),
|
|
*[
|
|
FadeOut(brace.words)
|
|
for brace in braces
|
|
]
|
|
)
|
|
self.play(FadeIn(ne_word, 0.5 * DOWN))
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
self.play(
|
|
ne_word.next_to, ez_word, UP, MED_LARGE_BUFF,
|
|
FadeIn(ez_word, 0.25 * DOWN),
|
|
randy.change, "confused", target_square,
|
|
)
|
|
self.play(Blink(randy))
|
|
for x in range(2):
|
|
self.play(toggle_bit_anim(block[0]))
|
|
self.play(randy.change, "pondering", questions)
|
|
|
|
# Count through again
|
|
for n in [*range(0, 16), 0]:
|
|
target_square.move_to(boxes[n])
|
|
for k, q_rect in enumerate(q_rects):
|
|
index = 3 if((1 << k) & n) else 1
|
|
q_rect.move_to(questions[k][index])
|
|
randy.look_at(target_square)
|
|
self.wait(0.5)
|
|
|
|
# Highlight no error
|
|
ne_rect = SurroundingRectangle(ne_word)
|
|
ne_rect.set_stroke(GREY_B, 2)
|
|
ne_comment = TextMobject("17th outcome")
|
|
ne_comment.set_color(GREY_B)
|
|
ne_comment.next_to(ne_rect, UP)
|
|
|
|
self.play(
|
|
ShowCreation(ne_rect),
|
|
FadeIn(ne_comment, 0.25 * DOWN),
|
|
randy.change, "maybe", ne_comment,
|
|
)
|
|
for x in range(2):
|
|
self.play(Blink(randy))
|
|
self.wait(2)
|
|
|
|
# Nix bit 0
|
|
zero_rect = SurroundingRectangle(boxes[0], buff=0)
|
|
zero_rect.set_fill(BLACK, 1)
|
|
zero_rect.set_stroke(BLACK, 0)
|
|
zero_rect.scale(0.98)
|
|
|
|
self.play(
|
|
GrowFromCenter(zero_rect),
|
|
boxes[0].set_stroke, {"width": 0},
|
|
FadeOut(target_square),
|
|
randy.change, "raise_left_hand", zero_rect,
|
|
)
|
|
ne_word.generate_target()
|
|
ne_word.target[0][-1].set_opacity(0)
|
|
ne_word.target.next_to(randy, UP, MED_LARGE_BUFF)
|
|
ne_word.target.set_color(YELLOW)
|
|
self.play(
|
|
MoveToTarget(ne_word),
|
|
LaggedStart(*map(FadeOut, [ne_rect, ne_comment, ez_word]))
|
|
)
|
|
for n in range(4):
|
|
highlight = get_bit_n_subgroup(h_rects.copy(), n)
|
|
one_rects = get_one_rects(get_bit_n_subgroup(block, n))
|
|
self.add(highlight, block, p_labels, zero_rect, one_rects)
|
|
self.wait()
|
|
self.remove(highlight, one_rects)
|
|
|
|
# (15, 11) setup
|
|
stat_words = VGroup(
|
|
TextMobject("15", "-bit block"),
|
|
TextMobject("11", " bits of\\\\message", alignment=""),
|
|
TextMobject("4 bits of\\\\redundancy", alignment=""),
|
|
)
|
|
stat_words.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT)
|
|
stat_words[0].set_color(YELLOW)
|
|
stat_words[2].set_color(GREEN)
|
|
stat_words.next_to(boxes, RIGHT, buff=MED_LARGE_BUFF, aligned_edge=UP)
|
|
stat_words.shift(0.5 * DOWN)
|
|
|
|
self.play(
|
|
LaggedStart(*map(FadeOut, [*q_rects, ne_word])),
|
|
randy.change, "tease", stat_words,
|
|
FadeIn(stat_words[0], 0.5 * LEFT)
|
|
)
|
|
self.play(Blink(randy))
|
|
self.play(
|
|
FadeIn(stat_words[1], 0.5 * LEFT),
|
|
LaggedStart(*[
|
|
toggle_bit_anim(bit)
|
|
for i, bit in enumerate(block)
|
|
if i not in [0, 1, 2, 4, 8]
|
|
])
|
|
)
|
|
self.play(
|
|
LaggedStart(*[
|
|
toggle_bit_anim(bit)
|
|
for i, bit in enumerate(block)
|
|
if i not in [0, 1, 2, 4, 8]
|
|
])
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(stat_words[2], 0.5 * LEFT),
|
|
LaggedStart(*map(ShowCreationThenFadeAround, [
|
|
block[2**n]
|
|
for n in range(4)
|
|
]))
|
|
)
|
|
self.play(Blink(randy))
|
|
|
|
code_name = TextMobject("(", "15", ", ", "11", ")", " Hamming code")
|
|
code_name.set_height(0.8)
|
|
code_name.to_edge(UP)
|
|
code_name.shift(UP)
|
|
|
|
self.play(
|
|
ApplyMethod(frame.set_height, 9, {"about_edge": DOWN}, run_time=2),
|
|
TransformFromCopy(stat_words[0][0], code_name[1]),
|
|
TransformFromCopy(stat_words[1][0], code_name[3]),
|
|
LaggedStart(*map(FadeIn, [
|
|
code_name[i] for i in [0, 2, 4, 5]
|
|
])),
|
|
)
|
|
self.wait()
|
|
|
|
# Bring back bit zero
|
|
self.play(
|
|
LaggedStart(*map(FadeOut, [
|
|
*stat_words, randy,
|
|
*questions, code_name,
|
|
])),
|
|
ApplyMethod(frame.match_x, block),
|
|
run_time=2
|
|
)
|
|
|
|
old_zero_rect = zero_rect
|
|
zero_rect = old_zero_rect.copy()
|
|
zero_rect.set_stroke(YELLOW, 2)
|
|
zero_rect.set_fill(YELLOW, 0.5)
|
|
zero_rect.save_state()
|
|
zero_rect.stretch(0, 1, about_edge=UP)
|
|
zero_words = TextMobject("Can we put this bit to work?")
|
|
zero_words.set_height(0.7)
|
|
zero_words.next_to(zero_rect, UP, MED_LARGE_BUFF)
|
|
zero_words.match_x(block)
|
|
zero_words.set_color(YELLOW)
|
|
|
|
self.add(zero_rect, block, p_labels)
|
|
block[0].set_opacity(0)
|
|
self.play(
|
|
FadeOut(old_zero_rect),
|
|
Restore(zero_rect),
|
|
Write(zero_words)
|
|
)
|
|
self.wait()
|
|
|
|
# Parity check the whole block
|
|
pc_words = TextMobject("Parity check\\\\ \\emph{the whole block}")
|
|
pc_words.set_height(1.2)
|
|
pc_words.next_to(boxes, LEFT, buff=MED_LARGE_BUFF)
|
|
|
|
one_rects = get_one_rects(block)
|
|
counter = get_ones_counter(boxes[-2:], one_rects)
|
|
counter.scale(0.5)
|
|
|
|
self.play(
|
|
Write(pc_words, run_time=1),
|
|
ShowCreationThenFadeOut(h_rects.copy()),
|
|
)
|
|
self.wait()
|
|
ecc_bits = VGroup(*[block[2**n] for n in range(4)])
|
|
for x in range(2):
|
|
self.play(LaggedStartMap(toggle_bit_anim, ecc_bits, lag_ratio=0.25, run_time=1))
|
|
self.wait()
|
|
|
|
self.add(counter)
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
self.wait()
|
|
block[0][1].set_opacity(1)
|
|
self.play(Write(block[0]))
|
|
one_rects.set_submobjects(get_one_rects(block))
|
|
self.wait()
|
|
self.play(*map(FadeOut, [one_rects, counter]))
|
|
|
|
# Walk through single and two bit errors
|
|
self.play(
|
|
zap_anim(block[9]),
|
|
toggle_bit_anim(block[9], target_color=RED),
|
|
)
|
|
one_rects.set_submobjects(get_one_rects(block))
|
|
counter.set_opacity(1)
|
|
self.add(counter)
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
self.wait()
|
|
|
|
self.play(
|
|
zap_anim(block[5]),
|
|
toggle_bit_anim(block[5], target_color=RED),
|
|
)
|
|
one_rects.set_submobjects(get_one_rects(block))
|
|
self.wait()
|
|
self.play(FadeOut(one_rects), FadeOut(counter))
|
|
|
|
for n in [2, 3]:
|
|
group = get_bit_n_subgroup(h_rects.copy(), n)
|
|
group.set_fill(opacity=0)
|
|
group.set_stroke(BLUE, 10)
|
|
self.play(ShowCreation(group, lag_ratio=0.5))
|
|
self.play(FadeOut(group))
|
|
self.wait()
|
|
|
|
scanim = scan_anim(boxes.get_corner(DR) + 2 * UR, block, lag_factor=0.5, run_time=2)
|
|
robot = scanim.mobject[-1]
|
|
robot.set_color(GREY_B)
|
|
robot.scale(2, about_edge=UP)
|
|
ded_words = TextMobject("At least\\\\2 errors!")
|
|
ded_words.set_color(RED)
|
|
ded_words.next_to(robot, UR, buff=SMALL_BUFF)
|
|
|
|
self.play(scanim)
|
|
self.play(FadeIn(ded_words, 0.5 * DOWN))
|
|
self.wait()
|
|
|
|
# Extended name
|
|
new_title = TextMobject("Extended Hamming Code")
|
|
new_title.replace(zero_words, dim_to_match=1)
|
|
self.play(
|
|
FadeIn(new_title, DOWN),
|
|
FadeOut(zero_words, UP),
|
|
FadeOut(pc_words),
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class WhatIfTheresAndArrowInECCBits(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"What if an\\\\error-correction bit\\\\needs to be corrected?",
|
|
bubble_kwargs={'width': 5, 'height': 4, "direction": LEFT},
|
|
added_anims=[self.teacher.change, "happy"]
|
|
)
|
|
self.change_student_modes("confused", "confused")
|
|
self.look_at(self.screen)
|
|
self.wait(2)
|
|
self.teacher_says("Try it!", target_mode="hooray")
|
|
self.change_student_modes(*3 * ["pondering"], look_at_arg=self.screen)
|
|
self.wait(2)
|
|
self.change_student_modes(*3 * ["thinking"], look_at_arg=self.screen)
|
|
self.wait(8)
|
|
|
|
|
|
class ErrorAtECCBit(Scene):
|
|
def construct(self):
|
|
bits = get_bit_grid(4, 4, height=6)
|
|
toggle_bit(bits[1])
|
|
toggle_bit(bits[4])
|
|
boxes = get_bit_grid_boxes(bits)
|
|
pos_labels = get_grid_position_labels(boxes)
|
|
ecc_boxes = VGroup(*[boxes[2**n] for n in range(4)])
|
|
ecc_boxes.set_fill(GREEN, 0.5)
|
|
bangs = TextMobject("!!!")
|
|
bangs.set_color(RED)
|
|
bangs.next_to(boxes[2], UP, SMALL_BUFF)
|
|
|
|
self.add(boxes, ecc_boxes, pos_labels, bits)
|
|
self.wait()
|
|
self.play(LaggedStartMap(Rotate, ecc_boxes, lambda m: (m, PI)))
|
|
self.wait()
|
|
self.play(
|
|
zap_anim(bits[2]),
|
|
toggle_bit_anim(bits[2], target_color=RED),
|
|
)
|
|
self.play(Write(bangs))
|
|
self.wait()
|
|
|
|
for bit in bits:
|
|
bit.remove(bit[1 - get_bit_mob_value(bit)])
|
|
for n in range(4):
|
|
bits.generate_target()
|
|
bits.target.set_opacity(1)
|
|
get_bit_n_subgroup(bits.target, n, 0).set_opacity(0)
|
|
self.play(MoveToTarget(bits))
|
|
self.wait()
|
|
self.play(bits.set_opacity, 1)
|
|
self.wait()
|
|
|
|
|
|
class HalfAsPowerful(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"Shouldn't that be\\\\only half as good?",
|
|
target_mode="sassy",
|
|
added_anims=[self.teacher.change, "happy"]
|
|
)
|
|
self.change_student_modes(
|
|
"pondering", "pondering", look_at_arg=self.screen,
|
|
added_anims=[self.teacher.change, "tease"]
|
|
)
|
|
self.look_at(self.screen)
|
|
self.wait(8)
|
|
|
|
|
|
class WhatAboutTwoErrors(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"What about\\\\2 errors?",
|
|
)
|
|
self.play(self.teacher.change, "guilty")
|
|
self.look_at(self.screen)
|
|
self.change_student_modes("erm", "confused")
|
|
self.look_at(self.screen)
|
|
self.wait(4)
|
|
|
|
|
|
class BlockSize256(Scene):
|
|
def construct(self):
|
|
N = 8
|
|
frame = self.camera.frame
|
|
|
|
# Bit block
|
|
bits = string_to_bits("You decoded an easter egg. Nice!")
|
|
block = get_bit_grid(2**(N // 2), 2**(N // 2), bits=bits)
|
|
block.set_height(6)
|
|
block.to_edge(LEFT, buff=LARGE_BUFF)
|
|
boxes = get_bit_grid_boxes(block)
|
|
|
|
parity_boxes = VGroup(*[boxes[2**k] for k in range(N)])
|
|
parity_boxes.set_fill(GREEN, 0.8)
|
|
|
|
bit_boxes = VGroup(*[VGroup(box, bit) for bit, box in zip(block, boxes)])
|
|
to_fade = VGroup()
|
|
to_keep = VGroup()
|
|
for i, bb in enumerate(bit_boxes):
|
|
if i >= 64 or (i % 16) >= 4:
|
|
to_fade.add(bb.copy())
|
|
else:
|
|
to_keep.add(bb.copy())
|
|
|
|
to_fade.save_state()
|
|
to_fade.fade(1)
|
|
frame.save_state()
|
|
frame.replace(to_keep, dim_to_match=1)
|
|
frame.scale(1.2)
|
|
|
|
self.add(to_keep)
|
|
self.play(
|
|
Restore(frame),
|
|
Restore(to_fade, lag_ratio=0.1),
|
|
run_time=5,
|
|
)
|
|
|
|
self.clear()
|
|
self.add(boxes)
|
|
self.add(block)
|
|
|
|
# Add title
|
|
title = TextMobject("$256 = 2^8$ bits")
|
|
title.set_height(0.7)
|
|
title.next_to(boxes, UP, MED_LARGE_BUFF)
|
|
title.set_x(0)
|
|
|
|
self.play(
|
|
frame.move_to, 0.5 * UP,
|
|
Write(title)
|
|
)
|
|
self.wait()
|
|
|
|
# Parity groups
|
|
parity_groups = VGroup()
|
|
for k in range(N):
|
|
group = boxes.copy()
|
|
group.set_fill(BLACK, opacity=0)
|
|
group.set_stroke(GREY_B, 1)
|
|
group.set_height(1.5)
|
|
get_bit_n_subgroup(group, k).set_fill(BLUE_E, 1)
|
|
parity_groups.add(group)
|
|
|
|
parity_groups.arrange_in_grid(2, 4, buff=MED_LARGE_BUFF)
|
|
parity_groups.set_width(7)
|
|
VGroup(parity_groups[:4], parity_groups[4:]).arrange(DOWN, buff=1.5)
|
|
parity_groups.to_edge(RIGHT)
|
|
|
|
# Question labels
|
|
q_labels = VGroup(*[TextMobject(f"Q{i + 1}") for i in range(N)])
|
|
for label, group in zip(q_labels, parity_groups):
|
|
label.set_height(0.3)
|
|
label.next_to(group, UP, SMALL_BUFF)
|
|
|
|
# Add questions
|
|
self.play(
|
|
LaggedStartMap(FadeIn, q_labels, lambda m: (m, DOWN), lag_ratio=0.3, run_time=5),
|
|
LaggedStartMap(FadeIn, parity_groups, lambda m: (m, DOWN), lag_ratio=0.3, run_time=5),
|
|
)
|
|
self.wait()
|
|
|
|
# Isolate one square
|
|
pos = 69 # Why not?
|
|
bits = "{0:b}".format(pos)
|
|
bits = (N - len(bits)) * '0' + bits
|
|
bits = bits[::-1]
|
|
|
|
yes_no_group = VGroup()
|
|
boxes.save_state()
|
|
possible_positions = list(range(2**N))
|
|
for k, bit, group in zip(it.count(), bits, parity_groups):
|
|
bit_value = int(bit)
|
|
if bit_value:
|
|
word = TextMobject("Yes", color=GREEN)
|
|
else:
|
|
word = TextMobject("No", color=RED)
|
|
word.next_to(group, DOWN)
|
|
yes_no_group.add(word)
|
|
|
|
intersect_positions = get_bit_n_sublist(range(2**N), k, bit_value)
|
|
globals()['intersect_positions'] = intersect_positions
|
|
possible_positions = list(filter(
|
|
lambda i: i in intersect_positions,
|
|
possible_positions,
|
|
))
|
|
|
|
boxes.generate_target()
|
|
boxes.target.match_style(boxes.saved_state)
|
|
globals()['possible_positions'] = possible_positions
|
|
globals()['boxes'] = boxes
|
|
VGroup(*[boxes.target[i] for i in possible_positions]).set_fill(BLUE, 1)
|
|
|
|
self.play(
|
|
FadeIn(word),
|
|
MoveToTarget(boxes)
|
|
)
|
|
self.wait()
|
|
|
|
# Highlight parity bits
|
|
parity_bits_label = TextMobject("8 parity bits")
|
|
parity_bits_label.next_to(boxes, UP, aligned_edge=LEFT)
|
|
parity_bits_label.set_color(GREEN)
|
|
|
|
self.play(
|
|
FadeIn(parity_bits_label, DOWN),
|
|
title.to_edge, RIGHT
|
|
)
|
|
self.add(parity_boxes, block)
|
|
self.play(
|
|
LaggedStartMap(Rotate, parity_boxes, lambda m: (m, TAU)),
|
|
)
|
|
self.add(boxes, block)
|
|
|
|
# Un-highlight isolated point
|
|
self.play(
|
|
boxes[pos].set_fill, BLACK, 0,
|
|
FadeOut(yes_no_group)
|
|
)
|
|
|
|
# Message bits
|
|
message_bits = VGroup(*[
|
|
bit
|
|
for i, bit in enumerate(block)
|
|
if i not in [2**k for k in range(N)]
|
|
])
|
|
message_bits.shuffle()
|
|
|
|
self.play(
|
|
LaggedStart(*[
|
|
toggle_bit_anim(bit)
|
|
for bit in message_bits
|
|
], lag_ratio=0.01, run_time=3)
|
|
)
|
|
self.wait()
|
|
|
|
# Write redundant
|
|
redundant_label = TextMobject("``Redundant''")
|
|
redundant_label.set_color(GREEN)
|
|
redundant_label.next_to(parity_bits_label[-1][-1], RIGHT, MED_LARGE_BUFF, DOWN)
|
|
|
|
self.play(Write(redundant_label))
|
|
|
|
block.save_state()
|
|
for k in range(N):
|
|
block.target = block.saved_state.copy()
|
|
get_bit_n_subgroup(block.target, k, 0).set_fill(opacity=0)
|
|
self.play(MoveToTarget(block))
|
|
self.wait(0.5)
|
|
self.play(Restore(block))
|
|
self.wait()
|
|
|
|
|
|
class WellAlmost(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says("Well...\\\\almost", target_mode="hesitant")
|
|
self.change_student_modes("angry", "sassy", "confused")
|
|
self.wait(3)
|
|
|
|
|
|
class ChecksSpellOutPositionInBinary(Scene):
|
|
def construct(self):
|
|
N = 4
|
|
pos = 7
|
|
|
|
# Setup block
|
|
random.seed(0)
|
|
bits = [random.choice([0, 1]) for n in range(2**N)]
|
|
bits[1] = 0
|
|
bits[2] = 1
|
|
bits[4] = 0
|
|
bits[8] = 0
|
|
block = get_bit_grid(2**(N // 2), 2**(N // 2), bits=bits)
|
|
block.set_height(5)
|
|
block.to_edge(LEFT, buff=LARGE_BUFF)
|
|
boxes = get_bit_grid_boxes(block)
|
|
VGroup(*[boxes[2**n] for n in range(N)]).set_fill(GREY_D, 1)
|
|
|
|
pos_labels = VGroup(*map(Integer, range(2**N)))
|
|
pos_labels.set_height(0.2)
|
|
for label, box, bit_label in zip(pos_labels, boxes, block):
|
|
label.move_to(box, DR)
|
|
label.shift(0.1 * UL)
|
|
label.set_color(GREY_A)
|
|
bit_label.scale(0.8)
|
|
|
|
self.add(boxes)
|
|
self.add(block)
|
|
self.add(pos_labels)
|
|
|
|
self.play(
|
|
zap_anim(block[pos]),
|
|
toggle_bit_anim(block[pos]),
|
|
)
|
|
|
|
# Setup questions
|
|
parity_groups = VGroup()
|
|
for n in range(N):
|
|
group = boxes.copy()
|
|
group.set_height(1)
|
|
group.set_width(1, stretch=True)
|
|
group.set_fill(BLACK, 0)
|
|
get_bit_n_subgroup(group, n).set_fill(BLUE_D, 1)
|
|
parity_groups.add(group)
|
|
parity_groups.arrange(DOWN, buff=MED_LARGE_BUFF)
|
|
parity_groups.set_height(6)
|
|
parity_groups.to_edge(RIGHT, buff=3)
|
|
|
|
q_labels = VGroup(*[TextMobject(f"Q{n + 1}:") for n in range(N)])
|
|
for label, group in zip(q_labels, parity_groups):
|
|
label.next_to(group, LEFT, MED_SMALL_BUFF)
|
|
|
|
self.play(
|
|
FadeIn(parity_groups),
|
|
FadeIn(q_labels),
|
|
)
|
|
|
|
# Binary search down
|
|
bits_word = "{0:b}".format(pos)
|
|
bits_word = (N - len(bits_word)) * '0' + bits_word
|
|
bits = list(map(int, bits_word[::-1]))
|
|
|
|
boxes.save_state()
|
|
yes_no_words = VGroup()
|
|
possible_positions = list(range(2**N))
|
|
for n, bit, group in zip(it.count(), bits, parity_groups):
|
|
if bit:
|
|
word = TextMobject("Yes", color=GREEN)
|
|
else:
|
|
word = TextMobject("No", color=RED)
|
|
word.next_to(group, RIGHT)
|
|
yes_no_words.add(word)
|
|
|
|
possible_positions = list(filter(
|
|
lambda i: i in get_bit_n_sublist(range(2**N), n, bit_value=bit),
|
|
possible_positions,
|
|
))
|
|
|
|
boxes.target = boxes.saved_state.copy()
|
|
VGroup(*[boxes.target[i] for i in possible_positions]).set_fill(BLUE_D, 0.8)
|
|
|
|
self.play(
|
|
FadeIn(word, 0.5 * LEFT),
|
|
MoveToTarget(boxes),
|
|
)
|
|
self.wait()
|
|
|
|
# Spell answer in binary
|
|
binary_answers = VGroup(*[
|
|
Integer(bit).move_to(word).match_color(word)
|
|
for bit, word in zip(bits, yes_no_words)
|
|
])
|
|
|
|
self.play(
|
|
LaggedStartMap(GrowFromCenter, binary_answers),
|
|
LaggedStartMap(ApplyMethod, yes_no_words, lambda m: (m.scale, 0), remover=True),
|
|
)
|
|
|
|
# Show value of 7
|
|
frame = self.camera.frame
|
|
|
|
binary_pos_label = binary_answers.copy()
|
|
binary_pos_label.generate_target()
|
|
binary_pos_label.target.arrange(LEFT, buff=SMALL_BUFF, aligned_edge=DOWN)
|
|
|
|
equation = VGroup(
|
|
Integer(7, color=BLUE),
|
|
TexMobject("\\rightarrow"),
|
|
binary_pos_label.target,
|
|
)
|
|
equation.arrange(RIGHT)
|
|
equation.to_edge(UP, buff=0)
|
|
|
|
arrow = Arrow(equation.get_left(), equation.get_right(), buff=0)
|
|
arrow.next_to(equation, DOWN, SMALL_BUFF)
|
|
trans_words = TextMobject("Decimal to binary")
|
|
trans_words.match_width(arrow)
|
|
trans_words.next_to(arrow, DOWN, SMALL_BUFF)
|
|
|
|
self.play(
|
|
MoveToTarget(binary_pos_label),
|
|
frame.move_to, 0.5 * UP
|
|
)
|
|
self.play(
|
|
Write(equation[:-1]),
|
|
Write(trans_words),
|
|
GrowArrow(arrow),
|
|
run_time=1,
|
|
)
|
|
self.wait()
|
|
|
|
bin_group = VGroup(*equation[:-1], binary_pos_label, arrow, trans_words)
|
|
|
|
# Spell out binary
|
|
bin_equation = TexMobject(
|
|
"{7} = {0} \\cdot 8 + {1} \\cdot 4 + {1} \\cdot 2 + {1} \\cdot 1",
|
|
tex_to_color_map={
|
|
"{0}": RED,
|
|
"{1}": GREEN,
|
|
"{7}": BLUE,
|
|
}
|
|
)
|
|
bin_equation.move_to(bin_group, UP)
|
|
|
|
bit_parts = list(it.chain(*[
|
|
bin_equation.get_parts_by_tex(f"{{{d}}}")
|
|
for d in [0, 1]
|
|
]))
|
|
|
|
self.play(
|
|
bin_group.next_to, bin_equation, DOWN, LARGE_BUFF,
|
|
*[
|
|
ApplyMethod(m1.copy().replace, m2, {"dim_to_match": 1}, remover=True, run_time=1.5)
|
|
for m1, m2 in zip(binary_pos_label[::-1], bit_parts)
|
|
],
|
|
ApplyMethod(equation[0].copy().replace, bin_equation[0], remover=True, run_time=1.5)
|
|
)
|
|
self.add(bin_equation[0], *bit_parts)
|
|
self.play(FadeIn(VGroup(*[
|
|
part
|
|
for part in bin_equation
|
|
if part.get_tex_string() not in ["{0}", "{1}", "{7}"]
|
|
]), lag_ratio=0.1))
|
|
self.add(bin_equation)
|
|
self.wait()
|
|
|
|
# Error at 7
|
|
toggle_bit(block[pos])
|
|
self.play(
|
|
zap_anim(block[pos]),
|
|
toggle_bit_anim(block[pos], target_color=RED),
|
|
)
|
|
self.wait()
|
|
|
|
# Four parity checks
|
|
for n, label, group, word in zip(it.count(), q_labels, parity_groups, yes_no_words):
|
|
boxes.target = boxes.saved_state.copy()
|
|
get_bit_n_subgroup(boxes.target, n).set_fill(BLUE, 0.8)
|
|
|
|
rect = SurroundingRectangle(VGroup(label, group, word), buff=MED_SMALL_BUFF)
|
|
one_rects = get_one_rects(get_bit_n_subgroup(block, n))
|
|
|
|
self.play(
|
|
MoveToTarget(boxes),
|
|
ShowCreation(rect),
|
|
ShowIncreasingSubsets(one_rects)
|
|
)
|
|
self.play(
|
|
FadeOut(one_rects),
|
|
FadeOut(rect),
|
|
)
|
|
self.wait(0.5)
|
|
self.play(Restore(boxes))
|
|
self.wait()
|
|
|
|
# Other examples
|
|
toggle_bit(block[7])
|
|
block[7].set_color(WHITE)
|
|
|
|
bit_parts = VGroup(*bit_parts)
|
|
to_save = VGroup(
|
|
equation[0], bin_equation[0],
|
|
bit_parts, binary_pos_label, binary_answers,
|
|
)
|
|
to_save.save_state()
|
|
|
|
ns = random.sample(list(range(16)), 10)
|
|
for n in ns:
|
|
toggle_bit(block[n])
|
|
block[n].set_color(YELLOW)
|
|
|
|
nc1 = Integer(n)
|
|
nc1.replace(equation[0], 1)
|
|
nc1.match_color(equation[0])
|
|
equation[0].set_opacity(0)
|
|
nc2 = nc1.copy()
|
|
nc2.replace(bin_equation[0], 1)
|
|
bin_equation[0].set_fill(0)
|
|
|
|
new_bits = int_to_bit_string(n, 4)
|
|
new_bit_mobs = VGroup()
|
|
for b1, b2, b3, value in zip(reversed(bit_parts), binary_pos_label, binary_answers, reversed(new_bits)):
|
|
new_mob = TexMobject(value)
|
|
new_mob.set_color(GREEN if int(value) else RED)
|
|
for b in (b1, b2, b3):
|
|
nmc = new_mob.copy()
|
|
nmc.replace(b, 1)
|
|
b.set_opacity(0)
|
|
new_bit_mobs.add(nmc)
|
|
|
|
self.play(*[
|
|
Animation(mob, remover=True, run_time=1)
|
|
for mob in [new_bit_mobs, nc1, nc2]
|
|
])
|
|
|
|
toggle_bit(block[n])
|
|
block[n].set_color(WHITE)
|
|
|
|
to_save.restore()
|
|
self.wait()
|
|
|
|
# Remove 7 stuff
|
|
self.play(
|
|
FadeOut(VGroup(
|
|
*bin_group, *bin_equation, *binary_answers
|
|
), lag_ratio=0.1),
|
|
block[pos].set_color, WHITE,
|
|
)
|
|
|
|
question_group = VGroup(q_labels, parity_groups)
|
|
|
|
# Show numbers 0 through 15
|
|
pos_labels_movers = pos_labels.copy()
|
|
bin_pos_groups = VGroup()
|
|
arrows = VGroup()
|
|
bin_labels = VGroup()
|
|
|
|
for n, label in enumerate(pos_labels_movers):
|
|
label.scale(2)
|
|
arrow = TexMobject("\\rightarrow")
|
|
bits_word = "{0:b}".format(n)
|
|
bits_word = (N - len(bits_word)) * '0' + bits_word
|
|
bin_label = VGroup(*[TexMobject(b) for b in bits_word])
|
|
bin_label.arrange(RIGHT, buff=SMALL_BUFF, aligned_edge=DOWN)
|
|
pos_group = VGroup(label, arrow, bin_label)
|
|
pos_group.arrange(RIGHT, buff=MED_SMALL_BUFF)
|
|
bin_pos_groups.add(pos_group)
|
|
arrows.add(pos_group[1])
|
|
bin_labels.add(bin_label)
|
|
|
|
bin_pos_groups.arrange_in_grid(8, 2, fill_rows_first=False)
|
|
bin_pos_groups.set_height(7)
|
|
bin_pos_groups.to_edge(RIGHT)
|
|
bin_pos_groups.set_y(0.5)
|
|
|
|
self.play(
|
|
FadeOut(question_group),
|
|
FadeIn(arrows, lag_ratio=0.02),
|
|
TransformFromCopy(pos_labels, pos_labels_movers),
|
|
)
|
|
self.play(ShowIncreasingSubsets(bin_labels, run_time=3, rate_func=bezier([0, 0, 1, 1])))
|
|
self.wait()
|
|
|
|
# Put bin labels in boxes
|
|
for label, box in zip(bin_labels, boxes):
|
|
label.generate_target()
|
|
label.target.set_width(0.7 * box.get_width())
|
|
label.target.move_to(box, DOWN)
|
|
label.target.shift(SMALL_BUFF * UP)
|
|
|
|
for bit in block:
|
|
bit.generate_target()
|
|
bit.target.scale(0.5, about_edge=UP),
|
|
bit.target.fade(0.5)
|
|
|
|
kw = {
|
|
"run_time": 5,
|
|
"lag_ratio": 0.3,
|
|
}
|
|
self.play(
|
|
LaggedStartMap(MoveToTarget, bin_labels, **kw),
|
|
LaggedStartMap(FadeOut, arrows, **kw),
|
|
LaggedStartMap(FadeOut, pos_labels_movers, **kw),
|
|
LaggedStartMap(FadeOut, pos_labels, **kw),
|
|
LaggedStartMap(MoveToTarget, block, **kw),
|
|
)
|
|
self.wait()
|
|
|
|
# Show confusion
|
|
randy = Randolph()
|
|
randy.flip()
|
|
randy.to_corner(DR, buff=LARGE_BUFF)
|
|
|
|
self.play(
|
|
VFadeIn(randy),
|
|
randy.change, "maybe", boxes,
|
|
)
|
|
self.play(PiCreatureBubbleIntroduction(
|
|
randy, "Wait...",
|
|
target_mode="confused",
|
|
bubble_class=ThoughtBubble,
|
|
look_at_arg=boxes.get_top(),
|
|
))
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
|
|
self.play(LaggedStart(*map(ShowCreationThenFadeAround, bin_labels), lag_ratio=0))
|
|
self.play(randy.change, "maybe")
|
|
self.play(
|
|
LaggedStart(*[ShowCreationThenFadeOut(SurroundingRectangle(b, color=GREEN)) for b in block], lag_ratio=0),
|
|
randy.look_at, boxes.get_bottom(),
|
|
)
|
|
self.play(Blink(randy))
|
|
self.play(
|
|
randy.change, 'pondering', boxes,
|
|
FadeOut(randy.bubble),
|
|
FadeOut(randy.bubble.content),
|
|
)
|
|
for x in range(2):
|
|
self.wait()
|
|
self.play(Blink(randy))
|
|
self.play(randy.change, "thinking")
|
|
self.play(FadeOut(randy))
|
|
|
|
# Go through parity group 1 (and setup others)
|
|
bit_arrow_groups = VGroup()
|
|
for n in range(N):
|
|
arrow_group = VGroup()
|
|
for bin_label in bin_labels:
|
|
char = bin_label[-(n + 1)]
|
|
arrow = Triangle(start_angle=-PI / 2)
|
|
arrow.stretch(0.8, 0)
|
|
arrow.set_height(0.8 * char.get_height())
|
|
arrow.next_to(char, UP, buff=0.05)
|
|
arrow.set_stroke(width=0)
|
|
if char.get_tex_string() == '0':
|
|
arrow.set_fill(GREY, 1)
|
|
else:
|
|
arrow.set_fill(BLUE, 1)
|
|
arrow_group.add(arrow)
|
|
bit_arrow_groups.add(arrow_group)
|
|
|
|
highlight_groups = VGroup()
|
|
for n in range(N):
|
|
highlight_group = boxes.copy()
|
|
highlight_group.set_fill(BLACK, 0)
|
|
get_bit_n_subgroup(highlight_group, n).set_fill(BLUE, 0.5)
|
|
highlight_groups.add(highlight_group)
|
|
|
|
questions = VGroup()
|
|
for n in range(N):
|
|
chars = ["\\underline{\\phantom{0}}" for x in range(4)]
|
|
chars[-(n + 1)] = "\\underline{1}"
|
|
question = TextMobject(
|
|
f"""
|
|
If there's an error, does\\\\
|
|
its position look like\\\\
|
|
""",
|
|
" ".join(chars),
|
|
"?"
|
|
)
|
|
question.scale(1.25)
|
|
question[1:].scale(1.5, about_edge=UP)
|
|
question[1:].shift(SMALL_BUFF * DOWN)
|
|
question[1].set_color(BLUE)
|
|
question.next_to(boxes, RIGHT, LARGE_BUFF)
|
|
questions.add(question)
|
|
|
|
self.play(
|
|
LaggedStartMap(FadeIn, bit_arrow_groups[0], lag_ratio=0.3, run_time=3),
|
|
FadeOut(block),
|
|
)
|
|
self.play(Transform(boxes, highlight_groups[0]))
|
|
self.wait()
|
|
self.play(Write(questions[0]))
|
|
self.wait()
|
|
|
|
# Go through parity groups 2-4
|
|
bit_arrows = bit_arrow_groups[0]
|
|
for n in range(1, N):
|
|
self.play(boxes.set_fill, BLACK, 0)
|
|
self.play(
|
|
Transform(bit_arrows, bit_arrow_groups[n]),
|
|
FadeOut(questions[n - 1])
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Transform(boxes, highlight_groups[n]),
|
|
FadeIn(questions[n])
|
|
)
|
|
self.wait()
|
|
|
|
# Organize questions
|
|
questions.generate_target()
|
|
questions.target.arrange(DOWN, buff=LARGE_BUFF)
|
|
questions.target.set_height(FRAME_HEIGHT - 1)
|
|
questions.target.next_to(boxes, RIGHT, buff=1.5)
|
|
questions.target.match_y(frame)
|
|
questions[:N - 1].set_opacity(0)
|
|
|
|
self.play(
|
|
MoveToTarget(questions),
|
|
Restore(boxes),
|
|
FadeOut(bit_arrows),
|
|
)
|
|
|
|
fade_anims = []
|
|
for n in range(N):
|
|
rect = SurroundingRectangle(questions[n])
|
|
rect.set_stroke(BLUE, 2)
|
|
self.play(
|
|
FadeIn(highlight_groups[n]),
|
|
FadeIn(rect),
|
|
*fade_anims
|
|
)
|
|
fade_anims = [
|
|
FadeOut(highlight_groups[n]),
|
|
FadeOut(rect),
|
|
]
|
|
self.play(*fade_anims)
|
|
|
|
# Note power of 2 points
|
|
parity_arrows = VGroup(Vector(DOWN), Vector(DOWN), Vector(RIGHT), Vector(RIGHT))
|
|
parity_arrows[0].next_to(boxes[1], UP, SMALL_BUFF)
|
|
parity_arrows[1].next_to(boxes[2], UP, SMALL_BUFF)
|
|
parity_arrows[2].next_to(boxes[4], LEFT, SMALL_BUFF)
|
|
parity_arrows[3].next_to(boxes[8], LEFT, SMALL_BUFF)
|
|
parity_arrows.set_color(GREEN)
|
|
|
|
parity_groups = VGroup()
|
|
for n, question in enumerate(questions):
|
|
pg = boxes.copy()
|
|
pg.set_width(pg.get_height(), stretch=True)
|
|
pg.set_height(0.8 * question.get_height())
|
|
pg.set_fill(BLACK, 0)
|
|
get_bit_n_subgroup(pg, n).set_fill(BLUE, 0.8)
|
|
pg.next_to(question, RIGHT, LARGE_BUFF)
|
|
parity_groups.add(pg)
|
|
|
|
self.play(
|
|
LaggedStartMap(GrowArrow, parity_arrows),
|
|
ApplyMethod(frame.set_x, -1, run_time=2)
|
|
)
|
|
self.wait()
|
|
rects = VGroup(*[boxes[2**n].copy() for n in range(N)])
|
|
rects.set_fill(BLUE, 0.8)
|
|
self.add(rects, bin_label)
|
|
self.play(
|
|
LaggedStartMap(VFadeInThenOut, rects, lag_ratio=0.5, run_time=5),
|
|
LaggedStartMap(FadeIn, parity_groups, lag_ratio=0.5, run_time=5),
|
|
)
|
|
self.wait()
|
|
self.add(rects, bin_label)
|
|
self.play(
|
|
LaggedStartMap(VFadeInThenOut, rects, lag_ratio=0.5, run_time=5),
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class PowerOfTwoPositions(Scene):
|
|
def construct(self):
|
|
block = get_bit_grid(4, 4)
|
|
block.set_height(5)
|
|
block.to_edge(LEFT, buff=LARGE_BUFF)
|
|
boxes = get_bit_grid_boxes(block)
|
|
|
|
numbers = VGroup(*[
|
|
Integer(n).move_to(box)
|
|
for n, box in enumerate(boxes)
|
|
])
|
|
|
|
self.add(numbers)
|
|
self.wait()
|
|
for n in range(4):
|
|
numbers[2**n].scale(1.5)
|
|
numbers[2**n].set_color(YELLOW)
|
|
self.wait(0.25)
|
|
self.wait()
|
|
|
|
|
|
class OneGroupPerParityBit(Scene):
|
|
def construct(self):
|
|
N = 4
|
|
block = get_bit_grid(2**(N // 2), 2**(N // 2))
|
|
block.set_height(5)
|
|
block.to_edge(LEFT, buff=LARGE_BUFF)
|
|
boxes = get_bit_grid_boxes(block)
|
|
pos_labels = get_grid_position_labels(boxes)
|
|
|
|
for bit in block:
|
|
bit.scale(0.7)
|
|
|
|
parity_boxes = VGroup(*[boxes[2**n] for n in range(N)])
|
|
parity_boxes.set_fill(GREEN, 0.8)
|
|
block.save_state()
|
|
self.add(boxes, pos_labels, block)
|
|
for n in range(4):
|
|
block.restore()
|
|
get_bit_n_subgroup(block, n, 0).set_fill(opacity=0)
|
|
self.wait(1.5)
|
|
|
|
|
|
class LetsWalkThroughAnExample(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"Can we walk through\\\\a full example?",
|
|
student_index=1,
|
|
added_anims=[self.teacher.change, "happy"]
|
|
)
|
|
self.change_student_modes("hooray", None, "hooray")
|
|
self.wait(5)
|
|
self.teacher_says(
|
|
"But of\\\\course!",
|
|
target_mode="tease"
|
|
)
|
|
self.change_student_modes("happy", "coin_flip_1", "happy")
|
|
self.wait(4)
|
|
|
|
|
|
class FullExampleWithNewEnd(Scene):
|
|
CONFIG = {
|
|
"random_seed": 3,
|
|
}
|
|
|
|
def construct(self):
|
|
# Pull bits out of an image
|
|
image = ImageMobject("Tom_In_Bowtie")
|
|
image.set_height(6)
|
|
image.to_edge(LEFT, buff=LARGE_BUFF)
|
|
|
|
bits = get_image_bits(image)
|
|
bits.match_height(image)
|
|
bits.generate_target()
|
|
bits.target.arrange_in_grid(n_cols=11, h_buff=SMALL_BUFF, v_buff=MED_SMALL_BUFF)
|
|
bits.target.next_to(image, RIGHT, LARGE_BUFF, UP)
|
|
for bit, bt in zip(bits, bits.target):
|
|
bit.save_state()
|
|
bit.target = bt
|
|
bit.fade(0.9)
|
|
|
|
words = TextMobject("11-bit\\\\chunks")
|
|
words.scale(1.5)
|
|
words.to_edge(RIGHT)
|
|
lines = VGroup()
|
|
for bit in bits.target[10:200:11]:
|
|
line = Line(RIGHT, LEFT)
|
|
line.set_stroke(BLUE, 1)
|
|
line.bit = bit
|
|
line.word = words[0][0]
|
|
line.add_updater(lambda m: m.put_start_and_end_on(
|
|
m.word.get_left() + SMALL_BUFF * LEFT,
|
|
m.bit.get_right() + SMALL_BUFF * RIGHT,
|
|
))
|
|
lines.add(line)
|
|
|
|
self.add(image)
|
|
self.add(bits)
|
|
self.save_state()
|
|
self.play(
|
|
LaggedStartMap(
|
|
Succession, bits,
|
|
lambda m: (Restore(m), MoveToTarget(m)),
|
|
lag_ratio=3 / len(bits),
|
|
),
|
|
Write(words, rate_func=squish_rate_func(smooth, 0.5, 0.7)),
|
|
ShowCreation(lines, lag_ratio=0.1, rate_func=squish_rate_func(smooth, 0.5, 1)),
|
|
run_time=8
|
|
)
|
|
|
|
for line, bit in zip(lines, bits[10::11]):
|
|
line.bit = bit
|
|
|
|
for bit in bits:
|
|
if bit.get_center()[1] < -FRAME_HEIGHT / 2:
|
|
bits.remove(bit)
|
|
|
|
self.wait()
|
|
|
|
# Show many 16 bit blocks
|
|
bits.generate_target()
|
|
bits.target.scale(1.5)
|
|
bits.target.arrange_in_grid(n_cols=11, h_buff=SMALL_BUFF, v_buff=LARGE_BUFF)
|
|
bits.target.move_to(bits, UR)
|
|
|
|
box_groups = VGroup()
|
|
box_arrows = VGroup()
|
|
for bit in bits.target[0:77:11]:
|
|
boxes = VGroup(*[Square() for x in range(16)])
|
|
boxes.arrange_in_grid(4, 4, buff=0)
|
|
boxes.set_height(0.8)
|
|
boxes.next_to(bit, LEFT, buff=1.5)
|
|
boxes.set_stroke(GREY_B, 2)
|
|
arrow = Arrow(bit.get_left(), boxes.get_right())
|
|
box_arrows.add(arrow)
|
|
box_groups.add(boxes)
|
|
|
|
self.play(
|
|
FadeOut(image, 2 * LEFT),
|
|
MoveToTarget(bits, run_time=2),
|
|
LaggedStartMap(FadeIn, box_groups, run_time=4),
|
|
LaggedStartMap(GrowArrow, box_arrows, run_time=4),
|
|
)
|
|
self.wait()
|
|
|
|
# Isolate to one box
|
|
first_bits = bits[:11]
|
|
first_boxes = box_groups[0]
|
|
|
|
first_bits.generate_target()
|
|
first_bits.target.set_height(0.6)
|
|
first_bits.target.to_edge(UP)
|
|
first_bits.target.set_x(-3)
|
|
first_boxes.generate_target()
|
|
first_boxes.target.set_height(5)
|
|
first_boxes.target.next_to(first_bits.target, DOWN, LARGE_BUFF)
|
|
|
|
self.play(
|
|
MoveToTarget(first_bits),
|
|
MoveToTarget(first_boxes),
|
|
LaggedStart(*map(FadeOut, [
|
|
*bits[11:], box_arrows, box_groups[1:],
|
|
*lines, words,
|
|
]), lag_ratio=0.01),
|
|
run_time=2
|
|
)
|
|
|
|
bits = first_bits
|
|
boxes = first_boxes
|
|
|
|
# Try it yourself
|
|
morty = Mortimer()
|
|
morty.to_edge(DR)
|
|
self.play(
|
|
PiCreatureSays(morty, "Try it\\\\yourself", target_mode="hooray"),
|
|
VFadeIn(morty)
|
|
)
|
|
self.play(Blink(morty))
|
|
self.wait(2)
|
|
self.play(LaggedStart(
|
|
FadeOut(morty),
|
|
FadeOut(morty.bubble),
|
|
FadeOut(morty.bubble.content),
|
|
))
|
|
|
|
# Fill block
|
|
N = 4
|
|
ecc_boxes = VGroup(*[boxes[2**n] for n in range(N)])
|
|
message_boxes = VGroup(*[
|
|
box for box in boxes[1:]
|
|
if box not in ecc_boxes
|
|
])
|
|
pos_labels = get_grid_position_labels(boxes, height=0.2)
|
|
pos_labels.set_color(GREY_B)
|
|
|
|
self.play(
|
|
ecc_boxes.set_fill, GREEN, 0.7,
|
|
boxes[0].set_fill, YELLOW, 0.5,
|
|
FadeIn(pos_labels, lag_ratio=0.1)
|
|
)
|
|
self.wait()
|
|
|
|
self.play(LaggedStart(*[
|
|
ApplyMethod(bit.move_to, box)
|
|
for bit, box in zip(bits, message_boxes)
|
|
], run_time=4, lag_ratio=0.3))
|
|
self.wait()
|
|
|
|
# Organize bits properly
|
|
bit_template = bits[0].copy()
|
|
if get_bit_mob_value(bit_template) == 1:
|
|
toggle_bit(bit_template)
|
|
|
|
new_bits = [None] * 16
|
|
for i in [0, 1, 2, 4, 8]:
|
|
new_bits[i] = bit_template.copy()
|
|
new_bits[i].move_to(boxes[i])
|
|
|
|
bits_iter = iter(bits)
|
|
for i, new_bit in enumerate(new_bits):
|
|
if new_bit is None:
|
|
new_bits[i] = next(bits_iter)
|
|
|
|
bits = VGroup(*new_bits)
|
|
|
|
# Show parity groups
|
|
boxes.save_state()
|
|
self.add(boxes, pos_labels, bits)
|
|
for bit in bits:
|
|
for part in bit:
|
|
if part.get_fill_opacity() > 0:
|
|
part.set_fill(opacity=1)
|
|
bit.save_state()
|
|
VGroup(*bits[:3], bits[4], bits[8]).set_opacity(0)
|
|
|
|
for n in range(N):
|
|
boxes.generate_target()
|
|
boxes.target.set_fill(BLACK, 0)
|
|
get_bit_n_subgroup(boxes.target, n).set_fill(BLUE, 0.8)
|
|
for k in range(n):
|
|
boxes.target[2**k].set_fill(GREEN, 0.5)
|
|
one_rects = get_one_rects(get_bit_n_subgroup(bits, n))
|
|
counter = get_ones_counter(boxes[10:12], one_rects, buff=1.5)
|
|
counter.match_height(bits[0])
|
|
|
|
self.play(MoveToTarget(boxes))
|
|
self.add(counter)
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
self.wait()
|
|
self.play(Restore(bits[2**n]))
|
|
if counter.get_value() % 2 == 1:
|
|
rect_copy = one_rects[0].copy()
|
|
rect_copy.move_to(bits[2**n])
|
|
one_rects.add(rect_copy)
|
|
bits[2**n][0].set_opacity(1)
|
|
toggle_bit(bits[2**n])
|
|
self.add(rect_copy)
|
|
self.wait()
|
|
self.play(
|
|
LaggedStartMap(FadeOut, VGroup(*one_rects, counter), run_time=1),
|
|
)
|
|
self.play(Restore(boxes))
|
|
|
|
# Final parity check
|
|
one_rects = get_one_rects(bits)
|
|
counter = get_ones_counter(boxes[10:12], one_rects, buff=1.5)
|
|
counter.match_height(bits[0])
|
|
|
|
self.add(counter)
|
|
self.play(ShowIncreasingSubsets(one_rects))
|
|
self.wait()
|
|
self.play(Restore(bits[0]))
|
|
self.play(LaggedStartMap(FadeOut, VGroup(*one_rects, counter)))
|
|
self.wait()
|
|
|
|
# Send as a message
|
|
block_group = VGroup(boxes, pos_labels, bits)
|
|
pis, names = get_sender_and_receiver()
|
|
randy, morty = pis
|
|
|
|
line = Line(randy.get_corner(UR), morty.get_corner(UL), buff=MED_SMALL_BUFF)
|
|
line.add(
|
|
Dot().move_to(line.get_start(), RIGHT),
|
|
Dot().move_to(line.get_end(), LEFT),
|
|
)
|
|
line.set_stroke(GREY, 2)
|
|
line_label = TextMobject("Noisy channel")
|
|
line_label.next_to(line, DOWN, SMALL_BUFF)
|
|
line_label.set_color(RED)
|
|
|
|
self.play(
|
|
VFadeIn(pis),
|
|
FadeIn(names, DOWN),
|
|
randy.change, "raise_right_hand",
|
|
block_group.set_height, 1,
|
|
block_group.move_to, randy.get_corner(UR), DOWN,
|
|
block_group.shift, 0.1 * UP,
|
|
)
|
|
self.play(
|
|
ShowCreation(line),
|
|
Write(line_label)
|
|
)
|
|
self.play(Blink(randy))
|
|
self.play(
|
|
block_group.match_x, line,
|
|
randy.change, "happy", morty.eyes,
|
|
)
|
|
|
|
# Possible changes
|
|
black_box = SurroundingRectangle(block_group, buff=0)
|
|
black_box.set_fill(GREY_D, 1)
|
|
black_box.set_stroke(WHITE, 2)
|
|
change_words = VGroup(
|
|
TextMobject("Maybe flip 0 bits"),
|
|
TextMobject("Maybe flip 1 bit"),
|
|
TextMobject("Maybe flip 2 bits"),
|
|
)
|
|
colors = [WHITE, RED_B, RED]
|
|
for word, color in zip(change_words, colors):
|
|
word.next_to(black_box, UP)
|
|
word.set_color(color)
|
|
|
|
morty_arrow = Vector(DOWN)
|
|
morty_arrow.next_to(morty, UP)
|
|
|
|
self.play(
|
|
GrowArrow(morty_arrow),
|
|
morty.change, "pondering", morty_arrow
|
|
)
|
|
self.play(ShowCreationThenFadeAround(morty))
|
|
self.play(Blink(morty))
|
|
self.wait()
|
|
self.play(
|
|
morty_arrow.next_to, block_group, UP,
|
|
FadeIn(black_box),
|
|
morty.look_at, black_box,
|
|
)
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
self.play(Blink(randy))
|
|
self.play(
|
|
FadeIn(change_words[0], 0.25 * DOWN),
|
|
FadeOut(morty_arrow, UP),
|
|
morty.change, "confused", change_words[0],
|
|
)
|
|
for i in [1, 2]:
|
|
self.play(
|
|
FadeIn(change_words[i], 0.25 * DOWN),
|
|
change_words[:i].shift, UP,
|
|
)
|
|
self.wait()
|
|
self.play(Blink(morty))
|
|
|
|
error_pos = 10
|
|
toggle_bit(bits[error_pos])
|
|
|
|
self.add(block_group, black_box)
|
|
self.play(
|
|
FadeOut(black_box),
|
|
FadeOut(change_words),
|
|
)
|
|
self.play(
|
|
block_group.set_x, line.get_end()[0],
|
|
morty.change, "pondering", line.get_end() + UP,
|
|
)
|
|
self.play(
|
|
Uncreate(line),
|
|
FadeOut(line_label, DOWN),
|
|
FadeOut(randy, DL),
|
|
FadeOut(morty, DR),
|
|
FadeOut(names, DOWN),
|
|
block_group.set_height, 5,
|
|
block_group.set_y, 0,
|
|
block_group.to_edge, RIGHT,
|
|
)
|
|
self.wait()
|
|
|
|
# Try it!
|
|
try_it_words = TextMobject("Try it\\\\yourself!")
|
|
try_it_words.scale(2)
|
|
try_it_words.next_to(boxes, LEFT, buff=2)
|
|
self.play(FadeIn(try_it_words, RIGHT))
|
|
self.wait()
|
|
self.play(FadeOut(try_it_words, LEFT))
|
|
|
|
# Do parity checks
|
|
working_grid = boxes.copy()
|
|
working_grid.to_edge(LEFT)
|
|
working_grid_words = TextMobject("Possibilities")
|
|
working_grid_words.set_color(BLUE)
|
|
working_grid_words.next_to(working_grid, UP)
|
|
working_grid.set_fill(BLUE, 0.7)
|
|
working_pos_labels = get_grid_position_labels(working_grid)
|
|
|
|
counter = Integer(0)
|
|
counter.set_height(0.7)
|
|
counter.set_color(YELLOW)
|
|
counter.next_to(boxes, LEFT, MED_LARGE_BUFF)
|
|
counter.counted = VGroup()
|
|
counter.add_updater(lambda m: m.set_value(len(m.counted)))
|
|
for n in range(N):
|
|
off_bits = get_bit_n_subgroup(bits, n, 0)
|
|
on_bits = get_bit_n_subgroup(bits, n, 1)
|
|
rects = get_one_rects(on_bits)
|
|
counter.counted = rects
|
|
|
|
self.play(FadeOut(off_bits))
|
|
self.add(counter)
|
|
self.play(ShowIncreasingSubsets(rects))
|
|
|
|
to_fade = get_bit_n_subgroup(working_grid, n, 1 - (len(rects) % 2))
|
|
if n == 0:
|
|
to_fade.set_fill(BLACK, 0)
|
|
self.play(
|
|
FadeIn(working_grid),
|
|
FadeIn(working_pos_labels),
|
|
FadeIn(working_grid_words),
|
|
)
|
|
else:
|
|
self.play(to_fade.set_fill, BLACK, 0)
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(counter),
|
|
FadeOut(rects),
|
|
FadeIn(off_bits),
|
|
)
|
|
|
|
# Move working grid
|
|
self.add(working_grid, block_group)
|
|
self.play(
|
|
ApplyMethod(working_grid.move_to, boxes, run_time=2),
|
|
FadeOut(working_grid_words),
|
|
FadeOut(working_pos_labels),
|
|
)
|
|
|
|
# Full parity check
|
|
rects = get_one_rects(bits)
|
|
counter.counted = rects
|
|
self.add(counter)
|
|
self.play(ShowIncreasingSubsets(rects))
|
|
self.wait()
|
|
self.play(FadeOut(rects), FadeOut(counter))
|
|
|
|
# Correct error bit
|
|
self.play(toggle_bit_anim(bits[error_pos]))
|
|
self.play(FadeOut(working_grid))
|
|
self.wait()
|
|
|
|
# Show 11 message bits
|
|
block_group.generate_target()
|
|
block_group.target.center()
|
|
block_group.target.to_edge(DOWN)
|
|
VGroup(*[
|
|
block_group.target[2][i]
|
|
for i in [0, 1, 2, 4, 8]
|
|
]).set_color(GREY_C)
|
|
self.play(MoveToTarget(block_group))
|
|
|
|
message_bits = VGroup(*[
|
|
bit for i, bit in enumerate(bits)
|
|
if i not in [0, 1, 2, 4, 8]
|
|
])
|
|
message_bits.generate_target()
|
|
message_bits.target.arrange(RIGHT, buff=SMALL_BUFF)
|
|
message_bits.target.to_edge(UP)
|
|
for mb, mt in zip(message_bits, message_bits.target):
|
|
mb.target = mt
|
|
|
|
self.play(LaggedStartMap(
|
|
MoveToTarget, message_bits,
|
|
lag_ratio=0.3,
|
|
run_time=4,
|
|
))
|
|
self.wait()
|
|
|
|
def old_parity_checks(self):
|
|
questions = VGroup(*[boxes.copy() for x in range(4)])
|
|
questions.set_height(1)
|
|
questions.arrange(DOWN, buff=0.5)
|
|
questions.set_height(6)
|
|
questions.to_edge(LEFT, buff=1)
|
|
|
|
counter = Integer(0, color=YELLOW)
|
|
counter.match_height(bits[0])
|
|
counter.next_to(boxes, LEFT, LARGE_BUFF)
|
|
|
|
boxes.save_state()
|
|
self.add(boxes, pos_labels, bits)
|
|
results = VGroup()
|
|
for n, question in enumerate(questions):
|
|
question.set_fill(BLACK, 0)
|
|
get_bit_n_subgroup(question, n).set_fill(BLUE, 0.7)
|
|
|
|
one_rects = get_one_rects(get_bit_n_subgroup(bits, n))
|
|
counter.set_value(len(one_rects))
|
|
|
|
boxes.generate_target()
|
|
boxes.target.match_style(question)
|
|
|
|
self.play(MoveToTarget(boxes))
|
|
self.play(FadeIn(one_rects))
|
|
|
|
if counter.get_value() % 2 == 0:
|
|
result = Integer(0, color=GREEN)
|
|
else:
|
|
result = Integer(1, color=RED)
|
|
result.next_to(question, RIGHT)
|
|
results.add(result)
|
|
|
|
counter_mover = counter.copy()
|
|
counter_mover.generate_target()
|
|
counter_mover.target.replace(result, stretch=True)
|
|
counter_mover.target.fade(1)
|
|
result.save_state()
|
|
result.replace(counter, stretch=True)
|
|
result.fade(1)
|
|
|
|
self.play(
|
|
ReplacementTransform(boxes.copy().set_fill(opacity=0), question),
|
|
MoveToTarget(counter_mover, remover=True),
|
|
Restore(result),
|
|
FadeOut(one_rects),
|
|
FadeOut(counter),
|
|
)
|
|
self.wait()
|
|
|
|
one_rects = get_one_rects(bits)
|
|
counter.set_value(len(one_rects))
|
|
words = TextMobject("Likely one error")
|
|
words.next_to(counter, DOWN, LARGE_BUFF, aligned_edge=RIGHT)
|
|
self.play(
|
|
boxes.set_fill, BLACK, 0,
|
|
FadeIn(counter),
|
|
FadeIn(one_rects),
|
|
)
|
|
self.wait()
|
|
self.play(FadeIn(words, 0.1 * UP))
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(VGroup(counter, words, one_rects), lag_ratio=0.2)
|
|
)
|
|
|
|
# Read result
|
|
final_result = results.copy()
|
|
final_result.arrange(LEFT, buff=SMALL_BUFF)
|
|
|
|
equation = VGroup(
|
|
final_result,
|
|
TexMobject("\\rightarrow"),
|
|
Integer(10)
|
|
)
|
|
equation.arrange(RIGHT)
|
|
equation.to_edge(UP)
|
|
|
|
self.play(TransformFromCopy(results, final_result, run_time=3, lag_ratio=0.3))
|
|
self.play(
|
|
Write(equation[1]),
|
|
FadeIn(equation[2], LEFT),
|
|
)
|
|
self.wait()
|
|
boxes.generate_target()
|
|
boxes.target[10].set_fill(BLUE, 0.7)
|
|
self.play(MoveToTarget(boxes))
|
|
self.play(toggle_bit_anim(bits[10]))
|
|
self.wait()
|
|
|
|
self.play(
|
|
LaggedStartMap(
|
|
FadeOut, VGroup(*equation, *questions, *results),
|
|
lambda m: (m, DOWN),
|
|
),
|
|
Restore(boxes),
|
|
)
|
|
|
|
|
|
class ByHandVsSoftwareVsHardware(Scene):
|
|
def construct(self):
|
|
self.add(get_background(GREY_E))
|
|
|
|
rects = VGroup(*[ScreenRectangle() for x in range(3)])
|
|
rects.set_stroke(GREY_B)
|
|
rects.set_fill(BLACK, 1)
|
|
rects.arrange(RIGHT, buff=MED_LARGE_BUFF)
|
|
rects.set_width(FRAME_WIDTH - 1)
|
|
rects[0].shift(UP)
|
|
rects[2].shift(DOWN)
|
|
self.add(rects)
|
|
|
|
labels = VGroup(
|
|
TextMobject("By hand"),
|
|
TextMobject("In software"),
|
|
TextMobject("In hardware"),
|
|
)
|
|
for label, rect in zip(labels, rects):
|
|
label.next_to(rect, DOWN)
|
|
|
|
randy = Randolph(height=1.25)
|
|
randy.next_to(rects[0], UP, SMALL_BUFF)
|
|
|
|
self.play(
|
|
LaggedStartMap(
|
|
FadeIn, labels,
|
|
lambda m: (m, 0.5 * UP),
|
|
lag_ratio=0.4,
|
|
),
|
|
randy.change, 'thinking', rects[0],
|
|
)
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
|
|
randy.generate_target()
|
|
randy.target.next_to(rects[1], UP, SMALL_BUFF)
|
|
randy.target.change("hooray", rects[1])
|
|
self.play(
|
|
MoveToTarget(randy, path_arc=-45 * DEGREES)
|
|
)
|
|
self.play(Blink(randy))
|
|
self.play(randy.change, 'thinking', rects[1])
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
|
|
|
|
class EndScreen(Scene):
|
|
def construct(self):
|
|
self.add(get_background(GREY_E))
|
|
|
|
rects = VGroup(*[ScreenRectangle() for x in range(2)])
|
|
rects.set_stroke(WHITE, 1)
|
|
rects.set_fill(BLACK, 1)
|
|
rects.set_height(3)
|
|
rects.arrange(RIGHT, buff=1)
|
|
rects.shift(UP)
|
|
self.add(rects)
|
|
|
|
labels = VGroup(
|
|
TextMobject("Part 2\\\\", "the elegance of it all"),
|
|
TextMobject("Ben Eater\\\\", "doing this on breadboards"),
|
|
)
|
|
|
|
for label, rect in zip(labels, rects):
|
|
label[0].scale(1.5, about_edge=DOWN)
|
|
label.scale(0.9)
|
|
label.next_to(rect, DOWN)
|
|
|
|
self.add(labels)
|
|
|
|
self.add(AnimatedBoundary(rects[0]))
|
|
self.wait()
|
|
self.add(AnimatedBoundary(rects[1]))
|
|
self.wait(19)
|
|
|
|
|
|
# Part 2
|
|
|
|
class Thumbnail2(Scene):
|
|
def construct(self):
|
|
code = ImageMobject("HammingCodeOneLine")
|
|
code.set_width(FRAME_WIDTH - 3)
|
|
self.add(code)
|
|
|
|
words = VGroup(
|
|
# TextMobject("Why one line\\\\"),
|
|
# TextMobject("finds bit errors"),
|
|
TextMobject("Hamming codes\\\\"),
|
|
TextMobject("in one(ish) line"),
|
|
)
|
|
words.set_width(FRAME_WIDTH - 3)
|
|
words[0].to_edge(UP, buff=0.5)
|
|
words[1].to_edge(DOWN, buff=0.5)
|
|
|
|
words[0].set_color(BLUE_C)
|
|
words[1].set_color(BLUE_B)
|
|
|
|
self.add(words)
|
|
|
|
|
|
class Part1Wrapper(Scene):
|
|
def construct(self):
|
|
self.add(get_background())
|
|
rect = ScreenRectangle()
|
|
rect.set_fill(BLACK, 1)
|
|
rect.set_stroke(GREY_B, 2)
|
|
rect.set_height(6)
|
|
rect.to_edge(DOWN)
|
|
title = TextMobject("Part 1")
|
|
title.set_height(0.7)
|
|
title.to_edge(UP)
|
|
|
|
self.add(rect)
|
|
self.add(AnimatedBoundary(rect, max_stroke_width=2, cycle_rate=0.25))
|
|
self.play(Write(title))
|
|
self.wait(35)
|
|
|
|
|
|
class AskHowItsImplemented(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says("How do you\\\\implement this?")
|
|
self.play(
|
|
self.teacher.change, "happy",
|
|
self.get_student_changes("pondering", "confused"),
|
|
)
|
|
self.look_at(self.screen)
|
|
self.wait(6)
|
|
|
|
|
|
class ScaleUp(Scene):
|
|
def construct(self):
|
|
square_template = Square()
|
|
square_template.set_stroke(GREY_B, 2)
|
|
square_template.set_height(1)
|
|
zero = Integer(0)
|
|
one = Integer(1)
|
|
|
|
last_grid = None
|
|
last_parity_groups = None
|
|
last_words = None
|
|
|
|
for N in range(4, 13):
|
|
grid = VGroup(*[square_template.copy() for x in range(2**N)])
|
|
grid.arrange_in_grid(
|
|
2**int(math.floor(N / 2)),
|
|
2**int(math.ceil(N / 2)),
|
|
buff=0
|
|
)
|
|
grid.set_width(5.5)
|
|
if N > 8:
|
|
grid.set_stroke(width=1)
|
|
elif N > 10:
|
|
grid.set_stroke(width=0.25)
|
|
grid.set_stroke(background=True)
|
|
|
|
grid[0].set_fill(YELLOW, 0.6)
|
|
for n in range(N):
|
|
grid[2**n].set_fill(GREEN, 0.7)
|
|
|
|
parity_groups = VGroup()
|
|
for n in range(N):
|
|
group = grid.copy()
|
|
group.set_fill(BLACK, 0)
|
|
get_bit_n_subgroup(group, n).set_fill(BLUE, 0.8)
|
|
parity_groups.add(group)
|
|
parity_groups.arrange_in_grid(n_cols=2, buff=1.5)
|
|
parity_groups.set_width(5)
|
|
max_height = 7
|
|
if parity_groups.get_height() > max_height:
|
|
parity_groups.set_height(max_height)
|
|
parity_groups.to_edge(RIGHT, buff=0.5)
|
|
|
|
random.seed(0)
|
|
for square in grid:
|
|
bit = random.choice([zero, one]).copy()
|
|
bit.replace(square, dim_to_match=1)
|
|
bit.scale(0.5)
|
|
square.add(bit)
|
|
|
|
redun = "{:.3}".format((N + 1) / (2**N))
|
|
words = TexMobject(
|
|
f"""
|
|
{{ {{{N + 1}}} \\text{{ parity bits}}
|
|
\\over
|
|
{2**N} \\text{{ bits per block}} }}
|
|
\\approx {redun}
|
|
""",
|
|
tex_to_color_map={
|
|
f"{{{N + 1}}}": GREEN,
|
|
f"{2**N}": WHITE,
|
|
f"{redun}": YELLOW,
|
|
},
|
|
fill_color=GREY_A
|
|
)
|
|
words.to_corner(UL, buff=MED_SMALL_BUFF)
|
|
grid.next_to(words, DOWN, aligned_edge=LEFT)
|
|
|
|
if last_grid is None:
|
|
self.add(grid)
|
|
self.add(parity_groups)
|
|
self.add(words)
|
|
else:
|
|
self.play(
|
|
ReplacementTransform(last_grid, grid[:2**(N - 1)]),
|
|
FadeIn(grid[2**(N - 1):], lag_ratio=0.1, run_time=2),
|
|
FadeOut(last_parity_groups),
|
|
LaggedStartMap(FadeIn, parity_groups, lag_ratio=0.2, run_time=2),
|
|
FadeOut(last_words, UP),
|
|
FadeIn(words, DOWN),
|
|
)
|
|
self.wait()
|
|
|
|
last_grid = grid
|
|
last_parity_groups = parity_groups
|
|
last_words = words
|
|
|
|
|
|
class MillionRatio(Scene):
|
|
def construct(self):
|
|
# Largely copied from above
|
|
N = 20
|
|
words = TexMobject(
|
|
"""
|
|
{21 \\text{ parity bits}
|
|
\\over
|
|
1{,}048{,}576 \\text{ bits per block} }
|
|
\\approx 0.00002
|
|
""",
|
|
tex_to_color_map={
|
|
"21": GREEN,
|
|
"1{,}048{,}576": WHITE,
|
|
"0.00002": YELLOW,
|
|
},
|
|
fill_color=GREY_A
|
|
)
|
|
words.to_corner(UL, buff=MED_SMALL_BUFF)
|
|
|
|
self.add(words)
|
|
|
|
st = Square()
|
|
st.set_stroke(GREY, 0.5)
|
|
grid = VGroup(*[st.copy() for x in range(2**(16))])
|
|
grid.arrange_in_grid(buff=0)
|
|
grid.set_height(6)
|
|
grid.next_to(words, DOWN, aligned_edge=LEFT)
|
|
|
|
self.add(grid)
|
|
|
|
k = 17
|
|
positions = VGroup(*[
|
|
TexMobject(int_to_bit_string(n, n_bits=20))
|
|
for n in [*range(k), *range(2**N - k // 2, 2**N)]
|
|
])
|
|
positions.replace_submobject(-k // 2, TexMobject("\\vdots"))
|
|
positions.arrange(DOWN)
|
|
positions.set_height(7)
|
|
positions.to_edge(RIGHT)
|
|
for n in [0, 1, 2, 4, 8, 16]:
|
|
positions[n].set_color(GREEN_B)
|
|
|
|
brace = Brace(positions, LEFT, buff=SMALL_BUFF)
|
|
p_label = TextMobject("$2^{20}$\\\\positions")
|
|
p_label.next_to(brace, LEFT, SMALL_BUFF)
|
|
|
|
self.play(
|
|
ShowIncreasingSubsets(positions, run_time=3),
|
|
GrowFromPoint(brace, brace.get_top(), run_time=3, rate_func=squish_rate_func(smooth, 0.5, 1)),
|
|
FadeIn(p_label, 0.5 * RIGHT, run_time=3, rate_func=squish_rate_func(smooth, 0.5, 1)),
|
|
)
|
|
self.wait()
|
|
|
|
grid.set_stroke(background=True)
|
|
for n in range(16):
|
|
grid.set_fill(BLACK, 0)
|
|
get_bit_n_subgroup(grid, n).set_fill(BLUE, 0.8)
|
|
self.wait(0.5)
|
|
|
|
|
|
class BurstErrors(Scene):
|
|
def construct(self):
|
|
# Setup
|
|
bl = 8
|
|
nb = 4
|
|
|
|
bits = get_bit_grid(1, bl * nb, bits=string_to_bits("3b1b"))
|
|
bits.set_height(0.5)
|
|
bits.arrange(RIGHT, buff=SMALL_BUFF)
|
|
bits.move_to(DOWN)
|
|
self.add(bits)
|
|
|
|
colors = [BLUE, YELLOW, MAROON_B, TEAL]
|
|
block_words = VGroup(*[
|
|
TextMobject(f"Block {n}", fill_color=color)
|
|
for n, color in zip(range(nb), colors)
|
|
])
|
|
block_words.set_height(0.5)
|
|
block_words.arrange(RIGHT, buff=LARGE_BUFF)
|
|
block_words.move_to(2 * UP)
|
|
self.add(block_words)
|
|
|
|
# Add lines
|
|
lines = VGroup()
|
|
for n, bit in enumerate(bits):
|
|
words = block_words[n // bl]
|
|
line = Line()
|
|
line.match_color(words)
|
|
line.set_stroke(width=2)
|
|
line.words = words
|
|
line.bit = bit
|
|
bit.line = line
|
|
underline = Underline(bit)
|
|
underline.set_stroke(words.get_color(), 4)
|
|
bit.add(underline)
|
|
line.add_updater(lambda m: m.put_start_and_end_on(
|
|
m.words.get_bottom() + SMALL_BUFF * DOWN,
|
|
m.bit.get_top(),
|
|
))
|
|
lines.add(line)
|
|
|
|
self.play(LaggedStartMap(ShowCreation, lines, suspend_mobject_updating=True))
|
|
self.wait()
|
|
|
|
# Show burst error
|
|
error_bits = bits[9:13]
|
|
error_words = TextMobject("Burst of errors")
|
|
error_words.next_to(error_bits, DOWN)
|
|
error_words.set_color(RED)
|
|
ruined_words = TextMobject("Ruined")
|
|
ruined_words.set_color(RED)
|
|
ruined_words.next_to(block_words[1], UP)
|
|
strike = Line(LEFT, RIGHT)
|
|
strike.replace(block_words[1])
|
|
strike.set_color(RED)
|
|
|
|
self.play(
|
|
LaggedStartMap(toggle_bit_anim, error_bits, target_color=RED),
|
|
LaggedStart(*map(zap_anim, error_bits)),
|
|
Write(error_words)
|
|
)
|
|
self.play(
|
|
ShowCreation(strike),
|
|
FadeIn(ruined_words, 0.5 * DOWN)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(ruined_words, 0.2 * UP),
|
|
FadeOut(error_words, 0.2 * DOWN),
|
|
FadeOut(strike),
|
|
LaggedStartMap(toggle_bit_anim, error_bits, target_color=WHITE),
|
|
run_time=1,
|
|
)
|
|
for bit in error_bits:
|
|
bit[-1].set_color(YELLOW)
|
|
|
|
# Rearrange
|
|
new_order = VGroup()
|
|
for i in range(bl):
|
|
for j in range(nb):
|
|
new_order.add(bits[bl * j + i])
|
|
new_order.generate_target()
|
|
new_order.target.arrange(RIGHT, buff=SMALL_BUFF)
|
|
new_order.target.replace(bits)
|
|
|
|
self.play(MoveToTarget(new_order, run_time=3, path_arc=30 * DEGREES))
|
|
self.wait()
|
|
|
|
# New burst
|
|
error_bits = new_order[9:13]
|
|
|
|
self.play(
|
|
LaggedStartMap(toggle_bit_anim, error_bits, target_color=RED),
|
|
LaggedStart(*map(zap_anim, error_bits)),
|
|
Write(error_words),
|
|
)
|
|
non_error_lines = VGroup()
|
|
for line in lines:
|
|
if line.bit not in error_bits:
|
|
non_error_lines.add(line)
|
|
self.play(non_error_lines.set_stroke, {"width": 1, "opacity": 0.5})
|
|
self.wait()
|
|
|
|
error_words = VGroup(*[TextMobject("1 error", fill_color=GREEN) for x in range(4)])
|
|
for ew, bw in zip(error_words, block_words):
|
|
ew.next_to(bw, UP, MED_LARGE_BUFF)
|
|
|
|
self.play(LaggedStartMap(FadeIn, error_words, lambda m: (m, DOWN)))
|
|
self.wait()
|
|
|
|
|
|
class BinaryCounting(Scene):
|
|
def construct(self):
|
|
def get_bit_grids(bit_values):
|
|
left_bits = get_bit_grid(4, 1, buff=LARGE_BUFF, bits=bit_values, height=6)
|
|
left_bits.move_to(3 * LEFT)
|
|
right_bits = get_bit_grid(1, 4, buff=SMALL_BUFF, bits=bit_values, height=0.6)
|
|
right_bits.set_submobjects(list(reversed(right_bits)))
|
|
right_bits.move_to(RIGHT + 2 * UP)
|
|
return VGroup(left_bits, right_bits)
|
|
|
|
bit_grids = get_bit_grids([0, 0, 0, 0])
|
|
|
|
brace = Brace(bit_grids[1], UP)
|
|
counter = Integer(0, edge_to_fix=ORIGIN)
|
|
counter.match_height(bit_grids[1])
|
|
counter.set_color(BLUE)
|
|
counter.next_to(brace, UP)
|
|
|
|
boxes = VGroup(*[Square() for x in range(16)])
|
|
boxes.arrange_in_grid(4, 4, buff=0)
|
|
boxes.set_height(4)
|
|
boxes.next_to(bit_grids[1], DOWN, LARGE_BUFF)
|
|
boxes.set_stroke(GREY_B, 2)
|
|
pos_labels = get_grid_position_labels(boxes)
|
|
|
|
self.add(bit_grids)
|
|
self.add(brace)
|
|
self.add(counter)
|
|
self.add(boxes)
|
|
self.add(pos_labels)
|
|
|
|
for n in range(16):
|
|
bit_values = list(map(int, int_to_bit_string(n, n_bits=4)))
|
|
boxes.generate_target()
|
|
boxes.target.set_fill(BLACK, 0)
|
|
boxes.target[n].set_fill(BLUE, 0.8)
|
|
anims = [
|
|
ChangeDecimalToValue(counter, n),
|
|
MoveToTarget(boxes)
|
|
]
|
|
for grid in bit_grids:
|
|
for bit, bv in zip(grid, reversed(bit_values)):
|
|
if get_bit_mob_value(bit) != bv:
|
|
anims.append(toggle_bit_anim(bit))
|
|
self.play(*anims, run_time=0.5)
|
|
self.wait()
|
|
|
|
|
|
class ReviewOfXOR(Scene):
|
|
CONFIG = {
|
|
"random_seed": 2,
|
|
}
|
|
|
|
def construct(self):
|
|
# Setup equations
|
|
xor = get_xor()
|
|
|
|
equations = VGroup()
|
|
for n in range(4):
|
|
bits = list(map(int, int_to_bit_string(n, n_bits=2)))
|
|
equation = VGroup(
|
|
Integer(bits[0]),
|
|
xor.copy(),
|
|
Integer(bits[1]),
|
|
TexMobject("="),
|
|
Integer(op.xor(*bits)),
|
|
)
|
|
equation.set_height(0.6)
|
|
equation.arrange(RIGHT)
|
|
equations.add(equation)
|
|
|
|
equations.arrange(DOWN, buff=LARGE_BUFF)
|
|
|
|
# Intro xor
|
|
equation = equations[1]
|
|
equation.save_state()
|
|
equation.center()
|
|
equation[3:].set_opacity(0)
|
|
|
|
arrow = Vector(0.7 * DOWN)
|
|
arrow.next_to(equation[1], UP, SMALL_BUFF)
|
|
xor_word = TextMobject("xor")
|
|
xor_word_long = TextMobject("``exclusive or''")
|
|
xor_words = VGroup(xor_word, xor_word_long)
|
|
xor_words.scale(1.5)
|
|
xor_words.match_color(xor)
|
|
xor_words.next_to(arrow, UP)
|
|
|
|
self.add(equation)
|
|
self.play(
|
|
FadeIn(equation[1], 0.5 * UP),
|
|
GrowArrow(arrow),
|
|
Write(xor_word),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
xor_word.next_to, xor_word_long, UP, MED_SMALL_BUFF,
|
|
FadeIn(xor_word_long, DOWN),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(arrow),
|
|
xor_words.to_corner, UL,
|
|
Restore(equation),
|
|
FadeIn(equations[2], UP),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(equations[0], DOWN),
|
|
FadeIn(equations[3], UP),
|
|
)
|
|
self.wait()
|
|
|
|
# Parity of two bits
|
|
parity_words = TextMobject("Parity of\\\\two bits")
|
|
parity_words.set_color(YELLOW)
|
|
parity_words.scale(1.5)
|
|
parity_words.to_edge(RIGHT, buff=MED_LARGE_BUFF)
|
|
|
|
arrows = VGroup()
|
|
for equation in equations:
|
|
globals()['equation'] = equation
|
|
new_arrows = VGroup(*[
|
|
Arrow(equation[i].get_top(), equation[4].get_top(), path_arc=-60 * DEGREES)
|
|
for i in [0, 2]
|
|
])
|
|
new_arrows.set_color(YELLOW)
|
|
arrows.add(new_arrows)
|
|
|
|
self.play(
|
|
LaggedStartMap(DrawBorderThenFill, arrows),
|
|
FadeIn(parity_words)
|
|
)
|
|
self.wait()
|
|
|
|
# Addition mod 2
|
|
mod2_words = TextMobject("Addition\\\\mod 2")
|
|
mod2_words.scale(1.5)
|
|
mod2_words.move_to(parity_words, RIGHT)
|
|
mod2_words.set_color(BLUE)
|
|
|
|
self.play(
|
|
FadeIn(mod2_words, DOWN),
|
|
FadeOut(parity_words, UP),
|
|
FadeOut(arrows)
|
|
)
|
|
self.wait()
|
|
|
|
# xor of two bit strings
|
|
row_len = 8
|
|
bit_strings = VGroup(*[
|
|
Integer(random.choice([0, 1]), edge_to_fix=ORIGIN)
|
|
for x in range(row_len * 3)
|
|
])
|
|
bit_strings.arrange_in_grid(
|
|
3, row_len,
|
|
h_buff=SMALL_BUFF,
|
|
v_buff=MED_LARGE_BUFF,
|
|
fill_rows_first=False
|
|
)
|
|
bit_strings.scale(equation[0][0].get_height() / bit_strings[0].get_height())
|
|
rows = VGroup(*[bit_strings[i::3] for i in range(3)])
|
|
rows[0].next_to(rows[1], UP, buff=0.25)
|
|
|
|
line = Line(LEFT, RIGHT)
|
|
line.set_width(bit_strings.get_width() + 1)
|
|
line.move_to(bit_strings[-2:], RIGHT)
|
|
line.set_stroke(GREY_B, 3)
|
|
line_xor = xor.copy()
|
|
line_xor.set_height(0.5)
|
|
line_xor.next_to(line, UP, aligned_edge=LEFT)
|
|
|
|
for b1, b2, b3 in zip(*[row for row in rows]):
|
|
b3.set_value(b1.get_value() ^ b2.get_value())
|
|
b2.match_x(b1)
|
|
b3.match_x(b1)
|
|
|
|
equations.generate_target()
|
|
for n, eq in enumerate(equations.target):
|
|
for k, b1 in enumerate(eq[0::2]):
|
|
target_bit = bit_strings[3 * n + k]
|
|
target_bit.set_value(b1.get_value())
|
|
b1.become(target_bit)
|
|
eq[1].become(line_xor)
|
|
eq[3].fade(1)
|
|
|
|
self.play(
|
|
MoveToTarget(equations, run_time=2),
|
|
FadeOut(mod2_words),
|
|
FadeIn(bit_strings[12:], run_time=2, rate_func=squish_rate_func(smooth, 0.5, 1), lag_ratio=0.1),
|
|
ShowCreation(line, run_time=2, rate_func=squish_rate_func(smooth, 0.5, 1))
|
|
)
|
|
self.remove(equations)
|
|
self.add(bit_strings, line_xor)
|
|
self.wait()
|
|
|
|
# Highlight columns
|
|
last_rect = VMobject()
|
|
for b1, b2, b3 in zip(*[row for row in rows]):
|
|
rect = SurroundingRectangle(VGroup(b1, b2, b3), buff=SMALL_BUFF)
|
|
rect.set_stroke(BLUE, 2)
|
|
self.play(FadeIn(rect), FadeOut(last_rect))
|
|
last_rect = rect
|
|
self.play(FadeOut(last_rect))
|
|
|
|
# Add more rows
|
|
new_rows = VGroup(*[rows[0].copy() for x in range(3)])
|
|
new_rows.arrange(DOWN, buff=0.2)
|
|
new_rows.next_to(rows, UP, buff=0.2)
|
|
for row in new_rows:
|
|
for bit in row:
|
|
bit.set_value(random.choice([0, 1]))
|
|
|
|
self.play(
|
|
LaggedStartMap(FadeIn, new_rows, lambda m: (m, DOWN)),
|
|
FadeOut(xor_words),
|
|
FadeOut(rows[2])
|
|
)
|
|
|
|
# Compute parities
|
|
parity_words = TextMobject("Computes\\\\parity\\\\of each\\\\column", alignment="")
|
|
parity_words.set_color(YELLOW)
|
|
parity_words.to_corner(UL)
|
|
self.add(parity_words)
|
|
|
|
for tup in zip(*[*new_rows, *rows]):
|
|
rects = VGroup()
|
|
for bit in tup[:-1]:
|
|
if bit.get_value() == 1:
|
|
rect = SurroundingRectangle(bit)
|
|
rect.set_stroke(YELLOW, 2)
|
|
rect.set_fill(YELLOW, 0.5)
|
|
rects.add(rect)
|
|
tup[-1].set_value(len(rects) % 2)
|
|
tup[-1].set_color(YELLOW)
|
|
self.add(rects, *tup)
|
|
self.wait()
|
|
self.remove(rects)
|
|
self.wait()
|
|
|
|
# Simpler sum
|
|
self.play(FadeOut(
|
|
VGroup(new_rows, rows[0][4:], rows[1][4:], rows[2][4:]),
|
|
lag_ratio=0.1,
|
|
))
|
|
for b1, b2, b3 in zip(*[row[:4] for row in rows]):
|
|
b3.set_value(b1.get_value() ^ b2.get_value())
|
|
self.wait(0.25)
|
|
|
|
arrows = VGroup()
|
|
integers = VGroup()
|
|
for row, n in zip(rows, [3, 5, 6]):
|
|
arrow = Vector(0.75 * RIGHT)
|
|
arrow.next_to(row[:4], RIGHT, buff=0.2)
|
|
integer = Integer(n)
|
|
integer.match_height(row[0])
|
|
integer.match_color(row[0])
|
|
integer.next_to(arrow, RIGHT, buff=0.2)
|
|
self.play(
|
|
GrowArrow(arrow),
|
|
FadeIn(integer, LEFT)
|
|
)
|
|
|
|
arrows.add(arrow)
|
|
integers.add(integer)
|
|
|
|
|
|
class ButWhy(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"Hang on...\\\\why?", target_mode="confused",
|
|
added_anims=[self.teacher.change, "tease"]
|
|
)
|
|
self.change_student_modes(
|
|
"maybe", "erm", "confused",
|
|
look_at_arg=self.screen,
|
|
)
|
|
self.wait(6)
|
|
|
|
|
|
class WhyPointToError(Scene):
|
|
def construct(self):
|
|
rect = SurroundingRectangle(TextMobject("0000").scale(2))
|
|
rect.to_edge(RIGHT)
|
|
rect.set_stroke(RED, 3)
|
|
words = TextMobject("Why do these\\\\point to an error?")
|
|
arrow = Vector(0.7 * RIGHT)
|
|
arrow.next_to(rect, LEFT)
|
|
words.next_to(arrow, LEFT)
|
|
words.set_color(RED)
|
|
|
|
bit = Integer(0).scale(2)
|
|
bit.next_to(words, UP, buff=0.45)
|
|
bit.shift(1.11 * words.get_width() * LEFT)
|
|
|
|
self.play(
|
|
Write(words),
|
|
ShowCreation(rect),
|
|
)
|
|
self.play(GrowArrow(arrow))
|
|
self.wait()
|
|
self.play(
|
|
rect.become, SurroundingRectangle(bit).match_style(rect),
|
|
FadeOut(arrow)
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class SimplePointer(Scene):
|
|
def construct(self):
|
|
arrow = Arrow(ORIGIN, [-4, 1.5, 0])
|
|
arrow.center()
|
|
arrow.set_fill(GREY_B)
|
|
self.play(DrawBorderThenFill(arrow))
|
|
self.wait()
|
|
|
|
|
|
class ArrowPair(Scene):
|
|
def construct(self):
|
|
arrows = VGroup(
|
|
Vector(LEFT),
|
|
Vector(LEFT),
|
|
)
|
|
arrows.scale(1.5)
|
|
arrows.arrange(DOWN, buff=2)
|
|
arrows[0].shift(4 * LEFT)
|
|
arrows.set_fill(YELLOW)
|
|
|
|
self.play(*map(GrowArrow, arrows))
|
|
self.wait()
|
|
|
|
|
|
class PythonXorExample(ExternallyAnimatedScene):
|
|
pass
|
|
|
|
|
|
class HammingCodesWithXOR(Scene):
|
|
def construct(self):
|
|
# Setup
|
|
bits = get_bit_grid(4, 4, bits=string_to_bits(":)"))
|
|
bits.to_edge(LEFT, buff=1.5)
|
|
boxes = get_bit_grid_boxes(bits)
|
|
block = VGroup(boxes, bits)
|
|
block.set_height(6)
|
|
for bit in bits:
|
|
bit.set_height(0.7)
|
|
|
|
bin_pos_labels = VGroup()
|
|
dec_pos_labels = VGroup()
|
|
for n, box, bit in zip(it.count(), boxes, bits):
|
|
bin_label = VGroup(
|
|
*[Integer(int(c)) for c in int_to_bit_string(n, n_bits=4)]
|
|
)
|
|
bin_label.arrange(RIGHT, buff=SMALL_BUFF, aligned_edge=DOWN)
|
|
bin_label.set_color(GREY_B)
|
|
bin_label.set_width(0.7 * box.get_width())
|
|
bin_label.move_to(box, DOWN)
|
|
bin_label.shift(SMALL_BUFF * UP)
|
|
bin_pos_labels.add(bin_label)
|
|
|
|
dec_label = Integer(n)
|
|
dec_label.match_height(bin_label)
|
|
dec_label.match_style(bin_label[0])
|
|
dec_label.move_to(bin_label, DR)
|
|
dec_pos_labels.add(dec_label)
|
|
|
|
bit.generate_target()
|
|
bit.target.scale(0.9)
|
|
bit.target.move_to(box, UP)
|
|
bit.target.shift(MED_SMALL_BUFF * DOWN)
|
|
|
|
# Enumerate
|
|
self.add(block)
|
|
kw = {"lag_ratio": 0.3, "run_time": 2}
|
|
self.play(LaggedStartMap(FadeIn, dec_pos_labels, **kw))
|
|
self.wait()
|
|
kw["lag_ratio"] = 0.1
|
|
self.play(
|
|
LaggedStartMap(FadeOut, dec_pos_labels, **kw),
|
|
LaggedStartMap(FadeIn, bin_pos_labels, **kw),
|
|
LaggedStartMap(MoveToTarget, bits, **kw),
|
|
)
|
|
self.wait()
|
|
|
|
# Highlight ones
|
|
summands = VGroup()
|
|
for bit, box, label in zip(bits, boxes, bin_pos_labels):
|
|
for mob in bit, box, label:
|
|
mob.save_state()
|
|
mob.generate_target()
|
|
|
|
if get_bit_mob_value(bit) == 1:
|
|
box.target.set_fill(BLUE, 0.7)
|
|
summands.add(label.copy())
|
|
else:
|
|
bit.target.fade(0.5)
|
|
label.target.fade(0.5)
|
|
|
|
self.play(*[
|
|
LaggedStartMap(MoveToTarget, mob, lag_ratio=0.02)
|
|
for mob in [boxes, bits, bin_pos_labels]
|
|
])
|
|
self.wait()
|
|
|
|
# Arrange sum
|
|
summands.generate_target()
|
|
summands.target.arrange(DOWN, buff=SMALL_BUFF)
|
|
summands.target.set_width(1.5)
|
|
summands.target.set_color(WHITE)
|
|
summands.target.to_edge(RIGHT, buff=1.5)
|
|
summands.target.to_edge(UP)
|
|
|
|
line = Line(LEFT, RIGHT)
|
|
xor = get_xor()
|
|
line.set_width(summands.target.get_width() + 0.75)
|
|
line.next_to(summands.target, DOWN, aligned_edge=RIGHT, buff=SMALL_BUFF)
|
|
xor.next_to(line, UP, aligned_edge=LEFT)
|
|
|
|
self.play(
|
|
MoveToTarget(summands, run_time=2),
|
|
ShowCreation(line),
|
|
ShowCreation(xor),
|
|
)
|
|
self.wait()
|
|
|
|
# Perform xor
|
|
result = VGroup()
|
|
rect_columns = VGroup()
|
|
for tup in zip(*summands):
|
|
rects = get_one_rects(tup)
|
|
result_bit = get_bit_grid(1, 1, bits=[len(rects) % 2])[0]
|
|
result_bit.replace(tup[-1], dim_to_match=1)
|
|
result_bit.shift(1.5 * result_bit.get_height() * DOWN)
|
|
result_bit.set_color(YELLOW)
|
|
|
|
result.add(result_bit)
|
|
rect_columns.add(rects)
|
|
|
|
pre_result = VGroup()
|
|
for summand in summands:
|
|
pr = result.copy()
|
|
pr.save_state()
|
|
pr.move_to(summand)
|
|
pr.fade(1)
|
|
pre_result.add(pr)
|
|
|
|
self.play(LaggedStartMap(Restore, pre_result, lag_ratio=0.05, remover=True))
|
|
self.add(result)
|
|
self.wait()
|
|
self.play(ShowCreationThenFadeOut(SurroundingRectangle(result, stroke_color=BLUE)))
|
|
self.wait()
|
|
|
|
for n, rects, result_bit in zip(it.count(), reversed(rect_columns), reversed(result)):
|
|
faders = VGroup()
|
|
for bit_vect in [*summands, result]:
|
|
for k, bit in enumerate(reversed(bit_vect)):
|
|
if k != n:
|
|
bit.generate_target()
|
|
bit.target.fade(0.7)
|
|
faders.add(bit)
|
|
for group in bits, bin_pos_labels:
|
|
sg = get_bit_n_subgroup(group, n, 0)
|
|
sg.generate_target()
|
|
sg.target.fade(1)
|
|
faders.add(sg)
|
|
faders.save_state()
|
|
|
|
self.play(LaggedStartMap(MoveToTarget, faders, lag_ratio=0, run_time=1))
|
|
|
|
new_rects = VGroup()
|
|
for bit, pos in zip(get_bit_n_subgroup(bits, n), get_bit_n_subgroup(bin_pos_labels, n)):
|
|
if get_bit_mob_value(bit) == 1:
|
|
nr = SurroundingRectangle(pos[3 - n], buff=0.05)
|
|
nr.set_fill(YELLOW, 0.25)
|
|
new_rects.add(nr)
|
|
|
|
self.play(
|
|
ShowIncreasingSubsets(rects),
|
|
ShowIncreasingSubsets(new_rects),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
faders.restore,
|
|
FadeOut(rects),
|
|
FadeOut(new_rects),
|
|
)
|
|
self.wait()
|
|
|
|
# Sender manipulations (Doing more by hand than should here...sorry)
|
|
parity_highlights = VGroup(*[boxes[2**n].copy() for n in range(4)])
|
|
parity_highlights.set_stroke(GREEN, 8)
|
|
parity_highlights.set_fill(BLACK, 0)
|
|
self.play(ShowCreation(parity_highlights))
|
|
|
|
words = TextMobject("Try to make\\\\this 0000")
|
|
words.set_color(GREEN)
|
|
words.next_to(boxes, RIGHT, MED_LARGE_BUFF)
|
|
words.to_edge(DOWN, buff=1)
|
|
arrow = Arrow(words.get_right(), result.get_left(), buff=0.1)
|
|
arrow.get_lp = words.get_right
|
|
arrow.get_rp = result.get_left
|
|
arrow.add_updater(lambda m: m.put_start_and_end_on(
|
|
m.get_lp() + SMALL_BUFF * RIGHT,
|
|
m.get_rp() + SMALL_BUFF * LEFT,
|
|
))
|
|
self.play(
|
|
Write(words),
|
|
DrawBorderThenFill(arrow),
|
|
)
|
|
self.wait()
|
|
|
|
strike = Cross(summands[0])
|
|
strike.set_stroke(RED, 8)
|
|
self.play(ShowCreation(strike))
|
|
self.add(summands[0], strike)
|
|
bits[2].generate_target()
|
|
toggle_bit(bits[2].target)
|
|
bits[2].target.fade(0.5)
|
|
self.play(
|
|
boxes[2].set_fill, BLACK, 0,
|
|
MoveToTarget(bits[2]),
|
|
bin_pos_labels[2].fade, 0.5,
|
|
toggle_bit_anim(result[2]),
|
|
FadeOut(summands[0]),
|
|
FadeOut(strike),
|
|
)
|
|
summands.remove(summands[0])
|
|
self.wait()
|
|
|
|
toggle_bit(bits[8].saved_state)
|
|
self.play(
|
|
boxes[8].set_fill, BLUE, 0.7,
|
|
Restore(bits[8]),
|
|
Restore(bin_pos_labels[8]),
|
|
)
|
|
new_term = bin_pos_labels[8].copy()
|
|
new_term.generate_target()
|
|
new_term.target.set_color(WHITE)
|
|
new_term.target.replace(summands[2])
|
|
self.play(
|
|
MoveToTarget(new_term),
|
|
summands[:3].move_to, summands[1], DOWN,
|
|
)
|
|
self.play(toggle_bit_anim(result[0]))
|
|
self.wait()
|
|
summands.set_submobjects([*summands[:3], new_term, *summands[3:]])
|
|
self.play(FadeOut(words), FadeOut(arrow), FadeOut(parity_highlights))
|
|
|
|
# Show 0 -> 1 error
|
|
pos = 11
|
|
self.play(
|
|
Restore(bits[pos]),
|
|
Restore(bin_pos_labels[pos]),
|
|
)
|
|
self.play(toggle_bit_anim(bits[pos], target_color=RED))
|
|
self.wait()
|
|
|
|
new_term = bin_pos_labels[pos].copy()
|
|
new_term.generate_target()
|
|
new_term.target.set_color(RED)
|
|
new_term.target.replace(summands[5])
|
|
bottom_group = VGroup(summands[5:], xor, line, result)
|
|
bottom_group.save_state()
|
|
self.play(
|
|
MoveToTarget(new_term),
|
|
bottom_group.move_to, summands[6], UR,
|
|
)
|
|
self.wait(0.25)
|
|
nt_copy = new_term.copy()
|
|
self.play(
|
|
nt_copy.replace, result,
|
|
nt_copy.fade, 1,
|
|
*[
|
|
toggle_bit_anim(b1, path_arc=0)
|
|
for b1, b2 in zip(result, new_term)
|
|
if b2.get_value() == 1
|
|
]
|
|
)
|
|
self.remove(nt_copy)
|
|
self.wait()
|
|
self.play(
|
|
Restore(bottom_group),
|
|
FadeOut(new_term),
|
|
toggle_bit_anim(bits[pos], target_color=WHITE)
|
|
)
|
|
self.play(
|
|
bits[pos].fade, 0.5,
|
|
bin_pos_labels[pos].fade, 0.5,
|
|
)
|
|
|
|
# Show 1 -> 0 error
|
|
pos = 6
|
|
self.play(
|
|
toggle_bit_anim(bits[pos], target_color=RED),
|
|
zap_anim(bits[pos]),
|
|
)
|
|
self.wait()
|
|
|
|
new_term = bin_pos_labels[pos].copy()
|
|
new_term.generate_target()
|
|
new_term.target.set_color(RED)
|
|
new_term.target.replace(summands[3])
|
|
bottom_group = VGroup(summands[3:], xor, line, result)
|
|
bottom_group.save_state()
|
|
self.play(
|
|
MoveToTarget(new_term),
|
|
bottom_group.move_to, summands[4], UR,
|
|
)
|
|
|
|
for bit in result:
|
|
bit[0].set_opacity(1)
|
|
bit[1].set_opacity(0)
|
|
nt_copy = new_term.copy()
|
|
self.play(
|
|
nt_copy.move_to, result,
|
|
nt_copy.fade, 1,
|
|
*[
|
|
toggle_bit_anim(b1, path_arc=0)
|
|
for b1, b2 in zip(result, new_term)
|
|
if b2.get_value() == 1
|
|
]
|
|
)
|
|
self.remove(nt_copy)
|
|
self.wait()
|
|
|
|
brace = Brace(VGroup(summands[2], new_term), LEFT, buff=SMALL_BUFF)
|
|
brace_bits = summands[2].copy()
|
|
for bb in brace_bits:
|
|
bb.set_value(0)
|
|
brace_bits.next_to(brace, LEFT)
|
|
self.play(GrowFromCenter(brace))
|
|
self.play(
|
|
Transform(summands[2].copy().unlock_triangulation(), brace_bits, remover=True),
|
|
ReplacementTransform(new_term.copy().unlock_triangulation(), brace_bits),
|
|
)
|
|
self.wait()
|
|
|
|
arrow = Arrow(result.get_left(), bin_pos_labels[pos].get_corner(DR), path_arc=-30 * DEGREES, buff=0.1)
|
|
self.play(DrawBorderThenFill(arrow))
|
|
self.wait()
|
|
|
|
self.play(toggle_bit_anim(bits[pos], target_color=WHITE))
|
|
self.play(
|
|
Restore(bottom_group),
|
|
FadeOut(new_term),
|
|
FadeOut(brace),
|
|
FadeOut(brace_bits),
|
|
FadeOut(arrow),
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class HammingSyndromePython(ExternallyAnimatedScene):
|
|
pass
|
|
|
|
|
|
class WhatAboutTwoBitDetection(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"What about\\\\detecting\\\\two bit errors?"
|
|
)
|
|
self.play(
|
|
self.get_student_changes("angry", "maybe", "raise_left_hand"),
|
|
self.teacher.change, "guilty",
|
|
)
|
|
self.look_at(self.screen)
|
|
self.wait(4)
|
|
self.play(self.teacher.change, "happy")
|
|
self.change_student_modes("confused", "erm", "pondering")
|
|
self.wait(3)
|
|
|
|
|
|
class ConflictingViewsOnXor(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.clear()
|
|
self.add(self.pi_creatures)
|
|
self.student_says(
|
|
"Um...can you\\\\say that again?",
|
|
target_mode="confused",
|
|
student_index=2,
|
|
added_anims=[self.teacher.change, "guilty"]
|
|
)
|
|
self.change_student_modes("pondering", "pondering", look_at_arg=self.screen)
|
|
self.wait(2)
|
|
self.student_says(
|
|
"Why didn't you\\\\just use xors\\\\from the start?",
|
|
target_mode="sassy",
|
|
student_index=1,
|
|
)
|
|
self.look_at(self.students[1].bubble)
|
|
self.wait(5)
|
|
|
|
|
|
class CompareXorToParityChecks(Scene):
|
|
def construct(self):
|
|
# Title
|
|
bg_rect = FullScreenRectangle()
|
|
bg_rect.set_fill(GREY_E, 1)
|
|
bg_rect.set_stroke(width=0)
|
|
self.add(bg_rect)
|
|
|
|
title = TextMobject("One algorithm, multiple perspectives")
|
|
title.scale(1.5)
|
|
title.to_edge(UP)
|
|
title.add_to_back(Underline(title))
|
|
self.add(title)
|
|
|
|
# Options
|
|
rects = VGroup(*[ScreenRectangle() for x in range(3)])
|
|
rects.set_fill(BLACK, 1)
|
|
rects.set_stroke(GREY_B, 3)
|
|
rects.set_height(3)
|
|
rects.arrange(RIGHT, buff=1)
|
|
rects.shift(-rects[:2].get_center())
|
|
|
|
labels = VGroup(
|
|
TextMobject("Multiple parity checks"),
|
|
TextMobject("One big xor"),
|
|
TextMobject("Matrix product"),
|
|
)
|
|
for label, rect in zip(labels, rects):
|
|
label.next_to(rect, DOWN)
|
|
labels.set_color(BLUE)
|
|
|
|
self.add(rects[:2])
|
|
self.add(labels[:2])
|
|
|
|
self.play(Write(title, run_time=2))
|
|
self.wait(2)
|
|
|
|
# Hardware/software labels
|
|
hw_label = TextMobject("(nicer for hardware)")
|
|
sw_label = TextMobject("(nicer for software)")
|
|
|
|
for l1, l2 in zip(labels, [hw_label, sw_label]):
|
|
l2.next_to(l1, DOWN)
|
|
|
|
self.play(FadeIn(hw_label, 0.25 * UP))
|
|
self.wait()
|
|
self.play(FadeIn(sw_label, 0.25 * UP))
|
|
self.wait()
|
|
|
|
# Third view
|
|
icons = Group(
|
|
ImageMobject("ParityCheckIcon"),
|
|
ImageMobject("XorViewIcon"),
|
|
)
|
|
for icon, rect in zip(icons, rects):
|
|
icon.replace(rect)
|
|
icon.scale(0.95)
|
|
|
|
groups = Group(
|
|
Group(rects[0], labels[0], icons[0], hw_label),
|
|
Group(rects[1], labels[1], icons[1], sw_label),
|
|
Group(rects[2], labels[2]),
|
|
)
|
|
groups.generate_target()
|
|
groups.target[:2].arrange(DOWN, buff=LARGE_BUFF)
|
|
groups.target[:2].match_height(groups.target[2])
|
|
groups.target[2].next_to(groups.target[:2], RIGHT, LARGE_BUFF)
|
|
groups.target.set_height(5)
|
|
groups.target.center()
|
|
groups.target.to_edge(DOWN, buff=1)
|
|
groups[2].set_opacity(0)
|
|
self.play(MoveToTarget(groups))
|
|
self.wait()
|
|
|
|
|
|
class LogTitle(Scene):
|
|
def construct(self):
|
|
title = TextMobject("$\\text{log}_2(256) = 8$ parity checks")
|
|
title.set_height(0.7)
|
|
title.to_edge(UP)
|
|
underline = Underline(title[0][:4])
|
|
underline.set_color(YELLOW)
|
|
self.add(title)
|
|
self.wait()
|
|
self.play(ShowCreationThenFadeOut(underline))
|
|
self.wait()
|
|
|
|
|
|
class MatrixProduct(Scene):
|
|
def construct(self):
|
|
title = TextMobject("(7, 4) Hamming code")
|
|
title.set_height(0.7)
|
|
title.to_edge(UP)
|
|
title.set_color(GREY_A)
|
|
|
|
encoder_matrix = np.array([
|
|
[1, 1, 0, 1],
|
|
[1, 0, 1, 1],
|
|
[1, 0, 0, 0],
|
|
[0, 1, 1, 1],
|
|
[0, 1, 0, 0],
|
|
[0, 0, 1, 0],
|
|
[0, 0, 0, 1],
|
|
])
|
|
message_matrix = np.array([1, 0, 1, 1]).reshape((4, 1))
|
|
result_matrix = np.dot(encoder_matrix, message_matrix) % 2
|
|
|
|
kw = {"v_buff": 0.6, "h_buff": 0.75}
|
|
encoder, message, result = [
|
|
IntegerMatrix(matrix, **kw)
|
|
for matrix in [encoder_matrix, message_matrix, result_matrix]
|
|
]
|
|
equation = VGroup(
|
|
encoder, message, TexMobject("="), result
|
|
)
|
|
equation.arrange(RIGHT, buff=MED_LARGE_BUFF)
|
|
equation.to_edge(LEFT, buff=2)
|
|
|
|
# Labels
|
|
message_label = TextMobject("Content")
|
|
message_label.move_to(message)
|
|
message_label.to_edge(DOWN)
|
|
message_arrow = Arrow(
|
|
message_label.get_top(),
|
|
message.get_bottom(),
|
|
)
|
|
message_label.set_color(YELLOW)
|
|
message_arrow.set_color(YELLOW)
|
|
|
|
brace = Brace(result, RIGHT)
|
|
brace_text = brace.get_text("Error-resistant\\\\block")
|
|
brace.set_color(BLUE)
|
|
brace_text.set_color(BLUE)
|
|
|
|
# Animate
|
|
self.add(title)
|
|
self.add(equation)
|
|
|
|
self.play(FadeIn(message_label, UP), GrowArrow(message_arrow))
|
|
self.play(GrowFromCenter(brace), FadeIn(brace_text, LEFT))
|
|
|
|
equation.set_fill(opacity=0.8)
|
|
for n in range(encoder.mob_matrix.shape[0]):
|
|
row = VGroup(*encoder.mob_matrix[n, :]).copy()
|
|
col = VGroup(*message.elements).copy()
|
|
rhs = result.mob_matrix[n, 0].copy()
|
|
|
|
mult_group = VGroup(row, col, rhs)
|
|
mult_group.set_fill(YELLOW, 1)
|
|
self.play(
|
|
ShowIncreasingSubsets(row),
|
|
ShowIncreasingSubsets(col),
|
|
FadeIn(rhs),
|
|
)
|
|
self.wait()
|
|
self.remove(mult_group)
|
|
|
|
|
|
class TooManyErrorsTripUpHamming(Scene):
|
|
def construct(self):
|
|
title = TextMobject(
|
|
"$>2$ Errors"," $\\Rightarrow$ ", "Invalid decoding"
|
|
)
|
|
title.set_height(0.7)
|
|
title[2].set_color(RED)
|
|
title.to_edge(UP)
|
|
self.add(title)
|
|
|
|
block = get_bit_grid(8, 8, bits=string_to_bits("EpicFail"), height=5.5)
|
|
block.next_to(title, DOWN, MED_LARGE_BUFF)
|
|
self.add(block)
|
|
|
|
# Animations
|
|
errors = random.sample(list(range(64)), 3)
|
|
kw = {"lag_ratio": 0.5}
|
|
self.play(
|
|
LaggedStart(*[
|
|
zap_anim(block[pos])
|
|
for pos in errors
|
|
], **kw),
|
|
LaggedStart(*[
|
|
toggle_bit_anim(block[pos], target_color=RED)
|
|
for pos in errors
|
|
], **kw),
|
|
)
|
|
|
|
scanim = scan_anim(block.get_corner(DR) + UR, block, run_time=5, lag_factor=1)
|
|
self.play(scanim)
|
|
|
|
self.play(ShowCreationThenFadeOut(Underline(title[2], color=RED)))
|
|
self.wait()
|
|
|
|
|
|
class LouisPasteurQuote(Scene):
|
|
def construct(self):
|
|
quote = TextMobject("``Luck favors a\\\\prepared mind''")
|
|
quote.scale(2)
|
|
quote.set_stroke(BLACK, 8, background=True)
|
|
self.play(Write(quote))
|
|
self.wait()
|
|
|
|
|
|
class ReedSolomonPreview(Scene):
|
|
def construct(self):
|
|
# Setup
|
|
title = TextMobject("Reed-Solomon basic idea")
|
|
title.set_height(0.5)
|
|
title.to_edge(UP, buff=MED_SMALL_BUFF)
|
|
|
|
axes = Axes(
|
|
x_range=(-1, 10, 1), y_range=(-1, 8, 1),
|
|
width=12,
|
|
height=7,
|
|
)
|
|
axes.to_edge(DOWN, buff=SMALL_BUFF)
|
|
axes.set_color(GREY_B)
|
|
|
|
cubic = axes.get_graph(
|
|
lambda x: -0.05 * x * (x - 2) * (x - 4) * (x - 8) + 2
|
|
)
|
|
cubic.set_stroke(TEAL, 3)
|
|
|
|
self.add(title)
|
|
self.add(axes)
|
|
|
|
# Data
|
|
dots = VGroup(*[
|
|
Dot(axes.input_to_graph_point(x, cubic))
|
|
for x in range(0, 8)
|
|
])
|
|
dots[:4].set_color(YELLOW)
|
|
dots[4:].set_color(BLUE)
|
|
|
|
# Input words
|
|
input_words = TextMobject("Input data")
|
|
poly_words = TextMobject("Polynomial\\\\fit")
|
|
redundant_words = TextMobject("Redundancy")
|
|
|
|
input_words.next_to(dots[:4], UP, buff=2)
|
|
input_words.set_color(YELLOW)
|
|
input_arrows = VGroup(*[
|
|
Arrow(input_words.get_bottom(), dot.get_center())
|
|
for dot in dots[:4]
|
|
])
|
|
input_arrows.set_fill(YELLOW)
|
|
|
|
poly_words.next_to(dots[5], LEFT)
|
|
poly_words.shift(0.5 * UR)
|
|
poly_words.match_color(cubic)
|
|
|
|
redundant_words.move_to(dots[4:].get_center(), LEFT)
|
|
redundant_words.shift(2 * DR + DOWN)
|
|
redundant_words.set_color(BLUE)
|
|
redundant_arrows = VGroup(*[
|
|
Arrow(redundant_words.get_corner(UL), dot.get_center())
|
|
for dot in dots[4:]
|
|
])
|
|
redundant_arrows.set_color(BLUE)
|
|
|
|
# Animations
|
|
kw = {"lag_ratio": 0.5}
|
|
self.play(
|
|
FadeIn(input_words),
|
|
LaggedStartMap(FadeIn, dots[:4], lambda m: (m, UP), **kw),
|
|
LaggedStartMap(GrowArrow, input_arrows, **kw),
|
|
)
|
|
self.add(cubic, *dots[:4])
|
|
self.play(
|
|
ShowCreation(cubic),
|
|
Write(poly_words)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(redundant_words),
|
|
LaggedStartMap(FadeIn, dots[4:], lambda m: (m, DR), **kw),
|
|
LaggedStartMap(GrowArrow, redundant_arrows, **kw),
|
|
)
|
|
self.wait()
|
|
|
|
self.play(LaggedStartMap(
|
|
FadeOut, VGroup(*reversed(self.mobjects)),
|
|
lambda m: (m, 0.2 * normalize(m.get_center())),
|
|
lag_ratio=0.1,
|
|
run_time=2,
|
|
))
|
|
self.wait()
|
|
|
|
|
|
class HammingThinking(Scene):
|
|
def construct(self):
|
|
hamming = ImageMobject("Richard_Hamming")
|
|
hamming.set_height(3)
|
|
hamming.to_corner(DL)
|
|
randy = Randolph()
|
|
randy.set_opacity(0)
|
|
randy.move_to(hamming)
|
|
|
|
self.add(hamming)
|
|
self.wait()
|
|
self.play(PiCreatureBubbleIntroduction(
|
|
randy, "What's the most efficient\\\\I could conceivably be?",
|
|
bubble_class=ThoughtBubble,
|
|
))
|
|
self.wait()
|
|
|
|
|
|
class RandomWalks(Scene):
|
|
CONFIG = {
|
|
"random_seed": 1,
|
|
}
|
|
|
|
def construct(self):
|
|
# Setup
|
|
N_PATHS = 25
|
|
frame = self.camera.frame
|
|
frame.set_height(2 * FRAME_HEIGHT)
|
|
frame.shift(2 * RIGHT)
|
|
|
|
idea_spot = 10 * RIGHT + 3 * UP
|
|
idea_dot = Dot(idea_spot)
|
|
idea_dot.set_color(YELLOW)
|
|
bulb = Lightbulb()
|
|
bulb.next_to(idea_dot, UP)
|
|
|
|
start_point = 7 * LEFT + 3 * DOWN
|
|
start_dot = Dot(start_point, color=WHITE)
|
|
start_dot.scale(2)
|
|
|
|
self.add(idea_dot, bulb, start_dot)
|
|
|
|
# Paths
|
|
paths = VGroup(*[VGroup() for n in range(N_PATHS)])
|
|
for path in paths:
|
|
path.add(Line(start_point, start_point))
|
|
path.set_stroke(WHITE, 3, 0.5)
|
|
|
|
path_dots = VGroup()
|
|
for path in paths:
|
|
# dot = Randolph(
|
|
# mode="thinking", height=0.25,
|
|
# # color=random.choice([BLUE_B, BLUE_C, BLUE_D, GREY_BROWN])
|
|
# )
|
|
dot = Dot(color=BLUE)
|
|
dot.set_stroke(BLACK, 3, background=True)
|
|
dot.path = path
|
|
dot.add_updater(lambda m: m.move_to(m.path[-1].get_end()))
|
|
path_dots.add(dot)
|
|
|
|
self.add(paths)
|
|
self.add(path_dots)
|
|
|
|
# Perform search
|
|
magic_path = None
|
|
while magic_path is None:
|
|
new_segments = VGroup()
|
|
for path in paths:
|
|
start = path[-1].get_end()
|
|
|
|
# Choose random direction based on loosely sniffing out idea spot
|
|
R_vect = start - idea_spot
|
|
R_vect = rotate_vector(10 * R_vect, TAU * random.random())
|
|
point = idea_spot + R_vect
|
|
to_point = point - start
|
|
angle = angle_of_vector(to_point)
|
|
if -3 * PI / 2 < angle <= -PI / 2:
|
|
vect = DOWN
|
|
elif -PI / 2 < angle <= PI / 2:
|
|
vect = RIGHT
|
|
elif PI / 2 < angle <= 3 * PI / 2:
|
|
vect = UP
|
|
else:
|
|
vect = LEFT
|
|
|
|
end = start + vect
|
|
new_segment = Line(start, end)
|
|
new_segment.match_style(path)
|
|
|
|
new_segments.add(new_segment)
|
|
path.add(new_segment)
|
|
|
|
if np.isclose(end, idea_spot).all():
|
|
magic_path = path.copy()
|
|
self.play(
|
|
LaggedStartMap(ShowCreation, new_segments, lag_ratio=10 / N_PATHS),
|
|
run_time=0.5,
|
|
)
|
|
self.add(paths)
|
|
|
|
# Highlight magic path
|
|
magic_path.set_stroke(YELLOW, 5, 1)
|
|
self.play(
|
|
paths.fade, 0.7,
|
|
ShowCreation(magic_path, run_time=2),
|
|
)
|
|
self.wait()
|
|
|
|
fake_path = VMobject()
|
|
fake_path.start_new_path(start_point)
|
|
for segment in magic_path:
|
|
fake_path.add_line_to(segment.get_end())
|
|
fake_path.match_style(magic_path)
|
|
|
|
line = Line(start_point, idea_spot)
|
|
line.match_style(magic_path)
|
|
line.set_stroke(TEAL, 8)
|
|
|
|
self.add(fake_path, start_dot)
|
|
self.play(
|
|
ApplyMethod(magic_path.set_opacity, 0.5),
|
|
Transform(fake_path, line, run_time=2),
|
|
paths.fade, 0.5,
|
|
path_dots.fade, 0.5,
|
|
)
|
|
self.wait(4)
|
|
|
|
|
|
class ThinkingInTermsOfBits(Scene):
|
|
def construct(self):
|
|
word = TextMobject("Information")
|
|
word.scale(2)
|
|
word.next_to(ORIGIN, LEFT, buff=0.7)
|
|
bits = get_bit_grid(11, 8, bits=string_to_bits("Information"))
|
|
bits.set_height(6)
|
|
bits.next_to(ORIGIN, RIGHT, buff=0.7)
|
|
bits.set_color(GREY_A)
|
|
|
|
for bit in bits:
|
|
toggle_bit(bit)
|
|
|
|
bits.shuffle()
|
|
|
|
self.add(word)
|
|
self.add(bits)
|
|
self.play(LaggedStartMap(
|
|
toggle_bit_anim, bits,
|
|
lag_ratio=5 / len(bits),
|
|
run_time=3,
|
|
))
|
|
self.wait(2)
|
|
|
|
|
|
class SimpleHoldUpBackground(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.play(self.teacher.change, "raise_right_hand", 3 * UP)
|
|
self.change_student_modes("pondering", "thinking", "tease", look_at_arg=3 * UP)
|
|
self.wait(4)
|
|
self.change_student_modes("tease", "hesitant", "happy", look_at_arg=3 * UP)
|
|
self.wait(5)
|
|
|
|
|
|
class HammingEndScreen(PatreonEndScreen):
|
|
CONFIG = {
|
|
"scroll_time": 25
|
|
}
|
|
|
|
|
|
# Extras
|
|
|
|
class TwoErrorGrids(Scene):
|
|
def construct(self):
|
|
grid = VGroup(*[Square() for x in range(16)])
|
|
grid.arrange_in_grid(buff=0)
|
|
grid.set_stroke(WHITE, 1)
|
|
grid.set_height(1)
|
|
|
|
grids = VGroup(*[grid.copy() for x in range(16)])
|
|
grids.arrange_in_grid(buff=MED_LARGE_BUFF)
|
|
grids.set_height(7)
|
|
grids.to_edge(RIGHT)
|
|
|
|
self.add(grids)
|
|
|
|
vects = [
|
|
np.array(tup)
|
|
for tup in it.product(*[[0, 1]] * 4)
|
|
]
|
|
|
|
def vect_to_int(vect):
|
|
return sum([b * (1 << i) for i, b in enumerate(reversed(vect))])
|
|
|
|
for vect in vects:
|
|
label = VGroup(*map(Integer, vect))
|
|
label.arrange(RIGHT, buff=SMALL_BUFF)
|
|
label.to_edge(LEFT)
|
|
self.add(label)
|
|
|
|
error_int = vect_to_int(vect)
|
|
for i, grid in enumerate(grids):
|
|
grid[i].set_fill(YELLOW, 1)
|
|
grid[i ^ error_int].set_fill(TEAL, 1)
|
|
|
|
self.wait()
|
|
grids.set_fill(opacity=0)
|
|
self.remove(label) |