Beispiel #1
0
    def __init__(self, n_qubit=5, period_1qb=30E-9, period_2qb=30E-9,
                 sample_rate=1.2E9, n_pts=240E3, first_delay=100E-9,
                 local_xy=True):
        # define parameters
        self.n_qubit = n_qubit
        self.period_1qb = period_1qb
        self.period_2qb = period_2qb
        self.local_xy = local_xy
        # waveform parameter
        self.sample_rate = sample_rate
        self.n_pts = n_pts
        self.first_delay = first_delay
        self.trim_to_sequence = True
        self.trim_start = False
        self.align_to_end = False
        # parameter for keeping track of current gate pulse time
        self.time_pulse = 0.0

        # waveforms
        self.wave_xy = [np.zeros(0, dtype=np.complex)
                        for n in range(MAX_QUBIT)]
        self.wave_z = [np.zeros(0) for n in range(MAX_QUBIT)]
        self.wave_gate = [np.zeros(0) for n in range(MAX_QUBIT)]
        # define pulses
        self.pulses_1qb = [Pulse() for n in range(MAX_QUBIT)]
        self.pulses_2qb = [Pulse() for n in range(MAX_QUBIT - 1)]

        # tomography
        self.perform_tomography = False
        self.tomography = Tomography()

        # cross-talk object
        self.compensate_crosstalk = False
        self.crosstalk = Crosstalk()

        # predistortion objects
        self.perform_predistortion = False
        self.predistortions = [Predistortion(n) for n in range(MAX_QUBIT)]

        # gate switch waveform
        self.generate_gate_switch = False
        self.uniform_gate = False
        self.gate_delay = 0.0
        self.gate_overlap = 20E-9
        self.minimal_gate_time = 20E-9

        # readout trig settings
        self.generate_readout_trig = False
        self.readout_delay = 0.0
        self.readout_amplitude = 1.0
        self.readout_duration = 20E-9

        # readout wave object and settings
        self.generate_readout_iq = False
        self.readout = Readout(max_qubit=MAX_QUBIT)
        self.readout_trig = np.array([], dtype=float)
        self.readout_iq = np.array([], dtype=np.complex)
