# 必要なライブラリのインポート
from qiskit import IBMQ, BasicAer, Aer
from qiskit.providers.ibmq import least_busy
from qiskit import QuantumCircuit, execute
import numpy as np

# 描画ツールのインポート
from qiskit.visualization import plot_histogram

#qiskitのversion確認
import qiskit
qiskit.__qiskit_version__

#入力用量子ビットの数を設定
n = 3

const_f = QuantumCircuit(n+1,n)  #量子回路を準備（アンシラ用にn+1個の量子レジスタを準備）
input = np.random.randint(2)  #乱数生成にランダム関数をつかいます
if input == 1:
    const_f.x(n)
for i in range(n):
    const_f.measure(i, i)
const_f.draw()

const_f = QuantumCircuit(n+1,n)

input = np.random.randint(2)  #乱数生成にランダム関数をつかいます
if input == 1:
    const_f.id(n)
for i in range(n):
    const_f.measure(i, i)
const_f.draw()

#上記の図のとおり4量子ビット回路を用意してください。
#各入力用量子ビットを制御に、出力用量子ビットを標的としたCNOT回路を作成してください。
q = QuantumCircuit(4)    # 4量子ビット回路を用意

q.cx(0,3)
q.cx(1,3)
q.cx(2,3)

q.draw(output="mpl")    # 回路を描画

#　上記回路にあるように入力|𝑞2𝑞1𝑞0⟩=|010⟩をつくって|𝑞3⟩の状態を確認してください。

q = QuantumCircuit(4)    # 4量子ビット回路を用意

q.x(1)   # Xゲートを1番目の量子ビットに操作します。

q.cx(0,3)
q.cx(1,3)
q.cx(2,3)

q.barrier() # 回路をみやすくするためにバリアを配置

q.x(1)   # 最後にXゲートで1番目の量子ビットを挟みます。 

q.draw(output="mpl")    # 回路を描画

balanced_f = QuantumCircuit(n+1,n)

b_str = "101"

for qubit in range(len(b_str)):
    if b_str[qubit] == '1':
        balanced_f.x(qubit)
balanced_f.draw()

balanced_f.barrier()   #回路をみやすくするためにバリアを配置

for qubit in range(n):    #入力用量子ビットにCNOTゲートを適用
    balanced_f.cx(qubit, n)

balanced_f.barrier() #回路をみやすくするためにバリアを配置
balanced_f.draw()

for qubit in range(len(b_str)):
    if b_str[qubit] == '1':
        balanced_f.x(qubit)
        
balanced_f.draw()

#ドイチ・ジョザアルゴリズムに均等な関数を実装した回路を"balanced_dj"と名付ける
balanced_dj = QuantumCircuit(n+1, n)

#Step 1:　各量子ビットはもともと|0>に初期化されているため、最後のアンシラだけ|1>に
balanced_dj.x(n) #Xゲートを最後のアンシラに適用

#Step 2: 全体にアダマールをかけて入力用量子ビットはすべて|+⟩ に、そして補助の量子ビットは|−⟩に
for qubit in range(n):    #全量子ビットにアダマールを適用
    balanced_dj.h(qubit)

#Step 3: オラクルUf |𝑥⟩|𝑦⟩↦|𝑥⟩|𝑓(𝑥)⊕𝑦⟩を適用
# 次ステップでUfを構築します

#Step 4: 最後の補助量子ビットにアダマールを適用して回路を描画
balanced_dj.h(n)    
balanced_dj.draw()

balanced_dj += balanced_f  #均等な関数の追加
balanced_dj.draw()

for qubit in range(n):
    balanced_dj.h(qubit)
balanced_dj.barrier()
for i in range(n):
    balanced_dj.measure(i, i)

balanced_dj.draw()

# 回路をシミュレーターに投げて結果を出力します
simulator = Aer.get_backend('qasm_simulator')
result = execute(balanced_dj, backend=simulator, shots=1).result()

# 結果をヒストグラムでプロットします
plot_histogram(result.get_counts(balanced_dj))

#IBM Q accountをロードししプロバイダを指定
IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q')
#一番空いているバックエンドを自動的に選択して実行
backend = least_busy(provider.backends(filters=lambda x: x.configuration().n_qubits >= (n+1) and
                                   not x.configuration().simulator and x.status().operational==True))
print("least busy backend: ", backend)

#上記で特定したバックエンドを指定してジョブを実行します。 
from qiskit.tools.monitor import job_monitor

shots = 1024
job = execute(balanced_dj, backend=backend, shots=shots, optimization_level=3)

#プログラムの実行ステータスをモニターします。
job_monitor(job, interval = 2)

#計算結果(results)を取得し、答え（answer)をプロットします。
results = job.result()
answer = results.get_counts()

# 結果をヒストグラムでプロットします
plot_histogram(answer)

#必要に応じてQiskitのライブラリ等のインポート
# from qiskit import *
# %matplotlib inline
# from qiskit.tools.visualization import plot_histogram

#適当なバイナリ文字列を設定
s = '0110001'

#qcという名の量子回路を準備
qc = QuantumCircuit(6+1,6)
qc.x(6)
qc.h(6)
qc.h(range(6))
qc.draw()

# Step 1
s = '0110001'
#$n+1$ 量子ビットと結果を格納する $n$個の古典レジスタを用意する。$n$は秘密の文字列の長さと同じ。
n = len(s)
qc = QuantumCircuit(n+1,n)

# Step 2

qc.x(n) #最後の量子ビットを|1⟩にする

qc.barrier() #回路をみやすくするためにバリアを配置

# Step 3

qc.h(range(n+1)) #全体にHゲートを適用

qc.barrier() #回路をみやすくするためにバリアを配置

# Step 4

for ii, yesno in enumerate(reversed(s)):
    if yesno == '1': 
        qc.cx(ii, n)
    
qc.barrier() #回路をみやすくするためにバリアを配置

# Step 5

qc.h(range(n+1)) #全量子ビットに再びHを適用

qc.barrier() #回路をみやすくするためにバリアを配置

qc.measure(range(n), range(n)) # 0 から n-1までの入力n量子ビットを測定し古典レジスタに結果を格納

%matplotlib inline
#回路図を描画
qc.draw(output='mpl')

# 回路をシミュレーターに投げて結果を出力します
simulator = Aer.get_backend('qasm_simulator')
# shots=1で実施
result = execute(qc, backend=simulator, shots=1).result()
# 結果をヒストグラムでプロットします
from qiskit.visualization import plot_histogram
plot_histogram(result.get_counts(qc))

# 必要似応じてIBM Q accountをロードししプロバイダを指定
# IBMQ.load_account()
# provider = IBMQ.get_provider(hub='ibm-q')
#一番空いているバックエンドを自動的に選択して実行
backend = least_busy(provider.backends(filters=lambda x: x.configuration().n_qubits >= (n+1) and
                                   not x.configuration().simulator and x.status().operational==True))
print("least busy backend: ", backend)

#上記で特定したバックエンドを指定してジョブを実行します。 
from qiskit.tools.monitor import job_monitor

shots = 1024
job = execute(qc, backend=backend, shots=shots, optimization_level=3)

#プログラムの実行ステータスをモニターします。
job_monitor(job, interval = 2)

#計算結果(results)を取得し、答え（answer)をプロットします。
results = job.result()
answer = results.get_counts()

# 結果をヒストグラムでプロットします
plot_histogram(answer)