Zum Hauptinhalt springen
Dieses Projekt befindet sich in der Beta-Phase. Einen Fehler gefunden? GitHub Issue erstellen. Frage oder Idee? In Discussions fragen — wir freuen uns über dein Feedback!
Wie ist die Übersetzungsqualität auf dieser Seite?

Wie dieses Paket zu ffsim steht

Wichtig

Die Konzepte in diesem Leitfaden sind nur in der Python-API verfügbar.

ffsim und dieses Paket teilen die Arbeit auf, statt sich zu überschneiden, entlang einer einzigen Linie:

ffsim ist zuständig fürqiskit-fermions ist zuständig für
Die übergeordneten Ansatz-Operatoren (UCJOpSpinBalanced, UCCSDOpRestrictedReal und ihre Verwandten), erstellt aus Coupled-Cluster-Amplituden oder einem ParametervektorDen fermionischen Circuit (FermionicCircuit), zu dem diese Operatoren werden, und seine Transpilation in einen Qubit-Circuit unter einer beliebigen Fermion-zu-Qubit-Kodierung
Schnelle Simulation in einem kompakten Raum fester TeilchenzahlDas Mapper-Framework und die Operator-Datenstrukturen, aus denen der Circuit erstellt wird

Die praktische Folge gilt in beide Richtungen. Die Ansatz-Mathematik liegt in ffsim, sodass UCJ und UCC einen ffsim-Operator direkt als Eingabe nehmen. Umgekehrt sind die eigenen Qiskit-Gates von ffsim spezifisch für die Jordan-Wigner-Transformation, sodass der Gewinn, einen seiner Ansatz-Operatoren hierher zu bringen, darin besteht, ihn über eine andere Kodierung abzusenken.

Die Simulation ist der Punkt, an dem sich beide treffen, und dieser Leitfaden erklärt, wie. ffsim ist das Simulations-Backend dieses Pakets, erreicht über seine eigenen Protokolle statt über einen Wrapper: Der SKQD-Leitfaden ruft ffsim.apply_unitary(), ffsim.linear_operator() und ffsim.sample_state_vector() direkt auf FermionicCircuit-Objekten und FermionOperator-Objekten ohne Konvertierungsschritt auf.

An die Simulationsschnittstelle von ffsim andocken​

ffsim ist ein Hochleistungssimulator für fermionische Quantencircuits, der Teilchenzahl- und Spin-Z-Erhaltung ausnutzt, um Zustandsvektoren kompakter darzustellen als ein generischer 2n2^n-dimensionaler Qubit-Zustandsvektor. Er definiert Protokolle, die jedes Objekt implementieren kann, um an seiner Simulationsmaschinerie teilzunehmen (siehe qiskit_fermions.protocols für die eigenen Protokolle dieses Pakets, die demselben Entwurf folgen):

Dieses Paket implementiert diese Schnittstelle: Jedes fermionische Gate in qiskit_fermions.circuit.library stellt _apply_unitary_ bereit, und FermionOperator stellt _linear_operator_ und _trace_ bereit. Dadurch sind die Operatoren und Circuits dieses Pakets mit ffsim kompatibel, sodass seine Werkzeuge nativ und ohne Konvertierungsschritt mit ihnen arbeiten. ffsim.apply_unitary() kann einen FermionicCircuit von Anfang bis Ende simulieren, ffsim.linear_operator() kann einen FermionOperator diagonalisieren, und Sampling-Werkzeuge wie ffsim.sample_state_vector() (im SKQD-Leitfaden verwendet, um einen simulierten Zustandsvektor in Messzählungen umzuwandeln) funktionieren ohne Änderung.

Was _linear_operator_ zurückgibt, ist ein gewöhnlicher SciPy-LinearOperator, sodass scipy.sparse.linalg.eigsh() und verwandte Funktionen auch darauf funktionieren.

