Pular para o conteúdo principal
Este projeto está em fase beta. Encontrou um bug? Abra uma issue no GitHub. Tem uma pergunta ou ideia? Pergunte no Discussions — adoraríamos receber seu feedback!
Como está a qualidade da tradução nesta página?

Entenda as opções de camada aberta versus camada fechada e o efeito na construção do subespaço

Neste guia, mostraremos como escolher as dimensões do subespaço no pacote qiskit_addon_sqd para pós-processar amostras quânticas usando a técnica de recuperação de configuração autoconsistente.

Mais importante ainda, este guia também destaca algumas diferenças no comportamento da construção do subespaço quando executada nos modos open_shell = False ou open_shell = True:

  • open_shell = False só funciona quando o número de elétrons de spin para cima e de spin para baixo é o mesmo.

  • open_shell = True deve ser usado quando o número de elétrons de spin para cima e de spin para baixo é diferente. Ele também pode ser usado quando o número de elétrons de spin para cima e de spin para baixo é o mesmo. No entanto, nesse último caso, há uma diferença nos tamanhos dos subespaços gerados entre open_shell = False e open_shell = True, como discutido neste guia.

NOTA: Algumas das manipulações de configurações eletrônicas (bitstrings) neste pacote têm como objetivo preservar a simetria de spin total S2S^2. Os solvers padrão de Interação de Configurações Selecionadas (SCI) não conseguem impor a conservação de S2S^2 exatamente. Consequentemente, eles o fazem de forma aproximada por meio de um multiplicador de Lagrange.

A escolha das configurações eletrônicas que entram no solver de autoestados também pode ter um forte efeito na conservação do spin. Por exemplo, em um sistema de (2 elétrons, 2 orbitais), pode-se amostrar a configuração ∣1001⟩|1001\rangle (que tem uma única excitação de spin para cima sobre o estado RHF ∣0101⟩|0101\rangle), que é uma combinação linear dos estados singleto e tripleto de camada aberta, respectivamente (∣1001⟩±∣0110⟩)/2(|1001\rangle ± |0110\rangle) /\sqrt{2}. Se a configuração |0110⟩ não for amostrada, não é possível construir nenhuma das autofunções de spin total, o que leva a contaminação de spin ou redundância (a configuração |1001⟩ participa de um cálculo CI, mas tem coeficiente 0 no vetor CI). Dado que uma única amostra ∣1001⟩|1001\rangle é gerada no computador quântico, é assim que o pacote sqd trata essa situação:

  • open_shell = False:

    1. A bitstring 10011001 é dividida ao meio, representando as configurações de spin para cima e de spin para baixo: 1010 (para cima) e 0101 (para baixo).

    2. A lista de configurações únicas spin-polarizadas é construída: U=[01,10]\mathcal{U} = [01, 10].

    3. Em seguida, consideramos todas as combinações possíveis dos elementos de U\mathcal{U} para formar a base: {∣0101⟩,∣0110⟩,∣1001⟩,∣1010⟩}\left \{ |0101\rangle, |0110\rangle , |1001\rangle , |1010\rangle \right \}, que contém os estados singleto e tripleto.

  • open_shell = True:

    1. Ao contrário do caso open_shell = False, não combinamos as metades da bitstring para formar a base.

Camada fechada​

Este exemplo mostra como as bitstrings são manipuladas em um sistema de (2 elétrons, 4 orbitais).

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"qiskit_addon_sqd": "qiskit-addon-sqd"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
# Specify molecule properties
num_orbitals = 4
num_elec_a = num_elec_b = 1
open_shell = False

Especifique manualmente um dicionário de resultados de medição:

counts_dict = {
"00010010": 1 / 2.0 - 0.01,
"01001000": 1 / 2.0 - 0.01,
"00010001": 0.02,
}

Transforme o dict de contagens em uma matriz de bitstrings e um array de probabilidades para pós-processamento:

from qiskit_addon_sqd.counts import counts_to_arrays

