Comprende las opciones de capa abierta frente a capa cerrada y su efecto en la construcción del subespacio
En esta guía mostraremos cómo elegir las dimensiones del subespacio en el paquete qiskit_addon_sqd para posprocesar muestras cuánticas usando la técnica de recuperación de configuraciones autoconsistente.
Más importante aún, esta guía también destaca algunas diferencias en el comportamiento de la construcción del subespacio cuando se ejecuta en los modos open_shell = False o open_shell = True:
-
open_shell = Falsesolo funciona cuando el número de electrones de espín arriba y de espín abajo es el mismo. -
open_shell = Truedebe usarse cuando el número de electrones de espín arriba y de espín abajo es diferente. También se puede usar cuando ese número es el mismo. Sin embargo, en este último caso hay una diferencia en los tamaños de los subespacios generados entreopen_shell = Falseyopen_shell = True, como se explica en esta guía.
NOTA: Algunas de las manipulaciones de configuraciones electrónicas (cadenas de bits) de este paquete tienen como objetivo preservar la simetría de espín total . Los solvers estándar de interacción de configuraciones seleccionadas (SCI) no pueden imponer exactamente la conservación de . En consecuencia, lo hacen de forma aproximada mediante un multiplicador de Lagrange.
La elección de las configuraciones electrónicas que entran en el solver de estados propios también puede tener un fuerte efecto en la conservación del espín. Por ejemplo, en un sistema de (2 electrones, 2 orbitales), se puede muestrear la configuración (que tiene una única excitación de espín arriba sobre el estado RHF ), que es una combinación lineal de los estados singlete y triplete de capa abierta, respectivamente . Si no se muestrea la configuración |0110⟩, no se puede construir ninguna de las funciones propias de espín total, lo que conduce a contaminación de espín o redundancia (la configuración |1001⟩ participa en un cálculo CI, pero tiene coeficiente 0 en el vector CI). Dado que el computador cuántico genera una única muestra , así es como el paquete sqd maneja esta situación:
-
open_shell = False:-
La cadena de bits se divide por la mitad, representando las configuraciones de espín arriba y espín abajo: (arriba) y (abajo).
-
Se construye la lista de configuraciones únicas con polarización de espín: .
-
Luego consideramos todas las combinaciones posibles de los elementos de para formar la base: , que contiene los estados singlete y triplete.
-
-
open_shell = True:- A diferencia del caso
open_shell = False, no combinamos las mitades de la cadena de bits para formar la base.
- A diferencia del caso
Capa cerrada
Este ejemplo muestra cómo se manipulan las cadenas de bits en un sistema de (2 electrones, 4 orbitales).
# 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
Especifica a mano un diccionario de resultados de medición:
counts_dict = {
"00010010": 1 / 2.0 - 0.01,
"01001000": 1 / 2.0 - 0.01,
"00010001": 0.02,
}
Transforma el dict de conteos en una matriz de cadenas de bits y un arreglo de probabilidades para el posprocesamiento:
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]
Submuestrea un único lote de tamaño dos:
-
n_batches = 1: número de lotes de configuraciones usados por las distintas llamadas al solver de estados propios -
samples_per_batch = 2: número de configuraciones únicas que se incluyen en 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]]
Obtén la representación decimal de las cadenas de bits de espín arriba y espín abajo usadas por el solver de estados propios.
El primer elemento de la tupla corresponde a la representación decimal de las configuraciones de espín arriba, mientras que el segundo elemento de la tupla corresponde a la representación decimal de las configuraciones de espín abajo:
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))
Ten en cuenta que, aunque el número de muestras por lote es dos y las cadenas de bits muestreadas son: y , se generan cuatro configuraciones electrónicas por especie de espín. En este caso, el conjunto de configuraciones únicas con polarización de espín está dado por:
cuya representación decimal en base 10 es
Base del subespacio:
El solver de estados propios toma todos los pares posibles de cadenas de bits de espín arriba y espín abajo para construir la base del subespacio:
-
Elemento 1:
-
Elemento 2:
-
Elemento 3:
-
Elemento 4:
-
Elemento 5:
-
Elemento 6:
-
Elemento 7:
-
Elemento 8:
-
Elemento 9:
-
Elemento 10:
-
Elemento 11:
-
Elemento 12:
-
Elemento 13:
-
Elemento 14:
-
Elemento 15:
-
Elemento 16:
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>
La dimensión del subespacio está acotada superiormente por: (samples_per_batch).
Capa abierta
Este ejemplo muestra cómo se manipulan las cadenas de bits en un sistema de (2 electrones, 4 orbitales).
# Specify molecule properties
num_orbitals = 4
num_elec_a = num_elec_b = 1
open_shell = True
Especifica a mano un diccionario de resultados de medición:
counts_dict = {
"00010010": 1 / 2.0 - 0.01,
"01001000": 1 / 2.0 - 0.01,
"00010001": 0.02,
}
Transforma el dict de conteos en una matriz de cadenas de bits y un arreglo de probabilidades para el posprocesamiento:
# 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]
Submuestrea un único lote de tamaño dos:
-
n_batches = 1: número de lotes de configuraciones usados por las distintas llamadas al solver de estados propios -
samples_per_batch = 2: número de configuraciones únicas que se incluyen en 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]]
Obtén la representación decimal de las cadenas de bits de espín arriba y espín abajo usadas por el solver de estados propios.
El primer elemento de la tupla corresponde a la representación decimal de las configuraciones de espín arriba, mientras que el segundo elemento de la tupla corresponde a la representación decimal de las configuraciones de espín abajo:
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))
Si especificamos open_shell = True, no incluimos todas las semicadenas de bits únicas como configuraciones de espín arriba y espín abajo, lo que produce una base más pequeña que al especificar open_shell = False.
Base del subespacio:
El solver de estados propios toma todos los pares posibles de cadenas de bits de espín arriba y espín abajo para construir la base del subespacio:
-
Elemento 1:
-
Elemento 2:
-
Elemento 3:
-
Elemento 4:
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>
La dimensión del subespacio está acotada superiormente por: (samples_per_batch).