Notes de mise à jour de Qiskit Fermions
0.2.0
Prélude
Cette version renforce les possibilités de contrôle offertes par ce module en matière de synthèse de circuits. Une Evolution porte accepte désormais une synthesis méthode explicite, un nouveau synthesis module fournit des formules de produit fermionique du premier ordre et d'ordre supérieur, et la FermionicTrotterization fonction sélectionne et applique l'une d'entre elles à toutes les évolutions d'un circuit en une seule fois. Par ailleurs, le mappeur Jordan-Wigner dispose désormais d’implémentations directes pour les quatre représentations d’opérateurs, et de nouveaux adaptateurs permettent de simplifier la sortie du mappeur ou de l’émettre groupe par groupe. L'API C a considérablement rattrapé son retard. Les représentations « edge-vertex » et « transfer-vertex », leurs mappeurs et leur algèbre sont désormais accessibles depuis le langage C, tout comme la prise en charge des opérateurs, la conservation des secteurs, l'arithmétique sur place et mise à l'échelle, ainsi que le classement par groupes. Ces deux API ont également obtenu l'accès aux intégrales électroniques analysées par un FCIDump, auxquelles on ne pouvait auparavant accéder qu'en convertissant l'intégralité de la structure de données en un opérateur. De plus, cette version définit plus clairement le champ d’application de ce package : les opérateurs fermioniques, les mappeurs qui les convertissent, ainsi que le circuit indépendant du mappeur et sa transpilation. La simulation relevant désormais de la responsabilité de ffsim, le noyau FCI natif et les utilitaires d'algèbre linéaire qui le reproduisaient ont été supprimés afin de déléguer cette tâche à ffsim; les UCJ portes UCC et utilisent désormais les opérateurs de ffsim au lieu de réimplémenter leurs paramétrages. La simulation nécessite donc des fonctionnalités supplémentaires ffsim et, en raison de la dépendance de ffsim à l’égard d’ PySCF,, elle n’est pas disponible sous Windows; la création d’opérateurs, leur mappage et la transcompilation des circuits obtenus restent toutefois pris en charge partout. Comme il s'agit encore d'une version « pre-1.0 », toutes ces modifications ont été effectuées par suppression plutôt que par dépréciation : les notes de mise à jour ci-dessous associent chaque API supprimée à son équivalent de remplacement, et il est recommandé de les lire dans leur intégralité avant de procéder à la mise à jour.
Python Fonctionnalités de l'API
-
La manière dont une porte
Evolutionest décomposée dans l'espace fermionique est désormais configurable grâce à son nouvel argumentsynthesis, accessible uniquement par mot-clé, qui reprend l'argumentsynthesisde la fonction de QiskitPauliEvolutionGate. Les méthodes disponibles se trouvent dans le nouveauqiskit_fermions.circuit.library.synthesismodule, qui fournit l'interfaceFermionicEvolutionSynthesiset son implémentationFermionicLieTrotterde premier ordre.Cette étape de conversion fermion-fermion est facultative : une
Evolutionporte peut être transmise directement à l'étape de conversion fermion-qubit, quel que soit le nombre de termes que contient son opérateur. Le décomposer au préalable est un choix qui vise à obtenir des facteurs moins coûteux ou à mettre en évidence une structure (telle que la commutation mutuellegroups) que l'étape suivante pourra exploiter. Comme les deux côtés de la réécriture restent dans l'espace fermionique, cette structure est préservée, ce dont ces méthodes peuvent tirer parti, contrairementEvolutionSynthesisà celles de Qiskit.La valeur par défaut reste inchangée : en laissant
synthesissur, on utiliseNoneFermionicLieTrotter, ce qui reproduit exactement la décomposition effectuéeEvolutionauparavant par, de sorte que les circuits existants sont synthétisés de manière identique. -
Les poids d'échantillonnage de
QDriftTrotterizationpeuvent désormais être fournis via son nouvel argumentweightsde type « mot-clé uniquement », au lieu d'être calculés à chaque appel à l'opérateur évolué. Si l'on conserve la valeurNonepar défaut, le comportement reste exactement le même qu'auparavant; les circuits existants sont donc échantillonnés de la même manière.Il s'agit d'une option de performance : le chemin par défaut réduit le nombre de valeurs par terme de l'opérateur à une seule par groupe à chaque appel à
run(), ce qui représente un travail redondant lors de la génération d'un ensemble à partir d'un seul hamiltonien, même si le résultat est identique à chaque fois. Dérivez-le une fois avecgroup_coeff_means()à la place, en parallèle degroup_order(), ce qui permet de sortir la recherche de groupe de cette boucle :hamiltonian = group_order(hamiltonian) weights = group_coeff_means(hamiltonian) pm.optimization = FermionicPassManager( [QDriftTrotterization(num_groups, weights=weights)] )Les valeurs doivent être non négatives, car un poids correspond à l’ de l’amplitude de la décomposition d’ qDRIFT , dans laquelle le signe d’un coefficient appartient à et se déduit directement de l’opérateur évolué.
Un tableau fourni s'accompagne de deux contraintes. Son échelle n'est pas libre : la somme des poids détermine également le temps d'évolution partagé; ainsi, multiplier chaque entrée par donne le même nombre d'échantillons
gamma, mais l'évolution s'effectue surgamma * t. Et comme le tableau décrit les termes (ou groupes) d'un opérateur spécifique, sa longueur est validée par rapport à chaque porteEvolutionà laquelle il est appliqué, et un circuit comportant plus d'une porte de ce type est rejeté; le fait de laisser ce paramètreweightsnon défini permet de conserver un tel circuit, puisque chaque porte détermine alors la sienne. -
FCIDumpexpose désormais les intégrales électroniques qu'il a analysées, auxquelles on ne pouvait auparavant accéder qu'en convertissant l'intégralité de la structure de données en un fichierFermionOperator. Les nouvelles méthodes renvoient des copies en lecture seule sous forme de tableaux de type « NumPy », dans la même structure aplatie que celle utilisée par les constructeurs « electronic-integral » :get_one_body_tril_a(),get_one_body_tril_b(),get_two_body_tril_aa(), etget_two_body_tril_ab()get_two_body_tril_bb(). Les tableaux « beta-spin » fournissent des informations surNoneun fichier soumis à des restrictions de spin; le nouvel attributis_unrestricted« beta-spin » fournit des informations sur les trois à la fois. Le nouvel attributconstantindique l'énergie constante (de répulsion nucléaire), ouNonelorsque le fichier n'en contient aucune :fcidump = FCIDump.from_file("molecule.fcidump") one_body = fcidump.get_one_body_tril_a() two_body = fcidump.get_two_body_tril_aa()Notez que
get_two_body_tril_ab()est structuré différemment des autres tableaux à deux corps : il ne présente qu'une symétrie quadruple, et contient donc la matrice(npair, npair)complète, dont les paires de lignes indexent les espèces à spin alpha et les paires de colonnes celles à spin bêta. Consultez la documentation de la classe pour connaître la formule d'indexation de chaque tableau. Il appartient à l'appelant de les transformer en matrices denses ou(norb, norb)en tenseurs(norb,) * 4, comme l'exigent par exemple les outils de diagonalisation basés sur des échantillons. -
Ajout
FermionicSuzukiTrotterd'une formule de produit d'ordre supérieur permettant de décomposer une porteEvolutiondans l'espace fermionique. Lorsque chaque facteur estFermionicLieTrotterappliqué une seule fois, cela permet de les combiner de manière symétrique afin d'annuler les termes d'erreur d'ordre inférieur, et sonrepsargument divise l'évolution en plusieurs étapes plus courtes :Evolution(num_modes, operator, time=1.0, synthesis=FermionicSuzukiTrotter(order=2, reps=4))Comme il agit sur l'opérateur fermionique, il le divise selon les valeurs
groupsqui lui sont attribuées – une partition qui n'est plus disponible pour une formule de produit appliquée après la mise en correspondance entre fermions et qubits. Sur une chaîne de Fermi-Hubbard à six modes regroupés en trois ensembles de flux,order=2on a obtenureps=4une erreur de Trotter environ deux fois inférieure à celle d'une formule du second ordre du côté des qubits, avec un nombre légèrement inférieur de portes à deux qubits.FermionicLieTrotterC'est l'élément de premier ordre de cette famille et il est désormais implémenté en tant que tel, ce qui lui confère également l'argumentrepsqui lui faisait défaut auparavant. Les deux sont interchangeables à valeur égalereps.Il convient de noter qu'un ordre supérieur offre une plus grande précision grâce à sa profondeur : une formule d'ordre
kémet environ5**((k-2)/2)fois plus de facteurs qu'une formule d' order-2. Il convient également de noter que la précision d'un circuit décomposé est déterminée par la plus faible des deux formules : celle des fermions et celle des fermions-qubits; ainsi, augmenter l'ordre ici alors que cette dernière reste au premier ordre n'apporte pas grand-chose. -
Ajout
FermionicTrotterizationd'un passage de transcompilation qui « trotterise » chaque porteEvolutiond'un circuit à l'aide d'une méthode de synthèse fermion-fermion :pm.optimization = FermionicPassManager( [FermionicTrotterization(FermionicSuzukiTrotter(order=2, reps=4))] )Choisir une méthode de synthèse par porte implique de l'intégrer à tous les éléments qui composent un
Evolution– y compris etUCCUCJ, qui la génèrent en interne. Cette étape permet au contraire de définir ce choix une seule fois pour l'ensemble du pipeline. Un prédicatfilterfacultatif permet de le restreindre à un sous-ensemble des portes; il est utile de l'utiliser lorsqu'un circuit mélange des évolutions qui tirent parti d'un ordre supérieur et d'autres qui n'en tirent pas parti, comme les opérateurs de Coulomb diagonaux tous commutatifs d'unUCJ.Ce pass développe chaque porte sélectionnée en fonction des facteurs générés par sa méthode; il n'est donc pas nécessaire de procéder à une étape de développement distincte. Utilisez « Pass » pour
apply=Falsene sélectionner que la méthode et laisser le développement à un autre élément, tel que celui de QiskitDecompose; notez qu’une porte qui n’est jamais développée atteint l’étape de conversion des fermions en qubits dans son intégralité, où elle est mappée sansEvolution.synthesisjamais être lue. -
Ce module
qiskit_fermions.operators.terms.groupingaborde désormais l' analyse d'un regroupement existant ainsi que l'attribution d'indices de groupe. Les indices de groupe n'ont aucune signification intrinsèque; par conséquent, aucune des nouvelles fonctions n'indique si un regroupement est « correct »; chacune répond à une question précise concernant un regroupement, ce qui permet de vérifier dès le départ une hypothèse émise par un utilisateur en aval, plutôt que de devoir payer pour cette vérification à chaque appel :groups_are_hermitian()indique, pour chaque groupe, si l'opérateur formé par ses éléments est hermitien. C'est cette propriété sur laquelle repose une formule de produit aléatoire lorsqu'elle échantillonne des groupes entiers, car seul un groupe hermitien présente une évolution temporelle unitaire.groups_have_uniform_coeffs()indique, pour chaque groupe, si ses coefficients sont numériquement égaux (en valeur absolue, par défaut).
Consultez la section « Conditions d'utilisation des opérateurs de groupe » : vous y trouverez un exemple concret illustrant la structure des opérateurs.
-
Ajout de la fonction
group_order(), qui renvoie une copie d'un opérateur dont les termes sont classés par indice de groupe. Chaque groupe forme une suite contiguë de termes et les indices des groupes deviennent non décroissants; le tri est stable, de sorte que les termes au sein d’un groupe conservent leur ordre relatif. Un opérateur ne prenant en compte aucun indice de groupe ne dispose d’aucun critère de tri et renvoie une copie inchangée.Les indices de groupe indiquent uniquement quels termes vont ensemble; cela modifie donc la disposition des termes d'un opérateur, mais pas sa valeur. C'est la structure qui rend la recherche de groupes peu coûteuse : en général, il
split_out_groups()faut parcourir tous les termes pour trouver les groupes demandés, mais avec un opérateur ordonné par groupes, on effectue plutôt une recherche binaire sur les limites des groupes; ainsi, une recherche coûte autant que les groupes demandés, et non autant que les termes contenus. Le tri initial est donc rentable lors de recherches répétées :from qiskit_fermions.operators.terms.ordering import group_order hamil = group_order(hamil) # every subsequent hamil.split_out_groups(group_indices=...) is now a binary searchIl est préférable de le faire avant de procéder à des transpilations répétées avec
QDriftTrotterization, qui recherche ses groupes échantillonnés à chaque appel. Consultez la section « Générer des circuits d' SqDRIFT » pour voir un exemple concret. -
Ajout de mappeurs Jordan-Wigner directs pour les trois structures de données d'opérateurs restantes :
majorana_jordan_wigner(), etedge_vertex_jordan_wigner()transfer_vertex_jordan_wigner(). -
jordan_wigner()Il gère désormais les quatre types d'opérateurs au lieu de se limiter à un seulFermionOperator, en déléguant à l'implémentation directe qui correspond à l'opérateur qui lui est fourni. Le fait de passer l’un des trois autres types d’opérateurs déclenchait auparavant une exceptionTypeError. -
Cette méthode
is_hermitian()fait désormais partie du protocoleOperatorTrait. Comme toutes les classes d'opérateurs le fournissaient déjà, il est désormais possible de l'appeler sur n'importe quelle valeur de type etOperatorTraitnon plus uniquement sur une classe d'opérateurs concrète.Il convient de noter que la force de cette vérification varie selon le type d’opérateur, ce que le protocole présente comme sa garantie la plus faible : un résultat
Trueest toujours fiable, tandis qu’un résultatFalseest prudent pour les types d’opérateurs dont la forme normale n’est pas une véritable forme canonique. Voiris_hermitian()pour un exemple de ce type de cas. -
Ajout de deux adaptateurs qui encapsulent une fonction de mappage afin de contrôler l'ordre du terme de Pauli qu'elle génère, lequel est
MapperFnEvolutionSynthesisdésormais conservé tout au long de la synthèse.simplify()simplifie l'opérateur projeté, en fusionnant les termes de Pauli redondants et en imposant un ordre canonique des termes.group_wise()applique un opérateur à une entréegroupsà la fois et additionne les résultats. L'opérateur reste inchangé, mais les termes de chaque groupe apparaissent côte à côte plutôt qu'entremêlés, ce qui permet à ceux qui agissent sur des qubits disjoints de partager une couche de circuit :MapperFnEvolutionSynthesis(group_wise(jordan_wigner)) -
QDriftTrotterizationEnregistre désormais le nombre de tirages que sonfilter_trivialmode a écartés. Lorsqu'au moins uneEvolutionporte était effectivement filtrée, les « reportsmetadata» du circuit renvoyé etfilter_trivial.discardedfilter_trivial.emitted, chacun contenant un compte par porte filtrée, dans l'ordre du circuit. Ce rapport permet d'estimer la probabilité d'acceptation, qui correspond au facteur par lequel le filtrage a amplifié les coefficients des termes qu'il a conservés. Aucun de ces champs n'est présent lorsqu'aucune porte n'a été filtrée; il faut donc les lire avec.get(). -
Evolutionpeut désormais être simulé pour n'importe quel type d'opérateur de ce package, et pas seulementFermionOperator. Un opérateur d'un autre type est converti via son image fermionique (leSupportsFermionOperatorprotocole) avant d'être simulé; ainsi, l'évolution d'unMajoranaOperator, ouEdgeVertexOperatorTransferVertexOperatorne déclenche plusNotImplementedError. -
FermionOperatorest désormais implémentéeffsim.SupportsTracevia une nouvelle_trace_()méthode, ce quiffsim.trace()permet de calculer la trace d'un opérateur sur un secteur(norb, nelec)fixe. Voilà ce que sont les conditions préalables tout au longscipy.sparse.linalg.expm_multiply()du parcours évolutif.
Fonctionnalités de l'API C
-
Ajout
qf_ferm_op_conserves_sector()d'un terme qui vérifie que chaque terme d'un préserveQfFermionOperatorle nombre de particules au sein de chaque bloc de modes. Elle vient compléter la fonctionnalité existanteqf_ferm_op_conserves_particle_number(), qui ne vérifie que le total. Dans le cas d'une configuration spin-orbitale d'orbitales spatialesnorb, le passage entre les deux blocs{norb, norb}nécessite que les secteurs alpha et bêta soient conservés séparément, c'est-à-dire une conservation stricte à la fois du nombre de particules et de la composante z du spin :uint32_t spin_blocks[2] = {norb, norb}; bool conserves = qf_ferm_op_conserves_sector(op, spin_blocks, 2);Le passage
NULLd'un nombre de blocs0considère tous les modes comme un seul bloc, ce qui revient à appelerqf_ferm_op_conserves_particle_number(). -
Ajout de la prise en charge de l'opérateur à l'API C, qui n'était auparavant disponible que sur Python via
get_support. Chacune des quatre représentations d'opérateurs s'est vu ajouter une paire de fonctions :qf_ferm_op_num_support()etqf_ferm_op_get_support(), ainsi que leurs équivalentsqf_transfer_opqf_maj_op,qf_edge_opet.Contrairement aux autres accesseurs, ceux-ci ne renvoient pas de pointeur vers l'opérateur, car la prise en charge est calculée à la demande plutôt que stockée. Appelez d'abord la fonction
num_supportpour dimensionner le tampon de sortie, puis transmettez ce tampon àget_support:uint32_t num_support = qf_ferm_op_num_support(op); uint32_t *support = malloc(num_support * sizeof(uint32_t)); qf_ferm_op_get_support(op, support);Les fonctions C écrivent les indices de mode par ordre croissant, tandis que la méthode
Pythonrenvoie un tableau non triéset. -
Ajout de l'arithmétique par opérateur in situ à l'API C, ce qui évite d'allouer un opérateur de résultat lorsque l'opérande de gauche peut être écrasé. Chacune des quatre représentations d'opérateurs a obtenu
add_inplace,scaled_add_inplaceetmul_inplace, par exempleqf_ferm_op_add_inplace(),qf_ferm_op_scaled_add_inplace()etqf_ferm_op_mul_inplace():// Accumulate `factor * right` into `left` without building an intermediate. QkComplex64 factor = {-1.0, 0.0}; qf_ferm_op_scaled_add_inplace(left, right, &factor);Il faut noter que ces trois approches diffèrent dans leur traitement des indices de groupe. Ces deux opérations d'addition additionnent les termes de l'opérande de droite et réinitialisent donc l'attribut
groupsàNULL, tout comme le faitqf_ferm_op_add().mul_inplacecela ne fait que mettre les coefficients à l'échelle, ce qui laisse le nombre de termes inchangé et préserve ainsi le regroupement. -
Ajout d'une API C pour les représentations des opérateurs « edge-vertex » et « transfer-vertex », qui n'étaient auparavant disponibles que via Python. Cela inclut deux nouvelles structures opaques,
QfEdgeVertexOperatoretQfTransferVertexOperator, chacune disposant d'un ensemble de fonctions équivalent à celui des structures d'opérateurs existantes.Ces deux opérateurs stockent deux tableaux d'indices parallèles, et
left_indicesright_indices, à la place du tableaumodesunique utilisé par l'opérateur de Majorana. Étant donné qu'un générateur est toujours identifié par exactement une paire(left, right), les deux tableaux ont nécessairement la même longueur et les constructeurs prennent donc un seul argumentnum_indicesqui les englobe tous les deux. -
Ajout de et
qf_edge_op_canonical_order()qf_transfer_op_canonical_order(), ainsi que des fonctions de commutateur, d'anticommutateur et de double commutateur pour les deux nouveaux types d'opérateurs (qf_edge_op_commutator()et leurs dérivés). -
Ajout de fonctions API C pour les mappeurs « edge-vertex » et « transfer-vertex » :
qf_edge_vertex_to_fermion(),qf_edge_vertex_to_majorana(),qf_transfer_vertex_to_fermion(),qf_transfer_vertex_to_majorana(), etqf_transfer_vertex_to_edge_vertex(). -
Ajouté à
scaled_addl'API C pour les quatre représentations d'opérateurs :qf_ferm_op_scaled_add(),qf_maj_op_scaled_add(),qf_edge_op_scaled_add()etqf_transfer_op_scaled_add(). Chacune renvoieleft + factor * right, en intégrant la mise à l'échelle dans l'addition, de sorte qu'aucune copie mise à l'échelle de l'opérande de droite ne soit créée au cours du calcul.C'est également ainsi que l'API C exprime la soustraction, puisqu'un facteur de inverse
-1le signe des coefficients qui suivent :QkComplex64 minus_one = {-1.0, 0.0}; QfFermionOperator *difference = qf_ferm_op_scaled_add(left, right, &minus_one);Quant à
qf_ferm_op_add(), les termes de l'opérande de droite sont ajoutés à ceux de gauche plutôt que combinés avec eux; le résultat ne comporte donc aucun indice de groupe. Appelez la fonctionsimplifycorrespondante pour regrouper les termes identiques. -
QfFCIDumpexpose désormais les intégrales électroniques qu'il a analysées, auxquelles on ne pouvait auparavant accéder qu'en convertissant l'intégralité de la structure de données en un fichierQfFermionOperator. Les nouveaux accesseursqf_fcidump_get_one_body_tril_a(),qf_fcidump_get_one_body_tril_b(),qf_fcidump_get_two_body_tril_aa(), etqf_fcidump_get_two_body_tril_ab()utilisentqf_fcidump_get_two_body_tril_bb()la mémoire tampon interne, en transmettant un pointeur et une longueur via des paramètres de sortie :double *integrals; uint64_t len; qf_fcidump_get_one_body_tril_a(fcidump, &integrals, &len);Le pointeur renvoyé ne doit pas être libéré et ne reste valide que jusqu'à ce que soit
QfFCIDumplibéré. Protégez les trois getters « beta-spin » à l'aide du nouveauqf_fcidump_is_unrestricted(), etqf_fcidump_constant()à l'aide du nouveauqf_fcidump_has_constant(); l'appel de l'un ou l'autre sans sa protection provoque une panique. Reportez-vous à la documentation de l'en-tête pour connaître la formule d'indexation de chaque tableau; notez en particulier queqf_fcidump_get_two_body_tril_ab()est structuré différemment des deux autres tableaux à deux corps, puisqu'il contient la(npair, npair)matrice complète car il n'est symétrique que par 4. -
Ajout de et
qf_ferm_op_groups_are_hermitian()qf_ferm_op_groups_have_uniform_coeffs(), qui indiquent pour chaque groupe si l’opérateur formé par ses termes est hermitien et si ses coefficients sont numériquement égaux, ainsi que les fonctions équivalentes pour les types d’opérateurs de Majorana, de bord-sommet et de transfert-sommet. -
Ajout de la fonction
qf_ferm_op_group_order(), qui renvoie une copie d'un opérateur dont les termes sont classés par indice de groupe, ainsi que les fonctions équivalentes pour les types d'opérateurs Majorana (qf_maj_op_group_order()), arête-sommet (qf_edge_op_group_order()) et transfert-sommet (qf_transfer_op_group_order()). Chaque groupe forme une suite contiguë de termes, ce qui permet d'effectuer une rechercheqf_ferm_op_split_out_groups()binaire sur les limites du groupe plutôt que de parcourir chaque terme. -
Des mappeurs Jordan-Wigner directs ont été ajoutés pour les trois structures de données d'opérateurs restantes, en plus des mappeurs existants
qf_ferm_op_jordan_wigner():qf_maj_op_jordan_wigner(),qf_edge_op_jordan_wigner()etqf_transfer_op_jordan_wigner().
Python Notes relatives à la mise à jour de l'API
-
Les
qiskit_fermions.linalg.linear_operatorfonctionsqiskit_fermions.linalg.apply_unitaryet ont été supprimées. Il s'agissait d'enveloppes légères autour des méthodes de protocole_linear_operator_et_apply_unitary_, qui faisaient double emploi avec etffsim.apply_unitary()ffsim.linear_operator(). Appelez plutôt les fonctions de ffsim ou, si ffsim n'est pas installé, la méthode « protocol » directement sur l'objet :# before from qiskit_fermions.linalg import apply_unitary, linear_operator vec = apply_unitary(vec, gate, norb, nelec) linop = linear_operator(operator, norb, nelec) # after (with ffsim) vec = ffsim.apply_unitary(vec, gate, norb=norb, nelec=nelec) linop = ffsim.linear_operator(operator, norb=norb, nelec=nelec) # after (without ffsim) vec = gate._apply_unitary_(vec, norb, nelec, copy=True) linop = operator._linear_operator_(norb, nelec)Les méthodes elles-mêmes restent inchangées.
-
Les protocoles
qiskit_fermions.protocols.SupportsLinearOperatoretqiskit_fermions.protocols.SupportsApplyUnitaryont été supprimés pour la même raison : ils reprenaient des contrats déjà définis par ffsim. Utilisez les protocoles propres à ffsim, qu’ils ont reproduits à l’identique :qiskit_fermions.protocols.SupportsApplyUnitarydevientffsim.SupportsApplyUnitaryqiskit_fermions.protocols.SupportsLinearOperatordevientffsim.SupportsLinearOperator
Les méthodes du protocole (
_apply_unitary_et_linear_operator_) restent inchangées; les objets de ce paquet qui les implémentaient continuent donc de le faire; seule la redéfinition locale de l'interface a été supprimée.SupportsApplyUnitaryPlacedreste, puisqu’il s’étend avecffsim.SupportsApplyUnitaryun placement de mode et n’a pas d’équivalent dans ffsim. -
from_file()lance désormais une exception « Python » interceptable lorsqu'il ne parvient pas à analyser un fichier, au lieu de propager une panique Rust. Un chemin d'accès qui ne peut être ouvert ou lu déclenche une exceptionOSError, tandis qu'un fichier qui ne respecte pas le format FCIDump (liste des noms d'en-tête manquante,NELECchamp ouNORBmanquant, ou champMS2mal formé) déclenche une exceptionValueError. Un FCIDump contenant une valeur d'énergie MO, qui n'est pas encore prise en charge, génère également une erreurValueError. -
La méthode
group_weightsa été supprimée de toutes les classes d'opérateurs et du protocoleOperatorTrait. Elle est remplacée pargroup_coeff_means(), une fonction libre du moduleqiskit_fermions.operators.terms.grouping, dont le comportement reste inchangé :# before weights = op.group_weights() # after from qiskit_fermions.operators.terms.grouping import group_coeff_means weights = group_coeff_means(op) -
L'affectation d'un tableau
groupsdont la longueur diffère du nombre de termes de l'opérateur génère désormais une exceptionValueErrorau lieu d'être acceptée sans message d'erreur. L'affectationNonepour effacer les indices du groupe n'est pas affectée. -
MapperFnEvolutionSynthesisne simplifie plus l'opérateur renvoyé par sonmapper_fn. La simplification classe les termes de Pauli selon un ordre canonique, tandis qu'une formule de produit les synthétise dans l'ordre où elle les reçoit; par conséquent, le comportement précédent ignorait l'ordre choisi par le mappeur.Cela modifie le circuit synthétisé pour les pipelines existants : le nombre de portes reste inchangé, mais l'ordre dans lequel les rotations sont émises (et donc la profondeur à deux qubits) peut varier. L'évolution que l'on estime reste inchangée.
Notez également que l'ordre des termes généré par un mappeur n'est pas nécessairement stable d'une exécution à l'autre, car les opérateurs du noyau Rust ne préservent pas l'ordre dans lequel leurs termes ont été ajoutés. Enveloppez le mappeur dans le nouvel élément
simplify()pour rétablir le comportement précédent et définir un ordre canonique :MapperFnEvolutionSynthesis(simplify(jordan_wigner)) -
Les utilitaires de double factorisation ont été supprimés de
qiskit_fermions.linalg:double_factorized_t2,double_factorized_t2_alpha_beta,reconstruct_t2, etreconstruct_t2_alpha_betadouble_factorized_2body. Ces variantest2existaient pour servirUCJ.from_t_amplitudes, qui se trouve désormais dans ffsim, et n’avaientdouble_factorized_2bodyaucun appelant. Utilisezffsim.linalg.double_factorized()plutôtffsim.linalg.double_factorized_t2(), etffsim.linalg.double_factorized_t2_alpha_beta(); cette dernière est un sur-ensemble de la routine à deux corps supprimée et propose en outre une décomposition optimisée (optimize=True). -
La simulation nécessite désormais ffsim. Le noyau FCI (Full Configuration Interaction) natif de Rust qui servait auparavant de support
_linear_operator_a été supprimé; la méthode est désormais implémentée en convertissant unFermionOperatoren un etffsim.FermionOperatoren déléguant àffsim.linear_operator().La simulation est au cœur des activités de ffsim : ce package se concentre sur les mappeurs fermioniques, ainsi que sur le circuit indépendant du mappeur et sa transpilation. Comme ffsim dépend d' PySCF,, qui ne prend pas en charge Windows, la simulation n'est donc pas disponible sous Windows; le reste du progiciel (construction des opérateurs, leur mappage et la transcompilation des circuits résultants) n'est pas affecté sur aucune plateforme. Les utilisateurs de Windows qui ont besoin de simuler un environnement peuvent le faire via le sous-système Windows pour l' Linux (WSL).
L'appel d'un point d'entrée de simulation sans que ffsim soit installé provoque désormais une erreur
MissingOptionalLibraryError. -
Le module
qiskit_fermions.linalg.fcia été supprimé en même temps que le noyau, y compris saFciLinearOperatorclasse ainsi que les fonctionsoccupation_axis_masketslater_determinant_statevector. ffsim fournit les équivalents :ffsim.slater_determinant()etffsim.addresses_to_strings()respectivement. -
OrbitalRotationne revient plus à un chemin « générateur plus exponentiel » lorsque ffsim est absent; il délègue toujours àffsim.apply_orbital_rotation(). Les résultats restent inchangés lorsque ffsim est installé. -
La
UCCporte est désormais construite à partir d’un des opérateurs UCCSD de ffsim, qui constitue son seul argument de constructeur. La variante de spin et le nombre de modes sont déterminés à partir de cet opérateur; l'argumentvariantet l'énumérationUCC.Variantont donc disparu :# before ansatz = UCC("restricted", t1, t2) # after ansatz = UCC(ffsim.UCCSDOpRestrictedReal(t1=t1, t2=t2))En conséquence,
UCC.from_t_amplitudes,UCC.num_parameters,UCC.from_parametersetUCC.to_parametersont été supprimés, tout comme laspinlessvariante et la paramétrisationantisymmetric« opt-in », qui n'ont pas d'équivalent dans ffsim. Les opérateurs de ffsim fournissentn_params(),from_parameters()etto_parameters()avec des conventions identiques. Pour construire un ansatz en dehors de cette famille (un ansatz sans spin ou une « » antisymétrisée), construisez directement un opérateur de cluster surEvolutionvotre propre espace, ce qui vous permet également de contrôler l'ordre de Trotter de ses termes.L'opérateur encapsulé est disponible sous la forme
UCC.uccsd_op, ses amplitudes sont donc accessibles sous la forme etgate.uccsd_op.t1gate.uccsd_op.t2; la porte ne les reflète plus en tant qu'attributs qui lui sont propres.UCC.cluster_operator()reste inchangé. Un transmisfinal_orbital_rotationpar l'opérateur ffsim est désormais ajouté en tant que fermetureOrbitalRotation.Étant donné que ffsim est désormais le type d'entrée de la porte plutôt qu'un accélérateur optionnel, la construction d'un
UCCnécessite l'ajoutffsimde (pip install "qiskit-fermions[ffsim]") et génère une exception si celui-ci estMissingOptionalLibraryErroromis. ffsim n'est pas compatible avec Windows; cette fonctionnalité n'y est donc pas disponible. Utilisez WSL. -
La
UCJporte est désormais construite à partir d'un des opérateurs UCJ de ffsim, qui constitue son seul argument de constructeur. La variante de spin, le nombre d'orbitales spatiales et le nombre de modes sont déterminés à partir de cet opérateur; l'argumentvariantet l'énumérationUCJ.Variantont donc disparu :# before ansatz = UCJ.from_t_amplitudes(nelec, t2, t1=t1, n_reps=2) ansatz = UCJ("balanced", diag_coulomb_mats, orbital_rotations) # after ansatz = UCJ(ffsim.UCJOpSpinBalanced.from_t_amplitudes(t2, t1=t1, n_reps=2)) ansatz = UCJ(ffsim.UCJOpSpinBalanced(diag_coulomb_mats, orbital_rotations))En conséquence,
UCJ.from_t_amplitudes,UCJ.num_parameters,UCJ.from_parametersetUCJ.to_parametersont été supprimés : les opérateurs de ffsim fournissentfrom_t_amplitudes(),n_params(),from_parameters()etto_parameters()avec des conventions identiques, et proposent en outre la factorisation double compressée (optimize=True) ainsi quefrom_cisd_vec(), que ce package n’a jamais implémentés. L'opérateur encapsulé est disponible sous la formeUCJ.ucj_op, de sorte que ses tenseurs sont accessibles sous la forme etgate.ucj_op.diag_coulomb_matsainsi de suite; la porte ne les reflète plus en tant qu'attributs qui lui sont propres.Étant donné que ffsim est désormais le type d'entrée de la porte plutôt qu'un accélérateur optionnel, la construction d'un
UCJnécessite l'ajoutffsimde (pip install "qiskit-fermions[ffsim]") et génère une exception si celui-ci estMissingOptionalLibraryErroromis. ffsim n'est pas compatible avec Windows; cette fonctionnalité n'y est donc pas disponible. Utilisez WSL. -
La désalémantisation d'un opérateur dont les indices de groupe alémantis ne commencent pas par « 1 » pour chaque terme génère désormais une exception
ValueErrorau lieu de produire un opérateur dont les indices de groupe ne correspondent plus à ses termes. Cette fonctionnalité n'est accessible que pour les charges utiles créées manuellement ou celles générées par une version incompatible; les objets « pickles » créés par cette version sont conservés tels quels lors de l'aller-retour.
Notes relatives à la mise à jour de l'API C
-
qf_fcidump_from_file()renvoie désormais un etQfExitCodetransmet le analysé viaQfFCIDumpun nouveau paramètre de sortie, conformément à la convention déjà utilisée par les fonctions de mappage :QfFCIDump *fcidump = NULL; QfExitCode exit = qf_fcidump_from_file("molecule.fcidump", &fcidump);Elle renvoie une erreur
QfExitCode_ValueErrorlorsqu'un fichier ne respecte pas le format FCIDump (liste de noms d'en-têtes manquante,NELECchamp ouNORBmanquant, champMS2mal formé, ou valeur d'énergie MO non prise en charge), sans modifier le paramètre de sortie. Cela remplace la signature précédente, qui renvoyait directement le pointeur et ne permettait pas de signaler un échec (un fichier impossible à analyser déclenchait une panique Rust au-delà de la frontière FFI, ce qui constitue un comportement indéfini). -
qf_ferm_op_group_weightset ses équivalents par type ont été renommés etqf_ferm_op_group_coeff_means()autres. Leur comportement reste inchangé. -
qf_ferm_op_set_groups()et ses équivalents par type renvoient désormais unQfExitCodeplutôt qu’unvoid. Le passage d'un tableau de groupes dont la longueur diffère du nombre de termes de l'opérateur renvoieQfExitCode_ValueErrorau lieu d'être accepté sans message d'erreur; le passage deNULLpour effacer les indices des groupes n'est pas affecté.
Modifications apportées au système de compilation
-
La dépendance facultative
simulationa été renomméeffsim, de sorte que devientpip install "qiskit-fermions[simulation]"pip install "qiskit-fermions[ffsim]". Le paramètre « extra » désigne la dépendance qu'il installe, et non une fonctionnalité. L'installation n'estqiskit-fermions[all]pas affectée. -
La dépendance facultative
optimizationa été renomméepyomo, de sorte que devientpip install "qiskit-fermions[optimization]"pip install "qiskit-fermions[pyomo]". Ce nouveau nom fait référence à la dépendance qu'il installe plutôt qu'à une fonctionnalité, ce que l'ancien nom laissait entendre à tort : Pyomo est un langage de modélisation; il génère donc le programme mixte-entier sous-jacent à etRelabelModesbuild_excitation_span_minimization_model()mais ne peut pas le résoudre. Le choix d'un solveur constitue une étape distincte et réfléchie (RelabelModesqui consiste à passer en revue ses argumentssolver, sans que ce package n'impose de choix particulier), et le fait de nommer ce module d'extension d'après Pyomo permet de mettre en évidence cette distinction dès l'installation. L'installation n'estqiskit-fermions[all]pas affectée.
Corrections des erreurs
-
Correction de la consommation excessive de mémoire de et
fermion_jordan_wigner()deqf_ferm_op_jordan_wigner()(et donc dejordan_wigner()lorsqu'elle est appliquée à unFermionOperator). Le mappeur a cumulé les termes mappés à l'aide d'une opération d'addition qui concatène les termes de Pauli en double au lieu de les fusionner; ainsi, l'observable intermédiaire a augmenté proportionnellement au nombre de termes de Pauli émis, et non au nombre de termes distincts. La représentation d'un grand hamiltonien pourrait donc épuiser la mémoire disponible, même si le résultat simplifié était d'un ordre de grandeur inférieur. Les accumulateurs sont désormais canonisés dès lors qu'ils ont atteint une taille supérieure d'un certain facteur à celle qu'ils avaient après leur fusion, ce qui limite la mémoire qu'ils occupent en fonction de l'opérateur qu'ils représentent effectivement.La mémoire maximale continue d'augmenter avec le nombre de threads de travail : les termes sont transmis au premier thread disponible plutôt que d'être répartis en fonction des chaînes de Pauli qu'ils produisent; ainsi, chaque accumulateur finit par contenir à peu près une copie complète de l'opérateur mappé. Ce qui a changé, c'est le facteur associé à chacun d'entre eux : auparavant proportionnel au nombre de termes de Pauli émis, il est désormais proportionnel au nombre de termes distincts. La réduction du nombre de threads via la variable d'environnement
RAYON_NUM_THREADS« rayon » permet donc de diminuer le pic, de manière à peu près proportionnelle, et constitue la solution à privilégier lorsque la mémoire est limitée.L'opérateur mappé reste inchangé : la canonisation ne fait que fusionner les termes en double, de sorte que le résultat représente exactement la même observable qu'auparavant. Il n'est toutefois plus garanti qu'il soit entièrement simplifié, et le nombre de termes qu'il contient peut varier en fonction du nombre de fils. Appelez
simplify()(qk_obs_canonicalizeen C) si vous souhaitez que tous les doublons soient fusionnés. -
Correction : suppression
QDriftTrotterizationde la méthodeEvolution.synthesisdes portes qu’il « trotterise », et émission de portes échantillonnées pouvant être décomposées en facteurs non unitaires. Les portes échantillonnées reprennent désormais la méthode de synthèse de la porte qu'elles remplacent et sont marquées d'unEvolution.atomic. -
Correction d'un problème : le système recherchait
RelabelModesune permutation pour un hamiltonien erroné alors que les portesEvolutiond'un circuit avaient déjà été décomposées. Une porte « narrowsFermionicEvolutionSynthesis» chaque facteur qu'elle émet sur le support de ce facteur; ainsi, l'opérateur du facteur porte des indices de mode propres à la porte, tandis que sa position dans le registre est donnée par les arguments de qubits du nœud. L'optimisation automatique a pris en compte uniquement l'opérateur, qui regroupait ces indices locaux : chaque facteur bimodal restreint devenait l'excitation,(0, 1)quels que soient les modes auxquels il était effectivement couplé, et un couplage à longue portée disparaissait complètement du modèle.La permutation ainsi obtenue était valide, mais optimisée pour un hamiltonien qui n'était pas celui du circuit; aucune exception n'a été levée, car les indices locaux sont eux-mêmes des indices de mode valides. Les circuits dont l'évolution a permis d'atteindre le col dans son intégralité n'ont pas été affectés. Si vous vous êtes basé sur la permutation d'un circuit décomposé, relancez l'itération : elle pourrait désormais renvoyer une permutation différente (et meilleure).
-
Correction d'une décomposition de la porte
Evolutionqui ne s'arrêtait jamais. L'évolution d'un terme à opérateur unique se décomposait en un terme unique identiqueEvolution; par conséquent, le fait de répéter le développement ne menait à rien. Cela a notamment entraînéinverse()l'apparition d'unRecursionError, car la définition de la porte est traitée de manière récursive.Un facteur émis par est
FermionicEvolutionSynthesisdésormais marquéEvolution.atomicet est conservé à sa place pardecompose()au lieu d'être à nouveau développé. La décomposition répétéeEvolutiond'un aboutit donc à un point fixe. -
Correction d'un problème entraînant la génération
decompose()de facteurs non unitaires lorsque cette opération était appliquée plus de deux fois à un opérateur comportant des groupes. Chaque groupe a été divisé davantage, terme par terme, mais un terme pris isolément n'est généralement pas hermitien, même lorsque le groupe qui le contient l'est – les paires conjuguées d'un générateur de clusterUCCen sont un exemple révélateur. L'exponentielle d'un tel facteur n'est pas unitaire.L'étape de conversion des fermions en qubits a écarté ces facteurs grâce à une correction
ValueErrorportant sur les coefficients complexes, mais la simulation par vecteur d'état les a appliqués sans problème et a renvoyé un état non normalisé. Comme un groupe est désormais atomique, la scission qui l'a créé ne se produit plus. -
Evolution.inverse()est désormais implémenté directement; il renvoie unEvolutionqui fait évoluer le même opérateur pour le temps inversé et préserve la méthodesynthesisde la porte. Auparavant, l'implémentation héritée effectuait une récursivité sur la définition de la porte et renvoyait un simpleGate, en écartant à la fois l'opérateur et la méthode de synthèse.
Améliorations des performances
-
Amélioration du conditionnement numérique de la simulation par vecteur d'état pour les portes
OrbitalRotationetEvolutiongrâce à l'utilisation de la trace exacte de l'opérateur à secteur fixe dans la routine d'exponential SciPy’s. -
fermion_jordan_wigner()ne copie plus l'opérateur qui lui est fourni. La liaisonPythonprenait son argument par valeur, ce qui entraînait la duplication de chaque tampon de terme de l'entrée, dans le seulFermionOperatorbut de le lire.
0.1.0
Prélude
Il s'agit de la première version de Qiskit Fermions, une extension de Qiskit proposant des outils destinés aux systèmes fermioniques. Il fournit des structures de données pour les opérateurs, un cadre et une bibliothèque permettant de convertir ces opérateurs en forme de qubits (mappeurs), ainsi qu'un cadre et une bibliothèque permettant de synthétiser les circuits quantiques correspondants. Conformément à la philosophie de conception de Qiskit, le cœur de ce package est écrit en Rust et accessible via des liaisons officielles pour Python et le langage C.