Example #1
0
    def test_application(self):
        """Test an end-to-end application."""
        bounds = np.array([0., 7.])
        num_qubits = 3

        # the distribution circuit is a normal distribution plus a QGAN-trained ansatz circuit
        dist = NormalDistribution(num_qubits, mu=1, sigma=1, bounds=bounds)

        ansatz = TwoLocal(num_qubits, 'ry', 'cz', reps=1, entanglement='circular')
        trained_params = [0.29399714, 0.38853322, 0.9557694, 0.07245791, 6.02626428, 0.13537225]
        ansatz.assign_parameters(trained_params, inplace=True)

        dist.compose(ansatz, inplace=True)

        # create the European call expected value
        strike_price = 2
        rescaling_factor = 0.25
        european_call = EuropeanCallExpectedValue(num_qubits, strike_price, rescaling_factor,
                                                  bounds)

        # create the state preparation circuit
        state_preparation = european_call.compose(dist, front=True)

        iae = IterativeAmplitudeEstimation(0.01, 0.05, state_preparation=state_preparation,
                                           objective_qubits=[num_qubits],
                                           post_processing=european_call.post_processing)

        backend = QuantumInstance(Aer.get_backend('qasm_simulator'),
                                  seed_simulator=125, seed_transpiler=80)
        result = iae.run(backend)
        self.assertAlmostEqual(result.estimation, 1.0364776997977694)
    def test_normal(self, num_qubits, mu, sigma, bounds, upto_diag):
        """Test the statevector produced by ``NormalDistribution`` and the default arguments."""

        # construct default values and kwargs dictionary to call the constructor of
        # NormalDistribution. The kwargs dictionary is used to not pass any arguments which are
        # None to test the default values of the class.
        kwargs = {'num_qubits': num_qubits, 'upto_diag': upto_diag}

        if mu is None:
            mu = np.zeros(len(num_qubits)) if isinstance(num_qubits,
                                                         list) else 0
        else:
            kwargs['mu'] = mu

        if sigma is None:
            sigma = np.eye(len(num_qubits)).tolist() if isinstance(
                num_qubits, list) else 1
        else:
            kwargs['sigma'] = sigma

        if bounds is None:
            bounds = [
                (-1, 1)
            ] * (len(num_qubits) if isinstance(num_qubits, list) else 1)
        else:
            kwargs['bounds'] = bounds

        normal = NormalDistribution(**kwargs)
        self.assertDistributionIsCorrect(normal, num_qubits, mu, sigma, bounds,
                                         upto_diag)
    def test_gaussian_copula_normal(self):
        """Test the Gaussian copula circuit against the normal case."""
        
        mu = [0.1, 0.9]
        sigma = [[1, -0.2], [-0.2, 1]]

        num_qubits = [3, 2]
        bounds = [(-1, 1), (-1, 1)] 
        
        def F(x, mean, std = 1):
            return norm.cdf(x, loc = mean, scale = std)

        def f(x, mean, std = 1):
            return norm.pdf(x, loc = mean, scale = std)

        cdfs = [lambda x:F(x, mu[0]), lambda x:F(x, mu[1])]
        pdfs = [lambda x:f(x, mu[0]), lambda x:f(x, mu[1])]

        normal = NormalDistribution(num_qubits, mu=mu, sigma=sigma, bounds=bounds)
        gc_normal = GaussianCopula(num_qubits, cdfs, sigma=sigma, bounds=bounds, pdfs = pdfs)

        sv_normal = Statevector.from_instruction(normal)
        sv_gc_normal = Statevector.from_instruction(gc_normal)

        np.testing.assert_array_almost_equal(sv_normal.data, sv_gc_normal.data)
    def test_readme_sample(self):
        """readme sample test"""

        # pylint: disable=import-outside-toplevel,redefined-builtin

        def print(*args):
            """overloads print to log values"""
            if args:
                self.log.debug(args[0], *args[1:])

        # --- Exact copy of sample code ----------------------------------------

        import numpy as np
        from qiskit import BasicAer
        from qiskit.algorithms import AmplitudeEstimation
        from qiskit.circuit.library import NormalDistribution
        from qiskit_finance.applications import FixedIncomePricing

        # Create a suitable multivariate distribution
        num_qubits = [2, 2]
        bounds = [(0, 0.12), (0, 0.24)]
        mvnd = NormalDistribution(num_qubits,
                                  mu=[0.12, 0.24],
                                  sigma=0.01 * np.eye(2),
                                  bounds=bounds)

        # Create fixed income component
        fixed_income = FixedIncomePricing(
            num_qubits,
            np.eye(2),
            np.zeros(2),
            cash_flow=[1.0, 2.0],
            rescaling_factor=0.125,
            bounds=bounds,
            uncertainty_model=mvnd,
        )

        # the FixedIncomeExpectedValue provides us with the necessary rescalings

        # create the A operator for amplitude estimation
        problem = fixed_income.to_estimation_problem()

        # Set number of evaluation qubits (samples)
        num_eval_qubits = 5

        # Construct and run amplitude estimation
        q_i = BasicAer.get_backend("statevector_simulator")
        algo = AmplitudeEstimation(num_eval_qubits=num_eval_qubits,
                                   quantum_instance=q_i)
        result = algo.estimate(problem)

        print("Estimated value:\t%.4f" % fixed_income.interpret(result))
        print("Probability:    \t%.4f" % result.max_probability)

        # ----------------------------------------------------------------------

        with self.subTest("test estimation"):
            self.assertAlmostEqual(result.estimation_processed, 2.46, places=4)
        with self.subTest("test max.probability"):
            self.assertAlmostEqual(result.max_probability, 0.8487, places=4)
    def test_readme_sample(self):
        """ readme sample test """

        # pylint: disable=import-outside-toplevel,redefined-builtin

        def print(*args):
            """ overloads print to log values """
            if args:
                self.log.debug(args[0], *args[1:])

        # --- Exact copy of sample code ----------------------------------------

        import numpy as np
        from qiskit import BasicAer
        from qiskit.aqua.algorithms import AmplitudeEstimation
        from qiskit.circuit.library import NormalDistribution
        from qiskit.finance.applications import FixedIncomeExpectedValue

        # Create a suitable multivariate distribution
        num_qubits = [2, 2]
        bounds = [(0, 0.12), (0, 0.24)]
        mvnd = NormalDistribution(num_qubits,
                                  mu=[0.12, 0.24],
                                  sigma=0.01 * np.eye(2),
                                  bounds=bounds)

        # Create fixed income component
        fixed_income = FixedIncomeExpectedValue(num_qubits,
                                                np.eye(2),
                                                np.zeros(2),
                                                cash_flow=[1.0, 2.0],
                                                rescaling_factor=0.125,
                                                bounds=bounds)

        # the FixedIncomeExpectedValue provides us with the necessary rescalings
        post_processing = fixed_income.post_processing

        # create the A operator for amplitude estimation by prepending the
        # normal distribution to the function mapping
        state_preparation = fixed_income.compose(mvnd, front=True)

        # Set number of evaluation qubits (samples)
        num_eval_qubits = 5

        # Construct and run amplitude estimation
        backend = BasicAer.get_backend('statevector_simulator')
        algo = AmplitudeEstimation(num_eval_qubits,
                                   state_preparation,
                                   post_processing=post_processing)
        result = algo.run(backend)

        print('Estimated value:\t%.4f' % result.estimation)
        print('Probability:    \t%.4f' % result.max_probability)

        # ----------------------------------------------------------------------

        self.assertAlmostEqual(result.estimation, 2.46, places=4)
        self.assertAlmostEqual(result.max_probability, 0.8487, places=4)
