Erstellen Sie Ihren eigenen Q # -Simulator - Teil 1

Simulatoren sind ein besonders vielseitiges Merkmal des QDK. Mit ihnen können Sie verschiedene Aufgaben in einem Q # -Programm ausführen, ohne es zu ändern. Solche Aufgaben umfassen eine vollständige Zustandssimulation , eine Ressourcenschätzung oder eine Verfolgungssimulation . Die neue Oberfläche IQuantumProcessormacht es sehr einfach, eigene Simulatoren zu erstellen und diese in Ihre Q # -Projekte zu integrieren.



Dieser Beitrag ist der erste einer Reihe auf dieser Oberfläche. Wir beginnen mit der Implementierung eines reversiblen Simulators als erstes Beispiel, das wir in zukünftigen Blog-Posts erweitern werden. Reversible Simulator können Quantenprogramme simulieren , die nur aus klassischen Operationen bestehen: X, CNOT,CCNOT(Toffoli Gate) oder zufällig kontrollierte X-Operationen. Da ein reversibler Simulator einen Quantenzustand darstellen kann, indem jedem Qubit ein Boolescher Wert zugewiesen wird, kann er sogar Quantenprogramme mit Tausenden von Qubits ausführen. Dieser Simulator ist sehr nützlich zum Testen von Quantenoperationen, die Boolesche Funktionen auswerten.







Simulator-Implementierung in C #



Dieser Blog-Beitrag behandelt grundlegende Code-Schnipsel. Der vollständige Quellcode befindet sich im Microsoft QDK Samples-Repository.


Sie beginnen mit dem Schreiben Ihres eigenen Simulators, indem Sie die Klasse erweitern QuantumProcessorBase:



class ReversibleSimulatorProcessor : QuantumProcessorBase {
    private IDictionary<Qubit, bool> simulationValues = new Dictionary<Qubit, bool>();

    //       (intrinsic operations)...
}


Ein Wörterbuch, in dem der aktuelle Simulationswert für jedes Qubit im Programm gespeichert wird, wurde der Klasse bereits hinzugefügt. Die klassischen Quantenzustände | 0⟩ und | 1⟩ werden als Boolesche Werte false bzw. true dargestellt. Simulatoren definieren interne Operationen in einem Q # -Programm. QuantumProcessorBaseenthält eine Methode für jede interne Operation, die Sie überschreiben können, um ihr Verhalten im neuen Simulator zu definieren. Beachten Sie, dass nicht implementierte Methoden eine Standardausnahme auslösen. Auf diese Weise können Sie Fälle identifizieren, in denen die Operation noch implementiert werden muss, und den Simulatorbenutzer darüber informieren, dass die Inline-Operation vom Simulator nicht unterstützt wird. Beispielsweise kann ein reversibler Simulator nicht verwendet werden, um Quantenprogramme zu simulieren, die nicht klassische Operationen wie H oder T enthalten.



Beginnen wir damit, die neu zugewiesenen Qubits fälschlicherweise zu initialisieren, indem wir die OnAllocateQubits-Methode überschreiben. Ebenso werden Qubits aus dem Wörterbuch entfernt, wenn sie freigegeben werden. In diesem Fall wird OnReleaseQubits aufgerufen.



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);
    }
}


Mit diesen beiden Operationen wird garantiert, dass Simulationswerte verfügbar sind, wenn die Operation auf ein bestimmtes Qubit angewendet wird, und dass im Wörterbuch keine Simulationswerte für Qubits mehr vorhanden sind, die sich nicht in der aktuellen Region befinden.



Als nächsten Schritt implementieren wir die klassischen Operationsaktionen. In diesem Fall sind zwei Methoden ausreichend: Die X-Methode wird aufgerufen, wenn die X-Operation simuliert wird, und die ControlledX-Methode wird aufgerufen, wenn die X-Operation simuliert wird, zu der auch CNOT und CCNOT gehören. Die X-Operation invertiert den Simulationswert des Qubits, während im Fall einer willkürlich gesteuerten X-Operation das Ziel-Qubit genau dann invertiert wird, wenn alle Steuer-Qubits auf true gesetzt sind.



public override void X(Qubit qubit) {
    simulationValues[qubit] = !simulationValues[qubit];
}

public override void ControlledX(IQArray controls, Qubit qubit) {
    simulationValues[qubit] ^= And(controls);
}


Schließlich liefert das Messen eines Qubits im Q # -Programm ein Ergebnis (Eins oder Null), das basierend auf dem aktuellen Simulationswert des Qubits berechnet werden kann. Wir implementieren auch eine Reset-Methode, die aufgerufen wird, wenn eine Reset-Operation in einem Q # -Programm aufgerufen wird. Letzteres entfernt das Qubit nicht aus dem aktuellen Bereich, sondern setzt den Simulationswert auf seinen Anfangswert zurück, der falsch ist.



public override Result M(Qubit qubit) {
    return simulationValues[qubit] ? Result.One : Result.Zero;
}

public override void Reset(Qubit qubit) {
    simulationValues[qubit] = false;
}


Der ReversibleSimulatorProcessor kann als Simulator verwendet werden, indem der QuantumProcessorDispatcher instanziiert wird. Der empfohlene Entwurfsstil besteht darin, eine benutzerdefinierte Klasse für den Simulator bereitzustellen:



public class ReversibleSimulator : QuantumProcessorDispatcher {
    public ReversibleSimulator() : base(new ReversibleSimulatorProcessor()) {}
}


Verwenden des neuen Simulators



Probieren wir den neuen Simulator für die folgende Q # -Operation aus:



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);
    }
}


Wir schreiben auch eine Operation, die eine Mehrheitsoperation durch Bereitstellung von drei booleschen Eingaben ausführt:



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;
    }
}


Schließlich können Sie alles zusammenfügen, indem Sie eine Q # -Operation mit einem neuen Simulator im C # -Hostprogramm aufrufen. Im folgenden Beispiel wird die Hauptoperation für alle verschiedenen Eingabeziele ausgewertet und alle Simulationsergebnisse angezeigt:



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}");
            }
        }
    }
}


Bist du bereit, deinen eigenen Simulator zu schreiben?



Wir hoffen, dieser Beitrag inspiriert Sie dazu, Ihren eigenen Simulator zu erstellen. In den nächsten Schritten werden wir diskutieren, wie die Leistung der aktuellen Implementierung verbessert werden kann (z. B. indem kein Wörterbuch zum Speichern von Simulationswerten verwendet wird), wie es in ein eigenständiges Q # -Projekt umgewandelt wird, wie benutzerdefinierte Aktionen für nicht intrinsische Vorgänge bereitgestellt werden und wie Diagnosevorgänge bereitgestellt werden Hilfe beim Debuggen.



All Articles