Beispiel #2
0
class Sequence(object):
    """This class represents a multi-qubit control sequence

    The class supports two ways of defining pulse sequences:

    (1) Use the functions `add_single_pulse` or `add_single_gate` to add pulses
        to individual qubit waveforms at arbitrary time positions, or,

    (2) Use the function `add_gates` to add a list of pulses to all qubits. The
        pulses will be separated by a fixed pulse period.

    Attributes
    ----------
    n_qubit : int
        Number of qubits controlled by the sequence.

    period_1qb : float
        Period for single-qubit gates.

    period_2qb : float
        Period for two-qubit gates.

    local_xy : bool
        Define if qubits have local XY control lines.  If False, all control
        pulses are added to a single output waveform.

    sample_rate : float
        Sample rate of output waveforms.

    n_pts : int
        Length of output waveforms. Note that the resulting waveform may be
        different if the waveforms are trimmed.

    first_delay : float
        Position of first pulse

    trim_to_sequence : bool
        If True, waveform is trimmed to fit sequence

    trim_start : bool
        If True, waveform is trimmed at both start and end (default is just end)

    perform_tomography : bool
        If True, tomography pulses will be added to the end of the qubit xy
        control waveforms.

    perform_predistortion : bool
        If True, the control waveforms will be pre-distorted.

    generate_gate_switch  : bool
        If True, generate waveform for microwave gate switch

    uniform_gate : bool
        If True, the gate is open during the entire xy waveform

    gate_delay : float
        Delay of gate switch wave relative to the I/Q pulse

    gate_overlap : float
        Extra time before/after I/Q pulse during which the gate switch is open

    minimal_gate_time : float
        Shortest time the gate switch will stay open/closed.

    generate_readout_trig : bool
        If True, generate waveform with readout trig at the end of the waveform.

    readout_delay : float
        Readout trig delay.

    readout_amplitude : float
        Amplitude of readout trig pulse.

    readout_duration : float
        Duration of readout trig pulse.

    generate_readout_iq : bool
        If True, generate complex waveform with multi-qubit I/Q readout signals.

    compensate_crosstalk : bool
        If True, Z-control waveforms will be compensated for cross-talk.

    """

    def __init__(self, n_qubit=5, period_1qb=30E-9, period_2qb=30E-9,
                 sample_rate=1.2E9, n_pts=240E3, first_delay=100E-9,
                 local_xy=True):
        # define parameters
        self.n_qubit = n_qubit
        self.period_1qb = period_1qb
        self.period_2qb = period_2qb
        self.local_xy = local_xy
        # waveform parameter
        self.sample_rate = sample_rate
        self.n_pts = n_pts
        self.first_delay = first_delay
        self.trim_to_sequence = True
        self.trim_start = False
        self.align_to_end = False
        # parameter for keeping track of current gate pulse time
        self.time_pulse = 0.0

        # waveforms
        self.wave_xy = [np.zeros(0, dtype=np.complex)
                        for n in range(MAX_QUBIT)]
        self.wave_z = [np.zeros(0) for n in range(MAX_QUBIT)]
        self.wave_gate = [np.zeros(0) for n in range(MAX_QUBIT)]
        # define pulses
        self.pulses_1qb = [Pulse() for n in range(MAX_QUBIT)]
        self.pulses_2qb = [Pulse() for n in range(MAX_QUBIT - 1)]

        # tomography
        self.perform_tomography = False
        self.tomography = Tomography()

        # cross-talk object
        self.compensate_crosstalk = False
        self.crosstalk = Crosstalk()

        # predistortion objects
        self.perform_predistortion = False
        self.predistortions = [Predistortion(n) for n in range(MAX_QUBIT)]

        # gate switch waveform
        self.generate_gate_switch = False
        self.uniform_gate = False
        self.gate_delay = 0.0
        self.gate_overlap = 20E-9
        self.minimal_gate_time = 20E-9

        # readout trig settings
        self.generate_readout_trig = False
        self.readout_delay = 0.0
        self.readout_amplitude = 1.0
        self.readout_duration = 20E-9

        # readout wave object and settings
        self.generate_readout_iq = False
        self.readout = Readout(max_qubit=MAX_QUBIT)
        self.readout_trig = np.array([], dtype=float)
        self.readout_iq = np.array([], dtype=np.complex)


    def init_waveforms(self):
        """Initialize waveforms according to sequence settings"""
        # clear waveforms
        for n in range(self.n_qubit):
            self.wave_xy[n] = np.zeros(self.n_pts, dtype=np.complex)
            self.wave_z[n] = np.zeros(self.n_pts, dtype=float)
            self.wave_gate[n] = np.zeros(self.n_pts, dtype=float)

        # readout trig
        pts = self.n_pts if self.generate_readout_trig else 0
        self.readout_trig = np.zeros(pts, dtype=float)
        # readout i/q waveform
        pts = self.n_pts if self.generate_readout_iq else 0
        self.readout_iq = np.zeros(pts, dtype=np.complex)

        # reset gate position counter
        self.time_pulse = self.first_delay


    def generate_sequence(self, config):
        """Generate sequence by adding gates/pulses to waveforms

        Parameters
        ----------
        config : dict
            Configuration as defined by Labber driver configuration window

        """
        # this function should be overloaded by specific sequence
        pass


    def calculate_waveforms(self, config):
        """Calculate waveforms for all qubits

        The function will initialize the waveforms, generate the qubit pulse
        sequence, create gates and readout pulses, perform pre-distortion,
        and finally return the qubit control waveforms.

        Parameters
        ----------
        config : dict
            Configuration as defined by Labber driver configuration window

        Returns
        -------
        waveforms : dict with numpy arrays
            Dictionary with qubit waveforms. Depending on the sequence
            configuration, the dictionary will have the following keys:
                wave_xy : list of complex numpy arrays
                    Waveforms for qubit XY control.
                wave_z : list of numpy arrays
                    Waveforms for qubit Z control.
                wave_gate : list of numpy arrays
                    Waveforms for gating qubit XY pulses.
                readout_trig : numpy array
                    Waveform for triggering/gating qubit readout
                readout_iq : complex numpy array
                    Waveform for readout IQ control

        """
        # start by initializing the waveforms
        self.init_waveforms()

        # generate sequence
        self.generate_sequence(config)

        # add tomography
        self.add_tomography_pulses()

        # collapse all xy pulses to one waveform if no local XY control
        if not self.local_xy:
            # sum all waveforms to first one
            self.wave_xy[0] = np.sum(self.wave_xy[:self.n_qubit], 0)
            # clear other waveforms
            for n in range(1, self.n_qubit):
                self.wave_xy[n][:] = 0.0

        # cross-talk compensation
        self.perform_crosstalk_compensation()

        # read-out signals
        self.generate_readout()

        # trim waveforms, if wanted
        self.trim_waveforms()

        # microwave gate switch waveform
        self.add_microwave_gate(config)

        # I/Q waveform predistortion
        self.predistort_waveforms()

        # create and return dictionary with waveforms
        data = dict()
        data['wave_xy'] = self.wave_xy
        data['wave_z'] = self.wave_z
        data['wave_gate'] = self.wave_gate
        data['readout_trig'] = self.readout_trig
        data['readout_iq'] = self.readout_iq
        return data


    def add_single_pulse(self, qubit, pulse, t0, align_left=False):
        """Add single pulse to specified qubit waveform

        Parameters
        ----------
        qubit : int or numpy array
            Qubit number, indexed from 0. If a numpy array is given, pulses will
            be added to the specified waveform instead of the qubit waveform.

        pulse : :obj:`Pulse`
            Definition of pulse to add.

        t0 : float
            Pulse position, referenced to center of pulse.

        align_left : bool, optional
            If True, the pulse position is referenced to the left edge of the
            pulse, otherwise to the center. Default is False.

        """
        # find waveform to add pulse to
        if isinstance(qubit, np.ndarray):
            waveform = qubit
        else:
            if pulse.z_pulse:
                waveform = self.wave_z[qubit]
            else:
                waveform = self.wave_xy[qubit]
        # calculate total length of pulse
        duration = pulse.total_duration()
        # shift time to mid point if user gave start point
        if align_left:
            t0 = t0 + duration / 2
        # get the range of indices in use
        indices = np.arange(
            max(np.floor((t0 - duration / 2) * self.sample_rate), 0),
            min(np.ceil((t0 + duration / 2) * self.sample_rate), self.n_pts),
            dtype=int
        )
        # return directly if no indices
        if len(indices) == 0:
            return

        # calculate time values for the pulse indices
        t = indices / self.sample_rate
        # calculate the pulse envelope for the selected indices
        y = pulse.calculate_envelope(t0, t)

        # proceed depending on Z- or XY gate
        if pulse.z_pulse or waveform.dtype != np.complex:
            # Z pulse, add directly to Z waveform
            waveform[indices] += y

        else:
            # XY pulse, apply DRAG, if wanted
            if pulse.use_drag:
                beta = pulse.drag_coefficient * self.sample_rate
                y = y + 1j * beta * np.gradient(y)

            # single-sideband mixing, get frequency
            omega = 2 * np.pi * pulse.frequency
            # apply SSBM transform
            data_i = (y.real * np.cos(omega * t - pulse.phase) +
                      -y.imag * np.cos(omega * t - pulse.phase + np.pi / 2))
            data_q = (y.real * np.sin(omega * t - pulse.phase) +
                      -y.imag * np.sin(omega * t - pulse.phase + np.pi / 2))
            # # apply SSBM transform
            # data_i = (-y.real * np.sin(omega * t - pulse.phase) +
            #           y.imag * np.sin(omega * t - pulse.phase + np.pi / 2))
            # data_q = (y.real * np.cos(omega * t - pulse.phase) +
            #           -y.imag * np.cos(omega * t - pulse.phase + np.pi / 2))

            # store result
            waveform[indices] += (data_i + 1j * data_q)


    def add_single_gate(self, qubit, gate, t0, align_left=False):
        """Add single gate to specified qubit waveform

        Parameters
        ----------
        qubit : int
            Qubit number, indexed from 0.

        gate : :enum:`Gate`
            Definition of gate to add.

        t0 : float
            Pulse position, referenced to center of pulse.

        align_left : bool, optional
            If True, the pulse position is referenced to the left edge of the
            pulse, otherwise to the center. Default is False.

        """
        # check if one- or two-qubit gate
        if gate in ONE_QUBIT_GATES:
            # get copy of pulse to use
            pulse = copy(self.pulses_1qb[qubit])

            # scale pulse by 0.5 if pi/2
            if gate in (Gate.X2p, Gate.Y2p, Gate.X2m, Gate.Y2m):
                pulse.amplitude *= 0.5
            # rotate by 90 deg if pulse is in Y
            if gate in (Gate.Yp, Gate.Y2p, Gate.Ym, Gate.Y2m):
                pulse.phase += np.pi / 2
            # negate if negative pulse
            if gate in (Gate.Xm, Gate.X2m, Gate.Ym, Gate.Y2m):
                pulse.amplitude = -pulse.amplitude
            if gate is (Gate.I):
                pulse.amplitude = 0

            # add pulse to waveform
            self.add_single_pulse(qubit, pulse, t0, align_left=align_left)

        else:
            # two-qubit gate, get pulse
            pulse = copy(self.pulses_2qb[qubit])
            # add pulse to waveform
            self.add_single_pulse(qubit, pulse, t0, align_left=align_left)
            # TODO (simon): Update two-qubit pulse to include phase correction,
            # compensation pulses to neighboring qubits, etc.


    def add_gates(self, gates):
        """Add multiple gates to qubit waveforms

        Add multiple gates to the qubit waveform.  Pulses are added to the end
        of the sequence, with gate period set by single- and two-qubit gate
        period parameters.

        Examples
        --------
        Add three gates to a two-qubit sequence, first a positive pi-pulse
        around X to qubit 1, then a negative pi/2-pulse to qubit 2, finally
        simultaneous positive pi-pulses to qubits 1 and 2.

        >>> add_gates([[Gate.Xp,  None    ],
                       [None,     Gate.Y2m],
                       [Gate.Xp,  Gate.Xp]])

        Parameters
        ----------
        gates : list of list of :enum:`gate`
            List of lists defining gates to add. The innermost list should
            have the same length as number of qubits in the sequence.

        """
        # make sure we have correct input
        if not isinstance(gates, (list, tuple)):
            raise Exception('The input must be a list of list with gates')
        if len(gates) == 0:
            return
        if not isinstance(gates[0], (list, tuple)):
            raise Exception('The input must be a list of list with gates')
        # add gates sequence to waveforms
        for gates_qubits in gates:
            # check if any two-qubit gates
            two_qubit = np.any([g in TWO_QUBIT_GATES for g in gates_qubits])
            # pulse period may be different for two-qubi gates
            period = self.period_2qb if two_qubit else self.period_1qb
            # go through all qubits
            for n, g in enumerate(gates_qubits):
                # ignore if gate is None
                if g is None:
                    continue
                # add gate to specific qubit waveform
                self.add_single_gate(n, g, t0=self.time_pulse + period / 2.0)
            # after adding all pulses, increment current gate time
            self.time_pulse += period


    def add_tomography_pulses(self):
        """Add tomography pulses to the end of the qubit xy waveforms

        """
        if not self.perform_tomography:
            return
        # get time for adding tomograph pulse
        t = self.find_range_of_sequence()[1]
        # TODO(morten): add code to add tomography pulses
        self.tomography.add_pulses(self, t)


    def predistort_waveforms(self):
        """Add tomography pulses to the end of the qubit xy waveforms

        """
        if not self.perform_predistortion:
            return
        # go through and predistort all waveforms
        n_wave = self.n_qubit if self.local_xy else 1
        for n in range(n_wave):
            self.wave_xy[n] = self.predistortions[n].predistort(self.wave_xy[n])


    def perform_crosstalk_compensation(self):
        """Compensate for Z-control crosstalk

        """
        if not self.compensate_crosstalk:
            return
        self.wave_z = self.crosstalk.compensate(self.wave_z)


    def find_range_of_sequence(self):
        """Find and return time at start and end of gate sequence

        Returns
        -------
        times : list
            List with two elements - Time at start and end of sequence.

        """
        # disable check based on pulses, always check actual waveforms
        if False:  # self.time_pulse > self.first_delay:
            # if pulses have been added with add_gates, get from pulse period
            t0 = self.first_delay - self.period_1qb
            t1 = self.time_pulse

        else:
            # find end by searching for last non-zero element
            sum_all = np.zeros_like(self.wave_xy[0])
            for n in range(self.n_qubit):
                sum_all += np.abs(self.wave_xy[n])
                sum_all += np.abs(self.wave_z[n])
            non_zero = np.where(sum_all > self.readout_noise)[0]
            # if data is not all zero, add after last pulse
            if len(non_zero) > 0:
                t0 = max(0.0, (non_zero[0] - 1) / self.sample_rate)
                t1 = (non_zero[-1] + 1) / self.sample_rate
            else:
                t0 = 0.0
                t1 = len(sum_all) / self.sample_rate
        return [t0, t1]


    def generate_readout(self):
        """Create read-out trig and waveform signals

        """
        # get positon of readout
        if self.generate_readout_trig or self.generate_readout_iq:
            t = self.find_range_of_sequence()[1] + self.readout_delay
            i0 = int(round(t * self.sample_rate))
        # start with readout trig signal
        if self.generate_readout_trig:
            # create trig waveform directly
            i1 = min(int(round((t + self.readout_duration) * self.sample_rate)),
                     len(self.readout_trig) - 1)
            self.readout_trig[i0:i1] = self.readout_amplitude

            # # create pulse object and insert into trig waveform
            # trig = Pulse(amplitude=self.readout_amplitude,
            #              width=0.0,
            #              plateau=self.readout_duration,
            #              shape=PulseShape.SQUARE)
            # self.add_single_pulse(self.readout_trig, trig, t, align_left=True)

        # readout I/Q waveform
        if self.generate_readout_iq:
            # ignore readout timestamp if pulses are aligned to end of waveform
            if self.align_to_end:
                wave = self.readout.create_waveform(t_start=0.0)
            else:
                wave = self.readout.create_waveform(t_start=t)
            # if not matching wave sizes, simply replace initialized waveform
            if not self.readout.match_main_size:
                self.readout_iq = wave
            else:
                i1 = min(len(self.readout_iq), i0 + len(wave))
                self.readout_iq[i0:i1] = wave[:(i1 - i0)]
            # add IQ offsets
            self.readout_iq.real += self.i_offset
            self.readout_iq.imag += self.q_offset


    def add_microwave_gate(self, config):
        """Create waveform for gating microwave switch

        """
        if not self.generate_gate_switch:
            return
        n_wave = self.n_qubit if self.local_xy else 1
        # go through all waveforms
        for n, wave in enumerate(self.wave_xy[:n_wave]):
            if self.uniform_gate:
                # the uniform gate is all ones
                gate = np.ones_like(wave)
                # if creating readout trig, turn off gate during readout
                if self.generate_readout_trig:
                    gate[-int((self.readout_duration - self.gate_overlap -
                               self.gate_delay) * self.sample_rate):] = 0.0
            else:
                # non-uniform gate, find non-zero elements
                gate = np.array(np.abs(wave) > 0.0, dtype=float)
                # fix gate overlap
                n_overlap = int(np.round(self.gate_overlap * self.sample_rate))
                diff_gate = np.diff(gate)
                indx_up = np.nonzero(diff_gate > 0.0)[0]
                indx_down = np.nonzero(diff_gate < 0.0)[0]
                # add extra elements to left and right for overlap
                for indx in indx_up:
                    gate[max(0, indx - n_overlap):(indx + 1)] = 1.0
                for indx in indx_down:
                    gate[indx:(indx + n_overlap + 1)] = 1.0

                # fix gaps in gate shorter than min (look for 1>0)
                diff_gate = np.diff(gate)
                indx_up = np.nonzero(diff_gate > 0.0)[0]
                indx_down = np.nonzero(diff_gate < 0.0)[0]
                # ignore first transition if starting in zero
                if gate[0] == 0:
                    indx_up = indx_up[1:]
                n_down_up = min(len(indx_down), len(indx_up))
                len_down = indx_up[:n_down_up] - indx_down[:n_down_up]
                # find short gaps
                short_gaps = np.nonzero(len_down < (self.minimal_gate_time *
                                                    self.sample_rate))[0]
                for indx in short_gaps:
                    gate[indx_down[indx]:(1 + indx_up[indx])] = 1.0

                # shift gate in time
                n_shift = int(np.round(self.gate_delay * self.sample_rate))
                if n_shift < 0:
                    n_shift = abs(n_shift)
                    gate = np.r_[gate[n_shift:], np.zeros((n_shift,))]
                elif n_shift > 0:
                    gate = np.r_[np.zeros((n_shift,)), gate[:(-n_shift)]]
            # make sure gate starts/ends in 0
            gate[0] = 0.0
            gate[-1] = 0.0
            # store results
            self.wave_gate[n] = gate


    def trim_waveforms(self):
        """Trim waveforms to match length of sequence

        """
        if not (self.trim_to_sequence or self.align_to_end):
            return
        # find range of sequence
        (t0, t1) = self.find_range_of_sequence()
        # don't trim past extent of microwave gate and readout trig, if in use
        dt_start = 0.0
        dt_end = 0.0
        if self.generate_gate_switch:
            dt_start = min(0.0, (self.gate_delay - self.gate_overlap))
            dt_end = max(0.0, (self.gate_delay + self.gate_overlap))
        if self.generate_readout_trig:
            # add a few extra points, to ensure read-out trig doesn't end high
            dt_end = max(dt_end, self.readout_delay + self.readout_duration +
                         1.0 / self.sample_rate)
        # same thing for I/Q readout
        if self.generate_readout_iq and self.readout.match_main_size:
            dt_end = max(dt_end, self.readout_delay + self.readout.duration +
                         1.0 / self.sample_rate)
        t0 += dt_start
        t1 += dt_end

        # get indices for start/end
        i0 = max(0, int(np.floor(t0 * self.sample_rate)))
        # check if don't trim beginning of waveform
        if not self.trim_start:
            i0 = 0
        i1 = min(self.n_pts, int(np.ceil(t1 * self.sample_rate)))

        if self.align_to_end:
            # align pulses to end of waveform
            m = self.n_pts - (i1 - i0)
            for n in range(self.n_qubit):
                self.wave_xy[n] = np.r_[np.zeros(m), self.wave_xy[n][i0:i1]]
                self.wave_z[n] = np.r_[np.zeros(m), self.wave_z[n][i0:i1]]
                self.wave_gate[n] = np.r_[np.zeros(m), self.wave_gate[n][i0:i1]]
            if self.generate_readout_trig:
                self.readout_trig = np.r_[np.zeros(m), self.readout_trig[i0:i1]]
                # force readout trig to end in zero
                self.readout_trig[-1] = 0.0
            if self.generate_readout_iq and self.readout.match_main_size:
                self.readout_iq = np.r_[np.zeros(m), self.readout_iq[i0:i1]]

        else:
            # trim waveforms
            for n in range(self.n_qubit):
                self.wave_xy[n] = self.wave_xy[n][i0:i1]
                self.wave_z[n] = self.wave_z[n][i0:i1]
                self.wave_gate[n] = self.wave_gate[n][i0:i1]
            if self.generate_readout_trig:
                self.readout_trig = self.readout_trig[i0:i1]
                # force readout trig to end in zero
                self.readout_trig[-1] = 0.0
            if self.generate_readout_iq and self.readout.match_main_size:
                self.readout_iq = self.readout_iq[i0:i1]


    def set_parameters(self, config={}):
        """Set base parameters using config from from Labber driver

        Parameters
        ----------
        config : dict
            Configuration as defined by Labber driver configuration window

        """
        # sequence parameters
        d = dict(Zero=0, One=1, Two=2, Three=3, Four=4, Five=5, Six=6, Seven=7,
                 Eight=8, Nine=9)
        self.n_qubit = d[config.get('Number of qubits')]
        self.period_1qb = config.get('Pulse period, 1-QB')
        self.period_2qb = config.get('Pulse period, 2-QB')
        self.local_xy = config.get('Local XY control')

        # waveform parameters
        self.sample_rate = config.get('Sample rate')
        self.readout_noise = 0.0
        self.n_pts = int(config.get('Number of points'))
        self.first_delay = config.get('First pulse delay')
        self.trim_to_sequence = config.get('Trim waveform to sequence')
        self.trim_start = config.get('Trim both start and end')
        self.align_to_end = config.get('Align pulses to end of waveform')

        # single-qubit pulses
        for n, pulse in enumerate(self.pulses_1qb):
            # pulses are indexed from 1 in Labber
            m = n + 1
            # global parameters
            pulse.shape = PulseShape(config.get('Pulse type'))
            pulse.truncation_range = config.get('Truncation range')
            pulse.start_at_zero = config.get('Start at zero')
            pulse.use_drag = config.get('Use DRAG')
            # pulse shape
            if config.get('Uniform pulse shape'):
                pulse.width = config.get('Width')
                pulse.plateau = config.get('Plateau')
            else:
                pulse.width = config.get('Width #%d' % m)
                pulse.plateau = config.get('Plateau #%d' % m)
            # pulse-specific parameters
            pulse.amplitude = config.get('Amplitude #%d' % m)
            pulse.frequency = config.get('Frequency #%d' % m)
            pulse.drag_coefficient = config.get('DRAG scaling #%d' % m)

        # two-qubit pulses
        for n, pulse in enumerate(self.pulses_2qb):
            # pulses are indexed from 1 in Labber
            s = ' #%d%d' % (n + 1, n + 2)
            # global parameters
            pulse.shape = PulseShape(config.get('Pulse type, 2QB'))
            pulse.z_pulse = True
            if config.get('Pulse type, 2QB') == 'CZ':
                pulse.F_Terms = d[config.get('Fourier terms, 2QB')]
                if config.get('Uniform 2QB pulses'):
                    pulse.width = config.get('Width, 2QB')
                    pulse.plateau = config.get('Plateau, 2QB')
                else:
                    pulse.width = config.get('Width, 2QB' + s)
                    pulse.plateau = config.get('Plateau, 2QB')

                # Get Fourier values
                if d[config.get('Fourier terms, 2QB')] == 4 :
                    pulse.Lcoeff = np.array([config.get('L1, 2QB' + s),config.get('L2, 2QB' + s),config.get('L3, 2QB' + s),config.get('L4, 2QB' + s)])
                elif d[config.get('Fourier terms, 2QB')] == 3 :
                    pulse.Lcoeff = np.array([config.get('L1, 2QB' + s),config.get('L2, 2QB' + s),config.get('L3, 2QB' + s)])
                elif d[config.get('Fourier terms, 2QB')] == 2 :
                    pulse.Lcoeff = np.array([config.get('L1, 2QB' + s),config.get('L2, 2QB' + s)])
                elif d[config.get('Fourier terms, 2QB')] == 1 :
                    pulse.Lcoeff = np.array([config.get('L1, 2QB' + s)])

                pulse.Coupling = config.get('Coupling, 2QB' + s)
                pulse.Offset = config.get('f11-f20 initial, 2QB' + s)
                pulse.amplitude = config.get('f11-f20 final, 2QB' + s)
                pulse.dfdV = config.get('df/dV, 2QB' + s)

            else:
                pulse.truncation_range = config.get('Truncation range, 2QB')
                pulse.start_at_zero = config.get('Start at zero, 2QB')
                # pulse shape
                if config.get('Uniform 2QB pulses'):
                    pulse.width = config.get('Width, 2QB')
                    pulse.plateau = config.get('Plateau, 2QB')
                else:
                    pulse.width = config.get('Width, 2QB' + s)
                    pulse.plateau = config.get('Plateau, 2QB' + s)
                # pulse-specific parameters
                pulse.amplitude = config.get('Amplitude, 2QB' + s)

        # tomography
        self.perform_tomography = config.get('Generate tomography pulse', False)
        self.tomography.set_parameters(config)

        # predistortion
        self.perform_predistortion = config.get('Predistort waveforms', False)
        # update all predistorting objects
        for p in self.predistortions:
            p.set_parameters(config)

        # crosstalk
        self.compensate_crosstalk = config.get('Compensate cross-talk', False)
        self.crosstalk.set_parameters(config)

        # gate switch waveform
        self.generate_gate_switch = config.get('Generate gate')
        self.uniform_gate = config.get('Uniform gate')
        self.gate_delay = config.get('Gate delay')
        self.gate_overlap = config.get('Gate overlap')
        self.minimal_gate_time = config.get('Minimal gate time')

        # readout, trig settings
        self.generate_readout_trig = config.get('Generate readout trig')
        self.readout_delay = config.get('Readout delay')
        self.readout_amplitude = config.get('Readout trig amplitude')
        self.readout_duration = config.get('Readout trig duration')
        self.iq_skew = config.get('Readout IQ skew')
        self.i_offset = config.get('Readout offset - I')
        self.q_offset = config.get('Readout offset - Q')

        # readout, wave settings
        self.generate_readout_iq = config.get('Generate readout waveform')
        self.readout.set_parameters(config)