Перейти до основного вмісту

Відкладене розв'язання часових параметрів за допомогою stretch

Специфікація мови OpenQASM 3 містить тип stretch, за допомогою якого можна вказувати відносний час операцій замість абсолютного. Підтримку stretch як тривалості для інструкцій Delay було додано у Qiskit v2.0.0. Конкретне значення тривалості stretch розв'язується під час компіляції, після того як стають відомі точні тривалості відкаліброваних вентилів. Компілятор намагається мінімізувати тривалість stretch з урахуванням часових обмежень на одному або кількох кубітах. Це дозволяє виражати такі конструкції вентилів, як рівномірне розташування вентилів (наприклад, для реалізації послідовності ехо-розв'язки вищого порядку), вирівнювання послідовності вентилів по лівому краю або застосування вентиля протягом тривалості певної підсхеми — без знання точного часу.

Приклади

Динамічне розв'язання

Поширеним варіантом використання stretch є застосування динамічного розв'язання до простоюючого кубіта, поки інший кубіт виконує умовні операції.

Наприклад, за допомогою stretch можна застосувати послідовність динамічного розв'язання XX до кубіта 1 протягом умовного блоку, що застосовується до кубіта 0, як показано на такій діаграмі:

Image illustrating the following circuit

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

from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits

# Add barriers to define the boundaries
circuit.barrier()
circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q0)
with else_:
circuit.x(q0)

# Apply an XX DD sequence with stretch on qubit 1
s = circuit.add_stretch("s")
circuit.delay(s, q1)
circuit.x(q1)
circuit.delay(expr.mul(s, 2), q1)
circuit.x(q1)
circuit.delay(s, q1)
circuit.barrier()

Вирівнювання за розкладом

У цьому прикладі stretch використовується для забезпечення вирівнювання послідовності вентилів між двома бар'єрами по лівому краю, незалежно від їхньої фактичної тривалості:

from qiskit import QuantumCircuit
from numpy import pi

qc = QuantumCircuit(5)
qc.barrier()
qc.cx(0, 1)
qc.u(pi/4, 0, pi/2, 2)
qc.cx(3, 4)

a = qc.add_stretch("a")
b = qc.add_stretch("b")
c = qc.add_stretch("c")

# Use the stretches as Delay duration.
qc.delay(a, [0, 1])
qc.delay(b, 2)
qc.delay(c, [3, 4])
qc.barrier()
примітка

При використанні stretch з Qiskit Runtime будь-який залишок від розв'язання stretch додається до першої затримки, що використовує цей stretch.

Приклад:

a = circuit.add_stretch("a")
circuit.barrier(q0, q1)
circuit.delay(100, q0)
circuit.delay(a, q1) # resolve to 26
circuit.x(q1) # duration: 8
circuit.delay(a, q1) # resolve to 25
circuit.x(q1) # duration: 8
circuit.delay(a, q1) # resolve to 25
circuit.x(q1) # duration: 8
circuit.barrier(q0, q1)

Наведений вище код розв'язується до значення 25 із залишком 1. До першої затримки [a] буде додано залишок.

Рівняння розв'язання stretch: a+8+a+8+a+8=100=3a+24a + 8 + a + 8 + a + 8 = 100 = 3*a + 24

Перегляд значень stretch у Qiskit Runtime

Фактичне значення тривалості stretch розв'язується під час компіляції після планування схеми. При запуску завдання Sampler у Qiskit Runtime ти можеш переглядати розв'язані значення stretch у метаданих результату завдання. Підтримка stretch у Qiskit Runtime наразі є експериментальною, тому спочатку потрібно встановити експериментальний параметр для увімкнення його отримання, а потім звернутися до даних безпосередньо з метаданих таким чином:

# Enable stretch value retrieval.
sampler.options.experimental = {
"execution": {
"stretch_values": True,
"scheduler_timing": True,
},
}

# Access the stretch values from the metadata.
job_result = job.result()
circuit_stretch_values = job_result[0].metadata["compilation"]["stretch_values"]

# Visualize the timing.
# Use the sliders at the bottom, the controls at the top, and the legend on the side
# of the output to customize the view.
draw_circuit_schedule_timing(ob.result()[0].metadata['compilation']['scheduler_timing']['timing'])
примітка

Хоча загальний час схеми повертається в метаданих "compilation", це НЕ час, що використовується для тарифікації (квантовий час).

