import numpy as np

import matplotlib.pyplot as plt



from qiskit import Aer, QuantumCircuit

from qiskit.circuit import Parameter

from qiskit.circuit.library import RealAmplitudes, ZZFeatureMap

from qiskit.opflow import StateFn, PauliSumOp, AerPauliExpectation, Gradient

from qiskit.utils import QuantumInstance

from qiskit.algorithms.optimizers import COBYLA

expval = AerPauliExpectation()

gradient = Gradient()

qi_sv = QuantumInstance(Aer.get_backend('statevector_simulator'))

qi_qasm = QuantumInstance(Aer.get_backend('qasm_simulator'), shots=1000)

from qiskit_machine_learning.neural_networks import OpflowQNN

# 演算子の定義

params1 = [Parameter('input1'), Parameter('weight1')]

qc1 = QuantumCircuit(1)

qc1.h(0)

qc1.ry(params1[0], 0)

qc1.rx(params1[1], 0)

qc_sfn1 = StateFn(qc1)



H1 = StateFn(PauliSumOp.from_list([('Z', 1.0), ('X', 1.0)]))

op1 = ~H1 @ qc_sfn1

qc1.draw('mpl')

qnn1 = OpflowQNN(op1, [params1[0]], [params1[1]], expval, gradient, qi_sv)



input1 = np.random.rand(qnn1.num_inputs)

weight1 = np.random.rand(qnn1.num_weights)

qnn1.forward(input1, weight1)

# 順伝播計算は演算子の期待値を出力していることを確認

from qiskit.opflow.converters import CircuitSampler

param_dict = dict(zip(params1, [input1[0], weight1[0]]))

op1_binded = op1.assign_parameters(param_dict)

expectation = expval.convert(op1_binded)

sampler = CircuitSampler(qi_qasm).convert(expectation) 

sampler.eval().real

# output: input gradiend, weight gradient

qnn1.backward(input1, weight1)

# 逆伝播計算は演算子の期待値関数の入力と重みに対する微分計算であることを確認

state_grad = Gradient(grad_method="param_shift").convert(operator=op1, params=params1)

result = state_grad.assign_parameters(param_dict).eval()

np.array(result).real

from qiskit_machine_learning.neural_networks import TwoLayerQNN

from qiskit_machine_learning.algorithms.classifiers import NeuralNetworkClassifier

num_inputs = 2

num_samples = 20

X = 2*np.random.rand(num_samples, num_inputs) - 1 # num_sample x num_inpuutsの配列

y01 = 1*(np.sum(X, axis=1) >= 0)  # in { 0,  1}

y = 2*y01-1                       # in {-1, +1}

y_one_hot = np.zeros((num_samples, 2))

for i in range(num_samples):

    y_one_hot[i, y01[i]] = 1



for x, y_target in zip(X, y):

    if y_target == 1:

        plt.plot(x[0], x[1], 'bo')

    else:

        plt.plot(x[0], x[1], 'go')

plt.plot([-1, 1], [1, -1], '--', color='black')

plt.show()

# default observable: Z^n

opflow_qnn = TwoLayerQNN(num_inputs, quantum_instance=qi_qasm)

opflow_classifier = NeuralNetworkClassifier(opflow_qnn, optimizer=COBYLA())

# QNNを構成している量子回路を確認する

opflow_qnn.circuit.draw('mpl')

opflow_qnn.feature_map.draw('mpl')

opflow_qnn.ansatz.draw('mpl')

opflow_classifier.fit(X, y)

opflow_classifier.score(X, y)

# 評価用のデータを生成

num_samples_test = 100

X_test = 2*np.random.rand(num_samples_test, num_inputs) - 1 # num_sample x num_inpuutsの配列

y01_test = 1*(np.sum(X_test, axis=1) >= 0)  # in { 0,  1}

y_test = 2*y01_test-1                       # in {-1, +1}

# plot results

def plot_predict_result(X_test, y_test, y_predict):

    for x, y_target, y_p in zip(X_test, y_test, y_predict):

        if y_p == 1:

            plt.plot(x[0], x[1], 'bo')

        else:

            plt.plot(x[0], x[1], 'go')

        if y_target != y_p:

            plt.scatter(x[0], x[1], s=200, facecolors='none', edgecolors='r', linewidths=2)

    plt.plot([-1, 1], [1, -1], '--', color='black')

    plt.show()

# evaluate data points

y_predict = opflow_classifier.predict(X_test)



plot_predict_result(X_test, y_test, y_predict)

num_samples = 200

X2 = 2*np.random.rand(num_samples, num_inputs) - 1 # num_sample x num_inpuutsの配列

y01_2 = 1*(np.sum(X2, axis=1) >= 0)  # in { 0,  1}

y2 = 2*y01_2-1                       # in {-1, +1}

opflow_classifier.fit(X2, y2)

opflow_classifier.score(X2, y2)

# evaluate data points

y_predict = opflow_classifier.predict(X_test)



plot_predict_result(X_test, y_test, y_predict)

params2 = [Parameter('test1'), Parameter('test2')]

simple_feature_map = QuantumCircuit(num_inputs)

for i in range(num_inputs):

    simple_feature_map.ry(params2[i], [i])

opflow_qnn2= TwoLayerQNN(num_inputs, feature_map=simple_feature_map, quantum_instance=qi_qasm)

opflow_classifier2 = NeuralNetworkClassifier(opflow_qnn2, optimizer=COBYLA())

opflow_classifier2.fit(X, y)

opflow_classifier2.score(X, y)

# evaluate data points

y_predict2 = opflow_classifier2.predict(X_test)



# plot results

plot_predict_result(X_test, y_test, y_predict2)

import qiskit.tools.jupyter

%qiskit_version_table