# 式の演算子と関数
:::{container} prog-cpp
以下の表は,`qbpp::Expr`オブジェクトで利用可能な演算子と関数をまとめたものです.
| 演算子/関数 | 演算子記号/関数名 | 関数タイプ | 戻り値の型 | 引数の型 |
|-------------------------------|------------------------------------------------------|---------------|----------------|------------------------|
| 型変換 | `toExpr()` | グローバル | `qbpp::Expr` | `ExprType` |
| 代入 | `=` | メンバ | `qbpp::Expr` | `ExprType` |
| 二項演算子 | `+`, `-`, `*` | グローバル | `qbpp::Expr` | `ExprType`-`ExprType` |
| 複合代入演算子 | `+=`, `-=`, `*=` | メンバ | `qbpp::Expr` | `ExprType` |
| 除算 | `/` | グローバル | `qbpp::Expr` | `ExprType`-`Int` |
| 複合除算 | `/=` | メンバ | `qbpp::Expr` | `Int` |
| 単項演算子 | `+`, `-` | グローバル | `qbpp::Expr` | `ExprType` |
| 比較(等値) | `==` | グローバル | `qbpp::Expr`(制約式) | `ExprType`-`Int` |
| 比較(範囲比較) | `<= <=` | グローバル | `qbpp::Expr`(制約式) | `IntInf`-`ExprType`-`IntInf` |
| 比較(片側) | `<=`, `>=` | グローバル | `qbpp::Expr`(制約式) | `ExprType`-`Int` |
| 二乗 | `sqr()` | グローバル | `qbpp::Expr` | `ExprType` |
| 二乗 | `sqr()` | メンバ | `qbpp::Expr` | - |
| 最大公約数 | `gcd()` | グローバル | `Int` | `ExprType` |
| 簡約化 | `simplify()`, `simplify_as_binary()`, `simplify_as_spin()` | グローバル | `qbpp::Expr` | `ExprType` |
| 簡約化 | `simplify()`, `simplify_as_binary()`, `simplify_as_spin()` | メンバ | `qbpp::Expr` | - |
| 評価 | `operator()` | メンバ | `Int` | `ExprType`-`qbpp::MapList` |
| 置換 | `replace()` | グローバル | `qbpp::Expr` | `ExprType`-`qbpp::MapList` |
| 置換 | `replace()` | メンバ | `qbpp::Expr` | `qbpp::MapList` |
| バイナリ/スピン変換 | `binary_to_spin()`, `spin_to_binary()` | グローバル | `qbpp::Expr` | `ExprType` |
| バイナリ/スピン変換 | `binary_to_spin()`, `spin_to_binary()` | メンバ | `qbpp::Expr` | - |
| HUBO→QUBO 変換 | `reduce()` | グローバル | `qbpp::Expr` | `ExprType` |
| HUBO→QUBO 変換 | `reduce()` | メンバ | `qbpp::Expr` | - |
| スライス(タプルインデックス) | `operator()` | メンバ | `Array` | - |
| 連結 | `concat()` | グローバル | `Array` | `Array`-`Array` |
| 連結(スカラー付き) | `concat()` | グローバル | `Array` | `Expr`-`Array` |
## 型変換: **`qbpp::toExpr()`**
グローバル関数**`qbpp::toExpr()`**は引数を`qbpp::Expr`インスタンスに変換して返します.
引数は以下のいずれかです:
- 整数
- 変数(`qbpp::Var`)
- 積項(`qbpp::Term`)
- 式(`qbpp::Expr`)-- この場合,変換は行われません
これらの引数の型を総称して`ExprType`と呼びます.
## 式関連の型: **`ExprType`**
**`ExprType`**という用語は,`qbpp::Expr`オブジェクトに変換可能な型のカテゴリを示します.
## 整数関連の型: **`Int`**と**`IntInf`**
- **`Int`**: 通常の整数
- **`IntInf`**: 整数,`-qbpp::inf`,または`+qbpp::inf`のいずれかで,無限の境界を表します.
## グローバル関数とメンバ関数
`qbpp::Expr`に関連する演算子と関数は2つの形式で提供されます:
- **グローバル関数**:
少なくとも1つのExprType引数を取り,通常は入力を変更せずに新しい`qbpp::Expr`オブジェクトを返します.
- **メンバ関数**:
`qbpp::Expr`クラスのメンバ関数です.
多くの場合,呼び出し元のオブジェクトを更新し,結果の`qbpp::Expr`も返します.
### 例: `sqr()`
`sqr()`関数は式の二乗を計算し,両方の形式で利用できます:
- `sqr(f)`(グローバル): fを変更せずにfの二乗を返します
- `f.sqr()`(メンバ): fをその二乗に更新し,更新された式を返します
## 代入演算子: `=`
左辺は`qbpp::Expr`オブジェクトでなければなりません.
右辺は`ExprType`でなければならず,まず`qbpp::Expr`に変換されます.
変換された式が左辺に代入されます.
## 二項演算子: `+`, `-`, `*`
これらの演算子はグローバル関数として定義されています.
2つの`ExprType`オペランドを取り,結果を計算して返します.
少なくとも1つのオペランドが`qbpp::Expr`の場合,結果は常に`qbpp::Expr`になります.
どちらのオペランドも`qbpp::Expr`でない場合,結果は`qbpp::Term`になることがあります.
### 例
`qbpp::Var`型の変数`x`の場合:
- `2 + x`: `qbpp::Expr`
- `2 * x`: `qbpp::Term`
## 複合代入演算子: `+=`, `-=`, `*=`
これらの演算子はメンバ関数として定義されています.
左辺は`qbpp::Expr`でなければなりません.
右辺のオペランドを使用して指定された演算が適用されます.
左辺の式がその場で更新されます.
## 除算`/`と複合除算`/=`
除算演算子`/`はグローバル関数として定義されています.
非整数の`ExprType`オペランドを**被除数**として,整数オペランドを**除数**として取り,**商**を`qbpp::Expr`として返します.
被除数の式は除数で割り切れなければなりません.つまり,
式内の整数定数項とすべての整数係数が除数で割り切れる必要があります.
複合除算演算子`/=`はメンバ関数として定義されています.
- 左辺は`qbpp::Expr`でなければなりません.
- 右辺は整数でなければなりません.
同じ割り切れ条件が適用され,除算はその場で実行され,左辺の式が更新されます.
## 比較(等値): `==`
等値比較演算子`==`は以下を取ります:
- 左辺に非整数の`ExprType`
- 右辺に整数
等値制約が満たされたときに最小値0となる式を返します.
より具体的には,非整数の`ExprType`オブジェクト`f`と整数`n`に対して,演算子は`qbpp::sqr(f-n)`を返します.
返されたオブジェクト`g`について:
- **`g`**は制約式`qbpp::sqr(f - n)`を表し,
- **`g.body()`**は基礎となる式`f`を返します.
### 制約式の `qbpp::Expr`
ここで `g` は制約式の `qbpp::Expr`(penalty + body のメタデータを持つ `qbpp::Expr`)です.
`g.body()` を呼ぶと,関連付けられた基礎となる `qbpp::Expr` オブジェクトが返されます.
## 比較(範囲比較): `<= <=`
**範囲比較演算子**は次の形式で記述されます:
```{include} /../programFiles/markDown/quickReference/qr-operation.md
:start-after:
:end-before:
```
ここで:
- `f`は非整数のExprType,
- `l`と`u`は整数です.
この演算子は,範囲制約が満たされたときに最小値0となる式を返します.
より具体的には,単位間隔を持つ補助整数変数`a`が範囲`[l,u-1]`の値を取るように暗黙的に導入され,演算子は以下を返します:
```{include} /../programFiles/markDown/quickReference/qr-operation.md
:start-after:
:end-before:
```
返された制約式 `qbpp::Expr` オブジェクト`g`について:
- **`g`**は制約式`(f - a)(f - (a + 1))`を表し,
- **`g.body()`**は基礎となる式`f`を返します.
## 片側比較: `<=`, `>=`
非整数 ExprType `f` と整数 `n` について,片側制約演算子が用意されています:
```{include} /../programFiles/markDown/quickReference/qr-operation.md
:start-after:
:end-before:
```
それぞれ片側制約が満たされたときに最小値 0 となる制約式を返します.
省略された側の境界は内部で `f.neg_sum()`(`f` の最小可能値)または `f.pos_sum()`(`f` の最大可能値)に設定されるので,補助整数変数の範囲は要素が取りうる範囲ぴったりに収まります.
単独片側演算子と `qbpp::inf` を用いたチェイン形は同一の制約式を生成します:
| 単独片側形 | 同等のチェイン形 |
|------------------|---------------------------------|
| `f <= n` | `-qbpp::inf <= f <= n` |
| `f >= n` | `n <= f <= +qbpp::inf` |
単独片側形が推奨記法です.チェイン形は無限境界を明示したい場合 (テンプレートコード等) のために残されています.
> **注意**
> 片側制約は必ず **式を左辺に置いて** 書きます.
> `n >= expr` はコンパイルエラー (`= delete` で明示的に禁止,エラーメッセージは
> *"use of deleted function"*).
> 逆向きの `n <= expr` は単独ではエラーにできません (チェイン `n <= expr <= m` の
> 開始部と同じトークン列のため).ただし `std::pair` を返すので `+`, `*`, `sqr()`,
> `simplify_*()` 等を一切持たず,その後の操作で必ずコンパイルエラーになります.
配列 `arr` に対しても要素ごとに `arr <= n` / `arr >= n` が動作し,各要素の `pos_sum()` / `neg_sum()` を自動的に他側境界とします (補助変数のサイズが要素ごとに最小化されます).
## 二乗関数: `sqr()`
qbpp::Exprオブジェクト`f`に対して:
- **`qbpp::sqr(f)`**(グローバル関数): 式`f * f`を返します.
引数`f`は非整数の`ExprType`オブジェクトでもかまいません.
- **`f.sqr()`**(メンバ関数):
`f`をその場で`f * f`に置き換え,更新された式を返します.
## 最大公約数関数`gcd()`
グローバル関数**`gcd()`**は`qbpp::Expr`オブジェクトを引数として取り,すべての整数係数と整数定数項の最大公約数(GCD)を返します.
与えられた`qbpp::Expr`オブジェクトは結果のGCDで割り切れるため,すべての整数係数と整数定数項をGCDで除算しても,式の構造や最適解は変わりません.
## 簡約化関数: `simplify()`, `simplify_as_binary()`, `simplify_as_spin()`
`qbpp::Expr`オブジェクト`f`に対して,メンバ関数**`f.simplify()`**は以下の操作をその場で実行します:
- 各項内の変数を一意な変数IDに従ってソート
- 重複する項をマージ
- 項を以下の規則でソート:
- 低次の項が先に現れる
- 同じ次数の項は辞書順に並べる
グローバル関数**`qbpp::simplify(f)`**は`f`を変更せずに同じ操作を実行します.
### バイナリとスピンの簡約化
簡約化関数の2つの特殊なバリアントが提供されています:
- **`simplify_as_binary()`**:
すべての変数がバイナリ値$\lbrace 0,1\rbrace$を取ることを仮定して簡約化が実行されます.
恒等式$x^2=x$がすべての変数$x$に適用されます.
- **`simplify_as_spin()`**
すべての変数がスピン値$\lbrace -1,+1\rbrace$を取ることを仮定して簡約化が実行されます.
恒等式$x^2=1$がすべての変数$x$に適用されます.
両方のバリアントはメンバ関数とグローバル関数として利用できます:
- メンバ関数: その場で簡約化を実行し,`f`を更新します.
- `f.simplify_as_binary()`
- `f.simplify_as_spin()`
- グローバル関数: fを変更せずに簡約化された式を返します.
- `qbpp::simplify_as_binary(f)`
- `qbpp::simplify_as_spin(f)`
## 評価関数
**`qbpp::MapList`**オブジェクトは,`qbpp::Var`オブジェクトと整数のペアのリストを格納します.
各ペアは変数から整数値へのマッピングを定義します.
`qbpp::Expr`オブジェクト`f`と`qbpp::MapList`オブジェクト`ml`に対して,評価関数`f(ml)`は`ml`で指定された変数割り当ての下で`f`の値を評価し,結果の整数値を返します.
`f`に現れるすべての変数は,`ml`に対応するマッピングが定義されていなければなりません.
## 置換関数: `replace()`
**`qbpp::MapList`**オブジェクトには,`qbpp::Var`オブジェクトと`ExprType`オブジェクトのペアも含めることができます.
このようなペアは変数から式へのマッピングを定義します.
`qbpp::Expr`オブジェクト`f`と`qbpp::MapList`オブジェクト`ml`に対して:
- **`qbpp::replace(f, ml)`**:
`f`を変更せずに,`ml`のマッピングに従って`f`内の変数を置換した新しい`qbpp::Expr`オブジェクトを返します.
- **`f.replace(ml)`**:
`ml`のマッピングに従って`f`内の変数をその場で置換し,結果の`qbpp::Expr`オブジェクトを返します.
## バイナリ/スピン変換関数: `spin_to_binary()`, `binary_to_spin()`
`x`をバイナリ変数,sをスピン変数とします.
`x = 1`であるとき,かつそのときに限り`s = 1`であると仮定します.
この仮定の下で,以下の関係が成り立ちます:
$$
\begin{aligned}
s &= 2x-1 \\
x &= (s+1)/2
\end{aligned}
$$
$f(s)$をスピン変数$s$の関数とします.
このとき,関数$g(x)=f(2x-1)$は上記の関係の下で同じ値を与えるバイナリ変数$x$の関数です.
**`spin_to_binary()`**関数はこの関係を使用して,スピン変数の関数を表す`qbpp::Expr`オブジェクトをバイナリ変数の関数を表す等価な`qbpp::Expr`オブジェクトに変換します.
具体的には,`f`内のすべてのスピン変数`s`を`2 * s - 1`に置換します.
- **`qbpp::spin_to_binary(f)`**:
`f`内のすべてのスピン変数`s`を`2 * s - 1`に置換した新しい`qbpp::Expr`オブジェクトを生成して返します.
- **`f.spin_to_binary()`**:
`qbpp::spin_to_binary(f)`を使用して`f`をその場で更新し,更新された式を返します.
同様に,**`binary_to_spin()`**関数は`f`内のすべてのバイナリ変数`x`を`(x + 1) / 2`に置換します.
結果の式には非整数の係数が含まれる場合があります.
そのため,すべての係数が整数になるように,式全体が$2^d$($d$はすべての項の最大次数)で乗算されます.
`spin_to_binary()`と同様に,`binary_to_spin()`にもグローバル関数とメンバ関数の両方のバリアントが提供されています.
## 非線形関数: `abs()`, `relu()`, `max()`, `min()`
式の中で絶対値・ReLU・最大値・最小値を直接使えます.これらを含む式は,
Hi-QUBO に同梱されているソルバーが関数値を直接扱って探索します
(補助変数やペナルティ多項式を自分で設計する必要はありません).
詳しくは[非線形関数](../advanced/non-linear.md)を参照してください.
- **`qbpp::abs(f)`** / **`qbpp::abs(f, k)`**:
$|f|$ / $|f|^k$ を表す `qbpp::Expr` を返します(`k` は 1 か 2).
- **`qbpp::relu(f)`** / **`qbpp::relu(f, k)`**:
$\max(0, f)$ / $\max(0, f)^k$ を表す `qbpp::Expr` を返します(`k` は 1 か 2).
- **`qbpp::max(f, g)`** / **`qbpp::min(f, g)`**:
2 つの式の最大値・最小値を表す `qbpp::Expr` を返します.
これらの関数を含む式には次の制限があります:
- 係数は任意の定数で加減・スケールできます(負の係数は関数値を最大化する
向きに働きます).ただし MIP ソルバーでは正の係数のみ使えます.
- `expand_cons()`・`reduce()`・`binarize()` には対応していません.
- 同梱ソルバーはすべての関数に対応します.MIP ソルバーでは,
[GurobiSolver](../solver/gurobi-solver-usage.md) と ScipSolver(Quadratic 方式)が `abs`・`relu`
(`k` = 1, 2)に,線形化方式の MIP ソルバーが `abs`・`relu`(`k` = 1)に
対応します.`max`・`min` は凸方向(`w*max` の最小化・min の最大化)なら
MIP ソルバーでも解けます.
## HUBO→QUBO 変換関数: `reduce()`
**`reduce()`**関数は,HUBO 式(3 次以上の項を含みうる多項式)を**等価な QUBO 式**(次数 2 以下)に変換します.次数が 3 以上のすべての項は,新しい補助二値変数を導入して二次式に書き換えられます.
この変換は最適値を保存します.元の変数の任意の割当に対して,変換後の式を補助変数について最小化した値が,元の HUBO の値に一致します.したがって変換後 QUBO の最小解を元の変数に射影したものは,元の HUBO の最小解になります.否定リテラルを含む HUBO 式も自動的に処理され,変換後の QUBO は正リテラルのみになります.
- **`qbpp::reduce(f)`**:
`f`と等価な,次数 2 以下の新しい`qbpp::Expr`オブジェクトを生成して返します.
- **`f.reduce()`**:
`qbpp::reduce(f)`を使用して`f`をその場で更新し,更新された式を返します.
```{literalinclude} /../programFiles/cppPrograms/quickReference/qr-operation-program1.cpp
:language: cpp
:caption: qr-operation-program1.cpp
```
二次のモデルしか受け付けないバックエンド(例: 一部の物理アニーラ)でモデルを解く場合に有用です.
## タプルインデックス `a(...)`
`Array` の `operator()` で Python タプルインデックス風のサブ配列取得ができます.各引数は軸ごとに以下のいずれか:
| 引数 | 意味 | 次元変化 |
|---|---|---|
| 整数 `i` | その軸を `i` に固定 | 軸が削除される |
| `qbpp::all` | 全範囲 `:` | 軸を保持 |
| `qbpp::slice(from, to)` | 範囲 `[from, to)` | 軸を保持 |
| `qbpp::end` / `qbpp::end - n` | 軸サイズから計算される位置 | 固定または範囲端 |
指定しなかった末尾の軸は自動的に `qbpp::all` とみなされます.
出力は内部で 1 パスで構築するため,結果サイズに比例した **O(output_size)** のコピーコストになります.
### 使用例
```{include} /../programFiles/markDown/quickReference/qr-operation.md
:start-after:
:end-before:
```
詳細と例については **[スライス関数と連結関数](../advanced/slice-connection.md)** を参照してください.
## 連結関数: `concat()`
連結関数は配列の結合やスカラーの追加・先頭追加を行います.
### 配列 + 配列
- **`qbpp::concat(a, b)`**: 同じ型の2つの配列を最外次元に沿って連結します.
### スカラー + 配列 / 配列 + スカラー
- **`qbpp::concat(scalar, v)`**: 配列の先頭にスカラーを追加します.
- **`qbpp::concat(v, scalar)`**: 配列の末尾にスカラーを追加します.
スカラーは`qbpp::Expr`に暗黙的に変換されます.
### 2次元の次元指定付き連結
- **`qbpp::concat(a, b, dim)`**: 2つの2次元配列を指定した次元に沿って連結します.
- `dim=0`: 行方向の連結(行を追加)
- `dim=1`: 列方向の連結(列を追加.両方の行数が同じである必要があります)
### 例: 境界差分
```{include} /../programFiles/markDown/quickReference/qr-operation.md
:start-after:
:end-before:
```
## Term メンバ関数
以下の `qbpp::Term` のメンバ関数は,項の内部構造への読み取り専用アクセスを提供します.
| 式 | 戻り値の型 | 説明 |
|------------|-------------|-------------|
| `t.coeff()` | `coeff_t` | 係数を返す |
| `t.degree()` | `uint32_t` | 次数(変数の数)を返す |
| `t.var(i)` | `qbpp::Var` | `i` 番目の変数を返す |
| `t.has(v)` | `bool` | `Var` `v` が項に含まれていれば `true` を返す |
### 例
```{include} /../programFiles/markDown/quickReference/qr-operation.md
:start-after:
:end-before:
```
## Expr メンバ関数
以下の `qbpp::Expr` のメンバ関数は,式の内部構造への読み取り専用アクセスを提供します.
| 式 | 戻り値の型 | 説明 |
|------------|-------------|-------------|
| `f.constant()` | `energy_t` | 定数項を返す |
| `f.term_count()` | `size_t` | 項の数を返す(定数項を除く) |
| `f.term_count(d)` | `size_t` | 次数 `d` の項の数を返す |
| `f.term(i)` | `qbpp::Term` | `i` 番目の項のコピーを返す |
| `f.max_degree()` | `uint32_t` | すべての項の最大次数を返す |
| `f.has(v)` | `bool` | `Var` `v` が式に含まれていれば `true` を返す |
| `f.has_varint(vi)` | `bool` | 整数変数 `vi` のすべての変数が式に含まれていれば `true` を返す |
### 例
```{include} /../programFiles/markDown/quickReference/qr-operation.md
:start-after:
:end-before:
```
:::
:::{container} prog-python
以下の表は,式(`pyqbpp.Expr`)を対象とする演算子と関数をまとめたものです.
ここで「式」は [VAREXPR](../basic/variables-and-expressions.md) で説明した3つの概念(整数・変数・式)で構成される多項式を指します.
引数の型の `式` 列は,整数・変数・式のいずれでも受け付けられることを意味します.
## グローバル関数と In-place メソッドの規則
式に対する処理は,原則として次の2形式を対で提供します:
- **グローバル関数** `qbpp.func(f, ...)` — **非破壊**.`f` を変更せず,新しい結果オブジェクトを返します.
- **In-place メンバ** `f.func(...)` — `f` を**処理結果で上書き**し,`self` を返します(メソッドチェーンできます).
「f をそのまま使い続けたい」ときはグローバル,「f を処理後の値に置き換えたい」ときはメンバを使います.
戻り値の型がグローバル側と異なる関数でも,メンバ版は「結果を self に書き戻す」設計に統一されています.
例: `qbpp.gcd(f)` は整数を返しますが,`f.gcd()` は `f` 自身を「その gcd の値を持つ定数式」に上書きします.
## 演算子・関数一覧
| 演算子/関数 | 構文 | 種別 | 戻り値の型 | 引数の型 |
|------------------------|---------------------------------------------------|----------|-----------------|------------------------|
| コピー | `qbpp.copy(f)` | Global | 式 | 整数・変数・式 |
| 二項演算子 | `f + g`, `f - g`, `f * g` | Global | 式 | 式 ⊕ 式 |
| 複合代入 | `f += g`, `f -= g`, `f *= g` | In-place | 式 | 式 |
| 除算 | `f / n` | Global | 式 | 式, 整数 |
| 複合除算 | `f /= n` | In-place | 式 | 整数 |
| 単項演算子 | `+f`, `-f` | Global | 式 | 式 |
| 等価制約 | `qbpp.constrain(f, equal=n)` または `f == n` | Global | 制約式 | 式, 整数 |
| 範囲制約 | `qbpp.constrain(f, between=(l, u))` または `(l <= f) & (qbpp.same <= u)` | Global | 制約式 | 式, 整数, 整数 |
| 上限制約 | `qbpp.constrain(f, between=(None, u))` または `f <= u` | Global | 制約式 | 式, 整数 |
| 下限制約 | `qbpp.constrain(f, between=(l, None))` または `f >= l` | Global | 制約式 | 式, 整数 |
| 二乗 | `qbpp.sqr(f)` | Global | 式 | 式 |
| 二乗 | `f.sqr()` | In-place | 式 | — |
| 最大公約数 | `qbpp.gcd(f)` | Global | 整数 | 式 |
| 最大公約数 | `f.gcd()` | In-place | 式(定数で上書き) | — |
| 簡約化 | `qbpp.simplify(f)` | Global | 式 | 式 |
| 簡約化 | `f.simplify()` | In-place | 式 | — |
| バイナリ簡約化 | `qbpp.simplify_as_binary(f)` | Global | 式 | 式 |
| バイナリ簡約化 | `f.simplify_as_binary()` | In-place | 式 | — |
| スピン簡約化 | `qbpp.simplify_as_spin(f)` | Global | 式 | 式 |
| スピン簡約化 | `f.simplify_as_spin()` | In-place | 式 | — |
| 評価 | `f(ml)` | Global | 整数 | 式, dict |
| 置換 | `qbpp.replace(f, ml)` | Global | 式 | 式, dict |
| 置換 | `f.replace(ml)` | In-place | 式 | dict |
| スピン → バイナリ変換 | `qbpp.spin_to_binary(f)` | Global | 式 | 式 |
| スピン → バイナリ変換 | `f.spin_to_binary()` | In-place | 式 | — |
| バイナリ → スピン変換 | `qbpp.binary_to_spin(f)` | Global | 式 | 式 |
| バイナリ → スピン変換 | `f.binary_to_spin()` | In-place | 式 | — |
| HUBO→QUBO 変換 | `qbpp.reduce(f)` | Global | 式 | 式 |
| HUBO→QUBO 変換 | `f.reduce()` | In-place | 式 | — |
| スライス | `v[from:to]`, `v[:, from:to]` | Global | array | array |
| 連結 | `qbpp.concat([a, b, ...], axis=0)` | Global | array | array/スカラーのリスト |
## 代入: `g = f` と `g = qbpp.copy(f)`
Python の `=` は**名前の束縛**であって値のコピーではありません.
つまり `g = f` は「新しい名前 `g` を `f` が指すのと同じオブジェクトに結びつける」だけで,
`f` と `g` は**同一の式オブジェクト**を共有します.
一方,`g = qbpp.copy(f)` は `f` と独立した新しい式を作ります.
この違いは,その後で **in-place な操作(複合代入や in-place メンバ)** を行ったときに顕在化します.
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program1.py
:language: python
:caption: qr-operation-program1.py
```
> **注釈**
> 右辺が `f + 1`, `2 * f`, `-f` などの**二項・単項演算**の場合は,
> 演算子がそのたびに新しい式を返すので,結果は自動的に `f` と独立したオブジェクトになります.
> 注意が必要なのは「`g = f` の後に `g` を in-place で書き換える」パターンだけです.
> 独立したコピーが欲しいときは **`qbpp.copy(f)`** を使ってください.
> **注釈**
> 右辺が **in-place メンバ呼び出しだけ** の場合にも同じ罠があります.
> In-place メンバは「`f` 自身を処理結果で上書きして `self` を返す」ので,
> `g = f.sqr()` は「新しい式を `g` に代入」ではなく「`f` を2乗して書き換え,その `f` を `g` にも別名として付ける」結果になります.
> 典型的には次の3通りで結果が異なります:
>
> | 書き方 | 効果 |
> |---|---|
> | `g = f.sqr()` | `f` がその場で2乗され,`g` は `f` と同じオブジェクトを指す(`g is f` → True)|
> | `g = qbpp.sqr(f)` | グローバル形(非破壊).`f` は不変,`g` は独立した新しい式 |
> | `g = qbpp.copy(f.sqr())` | `f` は2乗された後,その独立コピーが `g` に入る(`f` 側の書き換えは残る)|
>
> 「`f` を保ったまま2乗結果を得たい」ときは **グローバル形 `qbpp.sqr(f)`** を使うのが最も自然です.
> 他の in-place メンバ(`simplify_as_binary`, `replace`, `spin_to_binary` など)にも同じルールが当てはまります.
> **注釈**
> `qbpp.copy()` は整数・変数に対しても安全に呼び出せます.
> これらは**イミュータブル**なので共有されても書き換えで影響を受けず,
> `copy()` は元のオブジェクトをそのまま返します.
> したがって型を気にせず `copy()` を使えます.
## 二項演算子: `+`, `-`, `*`
`+`, `-`, `*` は整数・変数・式を自由に組み合わせて受け付け,結果の式を返します.
オペランドの種類を意識せずに `2 * x * y - x + 1` のように自然に書けます.
## 複合代入演算子: `+=`, `-=`, `*=`
左辺は式でなければなりません.右辺には整数・変数・式のいずれも渡せ,
指定された演算が適用されて左辺の式がその場で更新されます.
## 除算 `/` と複合除算 `/=`
除算演算子 `/` は **被除数** として式を,**除数** として整数を取り,**商** を新しい式として返します.
被除数の式は除数で割り切れなければなりません.すなわち,
式の整数定数項とすべての整数係数が除数で割り切れる必要があります.
複合除算演算子 `/=` は式をその場で除算します.
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program2.py
:language: python
:caption: qr-operation-program2.py
```
## 等価・範囲制約: `constrain()`
`constrain()` 関数は,式 `f` に対する制約をペナルティ式として表現します.
等価制約と範囲制約の両方を統一的に記述できます.
```{include} /../programFiles/markDown/quickReference/qr-operation.md
:start-after:
:end-before:
```
ここで `f` は式,`n` / `l` / `u` は整数です.
いずれの形式も,制約が満たされたときに最小値 0 となる式を返します.
- **`equal=n`**: `sqr(f - n)` を返します.
- **`between=(l, u)`**: 範囲 `[l, u-1]` の値を取る単位間隔の補助整数変数 `a` が暗黙的に導入され,関数は `(f - a) * (f - (a + 1))` を返します.
- **`between=(l, None)`** / **`between=(None, u)`**: `l` / `u` の一方のみを制約する半開区間です.
### 制約式
`constrain()` が返すオブジェクト `g` は,penalty + body のメタデータを持つ `pyqbpp.Expr` です.
- **`g`** はペナルティ式そのものを表し,通常の式として評価・簡約化・ソルバー入力に使えます.
- **`g.body`** は制約を作る前の元の式 `f` を返します.
### 演算子による省略記法: `==`, `<=`, `>=`, `qbpp.same`
上記の4種類の制約は Python の比較演算子を用いてより簡潔に書けます.
省略記法と明示的な `constrain()` 呼び出しは**同じペナルティ式**を構築するため,どちらを使っても自由に混在させられます.
| 省略記法 | 同等の `constrain()` 呼び出し | 意味 |
|----------------------------------|----------------------------------------------|--------------------|
| `f == n` | `qbpp.constrain(f, equal=n)` | $f = n$ |
| `(l <= f) & (qbpp.same <= u)` | `qbpp.constrain(f, between=(l, u))` | $l \le f \le u$ |
| `f <= u` | `qbpp.constrain(f, between=(None, u))` | $f \le u$ |
| `f >= l` | `qbpp.constrain(f, between=(l, None))` | $l \le f$ |
`f` は式,`n` / `l` / `u` は整数です.いずれも対応する `constrain()` 呼び出しと同じ制約式(`.body` も同じ)を返します.
両側制約では **`qbpp.same`** をプレースホルダとして用い,「`&` の反対側と同じ式」を意味します.`2 * x + 3 * y` のような長い式を `(0 <= 2 * x + 3 * y) & (2 * x + 3 * y <= 12)` のように二度書く手間を省け,左右で異なる式を書いてしまうミスも防げます.(なお `(l <= f) & (f <= u)` のように body を二度書く形式は `TypeError` として明示的に拒否されます.たとえ両辺が見た目には同じでも,別々にインラインで書かれた式は別オブジェクトになるため,body の比較は意図的にオブジェクト同一性で行っています.)
省略記法は **配列に対しても要素ごとに動作**します.`arr == 1` や `(0 <= arr) & (qbpp.same <= 1)` は,`qbpp.constrain(arr, ...)` と同じく制約式の配列を返します.
> **注釈**
> Python では `&` の優先順位が `<=`/`>=` より**高い**ため,両側制約は必ず各比較を**括弧で囲んで**ください:
> `(l <= f) & (qbpp.same <= u)` であって `l <= f & qbpp.same <= u` ではありません.
> 省略記法を `+`, `*` などと組み合わせる際も同様で,例えば
> `100 * ((0 <= f) & (qbpp.same <= u))` のように括弧が必要です(`100 * (0 <= f) & (qbpp.same <= u)` は誤り).
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program3.py
:language: python
:caption: qr-operation-program3.py
```
## 二乗関数: `sqr()`
式 `f` に対して:
- **`pyqbpp.sqr(f)`** (グローバル関数): `f * f` を計算して返します.
引数 `f` は整数・変数・式のいずれでも構いません.
配列 `v` に対して:
- **`pyqbpp.sqr(v)`**: 各要素を二乗した新しい配列を返します.
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program4.py
:language: python
:caption: qr-operation-program4.py
```
## 最大公約数関数: `gcd()`
式 `f` のすべての整数係数と整数定数項の最大公約数(GCD)を計算します.
グローバル形と in-place 形の2種類があります.
- **`qbpp.gcd(f)`** (グローバル,非破壊):
`f` を変更せず,GCD を**整数値**として返します.
- **`f.gcd()`** (メンバ,in-place):
`f` 自身を「その GCD の値を持つ定数式」に**上書き**します(返り値は `self`).
式を GCD で約分したい場合は,グローバル形と複合除算を組み合わせて `f /= qbpp.gcd(f)` と書きます.
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program5.py
:language: python
:caption: qr-operation-program5.py
```
## 簡約化関数: `simplify()`, `simplify_as_binary()`, `simplify_as_spin()`
式 `f` に対して,メンバ関数 **`f.simplify()`** は以下の操作をその場で行います.
- 各項内の変数を一意な変数IDに従ってソート
- 重複する項をマージ
- 項を以下のようにソート:
- 低次の項が先に配置される
- 同次の項は辞書順で並べられる
グローバル関数 **`pyqbpp.simplify(f)`** は `f` を変更せずに同じ操作を行います.
### バイナリとスピンの簡約化
簡約化関数の2つの特殊なバリアントが提供されています.
- **`simplify_as_binary()`**:
すべての変数がバイナリ値 $\lbrace 0,1\rbrace$ を取ることを前提として簡約化を行います.
すべての変数 $x$ に対して恒等式 $x^2=x$ が適用されます.
- **`simplify_as_spin()`**:
すべての変数がスピン値 $\lbrace -1,+1\rbrace$ を取ることを前提として簡約化を行います.
すべての変数 $x$ に対して恒等式 $x^2=1$ が適用されます.
両方のバリアントはメンバー関数とグローバル関数として利用可能です.
- メンバー関数(その場で更新): `f.simplify_as_binary()`, `f.simplify_as_spin()`
- グローバル関数(非破壊的): `qbpp.simplify_as_binary(f)`, `qbpp.simplify_as_spin(f)`
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program6.py
:language: python
:caption: qr-operation-program6.py
```
## 評価関数: `f(ml)`
評価関数は `{変数: 値}` の辞書を受け取ります.各エントリは変数から整数値へのマッピングを定義します.
式 `f` と辞書 `ml` に対して,評価関数 `f(ml)` は `ml` で指定された変数の割り当ての下で `f` の値を評価し,結果の整数値を返します.
`f` に出現するすべての変数は,`ml` に対応するマッピングが定義されていなければなりません.
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program7.py
:language: python
:caption: qr-operation-program7.py
```
## 置換関数: `replace()`
`replace()` 関数は `{変数: 式}` の辞書を受け取ります.値側には整数も指定できます.
式 `f` と辞書 `ml` に対して:
- **`pyqbpp.replace(f, ml)`** (グローバル関数):
`f` を変更せずに,`ml` のマッピングに従って `f` の変数を置換した新しい式を返します.
- **`f.replace(ml)`** (メンバ関数):
`ml` のマッピングに従って `f` の変数をその場で置換し,結果の式を返します.
### 辞書の作成
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program8.py
:language: python
:caption: qr-operation-program8.py
```
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program9.py
:language: python
:caption: qr-operation-program9.py
```
## バイナリ/スピン変換関数: `spin_to_binary()`, `binary_to_spin()`
`x` をバイナリ変数,`s` をスピン変数とします.
`x = 1` と `s = 1` が同値であると仮定します.
この仮定の下で,以下の関係が成り立ちます.
$$
\begin{aligned}
s &= 2x-1 \\
x &= (s+1)/2
\end{aligned}
$$
**`spin_to_binary()`** 関数は,すべてのスピン変数 `s` を `2 * s - 1` で置換することにより,スピン変数の式をバイナリ変数の式に変換します.
**`binary_to_spin()`** 関数は,すべてのバイナリ変数 `x` を `(x + 1) / 2` で置換することにより,バイナリ変数の式をスピン変数の式に変換します.
すべての係数が整数のままになるように,結果の式は $2^d$($d$ は最大次数)で乗算されます.
両方の関数はメンバー関数(その場で更新)とグローバル関数(非破壊的)として利用可能です.
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program10.py
:language: python
:caption: qr-operation-program10.py
```
## 非線形関数: `abs()`, `relu()`, `max()`, `min()`
式の中で絶対値・ReLU・最大値・最小値を直接使えます.これらを含む式は,
Hi-QUBO に同梱されているソルバーが関数値を直接扱って探索します
(補助変数やペナルティ多項式を自分で設計する必要はありません).
詳しくは[非線形関数](../advanced/non-linear.md)を参照してください.
- **`qbpp.abs(f)`** / **`qbpp.abs(f, k)`**:
$|f|$ / $|f|^k$ を表す `Expr` を返します(`k` は 1 か 2).
- **`qbpp.relu(f)`** / **`qbpp.relu(f, k)`**:
$\max(0, f)$ / $\max(0, f)^k$ を表す `Expr` を返します(`k` は 1 か 2).
- **`qbpp.max(f, g)`** / **`qbpp.min(f, g)`**:
2 つの式の最大値・最小値を表す `Expr` を返します.
これらの関数を含む式には次の制限があります:
- 係数は任意の定数で加減・スケールできます(負の係数は関数値を最大化する
向きに働きます).
- `expand_cons()`・`reduce()`・`binarize()` には対応していません.
- 同梱ソルバーはすべての関数に対応します.MIP ソルバーでは
[GurobiSolver](../solver/gurobi-solver-usage.md) が `abs`・`relu`(`k` = 1, 2)と `max`・`min` の
凸方向(`w*max` の最小化・min の最大化)に対応します(係数は正のみ).
他の MIP ラッパーは非線形関数に対応していません.
> **注**: `qbpp.abs` / `qbpp.max` / `qbpp.min` は Python 組み込みの
> `abs` / `max` / `min` とは別の関数です.`from pyqbpp import *` を
> 使う場合は名前の衝突に注意してください.
## HUBO→QUBO 変換関数: `reduce()`
**`reduce()`** 関数は,HUBO 式(3 次以上の項を含みうる多項式)を**等価な QUBO 式**(次数 2 以下)に変換します.次数が 3 以上のすべての項について,新しい補助二値変数を導入して二次式に書き換えます.
この変換は最適値を保存します.元の変数の任意の割当に対して,変換後の式を補助変数について最小化した値が,元の HUBO の値に一致します.したがって変換後 QUBO の最小解を元の変数に射影したものは,元の HUBO の最小解になります.否定リテラルを含む式も自動的に処理され,変換後の QUBO は正リテラルのみになります.
`reduce()` はグローバル関数(非破壊)とメンバ関数(in-place)の両方が提供されます.
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program11.py
:language: python
:caption: qr-operation-program11.py
```
二次のモデルしか受け付けないバックエンド(例: 一部の物理アニーラ)でモデルを解く場合に有用です.
## スライス関数: `v[from:to]`
Pythonのスライス記法で array から部分範囲を抽出します.スライスは新しい array を返します.
- **`v[from:to]`**: 最外次元の `[from, to)` の要素.
- **`v[:n]`**: 先頭 `n` 個.
- **`v[-n:]`**: 末尾 `n` 個.
多次元配列にはタプルインデックスで内側の次元をスライス:
- **`v[:, from:to]`**: 各行をスライス(dim=1).
- **`v[:, :, from:to]`**: dim=2 でスライス.任意の深さで動作.
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program12.py
:language: python
:caption: qr-operation-program12.py
```
## 連結関数: `concat()`
`concat()` 関数は配列とスカラーのリストを指定した軸で連結します.
- **`qbpp.concat([a, b, c, ...], axis=0)`**: 軸 `axis` に沿ってリスト内の各要素を連結.
- リスト要素は array とスカラー(整数・変数・式)の混在が可能.スカラーは他の配列の形状に合わせて軸方向にブロードキャストされます.
- 要素型の異なる array 同士を連結する場合は自動的に式の array に昇格されます.
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program13.py
:language: python
:caption: qr-operation-program13.py
```
## 式のメンバ
以下のメンバは式 `f` の内部構造に読み取り専用でアクセスします.
| 式 | 戻り値の型 | 説明 |
|------------|-------------|-------------|
| `f.constant` | 整数 | 定数項を返す(プロパティ) |
| `f.max_degree` | 整数 | すべての項の最大次数を返す(プロパティ) |
| `f.term_count()` | 整数 | 項の数を返す(定数項を除く) |
| `f.term_count(d)` | 整数 | 次数 `d` の項の数を返す |
| `f.term(i)` | 単項の式 | `i` 番目の項を1項だけからなる式として返す |
| `f.has(v)` | `bool` | 変数 `v` が式 `f` に含まれていれば `True` を返す |
`f.term(i)` が返す「単項の式」 `t` には,さらに以下のアクセッサがあります:
| 式 | 戻り値の型 | 説明 |
|------------|-------------|-------------|
| `t.coeff` | 整数 | 係数を返す(プロパティ) |
| `t.degree` | 整数 | その項の次数(変数の数)を返す(プロパティ) |
| `t.var(i)` | `Var` | その項の `i` 番目の変数を返す |
| `t.has(v)` | `bool` | 変数 `v` がその項に含まれていれば `True` を返す |
### 例
```{literalinclude} /../programFiles/pythonPrograms/quickReference/qr-operation-program14.py
:language: python
:caption: qr-operation-program14.py
```
:::