# Convert counts into bitstring and probability arrays
bitstring_matrix_full, probs_arr_full = counts_to_arrays(counts_dict)
print(bitstring_matrix_full)
print(probs_arr_full)
[[False False False True False False True False]
[False True False False True False False False]
[False False False True False False False True]]
[0.49 0.49 0.02]

Subamostre um único lote de tamanho dois:

  • n_batches = 1: Número de lotes de configurações usados pelas diferentes chamadas ao solver de autoestados

  • samples_per_batch = 2: Número de configurações únicas a incluir em cada lote

from qiskit_addon_sqd.subsampling import (
postselect_by_hamming_right_and_left,
subsample,
)

n_batches = 1
samples_per_batch = 2

# seed for random number generator
rand_seed = 48

# Post-select the configurations with the correct Hamming weight on each half
bitstring_matrix_postsel, probs_arr_postsel = (
postselect_by_hamming_right_and_left(
bitstring_matrix_full,
probs_arr_full,
hamming_right=num_elec_a,
hamming_left=num_elec_b,
)
)

# Generate the batches
batches = subsample(
bitstring_matrix_postsel,
probs_arr_postsel,
samples_per_batch,
n_batches,
rand_seed=rand_seed,
)

print(batches[0])
[[False False False True False False True False]
[False True False False True False False False]]

Obtenha a representação decimal das bitstrings de spin para cima e de spin para baixo usadas pelo solver de autoestados.

O primeiro elemento da tupla corresponde à representação decimal das configurações de spin para cima, enquanto o segundo elemento da tupla corresponde à representação decimal das configurações de spin para baixo:

from qiskit_addon_sqd.fermion import bitstring_matrix_to_ci_strs

ci_strs = bitstring_matrix_to_ci_strs(batches[0], open_shell=open_shell)
print(ci_strs)
(array([1, 2, 4, 8], dtype=int64), array([1, 2, 4, 8], dtype=int64))

Observe que, embora o número de amostras por lote seja dois e as bitstrings amostradas sejam 0001001000010010 e 0100100001001000, são geradas quatro configurações eletrônicas por espécie de spin. Neste caso, o conjunto de configurações únicas spin-polarizadas é dado por:

U={0001,0010,0100,1000}\mathcal{U} = \{ 0001, 0010, 0100, 1000 \}

cuja representação decimal na base 10 é

U10={1,2,4,8}\mathcal{U}_{10} = \{ 1, 2, 4, 8 \}

Base do subespaço:

O solver de autoestados toma todos os pares possíveis de bitstrings de spin para cima e de spin para baixo para construir a base B\mathcal{B} do subespaço:

  • Elemento 1: ∣00010001⟩|00010001\rangle

  • Elemento 2: ∣00010010⟩|00010010\rangle

  • Elemento 3: ∣00010100⟩|00010100\rangle

  • Elemento 4: ∣00011000⟩|00011000\rangle

  • Elemento 5: ∣00100001⟩|00100001\rangle

  • Elemento 6: ∣00100010⟩|00100010\rangle

  • Elemento 7: ∣00100100⟩|00100100\rangle

  • Elemento 8: ∣00101000⟩|00101000\rangle

  • Elemento 9: ∣01000001⟩|01000001\rangle

  • Elemento 10: ∣01000010⟩|01000010\rangle

  • Elemento 11: ∣01000100⟩|01000100\rangle

  • Elemento 12: ∣01001000⟩|01001000\rangle

  • Elemento 13: ∣10000001⟩|10000001\rangle

  • Elemento 14: ∣10000010⟩|10000010\rangle

  • Elemento 15: ∣10000100⟩|10000100\rangle

  • Elemento 16: ∣10001000⟩|10001000\rangle

ci_strs_up, ci_strs_dn = ci_strs

print("Basis elements of the subspace:")

