グローバーのアルゴリズムをQiskit 1.1で実装してみた

# 背景
Qiskitの1.0が2024年5月17日にリリースされた。[IBM QUANTUM QISKIT 1.0 RELEASE](chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/https://nand-research.com/wp-content/uploads/2024/05/2024-05-19-RN-IBM-QisKit-1.0.pdf) 
これまでQiskit 0.xx を使ったプログラミングをしてきたので今までと勝手が違う言語仕様に手間取ってしまった。
また、先日グローバーのアルゴリズムについて[量子コンピュータの頭の中](https://gihyo.jp/book/2023/978-4-297-13511-9) by 束野仁政先生 を使って原理を学ぶ機会を頂いたことから、実装をする機会を得た。著書の中ではQiskit 0.43.0 を使われていたので、それをQiskit 1.1に適合させつつ新たなバージョンのプログラミング方法を学びんだ。今回は、グローバーのアルゴリズムをQiskit1.1で書き直してみる。

# Groverのアルゴリズム
例えば52枚の裏になったトランプの中から目的のカードを見つけ出すには、1枚ずつめくって確認していけば良い。運がよければ初めの1回目で目的のカードを見つけられますが、運が悪ければ52回目で見つけられる。この場合、目的のカードを見つけられる試行回数の平均はN/2回となる。しかし、グローバーのアルゴリズムではおおよそ$\{\sqrt{N}}$となる。
本ページではプログラミングについて記載したいので、詳しくは@shnchr さんの[Groverのアルゴリズム](https://qiita.com/shnchr/items/4160d2d066873824e35e)、 または[量子コンピューター入門ハンズオン〜グローバーのアルゴリズムまで](https://www.youtube.com/watch?v=_b_yEgZh3BE)を参考にしていただきたい。

# Groverのアルゴリズム(シミュレータ)
ちなみに2025/5/15を境に[IBM Quantum Lab](https://quantum.ibm.com/lab)上での実装ができなくなってしまった。ローカル環境にアレコレ入れることを考えず手軽に環境が準備できたので重宝させて頂いたのだけど、ついに終わり。これまでありがとうIBMさん。ということで[Install Qiskit](https://docs.quantum.ibm.com/start/install)を元にローカル環境を構築した。(参照：[Qiskit1.1 環境の構築 (Ubuntu; WSL2版)](https://qiita.com/trtrbohz/items/1e8ee76351db98aa195f) )

## コード例

### 宣言部
 これまで通り量子回路を形成するためにQuantumCircuitと新たにシミュレータを使うためにStatevectorSimulator, AerSimulatorをインポートした。
 
    from qiskit import QuantumCircuit
    from qiskit_aer import StatevectorSimulator, AerSimulator

### オラクルを定義
本来は解にたどり着くまでのロジックがあるとのことだけど、グローバーのアルゴリズムでは肝となる考え方に注力するため、オラクル(神託、神のおつげ)を使う。以下が、そのオラクル。

    def oracle(circuit):
        circuit.x([0,2])
        circuit.mcx([0,1,2],3)
        circuit.x([0,2])

## 回路準備
オラクルの入力サイズを3、つまり量子ビットを3として考える場合に補助量子ビットを足して4量子ビットを準備した。そして、補助量子ビット以外の量子ビットにアダマール変換を作用させて重ね合わせ状態を作る。

    circuit = QuantumCircuit(4,3)
    circuit.x(3)
    circuit.h([0,1,2])

## 解にマーキング＆解の確率を上昇＆繰り返し
以下の2つを繰り返す。
①上で定義したオラクルを使って解へのマーク付け
②拡散変換という考え方を使って解の確率上昇、

    for _ in range(2):
        oracle(circuit) # 解にマーキング

        # 解の確率を上昇
        circuit.h([0,1,2])
        circuit.x([0,1,2])
        circuit.h(2)
        circuit.ccx(0,1,2)
        circuit.h(2)
        circuit.x([0,1,2])
        circuit.h([0,1,2])

## 測定
上記までの状態を測定

    circuit.measure([0,1,2],[0,1,2])
    circuit.barrier()

## シミュレータで実行＆結果確認
回路を最適化するため generate_preset_pass_manager をインポートし、最適化した回路をAerシミュレータで実行するために SamplerV2 をインポート。

    from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
    from qiskit_ibm_runtime import SamplerV2 as Sampler

    # シミュレーター準備
    backend = AerSimulator()
    # 回路を最適化
    pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
    isa_qc = pm.run(circuit)

    # Samplerで実行
    sampler = Sampler(backend)
    job = sampler.run([isa_qc])
    result = job.result()

    # 測定された回数を表示
    counts = result[0].data.c.get_counts()
    print(f" > Counts: {counts}")

## 結果
以下に結果を示す。

    > Counts: {'010': 564, '111': 69, '011': 69, '001': 54, '101': 54, '110': 67, '100': 73, '000': 74} 

ヒストグラムで表すと以下のようになる。

    from qiskit.visualization import plot_histogram
    plot_histogram(counts)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682574/3821557d-4a19-51ea-1e75-bd68f12796c8.png)


# 参考
[量子コンピュータの頭の中](https://gihyo.jp/book/2023/978-4-297-13511-9)
[Groverのアルゴリズム](https://qiita.com/shnchr/items/4160d2d066873824e35e)
[量子コンピューター入門ハンズオン〜グローバーのアルゴリズムまで](https://www.youtube.com/watch?v=_b_yEgZh3BE)
[Qiskit 1.0入門ハンズオン 量子ビット・ゲート・回路編](
https://www.youtube.com/watch?v=DfPcjrTclII)
[Qiskit1.1 環境の構築 (Ubuntu; WSL2版)](https://qiita.com/trtrbohz/items/1e8ee76351db98aa195f)