>>> import ffsim
>>> import numpy as np
>>>
>>> from qiskit_fermions.circuit import FermionicCircuit
>>> from qiskit_fermions.circuit.library import Evolution
>>> from qiskit_fermions.operators import FermionOperator, ann, cre
>>>
>>> norb, nelec = 2, (1, 1)
>>> hamiltonian = FermionOperator.from_terms([
... ([cre(0), ann(1)], 0.5),
... ([cre(1), ann(0)], 0.5),
... ])
>>>
>>> circuit = FermionicCircuit(2 * norb)
>>> circuit.append(Evolution(2 * norb, hamiltonian, time=1.0), circuit.modes)
>>>
>>> reference = ffsim.hartree_fock_state(norb, nelec)
>>> state = ffsim.apply_unitary(reference, circuit, norb=norb, nelec=nelec) # native ffsim call

Die Kopplung über die Protokolle von ffsim bringt Folgendes: Sein aktiv entwickeltes Ökosystem aus Simulations- und Sampling-Werkzeugen lässt sich unverändert auf die Circuits und Operatoren dieses Pakets anwenden, und Nutzer, die bereits mit ffsim arbeiten, können die Gates und Operatoren dieses Pakets einmischen, ohne eine weitere Simulations-API zu lernen.

ffsim ist eine optionale Abhängigkeit​

ffsim ist als optionales Extra deklariert (pip install "qiskit-fermions[ffsim]", oder transitiv durch [all]) und wird zur Laufzeit durch HAS_FFSIM abgesichert (wie oben gezeigt). Die Installation schaltet die Simulation frei: ffsim.SupportsLinearOperator und ffsim.SupportsTrace werden implementiert, indem der FermionOperator dieses Pakets in einen ffsim.FermionOperator umgewandelt wird, sodass sie ohne ffsim MissingOptionalLibraryError auslösen. Alles, was nicht simuliert (Operatoren erstellen, abbilden und die resultierenden Circuits transpilieren), braucht davon nichts.

Fermionische Simulation findet in einem Sektor fester Teilchenzahl statt​

Sowohl _apply_unitary_ als auch _linear_operator_ nehmen ein Argument nelec entgegen und stellen den Zustandsvektor über der Determinantenbasis fester Teilchenzahl für diesen (norb, nelec)-Sektor dar, entsprechend dem FCI-Raum-Aufbau von ffsim (und, transitiv, PySCF), statt über dem vollen 2num_modes2^{\text{num\_modes}}-dimensionalen Raum, den ein generischer Qubit-Simulator verwenden würde. Das ist ein viel kleinerer Raum (seine Größe ist ein Produkt von Binomialkoeffizienten statt einer Zweierpotenz), bringt aber eine harte Einschränkung mit sich. Nur Operatoren und Gates, die die Teilchenzahl erhalten (und im spinbehafteten Fall die Teilchenzahl jeder Spinsorte einzeln), lassen sich darin darstellen. Ein Operator, dessen Wirkung Amplitude in eine andere Teilchenzahl verschieben würde, hat dafür keinen Platz.

ffsim löst das, indem es einen solchen Operator rundweg ablehnt: Die Umwandlung in einen linearen Operator löst einen ValueError aus, der benennt, welcher Erhaltungssatz verletzt ist. Das ist die richtige Voreinstellung, denn die Anwendung von exp⁡(−itH)\exp(-i t H) für einen Hamiltonian HH mit einem Term, der die Teilchenzahl nicht erhält, würde sonst eine eigentlich unitäre Abbildung in eine nicht unitäre, physikalisch bedeutungslose verwandeln.

  • Evolution reicht diese Ablehnung von dem Operator weiter, den es entwickelt.

  • OrbitalRotation prüft, dass seine (eingebettete) Rotationsmatrix im spinbehafteten Fall über die Alpha/Beta-Aufteilung hinweg blockdiagonal ist, und löst einen ValueError aus, wenn sie Spinsektoren vermischt.

