トロピカル半環をPythonで実装してみる
はじめに
参考: 動的計画法を実現する代数〜トロピカル演算でグラフの最短経路を計算する〜
この記事で紹介されていたトロピカル演算でのグラフ最短経路計算が大変面白かったので、Pythonで実装してみる
トロピカル半環の実装
トロピカル半環の定義
- 加法
- 演算:
a \oplus b = \min(a, b) - 単位元:
\infty
- 演算:
- 乗法
- 演算:
a \otimes b = a + b - 単位元:
0
- 演算:
Python実装
-
TropicalNumber
クラスとして実装 - Pythonの演算子オーバーロードを使って、加法と乗法を定義
class TropicalNumber:
def __init__(self, value):
if isinstance(value, TropicalNumber):
self.value = value.value
else:
self.value = value
def __add__(self, other):
return TropicalNumber(min(self.value, other.value))
def __mul__(self, other):
return TropicalNumber(self.value + other.value)
@staticmethod
def zero():
return TropicalNumber(float("inf"))
@staticmethod
def one():
return TropicalNumber(0)
def __repr__(self):
return str(self.value)
動作確認
- 動作確認コード
tn1 = TropicalNumber(1)
tn2 = TropicalNumber(2)
tn3 = TropicalNumber(3)
tn4 = TropicalNumber(4)
print(tn1 * (tn2 + tn3))
print((tn1 + tn2) * (tn3 + tn4) )
- 出力
3
4
- 以下より期待通りの結果が得られていることが確認できる
1 \otimes (2 \oplus 3) = 1 + min(2, 3) = 3 (1 \oplus 2) \otimes (3 \oplus 4) = min(1, 2) + min(3, 4) = 4
トロピカル行列の実装
トロピカル行列の定義
- トロピカル行列は、トロピカル数の行列である
-
の例でトロピカル行列の和と積を紹介2 \times 2
トロピカル行列の和
トロピカル行列の積
トロピカル行列の累乗
Python実装
class TropicalMatrix:
def __init__(self, matrix):
self.matrix = [[TropicalNumber(value) for value in row] for row in matrix]
def __add__(self, other):
assert len(self.matrix) == len(other.matrix) and len(self.matrix[0]) == len(
other.matrix[0]
), "Matrices must have the same dimensions to add them"
result = [
[TropicalNumber.zero() for _ in range(len(self.matrix[0]))]
for _ in range(len(self.matrix))
]
for i in range(len(self.matrix)):
for j in range(len(self.matrix[0])):
result[i][j] = self.matrix[i][j] + other.matrix[i][j]
return TropicalMatrix(result)
def __mul__(self, other):
result = [
[TropicalNumber(float("inf")) for _ in range(len(other.matrix[0]))]
for _ in range(len(self.matrix))
]
for i in range(len(self.matrix)):
for j in range(len(other.matrix[0])):
for k in range(len(self.matrix[0])):
result[i][j] = result[i][j] + (
self.matrix[i][k] * other.matrix[k][j]
)
return TropicalMatrix(result)
def __pow__(self, exponent):
assert (
isinstance(exponent, int) and exponent >= 0
), "Exponent must be a non-negative integer"
if exponent == 0:
# Return the identity matrix
size = len(self.matrix)
return TropicalMatrix(
[
[
TropicalNumber.one() if i == j else TropicalNumber.zero()
for j in range(size)
]
for i in range(size)
]
)
result = self
for _ in range(1, exponent):
result = result * self
return result
def __repr__(self):
return str(self.matrix)
トロピカル行列の可視化コード
- トロピカル行列を隣接行列とするグラフとして可視化するためのコード
def visualize_tropical_graph(matrix):
dot = Digraph(comment="Tropical Graph")
# ノードの追加
n = len(matrix.matrix)
for i in range(n):
dot.node(str(i), f"Node {i}")
# エッジの追加
for i in range(n):
for j in range(n):
if matrix.matrix[i][j].value != float("inf"):
dot.edge(str(i), str(j), label=str(matrix.matrix[i][j].value))
return dot
可視化の動作確認
G = TropicalMatrix([[float('inf'), 2, 4, float('inf')],
[float('inf'), 0, 1, 9],
[float('inf'), float('inf'), float('inf'), 5],
[3, float('inf'), float('inf'), float('inf')],
])
dot = visualize_tropical_graph(G)
dot.render('G', format='png', cleanup=True)
トロピカル行列演算の動作確認
G の定義
サンプルグラフG = TropicalMatrix([[float('inf'), 2, 4, float('inf')],
[float('inf'), 0, 1, 9],
[float('inf'), float('inf'), float('inf'), 5],
[3, float('inf'), float('inf'), float('inf')],
])
G^0 の計算
G**0
[[0, inf, inf, inf], [inf, 0, inf, inf], [inf, inf, 0, inf], [inf, inf, inf, 0]]
G^2 の計算
G ** 2
[[inf, 2, 3, 9], [12, 0, 1, 6], [8, inf, inf, inf], [inf, 5, 7, inf]]
G^3 の計算
G ** 3
[[12, 2, 3, 8], [9, 0, 1, 6], [inf, 10, 12, inf], [inf, 5, 6, 12]]
G^6 の計算
G ** 6
[[11, 2, 3, 8], [9, 0, 1, 6], [19, 10, 11, 16], [14, 5, 6, 11]]
G^{100} の計算
-
は、G^{100} と同じ結果になるG^6
G ** 100
[[11, 2, 3, 8], [9, 0, 1, 6], [19, 10, 11, 16], [14, 5, 6, 11]]
最短経路の計算
G + E
G + (G ** 0)
[[0, 2, 4, inf], [inf, 0, 1, 9], [inf, inf, 0, 5], [3, inf, inf, 0]]
(G + E)^2
(G + (G ** 0)) ** 2
[[0, 2, 3, 9], [12, 0, 1, 6], [8, inf, 0, 5], [3, 5, 7, 0]]
(G + E)^3
(G + (G ** 0)) ** 3
[[0, 2, 3, 8], [9, 0, 1, 6], [8, 10, 0, 5], [3, 5, 6, 0]]
(G + E)^4
-
は、(G + E)^4 と同じ結果になる(G + E)^3
(G + (G ** 0)) ** 4
[[0, 2, 3, 8], [9, 0, 1, 6], [8, 10, 0, 5], [3, 5, 6, 0]]
おわりに
- トロピカル半環とトロピカル行列の実装を通じて、トロピカル演算の面白さを体感できた
- GPUで高速計算とかもやってみたい気がする🤔
Discussion