OpenQASM (Version 2.0) の読み方、書き方について調べた。
Open Quantum Assembly Language (OpenQASM) とは、量子回路を記述するための中間表現である。
だいたいは以下の内容を和訳したものだが、実際に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)
とすれば良い。
output
はlatex
なども指定でき、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]
はname
のi
番目の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すると、
と表示される。なぜかこの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'
で出力すると、以下のようになる。
複数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];
引数をとる場合、G(p) a,b,...;
と記述する。例えば、x軸回りにπ/2回転させるgateはrx(pi/2)
となる。
OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; rx(pi/2) q;
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に読ませると、
と表示される。Qiskitではoutput='mpl'
ではエラーが起こる。
同じ名前のgateを複数回定義しようとすると、エラーが起こる。そのため、Overrideはできない。
また、#if defined
のようなこともできないので、qelib1.inc
を複数回読み込むようなことをするとエラーが起こる。
measure
qubit(s) q
をZ
を基底として観測して、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];
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の回路図は
となる。barrierを置かないと基本的に要素は左詰めになるが、barrierを置くことで要素を自由に整列させることができる。
ここで、回路をcircuit = qiskit.transpile(circuit)
によってtranspileすると、U1(1)-U1(1)
はbarrierが無い時はU1(2)
と結合させるが、barrierを置くことでtranspileしても結合されなくなる。
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];
U
OpenQASMで最初から実装されているgateは1 qubit gateのCX
と、2 qubit gateのU(theta,phi,lambda)
だけで、その他のgateはqelib1.inc
を見るとわかるように、全てこの2つを組み合わせて作られる。
量子テレポーテーション
量子テレポーテーションを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];
テレポートしたい状態は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になる。