Add ShiftFrequency instruction to pulse (#4390)

* Add ShiftFrequency

* Review suggestions

* Lint

* remove unwanted newlines

* lint

* add newline

* Review suggestions and bugfix

* Reno

* Hz -> GHz conversion for ShiftFrequency -> PulseQobjInstruction

* Logic fixes

* review suggestion

* review suggestions

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
SooluThomas 2020-05-15 15:03:16 -04:00 committed by GitHub
parent 6dbf9dd06b
commit d4bbaf605c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 150 additions and 15 deletions

View File

@ -137,7 +137,8 @@ from .commands import AcquireInstruction, FrameChange, PersistentValue
from .configuration import LoConfig, LoRange, Kernel, Discriminator from .configuration import LoConfig, LoRange, Kernel, Discriminator
from .exceptions import PulseError from .exceptions import PulseError
from .instruction_schedule_map import InstructionScheduleMap from .instruction_schedule_map import InstructionScheduleMap
from .instructions import Acquire, Instruction, Delay, Play, ShiftPhase, Snapshot, SetFrequency from .instructions import (Acquire, Instruction, Delay, Play, ShiftPhase, Snapshot,
SetFrequency, ShiftFrequency)
from .interfaces import ScheduleComponent from .interfaces import ScheduleComponent
from .pulse_lib import (SamplePulse, Gaussian, GaussianSquare, Drag, from .pulse_lib import (SamplePulse, Gaussian, GaussianSquare, Drag,
Constant, ConstantPulse, ParametricPulse) Constant, ConstantPulse, ParametricPulse)

View File

@ -39,6 +39,7 @@ sequence of scheduled Pulse ``Instruction`` s over many channels. ``Instruction`
Delay Delay
Play Play
SetFrequency SetFrequency
ShiftFrequency
ShiftPhase ShiftPhase
Snapshot Snapshot
@ -53,7 +54,7 @@ Abstract Classes
from .acquire import Acquire from .acquire import Acquire
from .delay import Delay from .delay import Delay
from .instruction import Instruction from .instruction import Instruction
from .frequency import SetFrequency from .frequency import SetFrequency, ShiftFrequency
from .phase import ShiftPhase from .phase import ShiftPhase
from .play import Play from .play import Play
from .snapshot import Snapshot from .snapshot import Snapshot

View File

@ -60,3 +60,34 @@ class SetFrequency(Instruction):
scheduled on. scheduled on.
""" """
return self._channel return self._channel
class ShiftFrequency(Instruction):
"""Shift the channel frequency away from the current frequency."""
def __init__(self,
frequency: float,
channel: PulseChannel,
name: Optional[str] = None):
"""Creates a new shift frequency instruction.
Args:
frequency: Frequency shift of the channel in Hz.
channel: The channel this instruction operates on.
name: Name of this set channel frequency command.
"""
self._frequency = float(frequency)
self._channel = channel
super().__init__((frequency, channel), 0, (channel,), name=name)
@property
def frequency(self) -> float:
"""Frequency shift from the set frequency."""
return self._frequency
@property
def channel(self) -> PulseChannel:
"""Return the :py:class:`~qiskit.pulse.channels.Channel` that this instruction is
scheduled on.
"""
return self._channel

View File

@ -273,6 +273,25 @@ class InstructionToQobjConverter:
} }
return self._qobj_model(**command_dict) return self._qobj_model(**command_dict)
@bind_instruction(instructions.ShiftFrequency)
def convert_shift_frequency(self, shift, instruction):
"""Return converted `ShiftFrequency`.
Args:
shift (int): Offset time.
instruction (ShiftFrequency): Shift frequency instruction.
Returns:
dict: Dictionary of required parameters.
"""
command_dict = {
'name': 'shiftf',
't0': shift+instruction.start_time,
'ch': instruction.channel.name,
'frequency': instruction.frequency / 1e9
}
return self._qobj_model(**command_dict)
@bind_instruction(instructions.ShiftPhase) @bind_instruction(instructions.ShiftPhase)
def convert_shift_phase(self, shift, instruction): def convert_shift_phase(self, shift, instruction):
"""Return converted `ShiftPhase`. """Return converted `ShiftPhase`.
@ -545,6 +564,31 @@ class QobjToInstructionConverter:
return instructions.SetFrequency(frequency, channel) << t0 return instructions.SetFrequency(frequency, channel) << t0
@bind_name('shiftf')
def convert_shift_frequency(self, instruction):
"""Return converted `ShiftFrequency`.
Args:
instruction (PulseQobjInstruction): Shift frequency qobj instruction.
Returns:
Schedule: Converted and scheduled Instruction
"""
t0 = instruction.t0
channel = self.get_channel(instruction.ch)
frequency = instruction.frequency * 1e9
if isinstance(frequency, str):
frequency_expr = parse_string_expr(frequency, partial_binding=False)
def gen_sf_schedule(*args, **kwargs):
_frequency = frequency_expr(*args, **kwargs)
return instructions.ShiftFrequency(_frequency, channel) << t0
return ParameterizedSchedule(gen_sf_schedule, parameters=frequency_expr.params)
return instructions.ShiftFrequency(frequency, channel) << t0
@bind_name('delay') @bind_name('delay')
def convert_delay(self, instruction): def convert_delay(self, instruction):
"""Return converted `Delay`. """Return converted `Delay`.

View File

@ -36,7 +36,7 @@ from qiskit.pulse.channels import (DriveChannel, ControlChannel,
from qiskit.pulse.commands import FrameChangeInstruction from qiskit.pulse.commands import FrameChangeInstruction
from qiskit.pulse import (SamplePulse, FrameChange, PersistentValue, Snapshot, Play, from qiskit.pulse import (SamplePulse, FrameChange, PersistentValue, Snapshot, Play,
Acquire, PulseError, ParametricPulse, SetFrequency, ShiftPhase, Acquire, PulseError, ParametricPulse, SetFrequency, ShiftPhase,
Instruction, ScheduleComponent) Instruction, ScheduleComponent, ShiftFrequency)
class EventsOutputChannels: class EventsOutputChannels:
@ -105,6 +105,14 @@ class EventsOutputChannels:
return self._trim(self._frequencychanges) return self._trim(self._frequencychanges)
@property
def frequencyshift(self) -> Dict[int, ShiftFrequency]:
"""Set the frequency changes."""
if self._frequencychanges is None:
self._build_waveform()
return self._trim(self._frequencychanges)
@property @property
def conditionals(self) -> Dict[int, str]: def conditionals(self) -> Dict[int, str]:
"""Get conditionals.""" """Get conditionals."""
@ -194,6 +202,8 @@ class EventsOutputChannels:
pv[time:] = 0 pv[time:] = 0
elif isinstance(command, SetFrequency): elif isinstance(command, SetFrequency):
tmp_sf = command.frequency tmp_sf = command.frequency
elif isinstance(command, ShiftFrequency):
tmp_sf = command.frequency
elif isinstance(command, Snapshot): elif isinstance(command, Snapshot):
self._snapshots[time] = command.name self._snapshots[time] = command.name
if tmp_fc != 0: if tmp_fc != 0:

View File

@ -0,0 +1,10 @@
---
features:
- |
The :py:class:`~qiskit.pulse.instructions.ShiftFrequency` instruction allows users
to shift the frequency from the set frequency. For example::
sched += ShiftFrequency(-340e6, DriveChannel(0))
In this example, all the pulses applied to ``DriveChannel(0)`` after the
``ShiftFrequency`` command will have the envelope a frequency decremented by 340MHz.

View File

@ -20,7 +20,7 @@ import numpy as np
from qiskit.pulse import (Play, SamplePulse, ShiftPhase, Instruction, SetFrequency, Acquire, from qiskit.pulse import (Play, SamplePulse, ShiftPhase, Instruction, SetFrequency, Acquire,
pulse_lib, Snapshot, Delay, Gaussian, Drag, GaussianSquare, Constant, pulse_lib, Snapshot, Delay, Gaussian, Drag, GaussianSquare, Constant,
functional_pulse) functional_pulse, ShiftFrequency)
from qiskit.pulse.channels import (MemorySlot, RegisterSlot, DriveChannel, AcquireChannel, from qiskit.pulse.channels import (MemorySlot, RegisterSlot, DriveChannel, AcquireChannel,
SnapshotChannel, MeasureChannel) SnapshotChannel, MeasureChannel)
from qiskit.pulse.commands import PersistentValue, PulseInstruction from qiskit.pulse.commands import PersistentValue, PulseInstruction
@ -665,6 +665,7 @@ class TestScheduleFilter(BaseTestSchedule):
sched = sched.insert(10, Play(lp0, self.config.drive(1))) sched = sched.insert(10, Play(lp0, self.config.drive(1)))
sched = sched.insert(30, ShiftPhase(-1.57, self.config.drive(0))) sched = sched.insert(30, ShiftPhase(-1.57, self.config.drive(0)))
sched = sched.insert(40, SetFrequency(8.0, self.config.drive(0))) sched = sched.insert(40, SetFrequency(8.0, self.config.drive(0)))
sched = sched.insert(50, ShiftFrequency(4.0e6, self.config.drive(0)))
for i in range(2): for i in range(2):
sched = sched.insert(60, Acquire(5, self.config.acquire(i), MemorySlot(i))) sched = sched.insert(60, Acquire(5, self.config.acquire(i), MemorySlot(i)))
sched = sched.insert(90, Play(lp0, self.config.drive(0))) sched = sched.insert(90, Play(lp0, self.config.drive(0)))
@ -686,22 +687,30 @@ class TestScheduleFilter(BaseTestSchedule):
for _, inst in no_pulse_and_fc.instructions: for _, inst in no_pulse_and_fc.instructions:
self.assertFalse(isinstance(inst, (Play, ShiftPhase))) self.assertFalse(isinstance(inst, (Play, ShiftPhase)))
self.assertEqual(len(only_pulse_and_fc.instructions), 4) self.assertEqual(len(only_pulse_and_fc.instructions), 4)
self.assertEqual(len(no_pulse_and_fc.instructions), 3) self.assertEqual(len(no_pulse_and_fc.instructions), 4)
# test on ShiftPhase # test on ShiftPhase
only_fc, no_fc = \ only_fc, no_fc = \
self._filter_and_test_consistency(sched, instruction_types={ShiftPhase}) self._filter_and_test_consistency(sched, instruction_types={ShiftPhase})
self.assertEqual(len(only_fc.instructions), 1) self.assertEqual(len(only_fc.instructions), 1)
self.assertEqual(len(no_fc.instructions), 6) self.assertEqual(len(no_fc.instructions), 7)
# test on SetFrequency # test on SetFrequency
only_sf, no_sf = \ only_setf, no_setf = self._filter_and_test_consistency(
self._filter_and_test_consistency(sched, sched, instruction_types=[SetFrequency])
instruction_types=[SetFrequency]) for _, inst in only_setf.instructions:
for _, inst in only_sf.instructions:
self.assertTrue(isinstance(inst, SetFrequency)) self.assertTrue(isinstance(inst, SetFrequency))
self.assertEqual(len(only_sf.instructions), 1) self.assertEqual(len(only_setf.instructions), 1)
self.assertEqual(len(no_sf.instructions), 6) self.assertEqual(len(no_setf.instructions), 7)
# test on ShiftFrequency
only_shiftf, no_shiftf = \
self._filter_and_test_consistency(sched,
instruction_types=[ShiftFrequency])
for _, inst in only_shiftf.instructions:
self.assertTrue(isinstance(inst, ShiftFrequency))
self.assertEqual(len(only_shiftf.instructions), 1)
self.assertEqual(len(no_shiftf.instructions), 7)
def test_filter_intervals(self): def test_filter_intervals(self):
"""Test filtering on intervals.""" """Test filtering on intervals."""

View File

@ -23,7 +23,7 @@ from qiskit.qobj.converters import (InstructionToQobjConverter, QobjToInstructio
LoConfigConverter) LoConfigConverter)
from qiskit.pulse.commands import (SamplePulse, FrameChange, PersistentValue, Snapshot, Acquire, from qiskit.pulse.commands import (SamplePulse, FrameChange, PersistentValue, Snapshot, Acquire,
Gaussian, GaussianSquare, Constant, Drag) Gaussian, GaussianSquare, Constant, Drag)
from qiskit.pulse.instructions import ShiftPhase, SetFrequency, Play, Delay from qiskit.pulse.instructions import ShiftPhase, SetFrequency, Play, Delay, ShiftFrequency
from qiskit.pulse.channels import (DriveChannel, ControlChannel, MeasureChannel, AcquireChannel, from qiskit.pulse.channels import (DriveChannel, ControlChannel, MeasureChannel, AcquireChannel,
MemorySlot, RegisterSlot) MemorySlot, RegisterSlot)
from qiskit.pulse.schedule import ParameterizedSchedule, Schedule from qiskit.pulse.schedule import ParameterizedSchedule, Schedule
@ -157,6 +157,20 @@ class TestInstructionToQobjConverter(QiskitTestCase):
self.assertEqual(converter(0, instruction), valid_qobj) self.assertEqual(converter(0, instruction), valid_qobj)
def test_shift_frequency(self):
"""Test converted qobj from ShiftFrequency."""
converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2)
instruction = ShiftFrequency(8.0e9, DriveChannel(0))
valid_qobj = PulseQobjInstruction(
name='shiftf',
ch='d0',
t0=0,
frequency=8.0
)
self.assertEqual(converter(0, instruction), valid_qobj)
def test_persistent_value(self): def test_persistent_value(self):
"""Test converted qobj from PersistentValueInstruction.""" """Test converted qobj from PersistentValueInstruction."""
converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2) converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2)
@ -301,7 +315,7 @@ class TestQobjToInstructionConverter(QiskitTestCase):
self.assertEqual(converted_instruction.instructions[0][-1], instruction) self.assertEqual(converted_instruction.instructions[0][-1], instruction)
def test_set_frequency(self): def test_set_frequency(self):
"""Test converted qobj from FrameChangeInstruction.""" """Test converted qobj from SetFrequency."""
instruction = SetFrequency(8.0e9, DriveChannel(0)) instruction = SetFrequency(8.0e9, DriveChannel(0))
qobj = PulseQobjInstruction(name='setf', ch='d0', t0=0, frequency=8.0) qobj = PulseQobjInstruction(name='setf', ch='d0', t0=0, frequency=8.0)
@ -312,6 +326,18 @@ class TestQobjToInstructionConverter(QiskitTestCase):
self.assertEqual(converted_instruction.instructions[0][-1], instruction) self.assertEqual(converted_instruction.instructions[0][-1], instruction)
self.assertTrue('frequency' in qobj.to_dict()) self.assertTrue('frequency' in qobj.to_dict())
def test_shift_frequency(self):
"""Test converted qobj from ShiftFrequency."""
instruction = ShiftFrequency(8.0e9, DriveChannel(0))
qobj = PulseQobjInstruction(name='shiftf', ch='d0', t0=0, frequency=8.0)
converted_instruction = self.converter(qobj)
self.assertEqual(converted_instruction.start_time, 0)
self.assertEqual(converted_instruction.duration, 0)
self.assertEqual(converted_instruction.instructions[0][-1], instruction)
self.assertTrue('frequency' in qobj.to_dict())
def test_delay(self): def test_delay(self):
"""Test converted qobj from Delay.""" """Test converted qobj from Delay."""
instruction = Delay(10, DriveChannel(0)) instruction = Delay(10, DriveChannel(0))

View File

@ -232,6 +232,7 @@ class TestPulseQobj(QiskitTestCase):
PulseQobjInstruction(name='pv', t0=10, ch='d0', val=0.1 + 0.0j), PulseQobjInstruction(name='pv', t0=10, ch='d0', val=0.1 + 0.0j),
PulseQobjInstruction(name='pv', t0=10, ch='d0', val='P1'), PulseQobjInstruction(name='pv', t0=10, ch='d0', val='P1'),
PulseQobjInstruction(name='setf', t0=10, ch='d0', frequency=8.0), PulseQobjInstruction(name='setf', t0=10, ch='d0', frequency=8.0),
PulseQobjInstruction(name='shiftf', t0=10, ch='d0', frequency=4.0),
PulseQobjInstruction(name='acquire', t0=15, duration=5, PulseQobjInstruction(name='acquire', t0=15, duration=5,
qubits=[0], memory_slot=[0], qubits=[0], memory_slot=[0],
kernels=[ kernels=[
@ -266,6 +267,7 @@ class TestPulseQobj(QiskitTestCase):
{'name': 'pv', 't0': 10, 'ch': 'd0', 'val': 0.1+0j}, {'name': 'pv', 't0': 10, 'ch': 'd0', 'val': 0.1+0j},
{'name': 'pv', 't0': 10, 'ch': 'd0', 'val': 'P1'}, {'name': 'pv', 't0': 10, 'ch': 'd0', 'val': 'P1'},
{'name': 'setf', 't0': 10, 'ch': 'd0', 'frequency': 8.0}, {'name': 'setf', 't0': 10, 'ch': 'd0', 'frequency': 8.0},
{'name': 'shiftf', 't0': 10, 'ch': 'd0', 'frequency': 4.0},
{'name': 'acquire', 't0': 15, 'duration': 5, {'name': 'acquire', 't0': 15, 'duration': 5,
'qubits': [0], 'memory_slot': [0], 'qubits': [0], 'memory_slot': [0],
'kernels': [{'name': 'boxcar', 'kernels': [{'name': 'boxcar',

View File

@ -23,7 +23,7 @@ from qiskit.pulse import pulse_lib
from qiskit.pulse.channels import (DriveChannel, MeasureChannel, ControlChannel, AcquireChannel, from qiskit.pulse.channels import (DriveChannel, MeasureChannel, ControlChannel, AcquireChannel,
MemorySlot, RegisterSlot) MemorySlot, RegisterSlot)
from qiskit.pulse.commands import FrameChange from qiskit.pulse.commands import FrameChange
from qiskit.pulse.instructions import SetFrequency, Play, Acquire, Delay, Snapshot from qiskit.pulse.instructions import SetFrequency, Play, Acquire, Delay, Snapshot, ShiftFrequency
from qiskit.pulse.schedule import Schedule from qiskit.pulse.schedule import Schedule
from qiskit.tools.visualization import HAS_MATPLOTLIB from qiskit.tools.visualization import HAS_MATPLOTLIB
from qiskit.visualization import pulse_drawer from qiskit.visualization import pulse_drawer
@ -68,6 +68,7 @@ class TestPulseVisualizationImplementation(QiskitVisualizationTestCase):
ControlChannel(0))) ControlChannel(0)))
sched = sched.insert(60, FrameChange(phase=-1.57)(DriveChannel(0))) sched = sched.insert(60, FrameChange(phase=-1.57)(DriveChannel(0)))
sched = sched.insert(60, SetFrequency(8.0, DriveChannel(0))) sched = sched.insert(60, SetFrequency(8.0, DriveChannel(0)))
sched = sched.insert(70, ShiftFrequency(4.0e6, DriveChannel(0)))
sched = sched.insert(30, gp1(DriveChannel(1))) sched = sched.insert(30, gp1(DriveChannel(1)))
sched = sched.insert(60, gp0(ControlChannel(0))) sched = sched.insert(60, gp0(ControlChannel(0)))
sched = sched.insert(60, gs0(MeasureChannel(0))) sched = sched.insert(60, gs0(MeasureChannel(0)))