Matrix multiplication and Kronecker product.#

%3 outer_cluster_67 cluster_67 outer_cluster_98 cluster_98 outer_cluster_68 cluster_68 outer_cluster_93 cluster_93 outer_cluster_97 cluster_97 outer_cluster_64 cluster_64 outer_cluster_10 cluster_10 outer_cluster_41 cluster_41 outer_cluster_83 cluster_83 outer_cluster_62 cluster_62 outer_cluster_42 cluster_42 outer_cluster_75 cluster_75 outer_cluster_84 cluster_84 outer_cluster_88 cluster_88 outer_cluster_73 cluster_73 outer_cluster_85 cluster_85 outer_cluster_86 cluster_86 outer_cluster_56 cluster_56 outer_cluster_String-1487874418538667317 cluster_String-1487874418538667317 outer_cluster_String-8322038329334889976 cluster_String-8322038329334889976 outer_cluster_String-13339043360220189474 cluster_String-13339043360220189474 outer_cluster_String-1185624550498702114 cluster_String-1185624550498702114 outer_cluster_String-6487968203474512208 cluster_String-6487968203474512208 outer_cluster_String-3822497023596590183 cluster_String-3822497023596590183 nrows-935348310899929565:s->NamedMat-13339043360220189474 NamedMat-13339043360220189474:s->String-13339043360220189474 nrows-15225087114521759896:s->MMul-8708032918211032778 MMul-8708032918211032778:s->MMul-8708032918211032778 MMul-8708032918211032778:s->Id-6027701891887580415 NamedDim-6487968203474512208:s->String-6487968203474512208 ncols-935348310899929565:s->MMul-16294589661251316399 MMul-16294589661251316399:s->MMul-16294589661251316399 MMul-16294589661251316399:s->Id-6027701891887580415 ncols-15225087114521759896:s->MMul-8708032918211032778 nrows-207894513764770616:s->MMul-5949049476042656935 MMul-5949049476042656935:s->MMul-6270810559860744064 MMul-5949049476042656935:s->Kron-1277397060494327119 nrows-6911076574346317326:s->Kron-6353819159628767751 Kron-6353819159628767751:s->Id-11120055472875231265 Kron-6353819159628767751:s->NamedMat-1487874418538667317 ncols-6911076574346317326:s->Kron-6353819159628767751 Times-11457938113249271814:s->NamedDim-3822497023596590183 Times-11457938113249271814:s->ncols-3377577844511369682 NamedDim-3822497023596590183:s->String-3822497023596590183 ncols-3377577844511369682:s->NamedMat-1487874418538667317 nrows-6807129317463932018:s->NamedMat-8322038329334889976 NamedMat-8322038329334889976:s->String-8322038329334889976 nrows-4364899783852491901:s->MMul-4543523422062186886 MMul-4543523422062186886:s->MMul-4543523422062186886 MMul-4543523422062186886:s->Id-11899482898451582868 NamedDim-1185624550498702114:s->String-1185624550498702114 ncols-6807129317463932018:s->MMul-13870706696602893098 MMul-13870706696602893098:s->MMul-13870706696602893098 MMul-13870706696602893098:s->Id-11899482898451582868 ncols-4364899783852491901:s->MMul-4543523422062186886 nrows-1039295567782314873:s->Id-11120055472875231265 Id-11120055472875231265:s->ncols-1039295567782314873 nrows-3377577844511369682:s->NamedMat-1487874418538667317 NamedMat-1487874418538667317:s->String-1487874418538667317 ncols-1039295567782314873:s->Id-11120055472875231265 nrows-6859102945905124672:s->MMul-6975656641461157591 MMul-6975656641461157591:s->MMul-6975656641461157591 MMul-6975656641461157591:s->Kron-1277397060494327119 nrows-13562285006486671382:s->MMul-7430730895068077260 MMul-7430730895068077260:s->MMul-7430730895068077260 MMul-7430730895068077260:s->MMul-16173041864095337072 nrows-16108461796980496807:s->MMul-16173041864095337072 MMul-16173041864095337072:s->MMul-16173041864095337072 MMul-16173041864095337072:s->Kron-1277397060494327119 nrows-7742477628363861583:s->MMul-14965314009667168227 MMul-14965314009667168227:s->Kron-9900657283165580808 MMul-14965314009667168227:s->Kron-17125990715268860656 ncols-207894513764770616:s->MMul-6270810559860744064 MMul-6270810559860744064:s->Kron-6353819159628767751 MMul-6270810559860744064:s->MMul-16173041864095337072 ncols-6859102945905124672:s->Kron-9900657283165580808 Kron-9900657283165580808:s->NamedMat-13339043360220189474 Kron-9900657283165580808:s->Id-11899482898451582868 ncols-13562285006486671382:s->Kron-17125990715268860656 Kron-17125990715268860656:s->NamedMat-8322038329334889976 Kron-17125990715268860656:s->Id-6027701891887580415 ncols-16108461796980496807:s->Kron-1277397060494327119 Kron-1277397060494327119:s->MMul-8708032918211032778 Kron-1277397060494327119:s->MMul-4543523422062186886 ncols-7742477628363861583:s->Kron-17227345883999901159 Kron-17227345883999901159:s->MMul-16294589661251316399 Kron-17227345883999901159:s->MMul-13870706696602893098 Times-5353062789234408518:s->NamedDim-6487968203474512208 Times-5353062789234408518:s->NamedDim-1185624550498702114 Times-14155774642494946595:s->ncols-935348310899929565 Times-14155774642494946595:s->ncols-6807129317463932018 Id-11899482898451582868:s->ncols-4364899783852491901 Id-6027701891887580415:s->ncols-15225087114521759896 MMul-15520169410936116199:s->Kron-6353819159628767751 MMul-15520169410936116199:s->MMul-6975656641461157591 MMul-15146434698676836416:s->MMul-6270810559860744064 MMul-15146434698676836416:s->Kron-9900657283165580808 nrows-935348310899929565 ·.nrows NamedMat-13339043360220189474 Matrix.named nrows-15225087114521759896 ·.nrows MMul-8708032918211032778 · @ · NamedDim-6487968203474512208 Dim.named String-6487968203474512208 "n" ncols-935348310899929565 ·.ncols MMul-16294589661251316399 · @ · ncols-15225087114521759896 ·.ncols nrows-207894513764770616 ·.nrows MMul-5949049476042656935 · @ · nrows-6911076574346317326 ·.nrows Kron-6353819159628767751 kron ncols-6911076574346317326 ·.ncols Times-11457938113249271814 · * · NamedDim-3822497023596590183 Dim.named ncols-3377577844511369682 ·.ncols nrows-6807129317463932018 ·.nrows NamedMat-8322038329334889976 Matrix.named nrows-4364899783852491901 ·.nrows MMul-4543523422062186886 · @ · NamedDim-1185624550498702114 Dim.named String-1185624550498702114 "m" ncols-6807129317463932018 ·.ncols MMul-13870706696602893098 · @ · ncols-4364899783852491901 ·.ncols nrows-1039295567782314873 ·.nrows Id-11120055472875231265 Matrix.identity nrows-3377577844511369682 ·.nrows NamedMat-1487874418538667317 Matrix.named String-3822497023596590183 "p" ncols-1039295567782314873 ·.ncols nrows-6859102945905124672 ·.nrows MMul-6975656641461157591 · @ · nrows-13562285006486671382 ·.nrows MMul-7430730895068077260 · @ · nrows-16108461796980496807 ·.nrows MMul-16173041864095337072 · @ · nrows-7742477628363861583 ·.nrows MMul-14965314009667168227 · @ · ncols-207894513764770616 ·.ncols MMul-6270810559860744064 · @ · ncols-6859102945905124672 ·.ncols Kron-9900657283165580808 kron ncols-13562285006486671382 ·.ncols Kron-17125990715268860656 kron ncols-16108461796980496807 ·.ncols Kron-1277397060494327119 kron ncols-7742477628363861583 ·.ncols Kron-17227345883999901159 kron Times-5353062789234408518 · * · Times-14155774642494946595 · * · Id-11899482898451582868 Matrix.identity Id-6027701891887580415 Matrix.identity String-13339043360220189474 "A" String-1487874418538667317 "C" String-8322038329334889976 "B" MMul-15520169410936116199 · @ · MMul-15146434698676836416 · @ ·


