Q#で量子乱数ジェネレータつくって湯婆婆を実装してみる



# はじめに
本記事は、[Microsoft Azure Tech Advent Calendar 2020](https://qiita.com/advent-calendar/2020/microsoft-azure-tech) の投稿です。今回は流行りに乗っかって Q# つかって湯婆婆を実装してみます。
湯婆婆ネタでは、名前を奪う部分のロジックで各種プログラム言語の乱数メソッドを使ってランダム値を取得しているかと思いますが、それらは厳密には乱数ではなくあくまで擬似乱数かと思います。今回は、Q# で量子の性質を利用し完全な乱数を生成するロジックで名前を奪ってみようと思います。

# 実装に入る前に
Q# は量子アルゴリズムを開発および実行するための Microsoft のオープンソース プログラミング言語です。Q# は量子シミュレーターなど量子アプリケーションを開発するためにの標準のツールを含んでいる [Microsoft Quantum Development Kit(QDK)](https://docs.microsoft.com/ja-jp/quantum/quickstarts/) に付随しています。
現在一部の利用者に対する Limited Preview の段階ですが、Azureでは Azure Quantum というクラウドベースの量子コンピューティングサービスが提供されています。まだ身近でない技術ですが、きっと近い将来、今回作った湯婆婆が Azure Quantum を通じて量子ハードウェア上で実行できることになるはずです。（おそらく

Azure Quantum 
https://azure.microsoft.com/ja-jp/services/quantum/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192113/facb7fd9-5a20-6a4e-e748-e0f9ab532f58.png)

# ソースコード
ソースコードは以下です。Projectなどの一式は[こちら](https://github.com/y10e/yubaba-qsharp)に置いておきました。

```Program.cs
using System;
using System.Threading.Tasks;
using Microsoft.Quantum.Simulation.Simulators;
using Quantum.Samples.RandomGen;

namespace Quantum.Samples.Yubaba
{
    class Yubaba
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("契約書だよ。そこに名前を書きな。");
            String name = Console.ReadLine();
            Console.WriteLine($"フン。{name}というのかい。贅沢な名だねぇ。");

            using var sim = new QuantumSimulator(); //量子シミュレータの生成
            var randomNum = await CreateRandomNumberInRange.Run(sim, name.Length-1); //Q#メソッドの call

            var newName = name[(int)randomNum];
            Console.WriteLine($"今からお前の名前は{newName}だ。いいかい、{newName}だよ。分かったら返事をするんだ、{newName}!!");
        }
    }
}
```

```Library.qs
namespace Quantum.Samples.RandomGen {
    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Math;
    open Microsoft.Quantum.Measurement;
    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Intrinsic;

    // Random bit Generator
    operation CreateQuantumRandomNumberGenerator() : Result {
        using (q = Qubit())  {  // qubit を割り当てる
            H(q);               // qubit を初期化し重ね合わせ状態とする
            return MResetZ(q);  // qubit の値を観測する
        }
    }

   // Random number Generator
    operation CreateRandomNumberInRange(max : Int) : Int {
        mutable bits = new Result[0]; // bit 列の定義
        for (idxBit in 1..BitSizeI(max)) {
            set bits += [CreateQuantumRandomNumberGenerator()]; //生成した乱数bitを結合させる
        }
        let result = ResultArrayAsInt(bits);
        return result > max
               ? CreateRandomNumberInRange(max)
               | result;
    }
}
```


# コードの解説
残念ながら Q# では、コンソール入力をインタラクティブに受け取る機能がなさそうだったので、ホストプログラムという形で Q# (Program.qs) の量子乱数生成を別のプログラムから呼び出す形を採用しました。
ホストには、C# 以外にも python なども利用できるみたいですが、今回は C# (Program.cs)にしました。

![hostprograms_host_program_diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192113/db4118a9-71df-df45-d6ff-7d2652c6f870.png)


Yuababa.cs はほとんどただのコンソールアプリですが、using で Q# のnamespace を指定しているところと、 CreateRandomNumberInRange.Runで Q# 側のメソッドを呼び出しているところが独特です。
Q# 側のメソッドを呼び出す際は、[量子シミュレータインスタンス](https://docs.microsoft.com/ja-jp/quantum/user-guide/machines/#quantum-machine-classes)を第一引数に取る必要があり、第二引数にメソッドに定義されたint値（乱数生成の範囲上限）を指定しています。
一方、Library.qs が、量子乱数生成の本体になります。CreateQuantumRandomNumberGeneratorで乱数bitを生成し、CreateRandomNumberInRangeはこれを呼び出して指定された10進数の範囲で収まる bit 列を作成して乱数値をreturn します。
CreateQuantumRandomNumberGeneratorはたかだか3行のメソッドですが、ここが量子コンピューティングな部分になります。Qubitは、重ね合わせに状態になることができる量子情報の単位です。観測されたときに Qubit は 0 か 1 のいづれかの状態に確定しますが、測定前はいづれの値に確定するか確率的な状態にあります。この確率的な状態を利用して乱数 bit の生成を行っています。[H()](https://docs.microsoft.com/ja-jp/qsharp/api/qsharp/microsoft.quantum.intrinsic.h)は、Hadamard 変換をおこなう組み込みの関数でこれによりQubit が重ね合わせの状態にセットされます。次に [MResetZ()](https://docs.microsoft.com/ja-jp/qsharp/api/qsharp/microsoft.quantum.measurement.mresetz)で観測し値を確定させ乱数 bit を生成します。

#実行例
たまたま、千になりました。

```
> dotnet build
> dotnet run -p .\yubaba\ --no-build
契約書だよ。そこに名前を書きな。
荻野千尋  
フン。荻野千尋というのかい。贅沢な名だねぇ。
今からお前の名前は千だ。いいかい、千だよ。分かったら返事をするんだ、千!!
```

# 湯婆婆エラー
文字列を何もいれずに実行するとエラーがでます。
この場合、Q#側のコードで乱数生成の範囲の上限値として-1が渡されるのでエラーになります。

```
Unhandled exception. Microsoft.Quantum.Simulation.Core.ExecutionFailException: `a` must be non-negative
 ---> RandomGen.CreateRandomNumberInRange on C:\Users\yuyon\source\Yubaba\Program.qs:line 19
```

# おわりに
今回の実行例などは Azure Quantum の量子ハードウェア上で実行できていないので、通常のPCで動作させている限りは残念ながらシミュレータによって疑似乱数が生成されているはずです。。
Azure ネタかどうか微妙なところですが（むしろ湯婆婆カレンダーに行くべき?）、いつか Azure Quantum をつかって湯婆婆を実行してみたいと思います。