Korean
언어
English
Bengali
French
German
Japanese
Korean
Portuguese
Spanish
Tamil

참고

이 페이지는 tutorials/circuits_advanced/06_building_pulse_schedules.ipynb 에서 생성되었다.

펄스 스케줄 구성하기

펄스 게이트는 회로 게이트에 대한 낮은 레벨(low-level)의 정확한 표현을 정의한다. 하나의 연산은 다수의 낮은 레벨 명령들로 구성된 펄스 프로그램으로 구현될 수 있다. 펄스 게이트에 대해서 자세히 배우려면 이 문서 를 참고하라. 이 페이지에서는 펄스 프로그램을 어떻게 만드는지에 대해 자세히 다룬다.

참고: IBM 장치의 경우, 펄스 프로그램들이 게이트를 설명하는 서브루틴으로써 사용되고 있다. 예전에는 일부 장치들만 이 형식으로 전체 프로그램들을 받아들였지만, 이는 2021년 12월에 종료될 것이다. 다른 공급자들은 여전히 이 형식의 프로그램을 받아들일 수도 있다. 프로그램이 어떻게 사용되는지에 상관 없이, 프로그램을 작성하는 구문은 동일하다. 어떻게 하는지 읽어보라!

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

아직 명령어가 없다는 것을 알 수 있다. 이 페이지의 다음 절에서는 스케줄에 추가할 수 있는 각 명령어를 설명하고, 마지막 절에서는 다양한 정렬 컨텍스트 를 설명하며, 이 컨텍스트는 명령어가 시간 순서에 따라 상대적으로 배치되는 방법을 결정한다.

Schedule 명령어

각 명령어 유형은 고유한 피연산자 집합이 있다. 위에서 볼 수 있듯이, 이들은 명령어가 어디에 적용될 것인가를 지정하기 위해 적어도 하나의 Channel 을 포함하고 있다.

채널들(Channels) 은 제어 하드웨어에서 양자 칩까지의 신호 라인에 대한 레이블이다.

  • DriveChannel들은 일반적으로 단일 큐비트의 회전을 구동 하는데 사용되고,

  • ControlChannel들은 일반적으로 다중 큐비트 게이트 또는 조정 가능한 큐비트의 추가 드라이브 라인에 사용되고,

  • MeasureChannel들은 신호를 읽도록 하는 펄스의 전송에 한정되며,

  • AcquireChannel들은 읽은 (아날로그 신호를 디지털 신호로 바꾸어) 신호를 수집하는 디지타이저(digitizer)를 동작시키는데 사용된다.

DriveChannel들, ControlChannel들 그리고 MeasureChannel들은 모두 PulseChannel들이다. 이는 그들이 펄스의 전송 을 지원하는 반면, AcquireChannel은 수신 채널일 뿐 파형을 발생시킬 수는 없다는 뜻이다.

다음 예제들에서는 PulseChannel 을 허용하는 각 Instruction 에 대해 하나의 DriveChannel 인스턴스를 생성한다. 채널은 하나의 정수로된 index 인수를 사용한다. ControlChannel들을 제외하고 인덱스는 큐비트 레이블에 맞추어 매핑된다.

[21]:
from qiskit.pulse import DriveChannel

channel = DriveChannel(0)

이 펄스의 Schedule 은 그것이 실행되는 백엔드와는 독립적이다. 그러나 대상 백엔드를 pulse.build 에 제공함으로써 컨텍스트가 대상 백엔드를 인식하고 있는 상황에서 프로그램을 구현할 수 있다. 가능하면 백엔드를 제공하는 게 좋다. 채널 접근자 pulse.<type>_channel(<idx>) 를 사용함으로써 사용 가능한 장치 자원들만 사용하고 있는지 확인할 수 있다.

