Portuguese
Idiomas
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

Nota

Esta página foi gerada, a partir de tutorials/circuits_advanced/06_building_pulse_schedules.ipynb.

Construindo planejamento de pulsos

Portas de pulso definem uma representação exata de baixo nível para um portão de circuito. Uma única operação pode ser implementada com um programa de pulso, composto por múltiplas instruções de baixo nível. Para aprender mais sobre portas pulse, consulte a documentação aqui. Esta página detalha como criar programas de pulso.

Nota: Para dispositivos IBM, os programas de pulso são usados como subrotinas para descrever portas. Anteriormente, alguns dispositivos aceitaram programas completos neste formato, mas isso está sendo descontinuado eté dezembro de 2021. Outros provedores podem ainda aceitar programas completos neste formato. Independentemente de como o programa é usado, a sintaxe para construção do programa é a mesma. Leia mais para aprender como!

Pulse programs, which are called Schedules, describe instruction sequences for the control electronics. We build Schedules using the Pulse Builder. It’s easy to initialize a schedule:

[20]:
from qiskit import pulse

with pulse.build(name='my_example') as my_program:
    # Add instructions here
    pass

my_program
[20]:
ScheduleBlock(, name="my_example", transform=AlignLeft())

Você pode ver que ainda não há instruções. A próxima seção desta página explicará cada uma das instruções que você pode adicionar a um agendamento e a última seção descreverá vários contextos de alinhamento, que determinam como as instruções são posicionadas em relação ao tempo, umas com as outras.

Instruções da Schedule

  • `delay(duration, channel) <#delay>`__

  • `play(pulse, channel) <#play>`__

  • `set_frequency(frequency, channel) <#frequency>`__

  • `shift_phase(phase, channel) <#phase>`__

  • `acquire(duration, channel, mem_slot, reg_slot) <#acquire>`__

Cada tipo de instrução tem seu próprio conjunto de operações. Como você pode ver, acima, cada um deles inclui, pelo menos, um Canal para especificar, onde a instrução será aplicada.

Canais são rótulos de linhas de sinal do hardware de controle para o processador quântico.

  • DriveChannels são usadas tipicamente para orientar rotações simples de qubit,

  • ControlChannels são usadas tipicamente para portas multi-qubit ou linhas de direção adicionais para qubits ajustáveis,

  • MeasureChannels são específicos para transmissão de pulsos, que estimulam a leitura e

  • AcquireChannels são usados para acionar digitalizadores que recolhem sinais de leitura.

DriveChannels, ControlChannels, e MeasureChannels são todos PulseChannels; Isso significa que eles suportam pulsos transmissores, enquanto o AcquireChannel é apenas um canal que apenas recebe e não pode executar formas de ondas.

Para os seguintes exemplos, vamos criar uma instância DriveChannel para cada Instruction que aceita um PulseChannel. Canais recebem um argumento index tipo inteiro. Exceto por ControlChannels, o índice mapeia trivialmente para a label qubit.

[21]:
from qiskit.pulse import DriveChannel

channel = DriveChannel(0)

O pulso Schedule é independente do serviço em que é executado. No entanto, podemos construir nosso programa num contexto que está ciente do serviço alvo, fornecendo-o para pulse.build. Quando possível, você deve fornecer um serviço. Usando os acessores de canal pulse.<type>_channel(<idx>) nós podemos nos certificar que estamos utilizando apenas recursos de dispositivo disponíveis.

[22]:
from qiskit.providers.fake_provider import FakeValencia

backend = FakeValencia()

with pulse.build(backend=backend, name='backend_aware') as backend_aware_program:
    channel = pulse.drive_channel(0)
    print(pulse.num_qubits())
    # Raises an error as backend only has 5 qubits
    #pulse.drive_channel(100)
5

delay

Uma das instruções mais simples que podemos construir é delay. Esta é uma instrução de bloqueio, que diz para o controle eletrônico não produzir nenhum sinal no canal dado, durante a duração especificada. É útil para controlar o timing de outras instruções.

The duration here and elsewhere is in terms of the backend’s cycle time (1 / sample rate), dt. It must take an integer value.

Para adicionar uma instrução de delay passamos uma duração e um canal, onde canal pode ser qualquer tipo de canal, incluindo AcquireChannel. Usamos pulse.build para começar um contexto Pulse Builder. Isto automaticamente agenda o atraso no agendamento de delay_5dt.

[23]:
with pulse.build(backend) as delay_5dt:
    pulse.delay(5, channel)

That’s all there is to it. Any instruction added after this delay on the same channel will execute five timesteps later than it would have without this delay.

play

The play instruction is responsible for executing pulses. It’s straightforward to add a play instruction:

with pulse.build() as sched:
    pulse.play(pulse, channel)

Let’s clarify what the pulse argument is and explore a few different ways to build one.

Pulsos

Um Pulse especifica um pulso envelope arbitrário. A frequência de modulação e a fase da onda de saída são controladas pela instrução set_frequency e shift_phase, que abordaremos em seguida.

The image below may provide some intuition for why they are specified separately. Think of the pulses which describe their envelopes as input to an arbitrary waveform generator (AWG), a common lab instrument – this is depicted in the left image. Notice the limited sample rate discritizes the signal. The signal produced by the AWG may be mixed with a continuous sine wave generator. The frequency of its output is controlled by instructions to the sine wave generator; see the middle image. Finally, the signal sent to the qubit is demonstrated by the right side of the image below.

Nota: O hardware pode ser implementado de outras formas, mas se mantivermos as instruções separadas, evitamos perder informação explícita, como o valor da frequência de modulação.

alt texto

There are many methods available to us for building up pulses. Our library within Qiskit Pulse contains helpful methods for building Pulses. Let’s take for example a simple Gaussian pulse – a pulse with its envelope described by a sampled Gaussian function. We arbitrarily choose an amplitude of 1, standard deviation \(\sigma\) of 10, and 128 sample points.

Note: The amplitude norm is arbitrarily limited to 1.0. Each backend system may also impose further constraints – for instance, a minimum pulse size of 64. These additional constraints, if available, would be provided through the BackendConfiguration which is described here.

[24]:
from qiskit.pulse import library

amp = 1
sigma = 10
num_samples = 128

Pulsos paramétricos

Let’s build our Gaussian pulse using the Gaussian parametric pulse. A parametric pulse sends the name of the function and its parameters to the backend, rather than every individual sample. Using parametric pulses makes the jobs you send to the backend much smaller. IBM Quantum backends limit the maximum job size that they accept, so parametric pulses may allow you to run larger programs.

Outros pulsos paramétricos na library incluem GaussianSquare, Drag, e Constant.

Nota: O backend é responsável por decidir exatamente como amostrar os pulsos paramétricos. É possível desenhar pulsos paramétricos, mas não se garante que as amostras exibidas são as mesmas que são executadas no backend.

[25]:
gaus = pulse.library.Gaussian(num_samples, amp, sigma,
                              name="Parametric Gaus")
gaus.draw()
[25]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_12_0.png

Formas de ondas de pulso descritas pelas amostras

Uma Waveform é um sinal de pulso especificado, como um array de amplitudes complexas e ordenadas no tempo, ou amostras. Cada amostra é executada por um ciclo, um timestep dt, determinado pelo backend. Se queremos saber a dinâmica do nosso programa em tempo real, precisamos saber o valor de dt. A amostra \(i^{th}\) (zero-indexed) irá executar do passo i*dt até o (i + 1)*dt, modulada pela frequência do qubit.

[26]:
import numpy as np

times = np.arange(num_samples)
gaussian_samples = np.exp(-1/2 *((times - num_samples / 2) ** 2 / sigma**2))

gaus = library.Waveform(gaussian_samples, name="WF Gaus")
gaus.draw()
[26]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_14_0.png

Funções da biblioteca de pulsos

Nossa própria biblioteca pulso tem métodos de amostragem para construir uma Waveform, a partir de funções comuns.

[27]:
gaus = library.gaussian(duration=num_samples, amp=amp, sigma=sigma, name="Lib Gaus")
gaus.draw()
[27]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_16_0.png

Independente do método que você usar para especificar seu pulse, play é adicionado ao seu agendamento da mesma forma:

[28]:
with pulse.build() as schedule:
    pulse.play(gaus, channel)
schedule.draw()
[28]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_18_0.png

Você também pode fornecer uma lista complexa ou matriz diretamente para play

[29]:
with pulse.build() as schedule:
    pulse.play([0.001*i for i in range(160)], channel)
schedule.draw()
[29]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_20_0.png

A instrução play obtém a duração de seu Pulse: a duração de um pulso parametrizado é um argumento explícito e a duração de uma Waveform é o número de amostras de entrada.

set_frequency

Como explicado anteriormente, o envelope da forma de onda do pulso de saída também é modulado por uma frequência e fase. Cada canal possui uma frequência padrão listada no ``backend.defaults()` <09_gathering_system_information.ipynb#Defaults>`__.

A frequência de um canal pode ser atualizada, a qualquer momento, dentro de uma Schedule pela instrução set_frequency. Leva uma frequency do tipo flutuante e um PulseChannel canal como entrada. Todos os pulsos em um canal na sequência de uma instrução set_frequency serão modulados pela frequência determinada até que outra instrução set_frequency seja encontrada ou até que o programa termine.

A instrução tem uma duração implícita de 0.

Nota: as frequências que podem ser solicitadas são limitadas pela largura de banda total e pela largura de banda instantânea de cada canal de hardware. No futuro, estes serão reportados pelo backend.

[30]:
with pulse.build(backend) as schedule:
    pulse.set_frequency(4.5e9, channel)

shift_phase

A instrução shift_phase aumentará a fase da modulação de frequência por phase. Como set_frequency, essa alteração de fase afetará todas as instruções seguintes no mesmo canal até que o programa termine. Para desfazer o efeito de um shift_phase, o negativo da phase pode ser passado para uma nova instrução.

Como set_frequency, a instrução tem uma duração implícita de 0.

[31]:
with pulse.build(backend) as schedule:
    pulse.shift_phase(np.pi, channel)

acquire

A instrução acquire dispara a aquisição de dados para leitura. Tem a duração, um AcquireChannel que mapeia o qubit sendo medido, e um MemorySlot ou um RegisterSlot. O MemorySlot é a memória clássica onde o resultado da leitura será armazenado. O RegisterSlot mapeia para um registro em um controle eletrônico que armazena o resultado de leitura para rápido feedback.

As instruções acquire também podem considerar Discriminators personalizados e Kernels como argumentos de palavras-chave.

[32]:
from qiskit.pulse import Acquire, AcquireChannel, MemorySlot

with pulse.build(backend) as schedule:
    pulse.acquire(1200, pulse.acquire_channel(0), MemorySlot(0))

Now that we know how to add Schedule instructions, let’s learn how to control exactly when they’re played.

Construtor de pulso

Aqui, vamos analisar os recursos mais importantes do Pulse Builder para aprender como criar agendamentos. Isto não aborda à exaustão; para mais detalhes sobre o que você pode fazer usando o Construtor de Pulso, confira a Referência da API Pulso.

Contextos de alinhamento

O construtor tem contextos de alinhamento que influenciam a forma como um agendamento é construído. Os contextos também podem ser aninhados. Experimente-os usando .draw() para ver como os pulsos se alinham.

Regardless of the alignment context, the duration of the resulting schedule is as short as it can be while including every instruction and following the alignment rules. This still allows some degrees of freedom for scheduling instructions off the «longest path». The examples below illuminate this.

align_left

O construtor tem contextos de alinhamento que influenciam como um agendamento é construído. O padrão é align_left.

[33]:
with pulse.build(backend, name='Left align example') as program:
    with pulse.align_left():
        gaussian_pulse = library.gaussian(100, 0.5, 20)
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))

program.draw()
[33]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_28_0.png

Observe como não há liberdade de agendamento para os pulsos no D1. A segunda forma de onda começa imediatamente após a primeira. O pulso em D0 pode começar em qualquer momento entre t=0 e t=100 sem mudar a duração do agendamento geral. O contexto align_left define o horário de início deste pulso para t=0. Você pode pensar nisto como a justificação à esquerda de um documento de texto.

align_right

Unsurprisingly, align_right does the opposite of align_left. It will choose t=100 in the above example to begin the gaussian pulse on D0. Left and right are also sometimes called «as soon as possible» and «as late as possible» scheduling, respectively.

[34]:
with pulse.build(backend, name='Right align example') as program:
    with pulse.align_right():
        gaussian_pulse = library.gaussian(100, 0.5, 20)
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))

program.draw()
[34]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_30_0.png

align_equispaced(duration)

Se a duração de um determinado bloco é conhecida, você também pode usar align_equispaced para inserir atrasos de duração iguais entre cada instrução.

[35]:
with pulse.build(backend, name='example') as program:
    gaussian_pulse = library.gaussian(100, 0.5, 20)
    with pulse.align_equispaced(2*gaussian_pulse.duration):
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
    pulse.play(gaussian_pulse, pulse.drive_channel(1))
    pulse.play(gaussian_pulse, pulse.drive_channel(1))

program.draw()
[35]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_32_0.png

align_sequential

Este contexto de alinhamento não agenda as instruções em paralelo. Cada instrução começará ao final da instrução previamente adicionada.

[36]:
with pulse.build(backend, name='example') as program:
    with pulse.align_sequential():
        gaussian_pulse = library.gaussian(100, 0.5, 20)
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))
        pulse.play(gaussian_pulse, pulse.drive_channel(1))

program.draw()
[36]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_34_0.png

Deslocamentos de fase e frequência

Podemos usar o construtor para nos ajudar a deslocar temporariamente a frequência ou fase dos nossos pulsos em um canal.

[37]:
with pulse.build(backend, name='Offset example') as program:
    with pulse.phase_offset(3.14, pulse.drive_channel(0)):
        pulse.play(gaussian_pulse, pulse.drive_channel(0))
        with pulse.frequency_offset(10e6, pulse.drive_channel(0)):
            pulse.play(gaussian_pulse, pulse.drive_channel(0))

program.draw()
[37]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_36_0.png

Nós o encorajamos a visitar a Referência API Pulse para saber mais.

[38]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.22.3
qiskit-aer0.11.2
qiskit-ignis0.7.0
qiskit-ibmq-provider0.19.2
qiskit0.39.4
System information
Python version3.10.6
Python compilerGCC 11.3.0
Python buildmain, Nov 14 2022 16:10:14
OSLinux
CPUs4
Memory (Gb)3.7695083618164062
Thu Dec 22 18:42:20 2022 JST

This code is a part of Qiskit

© Copyright IBM 2017, 2022.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.