Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Як цей пакет пов'язаний з ffsim

Важливо

Концепції в цьому посібнику доступні лише в Python API.

ffsim і цей пакет розподіляють роботу, а не перекриваються, за єдиною лінією:

ffsim відповідає заqiskit-fermions відповідає за
Високорівневі оператори анзаців (UCJOpSpinBalanced, UCCSDOpRestrictedReal та їхні родичі), побудовані з амплітуд coupled-cluster або вектора параметрівФерміонне коло (FermionicCircuit), яким ці оператори стають, і його транспіляцію в кубітне коло за будь-якого ферміон-кубітного кодування
Швидка симуляція в компактному просторі з фіксованою кількістю частинокФреймворк мапперів і структури даних операторів, з яких побудовано коло

Практичний наслідок діє в обидва боки. Математика анзаців живе в ffsim, тож UCJ і UCC приймають оператор ffsim безпосередньо як вхід. І навпаки, власні гейти Qiskit у ffsim специфічні для перетворення Йордана-Вігнера, тож переведення одного з її операторів анзаців через інше кодування — це те, що дає перенесення його сюди.

Симуляція — це місце, де вони зустрічаються, і цей посібник пояснює, як. ffsim є бекендом симуляції цього пакета, до якого звертаються через власні протоколи, а не через обгортку: посібник SKQD викликає ffsim.apply_unitary(), ffsim.linear_operator() і ffsim.sample_state_vector() безпосередньо на об'єктах FermionicCircuitта FermionOperatorбез кроку перетворення.

Підключення до інтерфейсу симуляції ffsim​

ffsim — це високопродуктивний симулятор ферміонних квантових кіл, який використовує збереження кількості частинок і спіну Z, щоб представляти вектори стану компактніше, ніж загальний 2n2^n-вимірний кубітний вектор стану. Він визначає протоколи, які може реалізувати будь-який об'єкт, щоб брати участь у його механізмі симуляції (див. qiskit_fermions.protocols щодо власних протоколів цього пакета, які мають той самий дизайн):

  • ffsim.SupportsApplyUnitary через метод _apply_unitary_(vec, norb, nelec, copy), що застосовує об'єкт як унітарний оператор до вектора стану з фіксованою кількістю частинок

  • ffsim.SupportsLinearOperator через метод _linear_operator_(norb, nelec), що повертає подання об'єкта у вигляді scipy.sparse.linalg.LinearOperator у тому самому секторі

  • ffsim.SupportsTrace через метод _trace_(norb, nelec), що повертає слід об'єкта в цьому секторі.

Цей пакет реалізує цей інтерфейс: кожен ферміонний гейт у qiskit_fermions.circuit.library надає _apply_unitary_, а FermionOperator надає _linear_operator_ і _trace_. Саме це робить оператори та кола цього пакета сумісними з ffsim, тож його інструменти працюють з ними нативно, без кроку перетворення. ffsim.apply_unitary() може симулювати FermionicCircuit від початку до кінця, ffsim.linear_operator() може діагоналізувати FermionOperator, а утиліти семплування, як-от ffsim.sample_state_vector() (використовується в посібнику SKQD, щоб перетворити симульований вектор стану на лічильники вимірювань), працюють без змін.

Те, що повертає _linear_operator_, — це звичайний LinearOperator зі SciPy, тож scipy.sparse.linalg.eigsh() та подібні функції працюють і з ним.

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

Зв'язок через протоколи ffsim дає ось що: його екосистема утиліт симуляції та семплування, що активно розвивається, застосовується до кіл та операторів цього пакета без змін, а користувачі, які вже працюють з ffsim, можуть додавати гейти й оператори цього пакета, не вивчаючи ще один API симуляції.

ffsim — необов'язкова залежність​

ffsim оголошено як необов'язкове доповнення (pip install "qiskit-fermions[ffsim]", або транзитивно через [all]), що охороняється під час виконання через HAS_FFSIM (як показано вище). Його встановлення відкриває симуляцію: ffsim.SupportsLinearOperator і ffsim.SupportsTrace реалізовано через перетворення FermionOperator цього пакета на ffsim.FermionOperator, тож без ffsim вони викликають MissingOptionalLibraryError. Усе, що не симулює (побудова операторів, їх відображення та транспіляція отриманих кіл), не потребує нічого з цього.

Ферміонна симуляція відбувається у секторі з фіксованою кількістю частинок​

І _apply_unitary_, і _linear_operator_ приймають аргумент nelec і представляють вектор стану в базисі детермінантів із фіксованою кількістю частинок для цього сектора (norb, nelec), віддзеркалюючи налаштування простору FCI в ffsim (і, транзитивно, PySCF), а не повний 2num_modes2^{\text{num\_modes}}-вимірний простір, який використав би загальний кубітний симулятор. Це набагато менший простір (його розмір — добуток біноміальних коефіцієнтів, а не степінь двійки), але з жорстким обмеженням. У ньому можна представити лише оператори та гейти, що зберігають кількість частинок (а у спіновому випадку — окремо кількість частинок кожного сорту спіну). Оператору, дія якого перемістила б амплітуду до іншої кількості частинок, нікуди її подіти.

