はやし雑記

はやしです

OpenQASMの読み方・書き方

OpenQASM (Version 2.0) の読み方、書き方について調べた。

Open Quantum Assembly Language (OpenQASM) とは、量子回路を記述するための中間表現である。

github.com

だいたいは以下の内容を和訳したものだが、実際にQiskitでQASMを読み込み、回路を表示させてみる。

https://github.com/Qiskit/openqasm/blob/master/spec/qasm2.rst#language

言語仕様

Qiskit では、OpenQASMの拡張子は.qasm が使われている。

ファイルの1行目は、必ずOPENQASM M.m;で始まる。M.mはVersionである。

文は;で区切られ、空白は無視される。 コメントには//を用い、行末までコメントになる。 また、この言語はCase Sensitiveである。

次のようなQASMをQiskitで読んでみる。

OPENQASM 2.0;

QiskitのQuantumCircuitをQASMから生成するmethodとして、QuantumCircuit.from_qasm_file(path)QuantumCircuit.from_qasm_str(qasm_str)の2つのstaticmethodが用意されている。

from qiskit import QuantumCircuit

qasm = '''
OPENQASM 2.0;
'''

circuit = QuantumCircuit.from_qasm_str(qasm)
circuit.draw()

circuit.draw()を呼ぶと、量子回路が描写される。このQASMはqubitもgateもなにもないので、当然何も表示されない。

fig = circuit.draw(output='mpl')
fig.savefig("ex.png")

とすると、画像として出力して保存できる。jupyter labなどで画像を表示させたいときは、display(fig)とすれば良い。

outputlatexなども指定でき、defaultではtextとなるため、文字列が表示される。

ファイルの1行目は、必ずOPENQASM M.m;で始まるべきであるが、実際のところ、空文字ではエラーになるが、他の文が存在すればOPENQASM 2.0;がなくても問題無く読み込むことができた。

include

他のQASMはinclude “filename”で読み込むことができる。includeしたファイルは、その箇所で展開されたようになる。

qiskit-terra/qelib1.inc at main · Qiskit/qiskit-terra · GitHub

Qiskitでは、いくつかの基本的な量子gateがqelib1.incに定義されており、以下では基本的にinclude "qelib1.inc";を頭で行う。

bits and qubits

qreg name[size]でqubitsを、creg name[size]でbitsを宣言する。 name[i]namei番目のqubit/bitを表す。添字iは多くの言語と同様に0始まりである。 bitもqubitも0に初期化される。

OPENQASM 2.0;
include "qelib1.inc";
qreg q[3];
creg c[3];

このQASMでは、名前がqの3つのqubitsと、名前がcの3つのbitsを宣言した。 QuantumCircuitを作り、drawすると、

f:id:hayashikunsan:20200505143353p:plain

と表示される。なぜかこのQASMをoutput='mpl'として表示しようとすると変な感じになる。

gate

G a,b,...;と記述することで、gateGをqubit a,b,...に作用させることができる。

NOT gateのような、1 qubitに作用するgateは、X a;と記述し、CNOT gateのような2 qubitに作用するgateはCX a,b;と記述する。

NOT gateやCNOT gateもbitに作用できても良い感じはするが、qubitじゃないとエラーが出る。bitをcontrolledとしてCNOTをしたいような場合は、後述のifを用いる。

gateが作用するqubit(s)はqのような指定も、q[0]のような指定もできる。

OPENQASM 2.0;
include "qelib1.inc";
qreg a[3];
x a;
h a[1];

これをQiskitで読み、output='mpl'で出力すると、以下のようになる。

f:id:hayashikunsan:20200505013138p:plain

複数qubitsに作用するgateの場合、指定の仕方は少しややこしい。

OPENQASM 2.0;
include "qelib1.inc";
qreg a[3];
qreg b[3];
cx a,b;
cy a[0],b;
cz a[0],b[0];

f:id:hayashikunsan:20200505013726p:plain

引数をとる場合、G(p) a,b,...;と記述する。例えば、x軸回りにπ/2回転させるgateはrx(pi/2)となる。

OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
rx(pi/2) q;

f:id:hayashikunsan:20200505140536p:plain

define gate

既存のgateを組み合わせて新しくgateを定義するときは、

gate mygate(p1, p2) q1, q2
{
    rx(p1) q1;
    cx q1,q2;
    ry(p2) q2;
} 

のように記述する。

OPENQASM 2.0;
include "qelib1.inc";

gate mygate(p1, p2) q1, q2
{
    rx(p1) q1;
    cx q1,q2;
    ry(p2) q2;
} 

qreg q[3];
mygate(pi/2,pi/4) q[1],q[2];

をQiskitに読ませると、

f:id:hayashikunsan:20200505142723p:plain

と表示される。Qiskitではoutput='mpl'ではエラーが起こる。

同じ名前のgateを複数回定義しようとすると、エラーが起こる。そのため、Overrideはできない。 また、#if definedのようなこともできないので、qelib1.incを複数回読み込むようなことをするとエラーが起こる。

measure

qubit(s) qZを基底として観測して、bit(s) c に測定結果を格納するためには、measure q -> cと記述する。

OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q;
measure q[1] -> c[1];

f:id:hayashikunsan:20200505145858p:plain

reset

reset qとすると、qubit(s)q|0>にresetできる。

barrier

barrierを入れることで、最適化によって回路図が変形すること防ぐことができる。

OPENQASM 2.0;
include "qelib1.inc";
qreg a[2];
qreg b[2];

CX a[0],a[1];
h b[0];
barrier a;
u1(1) b[0];
barrier b[0];
u1(1) b[0];
CX a[0],a[1];
u1(1) b[1];
u1(1) b[1];
CX a[1],a[0];

以上QASMの回路図は

f:id:hayashikunsan:20200505161622p:plain

となる。barrierを置かないと基本的に要素は左詰めになるが、barrierを置くことで要素を自由に整列させることができる。

ここで、回路をcircuit = qiskit.transpile(circuit)によってtranspileすると、U1(1)-U1(1)はbarrierが無い時はU1(2)と結合させるが、barrierを置くことでtranspileしても結合されなくなる。

f:id:hayashikunsan:20200505161949p:plain

if

if(c==5) G a,b,...; のように記述すると、bits c の値が5(3bitのとき101)であれば、Gが実行される。 if(c[0]==1)のような指定はできない。

OPENQASM 2.0;
include "qelib1.inc";
qreg q[3];
creg c[3];
h q;
measure q -> c;
if(c==3) cx q[0],q[1];

f:id:hayashikunsan:20200505162622p:plain

U

OpenQASMで最初から実装されているgateは1 qubit gateのCXと、2 qubit gateのU(theta,phi,lambda)だけで、その他のgateはqelib1.incを見るとわかるように、全てこの2つを組み合わせて作られる。

f:id:hayashikunsan:20200505163823p:plain

量子テレポーテーション

blog.hayashikun.com

量子テレポーテーションをQASMで書いてみる。

OPENQASM 2.0;
include "qelib1.inc";
qreg q[3];
creg c0[1];
creg c1[1];
creg c2[1];

ry(pi/3) q[0];
barrier q;

h q[1];
cx q[1],q[2];
barrier q;
cx q[0],q[1];
h q[0];
measure q[0] -> c0[0];
measure q[1] -> c1[0];

barrier q;
if(c1==1) x q[2];
if(c0==1) z q[2];

measure q[2] -> c2[0];

f:id:hayashikunsan:20200505171454p:plain

テレポートしたい状態はry(pi/3) q[0];として作ったので、0と1は3:1の割合になる。

simulator = qiskit.Aer.get_backend('qasm_simulator')

circuit = QuantumCircuit.from_qasm_str(qasm)
job = qiskit.execute(circuit, simulator, shots=1000)
result = job.result()
counts = result.get_counts(circuit)

c2_count = [0, 0]
for bits, count in counts.items():
    c2_count[int(bits.split()[0])] += count
   

のように実行すると、c2_count[0]c2_count[1]が概ね3:1になる。