Home

Takeru Inoue edited this page Mar 2, 2017 · 22 revisions
Clone this wiki locally

Graphillion - 無数のグラフを効率的に扱うための高速・軽量ライブラリ

最新のドキュメントは英語版を参照ください.

ニュース

  • Graphillion 1.0 がとうとうリリースされました.
    • Python 3 をサポートしました (Python 2 でも使えます).
    • OpenMP を用いた 並列計算 が可能になりました.
    • より効率的な辺の順序づけが実装されました.
    • 高度な集合演算 (join, meet, quotient など) が追加されました.
  • 2015年4月に,Graphillion の本が出版されます.
  • 2015年1-2月に,奈良先端科学技術大学院大学の川原純先生による講義で Graphillion が使われました.

特徴

Graphillion は、グラフセットもしくはグラフ集合に対する検索、最適化および列挙のための Python パッケージです。

  • 超大規模グラフを扱うための軽量なデータ構造
  • 膨大な数のグラフに対する検索、最適化、列挙
  • 電力網の評価(DNET)、鉄道網分析(Ekillion)などでの使用実績 (詳しくは 参考資料)
  • C/C++ により Python を拡張する効率的な実装
  • OpenMP による 並列計算
  • NetworkX のようなグラフツールとの連携
  • オープンソースの MIT ライセンス
  • 600 以上のテストをクリアした信頼性
  • Python によるメリット:高速なプロトタイピング、教えやすさ、マルチ・プラットフォーム

Graphillion について興味を持たれた方は、以下の動画もご覧ください。

概要

Graphillion は、効率的なグラフセット操作のための Python ライブラリです。 NetworkX のような既存のグラフツールが1つのグラフを操作するために設計されているのと比較して、Graphillion は大規模なグラフ集合を効率的に扱うことができます。 驚くべきことに、Graphillion は1兆の1兆倍といった数のグラフをたった1台のコンピュータで扱うことができます。

「グラフセット」という概念は一般的ではないかもしれませんが、グラフから複数のサブグラフを切り出すような場合に必要となる考え方です。 たとえば、道路地図から走行可能なルートを割り出したり、電力網において実現可能な送電経路を調べたり、あるいは化学反応ネットワークの構造を評価したりする場合です。 サブグラフの数はグラフサイズによって指数関数的に増加するので、数百程度のエッジからなるグラフでも、サブグラフは何兆という数になり得ます。 上の動画でも示したように、このサブグラフを単純に数え上げていては何百万年もかかってしまいます。 Graphillion は、この問題を解決します。

Graphillion によって、複雑かつ非凸型制約のグラフセットに対する効率的で網羅的な検索が可能となります。 加えて、複雑なグラフセットから上位k個の最適グラフを見つけたり、集合に含まれる全グラフから共通の特性を抽出することもできます。 これらの特徴により、Graphillion はグラフ・データベース、組み合わせ最適化、グラフ構造分析などさまざまな目的に応用できます。 電力網の評価を含むいくつかの使用例を、このあとのチュートリアルで紹介します。

Graphillion は MIT ライセンスの下で自由に使用することができます。 また Graphillion は主に JST ERATO 湊離散構造処理系プロジェクトにて開発されています。 論文に Graphillion を使用される場合は、以下の論文を記載くださいますようお願いいたします。

Takeru Inoue, Hiroaki Iwashita, Jun Kawahara, and Shin-ichi Minato: "Graphillion: Software Library Designed for Very Large Sets of Labeled Graphs," International Journal on Software Tools for Technology Transfer, Springer, vol.18, issue 1, pp.57-66, February 2016. (pdf)

Graphillion は開発途上です。 利用していただき、多くの人々に利益をもたらす改良は大歓迎です。

では、Graphillion のインストールとチュートリアルに進みましょう。 Graphllion のパワーと実用性をぜひ体験して下さい。

インストール

インストール要件

すべてのOSに共通する項目

  • 64ビットマシン
    • 桁数の大きな数値を扱うために必要
  • Python 2.7/3.4 以上
  • NetworkX and Matplotlib (任意)
    • NetworkX および Matplotlib はグラフの作成および描画のための Python モジュールです。これらのパッケージは Graphillion には必要ありませんが、チュートリアルで使用します。次のコマンドでインストールできます。
$ sudo pip install networkx
$ sudo pip install matplotlib

UNIX (Linux や macOS)

  • Python 開発環境 (Python.h)
    • macOS では XCode に含まれています.Ubuntu では apt-get install python-dev でインストールできます.
  • GCC や Clang などの C++ コンパイラ
    • Graphillion のビルドには、gcc バージョン 4.2 以上が必要です。
    • Mac OS X で Graphillion を使うには、Apple が配布している Xcode の Clang をお使い下さい。でないと、メモリアロケーションエラーが発生する可能性があります。

Windows

桁数の大きな数値を効率的に扱うため、Graphillion には 64 ビット環境が必要です。

クイック・インストール

次のようにタイプするだけです。

$ sudo pip install graphillion

お使いの OS および Python に最適なバージョンがインストールされます。

