Skip to main content
IBM Quantum Platform

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 Evolution est décomposée dans l'espace fermionique est désormais configurable grâce à son nouvel argument synthesis , accessible uniquement par mot-clé, qui reprend l'argument synthesis de la fonction de Qiskit PauliEvolutionGate. Les méthodes disponibles se trouvent dans le nouveau qiskit_fermions.circuit.library.synthesis module, qui fournit l'interface FermionicEvolutionSynthesis et son implémentation FermionicLieTrotter de premier ordre.

    Cette étape de conversion fermion-fermion est facultative : une Evolution porte 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 mutuelle groups) 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, contrairement EvolutionSynthesis à celles de Qiskit.

    La valeur par défaut reste inchangée : en laissant synthesis sur, on utilise None FermionicLieTrotter, ce qui reproduit exactement la décomposition effectuée Evolution auparavant par, de sorte que les circuits existants sont synthétisés de manière identique.

  • Les poids d'échantillonnage de QDriftTrotterization peuvent désormais être fournis via son nouvel argument weights de type « mot-clé uniquement », au lieu d'être calculés à chaque appel à l'opérateur évolué. Si l'on conserve la valeur None par 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 avec group_coeff_means() à la place, en parallèle de group_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’ hjh_j de l’amplitude de la décomposition d’ qDRIFT H=∑jhjHjH = \sum_j h_j H_j, dans laquelle le signe d’un coefficient appartient à HjH_j 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 sur gamma * 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 porte Evolution à laquelle il est appliqué, et un circuit comportant plus d'une porte de ce type est rejeté; le fait de laisser ce paramètre weights non défini permet de conserver un tel circuit, puisque chaque porte détermine alors la sienne.

  • FCIDump expose 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 fichier FermionOperator. 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(), et get_two_body_tril_ab() get_two_body_tril_bb(). Les tableaux « beta-spin » fournissent des informations sur None un fichier soumis à des restrictions de spin; le nouvel attribut is_unrestricted « beta-spin » fournit des informations sur les trois à la fois. Le nouvel attribut constant indique l'énergie constante (de répulsion nucléaire), ou None lorsque 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 porte Evolution dans l'espace fermionique. Lorsque chaque facteur est FermionicLieTrotter appliqué 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 son reps argument 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 groups qui 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=2 on a obtenu reps=4 une 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.

    FermionicLieTrotter C'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'argument reps qui lui faisait défaut auparavant. Les deux sont interchangeables à valeur égale reps.

    Il convient de noter qu'un ordre supérieur offre une plus grande précision grâce à sa profondeur : une formule d'ordrek émet environ 5**((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 porte Evolution d'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 et UCC UCJ, 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édicat filter facultatif 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'un UCJ.

    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=False ne sélectionner que la méthode et laisser le développement à un autre élément, tel que celui de Qiskit Decompose; 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 sans Evolution.synthesis jamais être lue.

  • Ce module qiskit_fermions.operators.terms.grouping aborde 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 search

    Il 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(), et edge_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 seul FermionOperator, 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 exception TypeError.

  • Cette méthode is_hermitian() fait désormais partie du protocole OperatorTrait . 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 et OperatorTrait non 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 True est toujours fiable, tandis qu’un résultat False est prudent pour les types d’opérateurs dont la forme normale n’est pas une véritable forme canonique. Voir is_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 MapperFnEvolutionSynthesis dé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ée groups à 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))
  • QDriftTrotterization Enregistre désormais le nombre de tirages que son filter_trivial mode a écartés. Lorsqu'au moins une Evolution porte était effectivement filtrée, les « reports metadata » du circuit renvoyé et filter_trivial.discarded filter_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().

  • Evolution peut désormais être simulé pour n'importe quel type d'opérateur de ce package, et pas seulement FermionOperator. Un opérateur d'un autre type est converti via son image fermionique (le SupportsFermionOperator protocole) avant d'être simulé; ainsi, l'évolution d'un MajoranaOperator, ou EdgeVertexOperator TransferVertexOperator ne déclenche plus NotImplementedError.

  • FermionOperator est désormais implémentée ffsim.SupportsTrace via une nouvelle _trace_() méthode, ce qui ffsim.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 long scipy.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éserve QfFermionOperator le nombre de particules au sein de chaque bloc de modes. Elle vient compléter la fonctionnalité existante qf_ferm_op_conserves_particle_number(), qui ne vérifie que le total. Dans le cas d'une configuration spin-orbitale d'orbitales spatiales norb , 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 NULL d'un nombre de blocs 0 considère tous les modes comme un seul bloc, ce qui revient à appeler qf_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() et qf_ferm_op_get_support(), ainsi que leurs équivalents qf_transfer_op qf_maj_op, qf_edge_op et.

    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_support pour 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 Python renvoie 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_inplace et mul_inplace, par exemple qf_ferm_op_add_inplace(), qf_ferm_op_scaled_add_inplace() et qf_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 fait qf_ferm_op_add() . mul_inplace cela 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, QfEdgeVertexOperator et QfTransferVertexOperator, 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_indices right_indices, à la place du tableau modes unique 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 argument num_indices qui 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(), et qf_transfer_vertex_to_edge_vertex().

  • Ajouté à scaled_add l'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() et qf_transfer_op_scaled_add(). Chacune renvoie left + 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 -1 le 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 fonction simplify correspondante pour regrouper les termes identiques.

  • QfFCIDump expose 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 fichier QfFermionOperator. Les nouveaux accesseurs qf_fcidump_get_one_body_tril_a(), qf_fcidump_get_one_body_tril_b(), qf_fcidump_get_two_body_tril_aa(), et qf_fcidump_get_two_body_tril_ab() utilisent qf_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 QfFCIDump libéré. Protégez les trois getters « beta-spin » à l'aide du nouveau qf_fcidump_is_unrestricted(), et qf_fcidump_constant() à l'aide du nouveau qf_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 que qf_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 recherche qf_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() et qf_transfer_op_jordan_wigner().

Python Notes relatives à la mise à jour de l'API

  • Les qiskit_fermions.linalg.linear_operator fonctions qiskit_fermions.linalg.apply_unitary et 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 et ffsim.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.SupportsLinearOperator et qiskit_fermions.protocols.SupportsApplyUnitary ont é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 :

    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. SupportsApplyUnitaryPlaced reste, puisqu’il s’étend avec ffsim.SupportsApplyUnitary un 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 exception OSError, tandis qu'un fichier qui ne respecte pas le format FCIDump (liste des noms d'en-tête manquante, NELEC champ ou NORB manquant, ou champ MS2 mal formé) déclenche une exception ValueError. Un FCIDump contenant une valeur d'énergie MO, qui n'est pas encore prise en charge, génère également une erreur ValueError.

  • La méthode group_weights a été supprimée de toutes les classes d'opérateurs et du protocole OperatorTrait . Elle est remplacée par group_coeff_means(), une fonction libre du module qiskit_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 groups dont la longueur diffère du nombre de termes de l'opérateur génère désormais une exception ValueError au lieu d'être acceptée sans message d'erreur. L'affectation None pour effacer les indices du groupe n'est pas affectée.

  • MapperFnEvolutionSynthesis ne simplifie plus l'opérateur renvoyé par son mapper_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, et reconstruct_t2_alpha_beta double_factorized_2body. Ces variantes t2 existaient pour servir UCJ.from_t_amplitudes, qui se trouve désormais dans ffsim, et n’avaient double_factorized_2body aucun appelant. Utilisez ffsim.linalg.double_factorized() plutôt ffsim.linalg.double_factorized_t2(), et ffsim.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 un FermionOperator en un et ffsim.FermionOperator en 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.fci a été supprimé en même temps que le noyau, y compris sa FciLinearOperator classe ainsi que les fonctions occupation_axis_mask et slater_determinant_statevector . ffsim fournit les équivalents : ffsim.slater_determinant() et ffsim.addresses_to_strings() respectivement.

  • OrbitalRotation ne 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 UCC porte 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'argument variant et l'énumération UCC.Variant ont 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_parameters et UCC.to_parameters ont été supprimés, tout comme la spinless variante et la paramétrisation antisymmetric « opt-in », qui n'ont pas d'équivalent dans ffsim. Les opérateurs de ffsim fournissent n_params(), from_parameters() et to_parameters() avec des conventions identiques. Pour construire un ansatz en dehors de cette famille (un ansatz sans spin ou une « t2t_2 » antisymétrisée), construisez directement un opérateur de cluster sur Evolution votre 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 et gate.uccsd_op.t1 gate.uccsd_op.t2; la porte ne les reflète plus en tant qu'attributs qui lui sont propres. UCC.cluster_operator() reste inchangé. Un transmis final_orbital_rotation par l'opérateur ffsim est désormais ajouté en tant que fermeture OrbitalRotation.

    É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 UCC nécessite l'ajout ffsim de (pip install "qiskit-fermions[ffsim]") et génère une exception si celui-ci est MissingOptionalLibraryError omis. ffsim n'est pas compatible avec Windows; cette fonctionnalité n'y est donc pas disponible. Utilisez WSL.

  • La UCJ porte 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'argument variant et l'énumération UCJ.Variant ont 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_parameters et UCJ.to_parameters ont été supprimés : les opérateurs de ffsim fournissent from_t_amplitudes(), n_params(), from_parameters() et to_parameters() avec des conventions identiques, et proposent en outre la factorisation double compressée (optimize=True) ainsi que from_cisd_vec(), que ce package n’a jamais implémentés. L'opérateur encapsulé est disponible sous la forme UCJ.ucj_op, de sorte que ses tenseurs sont accessibles sous la forme et gate.ucj_op.diag_coulomb_mats ainsi 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 UCJ nécessite l'ajout ffsim de (pip install "qiskit-fermions[ffsim]") et génère une exception si celui-ci est MissingOptionalLibraryError omis. 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 ValueError au 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 et QfExitCode transmet le analysé via QfFCIDump un 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_ValueError lorsqu'un fichier ne respecte pas le format FCIDump (liste de noms d'en-têtes manquante, NELEC champ ou NORB manquant, champ MS2 mal 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_weights et ses équivalents par type ont été renommés et qf_ferm_op_group_coeff_means() autres. Leur comportement reste inchangé.

  • qf_ferm_op_set_groups() et ses équivalents par type renvoient désormais un QfExitCode plutôt qu’un void. Le passage d'un tableau de groupes dont la longueur diffère du nombre de termes de l'opérateur renvoie QfExitCode_ValueError au lieu d'être accepté sans message d'erreur; le passage de NULL pour effacer les indices des groupes n'est pas affecté.

