Q#入門するのに量子データ型の扱いを学ばない奴はおらんやろ

# 前書き
もう、まとめきれんから自分用という事でお願いします。


# 環境
WSL2のdocker,docker-composeから環境を作成していきます。(前も記載したが少し変更)

```
/q-sharp-train                       
┣ src/                  # Q#言語のプロジェクトが格納される場所
┃   ┗ ...               
┣ .editorconfig         # 省略
┣ .gitignore            # 省略
┣ docker-compose.yml    # Q#環境イメージ
┣ makefile              # ヘルパーコマンド
┗ README.md             # 省略
```

## docker-compose.ymlについて
イメージのタグについて、ここを[参照](https://mcr.microsoft.com/v2/quantum/iqsharp-base/tags/lis)してください。
```yml
version: '3'
services:
  q:
    image: mcr.microsoft.com/quantum/iqsharp-base:0.25.218240
    working_dir: /app
    tty: false
    volumes:
      - ./src:/app
```

## Makefileについて

このファイルで行うのは、以下の４つの対応を行います。
- Q#のテンプレートプロジェクトの一覧表示
- Q#のテンプレートプロジェクトから作成
- Q#プロジェクトの実行・出力
- Q#プロジェクトの出力削除

```makefile
show/projects:
	@docker-compose run --rm q dotnet new -i Microsoft.Quantum.ProjectTemplates -n console

project/%:
	@docker-compose run --rm q dotnet new $(*D) -lang Q# -o $(@F)

run/%:
	@docker-compose run --rm q dotnet run --project $(@F) --configuration $(*D)

clean/%:
	@docker-compose run --rm q dotnet clean $(@F) --configuration $(*D)

test/%:
	@docker-compose run --rm q /bin/bash -c "cd $(@F) && dotnet restore && dotnet test"

```

#### 操作例

```bash
# Q#のテンプレートプロジェクトの一覧表示
root@pc-name:/mnt/q-sharp-train# make show/projects            
The following template packages will be installed:
   Microsoft.Quantum.ProjectTemplates

...
Template Name                  Short Name     Language  Tags
-----------------------------  -------------  --------  --------------
Class library                  classlib       Q#        Common/Library
Console Application            console        Q#        Common/Console
Quantum Application Honeywell  azq-honeywell  Q#        Common/Console
Quantum Application IonQ       azq-ionq       Q#        Common/Console
xUnit Test Project             xunit          Q#        Test/xUnit

# Q#のテンプレートプロジェクトから作成 <consoleプロジェクトを作成する例>
root@pc-name:/mnt/q-sharp-train# project/console/sample
The template "Console Application" was created successfully.

# Q#プロジェクトの実行・出力
root@pc-name:/mnt/q-sharp-train# make run/Debug/sample
Hello quantum world!

# Q#プロジェクトの出力削除
root@pc-name:/mnt/q-sharp-train# make clean/Debug/sample
...
     1>Project "/app/sample/sample.csproj" on node 1 (Clean target(s)).
     1>CoreClean:

# Q#プロジェクトの単体テスト(xUnit)
root@pc-name:/mnt/q-sharp-train# make test/test-sample
  Determining projects to restore...
  Restored /app/test-sample1/test-sample1.csproj (in 5.27 sec).
  Restored /app/test-sample1/test-sample1.csproj (in 6.57 sec).
  ...
  Test passed.

  Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: < 1 ms - /app/test-sample1/bin/Debug/net6.0/test-sample1.dll (net6.0)
```

# 参考

これから説明する内容は全て以下の公式から情報を読み取り個人の感想を入れた物になる為
優秀な方は公式を直接読み取りマイクロソフトのドキュメントを読む練習でもした方が良いと思います。
- [Q#での量子データ型](https://docs.microsoft.com/ja-jp/azure/quantum/user-guide/language/typesystem/quantumdatatypes)
- [Q#での量子メモリの管理](https://docs.microsoft.com/ja-jp/azure/quantum/user-guide/language/statements/quantummemorymanagement)
- [Q# プログラミング言語と Quantum 開発キットとは?](https://docs.microsoft.com/ja-jp/azure/quantum/user-guide/language/statements/quantummemorymanagement)

# 量子ドメイン

それでは、練習用にコンソールプロジェクトを作成し
そこで量子データを扱い方を勉強していきたいと思います。

```bash
root@pc-name:/mnt/q-sharp-train# project/console/sample
The template "Console Application" was created successfully.
```

Q#では以下の３つを押さえておけば、量子を扱えるのでとっても簡単です。（大嘘）
| 型名 | 概要 |
|---|---|
| ```Qubit``` |量子ビットを表す定義リテラルです。Qubitの値はアロケーション操作を行い取得します。 Qubitの演算子は対等比較のみ可能です。 一度に 1 つまたは複数の量子ビットを割り当てることができます。 |
| ```Result``` |二値量子測定結果の定義リテラルです。ZeroとOneのいずれかが定義されます。Result が Zero の場合は +1 の固有値が測定されたことを示し、Result が One の場合は -1 の固有値が測定されたことを示します。|
| ```Pauli``` |このリテラルは次の4つのいずれかが定義されるリテラルになります。(PauliI, PauliX, PauliY, PauliZ) 相対位相の回転 |


### Qubit
量子ビット(Qubit)は２つの状態が存在するみたいです。

| 状態名 | 概要 |
|---|---|
| クリーン | もつれていない(0,1のいずれかの状態の判別ができる状態)、かつ計算の別の部分で使用されていない量子ビット |
| ダーティ | 状態が不明で、量子プロセッサのメモリの他の部分ともつれる可能性がある量子ビット |

量子ビットの定義は```use```もしくは```borrow```を使用し定義します。

量子ビットを直接読み込むと、```q:0```や```[q:1, 1:2]```といった値が表示されます。
これは量子ビットが確保された順番になります。値（結果）を読み込むには、```変換```を行います。

単一の量子ビットを読み込むには、```Microsoft.Quantum.Intrinsic```の```M```を使用します。

配列の量子ビットを読み込むには、```Microsoft.Quantum.Arithmetic```の```MeasureInteger```を使用し整数に変換しますが、その前にエンディアンで量子ビットを加工する必要があります。```LittleEndian```や```BigEndian```等を使用します。[詳細](https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.arithmetic#user-defined-types)

この辺の読み込みについては後で勉強しようと思います。


#### Use statements
```use```ステートメントで定義された量子ビットは全て```|0>```で定義されます。
```操作```（```operation```）の中で```use```ステートメントを使用して```Qubit```を定義することが可能です。
```関数```（```function```）の中では量子ビットは定義できません。(QS4007)


1~3までは、現在のoperation内全体のスコープで量子ビットを定義しますが、
4~5は、最小のスコープ```{``` ```}```内のみで定義されます。その為キュビットの18番目（```q:18```）から始まっています。


```C#
namespace sample {

    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Arithmetic;

    operation UseDefine() : Unit {
        let BYTE_SIZE = 8;

        // 1 
        use qbit = Qubit();
        Message($"{qbit}");
        // q:0

        // 2 
        use qbyte = Qubit[BYTE_SIZE];
        Message($"{qbyte} : {qbyte[0]}, {qbyte[1]}");
        // [q:1,q:2,q:3,q:4,q:5,q:6,q:7,q:8] : q:1, q:2

        // 3
        use (qbit1, qbyte1) = (Qubit(), Qubit[BYTE_SIZE]);
        Message($"{qbit1}, {qbyte1}");
        // q:9, [q:10,q:11,q:12,q:13,q:14,q:15,q:16,q:17]

        // 4
        use qbit2 = Qubit() {
            Message($"{qbit2}");
            // q:18
        }
        // Message($"{qbit2}"); QS5022
        
        // 5
        use qbyte2 = Qubit[BYTE_SIZE] {
            Message($"{qbyte2}");
            // [q:18,q:19,q:20,q:21,q:22,q:23,q:24,q:25]
        }
    }

    function ErrUseDefine() : Unit {
        // use q = Qubit(); error QS4007: Using-statements cannot be used in functions.
    }

    @EntryPoint()
    operation Main() : Unit {
        UseDefine();
    }
}
```

#### Borrow statements

```Use statements```と扱い方は一緒ですが、Qubitの並び順が変わってきます。
これ何に使うかわからない。。

また、公式では以下のような特性があることを示しています。
> 量子ビットを一時的に使用できるようにするのに使用されます。特定の状態である必要はありません。

> 量子ビットがバインドされた時点から、その量子ビットが最後に使用されるまで使用されないことが保証されます。


```C#
namespace quantum1 {

    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Arithmetic;

    ...

    operation BorrowDefine() : Unit {
        let BYTE_SIZE = 8;

        borrow qbit = Qubit();
        Message($"{qbit}");
        // q:0

        borrow qbyte = Qubit[BYTE_SIZE];
        Message($"{qbyte} : {qbyte[0]}, {qbyte[1]} ");
        // [q:8,q:7,q:6,q:5,q:4,q:3,q:2,q:1] : q:8, q:7

        borrow (qbit1, qbyte1) = (Qubit(), Qubit[BYTE_SIZE]);
        Message($"{qbit1}, {qbyte1}");
        // q:17, [q:16,q:15,q:14,q:13,q:12,q:11,q:10,q:9]

        borrow qbit2 = Qubit() {
            Message($"{qbit2}");
            // q:25
        }
        // Message($"{qbit2}"); QS5022

        borrow qbyte2 = Qubit[BYTE_SIZE] {
            Message($"{qbyte2}");
            // [q:25,q:24,q:23,q:22,q:21,q:20,q:19,q:18]
        }
    }

    @EntryPoint()
    operation Main() : Unit {

        UseDefine();

        BorrowDefine();
    }
}
```

### 無効なQubit
```関数```や```演算```で以下のように記載するとQubitを定義できますが、値は空っぽですし量子ビットの参照すら行われていない状態みたいです。
```c#
    function InvalidQubit(): Unit {
        mutable q = new Qubit[3];
        Message($"{q}");
        // [,,]
    }
```

## Qubitの操作

#### 単一のQubit
単一のQubitに対しての操作は、[Microsoft.Quantum.Intrinsic](https://docs.microsoft.com/ja-JP/qsharp/api/qsharp/microsoft.quantum.intrinsic)に定義されています。

とりあえず動かしたい私は以下のようなサンプルコードを作成し検証しました。
> 結果出力めんどくさく途中でやめました。。
```C#
namespace qbit {

    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Intrinsic;

    open Microsoft.Quantum.Diagnostics; // for DumpMachine

    operation QubitOperation(op: Qubit => Unit, msg: String) : Unit {
        use q = Qubit() {
            op(q);
            let result = M(q);
            Message($"{msg} : {result}");
            DumpMachine();
        }
    }

    @EntryPoint()
    operation HelloQ() : Unit {

        QubitOperation(Reset, "Reset");
        //Reset : Zero
        //|0⟩      1.000000 +  0.000000 i  ==     ******************** [ 1.000000 ]     --- [  0.00000 rad ]
        //|1⟩      0.000000 +  0.000000 i  ==                          [ 0.000000 ]
        QubitOperation(H, "Reflect H");
        //Reflect H : Zero
        //|0⟩      1.000000 +  0.000000 i  ==     ******************** [ 1.000000 ]     --- [  0.00000 rad ]
        //|1⟩      0.000000 +  0.000000 i  ==                          [ 0.000000 ]
        QubitOperation(X, "Reflect X");
        //Reflect X : One
        //|0⟩      0.000000 +  0.000000 i  ==                          [ 0.000000 ]
        //|1⟩      1.000000 +  0.000000 i  ==     ******************** [ 1.000000 ]     --- [  0.00000 rad ]
        QubitOperation(Y, "Reflect Y");
        QubitOperation(Z, "Reflect Z");
        QubitOperation(T, "Reflect T");
        QubitOperation(I, "Reflect I");
        QubitOperation(S, "Reflect S");

        for pauli in [PauliI, PauliX, PauliY, PauliZ] {
            let theta = 90.0;
            let PauliRTheta = q => R(pauli, theta, q);
            QubitOperation(PauliRTheta, $"R {theta} {pauli}");

            let ExpOp = q => Exp([pauli], theta, [q]);
            QubitOperation(ExpOp, $"Exp {theta} {pauli}");
            
            let numerator = 10;
            let power = 10;
            let ExpFracOp = q => ExpFrac([pauli], numerator, power, [q]);
            QubitOperation(ExpFracOp, $"ExpFrac {pauli} {numerator} {power}");
        }

        for r in [Rx, Ry, Rz] {
            let theta = 90.0;
            let R90 = q => r(theta, q);
            QubitOperation(R90, $"{r} {theta}");
        }

        use (q0, q1) = (Qubit(), Qubit()) {
            X(q0); // q1 = |1>
            SWAP(q0, q1);
            let result0 = M(q0);
            let result1 = M(q1);
            Message($"SWAP({q0},{q1}) = ({result0},{result1})");
            DumpMachine();
        }

        use q = Qubit() {
            let result = Measure([PauliI], [q]);
            Message($"Measure({q}) = {result}");
            DumpMachine();
        }

        use (q, qq) = (Qubit(), Qubit()) {
            let result = Measure([PauliI, PauliI], [q, qq]);
            Message($"Measure({q}, {qq}) = {result}");
            DumpMachine();
        }

        use q = Qubit() {
            Message($"X qubit");
            X(q);
            DumpMachine();
            Message($"reset qubit");
            Reset(q);
            DumpMachine();
        }
    }
}
```

#### 複数のQubit
複数のQubitに対しての操作は以下のようにし検証しました。
```C#
namespace multiple_qubit {

    open Microsoft.Quantum.Canon; // for ApplyToEach
    open Microsoft.Quantum.Intrinsic;
    
    open Microsoft.Quantum.Diagnostics; // for DumpMachine
    open Microsoft.Quantum.Measurement; // for MultiM


    operation QByteOperation(op: Qubit[] => Unit, msg: String, qsize: Int) : Unit {
        use qubyte = Qubit[qsize] {
            op(qubyte);
            let value = MultiM(qubyte);
            Message($"{msg} : {value}");
            DumpMachine();
        }
    }

    operation QByteOperationToEach(op: Qubit => Unit, msg: String, qsize: Int) : Unit {
        QByteOperation(q => ApplyToEach(op, q), msg, qsize);
    }

    @EntryPoint()
    operation HelloQ() : Unit {

        let BYTE_SIZE = 8;

        QByteOperation(ResetAll, $"Rset ALL", BYTE_SIZE);
        QByteOperationToEach(H, $"Reflect {H}", BYTE_SIZE);
        QByteOperationToEach(X, $"Reflect {X}", BYTE_SIZE);
        QByteOperationToEach(Y, $"Reflect {Y}", BYTE_SIZE);
        QByteOperationToEach(Z, $"Reflect {Z}", BYTE_SIZE);
        QByteOperationToEach(T, $"Reflect {T}", BYTE_SIZE);
        QByteOperationToEach(I, $"Reflect {I}", BYTE_SIZE);
        QByteOperationToEach(S, $"Reflect {S}", BYTE_SIZE);

        let pauliAllList = [PauliX, PauliY, PauliZ, PauliI];

        for pauli in pauliAllList {
            let theta = 180.0;
            let PauliRTheta = (q) => R(pauli, theta, q);
            QByteOperationToEach(PauliRTheta, $"{R} {theta} {pauli}", BYTE_SIZE);

            let ExpOp = q => Exp([pauli, size=BYTE_SIZE], theta, q);
            QByteOperation(ExpOp, $"{Exp} {theta} {pauli}", BYTE_SIZE);

            let numerator = 10;
            let power = 10;
            let ExpFracOp = q => ExpFrac([pauli, size=BYTE_SIZE], numerator, power, q);
            QByteOperation(ExpFracOp, $"{ExpFrac} {pauli} {numerator} {power}", BYTE_SIZE);
        }

        for r in [Rx, Ry, Rz] {
            let theta = 90.0;
            let RTheta = q => r(theta, q);
            QByteOperationToEach(RTheta, $"{r} {theta}", BYTE_SIZE);
        }

        for pauli in pauliAllList {
            use q = Qubit[BYTE_SIZE] {
                let result = Measure([pauli, size=BYTE_SIZE], q);
                Message($"Measure {pauli} {result}");
                ResetAll(q); // 重要ポイント１
            }
        }

    }
}
```
重要ポイント１は何かというと、ループ内の```use```ステートメントのQubitが再利用されている為
ResetAllで初期化しないとコンパイルエラーになってしまうみたいです。

スコープ周りの挙動が少し怪しいかも。。

# おまけ1
エセプログラマーの私は以下のようなことを思い

> Qubitの初期値は任意にできないのか？

該当する```演算```や```関数```が存在しなさそうだったので（あっても役に立たないから？）
自作してみようと思います。

今回は単体テストベースで作成してみます。

```bash
root@pc-name:/mnt/q-sharp-train# make project/xunit/testSample
```
作成したプロジェクト内に、```任意初期化できる演算```のファイルを作成します。
```c#:Initilize.qs
namespace Initilize {

    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Intrinsic;

    open Microsoft.Quantum.Arrays;

    /// # Summary
    /// 単一の量子ビットを任意のRsult型に合わせる。
    ///
    /// # Input
    /// ## v (Result)
    /// 初期化したいQubitの値を指定
    /// ## q (Qubit)
    /// 初期化対象のQubit
    ///
    /// # Example
    /// ```Q#
    /// let q = Qubit();
    /// Set(One, q);
    /// ```
    ///
    operation Set(v: Result, q: Qubit) : Unit {
        Reset(q);
        if v == One {
            X(q);
        }
    }

    /// # Summary
    /// 複数の量子ビットをResult配列で初期化を行う
    /// 
    /// # Input
    /// ## v (Result[])
    /// 初期化したいQubitの値を指定
    /// ## q (Qubit[])
    /// 初期化対象のQubit配列
    ///
    /// # Example
    /// ```Q#
    ///  let q = Qubit[2];
    ///  MultiSet([One, Zero], q);
    /// ```
    ///
    operation MultiSet(v: Result[], q: Qubit[]) : Unit {
        if Length(v) != Length(q) {
            fail "sized do not match.";
        }

        ResetAll(q);
        for idx in IndexRange(v) {
            if v[idx] == One {
                X(q[idx]);
            }
        }
    }

}
```
次にProgram.qsがデフォルトで用意されていると思いますが、
自分は別途TestInitilize.qsを作成し、以下のテストコードを作成しました。
```c#:TestInitilize.qs
namespace TestInitilize {

    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Diagnostics;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Arrays;
    open Microsoft.Quantum.Measurement; // for MultiM
    open Microsoft.Quantum.Arithmetic; // for LittleEndian

    open Initilize;

    /// # Summary
    /// AssertQubitの配列対応型
    ///
    /// # Input
    /// ## expected (Result[])
    /// 予想値
    /// ## q (Qubit[])
    /// 状態がアサートされているQubit配列
    ///
    operation AssertAllQubit (expected : Result[], q : Qubit[]) : Unit is Adj + Ctl {
        for (l, r) in Zipped(expected, q) {
            AssertQubit(l, r);
        }
    }

    @Test("QuantumSimulator")
    operation SetOneQubit() : Unit {
        let results = [One, Zero];
        use q = Qubit();
        for result in results {
            Set(result, q);
            AssertQubit(result, q);
        }
        Reset(q);
        Message("Test passed.");
    }

    @Test("QuantumSimulator")
    operation MultiSetQubit() : Unit {
        let ARRAYSIZE = 4;
        let rArrays = [
            [One, size = ARRAYSIZE],
            [Zero, size = ARRAYSIZE],
            [One, Zero, One, One]
        ];

        use qArray = Qubit[ARRAYSIZE * Length(rArrays)];
        mutable start = 0;
        mutable end = 0;
        for idx in IndexRange(rArrays) {
            set start = idx * ARRAYSIZE;
            set end = (start + ARRAYSIZE) - 1;
            let q = qArray[start..end];
            MultiSet(rArrays[idx], q);
            AssertAllQubit(rArrays[idx], q);
        }
        ResetAll(qArray);
        Message($"Test passed.");
    }
}
```
作成後、以下のコマンドを実行
```bash
> root@pc-name:/mnt/q-sharp-train# make test/testSample
  Determining projects to restore...
  Restored /app/testSample/testSample.csproj (in 3.93 sec).
  Restored /app/testSample/testSample.csproj (in 5.46 sec).
  Determining projects to restore...
  All projects are up-to-date for restore.
  Determining projects to restore...
  All projects are up-to-date for restore.
  
  ____________________________________________

  Q#: Success! (0 errors, 0 warnings) 

  testSample -> /app/testSample/bin/Debug/net6.0/testSample.dll
Test run for /app/testSample/bin/Debug/net6.0/testSample.dll (.NETCoreApp,Version=v6.0)
Microsoft (R) Test Execution Command Line Tool Version 17.2.0 (x64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Test passed.
Test passed.

Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 27 ms - /app/testSample/bin/Debug/net6.0/testSample.dll (net6.0)
```

ここでも、テスト終わりにReset系の処理を行わないと一度通ったテストがエラーになります。
よくわかりません。ｗ

# おまけ2
Q#のプロジェクトで追加のパッケージ設定したときとキャッシュのクリア方法について
### パッケージ追加
```
dotnet add <project name> package <package name>
```
### キャッシュのクリア
パッケージ追加でエラー出たときに一応、クリアして動かしてみると成功する可能性があります。
その他は[ここ](https://docs.microsoft.com/ja-jp/nuget/consume-packages/managing-the-global-packages-and-cache-folders#clearing-local-folders)を参照
```
dotnet nuget locals all --clear
```

# 所感

量子ビットについてまとめきれましぇん。。。

パウリ周り全く分からないのでその辺の勉強しようと思います。
それとは別にテストケースを属性の引数で指定できるようにできないかな？