This project is archived
Qiskit Finance is no longer maintained and this documentation is no longer updated. It is kept online for reference only, and the code it describes may not work with current versions of Qiskit. See the project repository for more information.
Source code for qiskit_finance.applications.optimization.portfolio_diversification
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2018, 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.
"""An application class for a portfolio diversification problem."""
from typing import List, Union
import numpy as np
from docplex.mp.advmodel import AdvModel
from qiskit_optimization.algorithms import OptimizationResult
from qiskit_optimization.applications import OptimizationApplication
from qiskit_optimization.problems import QuadraticProgram
from qiskit_optimization.translators import from_docplex_mp
[docs]class PortfolioDiversification(OptimizationApplication):
"""Optimization application for the "portfolio diversification" problem introduced in [1].
References:
[1]: GG. Cornuejols and R. Tutuncu, Optimization methods in finance, 2006
"""
def __init__(self, similarity_matrix: np.ndarray, num_assets: int, num_clusters: int) -> None:
"""
Args:
similarity_matrix: An asset-to-asset similarity matrix, such as the covariance matrix.
num_assets: The number of assets.
num_clusters: The number of clusters of assets to output.
"""
self._similarity_matrix = similarity_matrix
self._num_assets = num_assets
self._num_clusters = num_clusters
[docs] def to_quadratic_program(self) -> QuadraticProgram:
"""Convert a portfolio diversification problem instance into a
:class:`~qiskit_optimization.QuadraticProgram`.
Returns:
The :class:`~qiskit_optimization.QuadraticProgram` created
from the portfolio diversification problem instance.
"""
mdl = AdvModel(name="Portfolio diversification")
x = {
(i, j): mdl.binary_var(name=f"x_{i}_{j}")
for i in range(self._num_assets)
for j in range(self._num_assets)
}
y = {i: mdl.binary_var(name=f"y_{i}") for i in range(self._num_assets)}
mdl.maximize(
mdl.sum(
self._similarity_matrix[i, j] * x[(i, j)]
for i in range(self._num_assets)
for j in range(self._num_assets)
)
)
mdl.add_constraint(mdl.sum(y[j] for j in range(self._num_assets)) == self._num_clusters)
for i in range(self._num_assets):
mdl.add_constraint(mdl.sum(x[(i, j)] for j in range(self._num_assets)) == 1)
for j in range(self._num_assets):
mdl.add_constraint(x[(j, j)] == y[j])
for i in range(self._num_assets):
for j in range(self._num_assets):
mdl.add_constraint(x[(i, j)] <= y[j])
op = from_docplex_mp(mdl)
return op
[docs] def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]:
"""Interpret a result as a list of asset indices
Args:
result: The calculated result of the problem
Returns:
The list of asset indices whose corresponding variable is 1
"""
x = self._result_to_x(result)
return [i for i, x_i in enumerate(x[-self._num_assets :]) if x_i]
@property
def similarity_matrix(self) -> np.ndarray:
"""Getter of similarity_matrix
Returns:
An asset-to-asset similarity matrix, such as the covariance matrix.
"""
return self._similarity_matrix
@similarity_matrix.setter
def similarity_matrix(self, similarity_matrix: np.ndarray):
"""Setter of similarity_matrix
Args:
similarity_matrix: An asset-to-asset similarity matrix, such as the covariance matrix.
"""
self._similarity_matrix = similarity_matrix
@property
def num_assets(self) -> int:
"""Getter of num_assets
Returns:
The number of assets.
"""
return self._num_assets
@num_assets.setter
def num_assets(self, num_assets: int):
"""Setter of num_assets
Args:
num_assets: The number of assets.
"""
self._num_assets = num_assets
@property
def num_clusters(self) -> int:
"""Getter of num_clusters
Returns:
The number of clusters of assets to output
"""
return self._num_clusters
@num_clusters.setter
def num_clusters(self, num_clusters: int) -> None:
"""Setter of num_clusters
Args:
num_clusters: The number of clusters of assets to output
"""
self._num_clusters = num_clusters