Modifications apportées au système de compilation

  • La dépendance facultative simulation a été renommée ffsim, de sorte que devient pip 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'est qiskit-fermions[all] pas affectée.

  • La dépendance facultative optimization a été renommée pyomo, de sorte que devient pip 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 à et RelabelModes build_excitation_span_minimization_model() mais ne peut pas le résoudre. Le choix d'un solveur constitue une étape distincte et réfléchie (RelabelModes qui consiste à passer en revue ses arguments solver , 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'est qiskit-fermions[all] pas affectée.

Corrections des erreurs

  • Correction de la consommation excessive de mémoire de et fermion_jordan_wigner() de qf_ferm_op_jordan_wigner() (et donc de jordan_wigner() lorsqu'elle est appliquée à un FermionOperator). 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_canonicalize en C) si vous souhaitez que tous les doublons soient fusionnés.

  • Correction : suppression QDriftTrotterization de la méthode Evolution.synthesis des 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'un Evolution.atomic.

  • Correction d'un problème : le système recherchait RelabelModes une permutation pour un hamiltonien erroné alors que les portes Evolution d'un circuit avaient déjà été décomposées. Une porte « narrows FermionicEvolutionSynthesis » 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 Evolution qui ne s'arrêtait jamais. L'évolution d'un terme à opérateur unique se décomposait en un terme unique identique Evolution; par conséquent, le fait de répéter le développement ne menait à rien. Cela a notamment entraîné inverse() l'apparition d'un RecursionError, car la définition de la porte est traitée de manière récursive.

    Un facteur émis par est FermionicEvolutionSynthesis désormais marqué Evolution.atomic et est conservé à sa place par decompose() au lieu d'être à nouveau développé. La décomposition répétée Evolution d'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 cluster UCC en 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 ValueError portant 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 un Evolution qui fait évoluer le même opérateur pour le temps inversé et préserve la méthode synthesis de la porte. Auparavant, l'implémentation héritée effectuait une récursivité sur la définition de la porte et renvoyait un simple Gate, 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 OrbitalRotation et Evolution grâ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 liaison Python prenait son argument par valeur, ce qui entraînait la duplication de chaque tampon de terme de l'entrée, dans le seul FermionOperator but 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.

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