2.21. Instantiating Pulses: Obtaining Pulse Instances From Pulse Templates

In the previous examples, we have modelled pulses using the basic members of qupulse’s PulseTemplate class hierarchy. However, these are only templates (or classes) of pulses and may contain parameters so that they cannot be run directly on hardware (this is also the reason why we always have to provide some parameters during plotting). First, we have to instantiate a concrete pulse in a process we call instantiating. We achieve this by making use of the create_program() method and will need to provide concrete parameter values.

The example should be mostly self-contained and easy to follow, however, if you started here and don’t know what pulse templates are and how to create them, maybe it’s best to have a look at Modelling a Simple TablePulseTemplate first.

To start, let us first create a pulse template with a few parameters and two channels. ## Instantiating a TablePulse

[1]:
%matplotlib inline
from qupulse.pulses.plotting import plot
from qupulse.pulses import TablePT
template = TablePT(entries={'A': [(0, 0),
                                  ('ta', 'va', 'hold'),
                                  ('tb', 'vb', 'linear'),
                                  ('tend', 0, 'jump')],
                            'B': [(0, 0),
                                  ('ta', '-va', 'hold'),
                                  ('tb', '-vb', 'linear'),
                                  ('tend', 0, 'jump')]}, measurements=[('m', 0, 'ta'),
                                                                       ('n', 'tb', 'tend-tb')])

parameters = {'ta': 2,
              'va': 2,
              'tb': 4,
              'vb': 3,
              'tc': 5,
              'td': 11,
              'tend': 6}
_ = plot(template, parameters, sample_rate=100, show=False, plot_measurements={'m', 'n'})

../_images/examples_02CreatePrograms_1_0.png

The HardwareSetup class represents the actual hardware and interfaces to the devices in qupulse. It is thus responsible for uploading to and executing pulses on the hardware. To do so it currently expects an instantiated pulse which is represented by Loop objects. These can be obtained by plugging the desired parameters into the create_program method of your PulseTemplate object.

[2]:
program = template.create_program(parameters=parameters,
                                  channel_mapping={'A': 'A', 'B': 'B'})

print(program)
print('Defined on', program[0].waveform.defined_channels)
print(program.get_measurement_windows())
LOOP 1 times:
  ->EXEC <qupulse._program.waveforms.MultiChannelWaveform object at 0x00000000175562B0> 1 times
Defined on {'B', 'A'}
{'m': (array([0.]), array([2.])), 'n': (array([4.]), array([2.]))}

The output shows us that a simple Loop object was created which just executes a single waveform without repetitions, just as our PulseTemplate specifies. In the Loop object all parameter references from the template have been resolved and replaced by the values provided in the parameters dictionary, so this is our pulse ready to be executed on the hardware.

2.21.1. Mapping Channels and Measurements During Instantiation

The channel_mapping keyword argument allows us to rename channels or to drop them by mapping them to None. We can do the same to measurements using the measurement_mapping keyword argument.

[3]:
program = template.create_program(parameters=parameters,
                                  channel_mapping={'A': None, 'B': 'Y'},
                                  measurement_mapping={'m': 'foo', 'n': None})
print(program)
print('Defined on', program[0].waveform.defined_channels)
print(program.get_measurement_windows())
LOOP 1 times:
  ->EXEC <qupulse._program.waveforms.TableWaveform object at 0x0000000017546BE0> 1 times
Defined on {'Y'}
{'foo': (array([0.]), array([2.]))}

2.21.1.1. Instantiating Composed Pulses

Let’s have a brief look at a slightly more complex pulse. Say we want to repeat our previous pulse a few times and follow it up with a brief sine wave on each channel.

[4]:
from qupulse.pulses import FunctionPT, SequencePT, RepetitionPT, AtomicMultiChannelPT

repeated_template = RepetitionPT(template, 'n_rep')
sine_template = FunctionPT('sin_a*sin(t)', '2*3.1415')
two_channel_sine_template = AtomicMultiChannelPT(
                                (sine_template, {'default': 'A'}),
                                (sine_template, {'default': 'B'}, {'sin_a': 'sin_b'})
)
sequence_template = SequencePT(repeated_template, two_channel_sine_template)

sequence_parameters = dict(parameters) # we just copy our parameter dict from before
sequence_parameters['n_rep'] = 4       # and add a few new values for the new params from the sine wave
sequence_parameters['sin_a'] = 1
sequence_parameters['sin_b'] = 2

_ = plot(sequence_template, parameters=sequence_parameters, sample_rate=100, show=False)
sequence_program = sequence_template.create_program(parameters=sequence_parameters,
                                                    channel_mapping={'A': 'A', 'B': 'B'})
print(sequence_program)
print(sequence_program.get_measurement_windows())
LOOP 1 times:
  ->LOOP 4 times:
      ->EXEC <qupulse._program.waveforms.MultiChannelWaveform object at 0x000000001758BF60> 1 times
  ->EXEC <qupulse._program.waveforms.MultiChannelWaveform object at 0x000000001759B668> 1 times
{'m': (array([ 0.,  6., 12., 18.]), array([2., 2., 2., 2.])), 'n': (array([ 4., 10., 16., 22.]), array([2., 2., 2., 2.]))}
../_images/examples_02CreatePrograms_7_1.png

As we can see, our Loop now contains an inner Loop object which repeats a waveform four times and additionally executes another waveform. This reflects the structure of our pulse template. Note also that the single measurement window defined by our pulse template template is repeated four times as well in the Loop object, according to the number of repetitions of the corresponding pulse.

Don’t worry too much about the inner workings of the Loop objects, though. We were just taking a short look at them here. In practice it will be sufficient to just obtain them using the create_program method of PulseTemplate and pass them on to HardwareSetup when required.