# 比較演算子 :::{container} prog-cpp Hi-QUBOは,制約を作成するための2種類の演算子をサポートしています: - **等式演算子**: $f=n$,ここで $f$ は式,$n$ は整数. - **範囲演算子**: $l\leq f\leq u$,ここで $f$ は式,$l$ と $u$ ($l\leq u$) は整数. これらの演算子は,**対応する制約が満たされる場合に限り最小値0をとる**式を返します. ## 等式演算子 等式演算子 $f=n$ は以下の式を生成します: $$ (f−n)^2 $$ この式は,等式 $f=n$ が満たされる場合に限り最小値0をとります. 以下のHi-QUBOプログラムは,Exhaustive Solverを使用して $a+2b+3c=3$ を満たす全ての解を探索します: ```{literalinclude} /../programFiles/cppPrograms/advanced/comparison-operators-program1.cpp :language: cpp :caption: comparison-operators-program1.cpp ``` このプログラムでは,`f` は内部的に2つの qbpp::Expr オブジェクトを保持しています: - **`f`**: $(a+2b+3c−3)^2$,等式 $a+2b+3c=3$ が満たされる場合に最小値0をとります. - **`f.body()`**: 等式の左辺,$a+2b+3c$. `f` に対して作成されたExhaustive Solverオブジェクトを使用し,全ての最適解が **`sols`** に格納されます. `sols` を反復処理することで,全ての解と `f` および `f.body()` の値が以下のように出力されます: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` これらの結果から,2つの最適解が `f = 0` を達成し,`f.body() = 3` を満たしていることが確認できます. ## サポートされる等式の形式に関する注意 Hi-QUBOは等式演算子を以下の形式でのみサポートしています: - **`expression == integer`** 以下の形式はサポートされていません: - **`integer == expression`** - **`expression1 == expression2`** `expression1 == expression2` の代わりに,以下のように書き換えることができます: - **`expression1 - expression2 == 0`** これは完全にサポートされています. ## 範囲演算子 Hi-QUBO は $l\leq f \leq u$ ($l\leq u$) の形式の**両側範囲演算子**をサポートします: - **範囲演算子**: $l \leq f \leq u$ — `l <= expr <= u` と書きます この演算子は,制約が満たされる場合に限り最小値0をとる式を生成します. > **注意** > `l <= expr <= u` のチェイン構文は両端の境界が整数リテラルである必要があります. > 片側の境界を無限にしたい場合は,後述の**片側演算子** (`expr >= l` または `expr <= u`) > を使ってください.`l <= expr <= +qbpp::inf` や `-qbpp::inf <= expr <= u` の代わりに > 推奨される書き方です. $l$ と $u$ の値に応じて,以下の場合分けを考えます. - **場合1**: **$u=l$** - **場合2**: **$u=l+1$** - **場合3**: **$u=l+2$** - **場合4**: **$u\geq l+3$** ### 場合1: $u=l$ $u=l$ の場合,範囲制約は等式制約 $f=l$ に帰着し,等式演算子を直接使用して実装できます. ### 場合2: $u=l+1$ $u=l+1$ の場合,以下の式が生成されます: $$ (f-l)(f-u) $$ $l$ と $u$ の間に整数が存在しないため,この式は $f=l$ または $f=u$ の場合に限り最小値0をとります. ### 場合3: $u=l+2$ **補助バイナリ変数** $a \in \lbrace 0,1\rbrace$ を導入し,以下の式を使用します: $$ \begin{aligned} (f-l-a)(f-l-(a+1)) \end{aligned} $$ この式は $f=l$, $l+1$, $l+2$ に対して以下のように評価されます: $$ \begin{aligned} (f-l-a)(f-l-(a+1)) &= (-a)(-(a+1)) && \text{if } f=l \\ &= (1-a)(-a) && \text{if } f=l+1 \\ &=(2-a)(1-a) && \text{if } f=l+2 \end{aligned} $$ 全ての場合において,$a$ の適切な選択により最小値0が達成可能です. したがって,$l\leq f\leq u$ が満たされる場合,この式は最小値0をとります. $g = f-l-a$ とおくと, $$ \begin{aligned} (f-l-a)(f-l-(a+1)) &= g(g-1) \end{aligned} $$ となり,$g\leq -1$ または $g\geq 2$ の場合は常に正の値をとります. したがって,この式は $l\leq f\leq u$ が満たされる場合に限り最小値0をとります. ### 場合4: $u\geq l+3$ 範囲 $[l,u−1]$ の整数値をとる補助整数変数 $a$ を導入します. このような整数変数は,[整数変数と連立方程式の求解](../basic/integer-variables-and-linear-systems.md)で説明されているように,複数のバイナリ変数を用いて定義できます. この場合の式は: $$ \begin{aligned} (f-a)(f-(a+1)) \end{aligned} $$ 場合3と同様に,$f$ が $[l,u]$ に含まれない場合,この式は常に正の値をとることが示せます. $f$ が範囲 $[l,u]$ の整数値をとると仮定します. $a=f$ を選ぶと, $$ \begin{aligned} f-a &= 0 & {\rm if\,\,} f\in [l,u-1]\\ f-(a+1) &= 0& {\rm if\,\,} f\in [l+1,u] \end{aligned} $$ したがって,任意の $f\in[l,u]$ に対して $f−a=0$ または $f−(a+1)=0$ のいずれかが成り立ちます. よって,$(f−a)(f−(a+1))$ は $l\leq f\leq u$ の場合に限り最小値0をとります. ### バイナリ変数数の削減 [整数変数と連立方程式の求解](../basic/integer-variables-and-linear-systems.md)では,整数変数 $a\in [l,u]$ は $n$ 個のバイナリ変数 $x_0, x_1, \ldots, x_{n-1}$ を用いて以下のように表現されます: $$ \begin{aligned} a & = l+2^0x_0+2^1x_1+\cdots +2^{n-2}x_{n-2}+dx_{n-1} \end{aligned} $$ この式は $l$ から $l+2^{n-1}+d-1$ までの全ての整数を表現できます. したがって,以下を満たすように $n$ と $d$ を選ぶことができます: $$ \begin{aligned} u-1&=l+2^{n-1}+d-1. \end{aligned} $$ 場合4では,Hi-QUBOは代わりに $n-1$ 個のバイナリ変数 $x_1, \ldots, x_{n-1}$ を用いた以下の線形式を使用します: $$ \begin{aligned} a &= l+2^1x_1+\cdots +2^{n-2}x_{n-2}+dx_{n-1} \end{aligned} $$ この式は $l$ から $l+2^{n-1}+d-2$ までの整数を表現します. それに応じて,以下を満たすように $n$ と $d$ を選びます: $$ \begin{aligned} u-1&=l+2^{n-1}+d-2. \end{aligned} $$ このような整数変数 $a$ を**ユニットギャップ整数変数**と呼びます. $[l,u]$ 内の一部の値は $a$ で表現できませんが,表現できない任意の $k\in[l,u]$ に対して $k−1$ は表現可能です. したがって,$a$ または $a+1$ は範囲 $[l,u]$ の任意の値をとることができ,範囲制約の適用には十分です. ### 4つの場合のHi-QUBOプログラム 以下のプログラムは,Hi-QUBOにおける4つの場合の実装を示しています: ```{literalinclude} /../programFiles/cppPrograms/advanced/comparison-operators-program2.cpp :language: cpp :caption: comparison-operators-program2.cpp ``` このプログラムは以下の出力を生成します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` これらの出力は以下の式に対応します: $$ \begin{aligned} f_1 &= (f-1)^2\\ f_2 &= (f-1)(f-2)\\ f_3 &= (f-x_0)(f-(x_0+1))\\ f_4 &= (f-(2x_{1,0}+x_{1,1}+1))(f-(2x_{1,0}+x_{1,1}+2)) \end{aligned} $$ ### 範囲演算子を使用するHi-QUBOプログラム 以下のプログラムは,Hi-QUBOにおける範囲演算子の使用方法を示しています: ```{literalinclude} /../programFiles/cppPrograms/advanced/comparison-operators-program3.cpp :language: cpp :caption: comparison-operators-program3.cpp ``` 3つのバイナリ変数 $a$, $b$, $c$ に対して,このプログラムは以下の制約を満たす解を探索します: $$ \begin{aligned} 5\leq 4a+9b+15c \leq 14 \end{aligned} $$ このプログラムは以下の出力を生成します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` ## 下界・上界演算子 Hi-QUBOは以下の**片側境界演算子**をサポートしています: - **下界演算子**: $l\leq f$ — `expr >= n` と書きます (`n <= expr <= +qbpp::inf` の代わりに使えます) - **上界演算子**: $f\leq u$ — `expr <= n` と書きます (`-qbpp::inf <= expr <= n` の代わりに使えます) > **注意** > 整数を左辺に置いた **`n >= expr`** はコンパイルエラーになります. > **`n <= expr`** はチェイン形 `l <= expr <= u` 専用の中間値を返すため,単独の片側制約としては使えません. > 片側制約を書くときは常に式を `<=` / `>=` の左側に置いてください. 範囲演算子は内部的に補助変数を導入するため,真の無限大は明示的に表現できません. チェイン形 (後方互換用に残されています) で `qbpp::inf` を用いると,Hi-QUBOは式 $f$ の**有限の最大値と最小値**を推定し,それぞれ $+\infty$ と $-\infty$ の代わりに使用します. 例えば,以下の式を考えます: $$ \begin{aligned} f=4a + 9 b + 11 c \end{aligned} $$ ここで $a$, $b$, $c$ はバイナリ変数です. $f$ の取りうる最小値と最大値はそれぞれ0と24です. したがって,Hi-QUBOは対応する範囲制約を構築する際に,$-\infty$ と $+\infty$ の代わりに0と24を使用します. ### 下界・上界演算子のHi-QUBOプログラム 以下のプログラムは**下界演算子**を示しています: ```{literalinclude} /../programFiles/cppPrograms/advanced/comparison-operators-program4.cpp :language: cpp :caption: comparison-operators-program4.cpp ``` このプログラムでは,`f` は単独下界演算子 `>=` で構築されています.上界は内部で `expr.pos_sum() = 24` (本体が取りうる最大値) に設定されるので,`14 <= 4 * a + 9 * b + 11 * c <= +qbpp::inf` と書いた場合と等価です. このプログラムは以下の出力を生成します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` 以下のプログラムは**上界演算子**を示しています: ```{literalinclude} /../programFiles/cppPrograms/advanced/comparison-operators-program5.cpp :language: cpp :caption: comparison-operators-program5.cpp ``` このプログラムでは,`f` は単独上界演算子 `<=` で構築されています.下界は内部で `expr.neg_sum() = 0` (本体が取りうる最小値) に設定されるので,`-qbpp::inf <= 4 * a + 9 * b + 11 * c <= 14` と書いた場合と等価です. このプログラムは以下の出力を生成します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` ::: :::{container} prog-python PyQBPPは,2種類の制約をサポートしています: - **等式制約**: `qbpp.constrain(f, equal=n)`,ここで `f` は式,`n` は整数. - **範囲制約**: `qbpp.constrain(f, between=(l, u))`,ここで `f` は式,`l` と `u` ($l\leq u$) は整数. いずれも,**対応する制約が満たされる場合に限り最小値0をとる**制約式を返します. ## 等式制約 等式制約 `qbpp.constrain(f, equal=n)` は以下の式を生成します: $$ (f-n)^2 $$ この式は,等式 $f=n$ が満たされる場合に限り最小値0をとります. 以下のPyQBPPプログラムは,Exhaustive Solverを使用して $a+2b+3c=3$ を満たす全ての解を探索します: ```{literalinclude} /../programFiles/pythonPrograms/advanced/comparison-operators-program1.py :language: python :caption: comparison-operators-program1.py ``` このプログラムでは,`f` は内部的に2つの式を保持しています: - **`f`**: $(a+2b+3c-3)^2$,等式 $a+2b+3c=3$ が満たされる場合に最小値0をとります. - **`f.body`**: 等式の左辺,$a+2b+3c$. `f` に対して作成されたExhaustive Solverを使用し,全ての最適解が **`result.sols`** に格納されます. `result.sols` を反復処理することで,全ての解と `f` および `f.body` の値が以下のように出力されます: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` これらの結果から,2つの最適解が `f = 0` を達成し,`body = 3` を満たしていることが確認できます. ## サポートされる等式の形式に関する注意 PyQBPPは等式制約を以下の2つの等価な形式でサポートしています: - **`qbpp.constrain(expression, equal=integer)`** - **`expression == integer`**(省略形,[演算子による省略構文](#演算子による省略構文)を参照) `expression1 == expression2`(両辺が式)の形式は直接サポートされて**いません**. 代わりに,以下のように書き換えてください: - **`qbpp.constrain(expression1 - expression2, equal=0)`** — または等価に **`(expression1 - expression2) == 0`** これらは完全にサポートされています. ## 範囲制約 範囲制約 `qbpp.constrain(f, between=(l, u))` ($l\leq u$) は,制約が満たされる場合に限り最小値0をとる式を生成します. $l$ と $u$ の値に応じて,以下の場合分けを考えます. - **場合1**: **$u=l$** - **場合2**: **$u=l+1$** - **場合3**: **$u=l+2$** - **場合4**: **$u\geq l+3$** ### 場合1: $u=l$ $u=l$ の場合,範囲制約は等式制約 $f=l$ に帰着し,等式制約を直接使用して実装できます. ### 場合2: $u=l+1$ $u=l+1$ の場合,以下の式が生成されます: $$ (f-l)(f-u) $$ $l$ と $u$ の間に整数が存在しないため,この式は $f=l$ または $f=u$ の場合に限り最小値0をとります. ### 場合3: $u=l+2$ **補助バイナリ変数** $a \in \lbrace 0,1\rbrace$ を導入し,以下の式を使用します: $$ \begin{aligned} (f-l-a)(f-l-(a+1)) \end{aligned} $$ この式は $f=l$, $l+1$, $l+2$ に対して以下のように評価されます: $$ \begin{aligned} (f-l-a)(f-l-(a+1)) &= (-a)(-(a+1)) && \text{if } f=l \\ &= (1-a)(-a) && \text{if } f=l+1 \\ &=(2-a)(1-a) && \text{if } f=l+2 \end{aligned} $$ 全ての場合において,$a$ の適切な選択により最小値0が達成可能です. したがって,$l\leq f\leq u$ が満たされる場合,この式は最小値0をとります. $g = f-l-a$ とおくと, $$ \begin{aligned} (f-l-a)(f-l-(a+1)) &= g(g-1) \end{aligned} $$ となり,$g\leq -1$ または $g\geq 2$ の場合は常に正の値をとります. したがって,この式は $l\leq f\leq u$ が満たされる場合に限り最小値0をとります. ### 場合4: $u\geq l+3$ 範囲 $[l,u-1]$ の整数値をとる補助整数変数 $a$ を導入します. このような整数変数は,[整数変数と連立方程式の求解](../basic/integer-variables-and-linear-systems.md)で説明されているように,複数のバイナリ変数を用いて定義できます. この場合の式は: $$ \begin{aligned} (f-a)(f-(a+1)) \end{aligned} $$ 場合3と同様に,$f$ が $[l,u]$ に含まれない場合,この式は常に正の値をとることが示せます. $f$ が範囲 $[l,u]$ の整数値をとると仮定します. $a=f$ を選ぶと, $$ \begin{aligned} f-a &= 0 & {\rm if\,\,} f\in [l,u-1]\\ f-(a+1) &= 0& {\rm if\,\,} f\in [l+1,u] \end{aligned} $$ したがって,任意の $f\in[l,u]$ に対して $f-a=0$ または $f-(a+1)=0$ のいずれかが成り立ちます. よって,$(f-a)(f-(a+1))$ は $l\leq f\leq u$ の場合に限り最小値0をとります. ### バイナリ変数数の削減 [整数変数と連立方程式の求解](../basic/integer-variables-and-linear-systems.md)では,整数変数 $a\in [l,u]$ は $n$ 個のバイナリ変数 $x_0, x_1, \ldots, x_{n-1}$ を用いて以下のように表現されます: $$ \begin{aligned} a & = l+2^0x_0+2^1x_1+\cdots +2^{n-2}x_{n-2}+dx_{n-1} \end{aligned} $$ この式は $l$ から $l+2^{n-1}+d-1$ までの全ての整数を表現できます. したがって,以下を満たすように $n$ と $d$ を選ぶことができます: $$ \begin{aligned} u-1&=l+2^{n-1}+d-1. \end{aligned} $$ 場合4では,PyQBPPは代わりに $n-1$ 個のバイナリ変数 $x_1, \ldots, x_{n-1}$ を用いた以下の線形式を使用します: $$ \begin{aligned} a &= l+2^1x_1+\cdots +2^{n-2}x_{n-2}+dx_{n-1} \end{aligned} $$ この式は $l$ から $l+2^{n-1}+d-2$ までの整数を表現します. それに応じて,以下を満たすように $n$ と $d$ を選びます: $$ \begin{aligned} u-1&=l+2^{n-1}+d-2. \end{aligned} $$ このような整数変数 $a$ を**ユニットギャップ整数変数**と呼びます. $[l,u]$ 内の一部の値は $a$ で表現できませんが,表現できない任意の $k\in[l,u]$ に対して $k-1$ は表現可能です. したがって,$a$ または $a+1$ は範囲 $[l,u]$ の任意の値をとることができ,範囲制約の適用には十分です. ### 4つの場合のPyQBPPプログラム 以下のプログラムは,PyQBPPにおける4つの場合の実装を示しています: ```{literalinclude} /../programFiles/pythonPrograms/advanced/comparison-operators-program2.py :language: python :caption: comparison-operators-program2.py ``` このプログラムは以下の出力を生成します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` これらの出力は以下の式に対応します: $$ \begin{aligned} f_1 &= (f-1)^2\\ f_2 &= (f-1)(f-2)\\ f_3 &= (f-x_0)(f-(x_0+1))\\ f_4 &= (f-(2x_{1,0}+x_{1,1}+1))(f-(2x_{1,0}+x_{1,1}+2)) \end{aligned} $$ ### 範囲制約を使用するPyQBPPプログラム 以下のプログラムは,PyQBPPにおける範囲制約の使用方法を示しています: ```{literalinclude} /../programFiles/pythonPrograms/advanced/comparison-operators-program3.py :language: python :caption: comparison-operators-program3.py ``` 3つのバイナリ変数 $a$, $b$, $c$ に対して,このプログラムは以下の制約を満たす解を探索します: $$ \begin{aligned} 5\leq 4a+9b+15c \leq 14 \end{aligned} $$ このプログラムは以下のような出力を生成します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` ## 下界・上界制約 PyQBPPは以下の**片側境界制約**を独立した構文としては直接サポートしていません. 代わりに,PyQBPPは `between` の境界値を `None` とすることでこれらをサポートします: - **下界制約**: `between=(l, None)` → $l\leq f\leq +\infty$ - **上界制約**: `between=(None, u)` → $-\infty \leq f\leq u$ 範囲制約は内部的に補助変数を導入するため,真の無限大は明示的に表現できません. そのため,PyQBPPは式 $f$ の**有限の最大値と最小値**を推定し,それぞれ $+\infty$ と $-\infty$ の代わりに使用します. 例えば,以下の式を考えます: $$ \begin{aligned} f=4a + 9 b + 11 c \end{aligned} $$ ここで $a$, $b$, $c$ はバイナリ変数です. $f$ の取りうる最小値と最大値はそれぞれ0と24です. したがって,PyQBPPは対応する範囲制約を構築する際に,$-\infty$ と $+\infty$ の代わりに0と24を使用します. > **注釈** > `qbpp.constrain(f, between=(l, None))` や `qbpp.constrain(f, between=(None, u))` を使う場合, > PyQBPPは**QUBOスタイルの解釈**を採用します.すなわち,`None` 側は $f$ の係数から導かれる > 構造的な境界(全変数がバイナリのときに $f$ が取りうる最大値・最小値)で置き換えられます. > これは **MIPスタイルの解釈**(例: $f\leq u$ を $0\leq f\leq u$ と解釈する流儀)とは異なります. > QUBOスタイルを採用しているのは,ユーザーが書いた以上の暗黙の制約を勝手に追加しないためです. ### 下界・上界制約のPyQBPPプログラム PyQBPPでは,無限大の値は `between` の対応する側の `None` で表現されます. 以下のプログラムは**下界制約**を示しています: ```{literalinclude} /../programFiles/pythonPrograms/advanced/comparison-operators-program4.py :language: python :caption: comparison-operators-program4.py ``` このプログラムでは,`between=(14, None)` の `None` は正の無限大を表し,自動的に24に置き換えられます. このプログラムは以下のような出力を生成します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` 以下のプログラムは**上界制約**を示しています: ```{literalinclude} /../programFiles/pythonPrograms/advanced/comparison-operators-program5.py :language: python :caption: comparison-operators-program5.py ``` このプログラムでは,`between=(None, 14)` の `None` は負の無限大を表し,自動的に0に置き換えられます. このプログラムは以下のような出力を生成します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` ## 演算子による省略構文 利便性のため,PyQBPPは式と整数の間の比較演算子 `==`, `<=`, `>=` も受け付けます. これらは `qbpp.constrain()` の単なる糖衣構文であり,同等の制約式を生成します: | 演算子による形式 | 等価な `qbpp.constrain()` 形式 | 生成される penalty | | ---------------- | -------------------------------------- | ---------------------------------- | | `f == n` | `qbpp.constrain(f, equal=n)` | $(f-n)^2$ | | `f <= n` | `qbpp.constrain(f, between=(None, n))` | $\min(f) \leq f \leq n$ のとき $0$ | | `f >= n` | `qbpp.constrain(f, between=(n, None))` | $n \leq f \leq \max(f)$ のとき $0$ | ここで $\min(f)$ と $\max(f)$ は $f$ の係数から導かれる構造的な境界であり,上記の [下界・上界制約](#下界上界制約)で説明したルールに従います. 反転形式 `n <= f`, `n >= f` も Python の標準的な反射規則により動作します. 例えば,上の上界制約のプログラムは次のように書き換えられます: ```{literalinclude} /../programFiles/pythonPrograms/advanced/comparison-operators-program6.py :language: python :caption: comparison-operators-program6.py ``` これらの演算子は式の配列に対しても定義されており,その場合は要素ごとに制約が適用され, 制約式の配列が返されます. > **注釈** > 右辺には `int` のみを受け付けます.`expression1 <= expression2` の形式はサポートされておらず, > `(expression1 - expression2) <= 0` に書き換えるか,`qbpp.constrain()` で `between=` 範囲を > 明示的に指定してください.`x == 3` のように裸の `Var`/`Term` と整数の比較も制約式になります. > ただし `var1 == var2` のような**変数同士**の比較は同一性判定(辞書のキーとして使うために > bool を返すもの)のままで,制約にはなりません. ## `qbpp.same` による範囲制約の連鎖記法 `qbpp.constrain(f, between=(l, u))` を書くもう一つの方法として,上界制約と下界制約を **`&`** で連結する記法が用意されています. ここで,**もう一方の側にある式 `f`** をプレースホルダ **`qbpp.same`** で参照します: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` `<=` / `>=` の向きや左右はどの組合せでも構いません.`&` の左右どちらに `qbpp.same` を置いても解釈されます. `qbpp.same` を介在させる理由は,`f` が大きな式や配列の場合でも,両側の制約に対して**補助変数を 1 組だけ**生成するためです. `qbpp.constrain(f, between=(l, u))` と完全に同じ QUBO 式が得られます(補助変数の割り当ても同一です — 片側制約は `&` で合流するまで補助変数を確保しない遅延評価のため). ### 連鎖記法のPyQBPPプログラム 以下のプログラムは,[範囲制約を使用するPyQBPPプログラム](#範囲制約を使用するpyqbppプログラム) と同じ制約 $5 \leq 4a + 9b + 15c \leq 14$ を連鎖記法で書いたものです: ```{literalinclude} /../programFiles/pythonPrograms/advanced/comparison-operators-program7.py :language: python :caption: comparison-operators-program7.py ``` `qbpp.constrain(4*a + 9*b + 15*c, between=(5, 14))` を使った版と同一の出力が得られます. ### 配列に対する連鎖記法 `qbpp.same` は式の配列に対しても使えます.配列の各要素に同じ範囲制約が適用され,制約式の配列が返されます: ```{include} /../programFiles/markDown/advanced/comparison-operators.md :start-after: :end-before: ``` ### サポートされない形式 両側に同じ式を書いた `(l <= f) & (f <= u)` の形式はサポートされていません.連鎖記法では片側を `qbpp.same` にしてください. :::