注釈

このページは docs/tutorials/02_converters_for_quadratic_programs.ipynb から生成されました。

二次計画法のコンバーター#

Qiskitの最適化モジュールの最適化問題は、 QuadraticProgram クラスで表されます。これは、最適化問題の一般的で強力な表現です。一般に、最適化アルゴリズムは二次計画法の特定の定式化に対して定義されており、問題を適切なタイプに変換する必要があります。

たとえば、Qiskit optimizationは、 Quadratic Unconstrained Binary Optimization (QUBO)問題を処理できるいくつかの最適化アルゴリズムを提供します。これらは、Qiskit optimizationが qiskit.quantum_info.SparsePauliOp オブジェクトを使用するイジングハミルトニアンにマッピングされ、基底状態が近似されます。この最適化では、VQE や QAOA などの一般的に知られているアルゴリズムを基礎となるルーチンとして使用できます。詳細については、 Minimum Eigen Optimizer に関するチュートリアルを参照してください。 GroverOptimizer など、動作が異なる他のアルゴリズムも存在することに注意してください。

問題を正しい入力形式にマッピングするために、Qiskit optimizationの最適化モジュールはさまざまなコンバーターを提供します。当チュートリアルでは、この機能の概要を説明します。現在、Qiskit optimizationには次のコンバーターが含まれています。

  • InequalityToEquality :不等式制約を追加のスラック変数を使用した等式制約に変換します。

  • IntegerToBinary :整数変数をバイナリ変数と対応する係数に変換します。

  • LinearEqualityToPenalty :等式制約をオブジェクト関数の追加の項に変換します。

  • LinearEqualityToPenalty :等式制約をオブジェクト関数の追加の項に変換します。

  • MaximizeToMinimize: 等価最小化問題に変換します。

  • MinimizeToMaximize: 等価最大化問題に変換します。

  • QuadraticProgramToQubo : InequalityToEqualityIntegerToBinaryLinearEqualityToPenaltyLinearInequalityToPenaltyMaximizeToMinimize などが含まれている。

InequalityToEquality#

InequalityToEqualityConverter は、不等式制約を追加のスラック変数を使用して等式制約に変換し、 QuadraticProgram から不等式制約を削除します。スラック変数の上限と下限は、制約の左側と右側の差から計算されます。スラック変数の符号は、 \(\leq\)\(\geq\) などの制約内の記号に依存します。

以下は、2つの不等式制約がある最大化問題の例です。 変数 \(x\)\(y\) はバイナリ変数であり、変数 \(z\) は整数変数です。

QuadraticProgram では、問題の最適化モデルを次のように記述します。

[1]:
from qiskit_optimization import QuadraticProgram
from qiskit_optimization.translators.docplex_mp import to_docplex_mp
[2]:
qp = QuadraticProgram()
qp.binary_var("x")
qp.binary_var("y")
qp.integer_var(lowerbound=0, upperbound=7, name="z")

qp.maximize(linear={"x": 3, "y": 2, "z": 1})
qp.linear_constraint(linear={"x": 1, "y": 1, "z": 1}, sense="LE", rhs=5.5, name="xyz_leq")
qp.linear_constraint(linear={"x": 1, "y": 1, "z": 1}, sense="GE", rhs=2.5, name="xyz_geq")
print(qp.prettyprint())
Problem name:

Maximize
  3*x + 2*y + z

Subject to
  Linear constraints (2)
    x + y + z <= 5.5  'xyz_leq'
    x + y + z >= 2.5  'xyz_geq'

  Integer variables (1)
    0 <= z <= 7

  Binary variables (2)
    x y

変換するには IntegerToBinaryconvert メソッドを呼び出します。

[3]:
from qiskit_optimization.converters import InequalityToEquality
[4]:
ineq2eq = InequalityToEquality()
qp_eq = ineq2eq.convert(qp)
print(qp_eq.prettyprint())
Problem name:

Maximize
  3*x + 2*y + z

Subject to
  Linear constraints (2)
    x + xyz_leq@int_slack + y + z == 5  'xyz_leq'
    x - xyz_geq@int_slack + y + z == 3  'xyz_geq'

  Integer variables (3)
    0 <= z <= 7
    0 <= xyz_leq@int_slack <= 5
    0 <= xyz_geq@int_slack <= 6

  Binary variables (2)
    x y

変換後、問題の定式化は上記の出力のようになります。ご覧のとおり、不等式制約は、整数のスラック変数 \(xyz\_leg\text{@}int\_slack\)\(xyz\_geq\text{@}int\_slack\) が追加された等式制約に置き換えられています。

変換がどのように機能するかを説明しましょう。 たとえば、最初の制約の左側の下限は \(0\) です。これは、 \(x=0\)\(y=0\) 、および \(z=0\) の場合です。 したがって、追加の整数変数の上限は、 \(x=0\)\(y=0\) 、および \(z=0\) の場合でも満たすことができるように \(5\) でなければなりません。 元の定式化の最初の制約の左側は整数値のみである可能性があるため、変換された定式化では小数点以下の部分を切り取ることに注意してください。 2番目の制約については、基本的に同じアプローチを適用します。 ただし、2番目の制約の記号は \(\geq\) であるため、\(x=1, y=1\)\(z=7\) の場合でも満たすことができるように、 \(xyz\_geq\text{@}int\_slack\) の前にマイナスを追加します。

では、 interpret 法がどのように機能するか見てみましょう。この方法の目的は、変換された問題の解を元の問題の解に戻すことです。この方法を使うには、まず問題を解く必要があります。このチュートリアルではdocplexを使って解きます。まず、2次問題をdocplex.mpモデルに変換します。

[5]:
from qiskit_optimization.algorithms import CplexOptimizer

cplex_optimizer = CplexOptimizer()
[6]:
result_orig = cplex_optimizer.solve(qp)
print(result_orig)
fval=8.0, x=1.0, y=1.0, z=3.0, status=SUCCESS
[7]:
result_eq = cplex_optimizer.solve(qp_eq)
print(result_eq)
fval=8.0, x=1.0, y=1.0, z=3.0, xyz_leq@int_slack=0.0, xyz_geq@int_slack=2.0, status=SUCCESS

qp_eq の結果 result_eq には 5 つの変数値 (x=1.0, y=1.0, z=3.0, xyz_leq@int_slack=0.0, xyz_geq@int_slack=2.0) があり、元の qp の結果 result_orig には 3 つの値 (x=1.0, y=1.0, z=3.0) があります。 次のように、qp_eq.variables の値を持つメソッドにリストまたは配列を渡すことで、InequalityToEquality.interpret メソッドを呼び出すことができます。 result_eq.x には、変数リスト qp_eq.variables 内の位置に対応して、各変数が解の中で取る値のリストがあります。

[8]:
print("interpreting values of result_eq:", ineq2eq.interpret(result_eq.x))
print("values of result_orig:", result_orig.x)
interpreting values of result_eq: [1. 1. 3.]
values of result_orig: [1. 1. 3.]

我々は、 \([1., 1., 3.]\) が、変換された問題と元の問題の間の共通変数(変数:\(x\), \(y\), \(z\) )について、変換された問題の解で取られた値であることに気づきます。解釈法は元の問題の解と同じ値を示します。これは、変換後の問題と元の問題の目的関数が全く同じだからです。スラック変数は、変換後の問題の制約における等式を保証しているだけであり、制約も、元の問題が不等式制約を持っていることを除けば、元の問題と変換後の問題の間で全く同じです。

IntegerToBinary#

IntegerToBinary は、整数変数をバイナリー変数と係数に変換して、 QuadraticProgram から整数変数を削除します。 変換には、 arxiv:1706.01945 (式(5))で提案されている有界係数エンコーディングが使用されます。エンコード方法の詳細については、論文を参照してください。

InequalityToEquality の出力を開始点として使用します。 変数 \(x\)\(y\) はバイナリー変数であり、変数 \(z\) とスラック変数 \(xyz\_leq\text{@}int\_slack\)\(xyz\_geq\text{@}int\_slack\) は整数変数です。 参照用に問題を再度表示します。

[9]:
print(qp_eq.prettyprint())
Problem name:

Maximize
  3*x + 2*y + z

