TensorNetworkCodes.jlの勉強

# はじめに
[TensorNetworkCodes.jl](https://github.com/qecsim/TensorNetworkCodes.jl)は、T. Farrelly, *et. al.*, [Local Tensor-Network Codes](https://iopscience.iop.org/article/10.1088/1367-2630/ac5e87/meta), New J. Phys. 24 043015(2022)の手法をJuliaでパッケージ化したものである。
大雑把にいうと、量子誤り訂正符号のStabilizer generatorと論理operatorを、物理量子ビットと論理量子ビットの自由度を足と思ったテンソル表現に書き出して、コントラクトを取ると新しい符号が生成できるよって手法です。
研究でを使うことになったので、自分用の備忘録として日本語のライブラリの解説を残しておきます。
(日本人で使ってる人はほぼいないと思うが)間違いがあれば教えてください。

詳細は[英語版のdocumentation](https://qecsim.github.io/TensorNetworkCodes.jl/stable/)を読んでください。

# codeを表す型の種類
TNCライブラリには符号を表す型が以下の3つある。
- `Quantum code`
- `SimpleCode`
- `Tensor-network code`

`Quantum code`は全てのcodeとcontractが取れる型。Abstract supertype for quantum error correcting codesだと思えばいい。
`SimpleCode`は`Tensor-network code`のSeed codeとして使うことができるbasic imolementation。
`Tensor-network code`は`CodeGraph`に関係している型

## Quantum code
QuantumCode型は、quantum error correction codesのAbstruct supertype
Subtype(つまり、Simple CodeとかTensor-network codeとか)は、以下の変数を含んでいるべきである。
```
stabilizers::Vector{Vector{Int}}
logicals::Vector{Vector{Int}}
pure_errors::Vector{Vector{Int}}
```
`stabilizer`は、linearly independentなstabilizer generatorsの集合で、`[[1, 3, 3, 1, 0], [0, 1, 3, 3, 1], [1, 0, 1, 3, 3], [3, 1, 0, 1, 3]]`みたいに書く。(0,1,2,3はI,X,Y,Z)
`logicals`は、representatives of logical operators ordered as $X$-type, $Z$-type, ... for each logical qubit.
`pure_errors`(or destabilizers) は対応するstabilizer生成元とは非可換だが、その他の生成元とは可換な演算子

## Simple code
SimpleCodeは、`SimpleCode(name, stabilizers, logicals, pure_errors)`の関数で生成できる。
- `name`: strで名前をつけれる
- `stabilizers`: `Quantum code`と同様に配列でstabilizerを指定する
- `logicals`: `Quantum code`と同様に配列で論理Stabilizerを指定する
- `pure_errors`: `Quantum code`と同様に配列でpure errorを指定する

Simple code自体は`name`だけを持っていて、他の変数は`QuantumCode`が持ってるイメージ。

`~/src/exapmles/simple.jl`に記述されているsimple codeは、比較的小さなcodeをがビルトインされている。
以下にはその例を挙げておく。

- `five_qubit_code()`: 最小構成の誤り訂正符号(と信じられているもの)
- `five_qubit_surface_code()`: 表面符号の最終構成、code distance 2の符号
- `steane_code()`: 7-qubit codeと呼ばれる、CSS符号の最小構成
- `random_code(n, k)`: ランダム符号と呼ばれる、n-qubitsでcode-distance kの符号を生成する。根元のrandom状態は `random_stabilizer_state(n)`で生成
- `random_stabilizer_state(n)`: n-qubitのrandom stabilzier stateの生成

## Tensor-network code
Tensor-network code型は、添え字や`ITensor`の係数を含む"TensorNetworkCodeで使用するすべての幾何学データをまとめた型である。
`TensorNetworkCode`は`QuantumCode`のsubtypeなので、`TensorNetworkCode <: QuantumCode`になっている。
> <:演算子は、Juliaでの型のサブタイプ（subtype）関係を示す。
> 具体的には、TensorNetworkCodeはQuantumCodeのサブタイプであることを意味している。
> これは、TensorNetworkCodeがQuantumCodeのすべてのメソッドやフィールドを持ち、QuantumCodeの型のすべてのインスタンスがTensorNetworkCodeの型として扱えることを意味している。

TensorNetworkCodeは、`TensorNetworkCode(stabilizers, logicals, pure_errors, code_graph, seed_codes)`で生成できて、`QuantumCode`と比べると、以下の変数が追加されている。
```
code_graph::CodeGraph
seed_codes::Dict{String,SimpleCode}
```
- `code_graph`: 物理量子ビットのレイアウトに関す情報を格納
- `seed_codes`: the tensor-network codeを構築するために使うsimple codesのDict。

`~/src/examples/surface.jl`に記述されているsimple codeは、比較的小さなcodeをがビルトインされている。
以下にはその例を挙げておく。それぞれの意味はdocumentationや論文、サンプルのjupytorノートにあり。

`almost_rotated_surface_code(L::Int64, input_seed_code::SimpleCode, input_coords::Array{Int64}) -> TensorNetworkCode`: code distance Lの表面符号に他のcodeをdopeしたTNCを生成
`rotated_surface_code(L::Int64) -> TensorNetworkCode`: code distance Lの回転符号のTNCを生成
`surface_code(L::Int64)`: code distance Lの表面符号のTNCを生成

# 操作
ここでは、Tensor-network codesに対する操作を列挙していく
- `verify(code)`: 符号がQECとしての条件を満たしているのかチェックしてくれる。
- `combine(code0, code1)`: 2つのcodeのテンソル積状態に対応する新しい符号を返す。異なる物理量子ビットのセット上にある2つの符号と物理的には等価である。
- `fusion(code, qubit_pair)`: quantumCodeのうちの指定した2つの足をfusionする。物理的には[Type-II fusion gate](https://arxiv.org/abs/0705.4193v1)みたいに、$XX$と$ZZ$のパリティ測定をした後にstabilizerを更新するのでこの名前。
- `contract(code0,code1, contract_pair)`: TensorNetworkCode同士をcontractする。これは`combine`してから`fusion`してるのと同じ
- `gauge(code::SimpleCode, logical_qubit::Int, logical_pauli::Int) -> SimpleCode`
- `gauge(code::TensorNetworkCode, logical_qubit::Int, logical_pauli::Int)-> TensorNetworkCode` : `logical_qubit::Int`を`logical_pauli::Int`でゲージ固定する。code $[[n,k,\*]]$が与えられた時に上からi組目の論理演算子のうちの論理X,Y,Z (1,2,3で指定)でゲージ固定した新しい符号$[[n,k-1,\*]]$を返す ($k-1=0$のなった場合はstabilizer状態のStabilizerが帰ってくる。)。

-  `contract_by_coords(code0_TN,code2_TN)`: 座標が同じ物理量子ビット同士をontractする。
- `set_coords!(TensorNetworkCode, coordinates)`: `TensorNetworkCode`型に座標を登録する。座標の配列順序はTNC内部の`nodes`で表現される順番に対応する。
- `shift_coords!(TensorNetworkCode, shifts)`: 全ての座標をシフトする。
- `purify(code::SimpleCode)`: $[[n,k,*]]$を表す`SimpleCode`型のcode tensorを$[[n+k,0]]$の`SimpleCode`に変換する関数。この時、$[[n+k,0]]$code tensorは単に論理量子ビットの自由度に対応する$k$個の足が先頭に追加されるだけで、Stabilizer状態を表すStabilizer群には基本的にならない。直感的には、[TensorNetwork Codes](https://arxiv.org/abs/2009.10329)のTABLE. 1みたいな感じ。

# 具体的な使い方
5-qubit code[[5,1,3]]とStaeneの7-qubits code [[7,1,3]]をコントラクトして、新しい符号を作ってみる。
```julia:入力
# ライブラリのimport
using TensorNetworkCodes
```
Five qubit codeの生成
```julia:入力
# SimpleCode型のfive qubit codeを生成
five_qubit = five_qubit_code();
# stabilizer generatorの一覧
five_qubit.stabilizers
```
```:出力
4-element Vector{Vector{Int64}}:
 [1, 3, 3, 1, 0]
 [0, 1, 3, 3, 1]
 [1, 0, 1, 3, 3]
 [3, 1, 0, 1, 3]
```
```julia:入力
# 論理operatorsの確認
five_qubit.logicals
```
```:出力
2-element Vector{Vector{Int64}}:
 [1, 1, 1, 1, 1]
 [3, 3, 3, 3, 3]
```
Staene codeの生成
```julia:入力
five_qubit= five_qubit_code();
five_qubit.stabilizers
```
```:出力
4-element Vector{Vector{Int64}}:
 [1, 3, 3, 1, 0]
 [0, 1, 3, 3, 1]
 [1, 0, 1, 3, 3]
 [3, 1, 0, 1, 3]
```
```julia:入力
five_qubit.logicals
```
```:出力
2-element Vector{Vector{Int64}}:
 [1, 1, 1, 1, 1]
 [3, 3, 3, 3, 3]
```
```julia:入力
steane = steane_code();
steane.stabilizers
```
```:出力
6-element Vector{Vector{Int64}}:
 [1, 0, 0, 1, 0, 1, 1]
 [0, 1, 0, 1, 1, 0, 1]
 [0, 0, 1, 0, 1, 1, 1]
 [3, 0, 0, 3, 0, 3, 3]
 [0, 3, 0, 3, 3, 0, 3]
 [0, 0, 3, 0, 3, 3, 3]
```
```julia:入力
steane.logicals
```
```:出力
2-element Vector{Vector{Int64}}:
 [1, 1, 1, 1, 1, 1, 1]
 [3, 3, 3, 3, 3, 3, 3]
```

codeをTensor-Network codesに変換
```julia:入力
five_qubit_TN = TensorNetworkCode(five_qubit)
steane_TN = TensorNetworkCode(steane)
```
適当な物理qubitの足のcontractionをとると、合計で12-qubitあった物理qubitのうちの2箇所がcontractされて、[[10,2,3]]の符号が新たにできる。
```julia:入力
new_code_TN = contract(five_qubit_TN, steane_TN, [[1,2]])
v = verify(new_code_TN)
println("This is a QEC code?: $v")
```
```:出力
This is a QEC code?: true
```
`TensorNetworkCodes.TNDistance`を使えばcode distanceも計算できる。
```julia:入力
using TensorNetworkCodes.TNDistance

dist = tn_distance(new_code_TN)
n = num_qubits(new_code_TN)
k = Int64(length(new_code_TN.logicals)/2)

println("This is a [[$n,$k,$dist]] code!")
```
```:出力
This is a [[10,2,3]] code!
```
新しく作ったTensor-Network codeの描画も可能
```julia:入力
plot_code(new_code_TN)
```
![スクリーンショット 2023-09-13 18.34.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609832/66c6a044-aef6-a2a5-f60e-d174f8191593.png)

# まとめ
Tensor-Network codesは面白いので、みんなも使って遊んでみよう ！