エレファント・ビジュアライザー調査記録

ビジュアルプログラミングで数式の変形を表すことを考えていくブロクです。

人工知能的代数学(6)

交換子の計算(2)

「マグマの左単位元と右単位元」の問題の構文解析を ChatGPT でやってみようと入力したところ、「Lark ライブラリ」を使ったプログラムが返ってきました。そこでいったんこのプログラムを自分で改造して交換子の計算の構文解析をやってみることにしました。

class GroupExp:
    def __init__(self, exp, struct_exp = None):
        if exp == "e":
            self.exp = ""
        else:
            self.exp = exp
        if struct_exp is not None:
            self.struct_exp = struct_exp
        else:
            if exp == "":
                self.struct_exp = ("const", "e")
            else:
                self.struct_exp = ("const", exp)

    def __add__(self, other):
        # 積
        if isinstance(other, GroupExp):
            result_str = self.remove_opposite_case_pairs(self.exp + other.exp)
            return GroupExp(result_str, self.prod_struct(other))
        raise TypeError("Operand must be a GroupExp")

    def __sub__(self, other):
        # 逆元との積
        if isinstance(other, GroupExp):
            return self + - other
        raise TypeError("Operand must be a GroupExp")

    def __neg__(self):
        # 逆元
        result_str = self.reverse_and_swapcase(self.exp)
        return GroupExp(result_str, self.inv_struct())

    def __mul__(self, other):
        # 交換子
        if isinstance(other, GroupExp):
            return (- self - other + self + other).replace_struct(self.com_struct(other))
        raise TypeError("Operand must be a GroupExp")

    def __pow__(self, other):
        # 共役
        if isinstance(other, GroupExp):
            return (- other + self + other).replace_struct(self.conj_struct(other))
        raise TypeError("Operand must be a GroupExp")

    def prod_struct(self, other):
        # 積の構造
        return ("prod", self.struct_exp, other.struct_exp)

    def inv_struct(self):
        # 逆元の構造
        return ("inv", self.struct_exp)

    def com_struct(self, other):
        # 交換子の構造
        return ("com", self.struct_exp, other.struct_exp)

    def conj_struct(self, other):
        # 共役の構造
        return ("conj", self.struct_exp, other.struct_exp)

    def replace_struct(self, struct_exp):
        # 構造を置き換える
        return GroupExp(self.exp, struct_exp)

    def __str__(self):
        return self.exp

    def __repr__(self):
        return f"GroupExp({self.exp})"
    
    def reverse_and_swapcase(self, text):
        # 文字列を逆順にする
        reversed_text = text[::-1]
        # 大文字と小文字を入れ替える
        swapped_text = reversed_text.swapcase()
        return swapped_text
    
    def remove_opposite_case_pairs(self, text):
        # 文字列の中で大文字とその文字の小文字、または小文字とその文字の大文字が
        # 連続しているペアを削除します。
    
        i = 0
        while i < len(text) - 1:
            # 現在の文字と次の文字を比較
            current = text[i]
            next_char = text[i + 1]
        
            # 大文字と小文字の組み合わせ、またはその逆を検出
            if (current.lower() == next_char.lower() and
                current.islower() != next_char.islower()):
                # このペアを削除するために、前と次を繋げてスライス
                text = text[:i] + text[i + 2:]
                # 削除されたので、再度この位置からチェック
                i = max(0, i - 1)
            else:
                # 削除がない場合は、次に進む
                i += 1
    
        return text

class GroupEquation:
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def __str__(self):
        return str(self.left) + " = " + str(self.right)

    def __repr__(self):
        return f"GroupEquation({self.left} = {self.right})"

    def equal(self):
        return str(self.left) == str(self.right)

def tex_print(s):
    if len(s) == 2:
        struct_type, x = s
        if struct_type == "const":
            return x
        elif struct_type == "inv":
            return tex_print(x) + "^{-1}"
    if len(s) == 3:
        struct_type, x, y = s
        if struct_type == "prod":
            return tex_print(x) + tex_print(y)
        elif struct_type == "com":
            return "[" + tex_print(x) + ", " + tex_print(y) + "]"
        elif struct_type == "conj":
            return tex_print(x) + "^{" + tex_print(y) + "}"

def etex_print(eq):
    return tex_print(eq.left.struct_exp) + " = " + tex_print(eq.right.struct_exp)

def equation_print(src):
    print("src: ", src)
    tree = parser.parse(src)
    eq = custom_transformer.transform(tree)
    print(eq)
    if (eq.equal()):
        print("一致しました")
        print(etex_print(eq))
    else:
        print("違います")

