Skip to content

Commit

Permalink
Add ShiftFrequency instruction to pulse (#4390)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
SooluThomas and mergify[bot] authored May 15, 2020
1 parent 6dbf9dd commit d4bbaf6
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 15 deletions.
3 changes: 2 additions & 1 deletion qiskit/pulse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@
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)
Expand Down
3 changes: 2 additions & 1 deletion qiskit/pulse/instructions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
Delay
Play
SetFrequency
ShiftFrequency
ShiftPhase
Snapshot
Expand All @@ -53,7 +54,7 @@
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
31 changes: 31 additions & 0 deletions qiskit/pulse/instructions/frequency.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,34 @@ def channel(self) -> PulseChannel:
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
44 changes: 44 additions & 0 deletions qiskit/qobj/converters/pulse_instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,25 @@ def convert_set_frequency(self, shift, instruction):
}
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`.
Expand Down Expand Up @@ -545,6 +564,31 @@ def gen_sf_schedule(*args, **kwargs):

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`.
Expand Down
12 changes: 11 additions & 1 deletion qiskit/visualization/pulse/matplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
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:
Expand Down Expand Up @@ -105,6 +105,14 @@ def frequencychanges(self) -> Dict[int, SetFrequency]:

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."""
Expand Down Expand Up @@ -194,6 +202,8 @@ def _build_waveform(self):
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:
Expand Down
10 changes: 10 additions & 0 deletions releasenotes/notes/implement-shift-frequency-46d4ea16d8be2f58.yaml
Original file line number Diff line number Diff line change
@@ -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.
27 changes: 18 additions & 9 deletions test/python/pulse/test_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

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
Expand Down Expand Up @@ -665,6 +665,7 @@ def test_filter_inst_types(self):
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)))
Expand All @@ -686,22 +687,30 @@ def test_filter_inst_types(self):
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."""
Expand Down
30 changes: 28 additions & 2 deletions test/python/qobj/test_pulse_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
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
Expand Down Expand Up @@ -157,6 +157,20 @@ def test_set_frequency(self):

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)
Expand Down Expand Up @@ -301,7 +315,7 @@ def test_frame_change(self):
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)
Expand All @@ -312,6 +326,18 @@ def test_set_frequency(self):
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))
Expand Down
2 changes: 2 additions & 0 deletions test/python/qobj/test_qobj.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def setUp(self):
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=[
Expand Down Expand Up @@ -266,6 +267,7 @@ def setUp(self):
{'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',
Expand Down
3 changes: 2 additions & 1 deletion test/python/visualization/test_pulse_visualization_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
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
Expand Down Expand Up @@ -68,6 +68,7 @@ def sample_schedule(self):
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)))
Expand Down

0 comments on commit d4bbaf6

Please sign in to comment.