Graph Coloring Problem
Given an undirected graph $G=(V,E)$, the graph coloring problem aims to assign a color to each node so that adjacent nodes receive different colors. More specifically, for a set $C$ of colors, the goal is to find an assignment $\sigma:V\rightarrow C$ such that for every edge $(u,v)\in E$, we have $\sigma(u)\neq \sigma(v)$.
Let $V=\lbrace 0,1,\ldots ,n−1\rbrace$ and $C=\lbrace 0,1,\ldots ,m−1\rbrace$. We introduce an $n\times m$ matrix $X=(x_{i,j})$ of binary variables, where $x_{i,j}=1$ if and only if node $i$ is assigned color $j$.
One-hot constraint
Since exactly one color must be assigned to each node, each row of $X$ must be one-hot:
\[\begin{aligned} \text{onehot}&= \sum_{i=0}^{n-1}\Bigl(\sum_{j=0}^{m-1}x_{i,j}==1\Bigr) \end{aligned}\]Adjacent nodes must differ
For each edge, its endpoints must not share the same color:
\[\begin{aligned} \text{different}&= \sum_{(u,v)\in E}\sum_{j=0}^{m-1}x_{u,j}x_{v,j} \end{aligned}\]QUBO objective
\[\begin{aligned} f &= \text{onehot}+\text{different} \end{aligned}\]PyQBPP program
Since any planar graph can be colored with at most four colors, we use a planar graph with 16 nodes and $m=4$ colors as an example:
import pyqbpp as qbpp
n = 16
edges = [
(0, 1), (0, 2), (0, 4), (1, 3), (1, 4), (1, 7), (2, 5),
(2, 6), (3, 7), (3, 13), (3, 15), (4, 6), (4, 7), (4, 14),
(5, 8), (6, 8), (6, 14), (7, 14), (7, 15), (8, 9), (8, 12),
(9, 10), (9, 11), (9, 12), (10, 11),(10, 12),(10, 13),(10, 14),
(10, 15),(11, 13),(12, 14),(13, 15),(14, 15)]
m = 4
x = qbpp.var("x", n, m)
onehot = qbpp.sum(qbpp.vector_sum(x) == 1)
different = 0
for u, v in edges:
different += qbpp.sum(x[u] * x[v])
f = onehot + different
f.simplify_as_binary()
solver = qbpp.EasySolver(f)
sol = solver.search({"target_energy": 0})
print(f"onehot = {sol(onehot)}")
print(f"different = {sol(different)}")
# Extract node colors
for i in range(n):
for j in range(m):
if sol(x[i][j]) == 1:
print(f"Node {i}: color {j}")
break
In this program, we first define an $n\times m$ matrix x of binary variables, and then construct the expressions onehot, different, and f. We solve the resulting QUBO using the Easy Solver by passing {"target_energy": 0} to search().
Result for $m=4$
This program produces the following output:
onehot = 0
different = 0
Therefore, a valid 4-coloring is found.
Result for $m=3$
When running with $m=3$, the program produces:
onehot = 1
different = 0
This output indicates that the solver failed to assign a color to exactly one node (i.e., one row is not one-hot).
Visualization using matplotlib
The following code visualizes the Graph Coloring solution:
import matplotlib.pyplot as plt
import networkx as nx
G = nx.Graph()
G.add_nodes_from(range(n))
G.add_edges_from(edges)
pos = nx.spring_layout(G, seed=42)
palette = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12", "#9b59b6", "#1abc9c"]
node_color = [next((k for k in range(m) if sol(x[i][k]) == 1), 0) for i in range(n)]
colors = [palette[c % len(palette)] for c in node_color]
nx.draw(G, pos, with_labels=True, node_color=colors, node_size=400,
font_size=9, edge_color="#888888", width=1.2)
plt.title(f"Graph Coloring ({m} colors)")
plt.savefig("graph_color.png", dpi=150, bbox_inches="tight")
plt.show()
Each node is colored according to its assigned color.
グラフ彩色問題
無向グラフ $G=(V,E)$ が与えられたとき、グラフ彩色問題は、隣接するノードが異なる色を持つように各ノードに色を割り当てることを目的とします。 より具体的には、色の集合 $C$ に対して、すべての辺 $(u,v)\in E$ について $\sigma(u)\neq \sigma(v)$ を満たす割り当て $\sigma:V\rightarrow C$ を求めます。
$V=\lbrace 0,1,\ldots ,n−1\rbrace$、$C=\lbrace 0,1,\ldots ,m−1\rbrace$ とします。 $n\times m$ のバイナリ変数の行列 $X=(x_{i,j})$ を導入し、$x_{i,j}=1$ はノード $i$ に色 $j$ が割り当てられている場合にのみ成り立ちます。
One-hot 制約
各ノードにちょうど1つの色が割り当てられなければならないため、$X$ の各行は one-hot でなければなりません:
\[\begin{aligned} \text{onehot}&= \sum_{i=0}^{n-1}\Bigl(\sum_{j=0}^{m-1}x_{i,j}==1\Bigr) \end{aligned}\]隣接ノードは異なる色
各辺について、その両端点は同じ色を共有してはなりません:
\[\begin{aligned} \text{different}&= \sum_{(u,v)\in E}\sum_{j=0}^{m-1}x_{u,j}x_{v,j} \end{aligned}\]QUBO 目的関数
\[\begin{aligned} f &= \text{onehot}+\text{different} \end{aligned}\]PyQBPP プログラム
任意の平面グラフは最大4色で彩色可能であるため、16ノードの平面グラフと $m=4$ 色を例として使用します:
import pyqbpp as qbpp
n = 16
edges = [
(0, 1), (0, 2), (0, 4), (1, 3), (1, 4), (1, 7), (2, 5),
(2, 6), (3, 7), (3, 13), (3, 15), (4, 6), (4, 7), (4, 14),
(5, 8), (6, 8), (6, 14), (7, 14), (7, 15), (8, 9), (8, 12),
(9, 10), (9, 11), (9, 12), (10, 11),(10, 12),(10, 13),(10, 14),
(10, 15),(11, 13),(12, 14),(13, 15),(14, 15)]
m = 4
x = qbpp.var("x", n, m)
onehot = qbpp.sum(qbpp.vector_sum(x) == 1)
different = 0
for u, v in edges:
different += qbpp.sum(x[u] * x[v])
f = onehot + different
f.simplify_as_binary()
solver = qbpp.EasySolver(f)
sol = solver.search({"target_energy": 0})
print(f"onehot = {sol(onehot)}")
print(f"different = {sol(different)}")
# Extract node colors
for i in range(n):
for j in range(m):
if sol(x[i][j]) == 1:
print(f"Node {i}: color {j}")
break
このプログラムでは、まず $n\times m$ のバイナリ変数の行列 x を定義し、次に式 onehot、different、f を構築します。 得られた QUBO を、search() に {"target_energy": 0} を渡して Easy Solver で解きます。
$m=4$ の場合の結果
このプログラムは以下の出力を生成します:
onehot = 0
different = 0
したがって、有効な4彩色が見つかりました。
$m=3$ の場合の結果
$m=3$ で実行すると、プログラムは以下の出力を生成します:
onehot = 1
different = 0
この出力は、ソルバーがちょうど1つのノードに色を割り当てることに失敗したことを示しています(すなわち、1つの行が one-hot ではありません)。
matplotlib による可視化
以下のコードは、グラフ彩色の解を可視化します:
import matplotlib.pyplot as plt
import networkx as nx
G = nx.Graph()
G.add_nodes_from(range(n))
G.add_edges_from(edges)
pos = nx.spring_layout(G, seed=42)
palette = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12", "#9b59b6", "#1abc9c"]
node_color = [next((k for k in range(m) if sol(x[i][k]) == 1), 0) for i in range(n)]
colors = [palette[c % len(palette)] for c in node_color]
nx.draw(G, pos, with_labels=True, node_color=colors, node_size=400,
font_size=9, edge_color="#888888", width=1.2)
plt.title(f"Graph Coloring ({m} colors)")
plt.savefig("graph_color.png", dpi=150, bbox_inches="tight")
plt.show()
各ノードは割り当てられた色に従って彩色されます。