Hi-QUBO

多次元変数と式

多次元変数の定義

QUBO++ は多次元変数(または qbpp::Var オブジェクト)および多次元整数変数(または qbpp::VarInt オブジェクト)をサポートしています。 qbpp::var() および qbpp::var_int()を使用します。 基本的な使用法は以下の通りです:

以下の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つの二yy[i][j][k]変数によって表現される:

指定された範囲の整数をバイナリ符号化したものに対応します。

多次元式の定義

QUBO++では、関数を用いて任意の深さの多次元式(または qbpp::Expr オブジェクト)を定義できます。 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>と互換性があります。 テンプレートパラメータ Tqbpp::Expr, qbpp::Varまたは整数型です。

クラス qbpp::Vector<T> は互換性のために以下のメンバ関数を提供します std::vector<T>:

さらに、 std::vector<T>とは異なり、要素単位の演算に対してqbpp::Vector<T>以下の演算子をサポートします:

さらに、多次元配列は 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])

最終更新日: 2025.12.26