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