Source code for qiskit.finance.components.uncertainty_problems.fixed_income_expected_value

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2020.
#
# 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.

"""
The Fixed Income Expected Value.
"""

from typing import Optional, Union, List
import numpy as np
from qiskit.aqua.components.uncertainty_models import UncertaintyModel
from qiskit.aqua.components.uncertainty_problems import UncertaintyProblem

# pylint: disable=invalid-name


[docs]class FixedIncomeExpectedValue(UncertaintyProblem): """ The Fixed Income Expected Value. Evaluates a fixed income asset with uncertain interest rates. """ def __init__(self, uncertainty_model: UncertaintyModel, A: np.ndarray, b: int, cash_flow: List[float], c_approx: float, i_state: Optional[Union[List[int], np.ndarray]] = None, i_objective: Optional[int] = None) -> None: """ Constructor. Args: uncertainty_model: multivariate distribution A: PCA matrix for delta_r (changes in interest rates) b: offset for interest rates (= initial interest rates) cash_flow: cash flow time series c_approx: approximation scaling factor i_state: indices of qubits that represent the state i_objective: index of target qubit to apply the rotation to """ if not isinstance(A, np.ndarray): A = np.asarray(A) if i_state is None: i_state = list(range(uncertainty_model.num_target_qubits)) if i_objective is None: i_objective = uncertainty_model.num_target_qubits # TODO: remove dictionary and use direct attributes self._params = { 'i_state': i_state, 'i_objective': i_objective } # get number of time steps self.T = len(cash_flow) # get dimension of uncertain model self.K = uncertainty_model.dimension # get total number of target qubits num_target_qubits = 1 + uncertainty_model.num_target_qubits # initialize parent class super().__init__(num_target_qubits) self.uncertainty_model = uncertainty_model self.cash_flow = cash_flow self.c_approx = c_approx self.A = A self.b = b # construct PCA-based cost function (1st order approximation): # c_t / (1 + A_t x + b_t)^{t+1} ~ c_t / (1 + b_t)^{t+1} - (t+1) c_t A_t / # (1 + b_t)^{t+2} x = h + np.dot(g, x) self.h = 0 self.g = np.zeros(self.K) for t in range(self.T): self.h += cash_flow[t] / pow(1 + b[t], (t + 1)) self.g += -1.0 * (t + 1) * cash_flow[t] * A[t, :] / pow(1 + b[t], (t + 2)) # compute overall offset using lower bound for x (corresponding to x = min) self.offset = np.dot(uncertainty_model.low, self.g) + self.h # compute overall slope self.slope = np.zeros(uncertainty_model.num_target_qubits) index = 0 for k in range(self.K): nk = uncertainty_model.num_qubits[k] for i in range(nk): self.slope[index] = \ pow(2.0, i) / (pow(2.0, nk) - 1) * \ (uncertainty_model.high[k] - uncertainty_model.low[k]) * self.g[k] index += 1 # evaluate min and max values # for scaling to [0, 1] is then given by (V - min) / (max - min) self.min_value = self.offset + sum(self.slope) self.max_value = self.offset # reset offset / slope accordingly self.offset -= self.min_value self.offset /= (self.max_value - self.min_value) self.slope /= (self.max_value - self.min_value) # apply approximation scaling self.offset_angle = (self.offset - 1 / 2) * np.pi / 2 * self.c_approx + np.pi / 4 self.slope_angle = self.slope * np.pi / 2 * self.c_approx
[docs] def value_to_estimation(self, value): estimator = value - 1 / 2 estimator *= 2 / np.pi / self.c_approx estimator += 1 / 2 estimator *= (self.max_value - self.min_value) estimator += self.min_value return estimator
[docs] def required_ancillas(self): return 0
[docs] def required_ancillas_controlled(self): return self.uncertainty_model.required_ancillas_controlled()
[docs] def build(self, qc, q, q_ancillas=None, params=None): params = self._params # get qubits q_objective = q[params['i_objective']] # apply uncertainty model self.uncertainty_model.build(qc, q, q_ancillas) # apply approximate payoff function qc.ry(2 * self.offset_angle, q_objective) for i in params['i_state']: qc.cry(2 * self.slope_angle[i], q[i], q_objective)