from lark import Transformer, v_args

@v_args(inline=True)  # 引数をリストではなく個別の要素として受け取る
class CustomTransformer(Transformer):
    def equation(self, left, right):
        return GroupEquation(left, right)  # 等式

    def multiplication(self, left, right):
        return left + right  # 積

    def conjugate_trans(self, left, right):
        return left ** right  # 共役

    def commutator(self, left, right):
        return left * right  # 交換子

    def inverse_trans(self, exp):
        return - exp  # 逆元

    def expression(self, item):
        return item

    def conjugate(self, item):
        return item

    def inverse(self, item):
        return item

    def term(self, item):
        return item

    def atom(self, item):
        return item

    def VARIABLE(self, name):
        return GroupExp(str(name))  # Variable オブジェクトとして返す

    def CONSTANT(self, name):
        return GroupExp(str(name))  # Constant オブジェクトとして返す
    
# グラマー定義
grammar = """
?start: equation

equation: expression "=" expression

expression: conjugate
    | expression "*" conjugate -> multiplication      # 積

conjugate: inverse
    | inverse "^" inverse -> conjugate_trans        # 共役
    
inverse: term
    | inverse "~" -> inverse_trans                    # 逆元
    
term: atom
    | "(" expression ")"  # 括弧を使った演算
    | "[" expression "," expression "]" -> commutator # 交換子

atom: VARIABLE
    | CONSTANT  # 変数と定数

VARIABLE: /[A-Z][a-zA-Z_]*/  # 大文字で始まる変数
CONSTANT: /[a-z][a-zA-Z_]*/  # 小文字で始まる定数

%ignore " "  # 空白を無視
"""

# 構文解析
from lark import Lark
parser = Lark(grammar, parser="lalr")
custom_transformer = CustomTransformer()

# x^y = x[x, y]
equation_print("x^y = x*[x, y]")

# [y, x] = [x, y]^-1
equation_print("[y, x] = [x, y]~")

# [xy,z] = [x,z]^y[y,z]
equation_print("[x*y,z] = [x,z]^y*[y,z]")

# [x,yz] = [x,z][x,y]^z
equation_print("[x,y*z] = [x,z]*[x,y]^z")

# [x,y^−1] = [y,x]^y^−1
equation_print("[x,y~] = [y,x]^y~")

# [x^−1,y] = [y,x]^x^−1
equation_print("[x~,y] = [y,x]^x~")

# [[x,y^−1],z]^y[[y,z^−1],x]^z[[z,x^−1],y]^x = e
equation_print("[[x,y~],z]^y*[[y,z~],x]^z*[[z,x~],y]^x = e")

# [[x,y],z^x][[z,x],y^z][[y,z],x^y] = e
equation_print("[[x,y],z^x]*[[z,x],y^z]*[[y,z],x^y] = e")

# [x,y]^z = [x^z, y^z]
equation_print("[x,y]^z = [x^z, y^z]")
結果

src: x^y = x*[x, y]
Yxy = Yxy
一致しました
 x^{y} = x[x, y]

src: [y, x] = [x, y]~
YXyx = YXyx
一致しました
 [y, x] = [x, y]^{-1}

src: [x*y,z] = [x,z]^y*[y,z]
YXZxyz = YXZxyz
一致しました
 [xy, z] = [x, z]^{y}[y, z]

src: [x,y*z] = [x,z]*[x,y]^z
XZYxyz = XZYxyz
一致しました
 [x, yz] = [x, z][x, y]^{z}

src: [x,y~] = [y,x]^y~
XyxY = XyxY
一致しました
 [x, y^{-1}] = [y, x]^{y^{-1}}

src: [x~,y] = [y,x]^x~
xYXy = xYXy
一致しました
 [x^{-1}, y] = [y, x]^{x^{-1}}

src: [[x,y~],z]^y*[[y,z~],x]^z*[[z,x~],y]^x = e
=
一致しました
 [[x, y^{-1}], z]^{y}[[y, z^{-1}], x]^{z}[[z, x^{-1}], y]^{x} = e

src: [[x,y],z^x]*[[z,x],y^z]*[[y,z],x^y] = e
=
一致しました
 [[x, y], z^{x}][[z, x], y^{z}][[y, z], x^{y}] = e

src: [x,y]^z = [x^z, y^z]
ZXYxyz = ZXYxyz
一致しました
 [x, y]^{z} = [x^{z}, y^{z}]