はじめに
大阪大学で量子コンピューティングのオープンソース・ソフトウェアスタックOQTOPUSの開発をしているmassnです。
OQTOPUSのoqtopus-engineはマイクロサービス内の各モジュールの取り回しの
しやすさからGo言語で実装されています。このプロジェクトでは、直接、量子回路をGoで操作する処理はないですが、慣れ親しんだ言語で量子回路を操作してみたいと思い、Goで量子回路を描画するライブラリ gqcirc を作成しました。内部では量子回路をJSON形式で表現するデータ構造も定義し、様々な環境で量子回路を扱いやすくできればなと思っています。
量子コンピュータのシミュレータと組み合わせた発展的な使い方も最後に紹介いたします。
クイックスタート
まずは用意されたプログラムを実行して簡単な機能を見ていきたいと思います。
https://github.com/massn/gqcirc で公開しているのでまずはgit cloneしてください。
git clone https://github.com/massn/gqcirc.git
cd gqcirc
Draw機能
gqcircでは直線や円、テキストなどの基本的な図形を組み合わせて量子回路を描画しています。内部ではdraw2d というGoのベクターグラフィックスライブラリを利用していますが、特に意識せずに使えます。
JSONで定義された以下のような量子回路のデータ構造
{
"width": 4,
"inits": ["0", "1", "+", "0"],
"ops": [
{
"type": "gate",
"target_qubit": 0,
"name": "h"
},
{
"type": "gate",
"target_qubit": 1,
"name": "x"
},
...
}
を描画するには、
cd example/draw
go run main.go
を実行してみてください。同一ディレクトリに draw.svg という名前で量子回路が描画されたSVGファイルが生成されます。
taskというタスクランナーを導入されていれば、同様の処理がトップディレクトリから
task draw
で実行できます。
描画の設定は Config struct と DiagramConfig struct で行えます。
type Config struct {
FontPath string
OutputSVGPath string
}
type DiagramConfig struct {
Margin float64
LineSpacing float64
TimeSpacing float64
GateBlockSize float64
FontName string
FontSize int
ShowBarriers bool
BarrierLineOffset float64
CxRadius float64
MeasureRadius float64
MeasureCenterOffsetY float64
MeasureZeroOffset float64
MeasurePointerOffset float64
MeasurePointerLength float64
Scale float64
}
DiagramConfig struct の各フィールドを調整することで、量子回路の見た目を変更できます。基本的にはデフォルトの設定で、 Config.OutputSVGPath を設定して保存先を変更するだけで十分です。
Marshal機能
次は、Goのデータ構造をJSON形式に変換する機能を見てみましょう。
元になるGoのデータ構造は以下のようになっています。
&gqcirc.QC{
Width: 4,
Ops: []gqcirc.Op{
&gqcirc.Gate{
TargetQubit: uint32Ptr(0),
Name: "h",
},
&gqcirc.Gate{
TargetQubit: uint32Ptr(1),
Name: "x",
},
&gqcirc.Gate{
TargetQubit: uint32Ptr(0),
Name: "z",
},
...
これをJSON形式に変換するには、
cd gqcirc/example/marshal
go run main.go
を実行してみてください。標準出力にJSON形式で量子回路が出力されます。
{
"width": 4,
"ops": [
{
"type": "gate",
"target_qubit": 0,
"name": "h"
},
{
"type": "gate",
"target_qubit": 1,
"name": "x"
},
{
"type": "gate",
"target_qubit": 0,
"name": "z"
},
...
taskで実行する場合は、
task marshal
を実行してください。
量子回路のデータ構造
ライブラリ内部での量子回路の表現方法について説明します。
QC
gqcirc/qc.go に量子回路を表現するためのstruct QC が定義されています。
type QC struct {
Width uint32 `json:"width"`
Inits []string `json:"inits,omitempty"`
Ops Ops `json:"ops"`
}
- Width: 量子ビット数
- Inits: 量子ビットの初期状態
-
0、1、+、-、i、-iなどの任意の文字列を指定できます。描画されるとブラケット内に配置されます。
-
- Ops: 量子ゲートや測定、バリアなどの操作のリスト
-
Opのスライスで定義されています。Opについては次のセクションで説明します。
-
Op
Op は量子回路上の操作を表現するためのinterfaceです。以下のstructで実装されています。
Gate
量子ゲートを表現するためのstructです。
type Gate struct {
OpType string `json:"type" mapstructure:"type"`
ControlQubits []*uint32 `json:"control_qubits,omitempty" mapstructure:"control_qubits"`
TargetQubit *uint32 `json:"target_qubit" mapstructure:"target_qubit"`
Name string `json:"name" mapstructure:"name"`
}
- OpType:
SetType()を使うことで常に"gate"が指定されます。 - ControlQubits: 制御量子ビットのインデックスのスライス。
- TargetQubit: ターゲット量子ビットのインデックス。制御量子ビットがない場合は、操作対象の量子ビットになります。
- Name: 任意の量子ゲートの名前。例えば、
h、x、cxなど。
Measure
量子ビットの測定を表現するためのstructです。
type Measure struct {
OpType string `json:"type" mapstructure:"type"`
TargetQubit *uint32 `json:"target_qubit" mapstructure:"target_qubit"`
}
- OpType:
SetType()を使うことで常に"measure"が指定されます。 - TargetQubit: 測定対象の量子ビットのインデックス。
Barrier
量子ビット間のバリアを表現するためのstructです。
type Barrier struct {
OpType string `json:"type" mapstructure:"type"`
}
- OpType:
SetType()を使うことで常に"barrier"が指定されます。
量子ゲートの並べ方について
Op のスライスは、量子回路の上の量子ビットから Op が順番に適用されるように並べられています。
しかし、量子計算上の意味が同じでも、量子ゲートを縦一列に並べたいことがあります。
例えば、以下のような量子回路を考えます。
{
"width": 3,
"inits": ["0", "1", "+", "0"],
"ops": [
"type": "gate",
"target_qubit": 0,
"name": "A1"
},
{
"type": "gate",
"target_qubit": 1,
"name": "A2"
},
{
"type": "gate",
"target_qubit": 0,
"name": "B1"
},
{
"type": "gate",
"target_qubit": 1,
"name": "B2"
},
{
"type": "gate",
"target_qubit": 2,
"name": "C"
}
]
このとき、C を B1 と B2 の下に揃えたいことがあります。
そのときは Barrier を A1 と A2 の後ろに置くと、それ以降の量子ゲートは次の列に並ばせることができます。
{
"width": 3,
"inits": ["0", "1", "+", "0"],
"ops": [
"type": "gate",
"target_qubit": 0,
"name": "A1"
},
{
"type": "gate",
"target_qubit": 1,
"name": "A2"
},
{
"type": "gate",
"target_qubit": 0,
"name": "B1"
},
{
"type": "gate",
"target_qubit": 1,
"name": "B2"
},
{
"type": "barrier",
},
{
"type": "gate",
"target_qubit": 2,
"name": "C"
}
]
BarrierをConfigを使って見えないようにするとすっきりします。
conf.ShowBarriers = false
発展:量子計算と同時に量子回路描画
Goの量子計算シミュレータにqというライブラリがあります。
このライブラリとgqcircを組み合わせて、量子計算の結果を量子回路として描画する例を紹介します。
gacirc/example/q というディレクトリにサンプルコードを用意しています。
import (
"fmt"
"github.com/itsubaki/q"
"github.com/massn/gqcirc"
)
...
type QCembed struct {
*gqcirc.QC
*q.Q
}
以上のようにq.Qと gqcirc.QCを埋め込んだ構造体を定義します。
各量子ゲートを以下のように定義してやる必要があるので少し面倒くさもありますが、最初だけなので。
func (qce *QCembed) H(qb ...q.Qubit) *QCembed {
for _, v := range qb {
qce.Q.H(v)
qce.QC.Ops = append(qce.QC.Ops, &gqcirc.Gate{
Name: "H",
TargetQubit: uint32Ptr(uint32(v.Index())),
})
}
return qce
}
おわりに
量子回路をGoで描画するライブラリ gqcirc を紹介しました。
この名前はGeneral/Go Quantum Circuitの略となっていて、様々な環境で量子回路を取り扱いやすくなることも目指しています。
量子回路の表現としてはOpenQASMが有名ですが、JSON形式で量子回路を表現することで、より柔軟に量子回路を取り扱えるようになることを期待しています。