Subject to
  Linear constraints (2)
    x + xyz_leq@int_slack + y + z == 5  'xyz_leq'
    x - xyz_geq@int_slack + y + z == 3  'xyz_geq'

  Integer variables (3)
    0 <= z <= 7
    0 <= xyz_leq@int_slack <= 5
    0 <= xyz_geq@int_slack <= 6

  Binary variables (2)
    x y

変換するには IntegerToBinaryconvert メソッドを呼び出します。

[10]:
from qiskit_optimization.converters import IntegerToBinary
[11]:
int2bin = IntegerToBinary()
qp_eq_bin = int2bin.convert(qp_eq)
print(qp_eq_bin.prettyprint())
Problem name:

Maximize
  3*x + 2*y + z@0 + 2*z@1 + 4*z@2

Subject to
  Linear constraints (2)
    x + xyz_leq@int_slack@0 + 2*xyz_leq@int_slack@1 + 2*xyz_leq@int_slack@2 + y
    + z@0 + 2*z@1 + 4*z@2 == 5  'xyz_leq'
    x - xyz_geq@int_slack@0 - 2*xyz_geq@int_slack@1 - 3*xyz_geq@int_slack@2 + y
    + z@0 + 2*z@1 + 4*z@2 == 3  'xyz_geq'

  Binary variables (11)
    x y z@0 z@1 z@2 xyz_leq@int_slack@0 xyz_leq@int_slack@1 xyz_leq@int_slack@2
    xyz_geq@int_slack@0 xyz_geq@int_slack@1 xyz_geq@int_slack@2

変換後、整数変数 \(z\) は、上記のように、それぞれ係数1、2、および4の3つのバイナリー変数 \(z\text{@}0\)\(z\text{@}1\) 、および \(z\text{@}2\) に置き換えられます。 InequalityToEquality によって導入されたスラック変数 \(xyz\_leq\text{@}int\_slack\)\(xyz\_geq\text{@}int\_slack\) も、それぞれ係数1、2、2、および1、2、3の3つのバイナリー変数に置き換えられます。

注記:ここでの係数は、取り得る値 \(\{0, \ldots, 7\}\)\(\{0, \ldots, 5\}\)\(\{0, \ldots, 6\}\) を表現するために、係数をもつ各バイナリー変数の和は、元の整数変数の下界と上界に従うようサブセット \(\{1, 2, 4\}\)\(\{1, 2, 2\}\)\(\{1, 2, 3\}\) の和をとっています。

IntegerToBinary は、与えられたバイナリ結果を元の整数表現に変換する機能である interpret メソッドも提供します。この interpret メソッドがどのように動作するか見てみましょう。

[12]:
result_eq = cplex_optimizer.solve(qp_eq)
print(result_eq)
fval=8.0, x=1.0, y=1.0, z=3.0, xyz_leq@int_slack=0.0, xyz_geq@int_slack=2.0, status=SUCCESS
[13]:
result_eq_bin = cplex_optimizer.solve(qp_eq_bin)
print(result_eq_bin)
fval=8.0, x=1.0, y=1.0, z@0=1.0, z@1=1.0, z@2=0.0, xyz_leq@int_slack@0=0.0, xyz_leq@int_slack@1=0.0, xyz_leq@int_slack@2=0.0, xyz_geq@int_slack@0=0.0, xyz_geq@int_slack@1=1.0, xyz_geq@int_slack@2=0.0, status=SUCCESS

result_eq_binIntegerToBinary.convert による変換により、より多くのバイナリ変数を持っています。 IntegerToBinary.interpret は、 qp_eq の元の整数変数に関連付けられたバイナリ変数の値を集約することで、それらを整数値に戻します。

[14]:
print("interpreting values of result_eq_bin:", int2bin.interpret(result_eq_bin.x))
print("values of result_eq:", result_eq.x)
interpreting values of result_eq_bin: [1. 1. 3. 0. 2.]
values of result_eq: [1. 1. 3. 0. 2.]

LinearEqualityToPenalty#

LinearEqualityToPenalty は、 QuadraticProgram を制約なしの形式にマップするために、線形等式制約を目的関数の追加の2次ペナルティ項に変換します。コンバータへの入力は,線形等価制約のみを持つ QuadraticProgram でなければなりません。これらの等価性制約、例えば \(a_i\)\(b\) は数値、 \(x_i\) は変数であり、 \(M\) はペナルティファクターとしては大きな数である時、 \(x_i\)\(M(b - \sum_i a_i x_i)^2\) の形式で目的関数に追加されます。デフォルトでは \(M= 1e5\) です。 項の符号は、問題の種類が最大化か最小化かに依存します。

