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 .exceptions import PulseError
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 .pulse_lib import (SamplePulse, Gaussian, GaussianSquare, Drag,
Constant, ConstantPulse, ParametricPulse)

View File

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

View File

@ -60,3 +60,34 @@ class SetFrequency(Instruction):
scheduled on.
"""
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)
@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)
def convert_shift_phase(self, shift, instruction):
"""Return converted `ShiftPhase`.
@ -545,6 +564,31 @@ class QobjToInstructionConverter:
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')
def convert_delay(self, instruction):
"""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 import (SamplePulse, FrameChange, PersistentValue, Snapshot, Play,
Acquire, PulseError, ParametricPulse, SetFrequency, ShiftPhase,
Instruction, ScheduleComponent)
Instruction, ScheduleComponent, ShiftFrequency)
class EventsOutputChannels:
@ -105,6 +105,14 @@ class EventsOutputChannels:
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
def conditionals(self) -> Dict[int, str]:
"""Get conditionals."""
@ -194,6 +202,8 @@ class EventsOutputChannels:
pv[time:] = 0
elif isinstance(command, SetFrequency):
tmp_sf = command.frequency
elif isinstance(command, ShiftFrequency):
tmp_sf = command.frequency
elif isinstance(command, Snapshot):
self._snapshots[time] = command.name
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,
pulse_lib, Snapshot, Delay, Gaussian, Drag, GaussianSquare, Constant,
functional_pulse)
functional_pulse, ShiftFrequency)
from qiskit.pulse.channels import (MemorySlot, RegisterSlot, DriveChannel, AcquireChannel,
SnapshotChannel, MeasureChannel)
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(30, ShiftPhase(-1.57, 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):
sched = sched.insert(60, Acquire(5, self.config.acquire(i), MemorySlot(i)))
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:
self.assertFalse(isinstance(inst, (Play, ShiftPhase)))
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
only_fc, no_fc = \
self._filter_and_test_consistency(sched, instruction_types={ShiftPhase})
self.assertEqual(len(only_fc.instructions), 1)
self.assertEqual(len(no_fc.instructions), 6)
self.assertEqual(len(no_fc.instructions), 7)
# test on SetFrequency
only_sf, no_sf = \
self._filter_and_test_consistency(sched,
instruction_types=[SetFrequency])
for _, inst in only_sf.instructions:
only_setf, no_setf = self._filter_and_test_consistency(
sched, instruction_types=[SetFrequency])
for _, inst in only_setf.instructions:
self.assertTrue(isinstance(inst, SetFrequency))
self.assertEqual(len(only_sf.instructions), 1)
self.assertEqual(len(no_sf.instructions), 6)
self.assertEqual(len(only_setf.instructions), 1)
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):
"""Test filtering on intervals."""

View File

@ -23,7 +23,7 @@ from qiskit.qobj.converters import (InstructionToQobjConverter, QobjToInstructio
LoConfigConverter)
from qiskit.pulse.commands import (SamplePulse, FrameChange, PersistentValue, Snapshot, Acquire,
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,
MemorySlot, RegisterSlot)
from qiskit.pulse.schedule import ParameterizedSchedule, Schedule
@ -157,6 +157,20 @@ class TestInstructionToQobjConverter(QiskitTestCase):
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):
"""Test converted qobj from PersistentValueInstruction."""
converter = InstructionToQobjConverter(PulseQobjInstruction, meas_level=2)
@ -301,7 +315,7 @@ class TestQobjToInstructionConverter(QiskitTestCase):
self.assertEqual(converted_instruction.instructions[0][-1], instruction)
def test_set_frequency(self):
"""Test converted qobj from FrameChangeInstruction."""
"""Test converted qobj from SetFrequency."""
instruction = SetFrequency(8.0e9, DriveChannel(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.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):
"""Test converted qobj from Delay."""
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='P1'),
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,
qubits=[0], memory_slot=[0],
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': 'P1'},
{'name': 'setf', 't0': 10, 'ch': 'd0', 'frequency': 8.0},
{'name': 'shiftf', 't0': 10, 'ch': 'd0', 'frequency': 4.0},
{'name': 'acquire', 't0': 15, 'duration': 5,
'qubits': [0], 'memory_slot': [0],
'kernels': [{'name': 'boxcar',

View File

@ -23,7 +23,7 @@ from qiskit.pulse import pulse_lib
from qiskit.pulse.channels import (DriveChannel, MeasureChannel, ControlChannel, AcquireChannel,
MemorySlot, RegisterSlot)
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.tools.visualization import HAS_MATPLOTLIB
from qiskit.visualization import pulse_drawer
@ -68,6 +68,7 @@ class TestPulseVisualizationImplementation(QiskitVisualizationTestCase):
ControlChannel(0)))
sched = sched.insert(60, FrameChange(phase=-1.57)(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(60, gp0(ControlChannel(0)))
sched = sched.insert(60, gs0(MeasureChannel(0)))