変数の置き換え

QUBO++ には、式内の変数の値を固定するために使用できる次の replace 関数が用意されています。

qbpp::replace(const qbpp::Expr& f, const qbpp::MapList& ml)

この関数は、ml に指定されたマッピングに従って、式 f 内の変数の値を**置き換え(固定)**します。


replace 関数を用いた変数値の固定

ここでは、分割問題(partitioning problem) の QUBO++ プログラムを使って qbpp::replace() 関数を説明します。

このプログラムは、次のベクトル w に含まれる数値を 2 つの部分集合 \(P\)\(Q\)\(P \cap Q = \varnothing\))に分割し、それぞれの合計値の差が最小になるような分割を求めます:

  std::vector<uint32_t> w = {64, 27, 47, 74, 12, 83, 63, 40};

ここでは、この分割問題を次のように変更します。

  • 64 は必ず \(P\) に属する

  • 27 は必ず \(Q\) に属する

つまり、この 2 つの値が異なる部分集合に入ることを保証します。この制約を実現するために、qbpp::replace() 関数を使って x[0]x[1] の値をそれぞれ 1 と 0 に固定します。

完全な Hi-QUBO プログラムを以下に示します。

replace-functions-program1.cpp
#include <qbpp/qbpp.hpp>
#include <qbpp/exhaustive_solver.hpp>

int main() {
  auto w = qbpp::array({64, 27, 47, 74, 12, 83, 63, 40});
  auto x = qbpp::var("x", w.size());
  auto p = qbpp::sum(w * x);
  auto q = qbpp::sum(w * ~x);
  auto f = qbpp::sqr(p - q);
  f.simplify_as_binary();

  qbpp::MapList ml({{x[0], 1}, {x[1], 0}});
  auto g = qbpp::replace(f, ml);
  g.simplify_as_binary();
  auto solver = qbpp::ExhaustiveSolver(g);
  auto sol = solver.search();

  auto full_sol = qbpp::Sol(f).set(sol).set(ml);

  std::cout << "sol = " << sol << std::endl;
  std::cout << "ml = " << ml << std::endl;
  std::cout << "full_sol = " << full_sol << std::endl;
  std::cout << "f(full_sol) = " << f(full_sol) << std::endl;
  std::cout << "p(full_sol) = " << p(full_sol) << std::endl;
  std::cout << "q(full_sol) = " << q(full_sol) << std::endl;
  std::cout << "P :";
  for (size_t i = 0; i < w.size(); ++i) {
    if (x[i](full_sol) == 1) {
      std::cout << " " << w[i];
    }
  }
  std::cout << std::endl;
  std::cout << "Q :";
  for (size_t i = 0; i < w.size(); ++i) {
    if (x[i](full_sol) == 0) {
      std::cout << " " << w[i];
    }
  }
  std::cout << std::endl;
}

まず、変数 x[0]x[1] の固定値を指定する qbpp::MapList オブジェクト ml を定義します。分割問題の元の式 fqbpp::MapList オブジェクト ml が与えられると、qbpp::replace() 関数を使用して、f 内の x[0]x[1] をそれぞれ定数 1 と 0 に置き換えます。その結果得られる式は g に格納されます。

次に、Exhaustive Solverg に適用して最適解を求め、その結果を sol に格納します。ここで、式 g にはもはや x[0]x[1] が含まれていないため、sol にもこれらの変数に対する割り当ては含まれていない点に注意してください。

すべての変数を含む完全な解を構築するために、まず f に対して 0 で初期化された qbpp::Sol オブジェクトを作成します。その後、set() を使用して solml を用いて二値の値を設定します。

以下の出力から、64 が \(P\) に配置され、27 が \(Q\) に配置されていることが、意図どおりであると確認できます。

