進捗のようなもの

やったこと書きます

BlueqatでCirqバックエンドをつくってみた

はじめに

年末恒例のアドベントカレンダー量子コンピュータ関連の記事がたくさんアップされていて、2019年のまとめのような感じもしてとても参考になります。今回は、アドベントカレンダーで見つけた記事を参考にBlueqatのバックエンドをつくってみたいと思います。

Blueqatの開発者であるgyu-donさんの記事です。
qiita.com
この記事ではIBMのQiskitのバックエンドの実装方法が紹介されていますが、ここではGoogleのCirqを利用するバックエンドを作ります。

f:id:SJSY:20191213002142p:plain


\def\bra#1{\mathinner{\left\langle{#1}\right|}}
\def\ket#1{\mathinner{\left|{#1}\right\rangle}}
\def\braket#1#2{\mathinner{\left\langle{#1}\middle|#2\right\rangle}}

Cirqでの回路形式

参考記事によると量子回路をOpenQASM形式で入力できる場合、簡単にBlueqatバックエンドを作れようです。そこで、Cirqの公式ドキュメントを読むとOpenQASMの入力を受け入れるメソッドcircuit_from_qasmが用意されていることがわかります。これを使えばさっくりバックエンドが作れそうです。

cirq.readthedocs.io

バックエンドをつくる

基本は参考にした記事のとおりです。
テストに \ket{11}を出力する2ビットのGrover回路を仕様します。

過去にCirqでGroverのアルゴリズムを実装したヤツの使いまわしです。
sjsy.hatenablog.com

ではコードです。

まずは、必要なパッケージのインポート

import warnings
import collections
from collections import Counter

import blueqat
from blueqat import Circuit
from blueqat import BlueqatGlobalSetting

blueqat.__version__
'0.3.11-dev'

バックエンド作成機能があるBlueqatをインストールしてます。

def _qasm_runner_cirq(qasm, shots=None, returns=None, **kwargs):
    if returns is None:
        returns = "cirq_result"
    elif returns not in ("cirq_result", 'draw'):
        raise ValueError("`returns` shall be None, 'draw', 'cirq_result' or '_exception'")

    import_error = None
    try:
        with warnings.catch_warnings():
            from cirq import Circuit, Simulator
            from cirq.contrib.qasm_import import circuit_from_qasm
            import cirq
    except Exception as e:
        import_error = e

    if import_error:
        if returns == "_exception":
            return e
        if isinstance(import_error, ImportError):
            raise ImportError("Cannot import Cirq. To use this backend, please install qiskit." +
                              " `pip install Cirq`.")
        else:
            raise ValueError("Unknown error raised when importing Cirq. To get exception, " +
                             'run this backend with arg `returns="_exception"`')
    else:
        if returns == "_exception":
            return None
        cirq_circuit = circuit_from_qasm(qasm)
        if returns == "draw":
            return cirq_circuit
        if shots is None:
            shots = 1024
        simulator = cirq.Simulator()
        result = simulator.run(cirq_circuit, repetitions=shots, **kwargs)
        if returns == "cirq_result":
            return result
        return result

cirq_backend = blueqat.backends.qasm_parser_backend_generator.generate_backend(_qasm_runner_cirq)

BlueqatGlobalSetting.register_backend("mycirq", cirq_backend)

参考にした記事のコードをベースにCirqで実行できるように書き換えています。returnsで返す出力結果は改良の余地がありあそうですが、まずはシンプルに作りました。

作ったバックエンドをグローバーアルゴリズムで確認してみます。

# 量子回路初期化
qc = Circuit(2)

# オラクル(|11>を反転)
qc.h[:].cz[0, 1].h[:]

# 振幅増幅
qc.x[:].cz[0, 1].x[:].h[:]

# 測定
qc.m[:]

回路の表示

qc.run(returns='draw',shots=10000, backend="mycirq")
q_0: ───H───────@───H───X───@───X───H───M('c_0')───
                │           │
q_1: ───I───H───@───H───X───@───X───H───M('c_1')───

Buleqatは回路の表示がないので、回路表示ができるバックエンドは回路確認するのに便利ですね。

量子回路のシミュレーション実行

result = qc.run(shots=10000, backend="mycirq")
result.histogram(key=['c_0'])
result.histogram(key=['c_1'])
 Counter({1: 10000})  
 Counter({1: 10000})

ちゃんと実行できているようです。

Cirqを使うバックエンドの実装ができました!!! これでQiskitもCirqもBlueqatのバックエンドとして実行できるようになったので、量子回路のコードが書きやすいBlueqatをどんどん利用していきたいと思います!!!他のバックエンドもどんどん作っていきたいですね。