ffsim розв'язує це, прямо відхиляючи такий оператор: перетворення його на лінійний оператор викликає ValueError із зазначенням, який закон збереження порушено. Це правильна поведінка за замовчуванням, бо застосування exp⁡(−itH)\exp(-i t H) для гамільтоніана HH із доданком, що не зберігає кількість частинок, інакше перетворило б імовірно унітарне відображення на неунітарне й фізично безглузде.

  • Evolution передає цю відмову від оператора, еволюцію якого він описує.

  • OrbitalRotation перевіряє, що її (вбудована) матриця обертання є блочно-діагональною щодо розділення на alpha/beta у спіновому випадку, і викликає ValueError, якщо вона змішує спінові сектори.

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

Тому симуляція без збереження числа частинок недосяжна у ферміонному просторі за побудовою представлення з фіксованим сектором, і це ціна компактного FCI-простору, який узагалі робить ферміонну симуляцію здійсненною. Якщо твоєму алгоритму потрібні оператори, що порушують кількість частинок (наприклад, кубітний канал помилок або оператор, побудований для відображеного гамільтоніана, який не зберігає числа частинок доданок за доданком), спершу виконай транспіляцію до кубітів і натомість симулюй отриманий QuantumCircuit кубітним симулятором. Для цього підійде будь-який маршрут транспіляції; посібник зі ферміонних схем і посібник з транспіляції розглядають це детальніше, але generate_preset_jw_pass_manager() є розумним вибором за замовчуванням.

Конвенція блочного спіну для спінових систем​

nelec має тип int | tuple[int, int], і саме його тип, а не окремий прапорець, обирає одну з підтримуваних розкладок мод:

  • int обирає безспінову інтерпретацію. norb мод трактуються безпосередньо як безспінові орбіталі, а nelec є загальною кількістю частинок.

  • Пара (n_alpha, n_beta) обирає спінову інтерпретацію 2 * norb мод за фіксованою конвенцією блочного спіну. Моди 0 .. norb є alpha (спін угору) орбіталями, а моди norb .. 2 * norb є beta (спін униз) орбіталями. Кожен спіновий сорт зберігається незалежно; доданок лише з alpha може переміщувати електрон між alpha-модами, але ніколи в beta-моду, і навпаки.

Цей розподіл відбувається в кожній точці входу протоколу ffsim у цьому пакеті (конструктори гейтів, реалізації _apply_unitary_placed_ та компіляція секторів нативного ядра на Rust), і це конвенція, що використовується в посібниках LUCJ та SKQD (наприклад, InitializeModes.from_hartree_fock() записує alpha-заповнення в моди 0 .. n_alpha, а beta-заповнення в моди norb .. norb + n_beta). Варто протиставити це модулю qiskit_fermions.operators та FermionicCircuit загалом, які використовують загальні індекси мод без власної спінової семантики (див. посібник зі ферміонних схем). Блочно-спіновий сенс накладається лише тоді, коли у виклик симуляції передається спіновий nelec, а не вбудований у саме представлення оператора чи схеми.

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

Наступні кроки​

  • Пройди посібник SKQD, щоб побачити, як ffsim.apply_unitary() та ffsim.sample_state_vector() використовуються разом для вибірки зі схем, побудованих з гейтів цього пакета, і для обчислення очікуваного значення гамільтоніана через ffsim.linear_operator() (який сам спирається на той самий метод протоколу _linear_operator_, описаний тут).

  • Пройди посібник LUCJ, щоб побачити другу половину історії: як оператор анзаца ffsim проходить через конвеєр транспіляції цього пакета і потрапляє на карту зв'язності пристрою.

  • Прочитай посібник зі ферміонних схем про загальне, незалежне від спіну індексування мод поза викликами симуляції.

  • Прочитай посібник з транспіляції, щоб дізнатися, як вийти з ферміонного простору​

    і симулювати на кубітах, що потрібно для операторів, які не зберігають число частинок.

  • Перегляньте qiskit_fermions.protocols для повного каталогу протоколів, які визначає цей пакет, зокрема протоколи перетворення (SupportsFermionOperator, SupportsMajoranaOperator), які не пов'язані з ffsim.

  • Прочитай власні посібники ffsim про робочі процеси, якими він володіє, зокрема побудову анзаца UCJ та його варіаційну оптимізацію. Вектори параметрів, якими керують ці посібники, — це те, що приймають UCJ і UCC через оператори, які вони будують.