>>> from qiskit_fermions.circuit.library import Evolution
>>>
>>> non_conserving = FermionOperator.from_terms([([cre(0), cre(1)], 1.0)]) # creates 2 particles
>>> gate = Evolution(2 * norb, non_conserving, time=1.0)
>>>
>>> try:
... gate._apply_unitary_(reference, norb, nelec, copy=True)
... except ValueError as exc:
... print("rejected:", exc)
rejected: The given FermionOperator could not be converted to a LinearOperator because it does not conserve particle number and the z component of spin. Conserves particle number: False Conserves spin z: False

Eine Simulation ohne Teilchenzahlerhaltung ist im fermionischen Raum daher durch die Konstruktion der Darstellung mit festem Sektor nicht möglich, und das ist der Preis für den kompakten FCI-Raum, der die fermionische Simulation überhaupt erst handhabbar macht. Wenn dein Algorithmus Operatoren benötigt, die die Teilchenzahl verletzen (zum Beispiel einen Qubit-nativen Fehlerkanal oder einen Operator, der für einen abgebildeten Hamiltonian gebaut wurde, der die Teilchenzahlen nicht term für term erhält), transpiliere zuerst auf Qubits und simuliere den resultierenden QuantumCircuit stattdessen mit einem Simulator auf Qubit-Ebene. Dafür funktioniert jeder Transpilationsweg; die Leitfäden zum fermionischen Circuit und zur Transpilation gehen mehr ins Detail, aber generate_preset_jw_pass_manager() ist eine vernünftige Standardwahl.

Die Blockspin-Konvention für spinbehaftete Systeme​

nelec ist als int | tuple[int, int] typisiert, und sein Typ, nicht ein separates Flag, bestimmt, welches der unterstützten Modus-Layouts gewählt wird:

  • Ein int wählt die spinlose Interpretation. Die norb Modi werden direkt als spinlose Orbitale behandelt, und nelec ist die Gesamtteilchenzahl.

  • Ein Paar (n_alpha, n_beta) wählt die spinbehaftete Interpretation von 2 * norb Modi unter einer festen Blockspin-Konvention. Die Modi 0 .. norb sind die Alpha-Orbitale (Spin up) und die Modi norb .. 2 * norb sind die Beta-Orbitale (Spin down). Jede Spinsorte wird unabhängig erhalten; ein reiner Alpha-Term kann ein Elektron zwischen Alpha-Modi bewegen, aber nie in einen Beta-Modus, und umgekehrt.

Diese Fallunterscheidung geschieht an jedem ffsim-Protokoll-Einstiegspunkt in diesem Paket (Gate-Konstruktoren, _apply_unitary_placed_-Implementierungen und die Sektorkompilierung des nativen Rust-Kernels), und sie ist die Konvention, die in den Leitfäden zu LUCJ und SKQD durchgängig verwendet wird (zum Beispiel füllt InitializeModes.from_hartree_fock() Alpha-Besetzungen in die Modi 0 .. n_alpha und Beta-Besetzungen in die Modi norb .. norb + n_beta). Es lohnt sich, sie dem Modul qiskit_fermions.operators und allgemein FermionicCircuit gegenüberzustellen, die generische Modusindizes ohne inhärente Spin-Semantik verwenden (siehe den Leitfaden zum fermionischen Circuit). Die Blockspin-Bedeutung wird nur auferlegt, wenn einem Simulationsaufruf ein spinbehaftetes nelec übergeben wird, nicht in der Operator- oder Circuit-Darstellung selbst verankert.

>>> from qiskit_fermions.circuit.library import InitializeModes
>>>
>>> norb, nelec = 3, (2, 1)
>>> init = InitializeModes.from_hartree_fock(norb, nelec)
>>> print([bool(occ) for occ in init.occupation]) # alpha modes 0,1; beta mode 3 (= norb + 0)
[True, True, False, True, False, False]

Nächste Schritte​