QkObs
typedef struct QkObs QkObsUn observable sobre bases Pauli que almacena sus datos en un formato de qubit disperso.
mathematics
Este observable representa una suma sobre cadenas de operadores de Pauli y proyectores de estados propios de Pauli, con cada término ponderado por algún número complejo. Es decir, el observable completo es
para números complejos y operadores single-qubit que actúan sobre qubit a partir de un alfabeto restringido . La suma sobre es la suma de los términos individuales, y el producto tensorial produce las cadenas de operadores. El alfabeto de operadores de un solo qubit permitido del que se extraen los son los operadores de Pauli y los operadores de proyección Pauli-eigen-estado. Explícitamente, estos son:
Operador | QkBitTerm | Valor numérico |
|---|---|---|
| (identidad) | No guardado. | No guardado. |
| (Pauli X) | QkBitTerm_X | 0b0010 (2) |
| (Pauli Y) | QkBitTerm_Y | 0b0011 (3) |
| (Pauli Z) | QkBitTerm_Z | 0b0001 (1) |
| (proyector a estado propio positivo de X) | QkBitTerm_Plus | 0b1010 (10) |
| (proyector a estado propio negativo de X) | QkBitTerm_Minus | 0b0110 (6) |
| (proyector a estado propio positivo de Y) | QkBitTerm_Right | 0b1011 (11) |
| (proyector a estado propio negativo de Y) | QkBitTerm_Left | 0b0111 (7) |
| (proyector a estado propio positivo de Z) | QkBitTerm_Zero | 0b1001 (9) |
| (proyector a estado propio negativo de Z) | QkBitTerm_One | 0b0101 (5) |
Debido a que se permiten tanto los Paulis como sus proyectores, el alfabeto permitido forma una base sobrecompleta del espacio de operadores. Esto significa que no existe una suma única para representar un observable dado. Como consecuencia, la comparación requiere un cuidado adicional y el uso de qk_obs_canonicalize en dos observables matemáticamente equivalentes podría no dar como resultado la misma representación.
QkObs utiliza su particular base sobrecompleta con el objetivo de hacer equivalente la "eficacia de la medición" a la "eficacia de la representación". Por ejemplo, el observable puede medirse eficientemente en hardware con simples mediciones de , pero sólo puede representarse en términos de Paulis como , lo que requiere términos almacenados en . QkObs requiere un único término para almacenarlo. El inconveniente es que no resulta práctico tomar una matriz arbitraria y encontrar la mejor representación QkObs . Por lo general, querrá construir un QkObs directamente, en lugar de intentar descomponerlo en uno.
Representación
La representación interna de un QkObs almacena únicamente los operadores qubit no identitarios. Esto hace que sea significativamente más eficiente representar observables como ; QkObs requiere una cantidad de memoria lineal en el número total de qubits. Los términos se almacenan comprimidos, de forma similar al formato de filas dispersas comprimido de las matrices dispersas. En esta analogía, los términos de la suma son las "filas", y los términos del qubit son las "columnas", donde una entrada ausente representa la identidad en lugar de un cero. Más explícitamente, la representación se compone de cuatro matrices contiguas:
Atributo accesible por | Longitud | Descripción |
|---|---|---|
qk_obs_coeffs | El multiplicador escalar complejo para cada término. | |
qk_obs_bit_terms | Cada uno de los términos single-qubit no idénticos para todos los operadores, en orden. Corresponden a la no-identidad en la descripción de la suma, donde las entradas se almacenan en orden creciente primero, y en orden creciente dentro de cada término. | |
qk_obs_indices | El qubit correspondiente ( ) para cada uno de los términos del bit. QkObs requiere que esta lista esté ordenada por términos, y los algoritmos pueden confiar en que esta invariante se mantenga. | |
qk_obs_boundaries | Los índices que dividen los términos e índices de bits en términos completos. Para el término número , su coeficiente complejo se almacena en el índice i, y sus operadores single-qubit no-idénticos y sus qubits correspondientes están en el rango [boundaries[i], boundaries[i+1]) en los términos e índices de bits, respectivamente. Los límites siempre tienen un 0 explícito como primer elemento. |
El parámetro de longitud es el número de términos de la suma y puede consultarse mediante qk_obs_num_terms. El parámetro es el número total de términos single-qubit sin identidad y puede consultarse mediante qk_obs_len.
Como ejemplos ilustrativos:
- en el caso de un operador cero, los límites son de longitud 1 (un único 0) y todos los demás vectores están vacíos.
- en el caso de un operador de identidad totalmente simplificado, los límites son
{0, 0}, los coeficientes tienen una sola entrada, y tanto los términos de bits como los índices están vacíos. - para el operador , los límites son
{0, 2, 4}, los coeficientes son{1.0, -1.0}, los términos de bits son{QkBitTerm_Z, QkBitTerm_Z, QkBitTerm_Y, QkBitTerm_X}y los índices son{0, 2, 1, 3}. El operador puede actuar sobre más de cuatro qubits, dependiendo del número de qubits (véaseqk_obs_num_qubits). Obsérvese que los términos e índices de un bit se ordenan por términos.
Estos casos no son especiales, son totalmente coherentes con las normas y no deberían necesitar un tratamiento especial.
Orden canónico
Para cualquier observable matemático dado, existen varias formas de representarlo con QkObs. Por ejemplo, el mismo conjunto de términos de un solo bit y sus índices correspondientes pueden aparecer varias veces en el observable. Matemáticamente, esto equivale a tener un único término con todos los coeficientes sumados. Del mismo modo, los términos de la suma en un QkObs pueden estar en cualquier orden mientras representan el mismo observable, ya que la suma es conmutativa (aunque mientras que la suma en coma flotante no es asociativa, QkObs no ofrece garantías sobre el orden de la suma).
Estas dos categorías de degeneración de la representación pueden hacer que el operador igualdad, qk_obs_equal, afirme que dos observables no son iguales, a pesar de representar el mismo objeto. En estos casos, puede ser conveniente definir alguna forma canónica que permita comparar estructuralmente los observables. Puede poner un QkObs en forma canónica utilizando la función qk_obs_canonicalize . El orden preciso de los términos en el orden canónico no está especificado, y puede cambiar entre versiones de Qiskit. Sin embargo, dentro de la misma versión de Qiskit, puedes comparar dos observables estructuralmente comparando sus formas simplificadas.
Si desea tener en cuenta la tolerancia de coma flotante en la comparación, lo más seguro es utilizar una receta como:
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;
}La forma canónica producida por qk_obs_canonicalize por sí sola no detectará universalmente todos los observables que son equivalentes debido al alfabeto de bases sobrecompleto.
Indexación
Se puede acceder a los términos de suma observables individuales en QkObs a través de qk_obs_term y devolver objetos de tipo QkObsTerm. Estos términos contienen campos con el coeficiente del término, sus términos de bits, índices y el número de qubits en los que está definido. Junto con la información del número de términos, puede iterar sobre todos los términos observables como
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...
}Al rellenar un QkObsTerm a través de qk_obs_term se hará referencia a los datos del QkObs original. Modificar los términos de bits o los índices cambiará el observable y puede dejarlo en un estado incoherente.
Construcción
QkObs puede construirse inicializando un observable vacío (con qk_obs_zero) e iterativamente añadiendo términos (con qk_obs_add_term). Alternativamente, se puede construir un observable a partir de datos "brutos" (con qk_obs_new) si se especifican todos los datos internos. Para ello hay que tener cuidado de que los datos sean coherentes y den lugar a un observable válido.
Función | Resumen |
|---|---|
qk_obs_zero | Construir un observable vacío en un número dado de qubits. |
qk_obs_identity | Construir el observable de identidad en un número dado de qubits. |
qk_obs_new | Construir un observable a partir de las matrices de datos brutos. |
Manipulación matemática
QkObs admite operaciones aritméticas fundamentales entre observables o con escalares. Puede:
- añadir dos observables utilizando
qk_obs_addyqk_obs_add_inplace - multiplicar por un número complejo con
qk_obs_multiplyyqk_obs_multiply_inplace - componer (multiplicar) dos observables a través de
qk_obs_composeyqk_obs_compose_map - calcular
left + scalar * rightpara dos observables y un escalar complejo conqk_obs_scaled_addyqk_obs_scaled_add_inplace
Funciones
qk_obs_zero
QkObs *qk_obs_zero(uint32_t num_qubits)
Construir el observable cero (sin términos).
Ejemplo
QkObs *zero = qk_obs_zero(100);Parámetros
- num_qubits - El número de qubits en los que se define el observable.
Devuelve
Un puntero al observable creado.
qk_obs_identity
QkObs *qk_obs_identity(uint32_t num_qubits)
Construye el observable de identidad.
Ejemplo
QkObs *identity = qk_obs_identity(100);Parámetros
- num_qubits - El número de qubits en los que se define el observable.
Devuelve
Un puntero al observable creado.
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)
Construir un nuevo observable a partir de datos brutos.
Cualquiera de los argumentos de tipo puntero puede serlo NULL si y solo si su longitud correspondiente es cero.
Ejemplo
// 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, -1};
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
);Seguridad
El comportamiento es indefinido si se incumple alguna de las siguientes condiciones:
coeffses un puntero a una matrizQkComplex64de longitudnum_termsbit_termses un puntero a una matriz de elementos válidos deQkBitTermde longitudnum_bitsindiceses un puntero a una matrizuint32_tde longitudnum_bits, que está ordenada por términos en estricto orden ascendente, y cada elemento es menor quenum_qubitsboundarieses un puntero a una matrizsize_tde longitudnum_terms + 1, que está ordenada en orden ascendente, el primer elemento es 0 y el último elemento es menor quenum_terms
Parámetros
- num_qubits - El número de qubits en los que se define el observable.
- num\terms - El número de términos.
- num_bits - El número total de términos de bits no identificativos.
- coeffs - Puntero al primer elemento de la matriz de coeficientes, cuya longitud es
num_terms. - bit_terms - Puntero al primer elemento de la matriz de términos de bits, cuya longitud es
num_bits. - indices - Un puntero al primer elemento de la matriz indices, que tiene la longitud
num_bits. Tenga en cuenta que, por término, deben ordenarse de forma incremental. - boundaries - Puntero al primer elemento de la matriz boundaries, cuya longitud es
num_terms + 1.
Devuelve
Si los datos de entrada eran coherentes y la construcción tuvo éxito, el resultado es un puntero al observable. En caso contrario, se devuelve un puntero nulo.
qk_obs_free
void qk_obs_free(QkObs *obs)
Libera lo observable.
Ejemplo
QkObs *obs = qk_obs_zero(100);
qk_obs_free(obs);Seguridad
El comportamiento es indefinido si obs no es null o un puntero válido a QkObs.
Parámetros
- obs - Un puntero al observable a liberar.
qk_obs_add_term
QkExitCode qk_obs_add_term(QkObs *obs, const QkObsTerm *cterm)
Añade un término al observable.
Ejemplo
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);Seguridad
El comportamiento es indefinido si se incumple alguna de las siguientes condiciones:
obses un puntero válido y no nulo a un archivoQkObsctermes un puntero válido y no nulo a un archivoQkObsTerm
Parámetros
- obs - Puntero al observable.
- cterm - Un puntero al término a añadir.
Devuelve
Un código de salida. Esto es >0 si el término es incoherente o la adición del término falla.
qk_obs_term
QkExitCode qk_obs_term(QkObs *obs, uint64_t index, QkObsTerm *out)
Obtener un término observable por referencia.
Un QkObsTerm contiene punteros a los índices y términos de bits del término, que pueden utilizarse para modificar los datos internos del observable. Esto puede dejar el observable en un estado incoherente y debe evitarse, a menos que se tenga mucho cuidado. Generalmente es más seguro construir un nuevo observable en lugar de intentar modificaciones in situ.
Ejemplo
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);Seguridad
El comportamiento es indefinido si se incumple alguna de las siguientes condiciones
obses un puntero válido y no nulo a un archivoQkObsoutes un puntero válido y no nulo a un archivoQkObsTerm
Parámetros
- obs - Puntero al observable.
- index - El índice del término a obtener.
- out - Un puntero a
QkObsTermutilizado para devolver el término observable.
Devuelve
Un código de salida.
qk_obs_num_terms
size_t qk_obs_num_terms(const QkObs *obs)
Obtiene el número de términos del observable.
Ejemplo
QkObs *obs = qk_obs_identity(100);
size_t num_terms = qk_obs_num_terms(obs); // num_terms==1Seguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
Parámetros
- obs - Puntero al observable.
Devuelve
El número de términos del observable.
qk_obs_num_qubits
uint32_t qk_obs_num_qubits(const QkObs *obs)
Obtiene el número de qubits en los que está definido el observable.
Ejemplo
QkObs *obs = qk_obs_identity(100);
uint32_t num_qubits = qk_obs_num_qubits(obs); // num_qubits==100Seguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
Parámetros
- obs - Puntero al observable.
Devuelve
El número de qubits en los que se define el observable.
qk_obs_len
size_t qk_obs_len(const QkObs *obs)
Obtiene el número de términos/índices de bits en el observable.
Ejemplo
QkObs *obs = qk_obs_identity(100);
size_t len = qk_obs_len(obs); // len==0, as there are no non-trivial bit termsSeguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
Parámetros
- obs - Puntero al observable.
Devuelve
El número de términos del observable.
qk_obs_coeffs
QkComplex64 *qk_obs_coeffs(QkObs *obs)
Obtener un puntero a los coeficientes.
Permite leer y modificar los coeficientes del observable. El puntero resultante es válido para leer qk_obs_num_terms(obs) elementos de QkComplex64.
Ejemplo
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);
}Seguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
Parámetros
- obs - Puntero al observable.
Devuelve
Un puntero a los coeficientes.
qk_obs_indices
uint32_t *qk_obs_indices(QkObs *obs)
Obtener un puntero a los índices.
Permite leer y modificar los índices del observable. El puntero resultante es válido para leer qk_obs_len(obs) elementos de tamaño uint32_t.
Ejemplo
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);Seguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
Parámetros
- obs - Puntero al observable.
Devuelve
Un puntero a los índices.
qk_obs_boundaries
size_t *qk_obs_boundaries(QkObs *obs)
Obtener un puntero a los límites del término.
Puede utilizarse para leer y modificar los límites de los términos del observable. El puntero resultante es válido para leer qk_obs_num_terms(obs) + 1 elementos de tamaño size_t.
Ejemplo
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]);
}Seguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
Parámetros
- obs - Puntero al observable.
Devuelve
Un puntero a los límites.
qk_obs_bit_terms
QkBitTerm *qk_obs_bit_terms(QkObs *obs)
Obtener un puntero a los términos de bits.
Permite leer y modificar los términos de bits del observable. El puntero resultante es válido para leer qk_obs_len(obs) elementos de tamaño uint8_t.
Ejemplo
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);Seguridad
El comportamiento es indefinido obs si el puntero no es válido QkObs o no es nulo, o si se escriben valores no válidos en el puntero QkBitTerm resultante.
Parámetros
- obs - Puntero al observable.
Devuelve
Un puntero a los términos de bits.
qk_obs_multiply
QkObs *qk_obs_multiply(const QkObs *obs, const QkComplex64 *coeff)
Multiplica el observable por un coeficiente complejo.
Ejemplo
QkObs *obs = qk_obs_identity(100);
QkComplex64 coeff = {2, 0};
QkObs *result = qk_obs_multiply(obs, &coeff);Seguridad
El comportamiento es indefinido si se incumple alguna de las siguientes condiciones
obses un puntero válido y no nulo a un archivoQkObscoeffes un puntero válido y no nulo a un archivoQkComplex64
Parámetros
- obs - Puntero al observable.
- coeff - Coeficiente por el que se multiplica el observable.
Devuelve
Un puntero al resultado obs * coeff.
qk_obs_multiply_inplace
void qk_obs_multiply_inplace(QkObs *obs, const QkComplex64 *coeff)
Multiplica la variable observable in situ por un coeficiente complejo.
Ejemplo
QkObs *obs = qk_obs_identity(100);
QkComplex64 coeff = {2, 0};
qk_obs_multiply_inplace(obs, &coeff);Seguridad
El comportamiento es indefinido si se incumple alguna de las siguientes condiciones
obses un puntero válido y no nulo a un archivoQkObscoeffes un puntero válido y no nulo a un archivoQkComplex64
Parámetros
- obs - Puntero al observable.
- coeff - Coeficiente por el que se multiplica el observable.
qk_obs_add
QkObs *qk_obs_add(const QkObs *left, const QkObs *right)
Añade dos observables.
Ejemplo
QkObs *left = qk_obs_identity(100);
QkObs *right = qk_obs_zero(100);
QkObs *result = qk_obs_add(left, right);Seguridad
El comportamiento es indefinido si left o right no son válidos, punteros no nulos a QkObs\ s.
Parámetros
- left - Puntero al observable izquierdo.
- right - Un puntero al observable derecho.
Devuelve
Un puntero al resultado left + right.
qk_obs_add_inplace
void qk_obs_add_inplace(QkObs *left, const QkObs *right)
Añadir un observable a uno ya existente.
Ejemplo
QkObs *left = qk_obs_identity(100);
QkObs *right = qk_obs_zero(100);
qk_obs_add_inplace(left, right);Seguridad
El comportamiento es indefinido si left o right no son válidos, punteros no nulos a QkObs\ s.
Parámetros
- left - Puntero al observable izquierdo.
- right - Un puntero al observable derecho.
qk_obs_scaled_add
QkObs *qk_obs_scaled_add(const QkObs *left, const QkObs *right, const QkComplex64 *factor)
Añade dos observables y, al mismo tiempo, escala los coeficientes del de la derecha.
Ejemplo
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);Seguridad
El comportamiento es indefinido si left o right no son válidos, punteros no nulos a QkObs\ s.
Parámetros
- left - Puntero al observable izquierdo.
- right - Un puntero al observable derecho.
- factor : el factor por el que se multiplican los coeficientes.
Devuelve
Un puntero propio al resultado left + factor * right.
qk_obs_scaled_add_inplace
void qk_obs_scaled_add_inplace(QkObs *left, const QkObs *right, const QkComplex64 *factor)
Añadir un observable escalado a uno ya existente.
Ejemplo
QkObs *left = qk_obs_zero(100);
QkObs *right = qk_obs_identity(100);
QkComplex64 factor = {2, 0};
qk_obs_scaled_add_inplace(left, right, &factor);Seguridad
El comportamiento es indefinido si left o right no son válidos, punteros no nulos a QkObs\ s.
Parámetros
- left - Puntero al observable izquierdo.
- right - Un puntero al observable derecho.
- factor : el factor por el que se multiplican los coeficientes.
qk_obs_compose
QkObs *qk_obs_compose(const QkObs *first, const QkObs *second)
Componer (multiplicar) dos observables.
Ejemplo
QkObs *first = qk_obs_zero(100);
QkObs *second = qk_obs_identity(100);
QkObs *result = qk_obs_compose(first, second);Seguridad
El comportamiento es indefinido si first o second no son válidos, punteros no nulos a QkObs\ s.
Parámetros
- primero - Un observable.
- segundo - El otro observable.
Devuelve
first.compose(second) que es igual al observable result = second @ first, en términos de la multiplicación matricial @.
qk_obs_compose_map
QkObs *qk_obs_compose_map(const QkObs *first, const QkObs *second, const uint32_t *qargs)
Componer (multiplicar) dos observables según un orden de qubit personalizado.
En particular, esto permite componer dos observables de diferente tamaño.
Ejemplo
QkObs *first = qk_obs_zero(100);
QkObs *second = qk_obs_identity(100);
QkObs *result = qk_obs_compose(first, second);Seguridad
Para llamar a esta función de forma segura
firstyseconddeben ser punteros válidos y no nulos aQkObs\ sqargsdebe apuntar a una matriz deuint32_t, legible paraqk_obs_num_qubits(second)elementos (lo que significa el número de qubits ensecond)
Parámetros
- primero - Un observable.
- segundo - El otro observable. El número de qubits debe coincidir con la longitud de
qargs. - qargs - Los argumentos qubit especifican qué índices de
firstasociar con los desecond.
Devuelve
first.compose(second) que es igual al observable result = second @ first, en términos de la multiplicación matricial @.
qk_obs_apply_layout
QkExitCode qk_obs_apply_layout(QkObs *obs, const uint32_t *layout, uint32_t num_qubits)
Aplicar una nueva disposición de qubits al observable.
La disposición se establece mediante una matriz layout de nuevos índices, especificando que el qubit en el índice actual i se reetiqueta en el índice layout[i]. El número de qubits sobre los que actúa el observable puede ampliarse estableciendo un num_qubits mayor que el que tiene el observable actual.
Ejemplo
Esta interfaz permite reetiquetar y ampliar los índices de los qubits:
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);En un flujo de trabajo de compilador, esta función puede utilizarse convenientemente para aplicar un QkTranspileLayout* obtenido de un pase de transpilador, llamado transpile_layout en el siguiente ejemplo:
// 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);Seguridad
El comportamiento es indefinido si obs no es un puntero válido y no nulo a QkObs o si layout no es un puntero válido y no nulo a una secuencia de qk_obs_num_qubits(obs) elementos consecutivos de uint32_t.
Parámetros
- obs - Un puntero al observable, este observable se modificará en su lugar en caso de éxito. Compruebe el código de salida para asegurarse de que el diseño se ha aplicado correctamente.
- diseño - Un puntero al diseño. El puntero debe apuntar a una matriz a
qk_obs_num_qubits(obs)elementos de tipouint32_t. Cada elemento debe tener valores en[0, num_qubits). - num_qubits - El número de qubits de salida.
Devuelve
Un código de salida.
QkExitCode_Successtras el éxitoQkExitCode_DuplicteIndexErrorsi se han encontrado índices de qubits duplicadosQkExitCode_MismatchedQubitssinum_qubitses menor que el número de qubits del observableQkExitCode_IndexErrorpara cualquier otro error de índice, como valores no válidos enlayout.
qk_obs_canonicalize
QkObs *qk_obs_canonicalize(const QkObs *obs, double tol)
Calcular la representación canónica del observable.
Ejemplo
QkObs *iden = qk_obs_identity(100);
QkObs *two = qk_obs_add(iden, iden);
double tol = 1e-6;
QkObs *canonical = qk_obs_canonicalize(two, tol);Seguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
Parámetros
- obs - Puntero al observable.
- tol - Tolerancia por debajo de la cual se considera que los coeficientes son cero.
Devuelve
La representación canónica del observable.
qk_obs_copy
QkObs *qk_obs_copy(const QkObs *obs)
Copia el observable.
Ejemplo
QkObs *original = qk_obs_identity(100);
QkObs *copied = qk_obs_copy(original);Seguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
Parámetros
- obs - Puntero al observable.
Devuelve
Un puntero a una copia del observable.
qk_obs_equal
bool qk_obs_equal(const QkObs *obs, const QkObs *other)
Comparar dos observables para la igualdad.
Tenga en cuenta que no se compara la igualdad matemática, sino la igualdad de datos. Esto significa que dos observables pueden representar el mismo observable pero no compararse como iguales.
Ejemplo
QkObs *observable = qk_obs_identity(100);
QkObs *other = qk_obs_identity(100);
bool are_equal = qk_obs_equal(observable, other);Seguridad
El comportamiento es indefinido si obs o other no son válidos, punteros no nulos a QkObs\ s.
Parámetros
- obs - Puntero a un observable.
- other - Un puntero a otro observable.
Devuelve
true si los observables son iguales, false en caso contrario.
qk_obs_str
char *qk_obs_str(const QkObs *obs)
Devuelve una representación de cadena de un QkObs.
Ejemplo
QkObs *obs = qk_obs_identity(100);
char *string = qk_obs_str(obs);
qk_str_free(string);Seguridad
El comportamiento es indefinido obs no es un puntero válido no nulo a QkObs.
La cadena no debe ser liberada con el free normal de C, debes usar qk_str_free para liberar la memoria consumida por la Cadena. Si no se llama a qk_str_free se producirá una fuga de memoria.
No cambie la longitud de la cadena después de que sea devuelta (escribiendo un byte nul en algún lugar dentro de la cadena o eliminando el último), aunque los valores pueden ser mutados.
Parámetros
- obs - Puntero a
QkObspara obtener la cadena.
Devuelve
Un puntero a una matriz de caracteres sin terminación de la representación de cadena para obs
qk_str_free
void qk_str_free(char *string)
Libera una representación de cadena.
Seguridad
El comportamiento es indefinido si str no es un puntero devuelto por qk_obs_str o qk_obsterm_str.
Parámetros
- string - Un puntero a la representación de cadena devuelta desde
qk_obs_stroqk_obsterm_str.
qk_obs_to_python
PyObject *qk_obs_to_python(QkObs *obs)
Pasa la propiedad de un QkObs objeto a « Python ».
No es seguro utilizar el QkObs puntero después de llamar a esta función. En particular, no debes intentar borrarlo ni liberarlo. El autor de la llamada debe ser el propietario del objeto QkObs, y no tener una referencia prestada (por ejemplo, un objeto QkObs * recuperado de no qk_obs_borrow_from_python es de su propiedad).
Seguridad
La persona que llama debe estar conectada a un intérprete de Python. El comportamiento es indefinido si no obs es un puntero válido y no nulo a un objeto inicializado y del que se tiene propiedad QkObs.
Parámetros
- obs – El objeto en cuestión.
Devuelve
Una referencia de tipo « Python » al objeto.
qk_obs_borrow_from_python
QkObs *qk_obs_borrow_from_python(PyObject *ob)
Obtener un QkObs puntero de un objeto Python.
Esto toma prestada una referencia de tipo « Python » y extrae el QkObs puntero correspondiente, siempre que sea del tipo adecuado. El puntero devuelto se toma prestado del ob puntero. Si el PyObject tipo no es el correcto, el valor de retorno es NULL y se establece el estado de excepción del intérprete de Python.
Para llamar a esta función, debes estar conectado a un intérprete de Python.
También puedes utilizar qk_obs_convert_from_python, que lógicamente es exactamente igual que esta función, pero que se puede emplear directamente como función «convertidora» para la PyArg_Parse* familia de funciones de conversión de Python.
Seguridad
La persona que llama debe estar conectada a un intérprete de Python. El comportamiento es indefinido si no ob es un puntero válido y no nulo a un objeto Python.
Parámetros
- ob : un objeto de tipo « Python » tomado de otro lenguaje.
Devuelve
Un puntero al objeto nativo, o bien, NULL si el objeto Python es de un tipo incorrecto.
qk_obs_convert_from_python
int qk_obs_convert_from_python(PyObject *object, void *address)
Obtener un QkObs puntero de un objeto Python.
Esto toma prestada una referencia de tipo « Python » y extrae el QkObs puntero correspondiente a address, siempre que sea del tipo correcto. El puntero devuelto se toma prestado del object puntero. Si no es PyObject del tipo correcto, el valor de retorno es 1, se establece el estado de excepción del intérprete de Python, y address no sufre cambios.
Para llamar a esta función, debes estar conectado a un intérprete de Python.
También puedes usar qk_obs_borrow_from_python, que, lógicamente, es exactamente lo mismo que esto, pero con una sintaxis más natural para su uso directo.
Seguridad
La persona que llama debe estar conectada a un intérprete de Python. El comportamiento es indefinido si no object es un puntero válido y no nulo a un objeto Python, o si no address es un puntero a datos modificables del tipo correcto.
Parámetros
- objeto : un objeto de tipo
Pythontomado en préstamo. - dirección : la ubicación en la que se escribirá la salida.
Devuelve
1 si se realiza correctamente, 0 si falla.