注釈

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

ポートフォリオの最適化#

はじめに#

このチュートリアルでは、 \(n\) 個の資産の次の平均分散ポートフォリオ最適化問題を解決する方法を示します。

\[\begin{split}\begin{aligned} \min_{x \in \{0, 1\}^n} q x^T \Sigma x - \mu^T x\\ \text{subject to: } 1^T x = B \end{aligned}\end{split}\]

ここで、次の表記を使用します。

  • \(x \in \{0, 1\}^n\) は、バイナリー決定変数のベクトルを示し、ピックする資産 (\(x[i] = 1\)) と、ピックしない資産 (\(x[i] = 0\)) を示します。

  • \(\mu \in \mathbb{R}^n\) は、資産のリターンの期待値を定義します。

  • \(\Sigma \in \mathbb{R}^{n \times n}\) は、資産間の共分散を指定します

  • \(q > 0\) は、意思決定者のリスク選好度を制御します。

  • \(B\) は予算、つまり \(n\) の中から選択するアセットの数を表します。

ここで、次の簡略化を想定します。すべての資産は同じ価格 ( 1に正規化 ) を持ち、全予算 \(B\) を使用する必要があります。すなわち、厳密に \(B\) アセットを選択します。

等価制約 \(1^T x = B\) は、パラメーターによってスケールされ、目的関数から減算される、ペナルティー項 \((1^T x - B)^2\) にマップされます。得られた問題は、基底状態が最適解に対応するハミルトニアンにマッピングすることができます。 このノートブックでは、与えられたパラメーターセットによる最適解を見つけるために、Qiskit Algorithms の Sampling Variational Quantum Eigensolver (SamplingVQE) または Quantum Approximate Optimization Algorithm (QAOA) を使用する方法について説明します。

この問題に対する実際の量子ハードウェアに関する実験については、例えば以下の論文で報告しています。Improving Variational Quantum Optimization using CVaR. Barkoutsos et al. 2019.

[1]:
from qiskit.circuit.library import TwoLocal
from qiskit.result import QuasiDistribution
from qiskit_aer.primitives import Sampler
from qiskit_algorithms import NumPyMinimumEigensolver, QAOA, SamplingVQE
from qiskit_algorithms.optimizers import COBYLA
from qiskit_finance.applications.optimization import PortfolioOptimization
from qiskit_finance.data_providers import RandomDataProvider
from qiskit_optimization.algorithms import MinimumEigenOptimizer
import numpy as np
import matplotlib.pyplot as plt
import datetime

問題のインスタンスを定義#

ここで、Operatorインスタンスがこのハミルトニアン用に作成されます。 ここでのパウリ項は、ポートフォリオ問題から変換されたイジング・ハミルトニアンからのものです。このノートブックではランダムなポートフォリオ問題を使用します。Loading and Processing Stock-Market Time-Series Data で示されるように、実際の金融データに容易に適用することができます。

[2]:
# set number of assets (= number of qubits)
num_assets = 4
seed = 123

# Generate expected return and covariance matrix from (random) time-series
stocks = [("TICKER%s" % i) for i in range(num_assets)]
data = RandomDataProvider(
    tickers=stocks,
    start=datetime.datetime(2016, 1, 1),
    end=datetime.datetime(2016, 1, 30),
    seed=seed,
)
data.run()
mu = data.get_period_return_mean_vector()
sigma = data.get_period_return_covariance_matrix()
[3]:
# plot sigma
plt.imshow(sigma, interpolation="nearest")
plt.show()
../_images/tutorials_01_portfolio_optimization_5_0.png
[4]:
q = 0.5  # set risk factor
budget = num_assets // 2  # set budget
penalty = num_assets  # set parameter to scale the budget penalty term

portfolio = PortfolioOptimization(
    expected_returns=mu, covariances=sigma, risk_factor=q, budget=budget
)
qp = portfolio.to_quadratic_program()
qp
[4]:
<QuadraticProgram: minimize 0.001270694296030004*x_0^2 + 7.340221669347328e-05..., 4 variables, 1 constraints, 'Portfolio optimization'>

整ったフォーマットで出力するためのユーティリティー・メソッドをいくつか定義します。

[5]:
def print_result(result):
    selection = result.x
    value = result.fval
    print("Optimal: selection {}, value {:.4f}".format(selection, value))

    eigenstate = result.min_eigen_solver_result.eigenstate
    probabilities = (
        eigenstate.binary_probabilities()
        if isinstance(eigenstate, QuasiDistribution)
        else {k: np.abs(v) ** 2 for k, v in eigenstate.to_dict().items()}
    )
    print("\n----------------- Full result ---------------------")
    print("selection\tvalue\t\tprobability")
    print("---------------------------------------------------")
    probabilities = sorted(probabilities.items(), key=lambda x: x[1], reverse=True)

    for k, v in probabilities:
        x = np.array([int(i) for i in list(reversed(k))])
        value = portfolio.to_quadratic_program().objective.evaluate(x)
        print("%10s\t%.4f\t\t%.4f" % (x, value, v))