Example #6
0
    def test_application(self):
        """Test an end-to-end application."""
        try:
            from qiskit import Aer  # pylint: disable=unused-import,import-outside-toplevel
        except ImportError as ex:  # pylint: disable=broad-except
            self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex)))
            return

        bounds = np.array([0., 7.])
        num_qubits = 3

        # the distribution circuit is a normal distribution plus a QGAN-trained ansatz circuit
        dist = NormalDistribution(num_qubits, mu=1, sigma=1, bounds=bounds)

        ansatz = TwoLocal(num_qubits, 'ry', 'cz', reps=1, entanglement='circular')
        trained_params = [0.29399714, 0.38853322, 0.9557694, 0.07245791, 6.02626428, 0.13537225]
        ansatz.assign_parameters(trained_params, inplace=True)

        dist.compose(ansatz, inplace=True)

        # create the European call expected value
        strike_price = 2
        rescaling_factor = 0.25
        european_call = EuropeanCallPricingObjective(num_state_qubits=num_qubits,
                                                     strike_price=strike_price,
                                                     rescaling_factor=rescaling_factor,
                                                     bounds=bounds)

        # create the state preparation circuit
        state_preparation = european_call.compose(dist, front=True)

        problem = EstimationProblem(state_preparation=state_preparation,
                                    objective_qubits=[num_qubits],
                                    post_processing=european_call.post_processing)

        q_i = QuantumInstance(Aer.get_backend('qasm_simulator'),
                              seed_simulator=125, seed_transpiler=80)
        iae = IterativeAmplitudeEstimation(epsilon_target=0.01,
                                           alpha=0.05,
                                           quantum_instance=q_i)
        result = iae.estimate(problem)
        self.assertAlmostEqual(result.estimation_processed, 1.0127253837345427)
    def test_application(self):
        """Test an end-to-end application."""
        try:
            from qiskit import (
                Aer, )  # pylint: disable=unused-import,import-outside-toplevel
        except ImportError as ex:  # pylint: disable=broad-except
            self.skipTest(
                "Aer doesn't appear to be installed. Error: '{}'".format(
                    str(ex)))
            return

        a_n = np.eye(2)
        b = np.zeros(2)

        num_qubits = [2, 2]

        # specify the lower and upper bounds for the different dimension
        bounds = [(0, 0.12), (0, 0.24)]
        mu = [0.12, 0.24]
        sigma = 0.01 * np.eye(2)

        # construct corresponding distribution
        dist = NormalDistribution(num_qubits, mu, sigma, bounds=bounds)

        # specify cash flow
        c_f = [1.0, 2.0]

        # specify approximation factor
        rescaling_factor = 0.125

        # get fixed income circuit appfactory
        fixed_income = FixedIncomePricingObjective(num_qubits, a_n, b, c_f,
                                                   rescaling_factor, bounds)

        # build state preparation operator
        state_preparation = fixed_income.compose(dist, front=True)

        problem = EstimationProblem(
            state_preparation=state_preparation,
            objective_qubits=[4],
            post_processing=fixed_income.post_processing,
        )

        # run simulation
        q_i = QuantumInstance(Aer.get_backend("qasm_simulator"),
                              seed_simulator=2,
                              seed_transpiler=2)
        iae = IterativeAmplitudeEstimation(epsilon_target=0.01,
                                           alpha=0.05,
                                           quantum_instance=q_i)
        result = iae.estimate(problem)

        # compare to precomputed solution
        self.assertAlmostEqual(result.estimation_processed, 2.3389012822103044)
