Kyulacs.jl の紹介

# 本日は

アドベントカレンダー空いてたので書きます．

https://qiita.com/advent-calendar/2022/quantum

[Kyulacs.jl](https://github.com/AtelierArith/Kyulacs.jl) という Python インターフェースとしての 量子回路シミュレータ Qulacs をラップして Julia 側から使えるようにした(非公式な)ライブラリです． 元々は Julia から Python のライブラリを Julia らしく呼ぶための設計を考えるための素材として開発していましたので量子コンピュータに深い理解があるわけではないです． C++/Python に限らずいろんな言語で呼べるといいよね.

# 本家の Qulacs について

量子コンピュータの回路シミュレータ Qulacs の概要，開発の経緯は 5 日目のカレンダーが詳しいです．
ソフトウェア開発の観点からは自分も見習うべきところが多くあり良いプロジェクトだと思います.

https://qiita.com/forest1040/items/5736662de999b5e2e844

回路描画をサポートするライブラリも開発されているようです (6 日目のカレンダー)

https://qiita.com/Hiroya_W/items/5b3ffafd910aa78bacba

# Kyulacs.jl について

以前 Zenn 側で Kyulacs.jl 内部の設計について言及していました． Julia のマクロを使って機械的に Python の関数やクラスを包み込む方法について書いています． 

https://zenn.dev/terasakisatoshi/articles/983a7401524251

幸運なことに有志の方が量子計算をするサンプルを紹介して下さってましたのでこちらも見てみると良いです．

https://zenn.dev/rihitosakurai/articles/fd912e1fe1c986

## 動かし方

さっと環境を作るために Docker を使います． まず下記のようにして Julia の REPL を起動します．

```console
$ docker run --rm -it julia:1.8.3
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.8.3 (2022-11-14)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |
```

その後 `julia>` のプロンプトも含めて丸ごとターミナルにコピー＆ペーストをしてください． 
前半では Python の環境づくりと `qulacs`, `qulacsvis` をインストールしています.
後半では `using Kyulacs` によって機能を現在のセッションにロードして使えるようにしています.

```console
julia> using Pkg
julia> pkg"registry add General https://github.com/AtelierArith/Gallery.git"
julia> Pkg.add("Conda") # Install Conda.jl
julia> using Conda
julia> Conda.pip_interop(true)
julia> Conda.pip("install", "qulacs")
julia> Conda.pip("install", "qulacsvis")
julia> using Kyulacs: Observable, QuantumCircuit, QuantumState
julia> using Kyulacs.Gate: CNOT, Y, merge
julia> state = QuantumState(3)
julia> seed = 0  # set random seed
julia> state.set_Haar_random_state(seed)
julia> circuit = QuantumCircuit(3)
julia> circuit.add_X_gate(0)
julia> merged_gate = merge(CNOT(0, 1), Y(1))
julia> circuit.add_gate(merged_gate)
julia> circuit.add_RX_gate(1, 0.5)
julia> circuit.update_quantum_state(state)
julia> observable = Observable(3)
julia> observable.add_operator(2.0, "X 2 Y 1 Z 0")
julia> observable.add_operator(-3.0, "Z 2")
julia> value = observable.get_expectation_value(state)
```

REPL を使ったチュートリアルを Julia 側のカレンダーに書きました． どうぞ

https://qiita.com/SatoshiTerasaki/items/3c7fcee3a5716b763372

# アップデートしたこと

## 正常に docstring が表示できるようにありました． 

- qulacs 側が提供する docstring の文字列を対応する Julia の API の docstring とみなす仕組み
- Documenter.jl という Julia のドキュメント生成するライブラリとの連携

を作る必要がありました． 前者は PyPlot.jl, SciPy.jl などが採用している `LazyHelp` というdocstringを生成するための型を独自に作成して連携しています. `help?>` モードでアクセスする際に動的に docstring を要請する仕組みをとっています．

後者は Documenter.jl 側と連携する際に docstring が壊れてしまう現象がありました． `docs/make.jl` 側にて Documenter.jl の `mdconvert` のロジックを Kyulacs.jl の `LazyHelp` に対応する必要がありました． 一度文字列を Markdown のオブジェクトとして作成する必要があったようです．

https://github.com/AtelierArith/Kyulacs.jl/blob/5e7852ce69b1064880608d17c34509c33e78eac2/docs/make.jl#L7-L22

```julia
function Documenter.Writers.HTMLWriter.mdconvert(
    h::LazyHelp,
    parent;
    kwargs...,
)
    s = Kyulacs.gendocstr(h)
    # quote docstring `s` to prevent changing display result
    m = Markdown.parse(
        """
        ```
        $s
        ```
        """,
    )
    Documenter.Writers.HTMLWriter.mdconvert(m, parent; kwargs...)
end

mdflatten(io::IOBuffer, h::LazyHelp, md::Markdown.MD) = nothing
```

Julia では multiple dispatch によって他者が作ったライブラリの関数を独自型に対して適用することができます． これにより Documenter.jl に修正を求めずにパッチを適用することができます.

あとは見栄えをよくする必要がありますがこれは純粋に Documenter.jl を使いこなす話に帰着されます． (どれだけ真面目に書くかは執筆者の身体的余裕と精神的余裕にかかっています. 最近は調子悪いのであまり進捗は期待しないでください)

## 回路の描画をサポート (`qulacsvis`) の連携

たまたま `qulacsvis` を見つけたので本家の開発とは独立に機能を提供しようと思って作りました． 本家側のサンプルコードを真似してみましょう．

https://qiita.com/Hiroya_W/items/5b3ffafd910aa78bacba#%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%83%99%E3%83%BC%E3%82%B9%E3%81%A7%E3%81%AE%E6%8F%8F%E7%94%BB%E6%96%B9%E6%B3%95

```julia
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.8.3 (2022-11-14)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> using Kyulacs # QuantumCircuit などが使える
julia> using Kyulacs.Vis # この時点で circuit_drawer が使えるようになる
julia> circuit = QuantumCircuit(3)
julia> circuit.add_X_gate(0)
julia> circuit.add_Y_gate(1)
julia> circuit.add_Z_gate(2)
julia> circuit.add_dense_matrix_gate(
           [0, 1], [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]
       )
julia> circuit.add_CNOT_gate(2, 0)
julia> circuit.add_X_gate(2)
julia> circuit_drawer(circuit)
   ___     ___     ___
  | X |   |DeM|   |CX |
--|   |---|   |---|   |----------
  |___|   |   |   |___|
   ___    |   |     |
  | Y |   |   |     |
--|   |---|   |-----|------------
  |___|   |___|     |
   ___              |      ___
  | Z |             |     | X |
--|   |-------------●-----|   |--
  |___|                   |___|

```

`output_method=mpl` を指定した方法でも遊ぶことができます． ただこの方法だと内部で `plt.show()` も実行していることもあり別画面に描画結果が表示されます. Julia 側からプロセスをコントロールすることができません． 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/5973ecd0-e7dc-967c-13a9-1f34cd0c5fdf.png)