FreeBSD をお使いの場合は、FreeBSD Ports によってもインストールが可能です。

ソースコードからのインストール

ソースコードのアーカイブファイル(tar.gz または zip ファイル)をダウンロードして、または GitHub ソースコード・リポジトリからチェックアウトして、インストールすることができます。

ソースコードのアーカイブファイル

  1. ソースコード(tar.gz または zip ファイル)を https://github.com/takemaru/graphillion からダウンロードする
  2. アーカイブを展開し、ソースコードのディレクトリ(setup.py があるディレクトリ)に移動する
  3. python setup.py build を実行してビルドする
  4. (任意) python setup.py test -q でテストを行う
  5. sudo python setup.py install を実行してインストールする

GitHub リポジトリ

  1. Graphillion のリポジトリをクローンする git clone https://github.com/takemaru/graphillion.git
  2. "graphillion" ディレクトリに移動する
  3. python setup.py build を実行してビルドする
  4. (任意)python setup.py test -q でテストを行う
  5. sudo python setup.py install を実行してインストールする

もしシステムにソフトウェアをインストールする権限を持っていない場合は、-user-prefix-home オプションを setup.py に指定して、別のディレクトリへインストールすることができます。 以下の例を参照して下さい。

$ python setup.py install --prefix=/home/username/python
  or
$ python setup.py install --home=~
  or
$ python setup.py install --user

もし標準的な site-packages ディレクトリ以外に Python をインストールした場合は、PYTHONPATH 変数を適切に設定する必要があります。 詳しくは http://docs.python.org/inst/search-path.html を参照して下さい。

Windows

Graphillion for Windows をご覧ください.

チュートリアル

もしまだ おねえさんといっしょ! みんなで数えてみよう! 動画をご覧になっていない場合は、チュートリアルを始める前にぜひご覧下さい。 100万回以上再生されているこの動画を見れば、Graphillion の必要性が納得できると思います。 また Graphillion: 数え上げおねえさんを救え 動画は、このチュートリアルの要約になっています。

これらの動画によって、Graphillion の必要性と特徴は理解いただけたと思います。 それでは Graphillion について詳しく見ていきましょう。

まず、Graphillion で使われる用語についていくつか紹介します。

用語 説明
頂点(vertex) 任意のハッシュ可能オブジェクト 1, 'v1', (x, y)
辺(edge) 頂点のタプル(組) (1, 2)
重み付き辺(weighted edge) 重みを持った頂点のタプル (1, 2, -1.5)
グラフ(graph) (重みを持った)辺のリスト [(1, 2, -1.5), (1, 3)]
グラフ集合(set of graphs) グラフセット GraphSet([[(1, 2), (1, 3)], [(1, 2), (2, 3)]])

数値、テキスト文字列などハッシュ可能なあらゆるオブジェクトが頂点(ノード)になることができます。 辺(リンク)は頂点のペアとして定義され、グラフは辺のリストとして定義されます。 現時点では、Graphillion は無向グラフにのみ対応します。 グラフセットオブジェクトにはグラフの集合が格納されます。

何よりもまず、Python を起動し Graphillion およびヘルパー・モジュールをインポートしましょう。 ヘルパー・モジュールは、チュートリアルで用いるグラフの作成や描画に関するいくつかの機能を提供します。

$ python
>>> from graphillion import GraphSet
>>> import graphillion.tutorial as tl  # チュートリアルのためのヘルパー・モジュール

格子グラフ上のパス

まずはじめに、「ユニバース」を定義します。ユニバースはグラフであり、Graphillion が扱うグラフはこのグラフの部分グラフとなります。 このチュートリアルでは、8×8の格子グラフをユニバースとして使います(グラフサイズは9×9とみなすべきですが、ここでは動画での定義に従うことにします)。

>>> universe = tl.grid(8, 8)
>>> GraphSet.set_universe(universe)
>>> tl.draw(universe)  # ユニバースをポップアップウィンドウで表示する

A grid graph

対角頂点間を結ぶすべてのパスを見つけてみましょう。 動画では、スーパーコンピュータで4時間かかる処理でしたよね。

>>> start = 1
>>> goal = 81
>>> paths = GraphSet.paths(start, goal)
>>> len(paths)  # 結果が大規模のときは paths.len() を使う
3266598486981642

とても早かったでしょう? (もし結果が 980466698 なら、あなたのマシンは32ビット環境ではないでしょうか? Graphillion の実行には64ビット環境が必要です) paths オブジェクトにはすべてのパスが含まれているので、それらを一つずつ列挙することができます。

>>> for path in paths:
...     path
... # Ctrl-C で止めないと何年もかかってしまう
>>> tl.draw(paths.choice())  # パスのひとつを表示する

A path from start to goal

次に、Graphillion のフィルタリングおよび検索能力を示すために、条件を与えてパスを選んでみましょう。 図のように、宝箱とその鍵が格子グラフ上にあると仮定しましょう。

Key and treasure box

