2.6. Mapping with the MappingPulseTemplate

We will now have a look on how to remap parameters, channel ids and measurements. The definition of measurements is illustrated here. The MappingPulseTemplate

First we will have a look at the mapping of parameters:

In [1]:
from qctoolkit.pulses import MappingPT, FunctionPT, SequencePT, AtomicMultiChannelPT

sine = FunctionPT('a*sin(omega*t)', 't_duration')

my_parameter_mapping = dict(t_duration='2*pi/omega', omega='omega', a='a')

single_period_sine = MappingPT(sine, parameter_mapping=my_parameter_mapping)

print(single_period_sine.duration)
print(single_period_sine.parameter_names)
2*pi/omega
{'a', 'omega'}

Notice that we had to give mappings for all parameters, not only for the ones which changed. If we omit some of the encapsulated pulse tempaltes parameters an MissingMappingException is raised. This is done to enforce active thinking.

You can, however, allow partial parameter mappings by passing allow_partial_paramter_mappings=True to the constructor.

In [2]:
partial_parameter_mapping = dict(t_duration='2*pi/omega')
try:
    single_period_sine = MappingPT(sine, parameter_mapping=partial_parameter_mapping)
except Exception as exception:
    print(type(exception).__name__, ':',  exception)
print('')


single_period_sine = MappingPT(sine, parameter_mapping=partial_parameter_mapping, allow_partial_parameter_mapping=True)
print(single_period_sine.duration)
print(single_period_sine.parameter_names)
MissingMappingException : The template <qctoolkit.pulses.function_pulse_template.FunctionPulseTemplate object at 0x0000025E3A5DF4A8> needs a mapping function for parameter(s) {'a', 'omega'}

2*pi/omega
{'a', 'omega'}

2.6.1. Mapping of channel ids and measurement names:

Sometimes it is necessary to rename channels or measurements. Here we see a case where we want to play a sine and a cosine in parallel by using the AtomicMultiChannelPulseTemplate. Of course, this doesn’t work as both pulses are by default defined on the ‘default’ channel.

In [3]:
sine_measurements = [('M', 't_duration/2', 't_duration')]
sine = FunctionPT('a*sin(omega*t)', 't_duration', measurements=sine_measurements)

cos_measurements = [('M', 0, 't_duration/2')]
cos = FunctionPT('a*cos(omega*t)', 't_duration', measurements=cos_measurements)

try:
    both = AtomicMultiChannelPT(sine, cos)
except Exception as exception:
    print(type(exception).__name__, ':', exception)
ChannelMappingException : Channel <default> is defined in subtemplate 1 and subtemplate 2

The solution is to use the MappingPT and rename the channels as we see in the next cell. Additionally, we want to distinguish between the measurements, so we rename them, too.

In [4]:
cos_channel_mapping = dict(default='cos_channel')
cos_measurement_mapping = dict(M='M_cos')
remapped_cos = MappingPT(cos, channel_mapping=cos_channel_mapping, measurement_mapping=cos_measurement_mapping)
print('remapped_cos channels:', remapped_cos.defined_channels)
print('remapped_cos measurements:', remapped_cos.measurement_names)
print()

sine_channel_mapping = dict(default='sin_channel')
sine_measurement_mapping = dict(M='M_sin')
remapped_sine = MappingPT(sine, measurement_mapping=sine_measurement_mapping, channel_mapping=sine_channel_mapping)
print('remapped_sine channels:', remapped_sine.defined_channels)
print('remapped_sine measurements:', remapped_sine.measurement_names)
print()

both = AtomicMultiChannelPT(remapped_sine, remapped_cos)
print(both.defined_channels)
print(both.measurement_names)
remapped_cos channels: {'cos_channel'}
remapped_cos measurements: {'M_cos'}

remapped_sine channels: {'sin_channel'}
remapped_sine measurements: {'M_sin'}

{'cos_channel', 'sin_channel'}
{'M_cos', 'M_sin'}

2.6.2. Automatically created mapping templates

Besides the explicit usage of the template it is also used implicitly in some cases. All implicit uses make use of the static member function MappingPulseTemplate.from_tuple. This ‘constructor’ automatically decides which mapping belongs to which entity.

In [5]:
auto_mapped = MappingPT.from_tuple((sine, sine_measurement_mapping))
print('channels:', auto_mapped.defined_channels)
print('measurements', auto_mapped.measurement_names)
print('parameters', auto_mapped.parameter_names)
print()

auto_mapped = MappingPT.from_tuple((sine, sine_measurement_mapping, partial_parameter_mapping))
print('channels:', auto_mapped.defined_channels)
print('measurements', auto_mapped.measurement_names)
print('parameters', auto_mapped.parameter_names)
print()
channels: {'default'}
measurements {'M_sin'}
parameters {'t_duration', 'a', 'omega'}

channels: {'default'}
measurements {'M_sin'}
parameters {'a', 'omega'}

In many cases, you do not need to create the MappingPT yourself. Most PulseParameters accept a mapping tuple like the ones used in the last cell. We could create our combined pulse also by using this implicit conversion:

In [6]:
both_implicit = AtomicMultiChannelPT((sine, sine_channel_mapping, sine_measurement_mapping),
                                     (cos, cos_measurement_mapping, cos_channel_mapping))
print(both_implicit.defined_channels)
print(both_implicit.measurement_names)
{'cos_channel', 'sin_channel'}
{'M_cos', 'M_sin'}