Portuguese, Brazilian
Idiomas
English
Japanese
German
Korean
Portuguese, Brazilian
French
Shortcuts

Nota

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

Execute, interativamente, no IBM Quantum lab.

Construindo planejamento de pulsos

Programas pulso, que são chamados de Schedules, descrevem as sequências de instruções para os aparelhos de controle eletrônico. Preparamos a Schedules usando o Construtor de Pulsos. É fácil inicializar um agendamento:

[1]:
from qiskit import pulse

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

my_program
[1]:
Schedule(, name="my_example")

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

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.

[2]:
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.

[3]:
from qiskit.test.mock 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.

A duração aqui e, em outro lugar, é em termos de tempo de ciclo do backend (1 / taxa da amostra), dt. Deve levar um valor inteiro.

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.

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

Isto é tudo o que há nisto. Qualquer instrução adicionada, após este atraso, no mesmo canal, executará cinco vezes mais tarde, do que teria sem esse atraso.

play

A instrução Rodando é responsável para executar pulsos. É simples adicionar uma instrução para rodar:

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

Vamos esclarecer o que o argumento pulse é e explorar algumas maneiras diferentes de construir um.

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.

A imagem, abaixo, pode fornecer alguma intuição para o motivo pelo qual elas são especificadas, separadamente. Pense em pulsos que descrevem seus envelopes como entrada, para um gerador de onda arbitrária (AWG), um instrumento comum do laboratório – isto é retratado na imagem da esquerda. Observe que a taxa de amostragem limitada descreve o sinal. O sinal produzido pelo AWG pode ser misturado com um gerador de ondas senoidais contínuas. A frequência de sua saída é controlada por instruções para o gerador de ondas senoidais; veja a imagem do meio. Finalmente, o sinal enviado para o qubit é demonstrado pelo lado direito da imagem, abaixo.

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 text

Há muitos métodos disponíveis para a construção de pulsos. A nossa library, dentro do Qiskit Pulse, contém métodos úteis para a construção de Pulses. Tomemos, por exemplo, um pulso Gaussiano simples – um pulso com seu envelope, descrito por uma função gaussiana amostrada. Escolhemos, arbitrariamente, uma amplitude de 1, um desvio-padrão \(\sigma\) de 10, e 128 pontos de amostragem.

Nota: A norma de amplitude é, arbitrariamente, limitada a 1.0. Cada sistema de backend, também pode impor mais restrições – por exemplo, um tamanho de pulso mínimo de 64. Estas restrições adicionais, se disponíveis, seriam fornecidas, através da BackendConfiguration que é descrita aqui.

[5]:
from qiskit.pulse import library

amp = 1
sigma = 10
num_samples = 128

Pulsos paramétricos

Vamos construir nosso pulso gaussiano usando o pulso paramétrico Gaussian. Um pulso paramétrico envia o nome da função e seus parâmetros para o backend, ao invés de cada amostra individual. Usar pulsos paramétricos torna muito menor o trabalho que você manda para o backend. Os backends IBM Quantum limitam o tamanho máximo do trabalho que eles aceitam, então pulsos paramétricos podem permitir que você execute programas maiores.

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.

[6]:
gaus = pulse.library.Gaussian(num_samples, amp, sigma,
                              name="Parametric Gaus")
gaus.draw()
[6]:
../../_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.

[7]:
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()
[7]:
../../_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.

[8]:
gaus = library.gaussian(duration=num_samples, amp=amp, sigma=sigma, name="Lib Gaus")
gaus.draw()
[8]:
../../_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:

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

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

[10]:
with pulse.build() as schedule:
    pulse.play([0.001*i for i in range(160)], channel)
schedule.draw()
[10]:
../../_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

As explained previously, the output pulse waveform envelope is also modulated by a frequency and phase. Each channel has a default frequency listed in the backend.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.

[11]:
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.

[12]:
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.

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

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

Agora que sabemos como adicionar instruções Schedule, vamos aprender a controlar exatamente quando elas são executadas.

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.

Alignment contexts

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.

Independentemente do contexto do alinhamento, a duração do agendamento resultante é tão curta quanto pode ser enquanto inclui todas instruções e segue as regras do alinhamento. Isso ainda permite alguns graus de liberdade para agendar instruções fora do “caminho mais longo”. Os exemplos abaixo demonstram isso.

align_left

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

[14]:
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()
[14]:
../../_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

Não surpreendentemente, align_right faz o oposto de align_left. Ele escolherá t=100 no exemplo acima para começar o pulso gaussiano em D0. Esquerdo e direito são às vezes também chamados de agendamentos “assim que possível” e de “tão tarde quanto possível”, respectivamente.

[15]:
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()
[15]:
../../_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.

[16]:
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()
[16]:
../../_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.

[17]:
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()
[17]:
../../_images/tutorials_circuits_advanced_06_building_pulse_schedules_34_0.png

Phase and frequency offsets

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

[18]:
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()
[18]:
../../_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.

[19]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
/Users/thomas/opt/anaconda3/envs/qiskit-3.8/lib/python3.8/site-packages/qiskit/aqua/operators/operator_globals.py:48: DeprecationWarning: `from_label` is deprecated and will be removed no earlier than 3 months after the release date. Use Pauli(label) instead.
  X = make_immutable(PrimitiveOp(Pauli.from_label('X')))

Version Information

Qiskit SoftwareVersion
Qiskit0.23.6
Terra0.17.0
Aer0.7.5
Ignis0.5.2
Aqua0.8.2
IBM Q Provider0.11.1
System information
Python3.8.5 (default, Aug 5 2020, 03:39:04) [Clang 10.0.0 ]
OSDarwin
CPUs8
Memory (Gb)32.0
Sat Feb 27 11:04:23 2021 AST

This code is a part of Qiskit

© Copyright IBM 2017, 2021.

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.