Comprendre les options à couches ouvertes et à couches fermées et leur effet sur la construction du sous-espace
Dans ce guide, nous montrons comment choisir les dimensions des sous-espaces dans le package qiskit_addon_sqd pour post-traiter des échantillons quantiques à l'aide de la technique de récupération de configurations auto-cohérente.
Plus important encore, ce guide met aussi en évidence certaines différences de comportement dans la construction du sous-espace selon que l'on utilise le mode open_shell = False ou open_shell = True :
-
open_shell = Falsene fonctionne que lorsque le nombre d'électrons de spin up et de spin down est identique. -
open_shell = Truedoit être utilisé lorsque le nombre d'électrons de spin up et de spin down est différent. Il peut aussi être utilisé lorsque ce nombre est identique. Cependant, dans ce dernier cas, les tailles des sous-espaces générés diffèrent entreopen_shell = Falseetopen_shell = True, comme expliqué dans ce guide.
NOTE : Certaines manipulations des configurations électroniques (chaînes de bits) de ce package ont pour objectif de préserver la symétrie de spin totale . Les solveurs standards d'interaction de configurations sélectionnées (SCI) ne peuvent pas imposer exactement la conservation de . Ils le font donc de manière approchée via un multiplicateur de Lagrange.
Le choix des configurations électroniques fournies au solveur d'états propres peut aussi avoir un fort effet sur la conservation du spin. Par exemple, dans un système (2 électrons, 2 orbitales), on peut échantillonner la configuration (qui présente une seule excitation de spin up par rapport à l'état RHF ), qui est une combinaison linéaire des états singulet et triplet à couches ouvertes, respectivement . Si la configuration |0110⟩ n'est pas échantillonnée, on ne peut construire aucune des fonctions propres de spin total, ce qui conduit à une contamination de spin ou à une redondance (la configuration |1001⟩ intervient dans un calcul CI, mais a un coefficient 0 dans le vecteur CI). Étant donné qu'un seul échantillon est généré dans l'ordinateur quantique, voici comment le package sqd gère cette situation :
-
open_shell = False:-
La chaîne de bits est coupée en deux, représentant les configurations de spin up et de spin down : (up) et (down).
-
La liste des configurations uniques polarisées en spin est construite : .
-
Nous considérons ensuite toutes les combinaisons possibles des éléments de pour former la base : , qui contient les états singulet et triplet.
-
-
open_shell = True:- Contrairement au cas
open_shell = False, nous ne combinons pas les moitiés de la chaîne de bits pour former la base.
- Contrairement au cas
Couches fermées
Cet exemple montre comment les chaînes de bits sont manipulées dans un système (2 électrons, 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
Spécifie à la main un dictionnaire de résultats de mesure :
counts_dict = {
"00010010": 1 / 2.0 - 0.01,
"01001000": 1 / 2.0 - 0.01,
"00010001": 0.02,
}
Transforme le dict de comptages en une matrice de chaînes de bits et un tableau de probabilités pour le post-traitement :
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]
Sous-échantillonne un seul lot de taille deux :
-
n_batches = 1: nombre de lots de configurations utilisés par les différents appels au solveur d'états propres -
samples_per_batch = 2: nombre de configurations uniques à inclure dans chaque lot
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]]
Obtiens la représentation décimale des chaînes de bits de spin up et de spin down utilisées par le solveur d'états propres.
Le premier élément du tuple correspond à la représentation décimale des configurations de spin up, tandis que le second élément du tuple correspond à la représentation décimale des configurations de spin down :
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))
Note que, bien que le nombre d'échantillons par lot soit de deux et que les chaînes de bits échantillonnées soient : et , quatre configurations électroniques sont générées par espèce de spin. Dans ce cas, l'ensemble des configurations uniques polarisées en spin est donné par :
dont la représentation décimale en base 10 est
Base du sous-espace :
Le solveur d'états propres prend toutes les paires possibles de chaînes de bits de spin up et de spin down pour construire la base du sous-espace :
-
Élément 1 :
-
Élément 2 :
-
Élément 3 :
-
Élément 4 :
-
Élément 5 :
-
Élément 6 :
-
Élément 7 :
-
Élément 8 :
-
Élément 9 :
-
Élément 10 :
-
Élément 11 :
-
Élément 12 :
-
Élément 13 :
-
Élément 14 :
-
Élément 15 :
-
Élément 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 dimension du sous-espace est majorée par : (samples_per_batch).
Couches ouvertes
Cet exemple montre comment les chaînes de bits sont manipulées dans un système (2 électrons, 4 orbitales).
# Specify molecule properties
num_orbitals = 4
num_elec_a = num_elec_b = 1
open_shell = True
Spécifie à la main un dictionnaire de résultats de mesure :
counts_dict = {
"00010010": 1 / 2.0 - 0.01,
"01001000": 1 / 2.0 - 0.01,
"00010001": 0.02,
}
Transforme le dict de comptages en une matrice de chaînes de bits et un tableau de probabilités pour le post-traitement :
# 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]
Sous-échantillonne un seul lot de taille deux :
-
n_batches = 1: nombre de lots de configurations utilisés par les différents appels au solveur d'états propres -
samples_per_batch = 2: nombre de configurations uniques à inclure dans chaque lot
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]]
Obtiens la représentation décimale des chaînes de bits de spin up et de spin down utilisées par le solveur d'états propres.
Le premier élément du tuple correspond à la représentation décimale des configurations de spin up, tandis que le second élément du tuple correspond à la représentation décimale des configurations de spin down :
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 nous spécifions open_shell = True, nous n'incluons pas toutes les demi-chaînes de bits uniques comme configurations de spin up et de spin down, ce qui donne une base plus petite qu'avec open_shell = False.
Base du sous-espace :
Le solveur d'états propres prend toutes les paires possibles de chaînes de bits de spin up et de spin down pour construire la base du sous-espace :
-
Élément 1 :
-
Élément 2 :
-
Élément 3 :
-
Élément 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 dimension du sous-espace est majorée par : (samples_per_batch).