# 最小頂点被覆問題 :::{container} prog-cpp 無向グラフ $G=(V,E)$ の**頂点被覆**とは,すべての辺 $(u,v)\in E$ に対して,少なくとも一方の端点が含まれるような部分集合 $S\subseteq V$ のことです. **最小頂点被覆問題**は,要素数が最小の頂点被覆を求める問題です. この問題はQUBO式として定式化できます. $n$ 頂点のグラフ $G=(V,E)$(頂点に $0,1,\ldots,n-1$ のラベルが付いている)に対して, $n$ 個のバイナリ変数 $x_0,x_1,\ldots, x_{n-1}$ を導入します.ここで $x_i=1$ は頂点 $i$ が選択されている(すなわち $i\in S$)場合です. 否定リテラル $\overline{x}_i$($\overline{x}_i=1$ は $x_i=0$ のとき)を用いて, すべての辺が被覆されている場合にのみ0となる以下のペナルティ項を定義します: $$ \begin{aligned} \text{constraint} &= \sum_{(i,j)\in E} \overline{x}_i\,\overline{x}_j \end{aligned} $$ 辺 $(i,j)$ に対して,積 $\overline{x}_i\,\overline{x}_j$ はどちらの端点も選択されていないとき(辺が被覆されていないとき)にのみ1となります.したがって,この和は被覆されていない辺の数を数えます. 同等に,条件 $1\leq x_i+x_j\leq 2$ は一方または両方の端点が選択されていることを意味するので,Hi-QUBO形式の定式化として次のように書けます: $$ \begin{aligned} \text{constraint'} &= \sum_{(i,j)\in E} (1\leq x_i+x_j\leq 2) \end{aligned} $$ 目的関数は,選択された頂点の数を最小化することです: $$ \begin{aligned} \text{objective} &= \sum_{i=0}^{n-1}x_i \end{aligned} $$ 最終的に,QUBO式 $f$ は次のようになります: $$ \begin{aligned} f &= \text{objective} + 2\times \text{constraint}, \text{or}\\ &= \text{objective} + \text{constraint'} \end{aligned} $$ ペナルティ係数2は,目的関数の最小化よりも制約の充足を優先するために使用されます. ## 最小頂点被覆問題のHi-QUBOプログラム 以下のHi-QUBOプログラムは,$N=16$ 頂点のグラフに対する最小頂点被覆問題を解きます: ```{literalinclude} /../programFiles/cppPrograms/example/graph/minimum-vertex-cover-program1.cpp :language: cpp :caption: minimum-vertex-cover-program1.cpp ``` このプログラムでは,上記の定式化に従って `objective`,`constraint`,`f` を構成しています. Exhaustive Solver を `f` に適用して最適解を探索します. 得られた解 `sol` は可視化され,`vertexcover.svg` として保存されます. このプログラムは以下の出力を生成します: ```{include} /../programFiles/markDown/example/graph/minimum-vertex-cover.md :start-after: :end-before: ``` 目的関数値9,制約値0の最適解が得られました. 選択された頂点がハイライトされた結果の画像を以下に示します: ![最小頂点被覆問題の解](../../../programFiles/images/vertexcover.svg) ::: :::{container} prog-python 無向グラフ $G=(V,E)$ の**頂点被覆**とは,すべての辺 $(u,v)\in E$ に対して,少なくとも一方の端点が含まれるような部分集合 $S\subseteq V$ のことです. **最小頂点被覆問題**は,要素数が最小の頂点被覆を求める問題です. この問題はQUBO式として定式化できます. $n$ 頂点のグラフ $G=(V,E)$(頂点に $0,1,\ldots,n-1$ のラベルが付いている)に対して, $n$ 個のバイナリ変数 $x_0,x_1,\ldots, x_{n-1}$ を導入します.ここで $x_i=1$ は頂点 $i$ が選択されている(すなわち $i\in S$)場合です. 否定リテラル $\overline{x}_i$($\overline{x}_i=1$ は $x_i=0$ のとき)を用いて, すべての辺が被覆されている場合にのみ0となる以下のペナルティ項を定義します: $$ \begin{aligned} \text{constraint} &= \sum_{(i,j)\in E} \overline{x}_i\,\overline{x}_j \end{aligned} $$ 辺 $(i,j)$ に対して,積 $\overline{x}_i\,\overline{x}_j$ はどちらの端点も選択されていないとき(辺が被覆されていないとき)にのみ1となります.したがって,この和は被覆されていない辺の数を数えます. 同等に,条件 $1\leq x_i+x_j\leq 2$ は一方または両方の端点が選択されていることを意味するので,Hi-QUBO形式の定式化として次のように書けます: $$ \begin{aligned} \text{constraint'} &= \sum_{(i,j)\in E} (1\leq x_i+x_j\leq 2) \end{aligned} $$ 目的関数は,選択された頂点の数を最小化することです: $$ \begin{aligned} \text{objective} &= \sum_{i=0}^{n-1}x_i \end{aligned} $$ 最終的に,QUBO式 $f$ は次のようになります: $$ \begin{aligned} f &= \text{objective} + 2\times \text{constraint}, \text{or}\\ &= \text{objective} + \text{constraint'} \end{aligned} $$ ペナルティ係数2は,目的関数の最小化よりも制約の充足を優先するために使用されます. ## 最小頂点被覆問題のPyQBPPプログラム 以下のPyQBPPプログラムは,$N=16$ 頂点のグラフに対する最小頂点被覆問題を解きます: ```{literalinclude} /../programFiles/pythonPrograms/example/graph/minimum-vertex-cover-program1.py :language: python :caption: minimum-vertex-cover-program1.py ``` このプログラムでは,上記の定式化に従って `objective`,`constraint`,`f` を構築しています. Exhaustive Solverを `f` に適用して最適解を探索します. このプログラムは以下の出力を生成します: ```{include} /../programFiles/markDown/example/graph/minimum-vertex-cover.md :start-after: :end-before: ``` 目的関数値9,制約値0の最適解が得られました. ## matplotlibによる可視化 以下のコードは頂点被覆の解を可視化し,`vertex_cover.png` として保存します: ```{literalinclude} /../programFiles/pythonPrograms/example/graph/minimum-vertex-cover-program2.py :language: python :caption: minimum-vertex-cover-program2.py ``` 被覆頂点は赤色で表示されます.すべての辺は少なくとも1つの赤い端点を持ち,選択された部分集合が有効な頂点被覆であることを確認できます. :::