ベクトルに対する基本演算子と関数¶
基本的に、ベクトルに対する演算子や関数は**要素ごと(element-wise)**に適用されます。
ベクトルに対する基本演算子¶
基本演算子 +, -, *, / は、変数ベクトルおよび式ベクトルに対して使用できます。
QUBO++ では、これらの演算子は各要素に対して個別に適用されます。
PyQBPP では、これらの演算子は各要素に対して個別に適用されます。
ベクトルとスカラーの演算¶
ベクトルとスカラーを組み合わせる場合、スカラーはベクトルの各要素に適用されます。
例えば、サイズ3のベクトル x に対して:
2 * xは{2*x[0], 2*x[1], 2*x[2]}を生成します。x + 1は{x[0] + 1, x[1] + 1, x[2] + 1}を生成します。
次のプログラムは、この動作を示しています。
#include <qbpp/qbpp.hpp>
int main() {
auto x = qbpp::var("x", 3);
auto f = 2 * x + 1;
std::cout << "f = " << f << std::endl;
for (size_t i = 0; i < f.size(); ++i) {
std::cout << "f[" << i << "] = " << f[i] << std::endl;
}
}
import pyqbpp as qbpp
x = qbpp.var("x", 3)
f = 2 * x + 1
print("f =", f)
for i in range(len(f)):
print(f"f[{i}] =", f[i])
このプログラムは、2値変数のベクトル x = {x[0], x[1], x[2]} を生成します。2 * x は各要素を2倍し、+ 1 は各要素に1を加えるため、f は{1 + 2*x[0], 1 + 2*x[1], 1 + 2*x[2]}になります。
このプログラムの出力は次のとおりです。
f = {1 +2*x[0],1 +2*x[1],1 +2*x[2]}
f[0] = 1 +2*x[0]
f[1] = 1 +2*x[1]
f[2] = 1 +2*x[2]
ベクトル同士の演算¶
同じサイズの2つのベクトルを組み合わせる場合、演算は各インデックスごとに要素単位で実行されます。
次の例では、サイズ3のベクトル x と y を使用しています。
#include <qbpp/qbpp.hpp>
int main() {
auto x = qbpp::var("x", 3);
auto y = qbpp::var("y", 3);
auto f = 2 * x + 3 * y + 1;
std::cout << "f = " << f << std::endl;
for (size_t i = 0; i < f.size(); ++i) {
std::cout << "f[" << i << "] = " << f[i] << std::endl;
}
}
import pyqbpp as qbpp
x = qbpp.var("x", 3)
y = qbpp.var("y", 3)
f = 2 * x + 3 * y + 1
print("f =", f)
for i in range(len(f)):
print(f"f[{i}] =", f[i])
ここで
2 * xは{2*x[0], 2*x[1], 2*x[2]}になります。3 * yは{3*y[0], 3*y[1], 3*y[2]}になります。それらの加算は要素ごとに行われるため、i 番目の要素は
2*x[i] + 3*y[i]になります。+ 1も再び要素ごとに適用されます。
したがって、f は次のようになります。
{1 + 2*x[0] + 3*y[0], 1 + 2*x[1] + 3*y[1], 1 + 2*x[2] + 3*y[2]}
出力は次のとおりです。
f = {1 +2*x[0] +3*y[0],1 +2*x[1] +3*y[1],1 +2*x[2] +3*y[2]}
f[0] = 1 +2*x[0] +3*y[0]
f[1] = 1 +2*x[1] +3*y[1]
f[2] = 1 +2*x[2] +3*y[2]
ベクトル同士の演算では、同じサイズである必要があります。
より複雑な要素ごとの演算例¶
次の例では、ベクトルとスカラーの演算、ベクトル同士の演算、単項マイナス、および括弧を含む、より複雑な式を示します。
#include <qbpp/qbpp.hpp>
int main() {
auto x = qbpp::var("x", 3);
auto y = qbpp::var("y", 3);
auto f = 6 * -(x + 1) * (y - 1);
auto g = f / 3;
std::cout << "f = " << f << std::endl;
for (size_t i = 0; i < x.size(); ++i) {
std::cout << "f[" << i << "] = " << f[i] << std::endl;
}
std::cout << "g = " << g << std::endl;
for (size_t i = 0; i < x.size(); ++i) {
std::cout << "g[" << i << "] = " << g[i] << std::endl;
}
}
import pyqbpp as qbpp
x = qbpp.var("x", 3)
y = qbpp.var("y", 3)
f = 6 * -(x + 1) * (y - 1)
g = f / 3
print("f = ", f)
for i in range(len(x)):
print(f"f[{i}] = {f[i]}")
print("g = ", g)
for i in range(len(x)):
print(f"g[{i}] = {g[i]}")
この例でも、すべての演算は要素ごとに適用されます。
x + 1およびy - 1は、それぞれ{x[i] + 1}と{y[i] - 1}を生成します。単項マイナス
-(x + 1)も要素ごとに適用され、{-(x[i] + 1)}になります。乗算
6 * -(x + 1) * (y - 1)も要素ごとに実行されます。
したがって、各インデックス i に対して:
f[i] = 6 * (-(x[i] + 1)) * (y[i] - 1)
これを展開すると:
f[i] = 6 -6*x[i]*y[i] +6*x[i] -6*y[i]
最後に、g = f / 3 は各要素を 3 で割るため、
g[i] = 2 -2*x[i]*y[i] +2*x[i] -2*y[i]
となります。
出力は次のとおりです。
f = {6 -6*x[0]*y[0] +6*x[0] -6*y[0],6 -6*x[1]*y[1] +6*x[1] -6*y[1],6 -6*x[2]*y[2] +6*x[2] -6*y[2]}
f[0] = 6 -6*x[0]*y[0] +6*x[0] -6*y[0]
f[1] = 6 -6*x[1]*y[1] +6*x[1] -6*y[1]
f[2] = 6 -6*x[2]*y[2] +6*x[2] -6*y[2]
g = {2 -2*x[0]*y[0] +2*x[0] -2*y[0],2 -2*x[1]*y[1] +2*x[1] -2*y[1],2 -2*x[2]*y[2] +2*x[2] -2*y[2]}
g[0] = 2 -2*x[0]*y[0] +2*x[0] -2*y[0]
g[1] = 2 -2*x[1]*y[1] +2*x[1] -2*y[1]
g[2] = 2 -2*x[2]*y[2] +2*x[2] -2*y[2]
ベクトルに対する複合演算子¶
+=, -=, *=, /= といった複合演算子も、変数ベクトルおよび式ベクトルに対して使用できます。
これらの演算も 要素ごと(element-wise) に適用されます。
次の例は、サイズ3のベクトルに対する複合演算子の動作を示しています。
#include <qbpp/qbpp.hpp>
int main() {
auto x = qbpp::var("x", 3);
auto y = qbpp::var("y", 3);
auto f = 6 * x + 4;
f += 3 * y;
std::cout << "f = " << f << std::endl;
f -= 12;
std::cout << "f = " << f << std::endl;
f *= 2 * y;
std::cout << "f = " << f << std::endl;
f /= 2;
std::cout << "f = " << f << std::endl;
}
import pyqbpp as qbpp
x = qbpp.var("x", 3)
y = qbpp.var("y", 3)
f = 6 * x + 4
f += 3 * y
print("f =", f)
f -= 12
print("f =", f)
f *= 2 * y
print("f =", f)
f /= 2
print("f =", f)
このプログラムは次の出力を生成します。
f = {4 +6*x[0] +3*y[0],4 +6*x[1] +3*y[1],4 +6*x[2] +3*y[2]}
f = {-8 +6*x[0] +3*y[0],-8 +6*x[1] +3*y[1],-8 +6*x[2] +3*y[2]}
f = {12*x[0]*y[0] +6*y[0]*y[0] -16*y[0],12*x[1]*y[1] +6*y[1]*y[1] -16*y[1],12*x[2]*y[2] +6*y[2]*y[2] -16*y[2]}
f = {6*x[0]*y[0] +3*y[0]*y[0] -8*y[0],6*x[1]*y[1] +3*y[1]*y[1] -8*y[1],6*x[2]*y[2] +3*y[2]*y[2] -8*y[2]}
ベクトルに対する square 関数¶
sqr 関数もベクトルに対して使用できます。
こちらも各要素ごとに二乗が計算されます。
#include <qbpp/qbpp.hpp>
int main() {
auto x = qbpp::var("x", 3);
auto f = x + 1;
std::cout << "f = " << qbpp::sqr(f) << std::endl;
std::cout << "f = " << f << std::endl;
f.sqr();
std::cout << "f = " << f << std::endl;
}
import pyqbpp as qbpp
x = qbpp.var("x", 3)
f = qbpp.sqr(x + 1)
print("f =", f)
このプログラムは次の出力を生成します。
f = {1 +x[0]*x[0] +x[0] +x[0],1 +x[1]*x[1] +x[1] +x[1],1 +x[2]*x[2] +x[2] +x[2]}
f = {1 +x[0],1 +x[1],1 +x[2]}
f = {1 +x[0]*x[0] +x[0] +x[0],1 +x[1]*x[1] +x[1] +x[1],1 +x[2]*x[2] +x[2] +x[2]}
ベクトルに対する simplify 関数¶
simplify 関数もベクトルに対して利用可能で、各要素がそれぞれ簡約されます。
#include <qbpp/qbpp.hpp>
int main() {
auto x = qbpp::var("x", 3);
auto f = qbpp::sqr(x - 1);
std::cout << "f = " << f << std::endl;
std::cout << "simplified(f) = " << qbpp::simplify(f) << std::endl;
std::cout << "simplified_as_binary(f) = " << qbpp::simplify_as_binary(f) << std::endl;
std::cout << "simplified_as_spin(f) = " << qbpp::simplify_as_spin(f) << std::endl;
}
import pyqbpp as qbpp
x = qbpp.var("x", 3)
f = qbpp.sqr(x - 1)
print("f =", f)
print("qbpp.simplify(f) =", qbpp.simplify(f))
print("qbpp.simplify_as_binary(f) =", qbpp.simplify_as_binary(f))
print("qbpp.simplify_as_spin(f) =", qbpp.simplify_as_spin(f))
このプログラムは次の出力を生成します。
f = {1 +x[0]*x[0] -x[0] -x[0],1 +x[1]*x[1] -x[1] -x[1],1 +x[2]*x[2] -x[2] -x[2]}
simplified(f) = {1 -2*x[0] +x[0]*x[0],1 -2*x[1] +x[1]*x[1],1 -2*x[2] +x[2]*x[2]}
simplified_as_binary(f) = {1 -x[0],1 -x[1],1 -x[2]}
simplified_as_spin(f) = {2 -2*x[0],2 -2*x[1],2 -2*x[2]}
NOTE:
これらの演算子および関数は、多次元配列に対しても同様に動作します。