from qiskit import Aer
from qiskit.algorithms import VQE, QAOA, NumPyMinimumEigensolver
from qiskit.algorithms.optimizers import COBYLA
from qiskit.circuit.library import TwoLocal
from qiskit.utils import QuantumInstance
from qiskit_finance.applications.optimization import PortfolioOptimization
from qiskit_finance.data_providers import RandomDataProvider
from qiskit_optimization.algorithms import MinimumEigenOptimizer
from qiskit_optimization.applications import OptimizationApplication
from qiskit_optimization.converters import QuadraticProgramToQubo
from qiskit_finance import QiskitFinanceError
from qiskit_finance.data_providers import *
from qiskit import IBMQ, assemble, transpile
from qiskit.utils import algorithm_globals
import numpy as np
import matplotlib.pyplot as plt
import datetime

provider = IBMQ.load_account()

provider = IBMQ.get_provider(hub='ibm-q')
for backend in provider.backends():
    print(backend)

for backend in provider.backends():
    print(backend.status().to_dict())


#backend = provider.get_backend('ibmq_santiago')

backend = Aer.get_backend('statevector_simulator')



## Load Stock market Data 
stocks = ["AEO", "AAPL", "AEP", "AAL"]
try:
    data = YahooDataProvider(
                 tickers = stocks,
                 start=datetime.datetime(2018, 1, 1),
                 end=datetime.datetime(2018, 12, 31))
    data.run()
except QiskitFinanceError as ex:
    data = None
    print(ex)

#Plot Tickers over the time period


for (cnt, s) in enumerate(data._tickers):
    plt.plot(data._data[cnt], label=s)
plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.1), ncol=3)
plt.xticks(rotation=90)
plt.show()

# # set number of assets (= number of qubits)
# num_assets = 4
# seed = 124

# # Generate expected return and covariance matrix from (random) time-series
# stocks = [("TICKER%s" % i) for i in range(num_assets)]


# data = RandomDataProvider(tickers=stocks,
#                  start=datetime.datetime(2016,1,1),
#                  end=datetime.datetime(2016,1,30),
#                  seed=seed)
# data.run()


mu = data.get_period_return_mean_vector()
sigma = data.get_period_return_covariance_matrix()

print(stocks)
print(mu)

# plot sigma
plt.imshow(sigma, interpolation='nearest')
plt.show()

q = 0.5 
num_assets = 4
seed = 123# set risk factor
budget = num_assets // 2  # set budget
penalty = num_assets      # set parameter to scale the budget penalty term

portfolio = PortfolioOptimization(expected_returns=mu, covariances=sigma, risk_factor=q, budget=budget)
qp = portfolio.to_quadratic_program()
qp

def index_to_selection(i, num_assets):
    s = "{0:b}".format(i).rjust(num_assets)
    x = np.array([1 if s[i]=='1' else 0 for i in reversed(range(num_assets))])
    return x

def print_result(result):
    selection = result.x
    value = result.fval
    print('Optimal: selection {}, value {:.4f}'.format(selection, value))
    
    eigenstate = result.min_eigen_solver_result.eigenstate
    eigenvector = eigenstate if isinstance(eigenstate, np.ndarray) else eigenstate.to_matrix()
    probabilities = np.abs(eigenvector)**2
    i_sorted = reversed(np.argsort(probabilities))
    print('\n----------------- Full result ---------------------')
    print('selection\tvalue\t\tprobability')
    print('---------------------------------------------------')
    for i in i_sorted:
        x = index_to_selection(i, num_assets)
        value = QuadraticProgramToQubo().convert(qp).objective.evaluate(x)
        #value = portfolio.to_quadratic_program().objective.evaluate(x)
        probability = probabilities[i]
        print('%10s\t%.4f\t\t%.4f' %(x, value, probability))

exact_mes = NumPyMinimumEigensolver()
exact_eigensolver = MinimumEigenOptimizer(exact_mes)

result = exact_eigensolver.solve(qp)

print_result(result)

## Classical Optimiser 
cobyla = COBYLA()
cobyla.set_options(maxiter=5)

#ry == Ansatz
ry = TwoLocal(num_assets, 'ry', 'cz', reps=3, entanglement='full')
print(ry)

#Variation Quantum Eigensolver 

quantum_instance = QuantumInstance(backend=backend, seed_simulator=seed, seed_transpiler=seed)
vqe_mes = VQE(ry, optimizer=cobyla, quantum_instance=quantum_instance)
vqe = MinimumEigenOptimizer(vqe_mes)
result = vqe.solve(qp)

print_result(result)

algorithm_globals.random_seed = 1234
#backend = Aer.get_backend('statevector_simulator')

cobyla = COBYLA()
cobyla.set_options(maxiter=250)
quantum_instance = QuantumInstance(backend=backend, seed_simulator=seed, seed_transpiler=seed)
qaoa_mes = QAOA(optimizer=cobyla, reps=3, quantum_instance=quantum_instance)
qaoa = MinimumEigenOptimizer(qaoa_mes)
result = qaoa.solve(qp)

print_result(result)

import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

