演習 パート 2 - 量子乱数ジェネレーターを作成する
このユニットでは、量子乱数ジェネレーターの第 2 フェーズを実装します。つまり、複数の乱数ビットを組み合わせて、より大きな乱数を形成します。 このフェーズは、前のユニットで既に作成した乱数ビット ジェネレーターに基づいています。
複数のランダム ビットを結合して大きな数を形成する
前のユニットでは、量子ビットを重ね合わせにして測定することでランダム ビットを生成するランダム ビット ジェネレーターを作成しました。
量子ビットを測定すると、50% の確率で 0 か 1 の乱数ビットが得られます。 このビットの値は真にランダムであり、測定後に得られる値を把握する方法はありません。 しかし、この動作をどのように使えば、より大きな乱数を生成できるのでしょうか?
このプロセスを 4 回繰り返すと、次のような 2 進数の数列が生成されます。
$${0, 1, 1, 0}$$
これらのビットをビット文字列に連結 (結合) すると、より大きな数値を使用できます。 この例では、ビット シーケンス ${0110}$ は 10 進数の 6 に相当します。
$${0110_{\ 2 進} \equiv 6_{\ 10 進}}$$
このプロセスを何度も繰り返すと、複数のビットを組み合わせてあらゆる大きな数値を形成できます。
乱数ジェネレーターのロジックを定義する
前のユニットで乱数ビット ジェネレーターを構築したという前提で、乱数ジェネレーターのロジックがどうあるべきかの概要について説明します。
- 生成する最大数として
max
を定義します。 max
までの整数を表現するために必要なビット数nBits
を計算して、生成する必要がある乱数ビットの数を定義します。- 長さが
nBits
の乱数ビット文字列を生成します。 - ビット文字列が
max
より大きい数値を表す場合、ステップ 3 に戻ります。 - それ以外の場合、プロセスは終了です。 生成された数値を整数として返します。
例として、max
を 12 に設定します。 つまり、12 は乱数ジェネレーターから取得する最大の数値です。
0 から 12 までの数値を表すには、${\lfloor ln(12) / ln(2) + 1 \rfloor}$ ビット、つまり 4 ビットが必要です。 (簡潔にするために、この式の導出方法は省略します)。
たとえば、ビット文字列 ${1101_{\ binary}}$ を生成したとします。これは ${13_{\ decimal}}$ と等価です。 13 は 12 より大きいため、この処理を繰り返します。
次に、ビット文字列 ${0110_{\ binary}}$ を生成したとします。これは ${6_{\ decimal}}$ と等価です。 6 は 12 未満であるため、この処理を終了します。
量子乱数ジェネレーターは数値 6 を返します。
完全な乱数ジェネレーターを作成する
ここでは、Main.qs
ファイルを拡張して、より大きな乱数を構築します。
必要なライブラリをインポートする
まず、Q# 標準ライブラリからプログラムに必要な名前空間をインポートする必要があります。 Q# コンパイラは多数の一般的な関数と演算を自動的に読み込みますが、完全な量子乱数ジェネレーターには、Microsoft.Quantum.Math
と Microsoft.Quantum.Convert
の 2 つの Q# 名前空間からいくつかの追加の関数と演算が必要です。
次の import
ディレクティブをコピーして、Main.qs
ファイルの先頭に貼り付けます。
import Microsoft.Quantum.Convert.*;
import Microsoft.Quantum.Math.*;
Main
演算の名前を GenerateRandomBit
に変更する
完全な乱数ジェネレーターの場合は、前のユニットで定義した演算を再利用します。 ただし、演算名 Main
はプログラムのエントリ ポイントであり、一意である必要があります。 混乱を避けるため、Main
演算の名前を GenerateRandomBit
に変更する必要があります。
GenerateRandomBit
演算は次のようになります。
operation GenerateRandomBit() : Result {
// Allocate a qubit.
use q = Qubit();
// Set the qubit into superposition of 0 and 1 using the Hadamard
H(q);
// Measure the qubit and store the result.
let result = M(q);
// Reset qubit to the |0〉 state.
Reset(q);
// Return the result of the measurement.
return result;
}
量子乱数演算を定義する
ここでは、GenerateRandomNumberInRange
演算を定義します。 この演算は GenerateRandomBit
演算を繰り返し呼び出して、ビット文字列を構築します。
次のコードをコピーし、GenerateRandomBit
演算の前に Main.qs
ファイルに貼り付けます。
/// Generates a random number between 0 and `max`.
operation GenerateRandomNumberInRange(max : Int) : Int {
// Determine the number of bits needed to represent `max` and store it
// in the `nBits` variable. Then generate `nBits` random bits which will
// represent the generated random number.
mutable bits = [];
let nBits = BitSizeI(max);
for idxBit in 1..nBits {
set bits += [GenerateRandomBit()];
}
let sample = ResultArrayAsInt(bits);
// Return random number if it is within the requested range.
// Generate it again if it is outside the range.
return sample > max ? GenerateRandomNumberInRange(max) | sample;
}
少し時間を取って新しいコードを確認してみましょう。
- 最大
max
の整数を表現するために必要なビット数を計算する必要があります。Microsoft.Quantum.Math
ライブラリのBitSizeI
関数は、整数を、その表現に必要なビット数に変換します。 GenerateRandomNumberInRange
演算はfor
ループを使用して、max
以下の乱数を生成するまで乱数を生成します。for
ループは、他のプログラミング言語のfor
ループとまったく同じように動作します。- 変数
bits
は変更可能な変数です。 変更可能な変数は、計算中に変更できる変数です。set
ディレクティブを使用して、変更可能な変数の値を変更することができます。 ResultArrayAsInt
関数はMicrosoft.Quantum.Convert
ライブラリに付属しています。 この関数は、ビット文字列を正の整数に変換します。
エントリ ポイントを追加する
最後に、エントリ ポイントをプログラムに追加します。 既定では、Q# コンパイラは Main
演算を検索し、その場所に関係なくそこで処理を開始します。 Main
演算は、 GenerateRandomNumberInRange
演算を呼び出して、0 から max
番号までの乱数を生成します。 この例では、最大値を 100 として定義します。
次のコードをコピーし、Main.qs
ファイルに貼り付けます。
operation Main() : Int {
let max = 100;
Message($"Sampling a random number between 0 and {max}: ");
// Generate random number in the 0..max range.
return GenerateRandomNumberInRange(max);
}
最終プログラム
Main.qs
ファイルは、次のようになります。
import Microsoft.Quantum.Convert.*;
import Microsoft.Quantum.Math.*;
operation Main() : Int {
let max = 100;
Message($"Sampling a random number between 0 and {max}: ");
// Generate random number in the 0..max range.
return GenerateRandomNumberInRange(max);
}
/// Generates a random number between 0 and `max`.
operation GenerateRandomNumberInRange(max : Int) : Int {
// Determine the number of bits needed to represent `max` and store it
// in the `nBits` variable. Then generate `nBits` random bits which will
// represent the generated random number.
mutable bits = [];
let nBits = BitSizeI(max);
for idxBit in 1..nBits {
set bits += [GenerateRandomBit()];
}
let sample = ResultArrayAsInt(bits);
// Return random number if it is within the requested range.
// Generate it again if it is outside the range.
return sample > max ? GenerateRandomNumberInRange(max) | sample;
}
operation GenerateRandomBit() : Result {
// Allocate a qubit.
use q = Qubit();
// Set the qubit into superposition of 0 and 1 using the Hadamard operation
H(q);
// Measure the qubit value using the `M` operation, and store the
// measurement value in the `result` variable.
let result = M(q);
// Reset qubit to the |0〉 state.
Reset(q);
// Return the result of the measurement.
return result;
}
プログラムを実行する
新しい乱数ジェネレーターを試してみましょう。
- プログラムを実行する前に、ターゲット プロファイルを "無制限" に設定する必要があります。 [表示] > [コマンド パレット] を選び、QIR を検索して、[Q#:Azure Quantum QIR ターゲット プロファイルの設定] を選び、次に [Q#: 無制限] を選びます。
- プログラムを実行するには、
Main
演算の上にあるコマンドの一覧から [実行] を選択するか、Ctrl + F5 を押します。 出力がデバッグ コンソールに表示されます。 - プログラムを再度実行すると、異なる結果が表示されます。
Note
ターゲット プロファイルが "無制限" に設定されていない場合、プログラムを実行するとエラーが発生します。
お疲れさまでした。 これで、従来のロジックを Q# と組み合わせて、量子乱数ジェネレーターを作成する方法がわかりました。
ボーナス演習
生成される乱数が、ゼロではなく特定の最小値 min
よりも大きくなる必要があるように、プログラムの変更を試してください。