QkObs
typedef struct QkObs QkObsEin Observable über Pauli-Basen, das seine Daten in einem Qubit-sparsen Format speichert.
Mathematik
Diese Beobachtungsgröße ist eine Summe über die Pauli-Operatoren und Pauli-Eigenzustands-Projektoren, wobei jeder Term mit einer komplexen Zahl gewichtet ist. Das heißt, die vollständige Beobachtungsgröße ist
für komplexe Zahlen und Ein-Qubit-Operatoren, die auf ein Qubit aus einem beschränkten Alphabet wirken. Die Summe über ist die Summe der einzelnen Terme, und das Tensorprodukt ergibt die Operatorenketten. Das Alphabet der zulässigen Ein-Qubit-Operatoren, aus dem gezogen wird, besteht aus den Pauli-Operatoren und den Pauli-Eigenzustands-Projektionsoperatoren. Explizit sind dies:
Bediener | QkBitTerm | Numerischer Wert |
|---|---|---|
| (Identität) | Nicht gespeichert. | Nicht gespeichert. |
| (Pauli X) | QkBitTerm_X | 0b0010 (2) |
| (Pauli Y) | QkBitTerm_Y | 0b0011 (3) |
| (Pauli Z) | QkBitTerm_Z | 0b0001 (1) |
| (Projektor zum positiven Eigenzustand von X) | QkBitTerm_Plus | 0b1010 (10) |
| (Projektor zum negativen Eigenzustand von X) | QkBitTerm_Minus | 0b0110 (6) |
| (Projektor auf positiven Eigenzustand von Y) | QkBitTerm_Right | 0b1011 (11) |
| (Projektor auf den negativen Eigenzustand von Y) | QkBitTerm_Left | 0b0111 (7) |
| (Projektor zum positiven Eigenzustand von Z) | QkBitTerm_Zero | 0b1001 (9) |
| (Projektor zum negativen Eigenzustand von Z) | QkBitTerm_One | 0b0101 (5) |
Da sowohl die Paulis als auch ihre Projektoren erlaubt sind, bildet das erlaubte Alphabet eine überkomplette Basis des Operatorraums. Das bedeutet, dass es keine eindeutige Summierung gibt, um eine bestimmte Beobachtung darzustellen. Infolgedessen erfordert der Vergleich zusätzliche Sorgfalt, und die Verwendung von qk_obs_canonicalize für zwei mathematisch äquivalente Observablen führt möglicherweise nicht zur gleichen Darstellung.
QkObs verwendet seine besondere überkomplette Basis mit dem Ziel, die "Effizienz der Messung" mit der "Effizienz der Darstellung" gleichzusetzen. Zum Beispiel kann die beobachtbare effizient auf Hardware mit einfachen Messungen gemessen werden, kann aber nur in Form von Paulis als dargestellt werden, was gespeicherte Terme erfordert. QkObs benötigt nur einen einzigen Begriff, um dies zu speichern. Der Nachteil dabei ist, dass es unpraktisch ist, eine beliebige Matrix zu nehmen und die beste QkObs Darstellung zu finden. In der Regel werden Sie eine QkObs direkt erstellen wollen, anstatt zu versuchen, sie zu zerlegen.
Darstellung
Die interne Darstellung eines QkObs speichert nur die nicht-identischen Qubit-Operatoren. Dies macht es wesentlich effizienter, Observablen wie darzustellen; QkObs benötigt eine Speichermenge, die linear zur Gesamtzahl der Qubits ist. Die Terme werden komprimiert gespeichert, ähnlich wie beim komprimierten Sparse-Zeilen-Format von Sparse-Matrizen. In dieser Analogie sind die Terme der Summe die "Zeilen" und die Qubit-Terme die "Spalten", wobei ein fehlender Eintrag die Identität und nicht eine Null darstellt. Genauer gesagt, besteht die Darstellung aus vier zusammenhängenden Feldern:
Attribut zugänglich durch | Länge | Beschreibung |
|---|---|---|
qk_obs_coeffs | Der komplexe skalare Multiplikator für jeden Term. | |
qk_obs_bit_terms | Jeder der nicht-identischen Ein-Qubit-Terme für alle Operatoren, in dieser Reihenfolge. Diese entsprechen der Nicht-Identität in der Summenbeschreibung, bei der die Einträge in der Reihenfolge der aufsteigenden zuerst gespeichert werden und in der Reihenfolge der aufsteigenden innerhalb jedes Terms. | |
qk_obs_indices | Das entsprechende Qubit ( ) für jeden der Bit-Terme. QkObs erfordert, dass diese Liste begrifflich sortiert ist, und Algorithmen können sich darauf verlassen, dass diese Invariante eingehalten wird. | |
qk_obs_boundaries | Die Indizes, die die Bit-Terme und Indizes in vollständige Terme unterteilen. Für den Term wird sein komplexer Koeffizient unter dem Index i gespeichert, und seine nicht-identischen Ein-Qubit-Operatoren und ihre entsprechenden Qubits liegen im Bereich [boundaries[i], boundaries[i+1]) in den Bit-Termen bzw. Indizes. Die Begrenzungen haben immer eine explizite 0 als erstes Element. |
Der Längenparameter ist die Anzahl der Terme in der Summe und kann mit qk_obs_num_terms abgefragt werden. Der Parameter ist die Gesamtzahl der nicht-identischen Ein-Qubit-Terme und kann mit qk_obs_len abgefragt werden.
Zur Veranschaulichung einige Beispiele:
- im Falle eines Null-Operators haben die Grenzen die Länge 1 (eine einzige 0) und alle anderen Vektoren sind leer.
- im Falle eines vollständig vereinfachten Identitätsoperators sind die Grenzen
{0, 0}, die Koeffizienten haben einen einzigen Eintrag, und sowohl die Bit-Terme als auch die Indizes sind leer. - für den Operator , die Grenzen sind
{0, 2, 4}, die Koeffizienten sind{1.0, -1.0}, die Bit-Terme sind{QkBitTerm_Z, QkBitTerm_Z, QkBitTerm_Y, QkBitTerm_X}und die Indizes sind{0, 2, 1, 3}. Der Operator kann auf mehr als vier Qubits wirken, je nach Anzahl der Qubits (sieheqk_obs_num_qubits). Beachten Sie, dass die Einzelbit-Terme und -Indizes in begrifflich sortierter Reihenfolge angeordnet sind.
Diese Fälle sind nichts Besonderes, sie entsprechen voll und ganz den Regeln und sollten nicht besonders behandelt werden müssen.
Kanonische Ordnung
Für jede beliebige mathematische Beobachtungsgröße gibt es mehrere Möglichkeiten, sie mit QkObs darzustellen. So können beispielsweise dieselbe Menge von Einzelbit-Termen und ihre entsprechenden Indizes mehrfach in der Beobachtungsgröße erscheinen. Mathematisch gesehen ist dies gleichbedeutend mit einem einzigen Term, bei dem alle Koeffizienten addiert werden. In ähnlicher Weise können die Terme der Summe in einer QkObs in beliebiger Reihenfolge stehen, während sie dieselbe Beobachtungsgröße darstellen, da die Addition kommutativ ist (obwohl die Gleitkommadotation nicht assoziativ ist, gibt QkObs keine Garantien für die Reihenfolge der Addition).
Diese beiden Kategorien der Darstellungsentartung können dazu führen, dass der Operator Gleichheit, qk_obs_equal, behauptet, dass zwei Observablen nicht gleich sind, obwohl sie dasselbe Objekt darstellen. In diesen Fällen kann es sinnvoll sein, eine kanonische Form zu definieren, die einen strukturellen Vergleich der Beobachtungswerte ermöglicht. Sie können eine QkObs in kanonische Form bringen, indem Sie die Funktion qk_obs_canonicalize verwenden. Die genaue Reihenfolge der Begriffe in der kanonischen Ordnung ist nicht festgelegt und kann sich zwischen verschiedenen Versionen von Qiskit ändern. Innerhalb der gleichen Version von Qiskit können Sie jedoch zwei Observablen strukturell vergleichen, indem Sie ihre vereinfachten Formen vergleichen.
Wenn Sie beim Vergleich die Gleitkommatoleranz berücksichtigen wollen, ist es am sichersten, ein Rezept wie das folgende zu verwenden:
bool equivalent(QkObs *left, QkObs *right, double tol) {
// compare a canonicalized version of left - right to the zero observable
QkObs *neg_right = qk_obs_multiply(right, &(QkComplex64){-1, 0});
QkObs *diff = qk_obs_add(left, neg_right);
QkObs *canonical = qk_obs_canonicalize(diff, tol);
QkObs *zero = qk_obs_zero(qk_obs_num_qubits(left));
bool equiv = qk_obs_equal(diff, zero);
// free all temporary variables
qk_obs_free(neg_right);
qk_obs_free(diff);
qk_obs_free(canonical);
qk_obs_free(zero);
return equiv;
}Die kanonische Form, die von qk_obs_canonicalize allein erzeugt wird, wird aufgrund des überkompletten Basisalphabets nicht universell alle Observablen erkennen, die äquivalent sind.
Indexierung
Einzelne beobachtbare Summenterme in QkObs können über qk_obs_term aufgerufen werden und geben Objekte vom Typ QkObsTerm zurück. Diese Terme enthalten dann Felder mit dem Koeffizienten des Terms, seinen Bit-Terms, Indizes und der Anzahl der Qubits, für die er definiert ist. Zusammen mit der Information über die Anzahl der Terme können Sie über alle beobachtbaren Terme iterieren als
size_t num_terms = qk_obs_num_terms(obs); // obs is QkObs*
for (size_t i = 0; i < num_terms; i++) {
QkObsTerm term; // allocate term on stack
int exit = qk_obs_term(obs, i, &term); // get the term (exit > 0 upon index errors)
// do something with the term...
}Das Auffüllen eines QkObsTerm über qk_obs_term bezieht sich auf die Daten des ursprünglichen QkObs. Eine Änderung der Bit-Terme oder Indizes verändert das Beobachtungsobjekt und kann es in einen inkohärenten Zustand versetzen.
Bauwesen
QkObs kann durch Initialisierung einer leeren Beobachtungsgröße (mit qk_obs_zero) und iteratives Hinzufügen von Termen (mit qk_obs_add_term) konstruiert werden. Alternativ kann eine Beobachtungsgröße aus "rohen" Daten (mit qk_obs_new) erstellt werden, wenn alle internen Daten angegeben sind. Dabei ist darauf zu achten, dass die Daten kohärent sind und eine gültige Beobachtungsgröße ergeben.
Funktion | Zusammenfassung |
|---|---|
qk_obs_zero | Konstruieren Sie eine leere Observable für eine bestimmte Anzahl von Qubits. |
qk_obs_identity | Konstruieren Sie das Identitätsobservable für eine bestimmte Anzahl von Qubits. |
qk_obs_new | Konstruieren Sie eine Observable aus den Rohdaten-Arrays. |
Mathematische Manipulation
QkObs unterstützt grundlegende arithmetische Operationen zwischen Observablen oder mit Skalaren. Sie haben folgende Möglichkeiten:
- Füge zwei Observables mithilfe von
qk_obs_addundqk_obs_add_inplace - mit einer komplexen Zahl multiplizieren mit
qk_obs_multiplyundqk_obs_multiply_inplace - zwei Observablen über
qk_obs_composezusammensetzen (multiplizieren) undqk_obs_compose_map - berechnen
left + scalar * rightfür zwei Observablen und einen komplexen Skalar mitqk_obs_scaled_addundqk_obs_scaled_add_inplace
Funktionen
qk_obs_zero
QkObs *qk_obs_zero(uint32_t num_qubits)
Konstruieren Sie die Null-Beobachtungsgröße (ohne Terme).
Beispiel
QkObs *zero = qk_obs_zero(100);Parameter
- num_qubits - Die Anzahl der Qubits, für die die Observable definiert ist.
Rückgabe
Ein Zeiger auf das erstellte Observable.
qk_obs_identity
QkObs *qk_obs_identity(uint32_t num_qubits)
Konstruieren Sie den Identitätsbeobachter.
Beispiel
QkObs *identity = qk_obs_identity(100);Parameter
- num_qubits - Die Anzahl der Qubits, für die die Observable definiert ist.
Rückgabe
Ein Zeiger auf das erstellte Observable.
qk_obs_new
QkObs *qk_obs_new(uint32_t num_qubits, uint64_t num_terms, uint64_t num_bits, QkComplex64 *coeffs, QkBitTerm *bit_terms, uint32_t *indices, size_t *boundaries)
Konstruieren Sie eine neue Observable aus Rohdaten.
Jedes der Zeigerargumente darf genau dann null NULL sein, wenn seine entsprechende Länge null ist.
Beispiel
// define the raw data for the 100-qubit observable |01><01|_{0, 1} - |+-><+-|_{98, 99}
uint32_t num_qubits = 100;
uint64_t num_terms = 2; // we have 2 terms: |01><01|, -1 * |+-><+-|
uint64_t num_bits = 4; // we have 4 non-identity bits: 0, 1, +, -
QkComplex64 coeffs[] = {{1, 0}, {-1, 0}};
QkBitTerm bits[4] = {QkBitTerm_Zero, QkBitTerm_One, QkBitTerm_Plus, QkBitTerm_Minus};
uint32_t indices[4] = {0, 1, 98, 99}; // <-- e.g. {1, 0, 99, 98} would be invalid
size_t boundaries[3] = {0, 2, 4};
QkObs *obs = qk_obs_new(
num_qubits, num_terms, num_bits, coeffs, bits, indices, boundaries
);
qk_obs_free(obs);Sicherheit
Das Verhalten ist undefiniert, wenn eine der folgenden Bedingungen verletzt wird:
coeffsist ein Zeiger auf einQkComplex64Array der Längenum_termsbit_termsist ein Zeiger auf ein Array gültigerQkBitTermElemente der Längenum_bitsindicesist ein Zeiger auf ein Arrayuint32_tder Längenum_bits, das streng aufsteigend nach Begriffen sortiert ist, und jedes Element ist kleiner alsnum_qubitsboundariesist ein Zeiger auf ein Arraysize_tder Längenum_terms + 1, das in aufsteigender Reihenfolge sortiert ist, wobei das erste Element 0 ist und das letzte Element kleiner ist alsnum_terms
Parameter
- num_qubits - Die Anzahl der Qubits, für die die Observable definiert ist.
- num_terms - Die Anzahl der Begriffe.
- num_bits - Die Gesamtzahl der Nicht-Identitäts-Bit-Terme.
- coeffs - Ein Zeiger auf das erste Element des Koeffizienten-Arrays, das die Länge
num_termshat. - bit_terms - Ein Zeiger auf das erste Element des Bit-Terms-Arrays, das die Länge
num_bitshat. - indices - Ein Zeiger auf das erste Element des Arrays indices, das die Länge
num_bitshat. Beachten Sie, dass diese pro Begriff inkrementell sortiert werden müssen. - boundaries - Ein Zeiger auf das erste Element des boundaries-Arrays, das die Länge
num_terms + 1hat.
Rückgabe
Wenn die Eingabedaten kohärent und die Konstruktion erfolgreich war, ist das Ergebnis ein Zeiger auf das Observable. Andernfalls wird ein Null-Zeiger zurückgegeben.
qk_obs_free
void qk_obs_free(QkObs *obs)
Befreien Sie das Beobachtbare.
Beispiel
QkObs *obs = qk_obs_zero(100);
qk_obs_free(obs);Sicherheit
Das Verhalten ist undefiniert, wenn obs nicht entweder null oder ein gültiger Zeiger auf eine QkObs ist.
Parameter
- obs - Ein Zeiger auf das freizugebende Observable.
qk_obs_add_term
QkExitCode qk_obs_add_term(QkObs *obs, const QkObsTerm *cterm)
Fügen Sie der Beobachtungsgröße einen Begriff hinzu.
Beispiel
uint32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);
QkComplex64 coeff = {1, 0};
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t indices[3] = {0, 1, 2};
QkObsTerm term = {coeff, 3, bit_terms, indices, num_qubits};
QkExitCode exit_code = qk_obs_add_term(obs, &term);Sicherheit
Das Verhalten ist undefiniert, wenn einer der folgenden Punkte verletzt wird:
obsist ein gültiger, nicht-null-Zeiger auf eineQkObsctermist ein gültiger, nicht-null-Zeiger auf eineQkObsTerm
Parameter
- obs - Ein Zeiger auf das Observable.
- cterm - Ein Zeiger auf den hinzuzufügenden Begriff.
Rückgabe
Ein Exit-Code. Dies ist >0 , wenn der Begriff nicht kohärent ist oder das Hinzufügen des Begriffs fehlschlägt.
qk_obs_term
QkExitCode qk_obs_term(QkObs *obs, uint64_t index, QkObsTerm *out)
Holen Sie sich einen beobachtbaren Begriff per Referenz.
Ein QkObsTerm enthält Zeiger auf die Indizes und Bit-Terme im Term, die zur Änderung der internen Daten des Observable verwendet werden können. Dies kann zu einem inkohärenten Zustand des Beobachtungsgegenstandes führen und sollte vermieden werden, es sei denn, es wird sehr sorgfältig darauf geachtet. Im Allgemeinen ist es sicherer, eine neue Observable zu konstruieren, anstatt zu versuchen, Änderungen an Ort und Stelle vorzunehmen.
Beispiel
QkObs *obs = qk_obs_identity(100);
QkObsTerm term;
QkExitCode exit_code = qk_obs_term(obs, 0, &term);
// out-of-bounds indices return an error code
// QkExitCode error = qk_obs_term(obs, 12, &term);Sicherheit
Das Verhalten ist undefiniert, wenn einer der folgenden Punkte verletzt wird
obsist ein gültiger, nicht-null-Zeiger auf eineQkObsoutist ein gültiger, nicht-null-Zeiger auf eineQkObsTerm
Parameter
- obs - Ein Zeiger auf das Observable.
- index - Der Index des zu ermittelnden Begriffs.
- out - Ein Zeiger auf
QkObsTerm, der zur Rückgabe des beobachtbaren Terms verwendet wird.
Rückgabe
Ein Exit-Code.
qk_obs_num_terms
size_t qk_obs_num_terms(const QkObs *obs)
Ermittelt die Anzahl der Begriffe in der Beobachtungsgröße.
Beispiel
QkObs *obs = qk_obs_identity(100);
size_t num_terms = qk_obs_num_terms(obs); // num_terms==1Sicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Parameter
- obs - Ein Zeiger auf das Observable.
Rückgabe
Die Anzahl der Terme in der Beobachtungsgröße.
qk_obs_num_qubits
uint32_t qk_obs_num_qubits(const QkObs *obs)
Ermittelt die Anzahl der Qubits, für die die Observable definiert ist.
Beispiel
QkObs *obs = qk_obs_identity(100);
uint32_t num_qubits = qk_obs_num_qubits(obs); // num_qubits==100Sicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Parameter
- obs - Ein Zeiger auf das Observable.
Rückgabe
Die Anzahl der Qubits, für die die Beobachtungsgröße definiert ist.
qk_obs_len
size_t qk_obs_len(const QkObs *obs)
Ermittelt die Anzahl der Bit-Terme/Indizes in der Observable.
Beispiel
QkObs *obs = qk_obs_identity(100);
size_t len = qk_obs_len(obs); // len==0, as there are no non-trivial bit termsSicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Parameter
- obs - Ein Zeiger auf das Observable.
Rückgabe
Die Anzahl der Terme in der Beobachtungsgröße.
qk_obs_coeffs
QkComplex64 *qk_obs_coeffs(QkObs *obs)
Holt einen Zeiger auf die Koeffizienten.
Damit können die Koeffizienten der Beobachtungsgröße gelesen und geändert werden. Der resultierende Zeiger ist zum Lesen für qk_obs_num_terms(obs) Elemente von QkComplex64 gültig.
Beispiel
QkObs *obs = qk_obs_identity(100);
size_t num_terms = qk_obs_num_terms(obs);
QkComplex64 *coeffs = qk_obs_coeffs(obs);
for (size_t i = 0; i < num_terms; i++) {
printf("%f + i%f\n", coeffs[i].re, coeffs[i].im);
}Sicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Parameter
- obs - Ein Zeiger auf das Observable.
Rückgabe
Ein Zeiger auf die Koeffizienten.
qk_obs_indices
uint32_t *qk_obs_indices(QkObs *obs)
Holt einen Zeiger auf die Indizes.
Damit können die Indizes der Observablen gelesen und geändert werden. Der resultierende Zeiger ist für qk_obs_len(obs) Elemente der Größe uint32_t zum Lesen gültig.
Beispiel
uint32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);
QkComplex64 coeff = {1, 0};
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t term_indices[3] = {0, 1, 2};
QkObsTerm term = {coeff, 3, bit_terms, term_indices, num_qubits};
qk_obs_add_term(obs, &term);
size_t len = qk_obs_len(obs);
uint32_t *indices = qk_obs_indices(obs);
for (size_t i = 0; i < len; i++) {
printf("index %i: %i\n", i, indices[i]);
}
qk_obs_free(obs);Sicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Parameter
- obs - Ein Zeiger auf das Observable.
Rückgabe
Ein Zeiger auf die Indizes.
qk_obs_boundaries
size_t *qk_obs_boundaries(QkObs *obs)
Holen Sie sich einen Zeiger auf die Begriffsgrenzen.
Damit können die Termgrenzen der Observablen gelesen und geändert werden. Der resultierende Zeiger ist für qk_obs_num_terms(obs) + 1 Elemente der Größe size_t zum Lesen gültig.
Beispiel
uint32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);
QkComplex64 coeff = {1, 0};
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t indices[3] = {0, 1, 2};
QkObsTerm term = {coeff, 3, bit_terms, indices, num_qubits};
qk_obs_add_term(obs, &term);
size_t num_terms = qk_obs_num_terms(obs);
size_t *boundaries = qk_obs_boundaries(obs);
for (size_t i = 0; i < num_terms + 1; i++) {
printf("boundary %i: %i\n", i, boundaries[i]);
}Sicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Parameter
- obs - Ein Zeiger auf das Observable.
Rückgabe
Ein Zeiger auf die Grenzen.
qk_obs_bit_terms
QkBitTerm *qk_obs_bit_terms(QkObs *obs)
Holen Sie sich einen Zeiger auf die Bit-Begriffe.
Damit können die Bit-Terme der Observablen gelesen und geändert werden. Der resultierende Zeiger ist für qk_obs_len(obs) Elemente der Größe uint8_t zum Lesen gültig.
Beispiel
uint32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);
QkComplex64 coeff = {1, 0};
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t indices[3] = {0, 1, 2};
QkObsTerm term = {coeff, 3, bit_terms, indices, num_qubits};
qk_obs_add_term(obs, &term);
size_t len = qk_obs_len(obs);
QkBitTerm *bits = qk_obs_bit_terms(obs);
for (size_t i = 0; i < len; i++) {
printf("bit term %i: %i\n", i, bits[i]);
}
qk_obs_free(obs);Sicherheit
Das Verhalten ist nicht definiert, obs wenn kein gültiger, QkObsnicht-null-Zeiger auf ein ist oder wenn ungültige Werte in den resultierenden QkBitTerm Zeiger geschrieben werden.
Parameter
- obs - Ein Zeiger auf das Observable.
Rückgabe
Ein Zeiger auf die Bit-Terme.
qk_obs_multiply
QkObs *qk_obs_multiply(const QkObs *obs, const QkComplex64 *coeff)
Multiplizieren Sie die Beobachtungsgröße mit einem komplexen Koeffizienten.
Beispiel
QkObs *obs = qk_obs_identity(100);
QkComplex64 coeff = {2, 0};
QkObs *result = qk_obs_multiply(obs, &coeff);Sicherheit
Das Verhalten ist undefiniert, wenn einer der folgenden Punkte verletzt wird
obsist ein gültiger, nicht-null-Zeiger auf eineQkObscoeffist ein gültiger, nicht-null-Zeiger auf eineQkComplex64
Parameter
- obs - Ein Zeiger auf das Observable.
- coeff - Der Koeffizient, mit dem die Beobachtungsgröße multipliziert wird.
Rückgabe
Ein Zeiger auf das Ergebnis obs * coeff.
qk_obs_multiply_inplace
void qk_obs_multiply_inplace(QkObs *obs, const QkComplex64 *coeff)
Multipliziere die vor Ort beobachtbare Größe mit einem komplexen Koeffizienten.
Beispiel
QkObs *obs = qk_obs_identity(100);
QkComplex64 coeff = {2, 0};
qk_obs_multiply_inplace(obs, &coeff);Sicherheit
Das Verhalten ist undefiniert, wenn einer der folgenden Punkte verletzt wird
obsist ein gültiger, nicht-null-Zeiger auf eineQkObscoeffist ein gültiger, nicht-null-Zeiger auf eineQkComplex64
Parameter
- obs - Ein Zeiger auf das Observable.
- coeff - Der Koeffizient, mit dem die Beobachtungsgröße multipliziert wird.
qk_obs_add
QkObs *qk_obs_add(const QkObs *left, const QkObs *right)
Fügen Sie zwei Observablen hinzu.
Beispiel
QkObs *left = qk_obs_identity(100);
QkObs *right = qk_obs_zero(100);
QkObs *result = qk_obs_add(left, right);Sicherheit
Das Verhalten ist undefiniert, wenn left oder right keine gültigen, nicht-null Zeiger auf QkObs sind.
Parameter
- left - Ein Zeiger auf das linke Observatorium.
- right - Ein Zeiger auf das rechte Observable.
Rückgabe
Ein Zeiger auf das Ergebnis left + right.
qk_obs_add_inplace
void qk_obs_add_inplace(QkObs *left, const QkObs *right)
Ein Observable zu einem bestehenden hinzufügen.
Beispiel
QkObs *left = qk_obs_identity(100);
QkObs *right = qk_obs_zero(100);
qk_obs_add_inplace(left, right);Sicherheit
Das Verhalten ist undefiniert, wenn left oder right keine gültigen, nicht-null Zeiger auf QkObs sind.
Parameter
- left - Ein Zeiger auf das linke Observatorium.
- right - Ein Zeiger auf das rechte Observable.
qk_obs_scaled_add
QkObs *qk_obs_scaled_add(const QkObs *left, const QkObs *right, const QkComplex64 *factor)
Füge zwei Observables hinzu und skaliere dabei die Koeffizienten des rechten.
Beispiel
QkObs *left = qk_obs_zero(100);
QkObs *right = qk_obs_identity(100);
QkComplex64 factor = {2, 0};
QkObs *result = qk_obs_scaled_add(left, right, &factor);Sicherheit
Das Verhalten ist undefiniert, wenn left oder right keine gültigen, nicht-null Zeiger auf QkObs sind.
Parameter
- left - Ein Zeiger auf das linke Observatorium.
- right - Ein Zeiger auf das rechte Observable.
- Faktor – Der Faktor, mit dem die Koeffizienten multipliziert werden sollen.
Rückgabe
Ein eigener Zeiger auf das Ergebnis left + factor * right.
qk_obs_scaled_add_inplace
void qk_obs_scaled_add_inplace(QkObs *left, const QkObs *right, const QkComplex64 *factor)
Füge eine skalierte Beobachtungsgröße zu einer bestehenden hinzu.
Beispiel
QkObs *left = qk_obs_zero(100);
QkObs *right = qk_obs_identity(100);
QkComplex64 factor = {2, 0};
qk_obs_scaled_add_inplace(left, right, &factor);Sicherheit
Das Verhalten ist undefiniert, wenn left oder right keine gültigen, nicht-null Zeiger auf QkObs sind.
Parameter
- left - Ein Zeiger auf das linke Observatorium.
- right - Ein Zeiger auf das rechte Observable.
- Faktor – Der Faktor, mit dem die Koeffizienten multipliziert werden sollen.
qk_obs_compose
QkObs *qk_obs_compose(const QkObs *first, const QkObs *second)
Zwei Observablen zusammensetzen (multiplizieren).
Beispiel
QkObs *first = qk_obs_zero(100);
QkObs *second = qk_obs_identity(100);
QkObs *result = qk_obs_compose(first, second);Sicherheit
Das Verhalten ist undefiniert, wenn first oder second keine gültigen, nicht-null Zeiger auf QkObs sind.
Parameter
- erste - Eine Beobachtung.
- zweite - Die andere beobachtbare.
Rückgabe
first.compose(second) was der Beobachtungsgröße result = second @ first entspricht, in Bezug auf die Matrixmultiplikation @.
qk_obs_compose_map
QkObs *qk_obs_compose_map(const QkObs *first, const QkObs *second, const uint32_t *qargs)
Zusammensetzen (multiplizieren) von zwei Observablen gemäß einer benutzerdefinierten Qubit-Reihenfolge.
Dies ermöglicht insbesondere die Zusammenstellung von zwei Beobachtungswerten unterschiedlicher Größe.
Beispiel
QkObs *first = qk_obs_zero(100);
QkObs *second = qk_obs_identity(100);
QkObs *result = qk_obs_compose(first, second);Sicherheit
So rufen Sie diese Funktion sicher auf
firstundsecondmüssen gültige, nicht leere Zeiger aufQkObsseinqargsmuss auf ein Array vonuint32_tzeigen, das fürqk_obs_num_qubits(second)Elemente lesbar ist (d.h. die Anzahl der Qubits insecond)
Parameter
- erste - Eine Beobachtung.
- zweite - Die andere beobachtbare. Die Anzahl der Qubits muss mit der Länge von
qargsübereinstimmen. - qargs - Die Qubit-Argumente geben an, welche Indizes in
firstmit den Indizes insecondverknüpft werden sollen.
Rückgabe
first.compose(second) was der Beobachtungsgröße result = second @ first entspricht, in Bezug auf die Matrixmultiplikation @.
qk_obs_apply_layout
QkExitCode qk_obs_apply_layout(QkObs *obs, const uint32_t *layout, uint32_t num_qubits)
Wenden Sie ein neues Qubit-Layout auf die Beobachtungsgröße an.
Das Layout wird durch ein Array layout mit neuen Indizes festgelegt, das angibt, dass das Qubit mit dem aktuellen Index i auf den Index layout[i] umetikettiert wird. Die Anzahl der Qubits, auf die die Observable wirkt, kann erweitert werden, indem man eine größere num_qubits einstellt, als die aktuelle Observable hat.
Beispiel
Diese Schnittstelle ermöglicht es, die Qubit-Indizes neu zu benennen und zu erweitern:
QkObs *obs = qk_obs_zero(4);
// add a term to the observable
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z};
uint32_t qubits[3] = {1, 2, 3};
complex double coeff = 1;
QkObsTerm term = {coeff, 3, bit_terms, qubits, 4};
qk_obs_add_term(obs, &term);
uint32_t layout[3] = {0, 10, 9}; // qubit mapping is: 0->0, 1->10, 2->9
uint32_t num_output_qubits = 11;
QkExitCode exit = qk_obs_apply_layout(obs, layout, num_output_qubits);In einem Compiler-Workflow kann diese Funktion bequem verwendet werden, um eine QkTranspileLayout* anzuwenden, die aus einem Transpiler-Durchgang stammt und im folgenden Beispiel transpile_layout heißt:
// get the number of output qubits
uint32_t num_output_qubits = qk_transpile_layout_num_output_qubits(transpile_layout);
// get the layout including the ancillas (hence the ``false`` in the function call)
uint32_t *layout = malloc(sizeof(uint32_t) * num_output_qubits);
qk_transpile_layout_final_layout(transpile_layout, false, layout);
// apply the layout
QkExitCode exit = qk_obs_apply_layout(obs, layout, num_output_qubits);
// free the layout array
free(layout);Sicherheit
Das Verhalten ist undefiniert, wenn obs kein gültiger Nicht-Null-Zeiger auf QkObs ist oder wenn layout kein gültiger Nicht-Null-Zeiger auf eine Folge von qk_obs_num_qubits(obs) aufeinanderfolgenden Elementen von uint32_t ist.
Parameter
- obs - Ein Zeiger auf das Observable, das bei Erfolg an Ort und Stelle geändert wird. Überprüfen Sie den Exit-Code, um sicherzustellen, dass das Layout korrekt angewendet wurde.
- layout - Ein Zeiger auf das Layout. Der Zeiger muss auf ein Array zu
qk_obs_num_qubits(obs)Elementen vom Typuint32_tzeigen. Jedes Element muss Werte in[0, num_qubits)haben. - num_qubits - Die Anzahl der Ausgangsqubits.
Rückgabe
Ein Exit-Code.
QkExitCode_Successbei ErfolgQkExitCode_DuplicteIndexErrorwenn doppelte Qubit-Indizes gefunden wurdenQkExitCode_MismatchedQubitswennnum_qubitskleiner ist als die Anzahl der Qubits in der BeobachtungsgrößeQkExitCode_IndexErrorfür alle anderen Indexfehler, wie z. B. ungültige Werte inlayout.
qk_obs_canonicalize
QkObs *qk_obs_canonicalize(const QkObs *obs, double tol)
Berechnen Sie die kanonische Darstellung der Beobachtungsgröße.
Beispiel
QkObs *iden = qk_obs_identity(100);
QkObs *two = qk_obs_add(iden, iden);
double tol = 1e-6;
QkObs *canonical = qk_obs_canonicalize(two, tol);Sicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Parameter
- obs - Ein Zeiger auf das Observable.
- tol - Die Toleranz, unterhalb derer die Koeffizienten als Null angesehen werden.
Rückgabe
Die kanonische Darstellung der Beobachtungsgröße.
qk_obs_copy
QkObs *qk_obs_copy(const QkObs *obs)
Kopieren Sie das Observable.
Beispiel
QkObs *original = qk_obs_identity(100);
QkObs *copied = qk_obs_copy(original);Sicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Parameter
- obs - Ein Zeiger auf das Observable.
Rückgabe
Ein Zeiger auf eine Kopie des Observablen.
qk_obs_equal
bool qk_obs_equal(const QkObs *obs, const QkObs *other)
Vergleichen Sie zwei Messgrößen auf Gleichheit.
Beachten Sie, dass hier nicht die mathematische Gleichheit, sondern die Datengleichheit verglichen wird. Das bedeutet, dass zwei Messgrößen dieselbe Messgröße darstellen können, aber nicht als gleichwertig angesehen werden.
Beispiel
QkObs *observable = qk_obs_identity(100);
QkObs *other = qk_obs_identity(100);
bool are_equal = qk_obs_equal(observable, other);Sicherheit
Das Verhalten ist undefiniert, wenn obs oder other keine gültigen, nicht-null Zeiger auf QkObs sind.
Parameter
- obs - Ein Zeiger auf ein Observable.
- other - Ein Zeiger auf ein anderes Observable.
Rückgabe
true wenn die Beobachtungswerte gleich sind, ansonsten false .
qk_obs_str
char *qk_obs_str(const QkObs *obs)
Gibt eine String-Repräsentation eines QkObs zurück.
Beispiel
QkObs *obs = qk_obs_identity(100);
char *string = qk_obs_str(obs);
qk_str_free(string);
qk_obs_free(obs);Sicherheit
Das Verhalten ist undefiniert obs ist kein gültiger, nicht-null Zeiger auf ein QkObs.
Die Zeichenkette darf nicht mit dem normalen C free freigegeben werden, Sie müssen qk_str_free verwenden, um den von der Zeichenkette verbrauchten Speicher freizugeben. Der Nichtaufruf von qk_str_free führt zu einem Speicherleck.
Die Länge der Zeichenkette darf nach der Rückgabe nicht mehr geändert werden (durch Schreiben eines Null-Bytes irgendwo innerhalb der Zeichenkette oder Entfernen des letzten Bytes), obwohl die Werte geändert werden können.
Parameter
- obs - Ein Zeiger auf die
QkObs, für die die Zeichenfolge abgerufen werden soll.
Rückgabe
Ein Zeiger auf ein nul-terminiertes char-Array der String-Darstellung für obs
qk_str_free
void qk_str_free(char *string)
Geben Sie eine String-Darstellung frei.
Sicherheit
Das Verhalten ist undefiniert, wenn str kein von qk_obs_str oder qk_obsterm_str zurückgegebener Zeiger ist.
Parameter
- string - Ein Zeiger auf die zurückgegebene String-Darstellung von
qk_obs_stroderqk_obsterm_str.
qk_obs_to_python
PyObject *qk_obs_to_python(QkObs *obs)
Übergeben Sie die Eigentumsrechte an einem QkObs Objekt an „ Python “.
Nach dem Aufruf dieser Funktion ist die Verwendung des QkObs Zeigers nicht mehr sicher. Insbesondere sollten Sie nicht versuchen, den Speicher zu löschen oder freizugeben. Der Aufrufer muss Eigentümer des Objekts QkObs sein und darf keine geliehene Referenz darauf halten (beispielsweise ist ein qk_obs_borrow_from_python aus QkObs * abgerufenes Objekt kein Eigentum des Aufrufers).
Sicherheit
Der Anrufer muss mit einem Dolmetscher von Python verbunden sein. Das Verhalten ist nicht definiert, wenn kein gültiger, nicht-null-Zeiger auf ein obs initialisiertes und im Besitz befindliches ist QkObs.
Parameter
- obs – Das Objekt, das sich im Besitz befindet.
Rückgabe
Ein verweis auf das Objekt, der von „ Python “ verwaltet wird.
qk_obs_borrow_from_python
QkObs *qk_obs_borrow_from_python(PyObject *ob)
Rufe einen QkObs Zeiger von einem Python-Objekt ab.
Hierbei wird ein Zeiger auf Python übernommen und der QkObs Zeiger darauf extrahiert, sofern er vom richtigen Typ ist. Der zurückgegebene Zeiger wird vom ob Zeiger ausgeliehen. Wenn der Typ PyObject nicht korrekt ist, ist der NULL Rückgabewert und der Ausnahmezustand des Interpreter „ Python “ wird gesetzt.
Um diese Funktion aufzurufen, muss eine Verbindung zu einem „ Python “-Interpreter bestehen.
Sie können auch verwenden qk_obs_convert_from_python, was logisch gesehen genau dasselbe wie diese Funktion ist, aber direkt als „Konverter“-Funktion für die PyArg_Parse* Familie der Konverterfunktionen von Python genutzt werden kann.
Sicherheit
Der Anrufer muss mit einem Dolmetscher von Python verbunden sein. Das Verhalten ist nicht definiert, wenn kein gültiger, nicht-null-Zeiger auf ein ob Python-Objekt ist.
Parameter
- ob – Ein Objekt vom Typ „ Python “, das ausgeliehen wurde.
Rückgabe
Ein Zeiger auf das native Objekt oder NULL falls das Objekt „ Python “ vom falschen Typ ist.
qk_obs_convert_from_python
int qk_obs_convert_from_python(PyObject *object, void *address)
Rufe einen QkObs Zeiger von einem Python-Objekt ab.
Hierbei wird eine Referenz vom Typ Python übernommen und der QkObs Zeiger darauf in `` addressextrahiert, sofern er vom richtigen Typ ist. Der zurückgegebene Zeiger wird vom object Zeiger ausgeliehen. Wenn nicht vom PyObject richtigen Typ ist, ist der Rückgabewert 1, der Ausnahmezustand des Interpreter „ Python “ wird gesetzt und address bleibt unverändert.
Um diese Funktion aufzurufen, muss eine Verbindung zu einem „ Python “-Interpreter bestehen.
Sie können auch verwenden qk_obs_borrow_from_python, was logisch gesehen genau dasselbe ist wie dies, jedoch eine natürlichere Signatur für die direkte Verwendung bietet.
Sicherheit
Der Anrufer muss mit einem Dolmetscher von Python verbunden sein. Das Verhalten ist nicht definiert, wenn kein gültiger, nicht-null-Zeiger auf ein object Python-Objekt ist oder wenn kein Zeiger auf beschreibbare address Daten des richtigen Typs ist.
Parameter
- Objekt – Ein ausgeliehenes „ Python “-Objekt.
- Adresse – Der Speicherort, an den die Ausgabe geschrieben werden soll.
Rückgabe
1 bei Erfolg, 0 bei Misserfolg.