2018-04-01 10:21:42 -07:00
|
|
|
import numpy as np
|
|
|
|
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.animation.creation import ShowCreation
|
2019-02-09 09:08:57 -08:00
|
|
|
from manimlib.animation.fading import FadeOut
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.animation.transform import ApplyMethod
|
|
|
|
from manimlib.animation.transform import Transform
|
|
|
|
from manimlib.constants import *
|
|
|
|
from manimlib.mobject.geometry import Circle
|
|
|
|
from manimlib.mobject.geometry import Line
|
|
|
|
from manimlib.mobject.matrix import Matrix
|
2021-01-18 08:20:14 -10:00
|
|
|
from manimlib.mobject.svg.tex_mobject import Tex
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
|
|
|
from manimlib.scene.scene import Scene
|
2018-04-01 10:21:42 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-04-01 10:21:42 -07:00
|
|
|
class NumericalMatrixMultiplication(Scene):
|
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"left_matrix": [[1, 2], [3, 4]],
|
|
|
|
"right_matrix": [[5, 6], [7, 8]],
|
|
|
|
"use_parens": True,
|
2018-04-01 10:21:42 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-04-01 10:21:42 -07:00
|
|
|
def construct(self):
|
|
|
|
left_string_matrix, right_string_matrix = [
|
|
|
|
np.array(matrix).astype("string")
|
2018-06-02 08:59:26 -04:00
|
|
|
for matrix in (self.left_matrix, self.right_matrix)
|
2018-04-01 10:21:42 -07:00
|
|
|
]
|
|
|
|
if right_string_matrix.shape[0] != left_string_matrix.shape[1]:
|
|
|
|
raise Exception("Incompatible shapes for matrix multiplication")
|
|
|
|
|
|
|
|
left = Matrix(left_string_matrix)
|
|
|
|
right = Matrix(right_string_matrix)
|
|
|
|
result = self.get_result_matrix(
|
|
|
|
left_string_matrix, right_string_matrix
|
|
|
|
)
|
|
|
|
|
|
|
|
self.organize_matrices(left, right, result)
|
|
|
|
self.animate_product(left, right, result)
|
|
|
|
|
|
|
|
def get_result_matrix(self, left, right):
|
|
|
|
(m, k), n = left.shape, right.shape[1]
|
2018-04-06 13:58:59 -07:00
|
|
|
mob_matrix = np.array([VGroup()]).repeat(m * n).reshape((m, n))
|
2018-04-01 10:21:42 -07:00
|
|
|
for a in range(m):
|
|
|
|
for b in range(n):
|
|
|
|
template = "(%s)(%s)" if self.use_parens else "%s%s"
|
|
|
|
parts = [
|
2018-04-06 13:58:59 -07:00
|
|
|
prefix + template % (left[a][c], right[c][b])
|
2018-04-01 10:21:42 -07:00
|
|
|
for c in range(k)
|
|
|
|
for prefix in ["" if c == 0 else "+"]
|
|
|
|
]
|
2021-01-18 08:20:14 -10:00
|
|
|
mob_matrix[a][b] = Tex(parts, next_to_buff=0.1)
|
2018-04-01 10:21:42 -07:00
|
|
|
return Matrix(mob_matrix)
|
|
|
|
|
|
|
|
def add_lines(self, left, right):
|
|
|
|
line_kwargs = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"color": BLUE,
|
|
|
|
"stroke_width": 2,
|
2018-04-01 10:21:42 -07:00
|
|
|
}
|
|
|
|
left_rows = [
|
|
|
|
VGroup(*row) for row in left.get_mob_matrix()
|
|
|
|
]
|
|
|
|
h_lines = VGroup()
|
|
|
|
for row in left_rows[:-1]:
|
|
|
|
h_line = Line(row.get_left(), row.get_right(), **line_kwargs)
|
2018-04-06 13:58:59 -07:00
|
|
|
h_line.next_to(row, DOWN, buff=left.v_buff / 2.)
|
2018-04-01 10:21:42 -07:00
|
|
|
h_lines.add(h_line)
|
|
|
|
|
|
|
|
right_cols = [
|
|
|
|
VGroup(*col) for col in np.transpose(right.get_mob_matrix())
|
|
|
|
]
|
|
|
|
v_lines = VGroup()
|
|
|
|
for col in right_cols[:-1]:
|
|
|
|
v_line = Line(col.get_top(), col.get_bottom(), **line_kwargs)
|
2018-04-06 13:58:59 -07:00
|
|
|
v_line.next_to(col, RIGHT, buff=right.h_buff / 2.)
|
2018-04-01 10:21:42 -07:00
|
|
|
v_lines.add(v_line)
|
|
|
|
|
|
|
|
self.play(ShowCreation(h_lines))
|
|
|
|
self.play(ShowCreation(v_lines))
|
|
|
|
self.wait()
|
|
|
|
self.show_frame()
|
|
|
|
|
|
|
|
def organize_matrices(self, left, right, result):
|
2021-01-18 08:20:14 -10:00
|
|
|
equals = Tex("=")
|
2018-04-01 10:21:42 -07:00
|
|
|
everything = VGroup(left, right, equals, result)
|
2019-02-04 14:54:25 -08:00
|
|
|
everything.arrange()
|
2018-08-08 10:30:52 -07:00
|
|
|
everything.set_width(FRAME_WIDTH - 1)
|
2018-04-01 10:21:42 -07:00
|
|
|
self.add(everything)
|
|
|
|
|
|
|
|
def animate_product(self, left, right, result):
|
|
|
|
l_matrix = left.get_mob_matrix()
|
|
|
|
r_matrix = right.get_mob_matrix()
|
|
|
|
result_matrix = result.get_mob_matrix()
|
|
|
|
circle = Circle(
|
2018-04-06 13:58:59 -07:00
|
|
|
radius=l_matrix[0][0].get_height(),
|
|
|
|
color=GREEN
|
2018-04-01 10:21:42 -07:00
|
|
|
)
|
|
|
|
circles = VGroup(*[
|
|
|
|
entry.get_point_mobject()
|
2018-06-02 08:59:26 -04:00
|
|
|
for entry in (l_matrix[0][0], r_matrix[0][0])
|
2018-04-01 10:21:42 -07:00
|
|
|
])
|
|
|
|
(m, k), n = l_matrix.shape, r_matrix.shape[1]
|
|
|
|
for mob in result_matrix.flatten():
|
|
|
|
mob.set_color(BLACK)
|
|
|
|
lagging_anims = []
|
|
|
|
for a in range(m):
|
|
|
|
for b in range(n):
|
|
|
|
for c in range(k):
|
|
|
|
l_matrix[a][c].set_color(YELLOW)
|
|
|
|
r_matrix[c][b].set_color(YELLOW)
|
|
|
|
for c in range(k):
|
|
|
|
start_parts = VGroup(
|
|
|
|
l_matrix[a][c].copy(),
|
|
|
|
r_matrix[c][b].copy()
|
|
|
|
)
|
|
|
|
result_entry = result_matrix[a][b].split()[c]
|
|
|
|
|
|
|
|
new_circles = VGroup(*[
|
|
|
|
circle.copy().shift(part.get_center())
|
|
|
|
for part in start_parts.split()
|
|
|
|
])
|
|
|
|
self.play(Transform(circles, new_circles))
|
|
|
|
self.play(
|
|
|
|
Transform(
|
2018-04-06 13:58:59 -07:00
|
|
|
start_parts,
|
|
|
|
result_entry.copy().set_color(YELLOW),
|
|
|
|
path_arc=-np.pi / 2,
|
2019-02-08 15:49:06 -08:00
|
|
|
lag_ratio=0,
|
2018-04-01 10:21:42 -07:00
|
|
|
),
|
|
|
|
*lagging_anims
|
|
|
|
)
|
|
|
|
result_entry.set_color(YELLOW)
|
|
|
|
self.remove(start_parts)
|
|
|
|
lagging_anims = [
|
|
|
|
ApplyMethod(result_entry.set_color, WHITE)
|
|
|
|
]
|
|
|
|
|
|
|
|
for c in range(k):
|
|
|
|
l_matrix[a][c].set_color(WHITE)
|
|
|
|
r_matrix[c][b].set_color(WHITE)
|
|
|
|
self.play(FadeOut(circles), *lagging_anims)
|
2018-04-06 13:58:59 -07:00
|
|
|
self.wait()
|