鍵を取ってから宝箱を通るすべてのパスを考えます。同じ場所を2度通ってはいけません。 まず宝箱を通らずに鍵にたどりつくすべてのパスを検索し、それから宝箱を通ってゴールにたどり着くすべてのパスを検索します。

>>> key = 64
>>> treasure = 18
>>> paths_to_key = GraphSet.paths(start, key).excluding(treasure)  # 宝箱を通らずに鍵にたどり着くパス
>>> treasure_paths = paths.including(paths_to_key).including(treasure)  # 鍵と宝箱を通ってゴールにたどり着くパス
>>> len(treasure_paths)
789438891932744
>>> tl.draw(treasure_paths.choice())  # パスのひとつを表示する

A path on which the box is opened

いま求めた(鍵と宝箱を通ってゴールにたどり着く)パスが、最初に求めた全パスのサブセットになっていることを確認しましょう。

>>> treasure_paths < paths  # Graphillion において "<" は "subset-of" の意味
True

ランダムサンプリングによる統計的処理について紹介しておきましょう。 Graphillion はグラフセットから一様乱数に従うサンプル(グラフ)を選択することができます。 「宝箱パスが何回曲がるか」のヒストグラムを描くと次のようになります。

>>> i = 0
>>> data = []
>>> for path in treasure_paths.rand_iter():
...     data.append(tl.how_many_turns(path))  # パスが曲がる回数を数える
...     if i == 100: break
...     i += 1
...
>>> tl.hist(data)

Histogram of turn counts

1つのパスがおおよそ30〜50回曲がることをヒストグラムは示しています。 Graphillion なしでは、10^14 パスもの大規模な集合からこのような複雑な性質を調査することは容易ではありません。 また、Graphillion が提供する最適化メソッド min_iter() を用いると、その宝物パスが5回曲がっていることもわかります。

>>> for path in treasure_paths.min_iter():
...     print(tl.how_many_turns(path))
...     break  # break しないと、複数のパスが昇順で取り出される
...
5

パス列挙の応用例として、JRの大都市近郊区間におけるすべての経路を列挙する Ekillion もぜひご覧下さい。

配電ネットワークにおける電力フロー

Graphillion は格子グラフ以外のグラフにも使えますし、単純なパス以外の部分グラフも扱うことができます。 次の例として、図のような配電ネットワーク(電力供給網)を考えてみましょう。 このネットワークでは、頂点を家庭、辺をスイッチ付きの電力線だとします。 電力は四隅の発電所から供給されます。

>>> universe = tl.grid(8, 8, 0.37)  # 8x8 の格子からランダムに 37 % の辺が取り除かれる
>>> GraphSet.set_universe(universe)
>>> generators = [1, 9, 73, 81]
>>> tl.draw(universe)

A power distribution network

電力フローは、各電力線のスイッチの設定によって決定されます。 もしスイッチがオンであれば(グラフに辺があれば)、電力が送られます。 スイッチがオフであれば(グラフに辺がなければ)電力は送られません。 電力はすべての家庭に供給されなければなりませんが、ネットワークがショート(短絡)しないよう、ループを作ってはいけません。 つまり電力フローは、発電所を根(起点)とするツリーの集合として森になります。 そして、すべての森を次のように見つけることができます (上述のように,ネットワークは tl.grid() によってランダムに生成されるため,下記の数値 54060425088 は異なるかもしれません)。

>>> forests = GraphSet.forests(roots=generators, is_spanning=True)  # forest にはすべての家庭をカバーし、かつループのない電力フローが格納される
>>> len(forests)
54060425088
>>> tl.draw(forests.choice())

An unsafe power flow

1つの発電所から供給できる電力量には限りがあります。 上の図で示した例にはとても大きなツリーが含まれており、左上の発電所はその供給量を超えてしまうでしょう。 そこで、1つの発電所につき23家庭までにしか電力を供給できないことにしましょう。 それにはまず、供給量を超えた「危険な」ケースを見つけ出し、それを除いた「安全な」ケースを求めることにします。

>>> too_large_trees = GraphSet()  # 空のグラフセット
>>> for substation in generators:
...     too_large_trees |= GraphSet.trees(root=substation).larger(23)  # 危険な電力フロー
...
>>> safe_forests = forests.excluding(too_large_trees)  # 危険なケースを取り除いた、安全な電力フロー
>>> len(safe_forests)
294859080
>>> tl.draw(safe_forests.choice())

A safe power flow

安全なフローを求めることができたので、最適化のテクニックを利用して、スイッチの設定を現在のものから安全なものに切り替えてみましょう。 現在の設定は次のように求めます。

>>> closed_switches = (forests - safe_forests).choice()  # 安全でない電力フローにおけるオン状態(closed)スイッチの集合
>>> tl.draw(closed_switches)

Current unsafe configuration

新しい設定は安全なフローを実現しなければいけませんが、それと同時にスイッチの操作(オンからオフ、またはオフからオン)も少なくしたいものです。 そこで次の表のように、スイッチの現在および新しい状態に応じてスコア(辺の重み)をつけることにします。

