QkObs
typedef struct QkObs QkObsUm observável sobre bases Pauli que armazena seus dados em um formato qubit-esparso.
matemática
Esse observável representa uma soma sobre cadeias de operadores de Pauli e projetores de estado próprio de Pauli, com cada termo ponderado por algum número complexo. Ou seja, o observável completo é
para números complexos e operadores de um único qubit atuando no qubit a partir de um alfabeto restrito . A soma sobre é a soma dos termos individuais, e o produto tensorial produz as cadeias de operadores. O alfabeto de operadores de um único qubit permitido do qual o é extraído são os operadores de Pauli e os operadores de projeção de estado próprio de Pauli. Explicitamente, são eles:
Operador | QkBitTerm | Valor numérico |
|---|---|---|
| (identidade) | Não armazenado. | Não armazenado. |
| (Pauli X) | QkBitTerm_X | 0b0010 (2) |
| (Pauli Y) | QkBitTerm_Y | 0b0011 (3) |
| (Pauli Z) | QkBitTerm_Z | 0b0001 (1) |
| (projetor para o estado próprio positivo de X) | QkBitTerm_Plus | 0b1010 (10) |
| (projetor para o estado próprio negativo de X) | QkBitTerm_Minus | 0b0110 (6) |
| (projetor para o estado próprio positivo de Y) | QkBitTerm_Right | 0b1011 (11) |
| (projetor para o estado próprio negativo de Y) | QkBitTerm_Left | 0b0111 (7) |
| (projetor para o estado próprio positivo de Z) | QkBitTerm_Zero | 0b1001 (9) |
| (projetor para o estado próprio negativo de Z) | QkBitTerm_One | 0b0101 (5) |
Devido ao fato de permitir tanto os Paulis quanto seus projetores, o alfabeto permitido forma uma base supercompleta do espaço do operador. Isso significa que não há um somatório único para representar um determinado observável. Como consequência, a comparação requer mais cuidado e o uso do qk_obs_canonicalize em dois observáveis matematicamente equivalentes pode não resultar na mesma representação.
QkObs usa sua base particular supercompleta com o objetivo de tornar a "eficiência da medição" equivalente à "eficiência da representação". Por exemplo, o observável pode ser medido com eficiência no hardware com medições simples de , mas só pode ser representado em termos de Paulis como , o que requer termos armazenados em . QkObs requer apenas um único termo para armazenar isso. A desvantagem disso é que não é prático pegar uma matriz arbitrária e encontrar a melhor representação QkObs . Normalmente, você desejará construir um QkObs diretamente, em vez de tentar se decompor em um.
Representação
A representação interna de um QkObs armazena apenas os operadores de qubit não idênticos. Isso torna significativamente mais eficiente a representação de observáveis, como ; QkObs requer uma quantidade de memória linear no número total de qubits. Os termos são armazenados compactados, com espírito semelhante ao formato de linha esparsa compactada de matrizes esparsas. Nessa analogia, os termos da soma são as "linhas", e os termos do qubit são as "colunas", em que uma entrada ausente representa a identidade em vez de um zero. De forma mais explícita, a representação é composta por quatro matrizes contíguas:
Atributo acessível por | Duração | Descrição |
|---|---|---|
qk_obs_coeffs | O multiplicador escalar complexo para cada termo. | |
qk_obs_bit_terms | Cada um dos termos de um único qubit sem identidade para todos os operadores, em ordem. Elas correspondem à não identidade na descrição da soma, em que as entradas são armazenadas na ordem crescente de primeiro, e na ordem crescente de dentro de cada termo. | |
qk_obs_indices | O qubit correspondente ( ) para cada um dos termos de bit. QkObs exige que essa lista seja classificada por termos, e os algoritmos podem contar com a manutenção desse invariante. | |
qk_obs_boundaries | Os índices que dividem os termos de bits e os índices em termos completos. Para o termo número , seu coeficiente complexo é armazenado no índice i, e seus operadores de qubit único não idênticos e seus qubits correspondentes estão no intervalo [boundaries[i], boundaries[i+1]) nos termos e índices de bit, respectivamente. Os limites sempre têm um 0 explícito como seu primeiro elemento. |
O parâmetro de comprimento é o número de termos na soma e pode ser consultado usando qk_obs_num_terms. O parâmetro é o número total de termos de um único qubit sem identidade e pode ser consultado usando qk_obs_len.
Como exemplos ilustrativos:
- no caso de um operador zero, os limites têm comprimento 1 (um único 0) e todos os outros vetores são vazios.
- no caso de um operador de identidade totalmente simplificado, os limites são
{0, 0}, os coeficientes têm uma única entrada e os termos de bit e os índices são vazios. - para o operador , os limites são
{0, 2, 4}, os coeficientes são{1.0, -1.0}, os termos de bits são{QkBitTerm_Z, QkBitTerm_Z, QkBitTerm_Y, QkBitTerm_X}e os índices são{0, 2, 1, 3}. O operador pode atuar em mais de quatro qubits, dependendo do número de qubits (consulteqk_obs_num_qubits). Observe que os termos e índices de um único bit são classificados em ordem ordenada termwise.
Esses casos não são especiais, são totalmente consistentes com as regras e não devem precisar de tratamento especial.
Ordenação canônica
Para qualquer observável matemático dado, há várias maneiras de representá-lo com QkObs. Por exemplo, o mesmo conjunto de termos de um único bit e seus índices correspondentes podem aparecer várias vezes no observável. Matematicamente, isso é equivalente a ter apenas um único termo com todos os coeficientes somados. Da mesma forma, os termos da soma em um QkObs podem estar em qualquer ordem e representar o mesmo observável, já que a adição é comutativa (embora a adição de ponto flutuante não seja associativa, o QkObs não garante a ordem da soma).
Essas duas categorias de degenerescência de representação podem fazer com que o operador de igualdade, qk_obs_equal, afirme que dois observáveis não são iguais, apesar de representarem o mesmo objeto. Nesses casos, pode ser conveniente definir alguma forma canônica que permita que os observáveis sejam comparados estruturalmente. Você pode colocar um QkObs em formato canônico usando a função qk_obs_canonicalize . A ordenação precisa dos termos na ordenação canônica não é especificada e pode mudar entre as versões do Qiskit. Na mesma versão do Qiskit, no entanto, você pode comparar dois observáveis estruturalmente, comparando suas formas simplificadas.
Se você quiser levar em conta a tolerância de ponto flutuante na comparação, é mais seguro usar uma receita 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;
}A forma canônica produzida apenas pelo qk_obs_canonicalize não detectará universalmente todos os observáveis que são equivalentes devido ao alfabeto de base excessivamente completo.
Indexando
Os termos de soma observáveis individuais em QkObs podem ser acessados por meio de qk_obs_term e retornam objetos do tipo QkObsTerm. Esses termos contêm campos com o coeficiente do termo, seus termos de bit, índices e o número de qubits em que ele é definido. Juntamente com a informação do número de termos, você pode iterar sobre todos os termos observáveis 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...
}O preenchimento de um QkObsTerm por meio do qk_obs_term fará referência aos dados do QkObs original. A modificação dos termos ou índices de bits alterará o observável e poderá deixá-lo em um estado incoerente.
Construção
QkObs pode ser construído inicializando um observável vazio (com qk_obs_zero) e adicionando termos iterativamente (com qk_obs_add_term). Como alternativa, um observável pode ser construído a partir de dados "brutos" (com qk_obs_new) se todos os dados internos forem especificados. Isso requer cuidado para garantir que os dados sejam coerentes e resultem em um observável válido.
Função | Resumo |
|---|---|
qk_obs_zero | Construa um observável vazio em um determinado número de qubits. |
qk_obs_identity | Construa a identidade observável em um determinado número de qubits. |
qk_obs_new | Construa um observável a partir das matrizes de dados brutos. |
Manipulação matemática
QkObs suporta operações aritméticas fundamentais entre observáveis ou com escalares. Agora, você pode:
- adicionar duas variáveis observáveis usando
qk_obs_addeqk_obs_add_inplace - multiplicar por um número complexo com
qk_obs_multiplyeqk_obs_multiply_inplace - compor (multiplicar) dois observáveis via
qk_obs_composeeqk_obs_compose_map - calcular
left + scalar * rightpara duas variáveis observáveis e um escalar complexo comqk_obs_scaled_addeqk_obs_scaled_add_inplace
Funções
qk_obs_zero
QkObs *qk_obs_zero(uint32_t num_qubits)
Construa o observável zero (sem nenhum termo).
Exemplo
QkObs *zero = qk_obs_zero(100);Parâmetros
- num_qubits - O número de qubits em que o observável está definido.
Retorna
Um ponteiro para o observável criado.
qk_obs_identity
QkObs *qk_obs_identity(uint32_t num_qubits)
Construa o observável de identidade.
Exemplo
QkObs *identity = qk_obs_identity(100);Parâmetros
- num_qubits - O número de qubits em que o observável está definido.
Retorna
Um ponteiro para o observável criado.
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 um novo observável a partir de dados brutos.
Qualquer um dos argumentos de ponteiro pode ser NULL se e somente se seu comprimento correspondente for zero.
Exemplo
// 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
);Segurança
O comportamento é indefinido se qualquer uma das condições a seguir for violada:
coeffsé um ponteiro para uma matrizQkComplex64de comprimentonum_termsbit_termsé um ponteiro para uma matriz de elementosQkBitTermválidos de comprimentonum_bitsindicesé um ponteiro para uma matrizuint32_tde comprimentonum_bits, que é classificada por termos em ordem estritamente crescente, e cada elemento é menor quenum_qubitsboundariesé um ponteiro para uma matrizsize_tde comprimentonum_terms + 1, que é classificada em ordem crescente, o primeiro elemento é 0 e o último elemento é menor quenum_terms
Parâmetros
- num_qubits - O número de qubits em que o observável está definido.
- num_terms - O número de termos.
- num_bits - O número total de termos de bits não idênticos.
- coeffs - Um ponteiro para o primeiro elemento da matriz de coeficientes, que tem o comprimento
num_terms. - bit_terms - Um ponteiro para o primeiro elemento da matriz de termos de bits, que tem comprimento
num_bits. - indices - Um ponteiro para o primeiro elemento da matriz de índices, que tem o comprimento
num_bits. Observe que, por período, eles devem ser classificados de forma incremental. - boundaries - Um ponteiro para o primeiro elemento da matriz de limites, que tem o comprimento
num_terms + 1.
Retorna
Se os dados de entrada forem coerentes e a construção for bem-sucedida, o resultado será um ponteiro para o observável. Caso contrário, um ponteiro nulo é retornado.
qk_obs_free
void qk_obs_free(QkObs *obs)
Liberar o observável.
Exemplo
QkObs *obs = qk_obs_zero(100);
qk_obs_free(obs);Segurança
O comportamento é indefinido se obs não for nulo ou um ponteiro válido para QkObs.
Parâmetros
- obs - Um ponteiro para o observável a ser liberado.
qk_obs_add_term
QkExitCode qk_obs_add_term(QkObs *obs, const QkObsTerm *cterm)
Adicionar um termo ao observável.
Exemplo
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);Segurança
O comportamento é indefinido se algum dos itens a seguir for violado:
obsé um ponteiro válido e não nulo para umQkObsctermé um ponteiro válido e não nulo para umQkObsTerm
Parâmetros
- obs - Um ponteiro para o observável.
- cterm - Um ponteiro para o termo a ser adicionado.
Retorna
Um código de saída. O endereço eletrônico é >0 se o termo for incoerente ou se a adição do termo falhar.
qk_obs_term
QkExitCode qk_obs_term(QkObs *obs, uint64_t index, QkObsTerm *out)
Obter um termo observável por referência.
Um QkObsTerm contém ponteiros para os índices e termos de bits no termo, que podem ser usados para modificar os dados internos do observável. Isso pode deixar o observável em um estado incoerente e deve ser evitado, a menos que se tome muito cuidado. Em geral, é mais seguro construir um novo observável em vez de tentar fazer modificações no local.
Exemplo
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);Segurança
O comportamento é indefinido se qualquer um dos itens a seguir for violado
obsé um ponteiro válido e não nulo para umQkObsouté um ponteiro válido e não nulo para umQkObsTerm
Parâmetros
- obs - Um ponteiro para o observável.
- index - O índice do termo a ser obtido.
- out - Um ponteiro para um
QkObsTermusado para retornar o termo observável.
Retorna
Um código de saída.
qk_obs_num_terms
size_t qk_obs_num_terms(const QkObs *obs)
Obtenha o número de termos no observável.
Exemplo
QkObs *obs = qk_obs_identity(100);
size_t num_terms = qk_obs_num_terms(obs); // num_terms==1Segurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
Parâmetros
- obs - Um ponteiro para o observável.
Retorna
O número de termos no observável.
qk_obs_num_qubits
uint32_t qk_obs_num_qubits(const QkObs *obs)
Obtém o número de qubits em que o observável está definido.
Exemplo
QkObs *obs = qk_obs_identity(100);
uint32_t num_qubits = qk_obs_num_qubits(obs); // num_qubits==100Segurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
Parâmetros
- obs - Um ponteiro para o observável.
Retorna
O número de qubits em que o observável é definido.
qk_obs_len
size_t qk_obs_len(const QkObs *obs)
Obtém o número de termos/índices de bits no observável.
Exemplo
QkObs *obs = qk_obs_identity(100);
size_t len = qk_obs_len(obs); // len==0, as there are no non-trivial bit termsSegurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
Parâmetros
- obs - Um ponteiro para o observável.
Retorna
O número de termos no observável.
qk_obs_coeffs
QkComplex64 *qk_obs_coeffs(QkObs *obs)
Obtém um ponteiro para os coeficientes.
Isso pode ser usado para ler e modificar os coeficientes do observável. O ponteiro resultante é válido para leitura de qk_obs_num_terms(obs) elementos de QkComplex64.
Exemplo
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);
}Segurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
Parâmetros
- obs - Um ponteiro para o observável.
Retorna
Um ponteiro para os coeficientes.
qk_obs_indices
uint32_t *qk_obs_indices(QkObs *obs)
Obtém um ponteiro para os índices.
Isso pode ser usado para ler e modificar os índices do observável. O ponteiro resultante é válido para leitura em qk_obs_len(obs) elementos de tamanho uint32_t.
Exemplo
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);Segurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
Parâmetros
- obs - Um ponteiro para o observável.
Retorna
Um ponteiro para os índices.
qk_obs_boundaries
size_t *qk_obs_boundaries(QkObs *obs)
Obter um ponteiro para os limites do termo.
Isso pode ser usado para ler e modificar os limites de termo do observável. O ponteiro resultante é válido para leitura em qk_obs_num_terms(obs) + 1 elementos de tamanho size_t.
Exemplo
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]);
}Segurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
Parâmetros
- obs - Um ponteiro para o observável.
Retorna
Um ponteiro para os limites.
qk_obs_bit_terms
QkBitTerm *qk_obs_bit_terms(QkObs *obs)
Obtém um ponteiro para os termos de bits.
Isso pode ser usado para ler e modificar os termos de bits do observável. O ponteiro resultante é válido para leitura em qk_obs_len(obs) elementos de tamanho uint8_t.
Exemplo
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);Segurança
O comportamento é indefinido obs se o ponteiro não for válido QkObs ou não for nulo, ou se forem gravados valores inválidos no ponteiro QkBitTerm resultante.
Parâmetros
- obs - Um ponteiro para o observável.
Retorna
Um ponteiro para os termos de bits.
qk_obs_multiply
QkObs *qk_obs_multiply(const QkObs *obs, const QkComplex64 *coeff)
Multiplique o observável por um coeficiente complexo.
Exemplo
QkObs *obs = qk_obs_identity(100);
QkComplex64 coeff = {2, 0};
QkObs *result = qk_obs_multiply(obs, &coeff);Segurança
O comportamento é indefinido se qualquer um dos itens a seguir for violado
obsé um ponteiro válido e não nulo para umQkObscoeffé um ponteiro válido e não nulo para umQkComplex64
Parâmetros
- obs - Um ponteiro para o observável.
- coeff - O coeficiente com o qual multiplicar o observável.
Retorna
Um ponteiro para o resultado obs * coeff.
qk_obs_multiply_inplace
void qk_obs_multiply_inplace(QkObs *obs, const QkComplex64 *coeff)
Multiplique a variável observável no próprio local por um coeficiente complexo.
Exemplo
QkObs *obs = qk_obs_identity(100);
QkComplex64 coeff = {2, 0};
qk_obs_multiply_inplace(obs, &coeff);Segurança
O comportamento é indefinido se qualquer um dos itens a seguir for violado
obsé um ponteiro válido e não nulo para umQkObscoeffé um ponteiro válido e não nulo para umQkComplex64
Parâmetros
- obs - Um ponteiro para o observável.
- coeff - O coeficiente com o qual multiplicar o observável.
qk_obs_add
QkObs *qk_obs_add(const QkObs *left, const QkObs *right)
Adicione dois observáveis.
Exemplo
QkObs *left = qk_obs_identity(100);
QkObs *right = qk_obs_zero(100);
QkObs *result = qk_obs_add(left, right);Segurança
O comportamento é indefinido se left ou right não forem ponteiros válidos e não nulos para QkObs\ s.
Parâmetros
- left - Um ponteiro para o observável esquerdo.
- right - Um ponteiro para o observável correto.
Retorna
Um ponteiro para o resultado left + right.
qk_obs_add_inplace
void qk_obs_add_inplace(QkObs *left, const QkObs *right)
Adicionar um observável a um já existente.
Exemplo
QkObs *left = qk_obs_identity(100);
QkObs *right = qk_obs_zero(100);
qk_obs_add_inplace(left, right);Segurança
O comportamento é indefinido se left ou right não forem ponteiros válidos e não nulos para QkObs\ s.
Parâmetros
- left - Um ponteiro para o observável esquerdo.
- right - Um ponteiro para o observável correto.
qk_obs_scaled_add
QkObs *qk_obs_scaled_add(const QkObs *left, const QkObs *right, const QkComplex64 *factor)
Adicione duas variáveis observáveis enquanto ajusta os coeficientes da variável à direita.
Exemplo
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);Segurança
O comportamento é indefinido se left ou right não forem ponteiros válidos e não nulos para QkObs\ s.
Parâmetros
- left - Um ponteiro para o observável esquerdo.
- right - Um ponteiro para o observável correto.
- fator – O fator pelo qual os coeficientes devem ser multiplicados.
Retorna
Um ponteiro de propriedade para o resultado left + factor * right.
qk_obs_scaled_add_inplace
void qk_obs_scaled_add_inplace(QkObs *left, const QkObs *right, const QkComplex64 *factor)
Adicione um observável escalonado a um já existente.
Exemplo
QkObs *left = qk_obs_zero(100);
QkObs *right = qk_obs_identity(100);
QkComplex64 factor = {2, 0};
qk_obs_scaled_add_inplace(left, right, &factor);Segurança
O comportamento é indefinido se left ou right não forem ponteiros válidos e não nulos para QkObs\ s.
Parâmetros
- left - Um ponteiro para o observável esquerdo.
- right - Um ponteiro para o observável correto.
- fator – O fator pelo qual os coeficientes devem ser multiplicados.
qk_obs_compose
QkObs *qk_obs_compose(const QkObs *first, const QkObs *second)
Compor (multiplicar) dois observáveis.
Exemplo
QkObs *first = qk_obs_zero(100);
QkObs *second = qk_obs_identity(100);
QkObs *result = qk_obs_compose(first, second);Segurança
O comportamento é indefinido se first ou second não forem ponteiros válidos e não nulos para QkObs\ s.
Parâmetros
- primeiro - Um observável.
- segundo - O outro observável.
Retorna
first.compose(second) que é igual ao observável result = second @ first, em termos da multiplicação da matriz @.
qk_obs_compose_map
QkObs *qk_obs_compose_map(const QkObs *first, const QkObs *second, const uint32_t *qargs)
Componha (multiplique) dois observáveis de acordo com uma ordem de qubit personalizada.
Notavelmente, isso permite a composição de dois observáveis de tamanhos diferentes.
Exemplo
QkObs *first = qk_obs_zero(100);
QkObs *second = qk_obs_identity(100);
QkObs *result = qk_obs_compose(first, second);Segurança
Para chamar essa função com segurança
firsteseconddevem ser ponteiros válidos e não nulos paraQkObs\ sqargsdeve apontar para uma matriz deuint32_t, legível para os elementos deqk_obs_num_qubits(second)(ou seja, o número de qubits emsecond)
Parâmetros
- primeiro - Um observável.
- segundo - O outro observável. O número de qubits deve corresponder ao comprimento de
qargs. - qargs - Os argumentos do qubit especificam quais índices em
firstdevem ser associados aos índices emsecond.
Retorna
first.compose(second) que é igual ao observável result = second @ first, em termos da multiplicação da matriz @.
qk_obs_apply_layout
QkExitCode qk_obs_apply_layout(QkObs *obs, const uint32_t *layout, uint32_t num_qubits)
Aplique um novo layout de qubit ao observável.
O layout é definido por uma matriz layout de novos índices, especificando que o qubit no índice atual i é rotulado novamente para o índice layout[i]. O número de qubits em que o observável atua pode ser estendido definindo um num_qubits maior do que o observável atual tem.
Exemplo
Essa interface permite rotular novamente e ampliar os índices de qubit:
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);Em um fluxo de trabalho do compilador, essa função pode ser convenientemente usada para aplicar um QkTranspileLayout* obtido de uma passagem do transpilador, chamado transpile_layout no exemplo a seguir:
// 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);Segurança
O comportamento é indefinido se obs não for um ponteiro válido e não nulo para QkObs ou se layout não for um ponteiro válido e não nulo para uma sequência de qk_obs_num_qubits(obs) elementos consecutivos de uint32_t.
Parâmetros
- obs - Um ponteiro para o observável, esse observável será modificado no local em caso de sucesso. Verifique o código de saída para garantir que o layout foi aplicado corretamente.
- layout - Um ponteiro para o layout. O ponteiro deve apontar para uma matriz para
qk_obs_num_qubits(obs)elementos do tipouint32_t. Cada elemento deve ter valores em[0, num_qubits). - num_qubits - O número de qubits de saída.
Retorna
Um código de saída.
QkExitCode_Successapós o sucessoQkExitCode_DuplicteIndexErrorse forem encontrados índices de qubit duplicadosQkExitCode_MismatchedQubitssenum_qubitsfor menor que o número de qubits no observávelQkExitCode_IndexErrorpara quaisquer outros erros de índice, como valores inválidos emlayout.
qk_obs_canonicalize
QkObs *qk_obs_canonicalize(const QkObs *obs, double tol)
Calcule a representação canônica do observável.
Exemplo
QkObs *iden = qk_obs_identity(100);
QkObs *two = qk_obs_add(iden, iden);
double tol = 1e-6;
QkObs *canonical = qk_obs_canonicalize(two, tol);Segurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
Parâmetros
- obs - Um ponteiro para o observável.
- tol - A tolerância abaixo da qual os coeficientes são considerados zero.
Retorna
A representação canônica do observável.
qk_obs_copy
QkObs *qk_obs_copy(const QkObs *obs)
Copie o observável.
Exemplo
QkObs *original = qk_obs_identity(100);
QkObs *copied = qk_obs_copy(original);Segurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
Parâmetros
- obs - Um ponteiro para o observável.
Retorna
Um ponteiro para uma cópia do observável.
qk_obs_equal
bool qk_obs_equal(const QkObs *obs, const QkObs *other)
Compare dois observáveis para verificar a igualdade.
Observe que isso não compara a igualdade matemática, mas a igualdade de dados. Isso significa que dois observáveis podem representar o mesmo observável, mas não podem ser comparados como iguais.
Exemplo
QkObs *observable = qk_obs_identity(100);
QkObs *other = qk_obs_identity(100);
bool are_equal = qk_obs_equal(observable, other);Segurança
O comportamento é indefinido se obs ou other não forem ponteiros válidos e não nulos para QkObs\ s.
Parâmetros
- obs - Um ponteiro para um observável.
- other - Um ponteiro para outro observável.
Retorna
true se os observáveis forem iguais, false caso contrário.
qk_obs_str
char *qk_obs_str(const QkObs *obs)
Retorna uma representação em string de um QkObs.
Exemplo
QkObs *obs = qk_obs_identity(100);
char *string = qk_obs_str(obs);
qk_str_free(string);Segurança
O comportamento é indefinido obs não é um ponteiro válido e não nulo para um QkObs.
A string não deve ser liberada com o C free normal; você deve usar qk_str_free para liberar a memória consumida pela String. Não chamar o endereço qk_str_free resultará em um vazamento de memória.
Não altere o comprimento da cadeia de caracteres depois que ela for retornada (escrevendo um byte nulo em algum lugar dentro da cadeia ou removendo o byte final), embora os valores possam ser alterados.
Parâmetros
- obs - Um ponteiro para
QkObspara obter a cadeia de caracteres.
Retorna
Um ponteiro para uma matriz de caracteres com terminação nula da representação de string para obs
qk_str_free
void qk_str_free(char *string)
Libera uma representação de string.
Segurança
O comportamento é indefinido se str não for um ponteiro retornado por qk_obs_str ou qk_obsterm_str.
Parâmetros
- string - Um ponteiro para a representação da cadeia de caracteres retornada de
qk_obs_strouqk_obsterm_str.
qk_obs_to_python
PyObject *qk_obs_to_python(QkObs *obs)
Passe a propriedade de um QkObs objeto para Python.
Não é seguro usar o QkObs ponteiro após chamar esta função. Em particular, você não deve tentar apagá-lo ou liberá-lo. O chamador deve ser o proprietário do objeto QkObs, e não possuir uma referência emprestada (por exemplo, um objeto QkObs * recuperado de um retrieved from não qk_obs_borrow_from_python é de sua propriedade).
Segurança
O chamador deve estar conectado a um intérprete do tipo Python. O comportamento é indefinido se não obs for um ponteiro válido e diferente de nulo para um objeto inicializado e de propriedade do usuário QkObs.
Parâmetros
- obs – O objeto em questão.
Retorna
Uma referência de propriedade do tipo Python ao objeto.
qk_obs_borrow_from_python
QkObs *qk_obs_borrow_from_python(PyObject *ob)
Recuperar um QkObs ponteiro de um objeto Python.
Isso utiliza uma referência de tipo Python e extrai o QkObs ponteiro correspondente, caso seja do tipo correto. O ponteiro retornado é obtido do ponteiro ob . Se o não PyObject for do tipo correto, o valor de retorno é NULL e o estado de exceção do interpretador do Python é definido.
Você deve estar conectado a um interpretador Python para chamar esta função.
Você também pode usar qk_obs_convert_from_python, que é, em termos lógicos, exatamente igual a esta função, mas pode ser usada diretamente como uma função “conversora” para a PyArg_Parse* família de funções conversoras do Python.
Segurança
O chamador deve estar conectado a um intérprete do tipo Python. O comportamento é indefinido se não ob for um ponteiro válido e diferente de nulo para um objeto Python.
Parâmetros
- ob – Um objeto de empréstimo do tipo
Python.
Retorna
Um ponteiro para o objeto nativo, ou NULL se o objeto Python for do tipo incorreto.
qk_obs_convert_from_python
int qk_obs_convert_from_python(PyObject *object, void *address)
Recuperar um QkObs ponteiro de um objeto Python.
Isso utiliza uma referência do tipo Python e extrai o QkObs ponteiro correspondente para address``,, caso seja do tipo correto. O ponteiro retornado é obtido do ponteiro object . Se o não PyObject for do tipo correto, o valor de retorno é 1, o estado de exceção do interpretador do Python é definido e address permanece inalterado.
Você deve estar conectado a um interpretador Python para chamar esta função.
Você também pode usar qk_obs_borrow_from_python, que é, na prática, exatamente o mesmo que isto, mas com uma sintaxe mais natural para uso direto.
Segurança
O chamador deve estar conectado a um intérprete do tipo Python. O comportamento é indefinido se não object for um ponteiro válido e diferente de nulo para um objeto Python, ou se não address for um ponteiro para dados graváveis do tipo correto.
Parâmetros
- objeto – Um objeto
Pythonobtido por empréstimo. - endereço – O local onde a saída deve ser gravada.
Retorna
1 em caso de sucesso, 0 em caso de falha.