IntegerToBinary の出力を開始点として使用します。ここで、すべての変数はバイナリー変数であり、すべての不等式制約は等式制約にマップされています。 参照用に問題を再度表示します。

[15]:
print(qp_eq_bin.prettyprint())
Problem name:

Maximize
  3*x + 2*y + z@0 + 2*z@1 + 4*z@2

Subject to
  Linear constraints (2)
    x + xyz_leq@int_slack@0 + 2*xyz_leq@int_slack@1 + 2*xyz_leq@int_slack@2 + y
    + z@0 + 2*z@1 + 4*z@2 == 5  'xyz_leq'
    x - xyz_geq@int_slack@0 - 2*xyz_geq@int_slack@1 - 3*xyz_geq@int_slack@2 + y
    + z@0 + 2*z@1 + 4*z@2 == 3  'xyz_geq'

  Binary variables (11)
    x y z@0 z@1 z@2 xyz_leq@int_slack@0 xyz_leq@int_slack@1 xyz_leq@int_slack@2
    xyz_geq@int_slack@0 xyz_geq@int_slack@1 xyz_geq@int_slack@2

LinearEqualityToPenaltyconvert メソッドを呼び出して変換します。

[16]:
from qiskit_optimization.converters import LinearEqualityToPenalty
[17]:
lineq2penalty = LinearEqualityToPenalty()
qubo = lineq2penalty.convert(qp_eq_bin)
print(qubo.prettyprint())
Problem name:

Maximize
  -26*x^2 + 26*x*xyz_geq@int_slack@0 + 52*x*xyz_geq@int_slack@1
  + 78*x*xyz_geq@int_slack@2 - 26*x*xyz_leq@int_slack@0
  - 52*x*xyz_leq@int_slack@1 - 52*x*xyz_leq@int_slack@2 - 52*x*y - 52*x*z@0
  - 104*x*z@1 - 208*x*z@2 - 13*xyz_geq@int_slack@0^2
  - 52*xyz_geq@int_slack@0*xyz_geq@int_slack@1
  - 78*xyz_geq@int_slack@0*xyz_geq@int_slack@2 - 52*xyz_geq@int_slack@1^2
  - 156*xyz_geq@int_slack@1*xyz_geq@int_slack@2 - 117*xyz_geq@int_slack@2^2
  - 13*xyz_leq@int_slack@0^2 - 52*xyz_leq@int_slack@0*xyz_leq@int_slack@1
  - 52*xyz_leq@int_slack@0*xyz_leq@int_slack@2 - 52*xyz_leq@int_slack@1^2
  - 104*xyz_leq@int_slack@1*xyz_leq@int_slack@2 - 52*xyz_leq@int_slack@2^2
  + 26*y*xyz_geq@int_slack@0 + 52*y*xyz_geq@int_slack@1
  + 78*y*xyz_geq@int_slack@2 - 26*y*xyz_leq@int_slack@0
  - 52*y*xyz_leq@int_slack@1 - 52*y*xyz_leq@int_slack@2 - 26*y^2 - 52*y*z@0
  - 104*y*z@1 - 208*y*z@2 + 26*z@0*xyz_geq@int_slack@0
  + 52*z@0*xyz_geq@int_slack@1 + 78*z@0*xyz_geq@int_slack@2
  - 26*z@0*xyz_leq@int_slack@0 - 52*z@0*xyz_leq@int_slack@1
  - 52*z@0*xyz_leq@int_slack@2 - 26*z@0^2 - 104*z@0*z@1 - 208*z@0*z@2
  + 52*z@1*xyz_geq@int_slack@0 + 104*z@1*xyz_geq@int_slack@1
  + 156*z@1*xyz_geq@int_slack@2 - 52*z@1*xyz_leq@int_slack@0
  - 104*z@1*xyz_leq@int_slack@1 - 104*z@1*xyz_leq@int_slack@2 - 104*z@1^2
  - 416*z@1*z@2 + 104*z@2*xyz_geq@int_slack@0 + 208*z@2*xyz_geq@int_slack@1
  + 312*z@2*xyz_geq@int_slack@2 - 104*z@2*xyz_leq@int_slack@0
  - 208*z@2*xyz_leq@int_slack@1 - 208*z@2*xyz_leq@int_slack@2 - 416*z@2^2
  + 211*x - 78*xyz_geq@int_slack@0 - 156*xyz_geq@int_slack@1
  - 234*xyz_geq@int_slack@2 + 130*xyz_leq@int_slack@0 + 260*xyz_leq@int_slack@1
  + 260*xyz_leq@int_slack@2 + 210*y + 209*z@0 + 418*z@1 + 836*z@2 - 442

