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)
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)
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)
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)
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()
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)