[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

가장 간단한 명령어 중 하나는 delay 이다. 이것은 제어 전자 장치가 지정된 (지속) 시간 동안 주어진 채널에 신호를 출력하지 않도록 하는 차단 명령이다. 다른 명령어의 타이밍을 조절하는 데 유용하다.

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

delay 명령어를 추가하려면 지속 시간과 채널을 전달하는데, 여기서 channelAcquireChannel 을 포함한 어떤 종류의 채널도 될 수 있다. 펄스 빌더 컨텍스트를 시작할 때 pulse.build 를 사용한다. 이것은 자동으로 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.

펄스

Pulse 는 임의의 펄스 포락선(envelope) 을 지정한다. 출력 파형의 변조 주파수와 위상은 다음에 다룰 set_frequencyshift_phase 로 제어된다.

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.

Note: 하드웨어는 다른 방식으로 구현될 수 있지만, 명령어를 분리해서 설정하면 변조 주파수의 값과 같은 명시적 정보가 손실되는 것을 피할 수 있다.

대체 텍스트

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

매개변수 펄스

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.

library 에 포함된 다른 매개변수 펄스들은 GaussianSquare, Drag, 그리고 Constant 이다.

참고: 백엔드가 매개변수 펄스를 샘플링하는 방법을 결정한다. 매개변수 펄스를 그리는 것은 가능하지만, 그려진 샘플들이 백엔드에서 실행된 것과 동일하다는 것은 보장되진 않는다.

[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

샘플로 묘사되는 펄스 파형

Waveform 은 시간 순서의 복소 진폭이나 샘플 들의 배열로 지정된 펄스 신호이다. 각 샘플은 한 주기로 발생하며 시간 간격 dt 는 백엔드가 결정한다. 프로그램의 실시간 동역학을 알고 싶다면 dt 의 값을 알아야 한다. (0부터 시작하는) \(i^{th}\) 번째 샘플은 시간이 i*dt 일 때부터 (i + 1)*dt 까지 발생하며, 큐비트 주파수로 변조된다.

[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

펄스 라이브러리 함수

제공되는 펄스 라이브러리는 일반적인 함수로부터 Waveform 을 만들기 위한 샘플링 방법을 가지고 있다.

[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

pulse 를 특정하기 위해 어떤 방법을 사용하든 play 는 같은 방식으로 스케줄에 추가된다.

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

또한, 복소수 리스트나 배열을 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

Play 명령어의 지속 시간(duration)은 Pulse 에서 가져온다: 매개변수화된 펄스의 지속 시간은 명시적으로 인자에 전달되고, Waveform 의 지속 시간은 입력 샘플의 수이다.

set_frequency

앞서 설명한 바와 같이, 출력 펄스의 파형 포락선 또한 주파수와 위상에 변조된다. 각 채널은 backend.defaults()에 나열된 기본 주파수 를 가진다.

채널의 주파수는 set_frequency 명령어를 사용해 Schedule 내에서 언제든지 갱신할 수 있다. 이는 부동소숫점(float) 타입의 frequencyPulseChannel, channel 을 입력으로 받는다. set_frequency 명령어가 적용되는 채널 안의 모든 펄스는 다른 set_frequency 명령을 만나거나 프로그램이 종료될 때까지 주어진 주파수에 의해 변조된다.

명령어는 암묵적으로 0 의 지속시간을 갖는다.

참고: 요청할 수 있는 주파수는 총 대역폭과 각 하드웨어 채널의 순간 대역폭에 의해 제한된다. 향후 이러한 정보는 backend 를 통해 보고 받을 수 있을 것이다.

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

shift_phase

shift_phase 명령어는 주파수 변조의 위상을 phase 만큼 증가시킨다. set_frequency 와 마찬가지로 이 위상 이동은 프로그램이 끝날 때까지 동일한 채널의 모든 후속 명령에 영향을 미친다. shift_phase 의 영향을 되돌리려면 음수로 된 phase 를 새로운 명령어로 전달할 수 있다.

set_frequency 와 같이, 이 명령어는 암묵적으로 0 의 지속 시간을 갖는다.

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

acquire

acquire 명령어는 읽을 데이터 수집을 촉발한다. 측정될 큐비트에 매핑되는 AcquireChannel 과, MemorySlot 또는 RegisterSlot 을 수행하는 데는 시간이 걸린다. MemorySlot 은 읽은 결과가 저장될 고전적인 메모리이다. RegisterSlot 은 빠른 피드백을 위해 읽은 결과를 저장하는 제어 전자 장치의 레지스터에 매핑된다.

acqurie 명령어는 또한 키워드 인수로써 사용자 정의 Discriminator들과 커널 Kernel들을 받을 수 있다.

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

펄스 빌더 (Pulse Builder)

여기에서는 스케줄을 작성하는 방법을 학습하고자 가장 중요한 Pulse Builder 기능을 검토한다. 이는 완전하지 않는다. 펄스 빌더를 사용하여 수행할 수 있는 작업에 대한 자세한 내용은 Pulse API 참조 를 확인하라.

정렬 컨텍스트

빌더에는 스케줄 작성 방법에 영향을 주는 정렬 컨텍스트가 있다. 컨텍스트는 다중 구조가 될 수도 있다. 펄스가 어떻게 정렬되었는지 보려면 다중 구조 밖으로 나와서 .draw () 를 사용한다.

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

빌더에는 스케줄 작성 방법에 영향을 주는 정렬 컨텍스트가 있다. 기본값은 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

D1 의 펄스에는 스케줄링의 자유도가 없다는 것을 주목하라. 두 번째 파형은 첫 번째 파형 직후에 시작된다. D0 의 펄스는 전반적인 스케줄의 지속 시간을 바꾸지 않고 t=0t=100 사이에서 언제든지 시작할 수 있다. align_left 컨텍스트는 이 펄스의 시작 시간을 t=0 로 설정하는 것이다. 이것은 마치 글 문서에서의 왼쪽 맞춤과 비슷하게 생각할 수 있다.

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)

특정 블록의 지속 시간을 알고 있는 경우, 각 명령어 사이에 동일한 지속 시간 지연을 삽입하고자 align_equispaced 를 사용할 수도 있다.

[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

이 정렬 컨텍스트는 지시사항을 병렬로 스케줄하지 않는다. 각 명령어는 이전에 추가된 명령어의 끝에서 시작된다.

[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

위상 및 주파수 오프셋

이 빌더를 사용하여 채널의 펄스 주파수나 위상에 일시적인 오프셋을 줄 수 있다.

[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

더 많은 정보를 얻을 수 있도록 펄스 API 참조 를 방문하라.

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