Skip to main content
IBM Quantum Platform

Comprendre les options « à couche ouverte » et « à couche fermée », ainsi que leur incidence sur la construction des sous-espaces

Dans ce guide, nous allons vous montrer comment choisir les dimensions du sous-espace dans le qiskit_addon_sqd package afin de post-traiter des échantillons quantiques à l'aide de la technique de récupération de configuration auto-cohérente.

Mais surtout, ce guide met également en évidence certaines différences de comportement dans la construction du sous-espace lorsque l'on utilise les open_shell = False modes ou open_shell = True :

  • open_shell = False ne fonctionne que lorsque le nombre d'électrons en phase de montée en vitesse et en phase de ralentissement est identique.

  • open_shell = True doit être utilisé lorsque le nombre d'électrons en phase d'accélération et de décélération est différent. On peut également l'utiliser lorsque le nombre d'électrons en phase d'accélération et de décélération est identique. Toutefois, dans ce dernier cas, il existe une différence entre les dimensions des sous-espaces générés par open_shell = False et open_shell = True, comme l'explique ce guide.

REMARQUE : Certaines des manipulations de configuration électronique (chaîne de bits) effectuées dans ce progiciel ont pour objectif de préserver la symétrie de spin totale S2S^2. Les solveurs standard d’interaction de configuration sélectionnée (SCI) ne permettent pas d’imposer exactement la conservation de l’ S2S^2. Par conséquent, ils procèdent approximativement à l'aide d'un multiplicateur de Lagrange.

Le choix des configurations électroniques introduites dans le solveur d'états propres peut également avoir une forte incidence sur la conservation du spin. Par exemple, dans un système (à 2 électrons, 2 orbitales), on peut échantillonner la configuration 1001|1001\rangle (présentant une seule excitation de spin vers le haut par rapport à l’état RHF 0101|0101\rangle ), qui est une combinaison linéaire des états singulet et triplet à couche ouverte, respectivement (1001±0110)/2(|1001\rangle ± |0110\rangle) /\sqrt{2}. Si la configuration |0110⟩ n’est pas échantillonnée, il n’est possible de construire aucune fonction propre du spin total, ce qui entraîne une contamination ou une redondance de spin (la configuration |1001⟩ est impliquée dans un calcul CI, mais son coefficient est égal à 0 dans le vecteur CI). Étant donné qu’un seul échantillon 1001|1001\rangle est généré dans l’ordinateur quantique, voici comment le sqd module gère cette situation :

  • open_shell = False:

    1. La chaîne de bits « 10011001 » est divisée en deux parties, représentant respectivement les configurations « spin-up » et « spin-down » : 1010 (up) et 0101 (down).

    2. La liste des configurations uniques à polarisation de spin a été établie : U=[01,10]\mathcal{U} = [01, 10].

    3. Nous examinons ensuite toutes les combinaisons possibles d'éléments d' U\mathcal{U} s pour former la base : {0101,0110,1001,1010}\left \{ |0101\rangle, |0110\rangle , |1001\rangle , |1010\rangle \right \}, qui contient les états singulet et triplet.

  • open_shell = True:

    1. Contrairement à ce qui se passe open_shell = False dans ce cas, nous ne combinons pas les deux moitiés de la chaîne de bits pour former la base.

À coque fermée

Cet exemple montre comment les chaînes de bits sont manipulées dans un système (à 2 électrons et 4 orbitales).

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

Définissez manuellement un dictionnaire de résultats de mesure :

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

Convertissez les nombres d'occurrences dict en une matrice de chaînes de bits et en un tableau de probabilités en vue du 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)

Output:

[[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]

Prélever un sous-échantillon d'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_and_subsample

n_batches = 1
samples_per_batch = 2

# seed for random number generator
rand_seed = 48

# Generate the batches
batches = postselect_and_subsample(
    bitstring_matrix_full,
    probs_arr_full,
    hamming_right=num_elec_a,
    hamming_left=num_elec_b,
    samples_per_batch=samples_per_batch,
    num_batches=n_batches,
    rand_seed=rand_seed,
)

print(batches[0])

Output:

[[False False False  True False False  True False]
 [False  True False False  True False False False]]

Obtenir la représentation décimale des chaînes de bits « spin-up » et « 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 vers le haut, tandis que le deuxième élément du tuple correspond à la représentation décimale des configurations de spin vers le bas :

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)