NumPyMinimumEigensolver (古典の参照として)#

まず最初に古典的な手法で問題を解いてみます。

これで、上記で構築したOperatorを、どのように作成されたか、細かいことは気にせず使用できるようになりました。 NumPyMinimumEigensolver のアルゴリズムを設定することで、古典的な参照を行うことができます。 問題は 『ising』 に設定されています。量子計算を使わず古典的に計算しているので、バックエンドは不要です。 結果は辞書として返されます。

[6]:
exact_mes = NumPyMinimumEigensolver()
exact_eigensolver = MinimumEigenOptimizer(exact_mes)

result = exact_eigensolver.solve(qp)

print_result(result)
Optimal: selection [1. 0. 0. 1.], value -0.0149

----------------- Full result ---------------------
selection       value           probability
---------------------------------------------------
 [1 0 0 1]      -0.0149         1.0000

SamplingVQE を使用したソリューション#

これで、 Sampling Variational Quantum Eigensolver (SamplingVQE) を使用して問題を解くことができます。 使用する最適化プログラムおよび変分フォームを指定します。

[7]:
from qiskit_algorithms.utils import algorithm_globals

algorithm_globals.random_seed = 1234

cobyla = COBYLA()
cobyla.set_options(maxiter=500)
ry = TwoLocal(num_assets, "ry", "cz", reps=3, entanglement="full")
svqe_mes = SamplingVQE(sampler=Sampler(), ansatz=ry, optimizer=cobyla)
svqe = MinimumEigenOptimizer(svqe_mes)
result = svqe.solve(qp)

print_result(result)
Optimal: selection [1. 0. 0. 1.], value -0.0149

----------------- Full result ---------------------
selection       value           probability
---------------------------------------------------
 [0 1 1 0]      0.0008          0.8525
 [1 0 0 1]      -0.0149         0.0410
 [0 0 1 1]      -0.0010         0.0312
 [0 0 0 1]      -0.0008         0.0215
 [1 0 1 1]      -0.0150         0.0195
 [1 0 0 0]      -0.0140         0.0088
 [0 1 1 1]      -0.0000         0.0078
 [0 1 0 1]      0.0002          0.0078
 [0 1 0 0]      0.0009          0.0059
 [1 0 1 0]      -0.0140         0.0020
 [1 1 0 1]      -0.0139         0.0010
 [0 0 0 0]      0.0000          0.0010

QAOA を使用したソリューション#

また、Quantum Approximate Optimization Algorithm (QAOA) を使用した結果も示します。 これは別の変分アルゴリズムであり、問題に応じて作成される内部の変分フォームを使用します。

[8]:
algorithm_globals.random_seed = 1234

cobyla = COBYLA()
cobyla.set_options(maxiter=250)
qaoa_mes = QAOA(sampler=Sampler(), optimizer=cobyla, reps=3)
qaoa = MinimumEigenOptimizer(qaoa_mes)
result = qaoa.solve(qp)

print_result(result)
Optimal: selection [1. 0. 0. 1.], value -0.0149

----------------- Full result ---------------------
selection       value           probability
---------------------------------------------------
 [1 0 0 1]      -0.0149         0.1797
 [1 0 1 0]      -0.0140         0.1729
 [1 1 0 0]      -0.0130         0.1641
 [0 0 1 1]      -0.0010         0.1592
 [0 1 1 0]      0.0008          0.1553
 [0 1 0 1]      0.0002          0.1445
 [0 1 0 0]      0.0009          0.0049
 [1 1 0 1]      -0.0139         0.0039
 [1 1 1 1]      -0.0139         0.0039
 [0 0 0 0]      0.0000          0.0029
 [1 0 0 0]      -0.0140         0.0029
 [0 0 1 0]      -0.0001         0.0020
 [0 1 1 1]      -0.0000         0.0010
 [1 1 1 0]      -0.0130         0.0010
 [0 0 0 1]      -0.0008         0.0010
 [1 0 1 1]      -0.0150         0.0010
[9]:
import qiskit.tools.jupyter

%qiskit_version_table
%qiskit_copyright

Version Information

SoftwareVersion
qiskit0.45.0.dev0+ea871e0
qiskit_optimization0.6.0
qiskit_finance0.4.0
qiskit_aer0.12.2
qiskit_ibm_provider0.7.0
qiskit_algorithms0.3.0
System information
Python version3.9.7
Python compilerGCC 7.5.0
Python builddefault, Sep 16 2021 13:09:58
OSLinux
CPUs2
Memory (Gb)5.7784271240234375
Tue Sep 05 15:03:52 2023 EDT

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.

[ ]: