Variable and Expression Classes

qbpp::Var, qbpp::Term, and qbpp::Expr classes

QUBO++ provides the following fundamental classes:

  • qbpp::Var: Represents a variable symbolically and is associated with a string used for display. Internally, a 32-bit unsigned integer is used as its identifier.
  • qbpp::Term: Represents a product term consisting of an integer coefficient and one or more qbpp::Var objects. The data type of the integer coefficient is defined by the COEFF_TYPE macro, whose default value is int32_t. Each qbpp::Term stores its variables using a static array (inline buffer of 2 elements) combined with dynamic allocation for higher-degree terms, allowing terms of arbitrary degree with no upper limit.
  • qbpp::Expr: Represents an expanded expression consisting of an integer constant term and zero or more qbpp::Term objects. The data type of the integer constant term is defined by the ENERGY_TYPE macro, whose default value is int64_t.

In the following program, x and y are qbpp::Var objects, t is a qbpp::Term object, and f is a qbpp::Expr object:

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto y = qbpp::var("y");
  auto t = 2 * x * y;
  auto f = t - x + 1;

  std::cout << "x = " << x << std::endl;
  std::cout << "y = " << y << std::endl;
  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;
}

This program produces the following output:

x = x
y = y
t = 2*x*y
f = 1 -x +2*x*y

If the data types are to be explicitly specified, the program can be rewritten as follows:

#include <qbpp/qbpp.hpp>

int main() {
  qbpp::Var x = qbpp::var("x");
  qbpp::Var y = qbpp::var("y");
  qbpp::Term t = 2 * x * y;
  qbpp::Expr f = t - x + 1;

  std::cout << "x = " << x << std::endl;
  std::cout << "y = " << y << std::endl;
  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;
}

qbpp::Var objects are immutable and cannot be updated after creation. In contrast, qbpp::Term and qbpp::Expr objects are mutable and can be updated via assignment.

For example, as shown in the following program, compound assignment operators can be used to update qbpp::Term and qbpp::Expr objects:

#include <qbpp/qbpp.hpp>

int main() {
  qbpp::Var x = qbpp::var("x");
  qbpp::Var y = qbpp::var("y");
  qbpp::Term t = 2 * x * y;
  qbpp::Expr f = t - x + 1;

  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;

  t *= 3 * x;
  f += 2 * y;

  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;
}

This program prints the following output:

t = 2*x*y
f = 1 -x +2*x*y
t = 6*x*y*x
f = 1 -x +2*x*y +2*y

In most cases, there is no need to explicitly use qbpp::Term objects. They should only be used when maximum performance optimization is required.

However, note that auto type deduction may create a qbpp::Term object, which cannot store general expressions. For example, the following program results in a compilation error because an expression is assigned to a qbpp::Term object:

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto y = qbpp::var("y");

  auto t = 2 * x * y;
  t = x + 1;
}

If a qbpp::Expr object is intended, qbpp::toExpr() can be used to explicitly construct one, as shown below:

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto y = qbpp::var("y");
  auto t = qbpp::toExpr(2 * x * y);
  auto f = qbpp::toExpr(1);

  t += x + 1;
  f += t;

  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;
}

In this program, both t and f are qbpp::Expr objects and can store general expressions. In particular, f is created as a qbpp::Expr object containing only a constant term with value 1 and no product terms.

Integer Ranges: COEFF_TYPE and ENERGY_TYPE

The macros COEFF_TYPE and ENERGY_TYPE define the data types used for coefficients and energy values in expressions. The ENERGY_TYPE macro is also used as the data type for the integer constant term of a qbpp::Expr object. The following types can be specified:

Type Range Large constant syntax
int16_t ±3.3×10⁴ 1234 (integer literal)
int32_t ±2.1×10⁹ 12345 (integer literal)
int64_t ±9.2×10¹⁸ 1234567890123456789LL
qbpp::int128_t ±1.7×10³⁸ qbpp::int128_t("12345678901234567890")
qbpp::cpp_int unlimited qbpp::cpp_int("...")

The type qbpp::cpp_int represents an integer with an arbitrary number of digits.

By default, coeff_t is int32_t and energy_t is int64_t. To use a different type, define one of the following macros before including the header (or pass as a compiler flag -D...):

Macro coeff_t energy_t Library
INTEGER_TYPE_C16E32 int16_t int32_t libqbpp_c16e32.so
INTEGER_TYPE_C32E32 int32_t int32_t libqbpp_c32e32.so
(default) int32_t int64_t libqbpp_c32e64.so
INTEGER_TYPE_C64E64 int64_t int64_t libqbpp_c64e64.so
INTEGER_TYPE_C64E128 int64_t int128_t libqbpp_c64e128.so
INTEGER_TYPE_C128E128 int128_t int128_t libqbpp_c128e128.so
INTEGER_TYPE_CPP_INT cpp_int cpp_int libqbpp_cppint.so

Example:

#define INTEGER_TYPE_CPP_INT
#include <qbpp/easy_solver.hpp>

The appropriate library is automatically loaded at runtime based on the specified types; no explicit linking is required.

String constructors

For qbpp::int128_t and qbpp::cpp_int, constant values that exceed the 64-bit integer range can be specified using string constructors. The string is parsed as a decimal number at runtime.

Note: Standard integer literals (e.g., 12345) and 64-bit literals with the LL suffix can be used directly with any type via implicit conversion. String constructors are only needed when the value exceeds the int64_t range (±9.2×10¹⁸).

Example with qbpp::int128_t

The following program creates a qbpp::Expr object with coefficients exceeding 64-bit range:

#define INTEGER_TYPE_C128E128

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto y = qbpp::var("y");
  auto f = qbpp::int128_t("12345678901234567890") * x +
           qbpp::int128_t("98765432109876543210") * y;
  std::cout << "f = " << f << std::endl;
}

This program produces the following output:

f = 12345678901234567890*x +98765432109876543210*y

Example with qbpp::cpp_int

The following program creates a qbpp::Expr object with very large coefficient and constant terms:

#define INTEGER_TYPE_CPP_INT

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto f = qbpp::cpp_int("123456789012345678901234567890") * x +
           qbpp::cpp_int("987654321098765432109876543210");
  std::cout << "f = " << f << std::endl;
}

This program produces the following output:

f = 987654321098765432109876543210 +123456789012345678901234567890*x

変数クラスと式クラス

qbpp::Var、qbpp::Term、qbpp::Expr クラス

QUBO++は以下の基本クラスを提供します。

  • qbpp::Var: 変数をシンボリックに表現し、表示用の文字列が関連付けられます。内部的には32ビット符号なし整数が識別子として使用されます。
  • qbpp::Term: 整数係数と1つ以上の qbpp::Var オブジェクトからなる積の項を表現します。整数係数のデータ型は COEFF_TYPE マクロで定義され、デフォルト値は int32_t です。 各 qbpp::Term は変数を静的配列(インラインバッファ2要素)と動的確保の組み合わせで格納し、次数に上限なく任意の高次項を扱うことができます。
  • qbpp::Expr: 整数定数項と0個以上の qbpp::Term オブジェクトからなる展開された式を表現します。整数定数項のデータ型は ENERGY_TYPE マクロで定義され、デフォルト値は int64_t です。

以下のプログラムでは、xyqbpp::Var オブジェクト、tqbpp::Term オブジェクト、fqbpp::Expr オブジェクトです。

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto y = qbpp::var("y");
  auto t = 2 * x * y;
  auto f = t - x + 1;

  std::cout << "x = " << x << std::endl;
  std::cout << "y = " << y << std::endl;
  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;
}

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

x = x
y = y
t = 2*x*y
f = 1 -x +2*x*y

データ型を明示的に指定する場合、プログラムは以下のように書き直せます。

#include <qbpp/qbpp.hpp>

int main() {
  qbpp::Var x = qbpp::var("x");
  qbpp::Var y = qbpp::var("y");
  qbpp::Term t = 2 * x * y;
  qbpp::Expr f = t - x + 1;

  std::cout << "x = " << x << std::endl;
  std::cout << "y = " << y << std::endl;
  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;
}

qbpp::Var オブジェクトは 不変(immutable) であり、作成後に更新できません。 一方、qbpp::Termqbpp::Expr オブジェクトは 可変(mutable) であり、代入によって更新できます。

例えば、以下のプログラムに示すように、複合代入演算子を使用して qbpp::Termqbpp::Expr オブジェクトを更新できます。

#include <qbpp/qbpp.hpp>

int main() {
  qbpp::Var x = qbpp::var("x");
  qbpp::Var y = qbpp::var("y");
  qbpp::Term t = 2 * x * y;
  qbpp::Expr f = t - x + 1;

  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;

  t *= 3 * x;
  f += 2 * y;

  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;
}

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

t = 2*x*y
f = 1 -x +2*x*y
t = 6*x*y*x
f = 1 -x +2*x*y +2*y

ほとんどの場合、qbpp::Term オブジェクトを明示的に使用する必要はありません。 最大限のパフォーマンス最適化が必要な場合にのみ使用すべきです。

ただし、auto 型推論により qbpp::Term オブジェクトが作成される場合があり、一般的な式を格納できないことに注意してください。 例えば、以下のプログラムは、式が qbpp::Term オブジェクトに代入されるため、コンパイルエラーになります。

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto y = qbpp::var("y");

  auto t = 2 * x * y;
  t = x + 1;
}

qbpp::Expr オブジェクトを意図する場合、以下に示すように qbpp::toExpr() を使用して明示的に構築できます。

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto y = qbpp::var("y");
  auto t = qbpp::toExpr(2 * x * y);
  auto f = qbpp::toExpr(1);

  t += x + 1;
  f += t;

  std::cout << "t = " << t << std::endl;
  std::cout << "f = " << f << std::endl;
}

このプログラムでは、tf の両方が qbpp::Expr オブジェクトであり、一般的な式を格納できます。 特に、f は値 1 の定数項のみを持ち、積の項を持たない qbpp::Expr オブジェクトとして作成されます。

整数の範囲:COEFF_TYPE と ENERGY_TYPE

マクロ COEFF_TYPEENERGY_TYPE は、式内の係数とエネルギー値に使用されるデータ型を定義します。 ENERGY_TYPE マクロは、qbpp::Expr オブジェクトの整数定数項のデータ型としても使用されます。 これらには次の型を指定することができます。

範囲 大きな定数の構文
int16_t ±3.3×10⁴ 1234(整数リテラル)
int32_t ±2.1×10⁹ 12345(整数リテラル)
int64_t ±9.2×10¹⁸ 1234567890123456789LL
qbpp::int128_t ±1.7×10³⁸ qbpp::int128_t("12345678901234567890")
qbpp::cpp_int 無制限 qbpp::cpp_int("...")

qbpp::cpp_int は任意桁数の整数を表します。

デフォルトでは coeff_tint32_tenergy_tint64_t です。 デフォルト以外の型を使用するには、ヘッダのインクルード前に以下のマクロの一つを定義します(またはコンパイラフラグ -D... で指定):

マクロ coeff_t energy_t ライブラリ
INTEGER_TYPE_C16E32 int16_t int32_t libqbpp_c16e32.so
INTEGER_TYPE_C32E32 int32_t int32_t libqbpp_c32e32.so
(デフォルト) int32_t int64_t libqbpp_c32e64.so
INTEGER_TYPE_C64E64 int64_t int64_t libqbpp_c64e64.so
INTEGER_TYPE_C64E128 int64_t int128_t libqbpp_c64e128.so
INTEGER_TYPE_C128E128 int128_t int128_t libqbpp_c128e128.so
INTEGER_TYPE_CPP_INT cpp_int cpp_int libqbpp_cppint.so

使用例:

#define INTEGER_TYPE_CPP_INT
#include <qbpp/easy_solver.hpp>

指定された型に基づいて適切なライブラリが実行時に自動的にロードされるため、明示的なリンクは不要です。

文字列コンストラクタ

qbpp::int128_t および qbpp::cpp_int では、 64ビット整数の範囲を超える定数値を文字列コンストラクタで指定できます。 文字列は実行時に10進数として解析されます。

Note: 標準の整数リテラル(例: 12345)や LL サフィックス付きの64ビットリテラルは、 暗黙の型変換によりどの型でもそのまま使用できます。 文字列コンストラクタが必要になるのは、値が int64_t の範囲(±9.2×10¹⁸)を超える場合のみです。

qbpp::int128_t の例

以下のプログラムは、64ビット範囲を超える係数を持つ qbpp::Expr オブジェクトを作成します。

#define INTEGER_TYPE_C128E128

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto y = qbpp::var("y");
  auto f = qbpp::int128_t("12345678901234567890") * x +
           qbpp::int128_t("98765432109876543210") * y;
  std::cout << "f = " << f << std::endl;
}

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

f = 12345678901234567890*x +98765432109876543210*y

qbpp::cpp_int の例

以下のプログラムは、非常に大きな係数と定数項を持つ qbpp::Expr オブジェクトを作成します。

#define INTEGER_TYPE_CPP_INT

#include <qbpp/qbpp.hpp>

int main() {
  auto x = qbpp::var("x");
  auto f = qbpp::cpp_int("123456789012345678901234567890") * x +
           qbpp::cpp_int("987654321098765432109876543210");
  std::cout << "f = " << f << std::endl;
}

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

f = 987654321098765432109876543210 +123456789012345678901234567890*x