Зменшення глибини Circuit за допомогою аддону AQC-Tensor для Qiskit
У цьому ноутбуці ми пройдемо кроки патерну Qiskit, використовуючи наближену квантову компіляцію з те нзорними мережами (AQC-Tensor), щоб досягти меншої глибини Circuit, ніж зазвичай потрібно для виконання еволюції Троттера.
Ось кроки, які ми виконаємо:
- Крок 1: Відображення на квантову задачу
- Ініціалізація гамільтоніана задачі та спостережуваних
- Генерація цільового стану тензорної мережі для початкової частини Circuit
- Генерація Circuit з малою глибиною, що апроксимує стиснуту частину
- Генерація загального ансацу з цього Circuit
- Оптимізація параметрів для максимального наближення ансацу до цільового стану
- Додавання наступних кроків Троттера до оптимізованого ансацу
- Крок 2: Оптимізація для цільового обладнання
- Транспіляція Circuit для обладнання
- Крок 3: Виконання експериментів
- Використання фіктивного Backend для простоти
- Крок 4: Реконструкція результатів
- Н/Д; натомість ми просто виводимо виміряну спостережувану
Крок 1: Відображення на квантовий Circuit і оператор
Налаштування модельного гамільтоніана та спостережуваної
У цьому ноутбуці ми використовуємо модель Ізінга на кільці з 10 вузлів:
де граничні умови по колу означають, що при отримуємо , — константа зв'язку між двома вузлами, а — зовнішнє магнітне поле.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-addon-aqc-tensor qiskit-addon-utils qiskit-ibm-runtime quimb scipy
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian
# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)
# Choose a 10-qubit circle on this coupling map
reduced_coupling_map = coupling_map.reduce([0, 13, 1, 14, 10, 16, 4, 15, 3, 9])
# Get a qubit operator describing the Ising field model
hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(0.0, 0.0, 1.0),
ext_magnetic_field=(0.4, 0.0, 0.0),
)
Спостережувана, яку ми вимірюватимемо, — це повна намагніченість.
from qiskit.quantum_info import SparsePauliOp
L = reduced_coupling_map.size()
observable = SparsePauliOp.from_sparse_list([("Z", [i], 1 / L / 2) for i in range(L)], num_qubits=L)
Визначення обсягу часової еволюції для класичного моделювання
Наша загальна мета — моделювати часову еволюцію наведеного вище модельного гамільтоніана. Ми робимо це за допомогою еволюції Троттера, яку поділяємо на дві частини:
- Початкова частина, яка піддається моделюванню за допомогою матрично-добуткових станів (MPS). Ми «скомпілюємо» цю частину за допомогою AQC, як описано у https://arxiv.org/abs/2301.08609.
- Наступна частина Circuit, яка виконуватиметься на обладнанні. Плануємо використати AQC-Tensor для стиснення нашого Circuit часової еволюції до моменту , а потім еволюціонувати звичайними кроками Троттера до .
Генерація Circuit до та після розбиття
Тепер, коли ми обрали розбиття при , ми згенеруємо два Circuit:
- «Цільовий» Circuit для частини еволюції AQC, від до . Оскільки він моделюється симулятором тензорних мереж, кількість шарів впливає на час виконання лише як константний множник, тому можна сміливо використовувати велику кількість шарів для мінімізації помилки Троттера.
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit
aqc_evolution_time = 4.0
aqc_target_num_trotter_steps = 45
aqc_target_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
)
- Наступний Circuit еволюції, який еволюціонує від до . Оскільки він виконується на квантовому обладнанні, бажано використовувати якомога менше шарів Троттера.
subsequent_evolution_time = 1.0
subsequent_num_trotter_steps = 5
subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)
Для подальшого порівняння згенеруємо також третій Circuit: такий, що еволюціонує протягом aqc_evolution_time, але з тим самим часом еволюції на крок Троттера, що й наступний Circuit. Це той Circuit, з яким ми б працювали, якби не використовували велику кількість кроків Троттера для цільового Circuit. Будемо називати його порівняльним Circuit.
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps / subsequent_evolution_time * aqc_evolution_time
)
aqc_comparison_num_trotter_steps
20
comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Генерація ансацу та початкових параметрів із Circuit Троттера з меншою кількістю кроків
Спочатку ми будуємо «хороший» Circuit, що має той самий час еволюції, що й цільовий Circuit, але з меншою кількістю кроків Троттера (і, відповідно, менше шарів).
Потім ми передаємо цей «хороший» Circuit до функції generate_ansatz_from_circuit аддону AQC-Tensor. Ця функція аналізує зв'язність двох-Qubit у Circuit і повертає дві речі:
- загальний параметризований Circuit ансацу з тією ж двох-Qubit зв'язністю, що й вхідний Circuit; та
- параметри, які при підстановці в ансац відтворюють вхідний (хороший) Circuit.
Незабаром ми візьмемо ці параметри й ітеративно коригуватимемо їх, щоб максимально наблизити Circuit ансацу до цільового MPS.
from qiskit_addon_aqc_tensor import generate_ansatz_from_circuit
aqc_ansatz_num_trotter_steps = 5
aqc_good_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
)
aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit, qubits_initially_zero=True
)
aqc_ansatz.draw("mpl", fold=-1)

print(f"Comparison circuit: depth {comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters")
Comparison circuit: depth 120
Target circuit: depth 270
Ansatz circuit: depth 23, with 515 parameters
Вибір налаштувань для моделювання тензорних мереж
Тут ми використовуємо симулятор тензорних мереж на основі quimb. У цьому прикладі ми використовуємо симулятор матрично-добуткових станів (MPS) від quimb та JAX для автоматичного диференціювання. Докладніше про використання симулятора quimb дивись у документації API.
from functools import partial
import quimb.tensor
from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator
simulator_settings = QuimbSimulator(
partial(quimb.tensor.CircuitMPS, max_bond=100, cutoff=1e-8),
autodiff_backend="jax",
)
Побудова представлення матрично-добуткового стану цільового стану AQC
Далі ми будуємо матрично-добуткове представлення стану, що має бути апроксимований за допомогою AQC.
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
aqc_target_mps = tensornetwork_from_circuit(aqc_target_circuit, simulator_settings)
Зауваж, що оскільки ми обрали велику кількість кроків Троттера для цільового стану, він насправді має меншу похибку Троттера, ніж порівняльний Circuit. Ми можемо розрахувати вірність () стану, підготовленого порівняльним Circuit, відносно цільового стану:
from qiskit_addon_aqc_tensor.simulation import compute_overlap
comparison_mps = tensornetwork_from_circuit(comparison_circuit, simulator_settings)
comparison_fidelity = abs(compute_overlap(comparison_mps, aqc_target_mps)) ** 2
comparison_fidelity
0.9996761790297157