Subject to
  No constraints

  Binary variables (11)
    x y z@0 z@1 z@2 xyz_leq@int_slack@0 xyz_leq@int_slack@1 xyz_leq@int_slack@2
    xyz_geq@int_slack@0 xyz_geq@int_slack@1 xyz_geq@int_slack@2

変換後、等式制約はQiskit optimizationに提供されたデフォルトのペナルティ係数を持つ追加項として目的関数に追加されます。 変換後の問題はQUBOとなり、VQE、QAOAなどの多くの量子最適化アルゴリズムと互換性があります。

これは以前と同じ結果を与えます。

他のコンバーターでやったように、interpret メソッドがこのケースでどのように機能するか見てみましょう。

[18]:
result_eq_bin = cplex_optimizer.solve(qp_eq_bin)
print(result_eq_bin)
fval=8.0, x=1.0, y=1.0, z@0=1.0, z@1=1.0, z@2=0.0, xyz_leq@int_slack@0=0.0, xyz_leq@int_slack@1=0.0, xyz_leq@int_slack@2=0.0, xyz_geq@int_slack@0=0.0, xyz_geq@int_slack@1=1.0, xyz_geq@int_slack@2=0.0, status=SUCCESS
[19]:
result_qubo = cplex_optimizer.solve(qubo)
print(result_qubo)
fval=8.0, x=1.0, y=1.0, z@0=1.0, z@1=1.0, z@2=0.0, xyz_leq@int_slack@0=0.0, xyz_leq@int_slack@1=0.0, xyz_leq@int_slack@2=0.0, xyz_geq@int_slack@0=0.0, xyz_geq@int_slack@1=1.0, xyz_geq@int_slack@2=0.0, status=SUCCESS
[20]:
print("interpreting values of result_eq_bin:", lineq2penalty.interpret(result_qubo.x))
print("values of result_eq_bin:", result_eq_bin.x)
interpreting values of result_eq_bin: [1. 1. 1. 1. 0. 0. 0. 0. 0. 1. 0.]
values of result_eq_bin: [1. 1. 1. 1. 0. 0. 0. 0. 0. 1. 0.]

interpret メソッドの結果は、元の問題と変換された問題の両方が全く同じ解を持つことを意味していることがわかります。これは、変換された問題には元の問題と全く同じ変数があり、変換された問題には制約がないように目的が変更されているためです。

最後に、QUBO の結果をどのように解釈して元の問題 qp の解に戻すかを見てみましょう。以下のコードは、解釈された値が元の問題 qp の結果と等価であることを示しています。

[21]:
print("result_orig.x", result_orig.x)

x = result_qubo.x
for conv in [lineq2penalty, int2bin, ineq2eq]:
    x = conv.interpret(x)
print("interpreting result_qubo.x", x)
result_orig.x [1. 1. 3.]
interpreting result_qubo.x [1. 1. 3.]
[22]:
import qiskit.tools.jupyter

%qiskit_version_table
%qiskit_copyright

Version Information

SoftwareVersion
qiskit0.44.1
qiskit-terra0.45.0.dev0+dcec79e
qiskit_optimization0.6.0
qiskit_algorithms0.3.0
System information
Python version3.10.13
Python compilerClang 14.0.3 (clang-1403.0.22.14.1)
Python buildmain, Aug 24 2023 22:36:46
OSDarwin
CPUs10
Memory (Gb)64.0
Thu Sep 07 12:33:31 2023 JST

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

[ ]: