Skip to main content
IBM Quantum Platform

Générer des circuits « SqDRIFT »

Avec la diagonalisation quantique par échantillonnage ( SQD ), vous devez choisir une hypothèse à partir de laquelle échantillonner les chaînes de bits. La variante « SqDRIFT » utilise quant à elle un ensemble de circuits d'évolution temporelle construits directement à partir de l'hamiltonien cible. Pour ce faire, on procède à un sous-échantillonnage des opérateurs d'évolution temporelle de l'hamiltonien à partir de ses coefficients; cette méthode est connue sous le nom de « trotterisation » d' qDRIFT.

Ce guide de démarrage explique comment générer un ensemble de circuits aléatoires de ce type.


1. Configuration hamiltonienne

Aux fins du présent guide, chargez l'hamiltonien de la structure électronique de l' N2 e à partir d'un fichier FCIDUMP. Il existe d'autres moyens de construire le FermionOperator. N'oubliez pas de consulter sa documentation, ainsi que le qiskit_fermions.operators.library.

[x] PYTHON

>>> from qiskit_fermions.operators.library import FCIDump
>>> from qiskit_fermions.operators import FermionOperator
>>>
>>> fcidump = FCIDump.from_file("docs/guides/n2.fcidump")
>>> num_modes = 2 * fcidump.norb
>>> hamil = FermionOperator.from_fcidump(fcidump)

[] C

#include <qiskit_fermions.h>

QfFCIDump* fcidump = qf_fcidump_from_file("docs/guides/n2.fcidump");
QfFermionOperator* hamil = qf_ferm_op_from_fcidump(fcidump);
uint32_t num_modes = 2 * qf_fcidump_norb(fcidump);

2. Termes de l'hamiltonien de groupe

Exploitez les nombreuses symétries présentes dans l'hamiltonien de la structure électronique en regroupant les termes apparentés qui ont des coefficients identiques. Cette opération modifie la distribution des coefficients des opérateurs à partir de laquelle le protocole « qDRIFT » effectue ses échantillonnages, mais elle n'affecte pas ses garanties de convergence. Il est essentiel de noter que le regroupement des termes liés par symétrie entraîne une annulation favorable des termes de Pauli, ce qui se traduit par une profondeur de circuit globalement plus faible lors de l'évolution temporelle d'un état sous leur action.

Ce module qiskit_fermions.operators.terms.grouping propose des fonctions pratiques permettant de regrouper les termes d’un opérateur. Ce point est expliqué plus en détail dans ce guide.

Attention

La mise en œuvre de cette formule suppose group_terms_by_electronic_structure() que les termes de l'hamiltonien soient ordonnés selon l'ordre normal!

[x] PYTHON

>>> from qiskit_fermions.operators.terms.grouping import group_terms_by_electronic_structure
>>> from qiskit_fermions.operators.terms.ordering import canonical_order
>>>
>>> canon = canonical_order(hamil.normal_ordered().simplify(atol=1e-16))
>>> exit_code = group_terms_by_electronic_structure(canon, num_modes, two_body_physicist_order=False)
>>> assert exit_code is None
>>> print(canon.groups)  # the groups attribute now contains some list of group indices
[0, ...]

[] C

QfFermionOperator* normal;
QfExitCode exit = qf_ferm_op_group_terms_by_electronic_structure(normal, num_modes, false);
QfFermionOperator* canon = qf_ferm_op_canonical_order(normal);
Conseil

L'hamiltonien complet de la structure électronique contient certains termes dont l'intégration dans un circuit d'évolution temporelle n'a aucune incidence sur les chaînes de bits perçues et n'entraîne donc qu'une augmentation de la charge d'échantillonnage. Il est donc recommandé d'éliminer ces termes de l'hamiltonien à ce stade, avant de construire la porte Evolution à l'étape suivante.

Les termes qui correspondent à cette description sont ceux qui sont diagonaux dans la base occupation-nombre, c'est-à-dire les produits d'opérateurs numériques ( aiaia^\dagger_i a_i ). Cela inclut le décalage d'énergie constant, dont l'évolution temporelle n'introduit qu'une phase globale dans le circuit, les opérateurs numériques individuels dont l'évolution temporelle équivaut à des rotations Z d'un seul qubit, ainsi que des produits d'ordre supérieur tels que ninjn_i n_j. Aucun d'entre eux n'a d'impact sur les chaînes de bits échantillonnées.

La fonction filter_diagonal_terms() supprime ces termes d'un opérateur directement :

[x] PYTHON

>>> from qiskit_fermions.operators.terms.filtering import filter_diagonal_terms
>>>
>>> filter_diagonal_terms(canon)

[] C

qf_ferm_op_filter_diagonal_terms(canon);

Effectuer un filtrage ici, une seule fois, revient nettement moins cher que de le faire à plusieurs reprises. QDriftTrotterization s'exécute une seule fois par circuit transpilé; ainsi, le fait de filtrer l'hamiltonien dès le départ, plutôt qu'à chaque appel, évite de refaire le travail pour chaque circuit généré à partir de celui-ci.


3. Préparer le circuit d'évolution temporelle

Préparez le circuit d'évolution temporelle de l'hamiltonien et le circuit de base à partir duquel prélever des échantillons. Le qiskit_fermions.circuit.library contient tous les composants nécessaires à cet effet, conformément aux conventions de Qiskit.

[x] PYTHON

>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>>
>>> time = 1.0  # you can choose a desired scaling factor here
>>> evo_gate = Evolution(num_modes, canon, time)
>>>
>>> circ = FermionicCircuit(num_modes)
>>> circ.append(evo_gate, circ.modes)

[] C

// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions.
Remarque

Dans cet exemple, on n'initialise pas les modes fermioniques avec des particules, et on ne mesure pas non plus leur état final.


4. Transpiler le circuit à l'aide de QDrift Trotterization

Ce module qiskit_fermions.transpiler s'intègre directement au pipeline de transpilation de Qiskit, ce qui permet de transpilier directement le code construit FermionicCircuit ci-dessus en un fichier QuantumCircuit.

Utilisez la correspondance « jordan_wigner() fermion-qubit » pour convertir l'hamiltonien exprimé en termes de fermions afin qu'il soit exprimé sous forme de chaînes de Pauli. Cela peut être effectué directement dans le cadre du processus de transpilation à l'aide du plugin de passe de transpilation EvolutionSynthesis . Utilisez generate_preset_jw_pass_manager() pour créer FermionicStagedPassManager, ce qui garantit que le codage de Jordan-Wigner est utilisé de manière cohérente pour toutes les instructions du circuit.

Il est essentiel d'ajouter la passe de transpilation QDriftTrotterization à l'étape optimization correspondante du pipeline de transpilation. Cela garantit que le circuit n'utilise pas l'évolution temporelle de l'hamiltonien complet, dont la complexité dépasserait les capacités du matériel de calcul quantique actuellement disponible.

Au lieu de cela, il effectue un sous-échantillonnage d'un nombre fixe groups de termes hamiltoniens pour chaque circuit, à chaque fois que celui-ci est transpilé. Cela vous permet de générer plusieurs randomisations de circuits, comme l'exige l'algorithme « SqDRIFT », en exécutant de manière répétée le pipeline de transpilation.

Cette étape présente également les quelques paramètres que vous pouvez utiliser pour personnaliser les circuits à générer :

  • Nombre de circuits à générer : num_sqdrift_randomizations
  • La longueur de chaque circuit en termes de groupes d'excitation : num_groups

[x] PYTHON

>>> from qiskit_fermions.transpiler import FermionicPassManager
>>> from qiskit_fermions.transpiler.presets import generate_preset_jw_pass_manager
>>> from qiskit_fermions.transpiler.passes import QDriftTrotterization
>>>
>>> num_groups = 10
>>> qdrift = QDriftTrotterization(num_groups, rng=19)
>>>
>>> pm = generate_preset_jw_pass_manager()
>>> pm.optimization = FermionicPassManager([qdrift])
>>>
>>> num_sqdrift_randomizations = 10
>>> sqdrift_circuits = [
...     pm.run(circ) for _ in range(num_sqdrift_randomizations)
... ]

[] C

// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.
Remarque

L'exemple précédent définit les paramètres du seed générateur de nombres aléatoires utilisé au sein de la passe de transpilation QDriftTrotterization .


5. Éliminer les excitations mineures

Au-delà des termes diagonaux éliminés lors de l'étape précédente, une excitation échantillonnée peut encore ne pas affecter les chaînes de bits échantillonnées. Lorsqu'il agit exclusivement dans un ensemble de modes dont l'état d'occupation est déjà déterminé (tous occupés ou tous inoccupés), il ne peut pas faire passer une particule d'un mode à l'autre; il laisse donc l'état (et donc le résultat éventuel de la mesure) inchangé. Le réglage filter_trivial=True « pass QDriftTrotterization » rejette ces termes au fur et à mesure de leur échantillonnage et en tire un autre à la place, de sorte que chacun des emplacements num_groups du circuit obtenu apporte une excitation non triviale.

Ce filtrage doit savoir quels modes sont occupés dès le départ. Il faut donc une InitializeModes porte précédant les portes Evolution du circuit; constitue InitializeModes.from_hartree_fock() un moyen pratique d'en construire une. Ajoutez ici un exemple tiré de la référence de Hartree-Fock « N2 » (sept électrons alpha et sept électrons bêta dans 14 orbitales spatiales) et comparez les excitations échantillonnées avec et sans filter_trivial=True:

[x] PYTHON

