Skip to main content
IBM Quantum Platform

QkObs

typedef struct QkObs QkObs

Ein 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

QkObs=∑ici⨂nAi(n)\text{\texttt{QkObs}} = \sum_i c_i \bigotimes_n A^{(n)}_i

für komplexe Zahlen cic_i und Ein-Qubit-Operatoren, die auf ein Qubit nn aus einem beschränkten Alphabet Ai(n)A^{(n)}_i wirken. Die Summe über ii ist die Summe der einzelnen Terme, und das Tensorprodukt ergibt die Operatorenketten. Das Alphabet der zulässigen Ein-Qubit-Operatoren, aus dem Ai(n)A^{(n)}_i gezogen wird, besteht aus den Pauli-Operatoren und den Pauli-Eigenzustands-Projektionsoperatoren. Explizit sind dies:

Bediener
QkBitTerm
Numerischer Wert
II (Identität)Nicht gespeichert.Nicht gespeichert.
XX (Pauli X)QkBitTerm_X0b0010 (2)
YY (Pauli Y)QkBitTerm_Y0b0011 (3)
ZZ (Pauli Z)QkBitTerm_Z0b0001 (1)
∣+⟩⟨+∣\lvert+\rangle\langle+\rvert (Projektor zum positiven Eigenzustand von X)QkBitTerm_Plus0b1010 (10)
∣−⟩⟨−∣\lvert-\rangle\langle-\rvert (Projektor zum negativen Eigenzustand von X)QkBitTerm_Minus0b0110 (6)
∣r⟩⟨r∣\lvert r\rangle\langle r\rvert (Projektor auf positiven Eigenzustand von Y)QkBitTerm_Right0b1011 (11)
∣l⟩⟨l∣\lvert l\rangle\langle l\rvert (Projektor auf den negativen Eigenzustand von Y)QkBitTerm_Left0b0111 (7)
∣0⟩⟨0∣\lvert0\rangle\langle0\rvert (Projektor zum positiven Eigenzustand von Z)QkBitTerm_Zero0b1001 (9)
∣1⟩⟨1∣\lvert1\rangle\langle1\rvert (Projektor zum negativen Eigenzustand von Z)QkBitTerm_One0b0101 (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 ∣0⟩⟨0∣⊗n{\lvert0\rangle\langle0\rvert}^{\otimes n} effizient auf Hardware mit einfachen ZZ Messungen gemessen werden, kann aber nur in Form von Paulis als (I+Z)⊗n/2n{(I + Z)}^{\otimes n}/2^n dargestellt werden, was 2n2^n 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 ∑n∈qubitsZ(n)\sum_{n\in \text{qubits}} Z^{(n)} 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_coeffsttDer komplexe skalare Multiplikator für jeden Term.
qk_obs_bit_termsssJeder der nicht-identischen Ein-Qubit-Terme für alle Operatoren, in dieser Reihenfolge. Diese entsprechen der Nicht-Identität Ai(n)A^{(n)}_i in der Summenbeschreibung, bei der die Einträge in der Reihenfolge der aufsteigenden ii zuerst gespeichert werden und in der Reihenfolge der aufsteigenden nn innerhalb jedes Terms.
qk_obs_indicesssDas entsprechende Qubit ( nn ) 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_boundariest+1t+1Die Indizes, die die Bit-Terme und Indizes in vollständige Terme unterteilen. Für den Term ii 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 tt ist die Anzahl der Terme in der Summe und kann mit qk_obs_num_terms abgefragt werden. Der Parameter ss 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 Z2Z0−X3Y1Z_2 Z_0 - X_3 Y_1, 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 (siehe qk_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.

Anmerkung

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

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...
}
Achtung

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_zeroKonstruieren Sie eine leere Observable für eine bestimmte Anzahl von Qubits.
qk_obs_identityKonstruieren Sie das Identitätsobservable für eine bestimmte Anzahl von Qubits.
qk_obs_newKonstruieren 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_add und qk_obs_add_inplace
  • mit einer komplexen Zahl multiplizieren mit qk_obs_multiply und qk_obs_multiply_inplace
  • zwei Observablen über qk_obs_compose zusammensetzen (multiplizieren) und qk_obs_compose_map
  • berechnen left + scalar * right für zwei Observablen und einen komplexen Skalar mit qk_obs_scaled_add und qk_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:

  • coeffs ist ein Zeiger auf ein QkComplex64 Array der Länge num_terms
  • bit_terms ist ein Zeiger auf ein Array gültiger QkBitTerm Elemente der Länge num_bits
  • indices ist ein Zeiger auf ein Array uint32_t der Länge num_bits, das streng aufsteigend nach Begriffen sortiert ist, und jedes Element ist kleiner als num_qubits
  • boundaries ist ein Zeiger auf ein Array size_t der Länge num_terms + 1, das in aufsteigender Reihenfolge sortiert ist, wobei das erste Element 0 ist und das letzte Element kleiner ist als num_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_terms hat.
  • bit_terms - Ein Zeiger auf das erste Element des Bit-Terms-Arrays, das die Länge num_bits hat.
  • indices - Ein Zeiger auf das erste Element des Arrays indices, das die Länge num_bits hat. 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 + 1 hat.

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:

  • obs ist ein gültiger, nicht-null-Zeiger auf eine QkObs
  • cterm ist ein gültiger, nicht-null-Zeiger auf eine QkObsTerm

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

  • obs ist ein gültiger, nicht-null-Zeiger auf eine QkObs
  • out ist ein gültiger, nicht-null-Zeiger auf eine QkObsTerm

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==1

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

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==100

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

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 terms

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

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

  • obs ist ein gültiger, nicht-null-Zeiger auf eine QkObs
  • coeff ist ein gültiger, nicht-null-Zeiger auf eine QkComplex64

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

  • obs ist ein gültiger, nicht-null-Zeiger auf eine QkObs
  • coeff ist ein gültiger, nicht-null-Zeiger auf eine QkComplex64

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

  • first und second müssen gültige, nicht leere Zeiger auf QkObs sein
  • qargs muss auf ein Array von uint32_t zeigen, das für qk_obs_num_qubits(second) Elemente lesbar ist (d.h. die Anzahl der Qubits in second)

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 first mit den Indizes in second verknü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 Typ uint32_t zeigen. Jedes Element muss Werte in [0, num_qubits) haben.
  • num_qubits - Die Anzahl der Ausgangsqubits.

Rückgabe

Ein Exit-Code.

  • QkExitCode_Success bei Erfolg
  • QkExitCode_DuplicteIndexError wenn doppelte Qubit-Indizes gefunden wurden
  • QkExitCode_MismatchedQubits wenn num_qubits kleiner ist als die Anzahl der Qubits in der Beobachtungsgröße
  • QkExitCode_IndexError für alle anderen Indexfehler, wie z. B. ungültige Werte in layout.

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_str oder qk_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.

War diese Seite hilfreich?
Melden Sie auf GitHub einen Fehler, einen Tippfehler oder fordern Sie Inhalte an.