現在 \ 新状態 オフ オン
オフ 0 -1
オン 0 1
>>> scores = {}  # 新しい設定におけるオン状態スイッチのスコア(デフォルトは 0)
>>> for switch in universe:
...     # もし現在の状態がオンならスコアは 1、でなければスコアは -1
...     scores[switch] = 1 if switch in closed_switches else -1
...

スコアが最大となる設定(森)を求めてみます。 得られた設定はスコアが最大であり、かつスイッチ操作は最小となります。 さきほど見た現在の設定と比較すると、とても似ているのがわかりますね。 たった8か所のスイッチを変更するだけで、危険な設定から安全な設定になっているのがわかります。

>>> for forest in safe_forests.max_iter(scores):
...     tl.draw(forest)
...     break  # break しないと、複数の設定がスコアの高いものから取り出される
...

Similar but safe configuration

最後に、安全な電力供給を妨げるような深刻な障害について考えておきましょう。 そのためには最小のブロッキングセット、より一般的には極小ヒッティングセットを探す必要があります。 ヒッティングセットとは、与えられたすべての集合が、ヒッティングセット中の少なくとも1つの要素によってヒットされる(集合がその要素を含む)ような状態をいいます。 たとえば {1,2}, {2,3}, {3} の3つの集合が与えられているとき、極小ヒッティングセットは {1,3} および {2,3} となります。 ヒッティングセットは致命的な障害のパターンを示してくれます。 もしヒッティングセットに含まれる電力線に障害が発生した場合、電力フローが構成されなくなってしまいます。

>>> failures = safe_forests.blocking().minimal()  # すべての極小ブロッキングセットからなる集合

理解を助けるために、ネットワークからヒッティングセットに含まれるすべての電力線を取り除いてみましょう。 すると、安全なフローはひとつも残りません。

>>> failure = failures.choice()  # ヒッティングセット(クリティカルな電力線の集合)
>>> for line in failure:
...     safe_forests = safe_forests.excluding(line)  # ヒッティングセットに含まれる電力線を取り除く
...
>>> len(safe_forests)
0

たとえば5本未満の電力線からなる小さなヒッティングセットはネットワークの脆弱性であることが示唆されます。 いま 767 の小さな障害パターンが見つかりました。 これらは念入りに調査する必要があります。

>>> len(failures.smaller(5))
767

実際の電力供給ネットワークはより複雑ですが、私たちはこれらのアイデアを用いて電力供給ネットワークを研究しています。 電力ロスを最小化し、非凸制約のもと非線形目的関数をもちいてネットワークを最適化するツールは DNET でご覧いただけます。

グラフセットの作成

Graphillion では、グラフリスト、辺の制約、パスやツリーのようなグラフタイプ、の3つ方法でグラフセットを生成することができます。

チュートリアル で紹介したように、まずはグラフセットで作業する前にはユニバースを定義するのを忘れないようにして下さい。 このセクションでは、次のユニバースを用います。

>>> from graphillion import GraphSet
>>> universe = [(1, 2), (1, 4), (2, 3), (2, 5), (3, 6), (4, 5), (5, 6)]
>>> GraphSet.set_universe(universe)

グラフリスト

グラフセットを作成するもっとも簡単な方法です。 グラフのリストを指定して、グラフセットを作成します。

次の例では、1本の辺を持つグラフと、2本の辺を持つグラフの2つをまず作成しています。 グラフセットは、この2つのグラフから作成されています。

>>> graph1 = [(1, 4)]
>>> graph2 = [(1, 2), (2, 3)]
>>> gs = GraphSet([graph1, graph2])
>>> gs
GraphSet([[(1, 4)], [(1, 2), (2, 3)]])

もし引数を与えないと空のリスト [] が与えられたことになり、空のグラフセットが作成されます。

>>> gs = GraphSet()
>>> gs
GraphSet([])

辺の制約

辺の制約とは、含まれる辺・含まれない辺を指定するものです。 制約は、含まれる辺および取り除かれる辺のリストからなる辞書型オブジェクト(dict)で表現されます。 辞書に当てはまらない辺については考慮されません。 そのような辺はグラフセットに含まれるかもしれませんし、取り除かれるかもしれません。

次の例は、辺 (1,4) が含まれ、かつ辺 (1,2) および (2,3) が含まれないグラフセットが作成されます。