そこで描画アルゴリズムクラス `MPLCircuitlDrawer` を直接指定する方法を使うことにします. 

```julia
# circuit はあらかじめ何かしらの方法で定義されているとする
using PyPlot
drawer = MPLCircuitlDrawer(circuit)
fig = drawer.draw()
plt.show() # これは PyPlot 側が提供している `plt` 関数
```

`plt.show` が面倒であれば `PyPlot` の `Figure` オブジェクトに投げればOKです．

```julia
# circuit はあらかじめ何かしらの方法で定義されているとする
using PyPlot
drawer = MPLCircuitlDrawer(circuit)
Figure(drawer.draw()); # セミコロンをつけて冗長な出力を防ぐ
```

gist 置いておいたので触ってみてね．(・ω・｀)

https://gist.github.com/terasakisatoshi/ed83e42c9ad938dc7188a6308ef73ee4

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/a09db376-3329-bbdf-3e87-b00b8989a7f0.png)

`circuit_drawer` のデフォルトの方法は `TextCircuitDrawer` です. 下記のようにすることもできます.

```julia
julia> drawer = TextCircuitDrawer(circuit)
julia> drawer.draw(verbose=false)
   ___     ___     ___           
  | X |   |DeM|   |CX |          
--|   |---|   |---|   |----------
  |___|   |   |   |___|          
   ___    |   |     |            
  | Y |   |   |     |            
--|   |---|   |-----|------------
  |___|   |___|     |            
   ___              |      ___   
  | Z |             |     | X |  
--|   |-------------●-----|   |--
  |___|                   |___|  
```

(個人的には内部で標準出力せず `str` を返すと素朴なテストが書きやすく嬉しい)

## テストを書く

勉強も兼ねて本家側のテストコードを参考にちまちま書いています.

https://github.com/qulacs/qulacs/blob/main/python/test/test_qulacs.py

# 元気が出たらやりたいこと

最近， ぼっち・ざ・ろっくの顔が壊れたギターヒーロー状態になってるので体と心が回復したら考えていることを書いておこうと思います． 頑張りすぎると日常生活が死んじゃうので慎ましく生きようと思います．

## もう少し API の生成を真面目に行う

現状クラスかどうかは名前が大文字から始まるかどうかで判断しているので SciPy.jl でやったように `inspect` モジュールを使って判定するようにしたい.

https://github.com/AtsushiSakai/SciPy.jl/blob/master/src/scipy_api_list.py

## Example の充実

- せっかく作ったので例を作って潜在的なバグを発見してつぶす作業をしていく．
- Documenter.jl でドキュメントを充実させたいぞい．
あとせっかく型を作ったのに特定のAPIの戻り値が PyObject のままになってるのでAPIの仕様を眺めて適切な型を持つ値を返却する関数を作っていきたい気持ちもあります.
- Interface を Yao.jl と共通化できないかなぁ？
- skqulacs PennyLane の qulacs バックエンド周りの理解

というのを考えています．