from __future__ import annotations

from egglog import *

egraph = EGraph()


class Dim(Expr):
    """
    A dimension of a matix.

    >>> Dim(3) * Dim.named("n")
    Dim(3) * Dim.named("n")
    """

    @method(egg_fn="Lit")
    def __init__(self, value: i64Like) -> None: ...

    @method(egg_fn="NamedDim")
    @classmethod
    def named(cls, name: StringLike) -> Dim:  # type: ignore[empty-body]
        ...

    @method(egg_fn="Times")
    def __mul__(self, other: Dim) -> Dim:  # type: ignore[empty-body]
        ...


a, b, c, n = vars_("a b c n", Dim)
i, j = vars_("i j", i64)
egraph.register(
    rewrite(a * (b * c)).to((a * b) * c),
    rewrite((a * b) * c).to(a * (b * c)),
    rewrite(Dim(i) * Dim(j)).to(Dim(i * j)),
    rewrite(a * b).to(b * a),
)


class Matrix(Expr, egg_sort="MExpr"):
    @method(egg_fn="Id")
    @classmethod
    def identity(cls, dim: Dim) -> Matrix:  # type: ignore[empty-body]
        """
        Create an identity matrix of the given dimension.
        """

    @method(egg_fn="NamedMat")
    @classmethod
    def named(cls, name: StringLike) -> Matrix:  # type: ignore[empty-body]
        """
        Create a named matrix.
        """

    @method(egg_fn="MMul")
    def __matmul__(self, other: Matrix) -> Matrix:  # type: ignore[empty-body]
        """
        Matrix multiplication.
        """

    @method(egg_fn="nrows")
    def nrows(self) -> Dim:  # type: ignore[empty-body]
        """
        Number of rows in the matrix.
        """

    @method(egg_fn="ncols")
    def ncols(self) -> Dim:  # type: ignore[empty-body]
        """
        Number of columns in the matrix.
        """