for ci_str_up in ci_strs_up:
for ci_str_dn in ci_strs_dn:
format_name = "{0:0" + str(num_orbitals) + "b}"
print(
"|"
+ format_name.format(ci_str_up)
+ format_name.format(ci_str_dn)
+ ">"
)
Basis elements of the subspace:
|00010001>
|00010010>
|00010100>
|00011000>
|00100001>
|00100010>
|00100100>
|00101000>
|01000001>
|01000010>
|01000100>
|01001000>
|10000001>
|10000010>
|10000100>
|10001000>

A dimensão do subespaço é limitada superiormente por: 2⋅2 \cdot (samples_per_batch)2^2.

Camada aberta​

Este exemplo mostra como as bitstrings são manipuladas em um sistema de (2 elétrons, 4 orbitais).

# Specify molecule properties
num_orbitals = 4
num_elec_a = num_elec_b = 1
open_shell = True

Especifique manualmente um dicionário de resultados de medição:

counts_dict = {
"00010010": 1 / 2.0 - 0.01,
"01001000": 1 / 2.0 - 0.01,
"00010001": 0.02,
}

Transforme o dict de contagens em uma matriz de bitstrings e um array de probabilidades para pós-processamento:

# Convert counts into bitstring and probability arrays
bitstring_matrix_full, probs_arr_full = counts_to_arrays(counts_dict)
print(bitstring_matrix_full)
print(probs_arr_full)
[[False False False True False False True False]
[False True False False True False False False]
[False False False True False False False True]]
[0.49 0.49 0.02]

Subamostre um único lote de tamanho dois:

  • n_batches = 1: Número de lotes de configurações usados pelas diferentes chamadas ao solver de autoestados

  • samples_per_batch = 2: Número de configurações únicas a incluir em cada lote

n_batches = 1
samples_per_batch = 2

# seed for random number generator
rand_seed = 48

# Post-select the configurations with the correct Hamming weight on each half
bitstring_matrix_postsel, probs_arr_postsel = (
postselect_by_hamming_right_and_left(
bitstring_matrix_full,
probs_arr_full,
hamming_right=num_elec_a,
hamming_left=num_elec_b,
)
)

# Generate the batches
batches = subsample(
bitstring_matrix_postsel,
probs_arr_postsel,
samples_per_batch,
n_batches,
rand_seed=rand_seed,
)

print(batches[0])
[[False False False True False False True False]
[False True False False True False False False]]

Obtenha a representação decimal das bitstrings de spin para cima e de spin para baixo usadas pelo solver de autoestados.

O primeiro elemento da tupla corresponde à representação decimal das configurações de spin para cima, enquanto o segundo elemento da tupla corresponde à representação decimal das configurações de spin para baixo:

ci_strs = bitstring_matrix_to_ci_strs(batches[0], open_shell=open_shell)
print(ci_strs)
(array([2, 8], dtype=int64), array([1, 4], dtype=int64))

Se especificarmos open_shell = True, não incluímos todas as meias-bitstrings únicas como configurações de spin para cima e de spin para baixo, o que resulta em uma base menor do que ao especificar open_shell = False. Base do subespaço:

O solver de autoestados toma todos os pares possíveis de bitstrings de spin para cima e de spin para baixo para construir a base B\mathcal{B} do subespaço:

  • Elemento 1: ∣00010010⟩|00010010\rangle

  • Elemento 2: ∣00011000⟩|00011000\rangle

  • Elemento 3: ∣01000010⟩|01000010\rangle

  • Elemento 4: ∣01001000⟩|01001000\rangle

ci_strs_up, ci_strs_dn = ci_strs

print("Basis elements of the subspace:")

for ci_str_up in ci_strs_up:
for ci_str_dn in ci_strs_dn:
format_name = "{0:0" + str(num_orbitals) + "b}"
print(
"|"
+ format_name.format(ci_str_up)
+ format_name.format(ci_str_dn)
+ ">"
)
Basis elements of the subspace:
|00100001>
|00100100>
|10000001>
|10000100>

A dimensão do subespaço é limitada superiormente por: (samples_per_batch)2^2.