Розуміння виводу метаданих

Метадані stretch_values повертають таку інформацію:

  • Name (Ім'я): Назва застосованого stretch.
  • Value (Значення): Запитуване цільове значення.
  • Remainder (Залишок): Залишок від розв'язання stretch, що додається до першої затримки, яка використовує цей stretch.
  • Expanded values (Розгорнуті значення): Набори значень, що вказують початок stretch та його тривалість.

Приклад

# Define the circuit
circuit = QuantumCircuit(4)
foo = circuit.add_stretch("foo")
bar = circuit.add_stretch("bar")
circuit.barrier()
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.cz(0, 1)
circuit.cz(0, 1)

circuit.delay(foo, 2)
circuit.x(2)
# 3*foo
circuit.delay(expr.mul(3, foo), 2)
circuit.x(2)
# 2*foo
circuit.delay(expr.mul(2, foo), 2)

circuit.delay(bar, 3)
circuit.x(3)
circuit.delay(bar, 3)

circuit.measure_all()

Вивід метаданих

 [{'name': 'bar',
'value': 29,
'remainder': 1,
'expanded_values': [[1365, 30], [1404, 29]]},
{'name': 'foo',
'value': 8,
'remainder': 2,
'expanded_values': [[1365, 10], [1384, 24], [1417, 16]]}
]

Значення, що повертаються для тривалості, залежать від цільового значення та обчисленого залишку. Наприклад, ось тривалості, що повертаються для foo:

  • foo value + remainder (8+2 = 10)
  • foo value * 3 (8 x 3 = 24)
  • foo value * 2 (8 x 2 = 16)

Для розуміння та перевірки часових параметрів можна використати візуалізацію.

draw_circuit_schedule_timing(job.result()[0].metadata['compilation']['scheduler_timing']['timing'])

На наступному зображенні, що базується на прикладному виводі, foo відповідає stretch на кубіті 2. Перша затримка stretch, що використовує foo, починається наприкінці init_play (1365). Тривалість stretch становить 10, тому ця затримка закінчується, коли починається вентиль x (1365+10=1375). Аналогічно можна інтерпретувати другий і третій stretch.

The output from the draw_circuit_schedule_timing command is shown.

Використовуй повзунки внизу, елементи керування вгорі (наведи курсор на зображення виводу, щоб їх відобразити) та легенду збоку від виводу для налаштування вигляду. Наведи курсор на зображення, щоб переглянути точні дані.

Для отримання повної інформації дивись тему Візуалізація часових параметрів схеми.

Обмеження Qiskit Runtime

Підтримка stretch у Qiskit Runtime наразі є експериментальною і має такі обмеження:

  • Не більше однієї змінної stretch на набір кубітів між бар'єрами (неявними та явними). Набір кубітів — це один або кілька кубітів; ці набори мають бути взаємовиключними.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    circuit.delay(a, (q0, q1))
    circuit.delay(b, q0) # Invalid because 2 stretches are applied on q0
  • Область, оточена набором бар'єрів, називається бар'єрним регіоном. Змінна stretch не може використовуватися в кількох бар'єрних регіонах.

    # Stretch a is used in two barrier regions
    a = circuit.add_stretch("a")
    circuit.barrier((q0, q1))
    circuit.delay(a, q0)
    circuit.barrier((q0, q1))
    circuit.delay(a, q0)
    circuit.barrier((q0, q1))

    Illustration of the previous code output

  • Вирази stretch обмежені формою X*stretch + Y, де X та Y є константами з плаваючою точкою або цілими числами.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    c = circuit.add_stretch("c")

    # (a / b) * c is not supported
    circuit.delay(expr.mul(expr.div(a, b), c), q1)
  • Вирази stretch можуть містити лише одну змінну stretch.

    a = circuit.add_stretch("a")
    b = circuit.add_stretch("b")
    circuit.delay(expr.add(a, b), 0)
  • Вирази stretch не можуть розв'язуватися до від'ємних значень затримки. Поточний розв'язувач не враховує обмеження невід'ємності.

    from qiskit.circuit import Duration

    circuit.barrier((q0, q1))
    circuit.delay(20, q1)
    # The length of this barrier region is 20dt, meaning the
    # equation for solving stretch 'a' is a + 40dt = 20dt, giving a = -20dt.
    circuit.delay(expr.add(a, Duration.dt(40)), q0)
    circuit.barrier((q0, q1))