Transpiler-Durchläufe
Bei der Transpilierung wird eine gegebene Eingabeschaltung umgeschrieben, um sie an die Topologie eines bestimmten Quantengeräts anzupassen und/oder um die Schaltung für die Ausführung auf einem Quantensystem zu optimieren.
Die meisten Schaltungen müssen eine Reihe von Transformationen durchlaufen, die sie mit einem bestimmten Zielgerät kompatibel machen und sie optimieren, um die Auswirkungen von Rauschen auf die resultierenden Ergebnisse zu reduzieren. Das Umschreiben von Quantenschaltungen, um sie an die Hardwarebedingungen anzupassen, und die Optimierung der Leistung können alles andere als trivial sein. Der Logikfluss in der Rewriting-Toolkette muss nicht linear sein, sondern kann oft iterative Unterschleifen, bedingte Verzweigungen und andere komplexe Verhaltensweisen aufweisen.
In Qiskit wird der Transpiler durch die Ausführung einer Reihe von Durchläufen aufgebaut, die jeweils eine einzelne Aufgabe zur Analyse oder Transformation eines Quantenschaltkreises ausführen. Die Dokumentation Python transpiler enthält eine ausführlichere Erläuterung des Transpilierungsprozesses.
Die Qiskit-C-API stellt Transpiler-Pass-Funktionen in zwei Formen bereit: solche, die auf einem operieren QkDag , und eine weitere Gruppe, die auf einem QkCircuitoperiert. Die DAG-basierten Funktionen, die der Namenskonvention folgen qk_transpiler_pass_*, akzeptieren ein QkDag zusammen mit einem QkTarget sowie beliebige durchlaufspezifische Konfigurationsparameter. Diese Funktionen sind die empfohlene Wahl, wenn mehrere Durchläufe verkettet werden, z. B. beim Erstellen einer benutzerdefinierten Transpilation-Pipeline, da sie direkt auf das DAG-Objekt zugreifen und es ermöglichen, dieses innerhalb einer Transpilation-Sitzung effizient von einem Durchlauf zum nächsten weiterzugeben. Im Gegensatz dazu arbeiten die schaltkreisbasierten Funktionen gemäß der qk_transpiler_pass_standalone_* Namenskonvention mit einem QkCircuit und sind dafür vorgesehen, einzelne Durchläufe isoliert auszuführen. Zwar lassen sich damit auch benutzerdefinierte Workflows erstellen, doch verursacht jeder Aufruf zusätzlichen Overhead, da der Eingabeschaltkreis intern in einen DAG umgewandelt werden muss und, falls ein transformierter Schaltkreis zurückgegeben wird, der resultierende DAG anschließend wieder in einen Schaltkreis zurückkonvertiert werden muss.
DAG-basierte Funktionen
qk_transpiler_pass_elide_permutations
QkTranspileLayout *qk_transpiler_pass_elide_permutations(QkDag *dag)
Führen Sie den Transpiler-Durchlauf „ ElidePermutations “ für einen DAG aus.
Der Transpiler-Durchlauf „ ElidePermutations “ entfernt alle Permutationsoperationen aus einem DAG vor dem Layout.
Dieser Durchlauf soll ausgeführt werden, bevor im Rahmen der Transpilation-Pipeline ein Layout (die Zuordnung virtueller Qubits zu physikalischen Qubits) festgelegt wird. Dieser Durchlauf durchläuft den DAG, und sobald ein Swap-Gatter auftritt, permutiert er die virtuellen Qubits im DAG und entfernt das Swap-Gatter. Dadurch werden alle Swap-Gates im DAG vor der Layout-Ausführung effektiv entfernt. Diese Optimierung ist nach dem Festlegen eines Layouts nicht mehr gültig und sollte in diesem Fall nicht ausgeführt werden.
Beispiel
QkDag *dag = qk_dag_new();
QkQuantumRegister *qr = qk_quantum_register_new(4, "qr");
qk_dag_add_quantum_register(dag, qr);
for (uint32_t i = 0; i < qk_dag_num_qubits(dag) - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
for (uint32_t j = 0; j < i + 1; j++) {
qk_dag_apply_gate(dag, QkGate_CX, qargs, NULL, false);
}
}
QkTranspileLayout *elide_result = qk_transpiler_pass_elide_permutations(dag);
if (elide_result != NULL) {
qk_transpile_layout_free(elide_result);
}
qk_quantum_register_free(qr);
qk_dag_free(dag);Sicherheit
Das Verhalten ist nicht definiert, wenn kein gültiger, dag nicht-null-Zeiger auf ein ist QkDAG.
Parameter
- dag – Ein Zeiger auf den DAG, auf dem „ ElidePermutations “ ausgeführt werden soll. Wenn Änderungen vorgenommen werden, wird das Objekt, auf das verwiesen wird, an Ort und Stelle geändert. Falls Gatter weggelassen werden, werden die Zuweisungen des ursprünglichen DAG durch diese Funktion freigegeben.
Rückgabe
das Layout-Objekt, das die durch die weggelassenen Gatter im DAG induzierte Ausgabepermutation enthält. Wenn keine Auslassungen vorgenommen werden, ist dies ein Nullzeiger, und der Eingabe-DAG bleibt unverändert. Der Aufrufer ist dafür verantwortlich, das zurückgegebene Layout durch Aufruf von qk_transpile_layout_freefreizugeben.
qk_transpiler_pass_check_gate_direction
bool qk_transpiler_pass_check_gate_direction(const QkDag *dag, const QkTarget *target)
Führe den CheckGateDirection Durchlauf auf einem DAG-Circuit aus.
Der Durchlauf prüft, ob die Richtungen von Zwei-Qubit-Gattern mit den in einem bestimmten Ziel angegebenen Torrichtungen übereinstimmen.
Beispiel
QkTarget *target = qk_target_new(2);
uint32_t qargs[3] = {0,1};
QkTargetEntry *cx_entry = qk_target_entry_new(QkGate_CX);
qk_target_entry_add_property(cx_entry, qargs, 2, 0.0, 0.0);
qk_target_add_instruction(target, cx_entry);
QkDag *dag = qk_dag_new();
QkQuantumRegister *qr = qk_quantum_register_new(2, "qr");
qk_dag_add_quantum_register(dag, qr);
qk_dag_apply_gate(dag, QkGate_CX, (uint32_t[]){1,0}, NULL, false);
bool direction_ok = qk_transpiler_pass_check_gate_direction(dag, target);
qk_quantum_register_free(qr);
qk_dag_free(dag);
qk_target_free(target);Sicherheit
Das Verhalten ist nicht definiert, wenn dag oder keine gültigen, nicht-null-Zeiger target auf Objekte QkDag vom QkTarget Typ bzw. sind.
Parameter
- dag – Ein Zeiger auf den DAG-Circuit, auf dem der „ CheckGateDirection “-Durchlauf ausgeführt werden soll.
- target - Ein Zeiger auf das Ziel, das für die Überprüfung der Torrichtungen verwendet wird.
Rückgabe
bool - true, wenn die Richtungen aller Zwei-Qubit-Gatter in der Schaltung mit den angegebenen Zielbedingungen übereinstimmen.
qk_transpiler_pass_gate_direction
void qk_transpiler_pass_gate_direction(QkDag *dag, const QkTarget *target)
Führen Sie den GateDirection Pass in einer DAG aus.
Der GateDirection Durchgang modifiziert asymmetrische Gatter, damit sie mit den Hardware-Kopplungsrichtungen übereinstimmen. Dieser Pass unterstützt Ersetzungen für die Tore cx, cz, ecr, swap, rzx, rxx, ryy und rzz unter Verwendung vordefinierter Identitäten.
Beispiel
QkTarget *target = qk_target_new(3);
uint32_t qargs[2] = {0,1};
QkTargetEntry *cx_entry = qk_target_entry_new(QkGate_CX);
qk_target_entry_add_property(cx_entry, qargs, 2, 0.0, 0.0);
qk_target_add_instruction(target, cx_entry);
QkDag *dag = qk_dag_new();
QkQuantumRegister *qr = qk_quantum_register_new(3, "qr");
qk_dag_add_quantum_register(dag, qr);
qk_dag_apply_gate(dag, QkGate_CX, (uint32_t[]){1,0}, NULL, false);
qk_transpiler_pass_gate_direction(dag, target);
qk_quantum_register_free(qr);
qk_dag_free(dag);
qk_target_free(target);Sicherheit
Das Verhalten ist nicht definiert, wenn dag oder keine gültigen, nicht-null-Zeiger target auf Objekte QkDag vom QkTarget Typ bzw. sind.
Parameter
- dag – Ein Zeiger auf den DAG-Circuit, auf dem der „ GateDirection “-Durchlauf ausgeführt werden soll. Der DAG wird durch den Durchlauf an Ort und Stelle geändert.
- target - Ein Zeiger auf das Ziel, das für die Überprüfung der Torrichtungen verwendet wird.
qk_transpiler_pass_optimize_1q_sequences
void qk_transpiler_pass_optimize_1q_sequences(QkDag *dag, const QkTarget *target)
Führt den „ Optimize1qGatesDecomposition “-Pass im eigenständigen Modus auf einem DAG-Circuit aus.
Optimize1qGatesDecomposition optimiert Ein-Qubit-Gatesequenzen durch Neusynthetisierung der Unitary unter Berücksichtigung der Basisgatter und Fehlerraten des Ziels.
Die Entscheidung, ob die Originalkette ersetzt werden soll, hängt davon ab:
- Wenn die ursprüngliche Kette keine Basis mehr hatte.
- Wenn die ursprüngliche Kette in der Basis war, aber der Ersatz eine geringere Fehlerquote hat.
- Wenn die ursprüngliche Kette eine Identität ist (Kette wird entfernt).
Der Fehler ist die kombinierte Multiplikation der Fehler der einzelnen Gatter auf dem Qubit, auf dem es arbeitet.
Diese Funktion ist multithreaded und startet standardmäßig möglicherweise einen Thread-Pool mit einer Anzahl von Threads, die der Anzahl der CPUs entspricht. Die Anzahl der Threads können Sie über die RAYON_NUM_THREADS Umgebungsvariable anpassen. Wenn man beispielsweise einstellt RAYON_NUM_THREADS=4 , würde der Thread-Pool auf 4 Threads begrenzt werden.
Beispiel
QkTarget *target = qk_target_new(1);
double u_errors[3] = {0., 1e-4, 1e-4};
for (int idx = 0; idx < 3; idx++) {
QkTargetEntry *u_entry = qk_target_entry_new(QkGate_U);
uint32_t qargs[1] = {0};
qk_target_entry_add_property(u_entry, qargs, 1, NAN, u_errors[idx]);
qk_target_add_instruction(target, u_entry);
}
// Build circuit
QkDag *dag = qk_dag_new();
QkQuantumRegister *qr = qk_quantum_register_new(1, "qr");
qk_dag_add_quantum_register(dag, qr);
uint32_t qubits[1] = {0};
for (int iter = 0; iter < 3; iter++) {
qk_dag_apply_gate(dag, QkGate_H, qubits, NULL, false);
}
// Run transpiler pass
qk_transpiler_pass_optimize_1q_sequences(dag, target);
// Clean up
qk_target_free(target);
qk_dag_free(dag);
qk_quantum_register_free(qr);Sicherheit
Das Verhalten ist nicht definiert, wenn kein gültiger, dag nicht-null-Zeiger auf ein ist QkDag und wenn kein gültiger target Zeiger auf ein ist QkTarget.
Parameter
- dag – Ein Zeiger auf das zu
QkDagtransformierende Objekt. - target - Ein Zeiger auf das
QkTargetObjekt oder ein Null-Zeiger. Wird ein Null-Zeiger angegeben und sind Gatterfehler nicht bekannt, wählt der Durchlauf die Sequenz mit der geringsten Anzahl von Gattern und unterstützt alle Basistore in seiner Euler-Basismenge.
qk_transpiler_pass_remove_diagonal_gates_before_measure
void qk_transpiler_pass_remove_diagonal_gates_before_measure(QkDag *dag)
Führe den RemoveDiagonalGatesBeforeMeasure Durchlauf auf einem DAG-Circuit aus.
Transpiler-Durchlauf, um diagonale Gatter (wie RZ, T, Z, etc.) vor einer Messung zu entfernen. Einschließlich diagonaler 2Q Gates.
Beispiel
QkDag *dag = qk_dag_new();
QkQuantumRegister *qr = qk_quantum_register_new(1, "qr");
QkClassicalRegister *cr = qk_classical_register_new(1, "cr");
qk_dag_add_quantum_register(dag, qr);
qk_dag_add_classical_register(dag, cr);
qk_dag_apply_gate(dag, QkGate_Z, (uint32_t[1]){0}, NULL, false);
qk_dag_apply_measure(dag, 0, 0, false);
qk_transpiler_pass_remove_diagonal_gates_before_measure(dag);
// ...
qk_dag_free(dag);
qk_quantum_register_free(qr);
qk_classical_register_free(cr);Sicherheit
Das Verhalten ist nicht definiert, wenn kein gültiger, dag nicht-null-Zeiger auf ein ist QkDag.
Parameter
- dag – Ein Zeiger auf den DAG, auf dem dieser Durchlauf ausgeführt werden soll. Der DAG wird durch den Durchlauf an Ort und Stelle geändert.
qk_transpiler_pass_remove_identity_equivalent
void qk_transpiler_pass_remove_identity_equivalent(QkDag *dag, const QkTarget *target, double approximation_degree)
Führen Sie den Transpiler-Durchlauf „ RemoveIdentityEquivalent “ für einen DAG-Schaltkreis aus.
Entfernt Gatter, deren Wirkung einer Identitätsoperation nahe kommt, bis zu einer globalen Phase und bis zu der angegebenen Toleranz. Parametrisierte Gatter werden bei diesem Durchgang nicht berücksichtigt.
Für eine Cutoff-Treue werden bei diesem Durchgang Gatter entfernt, deren durchschnittliche Gattertreue in Bezug auf die Identität unter liegt. Konkret wird ein Gatter entfernt, wenn mit
wobei die Dimension des Gatters für qubits ist.
Diese Funktion ist multithreaded und startet standardmäßig einen Thread-Pool mit einer Anzahl von Threads, die der Anzahl der CPUs entspricht. Die Anzahl der Threads können Sie über die Umgebungsvariable RAYON_NUM_THREADS anpassen. Wenn man beispielsweise „ RAYON_NUM_THREADS=4 “ festlegt, wird der Thread-Pool auf 4 Threads begrenzt.
Beispiel
QkTarget *target = qk_target_new(5);
uint32_t current_num_qubits = qk_target_num_qubits(target);
QkTargetEntry *cx_entry = qk_target_entry_new(QkGate_CX);
for (uint32_t i = 0; i < current_num_qubits - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
double inst_error = 0.0090393 * (current_num_qubits - i);
double inst_duration = 0.020039;
qk_target_entry_add_property(cx_entry, qargs, 2, inst_duration, inst_error);
}
QkExitCode result_cx = qk_target_add_instruction(target, cx_entry);
QkDag *dag = qk_dag_new();
QkQuantumRegister *qr = qk_quantum_register_new(4, "qr");
qk_dag_add_quantum_register(dag, qr);
for (uint32_t i = 0; i < qk_dag_num_qubits(dag) - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
for (uint32_t j = 0; j<i+1; j++) {
qk_dag_apply_gate(dag, QkGate_CX, qargs, NULL, false);
}
}
uint32_t rz_qargs[1] = {1,};
double rz_params[1] = {0.,};
qk_dag_apply_gate(dag, QkGate_RZ, rz_qargs, rz_params, false);
qk_transpiler_pass_remove_identity_equivalent(dag, target, 1.0);
qk_quantum_register_free(qr);
qk_dag_free(dag);
qk_target_free(target);Sicherheit
Das Verhalten ist nicht definiert, wenn dag oder kein gültiger, target nicht-null-Zeiger auf ein QkDag bzw QkTarget. ist.
Parameter
- dag – Ein Zeiger auf den DAG, auf dem „ RemoveIdentityEquivalent “ ausgeführt werden soll. Der DAG, auf den hier verwiesen wird, wird mit dem geänderten DAG aktualisiert, sofern der Durchlauf in der Lage ist, Gatter zu entfernen.
- target - Das Ziel für den RemoveIdentityEquivalent Durchgang. Wenn
approximation_degreeaufNANgesetzt ist, wird die Toleranz für die Feststellung, ob ein Vorgang der Identität entspricht, auf die gemeldete Fehlerquote im Ziel gesetzt. Andernfalls wird dietargetnicht verwendet, da die Toleranz unabhängig vom Ziel ist. - approximation_degree - Der zu approximierende Grad für die Äquivalenzprüfung. Dies kann ein Fließkommawert zwischen 0 und 1 oder
NANsein. Wenn der Wert 1 ist, erfolgt keine Annäherung an die Fließkommagenauigkeit. Bei einem Wert < 1 wird dies als Skalierungsfaktor für die Abschneidetreue verwendet. Wenn der WertNANist, entspricht dies in etwa der intargetangegebenen Treue für das Tor.
qk_transpiler_pass_split_2q_unitaries
QkTranspileLayout *qk_transpiler_pass_split_2q_unitaries(QkDag *dag, double requested_fidelity, bool split_swaps)
Führen Sie den Transpiler-Durchlauf „ Split2QUnitaries “ für einen DAG-Circuit aus
Sicherheit
Das Verhalten ist nicht definiert, wenn kein gültiger, dag nicht-null-Zeiger auf ein ist QkDAG.
Parameter
- dag – Ein veränderbare Zeiger auf den DAG-Circuit, auf dem „ Split2QUnitaries “ ausgeführt werden soll. Dies wird durch den neuen DAG ersetzt, falls irgendwelche Gatter optimiert werden.
- requested_fidelity - Erlaubte Toleranz für die Aufteilung von Zwei-Qubit-Unitarien und Gatterzerlegungen.
- split_swaps - Ob versucht werden soll, Swap-Gates aufzuteilen, was zu einer Permutation der Qubits führt.
Rückgabe
Wenn swap-äquivalente Unitarien aufgeteilt werden, gibt diese Funktion einen Zeiger auf ein Objekt zurück TranspileLayout , das die durch diese DAG-Optimierung induzierte Permutation enthält. Wenn keine swap-äquivalenten Unitaries aufgeteilt werden, ist dies ein Nullzeiger.
qk_transpiler_pass_2q_peephole_optimization
void qk_transpiler_pass_2q_peephole_optimization(QkDag *dag, const QkTarget *target, double approximation_degree)
Führen Sie den Transpiler-Durchlauf „ TwoQubitPeepholeOptimization “ aus.
Dieser Transpiler-Durchlauf dient der Durchführung einer unitären Peephole-Optimierung für zwei Qubits. Dieser Durchlauf ermittelt alle 2-Qubit-Blöcke im Schaltkreis, berechnet die Unitarität dieses Blocks und synthetisiert diese anschließend. Ist die synthetisierte 2-Qubit-Unitar-Operation „besser“ als der ursprüngliche Teilschaltkreis, wird dieser Teilschaltkreis anstelle des ursprünglichen verwendet. Die Heuristik, mit der ermittelt wird, welche Variante besser ist, prüft zunächst die Anzahl der Zwei-Qubit-Gatter im Schaltkreis und bevorzugt die Synthese mit weniger Zwei-Qubit-Gattern. Sind die Anzahlen der Zwei-Qubit-Gatter gleich, wird die geschätzte Fidelity des Schaltkreises herangezogen und der Teilschaltkreis mit der höheren geschätzten Fidelity ausgewählt. Schließlich wird, falls erforderlich, der Teilschaltkreis mit der geringsten Gesamtanzahl an Gattern ausgewählt.
Falls das Ziel überkomplett ist, probiert der Durchlauf alle unterstützten Zerleger für alle Gatter aus, die auf einem bestimmten Qubit unterstützt werden. Die Zerlegung, die unter Verwendung der oben genannten Heuristik die beste erwartete Leistung aufweist, wird ausgewählt und zum Ersetzen des Blocks verwendet.
Dieser Durchlauf ist für die Ausführung auf einer physikalischen Schaltung ausgelegt, und es wird davon ausgegangen, dass es sich bei den Operationen an einem bestimmten Qubit um das Hardware-Qubit aus dem Zielsystem handelt. Die Ausgabe des Durchlaufs nutzt jedoch möglicherweise keine Hardware-Operationen; insbesondere können Einzelqubit-Gatter außerhalb der vom Ziel unterstützten Operationen ausgegeben werden, in der Regel jedoch nur dann, wenn ein vom Ziel TwoQubitControlledUDecomposer unterstütztes parametrisiertes Gatter für die Synthese verwendet wird. Wenn dieser Durchlauf also in einer physikalischen Optimierungsphase (wie beispielsweise der Optimierungsphase ) ausgeführt wird, sollte er mit Durchläufen wie BasisTranslator und/oder Optimize1qGatesDecomposition kombiniert werden, um sicherzustellen, dass diese fehlerhaften Einzelqubit-Gatter vor dem Verlassen der Phase durch hardwareunterstützte Operationen ersetzt werden.
Dieser Durchlauf ist multithreaded und führt die Analyse parallel durch, wobei alle auf Ihrem lokalen System verfügbaren Kerne genutzt werden. Weitere Informationen dazu, wie Sie das Threading-Verhalten von Qiskit allgemein steuern können – was auch diesen Durchlauf beeinflusst –, finden Sie im Kon figurationshandbuch
Beispiel
QkTarget *target = qk_target_new(2);
uint32_t current_num_qubits = qk_target_num_qubits(target);
QkTargetEntry *cx_entry = qk_target_entry_new(QkGate_CX);
for (uint32_t i = 0; i < current_num_qubits - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
double inst_error = 0.0090393 * (current_num_qubits - i);
double inst_duration = 0.020039;
qk_target_entry_add_property(cx_entry, qargs, 2, inst_duration, inst_error);
}
QkExitCode result_cx = qk_target_add_instruction(target, cx_entry);
QkCircuit *qc = qk_circuit_new(2, 0);
uint32_t forward[2] = {0, 1};
uint32_t reverse[2] = {1, 0};
for (int i = 0; i < 10; i++) {
if (i % 2) {
qk_circuit_gate(qc, QkGate_CX, forward, NULL);
} else {
qk_circuit_gate(qc, QkGate_CX, reverse, NULL);
}
}
QkDag *dag = qk_circuit_to_dag(qc);
qk_circuit_free(qc);
qk_transpiler_pass_2q_peephole_optimization(dag, target, 1.0);
qk_dag_free(dag);
qk_target_free(target);Sicherheit
Das Verhalten ist nicht definiert, wenn dag oder kein gültiger, target nicht-null-Zeiger auf ein QkDag bzw QkTarget. ist.
Diese Funktion darf nicht in einem Kontext aufgerufen werden, in dem der DAG-Zirkel Objekte enthält, die zu „ Python “ gehören. Da die Funktion intern multithreaded ist, müssen die Worker-Threads möglicherweise den GIL erwerben, um auf die Attribute der „ Python “-Gates in der Schaltung zugreifen zu können, was zu einem Deadlock führt. Sie sollten die Raumklasse TwoQubitPeepholeOptimization „ Python “ verwenden, um diesen Durchlauf auszuführen, wenn Schaltungselemente vorhanden sind, die zu „ Python “ gehören.
Parameter
- dag – Ein Zeiger auf den DAG-Schaltkreis, auf dem „ TwoQubitPeepholeOptimization “ ausgeführt werden soll
- target – Ein Zeiger auf das Ziel, mit dem „ TwoQubitPeepholeOptimization “ ausgeführt werden soll
- approximation_degree – Heuristischer Regler zur Schaltungsnäherung ( 1.0=no -Näherung, 0.0=maximal -Näherung). Durch eine Näherung lässt sich die synthetisierte Schaltung verkleinern, allerdings auf Kosten einer Abweichung vom ursprünglichen Unitar. Wenn NAN, basiert die Zielannäherung auf den Gate-Fidelitäten in der
target.
Schaltungsbasierte Funktionen
qk_transpiler_pass_standalone_basis_translator
void qk_transpiler_pass_standalone_basis_translator(QkCircuit *circuit, const QkTarget *target, size_t min_qubits)
Führen Sie den BasisTranslator Transpiler-Durchlauf in einem Stromkreis durch.
Der BasisTranslator transpiler pass übersetzt Gatter in eine Zielbasis, indem er nach einer Reihe von Übersetzungen aus dem Standard EquivalenceLibrary sucht.
Beispiel
#include <qiskit.h>
QkCircuit *circuit = qk_circuit_new(3, 0);
qk_circuit_gate(circuit, QkGate_CCX, (uint32_t[3]){0, 1, 2}, NULL);
// Create a Target with global properties.
QkTarget *target = qk_target_new(3);
qk_target_add_instruction(target, qk_target_entry_new(QkGate_H));
qk_target_add_instruction(target, qk_target_entry_new(QkGate_T));
qk_target_add_instruction(target, qk_target_entry_new(QkGate_Tdg));
qk_target_add_instruction(target, qk_target_entry_new(QkGate_CX));
// Run pass
qk_transpiler_pass_standalone_basis_translator(circuit, target, 0);
// Free the circuit and target pointers once you're done
qk_circuit_free(circuit);
qk_target_free(target);Sicherheit
Das Verhalten ist undefiniert, wenn circuit und/oder target keine gültigen, nicht-null-Zeiger auf QkCircuit oder QkTarget sind.
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem BasisTranslator laufen soll. Die Schaltung wird an Ort und Stelle geändert, es sei denn, die Schaltung befindet sich bereits in der Zielbasis; in diesem Fall bleibt die Schaltung unverändert.
- target - Das Ziel, von dem wir die Basis-Gates beziehen werden.
- min_qubits – Die Mindestanzahl an Qubits für Operationen im zu übersetzenden Eingangsschaltkreis.
qk_transpiler_pass_standalone_commutative_cancellation
QkExitCode qk_transpiler_pass_standalone_commutative_cancellation(QkCircuit *circuit, const QkTarget *target, double approximation_degree)
Führen Sie den CommutativeCancellation Transpiler-Durchlauf in einem Stromkreis durch.
Dieser Durchgang hebt die redundanten (selbstadjunkten) Gatter durch Kommutationsbeziehungen auf.
Diese Funktion ist multithreaded und startet standardmäßig möglicherweise einen Thread-Pool mit einer Anzahl von Threads, die der Anzahl der CPUs entspricht. Die Anzahl der Threads können Sie über die RAYON_NUM_THREADS Umgebungsvariable anpassen. Wenn man beispielsweise einstellt RAYON_NUM_THREADS=4 , würde der Thread-Pool auf 4 Threads begrenzt werden.
Beispiel
QkCircuit *qc = qk_circuit_new(4, 0);
uint32_t cx_qargs[2] = {0, 1};
qk_circuit_gate(qc, QkGate_CX, cx_qargs, NULL);
qk_circuit_gate(qc, QkGate_Z, (uint32_t[]){0}, NULL);
qk_circuit_gate(qc, QkGate_CX, cx_qargs, NULL);
qk_transpiler_pass_standalone_commutative_cancellation(qc, NULL, 1.0);Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder target nicht gültig ist, QkCircuit und QkTarget. QkCircuit wird nicht als Null erwartet, und das Verhalten ist undefiniert, wenn sie es ist.
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem CommutativeCancellation laufen soll. Dieser Schaltkreiszeiger wird mit dem geänderten Schaltkreis aktualisiert, wenn der Durchgang in der Lage ist, irgendwelche Gatter zu entfernen.
- target - In diesem Durchgang wird versucht, alle Z-Drehungen in einem RZ-, P- oder U1 -Gatter zu akkumulieren, je nachdem, welches bereits in der Schaltung verwendet wird. Ist in der Schaltung kein Gatter vorhanden, wird dieses (optionale) Zielargument als Ausweichmöglichkeit verwendet, um zu entscheiden, welches Gatter zu verwenden ist. Wenn keines der Elemente RZ, P oder U1 in der Schaltung oder im Ziel vorhanden ist, werden Z-Drehungen mit einem einzelnen Qubit nicht optimiert.
- approximation_degree - Der Approximationsgrad, der bei der Analyse von Kommutationen verwendet wird. Die Adresse muss
(0, 1]lauten.
Rückgabe
Ganzzahliger Rückgabecode, wobei 0 für keinen Fehler steht und 1 anzeigt, dass bei der Ausführung des Durchlaufs ein Fehler aufgetreten ist.
qk_transpiler_pass_standalone_consolidate_blocks
void qk_transpiler_pass_standalone_consolidate_blocks(QkCircuit *circuit, const QkTarget *target, double approximation_degree, bool force_consolidate)
Führen Sie die ConsolidateBlocks in einem Stromkreis durch.
ConsolidateBlocks ist ein Transpiler-Durchgang, der aufeinanderfolgende Blöcke von Gattern, die auf dieselben Qubits wirken, zu einem Unitary Gate zusammenfasst, das später resynthetisiert werden kann, was zu einer optimaleren Teilschaltung führt.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit kein gültiger Nicht-Null-Zeiger auf einen QkCircuit ist und wenn target kein gültiger Zeiger auf einen QkTarget ist.
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem ConsolidateBlocks laufen soll.
- target - Ein Zeiger auf das Ziel, auf dem ConsolidateBlocks ausgeführt werden soll.
- approximation_degree - Eine Gleitkommazahl zwischen
[0.0, 1.0]oderNaN, die standardmäßig auf1.0eingestellt ist. Ein niedrigerer Wert bedeutet eine größere Annäherung. - force_consolidate - Erzwingt Blockkonsolidierung.
qk_transpiler_pass_standalone_convert_to_pauli_rotations
void qk_transpiler_pass_standalone_convert_to_pauli_rotations(QkCircuit *circuit)
Führe den ConvertToPauliRotations Durchlauf direkt auf einer Schaltung durch.
Dieser Pass wandelt alle Standardgatter (mit weniger als 4 Qubits) im Schaltkreis in eine Folge von QkPauliProductRotation Gattern und Messungen in QkPauliProductMeasurement Anweisungen um. Beachten Sie, dass dieser Durchlauf eine Fehlermeldung ausgibt, wenn die Schaltung nicht standardmäßige Logikgatter enthält. Der empfohlene Arbeitsablauf sieht vor, zunächst in eine Standardbasis zu transpilieren, wobei Rotationsgates (wie QkGate_RXX und andere) nach Möglichkeit beibehalten werden, und anschließend diesen Durchlauf aufzurufen.
Sicherheit
Das Verhalten ist nicht definiert, wenn circuit ein ungültiger, nicht-null-Zeiger auf ein ist QkCircuit.
Parameter
- Schaltung – Ein Zeiger auf die Schaltung, auf der der Durchlauf ausgeführt werden soll.
qk_transpiler_pass_standalone_elide_permutations
QkTranspileLayout *qk_transpiler_pass_standalone_elide_permutations(QkCircuit *circuit)
Führen Sie den ElidePermutations Transpiler-Durchlauf in einem Stromkreis durch.
Weitere Informationen zum Pass finden Sie in der qk_transpiler_pass_elide_permutations Funktion.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit kein gültiger, nicht-null Zeiger auf QkCircuit ist.
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem ElidePermutations laufen soll. Wenn Änderungen vorgenommen werden, wird das Objekt, auf das verwiesen wird, an Ort und Stelle geändert. Wenn Gatter elidiert werden, werden die Zuweisungen der ursprünglichen Schaltung durch diese Funktion wieder freigegeben.
Rückgabe
das Layout-Objekt, das die durch die weggelassenen Gatter in der Schaltung induzierte Ausgangspermutation enthält. Wenn keine Auslassungen vorgenommen werden, ist dies ein Nullzeiger, und die Eingabeschaltung bleibt unverändert. Der Aufrufer ist dafür verantwortlich, das zurückgegebene Layout durch Aufruf von qk_transpile_layout_freefreizugeben.
qk_transpiler_pass_standalone_check_gate_direction
bool qk_transpiler_pass_standalone_check_gate_direction(const QkCircuit *circuit, const QkTarget *target)
Führen Sie die CheckGateDirection in einem Stromkreis durch.
Weitere Informationen zum Pass finden Sie in der qk_transpiler_pass_check_gate_direction Funktion.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder target keine gültigen, nicht-null Zeiger auf QkCircuit bzw. QkTarget Objekte sind.
Parameter
- Schaltung – Ein Zeiger auf die Schaltung, auf der der Durchlauf ausgeführt werden soll.
- target - Ein Zeiger auf das Ziel, das für die Überprüfung der Torrichtungen verwendet wird.
Rückgabe
Wahr, wenn alle Richtungen der Zwei-Qubit-Gatter den Zielbedingungen entsprechen.
qk_transpiler_pass_standalone_gate_direction
void qk_transpiler_pass_standalone_gate_direction(QkCircuit *circuit, const QkTarget *target)
Führen Sie die GateDirection in einem Stromkreis durch.
Weitere Informationen zum Pass finden Sie in der qk_transpiler_pass_gate_direction Funktion.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder target keine gültigen, nicht-null Zeiger auf QkCircuit bzw. QkTarget Objekte sind.
Parameter
- Schaltung – Ein Zeiger auf die Schaltung, die an Ort und Stelle geändert werden soll.
- target – Ein Zeiger auf das Ziel, das für die Gate-Richtungen verwendet wird.
qk_transpiler_pass_standalone_inverse_cancellation
void qk_transpiler_pass_standalone_inverse_cancellation(QkCircuit *circuit)
Führen Sie den InverseCancellation Transpiler-Durchlauf in einem Stromkreis durch.
Hebt Paare von aufeinander folgenden Gattern auf, die invers zueinander sind. Die gelöschten Gatter bestehen aus Paaren von selbstinversen Gattern:
- QkGate_H
- QkGate_X
- QkGate_Y
- QkGate_Z
- QkGate_CH
- QkGate_CX
- QkGate_CY
- QkGate_CZ
- QkGate_ECR
- QkGate_Swap
- QkGate_CCX
- QkGate_CCZ
- QkGate_CSwap
- QkGate_RCCX
- QkGate_C3X
und Paare von inversen Toren:
- (QkGate_T, QkGate_Tdg)
- (QkGate_S, QkGate_Sdg)
- (QkGate_SX, QkGate_SXdg)
- (QkGate_CS, QkGate_CSdg)
Beispiel
QkCircuit *qc = qk_circuit_new(2, 2);
uint32_t qargs[1] = {0};
qk_circuit_gate(qc, QkGate_X, qargs, NULL);
qk_circuit_gate(qc, QkGate_H, qargs, NULL);
qk_circuit_gate(qc, QkGate_H, qargs, NULL);
qk_circuit_gate(qc, QkGate_Y, qargs, NULL);
qk_transpiler_pass_standalone_inverse_cancellation(qc);Sicherheit
Das Verhalten ist undefiniert, wenn circuit kein gültiger, nicht-null Zeiger auf QkCircuit ist.
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem InverseCancellation laufen soll. Gelingt es dem Durchgang, Gatter zu entfernen, wird der ursprüngliche Schaltkreis durch den von diesem Durchgang erzeugten Schaltkreis ersetzt.
qk_transpiler_pass_standalone_litinski_transformation
void qk_transpiler_pass_standalone_litinski_transformation(QkCircuit *circuit, bool fix_clifford)
Führe den LitinskiTransformation Durchlauf direkt auf einer Schaltung durch.
Dieser Durchlauf wandelt alle Clifford-Gatter am Ende der Schaltung um, wobei Pauli-Rotationsgatter in QkPauliProductRotation Gatter und Messungen in QkPauliProductMeasurement Befehle umgewandelt werden. Beachten Sie, dass dieser Durchlauf derzeit nur Schaltungen unterstützt, die oder QkGate_RZ Gatter als QkGate_Tdg Nicht-Clifford-Gatter enthalten QkGate_T; andernfalls kommt es zu einem Panikfehler. Der empfohlene Arbeitsablauf sieht vor, zunächst in eine Clifford+RZ-Basis zu transpilieren und anschließend diesen Durchlauf aufzurufen.
Sicherheit
Das Verhalten ist nicht definiert, wenn circuit ein ungültiger, nicht-null-Zeiger auf ein ist QkCircuit.
Parameter
- Schaltung – Ein Zeiger auf die Schaltung, auf der der Durchlauf ausgeführt werden soll.
- fix_clifford – Falls
truezutreffend, die Clifford-Gatter am Ende der Schaltung belassen. Fallsfalsesie weggelassen werden.
qk_transpiler_standalone_optimize_1q_sequences
void qk_transpiler_standalone_optimize_1q_sequences(QkCircuit *circuit, const QkTarget *target)
Lässt den Optimize1qGatesDecomposition pass im Standalone-Modus in einem Stromkreis laufen.
Verwenden Sie qk_transpiler_pass_standalone_optimize_1q_sequences() stattdessen.
Diese Funktion ist multithreaded und startet standardmäßig möglicherweise einen Thread-Pool mit einer Anzahl von Threads, die der Anzahl der CPUs entspricht. Die Anzahl der Threads können Sie über die RAYON_NUM_THREADS Umgebungsvariable anpassen. Wenn man beispielsweise einstellt RAYON_NUM_THREADS=4 , würde der Thread-Pool auf 4 Threads begrenzt werden.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit kein gültiger Nicht-Null-Zeiger auf einen QkCircuit ist und wenn target kein gültiger Zeiger auf einen QkTarget ist.
Parameter
- circuit - Ein Zeiger auf das zu transformierende
QkCircuitObjekt. - target - Ein Zeiger auf das
QkTargetObjekt oder ein Null-Zeiger. Wird ein Null-Zeiger angegeben und sind Gatterfehler nicht bekannt, wählt der Durchlauf die Sequenz mit der geringsten Anzahl von Gattern und unterstützt alle Basistore in seiner Euler-Basismenge.
qk_transpiler_pass_standalone_optimize_1q_sequences
void qk_transpiler_pass_standalone_optimize_1q_sequences(QkCircuit *circuit, const QkTarget *target)
Lässt den Optimize1qGatesDecomposition pass im Standalone-Modus in einem Stromkreis laufen.
Weitere Informationen zum Pass finden Sie in der qk_transpiler_pass_optimize_1q_sequences Funktion.
Diese Funktion ist multithreaded und startet standardmäßig möglicherweise einen Thread-Pool mit einer Anzahl von Threads, die der Anzahl der CPUs entspricht. Die Anzahl der Threads können Sie über die RAYON_NUM_THREADS Umgebungsvariable anpassen. Wenn man beispielsweise einstellt RAYON_NUM_THREADS=4 , würde der Thread-Pool auf 4 Threads begrenzt werden.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit kein gültiger Nicht-Null-Zeiger auf einen QkCircuit ist und wenn target kein gültiger Zeiger auf einen QkTarget ist.
Parameter
- circuit - Ein Zeiger auf das zu transformierende
QkCircuitObjekt. - target - Ein Zeiger auf das
QkTargetObjekt oder ein Null-Zeiger. Wird ein Null-Zeiger angegeben und sind Gatterfehler nicht bekannt, wählt der Durchlauf die Sequenz mit der geringsten Anzahl von Gattern und unterstützt alle Basistore in seiner Euler-Basismenge.
qk_transpiler_pass_standalone_remove_diagonal_gates_before_measure
void qk_transpiler_pass_standalone_remove_diagonal_gates_before_measure(QkCircuit *circuit)
Führen Sie die RemoveDiagonalGatesBeforeMeasure in einem Stromkreis durch.
Weitere Informationen zum Pass finden Sie in der qk_transpiler_pass_remove_diagonal_gates_before_measure Funktion.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit kein gültiger, nicht-null Zeiger auf QkCircuit ist.
Parameter
- Schaltung – Ein Zeiger auf die Schaltung, auf der dieser Durchlauf ausgeführt werden soll.
qk_transpiler_pass_standalone_remove_identity_equivalent
void qk_transpiler_pass_standalone_remove_identity_equivalent(QkCircuit *circuit, const QkTarget *target, double approximation_degree)
Führen Sie den RemoveIdentityEquivalent Transpiler-Durchlauf in einem Stromkreis durch.
Weitere Informationen zum Pass finden Sie in der qk_transpiler_pass_remove_identity_equivalent Funktion.
Diese Funktion ist multithreaded und startet standardmäßig einen Thread-Pool mit einer Anzahl von Threads, die der Anzahl der CPUs entspricht. Die Anzahl der Threads können Sie über die Umgebungsvariable RAYON_NUM_THREADS anpassen. Wenn man beispielsweise „ RAYON_NUM_THREADS=4 “ festlegt, wird der Thread-Pool auf 4 Threads begrenzt.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder target kein gültiger, nicht-null Zeiger auf QkCircuit und QkTarget ist.
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem RemoveIdentityEquivalent laufen soll. Diese Schaltung, auf die verwiesen wird, wird mit der geänderten Schaltung aktualisiert, wenn der Durchgang in der Lage ist, irgendwelche Gatter zu entfernen.
- target - Das Ziel für den RemoveIdentityEquivalent Durchgang. Wenn
approximation_degreeaufNANgesetzt ist, wird die Toleranz für die Feststellung, ob ein Vorgang der Identität entspricht, auf die gemeldete Fehlerquote im Ziel gesetzt. Andernfalls wird dietargetnicht verwendet, da die Toleranz unabhängig vom Ziel ist. - approximation_degree - Der zu approximierende Grad für die Äquivalenzprüfung. Dies kann ein Fließkommawert zwischen 0 und 1 oder
NANsein. Wenn der Wert 1 ist, erfolgt keine Annäherung an die Fließkommagenauigkeit. Bei einem Wert < 1 wird dies als Skalierungsfaktor für die Abschneidetreue verwendet. Wenn der WertNANist, entspricht dies in etwa der intargetangegebenen Treue für das Tor.
qk_transpiler_pass_standalone_sabre_layout
QkTranspileLayout *qk_transpiler_pass_standalone_sabre_layout(QkCircuit *circuit, const QkTarget *target, const QkSabreLayoutOptions *options)
Führen Sie den SabreLayout Transpiler-Durchlauf in einem Stromkreis durch.
Der SabreLayout pass wählt ein Layout über ein iteratives bidirektionales Routing der Eingangsschaltung.
Ausgehend von einem zufälligen Anfangslayout führt der Algorithmus ein vollständiges Routing der Schaltung durch, um ein endgültiges Layout zu erhalten. Dieses endgültige Layout wird dann als erstes Layout für das Routing der umgekehrten Schaltung verwendet. Der Algorithmus durchläuft mehrere Iterationen, bis er ein anfängliches Layout findet, das die gesamten Routingkosten reduziert.
Diese Methode nutzt die Reversibilität von Quantenschaltungen und versucht, globale Schaltkreisinformationen in die Wahl des anfänglichen Layouts einzubeziehen.
In diesem Durchgang werden sowohl das Layout als auch das Routing ausgeführt und die Schaltung so umgewandelt, dass das Layout auf den Eingang angewendet wird (d. h. der Ausgangsschaltung werden Ancilla-Qubits für unbenutzte Qubits auf der Kopplungskarte zugewiesen und die Qubits werden neu geordnet, damit sie mit den zugeordneten physischen Qubits übereinstimmen), und dann wird das Routing angewendet. Dies geschieht, weil der Durchlauf parallele Seed-Versuche mit verschiedenen zufälligen Seeds durchführt, um das zufällige Anfangslayout auszuwählen und dann den gerouteten Ausgang zu wählen, der die geringste Anzahl an benötigten Swap-Gates ergibt. Diese abschließende Swap-Berechnung entspricht der Durchführung eines endgültigen Routings, so dass es effizienter ist, sie nach der Berechnung durchzuführen.
Diese Funktion ist multithreadingfähig und startet standardmäßig einen Thread-Pool mit Threads, die der Anzahl der CPUs entsprechen. Sie können die Anzahl der Threads mit der Umgebungsvariablen RAYON_NUM_THREADS einstellen. Zum Beispiel würde die Einstellung RAYON_NUM_THREADS=4 den Thread-Pool auf 4 Threads begrenzen.
Referenzen
[1] Henry Zou und Matthew Treinish und Kevin Hartman und Alexander Ivrii und Jake Lishman. “LightSABRE: Ein leichtgewichtiger und verbesserter SABRE-Algorithmus" arXiv:2409.08368
[2] Li, Gushu, Yufei Ding, und Yuan Xie. "Das Qubit-Mapping-Problem angehen
für Quantengeräte der NISQ-Ära" ASPLOS 2019.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder target kein gültiger, nicht-null Zeiger auf QkCircuit und QkTarget ist.
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem SabreLayout laufen soll. Die Schaltung wird an Ort und Stelle geändert und die Zuweisungen der ursprünglichen Schaltung werden durch diese Funktion freigegeben.
- target - Ein Zeiger auf das Ziel, auf dem SabreLayout ausgeführt werden soll
- options - Ein Zeiger auf die Optionen für SabreLayout
Rückgabe
Das Transpile-Layout, das die durch den Durchlauf verursachte Layout- und Ausgabe-Permutation beschreibt
qk_transpiler_pass_standalone_split_2q_unitaries
QkTranspileLayout *qk_transpiler_pass_standalone_split_2q_unitaries(QkCircuit *circuit, double requested_fidelity, bool split_swaps)
Führen Sie den Transpiler-Durchlauf „ Split2QUnitaries “ für eine Schaltung aus.
Weitere Informationen zum Pass finden Sie in der qk_transpiler_pass_split_2q_unitaries Funktion.
Sicherheit
Das Verhalten ist undefiniert, wenn circuit kein gültiger, nicht-null Zeiger auf QkCircuit ist.
Parameter
- circuit – Ein veränderbare Zeiger auf den Schaltkreis, auf dem „ Split2QUnitaries “ ausgeführt werden soll.
- requested_fidelity - Erlaubte Toleranz für die Aufteilung von Zwei-Qubit-Unitarien und Gatterzerlegungen.
- split_swaps - Ob versucht werden soll, Swap-Gates aufzuteilen, was zu einer Permutation der Qubits führt.
Rückgabe
Wenn irgendwelche Swap-äquivalenten Einheiten geteilt werden, gibt diese Funktion einen Zeiger auf eine TranspileLayout zurück, die die durch diese Schaltkreisoptimierung induzierte Permutation enthält. Wenn keine Swap-äquivalenten Einheiten geteilt werden, ist dies ein Null-Zeiger.
qk_transpiler_pass_standalone_2q_peephole_optimization
void qk_transpiler_pass_standalone_2q_peephole_optimization(QkCircuit *circuit, const QkTarget *target, double approximation_degree)
Führen Sie den Transpiler-Durchlauf „ TwoQubitPeepholeOptimization “ aus.
Einzelheiten zu den Durchläufen finden Sie in der qk_transpiler_pass_2q_peephole_optimization() Funktionsdokumentation.
Diese Funktion ist multithreaded und führt die Analyse parallel durch, wobei alle auf Ihrem lokalen System verfügbaren Kerne genutzt werden. Weitere Informationen dazu, wie Sie das Threading-Verhalten von Qiskit allgemein steuern können – was auch diesen Durchlauf beeinflusst –, finden Sie im Kon figurationshandbuch
Beispiel
QkTarget *target = qk_target_new(2);
uint32_t current_num_qubits = qk_target_num_qubits(target);
QkTargetEntry *cx_entry = qk_target_entry_new(QkGate_CX);
for (uint32_t i = 0; i < current_num_qubits - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
double inst_error = 0.0090393 * (current_num_qubits - i);
double inst_duration = 0.020039;
qk_target_entry_add_property(cx_entry, qargs, 2, inst_duration, inst_error);
}
QkExitCode result_cx = qk_target_add_instruction(target, cx_entry);
QkCircuit *qc = qk_circuit_new(2, 0);
uint32_t forward[2] = {0, 1};
uint32_t reverse[2] = {1, 0};
for (int i = 0; i < 10; i++) {
if (i % 2) {
qk_circuit_gate(qc, QkGate_CX, forward, NULL);
} else {
qk_circuit_gate(qc, QkGate_CX, reverse, NULL);
}
}
qk_transpiler_pass_standalone_2q_peephole_optimization(qc, target, 1.0);
qk_circuit_free(qc);
qk_target_free(target);Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder target kein gültiger, nicht-null Zeiger auf QkCircuit und QkTarget ist.
Diese Funktion darf nicht in einem Kontext aufgerufen werden, in dem der Circuit Objekte enthält, die von Python verwaltet werden. Da die Funktion intern multithreaded ist, müssen die Worker-Threads möglicherweise den GIL erwerben, um auf die Attribute der „ Python “-Gates in der Schaltung zugreifen zu können, was zu einem Deadlock führt. Sie sollten die Raumklasse TwoQubitPeepholeOptimization „ Python “ verwenden, um diesen Durchlauf auszuführen, wenn Schaltungselemente vorhanden sind, die zu „ Python “ gehören.
Parameter
- circuit – Ein Zeiger auf den Schaltkreis, auf dem „ TwoQubitPeepholeOptimization “ ausgeführt werden soll
- target – Ein Zeiger auf das Ziel, mit dem „ TwoQubitPeepholeOptimization “ ausgeführt werden soll
- approximation_degree – Heuristischer Regler zur Schaltungsnäherung ( 1.0=no -Näherung, 0.0=maximal -Näherung). Durch eine Näherung lässt sich die synthetisierte Schaltung verkleinern, allerdings auf Kosten einer Abweichung vom ursprünglichen Unitar. Wenn NAN, basiert die Zielannäherung auf den Gate-Fidelitäten in der
target.
qk_transpiler_pass_standalone_unitary_synthesis
void qk_transpiler_pass_standalone_unitary_synthesis(QkCircuit *circuit, const QkTarget *target, size_t min_qubits, double approximation_degree)
Führen Sie den UnitarySynthesis Transpiler-Durchgang aus.
Der UnitarySynthesis Transpiler-Pass synthetisiert alle UnitaryGates in der Schaltung in Gatter, die im Ziel verfügbar sind.
Dieser Durchlauf ist multithreaded und startet standardmäßig möglicherweise einen Thread-Pool mit einer Anzahl von Threads, die der Anzahl der CPUs entspricht. Die Anzahl der Threads können Sie über die RAYON_NUM_THREADS Umgebungsvariable anpassen. Wenn man beispielsweise einstellt RAYON_NUM_THREADS=4 , würde der Thread-Pool auf 4 Threads begrenzt werden.
Beispiel
QkTarget *target = qk_target_new(2);
uint32_t current_num_qubits = qk_target_num_qubits(target);
QkTargetEntry *cx_entry = qk_target_entry_new(QkGate_CX);
for (uint32_t i = 0; i < current_num_qubits - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
double inst_error = 0.0090393 * (current_num_qubits - i);
double inst_duration = 0.020039;
qk_target_entry_add_property(cx_entry, qargs, 2, inst_duration, inst_error);
}
QkExitCode result_cx = qk_target_add_instruction(target, cx_entry);
QkCircuit *qc = qk_circuit_new(2, 0);
QkComplex64 c0 = {0., 0.};
QkComplex64 c1 = {1., 0.};
QkComplex64 unitary[16] = {c1, c0, c0, c0, // row 0
c0, c1, c0, c0, // row 1
c0, c0, c1, c0, // row 2
c0, c0, c0, c1}; // row 3
uint32_t qargs[2] = {0, 1};
qk_circuit_unitary(qc, unitary, qargs, 2, false);
qk_transpiler_pass_standalone_unitary_synthesis(qc, target, 0, 1.0);Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder target kein gültiger, nicht-null Zeiger auf QkCircuit und QkTarget ist.
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem UnitarySynthesis ausgeführt werden soll
- target - Ein Zeiger auf das Ziel, auf dem UnitarySynthesis ausgeführt werden soll
- min_qubits - Die Mindestanzahl von Qubits in der zu synthetisierenden Unitary. Wenn die Unitary weniger als die angegebene Anzahl von Qubits hat, wird sie nicht synthetisiert.
- approximation_degree - heuristische Skala, die für die Annäherung von Schaltkreisen verwendet wird ( 1.0=no approximation, 0.0=maximal approximation). Eine Annäherung kann die synthetisierte Schaltung billiger machen, allerdings um den Preis, dass sie sich von der ursprünglichen Einheitlichkeit entfernt. Bei NAN basiert die Zielannäherung auf den Gattertreuewerten in
target.
qk_transpiler_pass_standalone_vf2_layout_average
QkVF2LayoutResult *qk_transpiler_pass_standalone_vf2_layout_average(const QkCircuit *circuit, const QkTarget *target, const QkVF2LayoutConfiguration *config, bool strict_direction)
Verwenden Sie den Algorithmus „ VF2 “, um (wenn möglich) ein Layout für die Eingangsschaltung auszuwählen, wobei eine rauschbewusste Bewertungsheuristik verwendet wird, die sich ausschließlich auf Hardware-Fehlerraten und nicht auf die spezifischen Gatter in der Schaltung stützt.
Diese Funktion entspricht dem Durchgang „ PythonVF2Layout -Raum“.
Diese Funktion eignet sich für Schaltungen, die noch nicht vollständig auf Hardware heruntergebracht wurden. Wenn Ihr Schaltkreis bereits vollständig auf Hardware heruntergebrochen wurde und Sie das Layout für einen exakten Interaktionsgraphen verbessern möchten, verwenden Sie qk_transpile_pass_standalone_vf2_layout_exact stattdessen.
Wenn bei diesem Durchgang eine Lösung gefunden wird, bedeutet dies, dass es ein "perfektes Layout" gibt und keine weitere Tauschzuordnung oder Routing erforderlich ist. Es gibt jedoch nicht immer eine mögliche Lösung, oder es gibt zwar eine Lösung, aber sie wird nicht innerhalb der beim Aufruf des Passes angegebenen Grenzen gefunden.
Beispiel
QkTarget *target = qk_target_new(5);
uint32_t current_num_qubits = qk_target_num_qubits(target);
QkTargetEntry *cx_entry = qk_target_entry_new(QkGate_CX);
for (uint32_t i = 0; i < current_num_qubits - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
double inst_error = 0.0090393 * (current_num_qubits - i);
double inst_duration = 0.020039;
qk_target_entry_add_property(cx_entry, qargs, 2, inst_duration, inst_error);
}
QkExitCode result_cx = qk_target_add_instruction(target, cx_entry);
QkCircuit *qc = qk_circuit_new(4, 0);
for (uint32_t i = 0; i < qk_circuit_num_qubits(qc) - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
for (uint32_t j = 0; j<i+1; j++) {
qk_circuit_gate(qc, QkGate_CX, qargs, NULL);
}
}
QkVF2LayoutConfiguration *config = qk_vf2_layout_configuration_new();
qk_vf2_layout_configuration_set_call_limit(config, 10000, 10000);
QkVF2LayoutResult *layout_result = qk_transpiler_pass_standalone_vf2_layout_average(qc, target, config, false);
qk_vf2_layout_result_free(layout_result);
qk_vf2_layout_configuration_free(config);Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder kein gültiger, von Null target ungleicher Zeiger auf und QkCircuit ist QkTarget. Das Verhalten ist undefiniert, wenn ein config Nicht-Null-Zeiger ist, der nicht auf ein gültiges QkVF2LayoutConfiguration Objekt zeigt (ein Null-Zeiger ist jedoch zulässig).
Parameter
- circuit - Ein Zeiger auf den Stromkreis, auf dem VF2Layout ausgeführt werden soll
- target - Ein Zeiger auf das Ziel, auf dem der VF2Layout Pass ausgeführt werden soll
- config – Ein Zeiger auf die
QkVF2LayoutConfigurationKonfigurationsstruktur. Wenn dieser Zeiger null ist, werden die Standardwerte für den Durchgang verwendet. - strict_direction – Wenn diese
trueOption gesetzt ist, berücksichtigt der Durchlauf die Kantenrichtung in der im beschriebenen Konnektivitätsmodelltarget. In der Regel ist es ratsam, diesenfalseWert auf einzustellen, da die Fehlerheuristik ohnehin schon sehr ungenau ist und Zwei-Qubit-Gatter fast immer so synthetisiert werden können, dass sie die Richtung „umkehren“, wobei nur lokale Ein-Qubit-Gatter und das Zwei-Qubit-Gatter in der nativen Richtung verwendet werden.
Rückgabe
Ein Zeiger auf ein Ergebnisobjekt, das die Ergebnisse des Durchlaufs enthält. Dieses Objekt wird im Heap zugewiesen und muss mit der qk_vf2_layout_result_free Funktion freigegeben werden.
qk_transpiler_pass_standalone_vf2_layout_exact
QkVF2LayoutResult *qk_transpiler_pass_standalone_vf2_layout_exact(const QkCircuit *circuit, const QkTarget *target, const QkVF2LayoutConfiguration *config)
Verwenden Sie den Algorithmus „ VF2 “, um (wenn möglich) ein Layout für die Eingangsschaltung auszuwählen, wobei eine rauschbewusste Bewertungsheuristik verwendet wird, die erfordert, dass das Ergebnis bereits vollständig mit der Hardware kompatibel ist.
Diese Funktion entspricht dem Durchlauf „ PythonVF2PostLayout -Raum“ mit strict_direction=True.
Diese Funktion eignet sich für Schaltungen, die bereits vollständig auf Hardware heruntergebrochen wurden, und Sie möchten nun prüfen, ob eine Qubit-Permutation zu besseren geschätzten Fehlerraten führen kann. Wenn Ihr Schaltkreis noch nicht durch Hardware unterstützt wird, verwenden Sie qk_transpile_pass_standalone_vf2_layout_average stattdessen.
In der Regel rufen Sie diesen Durchlauf nach dem Layout, dem Routing, der Übersetzung in einen nativen Basissatz und der Optimierung auf, sodass die Eingabeschaltung bereits auf Hardware mit den Qubit-Indizes, die sich auf physikalische Qubits beziehen, ausführbar ist. Der Pass gibt ein Ergebnis zurück, das eines der folgenden Elemente angibt:
- Es gibt eine bessere Wahl für die anfängliche Zuordnung von virtuellen zu physikalischen Qubits als die, die derzeit vom Schaltkreis verwendet wird.
- Die aktuelle Auswahl an physikalischen Qubits ist die beste, die innerhalb der Aufrufgrenze gefunden wurde.
- Es gibt keine gültige Auswahl für die Zuordnung von virtuellen zu physischen Elementen, die zu einer ausführbaren Schaltung führt (oder zumindest konnte der Durchlauf innerhalb der festgelegten Grenzen keine finden).
In den ersten beiden Fällen wird qk_vf2_layout_has_match zurückgegeben true. Nur im ersten Fall wird qk_vf2_layout_has_improvement zurückgegeben true.
Beispiel
QkTarget *target = qk_target_new(5)
QkTargetEntry *cx_entry = qk_target_entry_new(QkGate_CX);
for (uint32_t i = 0; i < current_num_qubits - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
double inst_error = 0.0090393 * (current_num_qubits - i);
double inst_duration = 0.020039;
qk_target_entry_add_property(cx_entry, qargs, 2, inst_duration, inst_error);
}
QkExitCode result_cx = qk_target_add_instruction(target, cx_entry);
QkCircuit *qc = qk_circuit_new(4, 0);
for (uint32_t i = 0; i < qk_circuit_num_qubits(qc) - 1; i++) {
uint32_t qargs[2] = {i, i + 1};
for (uint32_t j = 0; j<i+1; j++) {
qk_circuit_gate(qc, QkGate_CX, qargs, NULL);
}
}
QkVF2LayoutConfiguration *config = qk_vf2_layout_configuration_new();
qk_vf2_layout_configuration_call_limit(config, 10000, 10000);
QkVF2LayoutResult *layout_result = qk_transpiler_pass_standalone_vf2_layout_exact(qc, target, config);
qk_vf2_layout_result_free(layout_result);
qk_vf2_layout_configuration_free(config);Sicherheit
Das Verhalten ist undefiniert, wenn circuit oder kein gültiger, von Null target ungleicher Zeiger auf und QkCircuit ist QkTarget. Das Verhalten ist undefiniert, wenn ein config Nicht-Null-Zeiger ist, der nicht auf ein gültiges QkVF2LayoutConfiguration Objekt zeigt (ein Null-Zeiger ist jedoch zulässig).
Parameter
- Schaltkreis – Ein Zeiger auf den Schaltkreis, auf dem die Layout-Suche durchgeführt werden soll.
- Ziel – Ein Zeiger auf das Ziel, das die QPU darstellt.
- config – Ein Zeiger auf die
QkVF2LayoutConfigurationKonfigurationsstruktur. Wenn dieser Zeiger null ist, werden die Standardwerte für den Durchgang verwendet.
Rückgabe
Ein Zeiger auf ein Ergebnisobjekt, das die Ergebnisse des Durchlaufs enthält. Dieses Objekt wird im Heap zugewiesen und muss mit der qk_vf2_layout_result_free Funktion freigegeben werden.
qk_transpiler_pass_standalone_vf2_layout
QkVF2LayoutResult *qk_transpiler_pass_standalone_vf2_layout(const QkCircuit *circuit, const QkTarget *target, bool strict_direction, int64_t call_limit, double time_limit, int64_t max_trials)
Veraltete Version von qk_transpiler_pass_standalone_vf2_layout_average.
Diese veraltete Schnittstelle verwendet nicht QkVf2LayoutConfiguration und hat einen Namen, der keine klare Aussage darüber trifft, wie sie mit der Fehlerheuristik umgeht (sie bildet den Durchschnitt aller Gates im QkTarget für ein bestimmtes Qubit oder eine bestimmte Verbindung).
Ersetzt durch qk_transpiler_pass_standalone_vf2_layout_average().
Sicherheit
Die Sicherheitsanforderungen von qk_transpiler_pass_standalone_vf2_layout_average müssen für circuit und eingehalten target werden.
Parameter
- Schaltkreis – Wie in
qk_transpiler_pass_standalone_vf2_layout_average. - Ziel – Wie in
qk_transpiler_pass_standalone_vf2_layout_average. - strict_direction – Wie in
qk_transpiler_pass_standalone_vf2_layout_average. - call_limit – Wie in
qk_vf2_layout_configuration_set_call_limit, aber derselbe Wert wird sowohl fürbeforeals auch fürafterverwendet. - time_limit – Wie in
qk_vf2_layout_configuration_set_time_limit. - max_trials – Wie in
qk_vf2_layout_configuration_set_max_trials.
Rückgabe
Wie in qk_transpiler_pass_standalone_vf2_layout_average.