多次元変数と式
多次元変数の定義
QUBO++ は多次元変数(または qbpp::Var オブジェクト)および多次元整数変数(または qbpp::VarInt オブジェクト)をサポートしています。 qbpp::var() および qbpp::var_int()を使用します。
基本的な使用法は以下の通りです:
qbpp::var("name",s1,s2,...,sd): 指定されたqbpp::Varオブジェクトの配列を作成し、nameと形状 $s1\times s2\times \cdots\times sd$ を持つl <= qbpp::var_int("name",s1,s2,...,sd) <= u: 指定された範囲と形状 $s1\times s2\times \cdots\times sd$ を持つqbpp::VarIntオブジェクトの配列を作成します。
以下のQUBO++プログラムは、それぞれ次元$2\times 3\times 4$の二値変数と整数変数を作成します。
#include "qbpp.hpp"
int main() {
auto x = qbpp::var("x", 2, 3, 4);
auto y = 1 <= qbpp::var_int("y", 2, 3, 4) <= 8;
std::cout << "x : " << x << std::endl;
std::cout << "y : " << y << std::endl;
}
このプログラムは以下を出力します:
x : {{{x[0][0][0],x[0][0][1],x[0][0][2],x[0][0][3]},{x[0][1][0],x[0][1][1],x[0][1][2],x[0][1][3]},{x[0][2][0],x[0][2][1],x[0][2][2],x[0][2][3]}},{{x[1][0][0],x[1][0][1],x[1][0][2],x[1][0][3]},{x[1][1][0],x[1][1][1],x[1][1][2],x[1][1][3]},{x[1][2][0],x[1][2][1],x[1][2][2],x[1][2][3]}}}
y : {{{1 +y[0][0][0][0] +2*y[0][0][0][1] +4*y[0][0][0][2],1 +y[0][0][1][0] +2*y[0][0][1][1] +4*y[0][0][1][2],1 +y[0][0][2][0] +2*y[0][0][2][1] +4*y[0][0][2][2],1 +y[0][0][3][0] +2*y[0][0][3][1] +4*y[0][0][3][2]},{1 +y[0][1][0][0] +2*y[0][1][0][1] +4*y[0][1][0][2],1 +y[0][1][1][0] +2*y[0][1][1][1] +4*y[0][1][1][2],1 +y[0][1][2][0] +2*y[0][1][2][1] +4*y[0][1][2][2],1 +y[0][1][3][0] +2*y[0][1][3][1] +4*y[0][1][3][2]},{1 +y[0][2][0][0] +2*y[0][2][0][1] +4*y[0][2][0][2],1 +y[0][2][1][0] +2*y[0][2][1][1] +4*y[0][2][1][2],1 +y[0][2][2][0] +2*y[0][2][2][1] +4*y[0][2][2][2],1 +y[0][2][3][0] +2*y[0][2][3][1] +4*y[0][2][3][2]}},{{1 +y[1][0][0][0] +2*y[1][0][0][1] +4*y[1][0][0][2],1 +y[1][0][1][0] +2*y[1][0][1][1] +4*y[1][0][1][2],1 +y[1][0][2][0] +2*y[1][0][2][1] +4*y[1][0][2][2],1 +y[1][0][3][0] +2*y[1][0][3][1] +4*y[1][0][3][2]},{1 +y[1][1][0][0] +2*y[1][1][0][1] +4*y[1][1][0][2],1 +y[1][1][1][0] +2*y[1][1][1][1] +4*y[1][1][1][2],1 +y[1][1][2][0] +2*y[1][1][2][1] +4*y[1][1][2][2],1 +y[1][1][3][0] +2*y[1][1][3][1] +4*y[1][1][3][2]},{1 +y[1][2][0][0] +2*y[1][2][0][1] +4*y[1][2][0][2],1 +y[1][2][1][0] +2*y[1][2][1][1] +4*y[1][2][1][2],1 +y[1][2][2][0] +2*y[1][2][2][1] +4*y[1][2][2][2],1 +y[1][2][3][0] +2*y[1][2][3][1] +4*y[1][2][3][2]}}}
各 qbpp::Var オブジェクトはとしてx[i][j][k]アクセスx可能。
各 qbpp::VarInt オブジェクトは、
内部的には3つの二y進y[i][j][k]変数によって表現される:
y[i][j][k][0]y[i][j][k][1]y[i][j][k][2]
指定された範囲の整数をバイナリ符号化したものに対応します。
多次元式の定義
QUBO++では、関数を用いて任意の深さの多次元式(または qbpp::Expr オブジェクト)を定義できます。 qbpp::expr() を次のように使用して定義できます:
qbpp::expr(s1,s2,...,sd): $s1\times s2\times \cdots\times sd$ の形状を持つqbpp::Exprオブジェクトの多次元配列を作成します。
以下のプログラムは、形状 $2\times x3\times 4$ の qbpp::Var オブジェクトの3次元配列
$2\times 3\times 4$
と
2次元配列 f サイズ $2\times 3$ の配列を定義します。
その後、三重の for ループを用いて、各 f[i][j] 各要素の和を累積する x[i][j][0], x[i][j][1], x[i][j][2]を累積する。 x[i][j][3]:
#include "qbpp.hpp"
int main() {
auto x = qbpp::var("x", 2, 3, 4);
auto f = qbpp::expr(2, 3);
for (size_t i = 0; i < 2; ++i) {
for (size_t j = 0; j < 3; ++j) {
for (size_t k = 0; k < 4; ++k) {
f[i][j] += x[i][j][k];
}
}
}
f.simplify_as_binary();
std::cout << "f = " << f << std::endl;
for (size_t i = 0; i < 2; ++i) {
for (size_t j = 0; j < 3; ++j) {
std::cout << "f[" << i << "][" << j << "] = " << f[i][j] << std::endl;
}
}
}
注意: simplify_as_binary() メンバー関数は、 qbpp::Expr オブジェクトの多次元配列に対しても適用可能である。
このような配列に対して呼び出されると、 simplify_as_binary() 各要素に対して個別に(要素単位で)適用します。
このプログラムは次の出力を生成します:
f = {{x[0][0][0] +x[0][0][1] +x[0][0][2] +x[0][0][3],x[0][1][0] +x[0][1][1] +x[0][1][2] +x[0][1][3],x[0][2][0] +x[0][2][1] +x[0][2][2] +x[0][2][3]},{x[1][0][0] +x[1][0][1] +x[1][0][2] +x[1][0][3],x[1][1][0] +x[1][1][1] +x[1][1][2] +x[1][1][3],x[1][2][0] +x[1][2][1] +x[1][2][2] +x[1][2][3]}}
f[0][0] = x[0][0][0] +x[0][0][1] +x[0][0][2] +x[0][0][3]
f[0][1] = x[0][1][0] +x[0][1][1] +x[0][1][2] +x[0][1][3]
f[0][2] = x[0][2][0] +x[0][2][1] +x[0][2][2] +x[0][2][3]
f[1][0] = x[1][0][0] +x[1][0][1] +x[1][0][2] +x[1][0][3]
f[1][1] = x[1][1][0] +x[1][1][1] +x[1][1][2] +x[1][1][3]
f[1][2] = x[1][2][0] +x[1][2][1] +x[1][2][2] +x[1][2][3]
自動型推論による式配列の作成
オブジェクトqbpp::Exprの配列は明示的に呼び出さずに作成できる qbpp::expr()関数を明示的に呼び出さずに作成できます。
関数呼び出しや算術演算の結果が配列形状の場合、同じ形状の qbpp::Expr オブジェクトの配列を定義できます。
以下のQUBO++プログラムは、f qbpp::Expr オブジェクトの配列xを作成します。 qbpp::Var オブジェクトの配列を作成します:
#include "qbpp.hpp"
int main() {
auto x = qbpp::var("x", 2, 3);
auto f = x + 1;
f += x - 2;
f.simplify_as_binary();
for (size_t i = 0; i < 2; ++i) {
for (size_t j = 0; j < 3; ++j) {
std::cout << "f[" << i << "][" << j << "] = " << f[i][j] << std::endl;
}
}
}
このプログラムでは、 x は $2 \times 3$ の qbpp::Var オブジェクトの配列として定義されます。
式 x + 1 は、オブジェクトの $2 \times 3$ 配列を生成します。 qbpp::Expr オブジェクトの$2 \times 3$配列を生成し、自動型推論によって f が自動型推論によって初期化される。
その後、式 x - 2 は要素ごとに f.
このプログラムは以下を出力する:
f[0][0] = -1 +2*x[0][0]
f[0][1] = -1 +2*x[0][1]
f[0][2] = -1 +2*x[0][2]
f[1][0] = -1 +2*x[1][0]
f[1][1] = -1 +2*x[1][1]
f[1][2] = -1 +2*x[1][2]
ベクトルと配列の実装
QUBO++ はベクトル(すなわち一次元配列)をqbpp::Vector<T> オブジェクトとして実装しており、これらは std::vector<T>と互換性があります。
テンプレートパラメータ T は qbpp::Expr, qbpp::Varまたは整数型です。
クラス qbpp::Vector<T> は互換性のために以下のメンバ関数を提供します std::vector<T>:
size(): ベクトル内の要素数を返します。resize(): ベクトルの要素数を変更します。reserve(): ベクトルのメモリ領域を確保します。push_back(): ベクトルの末尾に要素を追加します。emplace_back(): 要素を構築し、ベクトルの末尾に追加します。empty(): ベクトルに要素が含まれていない場合に true を返します。operator[]: 指定されたインデックスの要素を返します。begin(),end(): 要素へのアクセスと操作を行うイテレータ。
さらに、 std::vector<T>とは異なり、要素単位の演算に対してqbpp::Vector<T>以下の演算子をサポートします:
+: 2つのベクトル、またはベクトルとスカラーの要素ごとの加算。-: 2つのベクトル、またはベクトルとスカラーの要素ごとの減算。*: 2つのベクトル、またはベクトルとスカラーの要素ごとの乗算。- 一項演算子
-: ベクトルの全要素を否定します。
さらに、多次元配列は qbpp::Vector<T>として実装される。
例えば、以下のコードにおける x の型qbpp::Vector<qbpp::Vector<qbpp::Var>>は
auto x = qbpp::var("x", 2, 3);
上記の要素ごとの演算は、オペランドが同じ形状の場合にのみ多次元配列でサポートされます。
qbpp::Vector<T> はイテレータをサポートしているため、範囲ベースの for ループは auto 型推論を用いた範囲ベースのループを以下のように使用できます:
#include "qbpp.hpp"
int main() {
auto x = qbpp::var("x", 2, 3);
auto f = x + 1;
f += x - 2;
f.simplify_as_binary();
for (const auto& vec : f) {
for (const auto& element : vec) {
std::cout << "(" << element << ")";
}
std::cout << std::endl;
}
}
外側の for ループにおいて、各 qbpp::Vector<qbpp::Expr> オブジェクトは qbpp::Vector<qbpp::Vector<qbpp::Expr>> オブジェクト f は順に vec。
内側の for ループでは、 qbpp::Expr オブジェクトは vec は順番に element.
このプログラムは以下を出力します:
(-1 +2*x[0][0])(-1 +2*x[0][1])(-1 +2*x[0][2])
(-1 +2*x[1][0])(-1 +2*x[1][1])(-1 +2*x[1][2])