IQuantumProcessor
により、独自のシミュレーターを作成し、それらをQ#プロジェクトに統合することが非常に簡単になります。
この投稿は、このインターフェイスに関するシリーズの最初の投稿です。最初の例としてリバーシブルシミュレーターを実装することから始めます。これについては、今後のブログ投稿で詳しく説明します。リバーシブルシミュレータは、古典的な操作からのみ構成された量子プログラムをシミュレートすることができます:
X
、CNOT
、CCNOT
(Toffoliゲート)またはランダムに制御されたX操作。リバーシブルシミュレータは、各キュービットに1つのブール値を割り当てることで量子状態を表すことができるため、数千キュービットの量子プログラムを実行することもできます。このシミュレータは、ブール関数を評価する量子演算をテストするのに非常に役立ちます。
Cでのシミュレーターの実装#
このブログ投稿では、基本的なコードスニペットについて説明しています。完全なソースコードは、MicrosoftQDKサンプルリポジトリにあります。
クラスを拡張して、独自のシミュレーターを作成し始めます
QuantumProcessorBase
。
class ReversibleSimulatorProcessor : QuantumProcessorBase {
private IDictionary<Qubit, bool> simulationValues = new Dictionary<Qubit, bool>();
// (intrinsic operations)...
}
プログラム内の各キュービットの現在のシミュレーション値を格納する辞書は、すでにクラスに追加されています。古典的な量子状態|0⟩と|1⟩は、それぞれブール値falseとtrueとして表されます。シミュレーターは、Q#プログラムで内部操作を定義します。
QuantumProcessorBase
新しいシミュレータでその動作を定義するためにオーバーライドできる各内部操作のメソッドが含まれています。実装されていないメソッドはデフォルトの例外をスローすることに注意してください。これは、操作をまだ実装する必要がある場合を特定するのに役立ち、インライン操作がシミュレーターでサポートされていないことをシミュレーターユーザーに通知します。たとえば、リバーシブルシミュレータを使用して、HやTなどの非古典的な操作を含む量子プログラムをシミュレートすることはできません。
OnAllocateQubitsメソッドをオーバーライドして、新しく割り当てられたキュービットを誤って初期化することから始めましょう。同様に、キュービットはリリース時に辞書から削除されます。この場合、OnReleaseQubitsが呼び出されます。
public override void OnAllocateQubits(IQArray qubits) {
foreach (var qubit in qubits) {
simulationValues[qubit] = false;
}
}
public override void OnReleaseQubits(IQArray qubits) {
foreach (var qubit in qubits) {
simulationValues.Remove(qubit);
}
}
これらの2つの操作により、操作が一部のキュービットに適用されたときにシミュレーション値が使用可能であり、現在の領域にないキュービットのシミュレーション値が辞書に残っていないことが保証されます。
次のステップとして、従来の操作アクションを実装しましょう。この場合、2つのメソッドで十分です。X操作がシミュレートされるときにXメソッドが呼び出され、X操作がシミュレートされるときにControlledXメソッドが呼び出されます。これには、CNOTとCCNOTも含まれます。 X操作は、キュービットのシミュレーション値を反転しますが、任意に制御されたX操作の場合、すべての制御キュービットがtrueに設定されている場合にのみ、ターゲットキュービットが反転されます。
public override void X(Qubit qubit) {
simulationValues[qubit] = !simulationValues[qubit];
}
public override void ControlledX(IQArray controls, Qubit qubit) {
simulationValues[qubit] ^= And(controls);
}
最後に、Q#プログラムでキュービットを測定すると、結果(1またはゼロ)が返されます。これは、キュービットの現在のシミュレーション値に基づいて計算できます。また、Q#プログラムでリセット操作が呼び出されたときに呼び出されるResetメソッドを実装します。後者は現在の領域からキュービットを削除しませんが、シミュレーション値を初期値にリセットします。これはfalseです。
public override Result M(Qubit qubit) {
return simulationValues[qubit] ? Result.One : Result.Zero;
}
public override void Reset(Qubit qubit) {
simulationValues[qubit] = false;
}
ReversibleSimulatorProcessorは、QuantumProcessorDispatcherをインスタンス化することでシミュレーターとして使用できます。推奨される設計スタイルは、シミュレーターにカスタムクラスを提供することです。
public class ReversibleSimulator : QuantumProcessorDispatcher {
public ReversibleSimulator() : base(new ReversibleSimulatorProcessor()) {}
}
新しいシミュレーターの使用
次のQ#操作で新しいシミュレーターを試してみましょう。
operation ApplyMajority(a : Qubit, b : Qubit, c : Qubit, f : Qubit) : Unit {
within {
CNOT(b, a);
CNOT(b, c);
} apply {
CCNOT(a, c, f);
CNOT(b, f);
}
}
また、3つのブール入力を提供することにより、多数決操作を実行する操作を記述します。
operation RunMajority(a : Bool, b : Bool, c : Bool) : Bool {
using ((qa, qb, qc, f) = (Qubit(), Qubit(), Qubit(), Qubit())) {
within {
ApplyPauliFromBitString(PauliX, true, [a, b, c], [qa, qb, qc]);
} apply {
ApplyMajority(qa, qb, qc, f);
}
return MResetZ(f) == One;
}
}
最後に、C#ホストプログラムの新しいシミュレーターでQ#操作を呼び出すことにより、すべてをまとめることができます。次の例では、すべての異なる入力先のメイン操作を評価し、すべてのシミュレーション結果を表示します。
public static void Main(string[] args) {
var sim = new ReversibleSimulator();
var bits = new[] {false, true};
foreach (var a in bits) {
foreach (var b in bits) {
foreach (var c in bits) {
var f = RunMajority.Run(sim, a, b, c).Result;
Console.WriteLine($"Majority({a,5}, {b,5}, {c,5}) = {f,5}");
}
}
}
}
独自のシミュレーターを作成する準備はできましたか?
この投稿が、独自のシミュレーターを作成するきっかけになることを願っています。次のステップでは、現在の実装のパフォーマンスを向上させる方法(たとえば、シミュレーション値を格納するために辞書を使用しないことによって)、スタンドアロンのQ#プロジェクトに変換する方法、非組み込み操作にカスタムアクションを提供する方法、および診断操作を提供する方法について説明します。デバッグに役立ちます。