Example #8
0
    def test_application(self):
        """Test an end-to-end application."""
        a_n = np.eye(2)
        b = np.zeros(2)

        num_qubits = [2, 2]

        # specify the lower and upper bounds for the different dimension
        bounds = [(0, 0.12), (0, 0.24)]
        mu = [0.12, 0.24]
        sigma = 0.01 * np.eye(2)

        # construct corresponding distribution
        dist = NormalDistribution(num_qubits, mu, sigma, bounds=bounds)

        # specify cash flow
        c_f = [1.0, 2.0]

        # specify approximation factor
        rescaling_factor = 0.125

        # get fixed income circuit appfactory
        fixed_income = FixedIncomeExpectedValue(num_qubits, a_n, b, c_f,
                                                rescaling_factor, bounds)

        # build state preparation operator
        state_preparation = fixed_income.compose(dist, front=True)

        # run simulation
        iae = IterativeAmplitudeEstimation(
            epsilon=0.01,
            alpha=0.05,
            state_preparation=state_preparation,
            post_processing=fixed_income.post_processing)
        backend = QuantumInstance(Aer.get_backend('qasm_simulator'),
                                  seed_simulator=2,
                                  seed_transpiler=2)
        result = iae.run(backend)

        # compare to precomputed solution
        self.assertAlmostEqual(result.estimation, 2.3389012822103044)
Example #9
0
    def __init__(
        self,
        n_normal: int,
        normal_max_value: float,
        p_zeros: Union[List[float], np.ndarray],
        rhos: Union[List[float], np.ndarray],
    ) -> None:
        """
        Args:
            n_normal: Number of qubits to represent the latent normal random variable Z
            normal_max_value: Min/max value to truncate the latent normal random variable Z
            p_zeros: Standard default probabilities for each asset
            rhos: Sensitivities of default probability of assets with respect to latent variable Z
        """
        self.n_normal = n_normal
        self.normal_max_value = normal_max_value
        self.p_zeros = p_zeros
        self.rhos = rhos
        num_qubits = n_normal + len(p_zeros)

        # get normal (inverse) CDF and pdf (these names are from the paper, therefore ignore
        # pylint)
        def F(x):  # pylint: disable=invalid-name
            return norm.cdf(x)

        def F_inv(x):  # pylint: disable=invalid-name
            return norm.ppf(x)

        def f(x):  # pylint: disable=invalid-name
            return norm.pdf(x)

        # call super constructor
        super().__init__(num_qubits, name='P(X)')

        # create linear rotations for conditional defaults
        slopes = []
        offsets = []
        for rho, p_zero in zip(rhos, p_zeros):
            psi = F_inv(p_zero) / np.sqrt(1 - rho)

            # compute slope / offset
            slope = -np.sqrt(rho) / np.sqrt(1 - rho)
            slope *= f(psi) / np.sqrt(1 - F(psi)) / np.sqrt(F(psi))
            offset = 2 * np.arcsin(np.sqrt(F(psi)))

            # adjust for integer to normal range mapping
            offset += slope * (-normal_max_value)
            slope *= 2 * normal_max_value / (2**n_normal - 1)

            offsets += [offset]
            slopes += [slope]

        # create normal distribution
        normal_distribution = NormalDistribution(n_normal,
                                                 0,
                                                 1,
                                                 bounds=(-normal_max_value,
                                                         normal_max_value))

        # build circuit
        self.append(normal_distribution.to_gate(), list(range(n_normal)))
        for k, (slope, offset) in enumerate(zip(slopes, offsets)):
            lry = LinearPauliRotations(n_normal, slope, offset)
            qubits = list(range(n_normal)) + [n_normal + k]
            self.append(lry.to_gate(), qubits)
    def test_bounds_invalid(self, bounds):
        """Test passing invalid bounds raises."""

        with self.assertRaises(ValueError):
            _ = NormalDistribution([1, 1], [0, 0], [[1, 0], [0, 1]], bounds)
    def test_mismatching_dimensions(self, num_qubits, mu, sigma, bounds):
        """Test passing mismatching dimensions raises an error."""

        with self.assertRaises(ValueError):
            _ = NormalDistribution(num_qubits, mu, sigma, bounds)