sol = 4:{{x[2],1},{x[3],0},{x[4],1},{x[5],1},{x[6],0},{x[7],0}}
ml = {{x[0],1},{x[1],0}}
full_sol = 4:{{x[0],1},{x[1],0},{x[2],1},{x[3],0},{x[4],1},{x[5],1},{x[6],0},{x[7],0}}
f(full_sol) = 4
p(full_sol) = 206
q(full_sol) = 204
P : 64 47 12 83
Q : 27 74 63 40

変数を式で置き換えるための replace 関数の使用

replace() 関数は、定数値だけでなく、変数を式で置き換えることもできます。

ここでは、先に紹介した分割問題において、64 と 27 が必ず異なる部分集合に配置されるようにする、より洗練された方法を示します。
重要なアイデアは、式 f に含まれる変数 x[0] を、式 ~x[1] に置き換えることです。これにより、x[0]x[1] が常に反対の値を取るという制約が課され、対応する要素(64 と 27)が必ず異なる部分集合に属することが保証されます。

次の Hi-QUBO プログラムは、このアイデアを実装したものです:

  qbpp::MapList ml({{x[0], 1 - x[1]}});
  auto g = qbpp::replace(f, ml);
  g.simplify_as_binary();
  auto solver = qbpp::exhaustive_solver::ExhaustiveSolver(g);
  auto sol = solver.search();

  auto full_sol = qbpp::Sol(f).set(sol, ml);

このプログラムでは、変数 x[0] を式 ~x[1] に置き換えるように、qbpp::MapList オブジェクト ml が定義されています。

qbpp::replace() 関数は、この置換を元の式 f に適用し、その結果の式は g に格納されます。その結果、g にはもはや変数 x[0] は含まれず、x[0] のすべての出現箇所が式 ~x[1] に置き換えられます。

その後、Exhaustive Solver を使用して g の最適解を求め、その結果は sol に格納されます。g には x[0] が現れないため、解 sol にも x[0] に対する割り当ては含まれません。

元の式 f に含まれる変数に対する完全な解を構成するために、まず 0 で初期化された qbpp::Sol(f) を作成し、その後 set(sol, ml) を呼び出して値を設定します。ここで set() には solml を同時に渡す必要がある点に注意してください。なぜなら、ml に含まれるマッピング(例:x[0] = ~x[1])が、sol に含まれる変数の値に依存する可能性があるためです。

このプログラムは、次の出力を生成します:

sol = 4:{{x[1],0},{x[2],1},{x[3],0},{x[4],1},{x[5],1},{x[6],0},{x[7],0}}
ml = {{x[0],1 -x[1]}}
full_sol = 4:{{x[0],1},{x[1],0},{x[2],1},{x[3],0},{x[4],1},{x[5],1},{x[6],0},{x[7],0}}
f(full_sol) = 4
p(full_sol) = 206
q(full_sol) = 204
P : 64 47 12 83
Q : 27 74 63 40

次のことを確認できます:

  • sol には x[0] が含まれていないこと

  • x[0]x[1] が反対の値を取っていること

  • 64 と 27 が意図どおり異なる部分集合に配置されていること

整数変数に対する replace 関数

整数変数は、replace() 関数を使用して固定の整数値に置き換えることができます。

ここでは、この機能を簡単な乗算式を用いて説明します。
\(p,q,r\) を整数変数とし、次の制約を考えます:

\[ p \times q-r=0 \]

この式は、いくつかの異なる解釈が可能であり、それぞれ異なる種類の問題になります:

  • 乗算 (Multiplication):
    \(p\)\(q\) の値が固定されているとき、この式を満たす \(r\) を求める。

  • 因数分解 (Factorization):
    \(r\) の値が固定されているとき、この式を満たす \(p\)\(q\) を求める。

  • 除算 (Division):
    \(p\)\(r\) の値が固定されているとき、この式を満たす \(q\) を求める。

qbpp::replace() 関数を使用すると、整数変数を定数値に固定することができます。
ここでは、qbpp::replace() を用いてこれらの問題を解く QUBO++ プログラムを示します。

乗算

次のプログラムでは、\(p=5\)\(q=7\) を固定し、その積 \(r=35\) を求めます:

replace-functions-program2.cpp
#include <qbpp/qbpp.hpp>
#include <qbpp/easy_solver.hpp>

int main() {
  auto p = 2 <= qbpp::var_int("p") <= 8;
  auto q = 2 <= qbpp::var_int("q") <= 8;
  auto r = 2 <= qbpp::var_int("r") <= 40;
  auto f = p * q - r == 0;
  f.simplify_as_binary();

  qbpp::MapList ml({{p, 5}, {q, 7}});
  auto g = qbpp::replace(f, ml);
  g.simplify_as_binary();
  std::cout << "g = " << g << std::endl;

  auto solver = qbpp::EasySolver(g);
  auto sol = solver.search({{"target_energy", 0}});

  auto full_sol = qbpp::Sol(f).set(sol).set(ml);
  std::cout << "p= " << full_sol(p) << ", q= " << full_sol(q)
            << ", r= " << full_sol(r) << std::endl;
}

このプログラムでは、元の式 f に含まれる整数変数 pq の値を固定するために、qbpp::MapList オブジェクト ml が使用されています。qbpp::replace(f, ml) を適用することで、f に含まれる変数 pq は、それぞれ定数 57 に置き換えられます。その結果の式は g に格納され、この式には変数 r のみが含まれるようになります。

その後、Easy Solver が g に適用され、その結果の解は sol に格納されます。すべての変数を含む完全な解を構築するために、まず f に対して 0 で初期化された qbpp::Sol オブジェクトを作成し、その後 solml を用いて set() を呼び出し、バイナリ値を設定します。最後に、pqr の値が出力されます。

このプログラムは次の出力を生成し、乗算の結果が正しく得られていることを確認できます:

g = 1089 -65*r[0] -128*r[1] -248*r[2] -464*r[3] -800*r[4] -413*r[5] +4*r[0]*r[1] +8*r[0]*r[2] +16*r[0]*r[3] +32*r[0]*r[4] +14*r[0]*r[5] +16*r[1]*r[2] +32*r[1]*r[3] +64*r[1]*r[4] +28*r[1]*r[5] +64*r[2]*r[3] +128*r[2]*r[4] +56*r[2]*r[5] +256*r[3]*r[4] +112*r[3]*r[5] +224*r[4]*r[5]
p= 5, q= 7, r= 35

因数分解

\(r=35\) の因数分解を行うために、QUBO++ プログラム内の qbpp::MapList オブジェクト ml は次のように変更されます。

qbpp::MapList ml({{r, 35}});

\(r\) の値を固定することで、ソルバーは次の制約を満たす整数値 \(p\)\(q\) を探索します。

\[ p \times q = 35 \]

このプログラムは次の出力を生成します。

g = 961 -120*p[0] -232*p[1] -336*p[2] -120*q[0] -232*q[1] -336*q[2] +16*p[0]*p[1] +24*p[0]*p[2] -45*p[0]*q[0] -80*p[0]*q[1] -105*p[0]*q[2] +48*p[1]*p[2] -80*p[1]*q[0] -136*p[1]*q[1] -168*p[1]*q[2] -105*p[2]*q[0] -168*p[2]*q[1] -189*p[2]*q[2] +16*q[0]*q[1] +24*q[0]*q[2] +48*q[1]*q[2] +20*p[0]*p[1]*q[0] +48*p[0]*p[1]*q[1] +84*p[0]*p[1]*q[2] +30*p[0]*p[2]*q[0] +72*p[0]*p[2]*q[1] +126*p[0]*p[2]*q[2] +20*p[0]*q[0]*q[1] +30*p[0]*q[0]*q[2] +60*p[0]*q[1]*q[2] +60*p[1]*p[2]*q[0] +144*p[1]*p[2]*q[1] +252*p[1]*p[2]*q[2] +48*p[1]*q[0]*q[1] +72*p[1]*q[0]*q[2] +144*p[1]*q[1]*q[2] +84*p[2]*q[0]*q[1] +126*p[2]*q[0]*q[2] +252*p[2]*q[1]*q[2] +16*p[0]*p[1]*q[0]*q[1] +24*p[0]*p[1]*q[0]*q[2] +48*p[0]*p[1]*q[1]*q[2] +24*p[0]*p[2]*q[0]*q[1] +36*p[0]*p[2]*q[0]*q[2] +72*p[0]*p[2]*q[1]*q[2] +48*p[1]*p[2]*q[0]*q[1] +72*p[1]*p[2]*q[0]*q[2] +144*p[1]*p[2]*q[1]*q[2]
p= 5, q= 7, r= 35

除算

\(r=35\)\(p=5\) を使った除算 \(r/p\) を計算するために、Hi-QUBO プログラム内の qbpp::MapList オブジェクト ml を次のように変更します。

qbpp::MapList ml({{p, 5}, {r, 35}});

このプログラムは次の出力を生成します。

g = 625 -225*q[0] -400*q[1] -525*q[2] +100*q[0]*q[1] +150*q[0]*q[2] +300*q[1]*q[2]
p= 5, q= 7, r= 35

これにより、除算の結果 \(q=r/p=7\) が正しく得られることが確認できます。

補足: Hi-QUBO は、式に対するメンバ関数版の replace() も提供しています。つまり:

  • f.replace(ml) は、ml に指定された置換を適用して、式 f をその場で更新します。

  • qbpp::replace(f, ml) は、元の式 f を変更せずに、置換を適用した新しい式を返します。既存の式を永久的に変更したい場合は f.replace(ml) を使用し、元の式を保持したい場合は qbpp::replace(f, ml) を使用します。

注意: Term に対する replace() フリー関数 qbpp::replace()Term 引数も受け付けます。 Term は暗黙的に Expr に変換され、置換が適用されます:

auto t = ~a * b * ~c * ~d;  // Term
auto e = qbpp::replace(t, {{~a, 1 - a}, {~c, 1 - c}, {d, 1 - d}});  // Exprを返す

なお、Term にはメンバ関数版の replace() はありません(ヘッダ内で TermExpr より先に定義されるため)。

注意: 否定リテラルと replace() 関数は x~x を独立したキーとして扱います。 MapList{x, 0} を指定しても、~x が自動的に 1 に置換されるわけではありません。 式に ~x のような否定リテラルが含まれている場合、両方のマッピングを明示的に指定してください:

qbpp::MapList ml({{x, 0}, {~x, 1}});

PyQBPP には、式内の変数の値を固定するために使用できる次の replace 関数が用意されています。

  • qbpp.replace(f, ml): ml で指定されたマッピングに従って f 中の変数を置換した新しい式を返します。

  • f.replace(ml): 式 f の変数をその場で置換します( f を変更します)。

ここで ml は Python の辞書 {変数: 値, ...} もしくは (変数, 値) のタプルのリストです。値は整数、VarTermExpr のいずれも指定できます(例: {x: 0, y: ~z}[(x, 0), (y, ~z)])。


replace 関数を用いた変数値の固定

ここでは、分割問題(partitioning problem) の PyQBPP プログラムを使って replace() 関数を説明します。

このプログラムは、次のベクトル w に含まれる数値を 2 つの部分集合 \(P\)\(Q\)\(P \cap Q = \varnothing\))に分割し、それぞれの合計値の差が最小になるような分割を求めます:

ここでは、この分割問題を次のように変更します。

  • 64 は必ず \(P\) に属する

  • 27 は必ず \(Q\) に属する

つまり、この 2 つの値が異なる部分集合に入ることを保証します。 この制約を実現するために、replace() 関数を使って x[0]x[1] の値をそれぞれ 1 と 0 に固定します。

完全な PyQBPP プログラムを以下に示します。

replace-functions-program1.py
import pyqbpp as qbpp

w = qbpp.array([64, 27, 47, 74, 12, 83, 63, 40])
x = qbpp.var("x", shape=len(w))
p = qbpp.sum(w * x)
q = qbpp.sum(w * ~x)
f = qbpp.sqr(p - q)
f.simplify_as_binary()

ml = {x[0]: 1, x[1]: 0}
g = qbpp.replace(f, ml)
g.simplify_as_binary()
solver = qbpp.ExhaustiveSolver(g)
sol = solver.search()

full_sol = qbpp.Sol(f).set(sol, ml)

print("sol =", sol)
print("ml =", ml)
print("full_sol =", full_sol)
print("f(full_sol) =", f(full_sol))
print("p(full_sol) =", p(full_sol))
print("q(full_sol) =", q(full_sol))
P = [w[i] for i in range(len(w)) if x[i](full_sol) == 1]
Q = [w[i] for i in range(len(w)) if x[i](full_sol) == 0]
print("P:", P)
print("Q:", Q)

まず、変数 x[0]x[1] の固定値を指定するリスト ml を定義します。
replace() 関数を使用して、f 内の x[0]x[1] をそれぞれ定数 1 と 0 に置き換えます。
その結果得られる式は g に格納されます。

次に、Exhaustive Solverg に適用して最適解を求め、その結果を sol に格納します。
ここで、式 g にはもはや x[0]x[1] が含まれていないため、sol にもこれらの変数に対する割り当ては含まれていない点に注意してください。

すべての変数を含む完全な解を構築するために、まず f に対して 0 で初期化された Sol オブジェクトを作成します。
その後、set() を使用して solml を用いて二値の値を設定します。

以下の出力から、64 が \(P\) に配置され、27 が \(Q\) に配置されていることが、意図どおりであると確認できます。

sol = 4:{{x[2],1},{x[3],0},{x[4],1},{x[5],1},{x[6],0},{x[7],0}}
ml = {{x[0],1},{x[1],0}}
full_sol = 4:{{x[0],1},{x[1],0},{x[2],1},{x[3],0},{x[4],1},{x[5],1},{x[6],0},{x[7],0}}
f(full_sol) = 4
p(full_sol) = 206
q(full_sol) = 204
P : 64 47 12 83
Q : 27 74 63 40

変数を式で置き換えるための replace 関数の使用

replace() 関数は、定数値だけでなく、変数を式で置き換えることもできます。

ここでは、先に紹介した分割問題において、64 と 27 が必ず異なる部分集合に配置されるようにする、より洗練された方法を示します。
重要なアイデアは、式 f に含まれる変数 x[0] を、式 ~x[1] に置き換えることです。これにより、x[0]x[1] が常に反対の値を取るという制約が課され、対応する要素(64 と 27)が必ず異なる部分集合に属することが保証されます。

次の pyQBPP プログラムは、このアイデアを実装したものです:

ml = {x[0]: ~x[1]}
g = qbpp.replace(f, ml)
g.simplify_as_binary()
solver = qbpp.ExhaustiveSolver(g)
sol = solver.search()

full_sol = qbpp.Sol(f).set(sol, ml)

このプログラムでは、変数 x[0] を式 ~x[1] に置き換えるように、辞書 ml が定義されています。

qbpp.replace() 関数は、この置換を元の式 f に適用し、その結果の式は g に格納されます。その結果、g にはもはや変数 x[0] は含まれず、x[0] のすべての出現箇所が式 ~x[1] に置き換えられます。

その後、Exhaustive Solver を使用して g の最適解を求め、その結果は sol に格納されます。g には x[0] が現れないため、解 sol にも x[0] に対する割り当ては含まれません。

元の式 f に含まれる変数に対する完全な解を構成するために、まず 0 で初期化された Sol(f) を作成し、その後 set([sol, ml]) を呼び出して値を設定します。ここで set() には solml を同時に渡す必要がある点に注意してください。なぜなら、ml に含まれるマッピング(例:x[0] = ~x[1])が、sol に含まれる変数の値に依存する可能性があるためです。

このプログラムは、次の出力を生成します:

sol = 4:{{x[1],0},{x[2],1},{x[3],0},{x[4],1},{x[5],1},{x[6],0},{x[7],0}}
ml = {{x[0],1 -x[1]}}
full_sol = 4:{{x[0],1},{x[1],0},{x[2],1},{x[3],0},{x[4],1},{x[5],1},{x[6],0},{x[7],0}}
f(full_sol) = 4
p(full_sol) = 206
q(full_sol) = 204
P : 64 47 12 83
Q : 27 74 63 40

次のことを確認できます:

  • sol には x[0] が含まれていないこと

  • x[0]x[1] が反対の値を取っていること

  • 64 と 27 が意図どおり異なる部分集合に配置されていること

整数変数に対する replace 関数

整数変数は、replace() 関数を使用して固定の整数値に置き換えることができます。

ここでは、この機能を簡単な乗算式を用いて説明します。
\(p,q,r\) を整数変数とし、次の制約を考えます:

\[ p \times q-r=0 \]

この式は、いくつかの異なる解釈が可能であり、それぞれ異なる種類の問題になります:

  • 乗算 (Multiplication):
    \(p\)\(q\) の値が固定されているとき、この式を満たす \(r\) を求める。

  • 因数分解 (Factorization):
    \(r\) の値が固定されているとき、この式を満たす \(p\)\(q\) を求める。

  • 除算 (Division):
    \(p\)\(r\) の値が固定されているとき、この式を満たす \(q\) を求める。

replace() 関数を使用すると、整数変数を定数値に固定することができます。
ここでは、replace() を用いてこれらの問題を解く QUBO++ プログラムを示します。

乗算

次のプログラムでは、\(p=5\)\(q=7\) を固定し、その積 \(r=35\) を求めます:

replace-functions-program2.py
import pyqbpp as qbpp

p = qbpp.var("p", between=(2, 8))
q = qbpp.var("q", between=(2, 8))
r = qbpp.var("r", between=(2, 40))
f = qbpp.constrain(p * q - r, equal=0)
f.simplify_as_binary()

ml = {p: 5, q: 7}
g = qbpp.replace(f, ml)
g.simplify_as_binary()
print("g =", g)

solver = qbpp.EasySolver(g)
sol = solver.search(target_energy=0)

full_sol = qbpp.Sol(f).set(sol, ml)
print(f"p={full_sol(p)}, q={full_sol(q)}, r={full_sol(r)}")

このプログラムでは、元の式 f に含まれる整数変数 pq の値を固定するために、辞書 ml が使用されています。qbpp.replace(f, ml) を適用することで、f に含まれる変数 pq は、それぞれ定数 57 に置き換えられます。その結果の式は g に格納され、この式には変数 r のみが含まれるようになります。

その後、Easy Solver が g に適用され、その結果の解は sol に格納されます。すべての変数を含む完全な解を構築するために、まず f に対して 0 で初期化された Sol オブジェクトを作成し、その後 solml を用いて set() を呼び出し、バイナリ値を設定します。最後に、pqr の値が出力されます。

このプログラムは次の出力を生成し、乗算の結果が正しく得られていることを確認できます:

g = 1089 -65*r[0] -128*r[1] -248*r[2] -464*r[3] -800*r[4] -413*r[5] +4*r[0]*r[1] +8*r[0]*r[2] +16*r[0]*r[3] +32*r[0]*r[4] +14*r[0]*r[5] +16*r[1]*r[2] +32*r[1]*r[3] +64*r[1]*r[4] +28*r[1]*r[5] +64*r[2]*r[3] +128*r[2]*r[4] +56*r[2]*r[5] +256*r[3]*r[4] +112*r[3]*r[5] +224*r[4]*r[5]
p= 5, q= 7, r= 35

因数分解

\(r=35\) の因数分解を行うために、PyQBPP プログラム内の辞書 ml は次のように変更されます。

ml = {r: 35}

\(r\) の値を固定することで、ソルバーは次の制約を満たす整数値 \(p\)\(q\) を探索します。

\[ p \times q = 35 \]

このプログラムは次の出力を生成します。

g = 961 -120*p[0] -232*p[1] -336*p[2] -120*q[0] -232*q[1] -336*q[2] +16*p[0]*p[1] +24*p[0]*p[2] -45*p[0]*q[0] -80*p[0]*q[1] -105*p[0]*q[2] +48*p[1]*p[2] -80*p[1]*q[0] -136*p[1]*q[1] -168*p[1]*q[2] -105*p[2]*q[0] -168*p[2]*q[1] -189*p[2]*q[2] +16*q[0]*q[1] +24*q[0]*q[2] +48*q[1]*q[2] +20*p[0]*p[1]*q[0] +48*p[0]*p[1]*q[1] +84*p[0]*p[1]*q[2] +30*p[0]*p[2]*q[0] +72*p[0]*p[2]*q[1] +126*p[0]*p[2]*q[2] +20*p[0]*q[0]*q[1] +30*p[0]*q[0]*q[2] +60*p[0]*q[1]*q[2] +60*p[1]*p[2]*q[0] +144*p[1]*p[2]*q[1] +252*p[1]*p[2]*q[2] +48*p[1]*q[0]*q[1] +72*p[1]*q[0]*q[2] +144*p[1]*q[1]*q[2] +84*p[2]*q[0]*q[1] +126*p[2]*q[0]*q[2] +252*p[2]*q[1]*q[2] +16*p[0]*p[1]*q[0]*q[1] +24*p[0]*p[1]*q[0]*q[2] +48*p[0]*p[1]*q[1]*q[2] +24*p[0]*p[2]*q[0]*q[1] +36*p[0]*p[2]*q[0]*q[2] +72*p[0]*p[2]*q[1]*q[2] +48*p[1]*p[2]*q[0]*q[1] +72*p[1]*p[2]*q[0]*q[2] +144*p[1]*p[2]*q[1]*q[2]
p= 5, q= 7, r= 35

除算

\(r=35\)\(p=5\) を使った除算 \(r/p\) を計算するために、PyQBPP プログラム内の辞書 ml は次のように変更されます。

ml = {p: 5, r: 35}

このプログラムは次の出力を生成します。

g = 625 -225*q[0] -400*q[1] -525*q[2] +100*q[0]*q[1] +150*q[0]*q[2] +300*q[1]*q[2]
p= 5, q= 7, r= 35

これにより、除算の結果 \(q=r/p=7\) が正しく得られることが確認できます。

補足: PyQBPP は、式に対するメンバ関数版の replace() も提供しています。つまり:

  • f.replace(ml) は、ml に指定された置換を適用して、式 f をその場で更新します。

  • qbpp.replace(f, ml) は、元の式 f を変更せずに、置換を適用した新しい式を返します。

既存の式を変更したい場合は f.replace(ml) を、元の式を変更せずに保持したい場合は qbpp.replace(f, ml) を使用してください。

注意: Term に対する replace() qbpp.replace(t, ml)t.replace(ml) の両方が項に対して動作します。 項は式に昇格され、新しい式が返されます:

t = ~a * b * ~c * ~d  # Term
e = t.replace({~a: 1 - a, ~c: 1 - c, d: 1 - d})  # 式を返す

注意: 制約式に対する replace() メンバ関数の replace() は制約式では利用できません。 代わりにフリー関数形式を使用してください:

ee2 = qbpp.replace(ee, {x: 0, y: 1})  # 制約式で使用可能

注意: タプルのリストによるマッピング マッピング ml は辞書の代わりに (変数, 値) のタプルのリストとして渡すこともできます:

ml = [(x[0], 1), (x[1], 0)]
g = qbpp.replace(f, ml)

注意: 否定リテラルと replace() replace() 関数は x~x を独立したキーとして扱います。 辞書に {x: 0} を指定しても、~x が自動的に 1 に置換されるわけではありません。 式に ~x のような否定リテラルが含まれている場合、両方のマッピングを明示的に指定してください:

ml = {x: 0, ~x: 1}