>>> edges1 = [(1, 4)]
>>> edges2 = [(1, 2), (2, 3)]
>>> GraphSet({'include': edges1, 'exclude': edges2})
GraphSet([[(1, 4)], [(1, 4), (2, 5)], [(1, 4), (3, 6)], ...

空の辞書 {} は何の制約も指定されなかったことになり、その場合ユニバースに含まれるすべてのグラフを含むグラフセットが返されます(N をユニバースの辺の数だとすると、2^N のグラフが得られます)。

>>> gs = GraphSet({})
>>> len(gs)
128  # 2^7

グラフタイプ

パスやツリーといったグラフのタイプを指定して、その条件を満たすすべてのグラフからなるグラフセットを作成することができます。 Graphillion は次のタイプをサポートします。

  • 接続要素
  • クリーク
  • ツリー
  • フォレスト
  • サイクル
  • パス

たとえば paths() メソッドは、2つの頂点間のすべてのパスを探索します。

>>> paths = GraphSet.paths(1, 6)
>>> paths
GraphSet([[(1, 2), (2, 3), (3, 6)], [(1, 2), (2, 5), (5, 6)], [(1, 4), (4, 5 ...

各タイプの引数については、ライブラリ・リファレンスを参照して下さい。

より複雑なグラフタイプを指定するために、Graphillion は低レベルのgraphs() インタフェースを提供しています。 実際には、個々のメソッドはこの低レベルのインタフェースを内部的に呼び出しています。 以下の例は、paths(1,6) と同じことをしています。

>>> start = 1
>>> end = 6
>>> zero_or_two = xrange(0, 3, 2)
>>> degree_constraints = {start: 1, end: 1,
...                       2: zero_or_two, 3: zero_or_two,
...                       4: zero_or_two, 5: zero_or_two}
>>> GraphSet.graphs(vertex_groups=[[start, end]],
...                 degree_constraints=degree_constraints,
...                 no_loop=True)
GraphSet([[(1, 2), (2, 3), (3, 6)], [(1, 2), (2, 5), (5, 6)], [(1, 4), (4, 5 ...

たとえば gs.paths(1, 6) のような、オブジェクト・メソッドと呼ばれるメソッド群において、グラフはグラフセットの中からのみ選ばれます。 詳しくはライブラリ・リファレンスを参照して下さい。 graphs() メソッドの内部 C++ コードは TdZdd としても配布されています。

グラフセットの操作

Graphillion は、グラフセット内のグラフを操作するためのさまざまなメソッドを用意しています。 これらの操作は選択、変更、比較のいずれかに分類されます(ただし、そのうちいくつかは Python の集合演算機能によって実現されています)。 さらに Graphillion は、イテレータやシリアライゼーションの機能も提供します。 これらについて詳しくはライブラリ・リファレンスを参照して下さい。

選択メソッド

グラフセットからグラフを選択するには、以下のメソッドが使えます(2つのグラフセットを対象とするメソッドもあります)。 これらの操作によって、新しいグラフが生成されることはありません。

メソッド 説明
gs.union(other(s)), gs (pipe) other 自身(gs)と他のすべてのグラフからなる、新しいグラフセットを返す
gs.intersection(other(s)), gs & other 自身(gs)と他のすべてとの共通部分となるグラフからなる、新しいグラフセットを返す
gs.difference(other(s)), gs - other 自身(gs)に含まれるグラフのうち、他のグラフに含まれないグラフからなる、新しいグラフセットを返す
gs.symmetric_difference(other(s)), gs ^ other 自身(gs)か、他(other)のどちらか一方に含まれるグラフからなる、新しいグラフセットを返す
gs.quotient(other), gs / other 「商」を表す新しいグラフセットを返す
gs.remainder(other), gs % other 「剰余」を表す新しいグラフセットを返す
gs.update(other(s)) 他のすべてのグラフを追加し、自身(gs)を更新する
gs.join(other) 自身(gs)と他(other)の join を表す新しいグラフセットを返す
gs.meet(other) 自身(gs)と他(other)の meet を表す新しいグラフセットを返す
gs.subgraphs(other) 他(other)グラフの部分グラフからなる新しいグラフセットを返す
gs.supergraphs(other) 他(other)グラフのスーパーグラフからなる新しいグラフセットを返す
gs.non_subgraphs(other) 他(other)グラフの非部分グラフからなる新しいグラフセットを返す
gs.non_supergraphs(other) 他(other)グラフの非スーパーグラフからなる新しいグラフセットを返す
gs.including(obj) obj(グラフセット、グラフ、辺、頂点)のスーパーグラフからなる、新しいグラフセットを返す
gs.excluding(obj) obj(グラフセット、グラフ、辺、頂点)を含まない、新しいグラフセットを返す
gs.included(obj) obj(グラフセット、グラフ、辺、頂点)に含まれるグラフの部分グラフからなる、新しいグラフセットを返す
gs.larger(size) 辺数が size より多いグラフからなる、新しいグラフセットを返す
gs.smaller(size) 辺数が size より少ないグラフからなる、新しいグラフセットを返す
gs.len(size) 辺数が size のグラフからなる、新しいグラフセットを返す
gs.minimal() 極小グラフからなる、新しいグラフセットを返す
gs.maximal() 極大グラフからなる、新しいグラフセットを返す

グラフタイプを指定する作成メソッドは、選択メソッドとしても使用できます。

メソッド 説明
gs.graphs(constraints) 指定された制約を満たすグラフからなる、グラフセットを返す
gs.connected_components(vertices) 接続された要素からなる、グラフセットを返す
gs.cliques(k) 頂点数がk個のクリークからなる、グラフセットを返す
gs.trees(root, is_spanning) ツリーからなるグラフセットを返す
gs.forests(roots, is_spanning) フォレスト(ツリーの集合)からなるグラフセットを返す
gs.cycles(is_hamilton) サイクルからなるグラフセットを返す
gs.paths(terminal1, terminal2, is_hamilton) パスからなるグラフセットを返す

変更または作成メソッド

以下のメソッドは新しいグラフを作成します。 いくつかのメソッドは gs (self) に格納されたグラフを変更しますが、他のメソッドは新たに作成されたグラフからなるグラフセットを返します。

自身のグラフを変更する

メソッド 説明
gs.add(graph_or_edge) 与えられたグラフを gs に加える、もしくは gs 内のグラフに辺を接合する
gs.remove(obj), gs.discard(obj) gs からグラフ、辺、頂点を削除する
gs.flip(edge) gs 内のすべてのグラフの辺について、状態を反転する
gs.clear() gs からすべてのグラフを削除する

新しいグラフを作成する

メソッド 説明
~gs gs に格納されていないグラフからなる、新しいグラフセットを返す
gs.complement() gs に格納されているグラフの補グラフからなる、新しいグラフセットを返す
gs.blocking() すべてのヒッティングセットからなる、新しいグラフセットを返す
gs.hitting() gs.blocking() と同じ

比較および評価のメソッド

以下のメソッドは、グラフセットの比較および評価を行います。

メソッド 説明
gs.isdisjoint(other) gsother とのあいだに共通のグラフがなければ True を返す
gs.issubset(other) gs のすべてのグラフが other に含まれているかどうかを評価する
gs.issuperset(other) other のすべてのグラフが gs に含まれているかどうかを評価する
obj in gs obj(グラフ、辺、頂点)が gs に含まれていれば True、でなければ False を返す
len(gs), gs.len() gs に含まれるグラフの数を返す
gs.probability(probabilities) 与えられた probabilities に対し,gs の実現確率を返す

イテレータ

Graphillion はさまざまなイテレータを提供します。 rand_iter() は統計的な分析におけるランダムサンプリングに使えます。 min_iter()max_iter() は最適化に使え、1個だけでなく上位k個のグラフを得ることができます。 pop()choice() はイテレータではありませんが、グラフセットを返します。

メソッド 説明
iter(gs) グラフに対してイテレーション(反復処理)を行う
gs.rand_iter() 一様乱数に従いグラフのイテレーションを行う
gs.min_iter() 重みの昇順でグラフのイテレーションを行う
gs.max_iter() 重みの降順でグラフのイテレーションを行う
gs.pop() gs から任意のグラフを取り除き、残りを返す
gs.choice() gs から任意のグラフを返す

ダンプおよびロードのメソッド

Graphillion はグラフセットをファイルに書き出したり、ファイルから読み込んだりすることができます。 これらのメソッドは、ユニバースの pickle 化(データ構造の保存・復元)と同時に行われます。 詳しくはライブラリ・リファレンスを参照して下さい。

メソッド 説明
gs.dump(fp) gs をシリアライズしファイル fp に書き出す
GraphSet.load(fp) シリアライズされたファイル fp を読み込み、新しいグラフセットを返す

Python の集合演算メソッド

Graphillion は Python の持つ集合演算メソッドをサポートします。 これらのメソッドはグラフを集合の要素として扱い、グラフの構造については意識しません。

  • gs.union(other), gs | other,
  • gs.intersection(other), gs & other,
  • gs.difference(other), gs - other,
  • gs.symmetric_difference(other), gs ^ other,
  • gs.update(other), gs |= other,
  • gs.add(graph),
  • gs.remove(graph), gs.discard(graph),
  • gs.clear(),
  • gs.isdisjoint(gs),
  • gs.issubset(gs),
  • gs.issuperset(gs),
  • graph in gs,
  • len(gs),
  • gs.pop(), and
  • gs.copy().

並列計算

Graphillion は,OpenMP (多くの環境で共有メモリ型並列計算を行うためのAPI) を用いて並列計算を実行します.利用したい CPU コア数を,環境変数 OMP_NUM_THREADS として設定してください.たとえば,4コアを利用するのであれば下記のようになります.

$ OMP_NUM_THREADS=4 python your_graphillion_script.py

現在 (v1.0) 並列化の対象となっているメソッドは下記になります.

  • GraphSet.graphs(constraints)
  • GraphSet.connected_components(vertices)
  • GraphSet.cliques(k)
  • GraphSet.trees(root, is_spanning)
  • GraphSet.forests(roots, is_spanning)
  • GraphSet.cycles(is_hamilton)
  • GraphSet.paths(terminal1, terminal2, is_hamilton)

NetworkXとの連携

Graphillion は、NetworkX のような既存のグラフツールと透過的に併用することができます。 Graphillion では、networkx.Graph など任意のオブジェクトはグラフとして認識されます。

エッジリストから新しいグラフオブジェクトを生成するには2つのメソッドを定義します。 1つはエッジリストをグラフに変換するために使われ、もう1つはその逆です。 NetworkX の例を見てみましょう。

>>> import networkx as nx
>>> GraphSet.converters['to_graph'] = nx.Graph
>>> GraphSet.converters['to_edges'] = nx.Graph.edges

NetworkX のグラフオブジェクトは、このように Graphillion に引き渡すことができます。

>>> g = nx.Graph(...)  # NetworkX のグラフを生成する
>>> GraphSet.set_universe(g)

同様に、Graphillion は NetworkX のグラフオブジェクトを受け取ることもできます。

>>> gs.choice()  # NeworkX のグラフオブジェクトを返す
<networkx.classes.graph.Graph object at 0x100456d10>

グラフ可視化のために、NetworkX はいくつかのノード配置アルゴリズムと共に Matplotlib プロットパッケージへのインタフェースを提供しています。

>>> nx.draw(gs.choice())
>>> import matplotlib.pyplot as plt
>>> plt.show()  # ポップアップウィンドウを表示する

ライブラリ・リファレンス

pydoc を使えば、ターミナルウィンドウでもライブラリ・リファレンスが参照できます。

$ pydoc graphillion.GraphSet

HTML で参照するには次のようにします。

$ pydoc -w graphillion.GraphSet

サンプルコード

こちらを参照ください。

今後の予定

  • より効率的な内部データ変換
  • 最適化のためのより効率的な検索アルゴリズム
  • 最適化における非線形目的関数
  • ヒッティングセットやクリークのためのよい効率的なアルゴリズム
  • Python の乱数と内部乱数シードとの同期
  • パフォーマンスに関するドキュメント化
  • マルチスレッディング
  • ガベージコレクション
  • メーリングリスト
  • コーディングルール
  • 開発者向けドキュメント

参考資料

Graphillion (内部アルゴリズムを含む)

  • Takeru Inoue, Hiroaki Iwashita, Jun Kawahara, and Shin-ichi Minato: "Graphillion: Software Library Designed for Very Large Sets of Labeled Graphs," International Journal on Software Tools for Technology Transfer, Springer, vol.18, issue 1, pp.57-66, February 2016. (pdf)

  • Yuma Inoue and Shin-ichi Minato, "Acceleration of ZDD Construction for Subgraph Enumeration via Path-width Optimization," TCS Technical Reports, TCS-TR-A-16-80, October 2016. (pdf)

  • Hana Ito, Yuma Inoue, and Shin-ichi Minato, "Experiments and Considerations on Variable Ordering in Top-Down Construction of ZDDs," Forum on Information Technology, vol.14, no.1, pp.115-116, September 2015. (pdf, in Japanese)

  • ERATO湊離散構造処理系プロジェクト 著, 湊真一 編, “超高速グラフ列挙アルゴリズム –<フカシギの数え方>が拓く,組合せ問題の新アプローチ−,” 森北出版, 2015年4月. (amazon)

  • 川原純, "graphillion – 莫大な数の部分グラフを扱う Python ライブラリ," Pythonセミナー, 2014年12月. (pdf, in Japanese)

  • Jun Kawahara, Takeru Inoue, Hiroaki Iwashita and Shin-ichi Minato, "Frontier-based Search for Enumerating All Constrained Subgraphs with Compressed Representation," Hokkaido University, Division of Computer Science, TCS Technical Reports, TCS-TR-A-14-76, September 2014. (pdf)

  • 井上武, "Graphillion updates," ERATO 湊離散構造処理プロジェクト セミナー, 2014年2月. (pdf)

  • Hiroaki Iwashita and Shin-ichi Minato, "Efficient Top-Down ZDD Construction Techniques Using Recursive Specifications," TCS Technical Reports, TCS-TR-A-13-69, December 2013. (pdf)

  • Takeru Inoue, Hiroaki Iwashita, Jun Kawahara, and Shin-ichi Minato, "Graphillion: ZDD-based Software Library for Very Large Sets of Graphs," Proc. of the 18th Workshop on Synthesis And System Integration of Mixed Information Technologies (SASIMI), Poster Presentation, October 2013. (html)

  • Takeru Inoue, "Graphillion: Python module for very large sets of graphs," PyCon APAC, September 2013.

  • 井上武, "[招待講演] Graphillion:巨大なグラフ集合を扱うソフトウェアライブラリ," 信学技報, vol. 113, no. 140, IN2013-43, pp. 43-47, 2013年7月. (pdf)

  • Takahisa Toda, "Hypergraph Transversal Computation with Binary Decision Diagrams," Proc. of 12th International Symposium on Experimental Algorithms (SEA), pp.91-102, June 2013. (doi)

  • Graphillion for Windows

  • TdZdd

  • GGCount

Graphillion や関連アルゴリズムの応用

  • DNET - 給電ネットワーク評価ツール

  • Ekillion - JR路線全経路の列挙

  • Daisuke Yoshino and Eiji Hato, "Fast Enumeration Method of Travel Route of DRT Using Zero-suppressed Binary Decision Diagram," Journal of Japan Society of Civil Engineers, vol.72, no.5, pp.1229-1239, December 2016. (doi)

  • Jun Kawahara, Toshiki Saitoh, Hirofumi Suzuki, and Ryo Yoshinaka, "Solving the Longest Oneway-Ticket Problem and Enumerating Letter Graphs by Augmenting the Two Representative Approaches with ZDDs," Proc. of the Computational Intelligence in Information Systems Conference (CIIS), pp.294-305, November 2016. (doi)

  • Yuji Takenobu, Norihito Yasuda, Shunsuke Kawano, Yasuhiro Hayashi, and Shin-ichi Minato, "Evaluation of Annual Energy Loss Reduction Based on Reconfiguration Scheduling," IEEE Transactions on Smart Grid, September 2016. (doi)

  • Masashi Hashimoto, Tomihiro Utsumi, and Takeru Inoue, "[Invited Talk] Availability Analyses for Photonic Network by Minimal Blocking Set using ZDD based Graphillion," Technical Report of IEICE, vol.116, no.205, PN2016-22, pp.45-51, September 2016. (html, in Japanese)

  • Y. Takenobu, S. Kawano, Y. Hayashi, N. Yasuda and S. Minato, "Maximizing hosting capacity of distributed generation by network reconfiguration in distribution system," Proc. of Power Systems Computation Conference (PSCC), pp.1-7, June 2016. (doi)

  • Subaru Fukuda and Naoshi Sakamoto, "A Failure Estimation System for Networks Applying Graphillion," Technical Report of IEICE, vol.115, no.483, NS2015-212, pp.255-260, March 2016. (html, in Japanese)

  • Arthur Choi, Nazgol Tavabi, and Adnan Darwiche, "Structured Features in Naive Bayes Classification," AAAI, pp.3233-3240, February 2016. (pdf)

  • Ikki Fujiwara, Satoshi Fujita, Koji Nakano, Takeru Inoue, and Michihiro Koibuchi, "Let's Solve the Order/Degree Problem to Make the Lowest-latency Interconnections," Technical Report of IEICE, vol.115, no.174, CPSY2015-38, pp.223-228, August 2015. (html, in Japanese)

  • Atsushi Takizawa, Yushi Miyata, and Naoki Katoh, "Enumeration of Floor Plans Based on a Zero-Suppressed Binary Decision Diagram," International Journal of Architectural Computing, vol.13, no.1, pp.25-44, March 2015. (pdf)

  • Hiroyuki Hanada, Shuhei Denzumi, Yuma Inoue, Hiroshi Aoki, Norihito Yasuda, Shogo Takeuchi, and Shin-ichi Minato, "Enumerating Eulerian Trails via Hamiltonian Path Enumeration," International Workshop on Algorithms and Computation (WALCOM), pp.161-174, February 2015. (doi)

  • Takeru Inoue, Norihito Yasuda, Shunsuke Kawano, Yuji Takenobu, Shin-ichi Minato, and Yasuhiro Hayashi, "Distribution Network Verification for Secure Restoration by Enumerating All Critical Failures," IEEE Transactions on Smart Grid, October 2014. (doi)

  • Takeru Inoue, Keiji Takano, Takayuki Watanabe, Jun Kawahara, Ryo Yoshinaka, Akihiro Kishimoto, Koji Tsuda, Shin-ichi Minato, and Yasuhiro Hayashi, "Distribution Loss Minimization with Guaranteed Error Bound," IEEE Transactions on Smart Grid, vol.5, issue.1, pp.102-111, January 2014. (doi)

  • Ryo Yoshinaka, Toshiki Saitoh, Jun Kawahara, Koji Tsuruma, Hiroaki Iwashita, and Shin-ichi Minato, "Finding All Solutions and Instances of Numberlink and Slitherlink by ZDDs," Algorithms 2012, 5(2), pp.176-213, 2012. (doi)

Graphillion を引用している文献

  • Michihiro Koibuchi, Ikki Fujiwara, Fabien Chaix, and Henri Casanova, "Towards Ideal Hop Counts in Interconnection Networks with Arbitrary Size," Proc. of 4th International Symposium on Computing and Networking (CANDAR), pp.188-194, November 2016. (doi)
  • Shin-ichi Minato, "Power of Enumeration -- BDD/ZDD-Based Techniques for Discrete Structure Manipulation," IEEE 46th International Symposium on Multiple-Valued Logic (ISMVL), pp.143-143, July 2016. (doi)
  • Masahiro Kawahara, Takehide Soh, Mutsunori Banbara, and Naoyuki Tamura, "Constraint Models for SAT-based Subgraph Search," Proc. of the 30th Annual Conference of the Japanese Society for Artificial Intelligence, 1D4-OS-02a-4, June 2016. (pdf, in Japanese)
  • Shin-ichi Minato, "Counting by ZDD," Encyclopedia of Algorithms, Springer, pp.454-458, April 2016. (doi)
  • Yasuhiro Takei, Masanori Hariyama, and Michitaka Kameyama, "Evaluation of an FPGA-Based Shortest-Path-Search Accelerator," Proc. of the International Conference on Parallel and Distributed Processing Techniques and Applications (PDPTA), pp.613-617, 2015. (pdf)
  • Tsutomu Sasao and Jon T. Butler, "Applications of Zero-Suppressed Decision Diagrams," Morgan & Claypool Publishers, November 2014. (doi)