Example #12
0
    def test_distribution_load(self):
        """ Test that calculates a cumulative probability from the P&L distribution."""

        correl = ft.get_correl("AAPL", "MSFT")

        bounds_std = 3.0
        num_qubits = [3, 3]
        sigma = correl
        bounds = [(-bounds_std, bounds_std), (-bounds_std, bounds_std)]
        mu = [0, 0]

        # starting point is a multi-variate normal distribution
        normal = NormalDistribution(num_qubits,
                                    mu=mu,
                                    sigma=sigma,
                                    bounds=bounds)

        pl_set = []
        coeff_set = []
        for ticker in ["MSFT", "AAPL"]:
            ((cdf_x, cdf_y), sigma) = ft.get_cdf_data(ticker)
            (x, y) = ft.get_fit_data(ticker, norm_to_rel=False)
            (pl, coeffs) = ft.fit_piecewise_linear(x, y)
            # scale, to apply an arbitrary delta (we happen to use the same value here, but could be different)
            coeffs = ft.scaled_coeffs(coeffs, 1.2)
            pl_set.append(lambda z: ft.piecewise_linear(z, *coeffs))
            coeff_set.append(coeffs)

        # calculate the max and min P&Ls
        p_max = max(pl_set[0](bounds_std), pl_set[1](bounds_std))
        p_min = min(pl_set[0](-bounds_std), pl_set[1](-bounds_std))

        # we discretise the transforms and create the circuits
        transforms = []
        i_to_js = []
        for i, ticker in enumerate(["MSFT", "AAPL"]):
            (i_0, i_1, a0, a1, a2, b0, b1, b2, i_to_j, i_to_x,
             j_to_y) = ft.integer_piecewise_linear_coeffs(coeff_set[i],
                                                          x_min=-bounds_std,
                                                          x_max=bounds_std,
                                                          y_min=p_min,
                                                          y_max=p_max)
            transforms.append(
                PiecewiseLinearTransform3(i_0, i_1, a0, a1, a2, b0, b1, b2))
            i_to_js.append(np.vectorize(i_to_j))

        i1, i2 = get_sims(normal)
        j1 = i_to_js[0](i1)
        j2 = i_to_js[1](i2)
        j_tot = j1 + j2

        num_ancillas = transforms[0].num_ancilla_qubits

        qr_input = QuantumRegister(6, 'input')  # 2 times 3 registers
        qr_objective = QuantumRegister(1, 'objective')
        qr_result = QuantumRegister(6, 'result')
        qr_ancilla = QuantumRegister(num_ancillas, 'ancilla')
        #output = ClassicalRegister(6, 'output')

        state_preparation = QuantumCircuit(qr_input, qr_objective, qr_result,
                                           qr_ancilla)  #, output)
        state_preparation.append(normal, qr_input)

        for i in range(2):
            offset = i * 3
            state_preparation.append(
                transforms[i],
                qr_input[offset:offset + 3] + qr_result[:] + qr_ancilla[:])

        # to calculate the cdf, we use an additional comparator
        x_eval = 4
        comparator = IntegerComparator(len(qr_result), x_eval + 1, geq=False)
        state_preparation.append(
            comparator, qr_result[:] + qr_objective[:] +
            qr_ancilla[0:comparator.num_ancillas])

        # now check
        check = False
        if check:
            job = execute(state_preparation,
                          backend=Aer.get_backend('statevector_simulator'))
            var_prob = 0
            for i, a in enumerate(job.result().get_statevector()):
                b = ('{0:0%sb}' %
                     (len(qr_input) + 1)).format(i)[-(len(qr_input) + 1):]
                prob = np.abs(a)**2
                if prob > 1e-6 and b[0] == '1':
                    var_prob += prob
            print('Operator CDF(%s)' % x_eval + ' = %.4f' % var_prob)

        # now do AE

        problem = EstimationProblem(state_preparation=state_preparation,
                                    objective_qubits=[len(qr_input)])

        # target precision and confidence level
        epsilon = 0.01
        alpha = 0.05
        qi = QuantumInstance(Aer.get_backend('aer_simulator'), shots=100)
        ae_cdf = IterativeAmplitudeEstimation(epsilon,
                                              alpha=alpha,
                                              quantum_instance=qi)
        result_cdf = ae_cdf.estimate(problem)

        conf_int = np.array(result_cdf.confidence_interval)
        print('Estimated value:\t%.4f' % result_cdf.estimation)
        print('Confidence interval: \t[%.4f, %.4f]' % tuple(conf_int))

        state_preparation.draw()