Output:

(array([1, 2, 4, 8], dtype=int64), array([1, 2, 4, 8], dtype=int64))

Il convient de noter que, bien que le nombre d'échantillons par lot soit de deux et que les chaînes de bits échantillonnées soient : 0001001000010010 et 0100100001001000, quatre configurations électroniques sont générées par espèce de spin. Dans ce cas, l'ensemble des configurations uniques à polarisation de spin est donné par :

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

dont la représentation décimale base-10 est

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

Base du sous-espace :

Le solveur d'états propres prend en compte toutes les paires possibles de chaînes de bits « spin-up » et « spin-down » pour construire la base B\mathcal{B} du sous-espace :

  • Élément 1 : 00010001|00010001\rangle

  • Élément 2 : 00010010|00010010\rangle

  • Élément 3 : 00010100|00010100\rangle

  • Élément 4 : 00011000|00011000\rangle

  • Élément 5 : 00100001|00100001\rangle

  • Élément 6 : 00100010|00100010\rangle

  • Élément 7 : 00100100|00100100\rangle

  • Élément 8 : 00101000|00101000\rangle

  • Élément 9 : 01000001|01000001\rangle

  • Élément 10 : 01000010|01000010\rangle

  • Élément 11 : 01000100|01000100\rangle

  • Élément 12 : 01001000|01001000\rangle

  • Élément 13 : 10000001|10000001\rangle

  • Élément 14 : 10000010|10000010\rangle

  • Élément 15 : 10000100|10000100\rangle

  • Élément 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)
            + ">"
        )

Output:

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 bornée supérieurement par : 22 \cdot (samples_per_batch) 2^2.

À coque ouverte

Cet exemple montre comment les chaînes de bits sont manipulées dans un système (à 2 électrons et 4 orbitales).

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

Définissez manuellement un dictionnaire de résultats de mesure :

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

Convertissez les nombres d'occurrences dict en une matrice de chaînes de bits et en un tableau de probabilités en vue du 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)

Output:

[[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]

Prélever un sous-échantillon d'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

# Generate the batches
batches = postselect_and_subsample(
    bitstring_matrix_full,
    probs_arr_full,
    hamming_right=num_elec_a,
    hamming_left=num_elec_b,
    samples_per_batch=samples_per_batch,
    num_batches=n_batches,
    rand_seed=rand_seed,
)

print(batches[0])

Output:

[[False False False  True False False  True False]
 [False  True False False  True False False False]]

Obtenir la représentation décimale des chaînes de bits « spin-up » et « 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 vers le haut, tandis que le deuxième élément du tuple correspond à la représentation décimale des configurations de spin vers le bas :

ci_strs = bitstring_matrix_to_ci_strs(batches[0], open_shell=open_shell)
print(ci_strs)

Output:

(array([2, 8], dtype=int64), array([1, 4], dtype=int64))

Si l'on spécifie que open_shell = True, on n'inclut pas toutes les demi-chaînes de bits uniques en tant que configurations « spin-up » et « spin-down », ce qui donne une base plus restreinte que lorsque l'on spécifie open_shell = False.

Base du sous-espace :

Le solveur d'états propres prend en compte toutes les paires possibles de chaînes de bits « spin-up » et « spin-down » pour construire la base B\mathcal{B} du sous-espace :

  • Élément 1 : 00010010|00010010\rangle

  • Élément 2 : 00011000|00011000\rangle

  • Élément 3 : 01000010|01000010\rangle

  • Élément 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)
            + ">"
        )

Output:

Basis elements of the subspace:
|00100001>
|00100100>
|10000001>
|10000100>

La dimension du sous-espace est bornée supérieurement par : (samples_per_batch) 2^2.

Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.