@function(egg_fn="Kron")
def kron(a: Matrix, b: Matrix) -> Matrix:  # type: ignore[empty-body]
    """
    Kronecker product of two matrices.

    https://en.wikipedia.org/wiki/Kronecker_product#Definition
    """


A, B, C, D = vars_("A B C D", Matrix)
egraph.register(
    # The dimensions of a kronecker product are the product of the dimensions
    rewrite(kron(A, B).nrows()).to(A.nrows() * B.nrows()),
    rewrite(kron(A, B).ncols()).to(A.ncols() * B.ncols()),
    # The dimensions of a matrix multiplication are the number of rows of the first
    # matrix and the number of columns of the second matrix.
    rewrite((A @ B).nrows()).to(A.nrows()),
    rewrite((A @ B).ncols()).to(B.ncols()),
    # The dimensions of an identity matrix are the input dimension
    rewrite(Matrix.identity(n).nrows()).to(n),
    rewrite(Matrix.identity(n).ncols()).to(n),
)
egraph.register(
    # Multiplication by an identity matrix is the same as the other matrix
    rewrite(Matrix.identity(n) @ A).to(A),
    rewrite(A @ Matrix.identity(n)).to(A),
    # Matrix multiplication is associative
    rewrite(A @ (B @ C)).to((A @ B) @ C),
    rewrite((A @ B) @ C).to(A @ (B @ C)),
    # Kronecker product is associative
    rewrite(kron(A, kron(B, C))).to(kron(kron(A, B), C)),
    rewrite(kron(kron(A, B), C)).to(kron(A, kron(B, C))),
    # Kronecker product distributes over matrix multiplication
    rewrite(kron(A @ C, B @ D)).to(kron(A, B) @ kron(C, D)),
    rewrite(kron(A, B) @ kron(C, D)).to(
        kron(A @ C, B @ D),
        # Only when the dimensions match
        eq(A.ncols()).to(C.nrows()),
        eq(B.ncols()).to(D.nrows()),
    ),
)
egraph.register(
    # demand rows and columns when we multiply matrices
    rule(eq(C).to(A @ B)).then(
        A.ncols(),
        A.nrows(),
        B.ncols(),
        B.nrows(),
    ),
    # demand rows and columns when we take the kronecker product
    rule(eq(C).to(kron(A, B))).then(
        A.ncols(),
        A.nrows(),
        B.ncols(),
        B.nrows(),
    ),
)


# Define a number of dimensions
n = egraph.let("n", Dim.named("n"))
m = egraph.let("m", Dim.named("m"))
p = egraph.let("p", Dim.named("p"))

# Define a number of matrices
A = egraph.let("A", Matrix.named("A"))
B = egraph.let("B", Matrix.named("B"))
C = egraph.let("C", Matrix.named("C"))

# Set each to be a square matrix of the given dimension
egraph.register(
    union(A.nrows()).with_(n),
    union(A.ncols()).with_(n),
    union(B.nrows()).with_(m),
    union(B.ncols()).with_(m),
    union(C.nrows()).with_(p),
    union(C.ncols()).with_(p),
)
# Create an example which should equal the kronecker product of A and B
ex1 = egraph.let("ex1", kron(Matrix.identity(n), B) @ kron(A, Matrix.identity(m)))
rows = egraph.let("rows", ex1.nrows())
cols = egraph.let("cols", ex1.ncols())

egraph.run(20)

egraph.check(eq(B.nrows()).to(m))
egraph.check(eq(kron(Matrix.identity(n), B).nrows()).to(n * m))

# Verify it matches the expected result
simple_ex1 = egraph.let("simple_ex1", kron(A, B))
egraph.check(eq(ex1).to(simple_ex1))

ex2 = egraph.let("ex2", kron(Matrix.identity(p), C) @ kron(A, Matrix.identity(m)))

egraph.run(10)
# Verify it is not simplified
egraph.check_fail(eq(ex2).to(kron(A, C)))
egraph

Total running time of the script: (0 minutes 0.081 seconds)

Gallery generated by Sphinx-Gallery