>>> from qiskit_fermions.circuit.library import InitializeModes
>>>
>>> init = InitializeModes.from_hartree_fock(fcidump.norb, (7, 7))
>>>
>>> hf_circ = FermionicCircuit(num_modes)
>>> hf_circ.append(init, hf_circ.modes)
>>> hf_circ.append(Evolution(num_modes, canon, time), hf_circ.modes)
>>>
>>> num_groups = 5
>>> qdrift_unfiltered = QDriftTrotterization(num_groups, rng=3480)
>>> qdrift_trivial = QDriftTrotterization(
...     num_groups, filter_trivial=True, rng=3480
... )
>>>
>>> for instruction in FermionicPassManager(qdrift_unfiltered).run(hf_circ)._inner.data:
...     if instruction.operation.name == "Evolution":
...         print(sorted(instruction.operation.operator.get_support()))
[2, 4]
[41, 45, 52]
[15, 45, 55]
[41, 52, 53]
[10, 16, 37, 38]
>>>
>>> for instruction in FermionicPassManager(qdrift_trivial).run(hf_circ)._inner.data:
...     if instruction.operation.name == "Evolution":
...         print(sorted(instruction.operation.operator.get_support()))
[0, 1, 6, 7]
[0, 1, 28, 29]
[4, 13, 55]
[13, 20, 40, 41]
[0, 1, 28, 29]

[] C

// WARNING: Qiskit's C API does not yet allow us to implement circuits
// with custom gate definitions, which we therefore also cannot transpile
// via this API.

Aucune des excitations échantillonnées sans filtrage n'affecte en quoi que ce soit l'ensemble occupé (0-6 et 28-34), de sorte qu'aucune d'entre elles ne peut faire passer une particule d'un mode occupé à un mode inoccupé; chacune d'entre elles est triviale et n'aurait aucun effet sur les chaînes de bits échantillonnées. Avec filter_trivial=True, les cinq sont rejetées et remplacées par des excitations qui établissent effectivement un couplage entre un mode occupé et un mode inoccupé. Par exemple, la première excitation acceptée [0, 1, 6, 7] fait passer une particule entre les modes occupés 0, 1, et 6 et le mode inoccupé 7.

Une fois qu'une excitation est acceptée, chaque mode de son support devient « incertain » et peut donc jouer l'un ou l'autre rôle pour les échantillons suivants; ainsi, les ensembles de modes occupés et inoccupés ne cessent de s'agrandir à mesure que de nouvelles excitations sont acceptées. C'est ce qui rend la deuxième excitation, [0, 1, 28, 29], acceptable. Ses quatre modes font tous partie des modes initialement occupés; il ne se couple donc avec aucun mode initialement inoccupé. Cela n'est admis que parce que les modes 0 et sont devenus 1 incertains (et donc éligibles en tant que côté « inoccupé » du couplage) dès que la première excitation les a touchés.

Remarque

En l'absence de InitializeModes porte précédente, il ne filter_trivial=True dispose d'aucune information sur l'occupation à utiliser comme critère de filtrage. Il émet un UserWarning et laisse l'échantillon non filtré pour cette Evolution porte.


(Facultatif) Optimisation de l'indexation des modes fermioniques

Vous pouvez ajouter une étape d'optimisation supplémentaire au pipeline de transpilation afin de minimiser la distance entre les intervalles d'excitation fermioniques, en renommant les indices des modes fermioniques. Cette optimisation a été présentée dans l'article « SqDRIFT » et est mise en œuvre par build_excitation_span_minimization_model(). Il peut être facilement intégré au pipeline du transpileur à l'aide de la commande pass RelabelModes :

[x] PYTHON

>>> from pyomo.environ import SolverFactory
>>> from qiskit_fermions.transpiler.passes import RelabelModes
>>>
>>> solver = SolverFactory("appsi_highs")
>>> solver.options["time_limit"] = 10
>>>
>>> qdrift = QDriftTrotterization(5, rng=19)
>>> relabel = RelabelModes(solver=solver)
>>>
>>> pm.optimization = FermionicPassManager([qdrift, relabel])
>>>
>>> relabeled_circ = pm.run(circ)
>>> # if the automatic mode relabeling was successful, the circuit's
>>> # metadata will contain the mode `permutation` information

[] C

// WARNING: This feature is not available via the C API.
Remarque

L'utilisation de l'optimisation automatique intégrée ( RelabelModes qui s'appuie sur build_excitation_span_minimization_model()) nécessite la dépendance optionnelle gérée par HAS_PYOMO.

Important

Afin de réaliser correctement la diagonalisation du sous-espace, les chaînes de bits échantillonnées à partir des circuits qui ont été transpilés lors de la passe d’optimisation RelabelModes doivent être post-traitées en fonction des informations permutation contenues dans les métadonnées de ces circuits!


Etapes suivantes

Maintenant que vous avez réussi à générer un ensemble de circuits, vous pouvez en échantillonner des chaînes de bits. Pour ce faire, les circuits doivent être mis en œuvre sur du matériel. Pour obtenir des instructions détaillées, consultez la documentation de Qiskit.

Une fois les échantillons de chaînes de bits obtenus, ceux-ci peuvent être utilisés en combinaison avec les coefficients hamiltoniens pour effectuer le post-traitement SQD, comme expliqué dans les tutoriels consacrés au module complémentaire SQD.

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