変数の定義と式の作成
PyQBPPのインストール
PyQBPPを使用するには、pipでインストールしてください:
pip install pyqbpp
ライブラリのインポート
PyQBPPを使用するには、pyqbppモジュールをインポートします:
import pyqbpp as qbpp
変数と式の定義
変数はqbpp.var("name")を使って定義できます。 指定したnameは変数を表示する際に使用されます。
式は+、-、*などの標準的な算術演算子を使って構築します。
以下のプログラムは、3つの変数a、b、cと式fを定義し、表示します:
import pyqbpp as qbpp
a = qbpp.var("a")
b = qbpp.var("b")
c = qbpp.var("c")
f = (a + b - 1) * (b + c - 1)
print("f =", f)
式(a + b - 1) * (b + c - 1)は自動的に展開され、fに格納されます。
このプログラムでは、a、b、cは変数であり、fは式です。
プログラムを実行すると、展開された式が表示されます:
f = 1 +a*b +b*b +a*c +b*c -a -b -b -c
注釈
qbpp.var()の変数名は省略可能です。 省略した場合、{0}、{1}、…のようなデフォルト名が自動的に割り当てられます。
注意 式をはじめとするPyQBPPのほとんどのオブジェクトは、
print()を使ってテキストとして出力できます。 ただし、このテキスト出力は安定性が保証されておらず、フォーマットが将来のリリースで変更される可能性があるため、後続の計算の入力として使用すべきではありません。 また、PyQBPPドキュメントに示されている出力は古いバージョンで生成されたものである可能性があり、最新バージョンの出力とは異なる場合があります。
バイナリ変数の配列については 配列、多次元配列については 多次元変数、整数変数については 整数変数 を参照してください。
式の簡約化
fに格納された式は、simplify()メンバ関数を呼び出すことで簡約化できます:
print("f =", f.simplify())
この変更により、プログラムの出力は以下のようになります:
f = 1 -a -2*b -c +a*b +a*c +b*b +b*c
メンバ関数呼び出しf.simplify()は式fをその場で簡約化し、自身を返します。その値が出力されます。
バイナリ変数による式の簡約化
すべての変数がバイナリ値(0または1)を取ると仮定すると、恒等式$b^2=b$を使って式をさらに簡約化できます。 この目的には、代わりにsimplify_as_binary()を使用します:
print("f =", f.simplify_as_binary())
すると出力は以下のようになります:
f = 1 -a -b -c +a*b +a*c +b*c
簡約化関数は、各項内の変数と式内の項を並べ替え、低次の項が先に表示されるようにし、同じ次数の項は変数の辞書順でソートします。 変数自体は定義された順序で並べられます。
スピン変数による式の簡約化
変数がスピン値 $-1$/$+1$を取ると仮定する場合、恒等式$b^2 = 1$を使って式をさらに簡約化できます。 この場合、simplify_as_spin()メンバ関数を使って式を簡約化できます:
print("f =", f.simplify_as_spin())
すると出力は以下のようになります:
f = 2 -a -2*b -c +a*b +a*c +b*c
簡約化のためのグローバル関数
メンバ関数はfに格納された式を更新します。 fを変更したくない場合は、代わりにグローバル関数qbpp.simplify(f)、qbpp.simplify_as_binary(f)、qbpp.simplify_as_spin(f)を使用できます。これらはfを変更せずに簡約化された式を返します。
import pyqbpp as qbpp
g = qbpp.simplify_as_binary(f) # fは変更されず、gは新しい簡約化された式
注釈 PyQBPPでは、ほとんどの式オブジェクトに関するメンバ関数は処理結果でオブジェクト更新しますが、グローバル関数は元のオブジェクトを変更せずに新しい値を返します。
否定リテラル
PyQBPPは~演算子を使った否定リテラルをネイティブにサポートしています。 バイナリ変数xに対して、式~xは$1 - x$を表します。
import pyqbpp as qbpp
a = qbpp.var("a")
b = qbpp.var("b")
c = qbpp.var("c")
d = qbpp.var("d")
f = ~a * ~b * ~c * ~d + a * b
print("f =", f)
print("f =", qbpp.simplify_as_binary(f))
出力:
f = ~a*~b*~c*~d +a*b
f = a*b +~a*~b*~c*~d
否定リテラル~xは、1 - xとして展開されるのではなく、否定フラグを持つ単一の変数として内部的に格納されます。 これはパフォーマンスにとって重要です。~xを単純に展開すると、~x1 * ~x2 * ... * ~xkのような$k$個の否定リテラルの積は、$(1-x_1)(1-x_2)\cdots(1-x_k)$を展開した後に最大$2^k$個の項を生成します。 例えば、上記の項~a*~b*~c*~dは単一の4次項として格納されますが、展開形$(1-a)(1-b)(1-c)(1-d)$は16個の項を生成します:
1 -a -b -c -d +a*b +a*c +a*d +b*c +b*d +c*d -a*b*c -a*b*d -a*c*d -b*c*d +a*b*c*d
PyQBPPに同梱されているすべてのソルバー(EasySolver、ExhaustiveSolver、ABS3 GPU Solver)は否定リテラルをネイティブに処理するため、求解前に展開する必要はありません。