Example #13
0
    def in_progress_test_piecewise_transform(self):
        """Simple end-to-end test of the (semi-classical) multiply and add building block."""

        import numpy as np
        import matplotlib.pyplot as plt

        from qiskit import execute, Aer, QuantumCircuit, QuantumRegister, ClassicalRegister, AncillaRegister
        from qiskit.aqua.algorithms import IterativeAmplitudeEstimation
        from qiskit.circuit.library import NormalDistribution, LogNormalDistribution, LinearAmplitudeFunction, IntegerComparator, WeightedAdder
        from qiskit.visualization import plot_histogram
        from quantum_mc.arithmetic import multiply_add

        qr_input = QuantumRegister(3, 'input')
        qr_result = QuantumRegister(6, 'result')
        qr_comp = QuantumRegister(2, 'comparisons')
        qr_ancilla = QuantumRegister(6, 'ancilla')
        qr_comp_anc = QuantumRegister(3, 'cond_ancilla')
        output = ClassicalRegister(6, 'output')
        circ = QuantumCircuit(qr_input, qr_result, qr_comp, qr_ancilla,
                              qr_comp_anc, output)

        # our test piece-wise transforms:
        # trans0 if x <= 2, x => 6*x + 7
        # trans1 if 2 < x <= 5, x => x + 17
        # trans2 if x > 5, x => 3*x + 7

        sigma = 1
        low = -3
        high = 3
        mu = 0

        normal = NormalDistribution(3,
                                    mu=mu,
                                    sigma=sigma**2,
                                    bounds=(low, high))
        circ.append(normal, qr_input)

        comp0 = IntegerComparator(num_state_qubits=3,
                                  value=3,
                                  name="comparator0")  # true if i >= point
        comp1 = IntegerComparator(num_state_qubits=3,
                                  value=6,
                                  name="comparator1")  # true if i >= point
        trans0 = multiply_add.cond_classical_add_mult(6, 7, qr_input,
                                                      qr_result, qr_ancilla)
        trans1 = multiply_add.cond_classical_add_mult(1, 17, qr_input,
                                                      qr_result, qr_ancilla)
        trans2 = multiply_add.cond_classical_add_mult(3, 7, qr_input,
                                                      qr_result, qr_ancilla)

        circ.append(
            comp0,
            qr_input[:] + [qr_comp[0]] + qr_ancilla[0:comp0.num_ancillas])
        circ.append(
            comp1,
            qr_input[:] + [qr_comp[1]] + qr_ancilla[0:comp0.num_ancillas])

        # use three additional ancillas to define the ranges
        circ.cx(qr_comp[0], qr_comp_anc[0])
        circ.x(qr_comp_anc[0])
        circ.cx(qr_comp[1], qr_comp_anc[2])
        circ.x(qr_comp_anc[2])
        circ.ccx(qr_comp[0], qr_comp_anc[2], qr_comp_anc[1])

        circ.append(trans0, [qr_comp_anc[0]] + qr_input[:] + qr_result[:] +
                    qr_ancilla[:])
        circ.append(trans1, [qr_comp_anc[1]] + qr_input[:] + qr_result[:] +
                    qr_ancilla[:])
        circ.append(trans2,
                    [qr_comp[1]] + qr_input[:] + qr_result[:] + qr_ancilla[:])

        # can uncompute qr_comp_anc
        # then uncompute the comparators

        circ.measure(qr_result, output)