示例#1
0
def align(label, pulse, blockLength, alignment, cutoff=12):
    # check for composite pulses
    if hasattr(pulse, 'pulses'):
        entries = [LLWaveform(p) for p in pulse.pulses]
        entries[0].label = label
        shapes = [p.shape for p in pulse.pulses]
    else:
        entries = [LLWaveform(pulse, label)]
        shapes = [pulse.shape]
    padLength = blockLength - pulse.length
    if padLength == 0:
        # no padding element required
        return shapes, entries
    if (padLength < cutoff) and (alignment == "left" or alignment == "right"):
        # pad the first/last shape on one side
        if alignment == "left":
            shapes[-1] = np.hstack((shapes[-1], np.zeros(padLength)))
            entries[-1].key = PatternUtils.hash_pulse(shapes[-1])
        else:  #right alignment
            shapes[0] = np.hstack((np.zeros(padLength), shapes[0]))
            entries[0].key = PatternUtils.hash_pulse(shapes[0])
    elif (padLength < 2 * cutoff and alignment == "center"):
        # pad the both sides of the shape(s)
        if len(shapes) == 1:
            shapes[0] = np.hstack(
                (np.zeros(np.floor(padLength / 2)), shapes[0],
                 np.zeros(np.ceil(padLength / 2))))
            entries[0].key = PatternUtils.hash_pulse(shapes[0])
        else:
            shapes[0] = np.hstack(
                (np.zeros(np.floor(padLength / 2)), shapes[0]))
            shapes[-1] = np.hstack(
                (np.zeroes(np.ceil(padLength / 2)), shapes[-1]))
            entries[0].key = PatternUtils.hash_pulse(shapes[0])
            entries[-1].key = PatternUtils.hash_pulse(shapes[-1])
    elif padLength == blockLength:
        #Here we have a zero-length sequence which just needs to be expanded
        entries[0].key = PatternUtils.TAZKey
        entries[0].length = blockLength
        shapes = [np.zeros(1, dtype=np.complex)]
    else:
        #split the entry into the shape and one or more TAZ
        if alignment == "left":
            padEntry = create_padding_LL(padLength)
            entries = entries + [padEntry]
        elif alignment == "right":
            padEntry = create_padding_LL(padLength)
            entries = [padEntry] + entries
        else:
            padEntry1 = create_padding_LL(np.floor(padLength / 2))
            padEntry2 = create_padding_LL(np.ceil(padLength / 2))
            entries = [padEntry1] + entries + [padEntry2]
    return shapes, entries
示例#2
0
def align(label, pulse, blockLength, alignment, cutoff=12):
    # check for composite pulses
    if hasattr(pulse, 'pulses'):
        entries = [LLWaveform(p) for p in pulse.pulses]
        entries[0].label = label
        shapes = [p.shape for p in pulse.pulses]
    else:
        entries = [LLWaveform(pulse, label)]
        shapes = [pulse.shape]
    padLength = blockLength - pulse.length
    if padLength == 0:
        # no padding element required
        return shapes, entries
    if (padLength < cutoff) and (alignment == "left" or alignment == "right"):
        # pad the first/last shape on one side
        if alignment == "left":
            shapes[-1] = np.hstack((shapes[-1], np.zeros(padLength)))
            entries[-1].key = PatternUtils.hash_pulse(shapes[-1])
        else: #right alignment
            shapes[0] = np.hstack((np.zeros(padLength), shapes[0]))
            entries[0].key = PatternUtils.hash_pulse(shapes[0])
    elif (padLength < 2*cutoff and alignment == "center"):
        # pad the both sides of the shape(s)
        if len(shapes) == 1:
            shapes[0] = np.hstack(( np.zeros(np.floor(padLength/2)), shapes[0], np.zeros(np.ceil(padLength/2)) ))
            entries[0].key = PatternUtils.hash_pulse(shapes[0])
        else:
            shapes[0] = np.hstack(( np.zeros(np.floor(padLength/2)), shapes[0]))
            shapes[-1] = np.hstack(( np.zeroes(np.ceil(padLength/2)), shapes[-1]))
            entries[0].key = PatternUtils.hash_pulse(shapes[0])
            entries[-1].key = PatternUtils.hash_pulse(shapes[-1])
    elif padLength == blockLength:
        #Here we have a zero-length sequence which just needs to be expanded
        entries[0].key = PatternUtils.TAZKey
        entries[0].length = blockLength
        shapes = [np.zeros(1, dtype=np.complex)]
    else:
        #split the entry into the shape and one or more TAZ
        if alignment == "left":
            padEntry = create_padding_LL(padLength)
            entries = entries + [padEntry]
        elif alignment == "right":
            padEntry = create_padding_LL(padLength)
            entries = [padEntry] + entries
        else:
            padEntry1 = create_padding_LL(np.floor(padLength/2))
            padEntry2 = create_padding_LL(np.ceil(padLength/2))
            entries = [padEntry1] + entries + [padEntry2]
    return shapes, entries
示例#3
0
def channel_delay_map(awgData):
    chanDelays = {}
    # loop through all used IQkeys
    for IQkey in [awgName + '-' + chanName[2:] for awgName, awg in awgData.items() for chanName in awg.keys()]:
        chan = channelLib[IQkey]
        chanDelays[IQkey] = chan.delay + chan.AWG.delay
    return PatternUtils.normalize_delays(chanDelays)
示例#4
0
def merge_channels(linkLists, wfLib, channels):
    chan = channels[0]
    newLinkList = [[] for _ in range(len(linkLists[chan]))]
    newWfLib = {}
    for ct, segment in enumerate(newLinkList):
        entryIterators = [iter(linkLists[ch][ct]) for ch in channels]
        while True:
            try:
                entries = [e.next() for e in entryIterators]
                # control flow on any channel should pass thru
                if any(isinstance(e, ControlFlow.ControlInstruction) for e in entries):
                    # for the moment require uniform control flow so that we
                    # can pull from the first channel
                    assert all(e == entries[0] for e in entries), "Non-uniform control flow"
                    segment.append(entries[0])
                    continue
                # at this point we have at least one waveform instruction
                blocklength = pull_uniform_entries(entries, entryIterators, wfLib, channels)
                newentry = copy(entries[0])
                newentry.length = blocklength
                # sum waveforms
                wfnew = reduce(operator.add, [wfLib[channel][e.key] for channel, e in zip(channels, entries)])
                newentry.key = PatternUtils.hash_pulse(wfnew)
                newWfLib[newentry.key] = wfnew
                segment.append(newentry)
            except StopIteration:
                break
    return newLinkList, newWfLib
示例#5
0
def merge_channels(linkLists, wfLib, channels):
    chan = channels[0]
    newLinkList = [[] for _ in range(len(linkLists[chan]))]
    newWfLib = {}
    for ct, segment in enumerate(newLinkList):
        entryIterators = [iter(linkLists[ch][ct]) for ch in channels]
        while True:
            try:
                entries = [e.next() for e in entryIterators]
                # control flow on any channel should pass thru
                if any(
                        isinstance(e, ControlFlow.ControlInstruction)
                        for e in entries):
                    # for the moment require uniform control flow so that we
                    # can pull from the first channel
                    assert all(e == entries[0]
                               for e in entries), "Non-uniform control flow"
                    segment.append(entries[0])
                    continue
                # at this point we have at least one waveform instruction
                blocklength = pull_uniform_entries(entries, entryIterators,
                                                   wfLib, channels)
                newentry = copy(entries[0])
                newentry.length = blocklength
                # sum waveforms
                wfnew = reduce(operator.add, [
                    wfLib[channel][e.key]
                    for channel, e in zip(channels, entries)
                ])
                newentry.key = PatternUtils.hash_pulse(wfnew)
                newWfLib[newentry.key] = wfnew
                segment.append(newentry)
            except StopIteration:
                break
    return newLinkList, newWfLib
示例#6
0
def channel_delay_map(awgData):
    chanDelays = {}
    # loop through all used IQkeys
    for IQkey in [
            awgName + '-' + chanName[2:] for awgName, awg in awgData.items()
            for chanName in awg.keys()
    ]:
        chan = channelLib[IQkey]
        chanDelays[IQkey] = chan.delay + chan.AWG.delay
    return PatternUtils.normalize_delays(chanDelays)
示例#7
0
def concatenate_entries(entry1, entry2, wfLib):
    newentry = copy(entry1)
    # TA waveforms with the same amplitude can be merged with a just length update
    # otherwise, need to concatenate the pulse shapes
    if not (entry1.isTimeAmp and entry2.isTimeAmp and entry1.key == entry2.key and entry1.phase == entry2.phase and entry1.frameChange == 0):
        # otherwise, need to expand pulses and stack them
        wf = np.hstack((np.resize(wfLib[entry1.key], len(entry1)),
                        np.resize(wfLib[entry2.key], len(entry2))))
        newentry.isTimeAmp = False
        newentry.key = PatternUtils.hash_pulse(wf)
        newentry.frameChange += entry2.frameChange
        wfLib[newentry.key] = wf
    newentry.repeat = 1
    newentry.length = len(entry1) + len(entry2)
    return newentry
示例#8
0
 def __init__(self, pulse=None, label=None):
     self.repeat = 1
     self.label = label
     if pulse is None:
         self.key = None
         self.length = 0
         self.phase = 0
         self.frameChange = 0
         self.isTimeAmp = False
         self.repeat = 1
     else:
         self.key = PatternUtils.hash_pulse(pulse.shape)
         self.length = pulse.length
         self.phase = pulse.phase
         self.frameChange = pulse.frameChange
         self.isTimeAmp = pulse.isTimeAmp
示例#9
0
 def __init__(self, pulse=None, label=None):
     self.repeat = 1
     self.label = label
     if pulse is None:
         self.key = None
         self.length = 0
         self.phase = 0
         self.frameChange = 0
         self.isTimeAmp = False
         self.repeat = 1
     else:
         self.key = PatternUtils.hash_pulse(pulse.shape)
         self.length = pulse.length
         self.phase = pulse.phase
         self.frameChange = pulse.frameChange
         self.isTimeAmp = pulse.isTimeAmp
示例#10
0
def concatenate_entries(entry1, entry2, wfLib):
    newentry = copy(entry1)
    # TA waveforms with the same amplitude can be merged with a just length update
    # otherwise, need to concatenate the pulse shapes
    if not (entry1.isTimeAmp and entry2.isTimeAmp and entry1.key == entry2.key
            and entry1.phase == entry2.phase and entry1.frameChange == 0):
        # otherwise, need to expand pulses and stack them
        wf = np.hstack((np.resize(wfLib[entry1.key], len(entry1)),
                        np.resize(wfLib[entry2.key], len(entry2))))
        newentry.isTimeAmp = False
        newentry.key = PatternUtils.hash_pulse(wf)
        newentry.frameChange += entry2.frameChange
        wfLib[newentry.key] = wf
    newentry.repeat = 1
    newentry.length = len(entry1) + len(entry2)
    return newentry
示例#11
0
def compile_to_hardware(seqs, fileName, suffix=''):
    '''
    Compiles 'seqs' to a hardware description and saves it to 'fileName'. Other inputs:
        suffix : string to append to end of fileName (e.g. with fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5)
    '''

    # Add the digitizer trigger to measurements
    PatternUtils.add_digitizer_trigger(seqs, channelLib['digitizerTrig'])

    # Add gating/blanking pulses
    PatternUtils.add_gate_pulses(seqs)

    # Add the slave trigger
    PatternUtils.add_slave_trigger(seqs, channelLib['slaveTrig'])

    # find channel set at top level to account for individual sequence channel variability
    channels = set([])
    for seq in seqs:
        channels |= find_unique_channels(seq)

    # Compile all the pulses/pulseblocks to sequences of pulses and control flow
    wireSeqs = compile_sequences(seqs, channels)

    if not validate_linklist_channels(wireSeqs.keys()):
        print "Compile to hardware failed"
        return

    # apply gating constraints
    for chan, seq in wireSeqs.items():
        if isinstance(chan, Channels.LogicalMarkerChannel):
            wireSeqs[chan] = PatternUtils.apply_gating_constraints(
                chan.physChan, seq)
    # map logical to physical channels
    physWires = map_logical_to_physical(wireSeqs)

    # construct channel delay map
    delays = channel_delay_map(physWires)

    # apply delays
    for chan, wire in physWires.items():
        PatternUtils.delay(wire, delays[chan])

    # generate wf library (base shapes)
    wfs = generate_waveforms(physWires)

    # replace Pulse objects with Waveforms
    physWires = pulses_to_waveforms(physWires)

    # bundle wires on instruments
    awgData = bundle_wires(physWires, wfs)

    # convert to hardware formats
    fileList = []
    for awgName, data in awgData.items():
        # create the target folder if it does not exist
        targetFolder = os.path.split(
            os.path.normpath(os.path.join(config.AWGDir, fileName)))[0]
        if not os.path.exists(targetFolder):
            os.mkdir(targetFolder)
        fullFileName = os.path.normpath(
            os.path.join(
                config.AWGDir, fileName + '-' + awgName + suffix +
                instrumentLib[awgName].seqFileExt))
        instrumentLib[awgName].write_sequence_file(data, fullFileName)

        fileList.append(fullFileName)

    # Return the filenames we wrote
    return fileList
示例#12
0
def channel_delay_map(physicalWires):
    chanDelays = {
        chan: chan.delay + chan.AWG.delay
        for chan in physicalWires.keys()
    }
    return PatternUtils.normalize_delays(chanDelays)
示例#13
0
def compile_to_hardware(seqs, fileName=None, suffix='', alignMode="right", nbrRepeats=1):
    #Add the digitizer trigger to each sequence
    #TODO: Make this more sophisticated.
    PatternUtils.add_digitizer_trigger(seqs, channelLib['digitizerTrig'])
    
    # normalize sequences
    channels = set([])
    for seq in seqs:
        channels |= find_unique_channels(seq)
    seqs = [normalize(seq, channels) for seq in seqs]

    #Compile all the pulses/pulseblocks to linklists and waveform libraries
    linkLists, wfLib = compile_sequences(seqs)

    # align channels
    # this horrible line finds the longest miniLL across all channels
    longestLL = max([sum([entry.totLength for entry in miniLL]) for LL in linkLists.values() for miniLL in LL])
    
    for chan, LL in linkLists.items():
        PatternUtils.align(LL, alignMode, longestLL+SEQUENCE_PADDING)

    #Add the slave trigger
    #TODO: only add to slave devices
    linkLists[channelLib['slaveTrig']], wfLib[channelLib['slaveTrig']] = PatternUtils.slave_trigger(len(seqs))

    # map logical to physical channels
    awgData = map_logical_to_physical(linkLists, wfLib)

    # for each physical channel need to:
    # 1) delay
    # 2) apply SSB if necessary
    # 3) mixer correct
    for awgName, awg in awgData.items():
        for chanName, chanData in awg.items():
            if chanData:
                # construct IQkey using existing convention
                IQkey = awgName + '-' + chanName[2:]
                chanObj = channelLib[IQkey]
                #We handle marker and quadrature channels differently
                #For now that is all we handle
                if isinstance(chanObj, Channels.PhysicalQuadratureChannel):
                    #Apply mixer corrections and channel delay 
                    PatternUtils.delay(chanData['linkList'], chanObj.delay + chanObj.AWG.delay, chanObj.samplingRate)

                    #At this point we finally have the timing of all the pulses so we can apply SSB
                    if hasattr(chanObj, 'SSBFreq') and abs(chanObj.SSBFreq) > 0:
                        PatternUtils.apply_SSB(chanData['linkList'], chanData['wfLib'], chanObj.SSBFreq, chanObj.samplingRate)

                    PatternUtils.correctMixer(chanData['wfLib'], chanObj.correctionT)

                    # add gate pulses on the marker channel
                    # note that the marker may be on an entirely different AWG
                    markerAwgName, markerKey = chanObj.gateChan.label.split('-')
                    markerKey = 'ch' + markerKey
                    markerAwg = awgData[markerAwgName]
                    genObj = chanObj.generator
                    #TODO: check if this actually catches overwriting markers
                    if markerAwg[markerKey]:
                        warn('Reuse of marker gating channel: {0}'.format(markerKey))
                    markerAwg[markerKey] = {'linkList':None, 'wfLib':markerWFLib}
                    markerAwg[markerKey]['linkList'] = PatternUtils.create_gate_seqs(
                        chanData['linkList'], genObj.gateBuffer, genObj.gateMinWidth, chanObj.samplingRate)
                    markerDelay = genObj.gateDelay + chanObj.gateChan.delay + (chanObj.AWG.delay - chanObj.gateChan.AWG.delay)
                    PatternUtils.delay(markerAwg[markerKey]['linkList'], markerDelay, chanObj.gateChan.samplingRate )


                elif isinstance(chanObj, Channels.PhysicalMarkerChannel):
                    PatternUtils.delay(chanData['linkList'], chanObj.delay+chanObj.AWG.delay, chanObj.samplingRate)

                else:
                    raise NameError('Unable to handle channel type.')

                #Remove unused waveforms
                compress_wfLib(chanData['linkList'], chanData['wfLib'])

    #Loop back through to fill empty channels and write to file
    fileList = []
    for awgName, awg in awgData.items():
        #If all the channels are empty then do not bother writing the file
        if not all([chan is None for chan in awg.values()]):
            for chan in awg.keys():
                if not awg[chan]:
                    #"Seems hackish but check for marker
                    if chan[-2] == 'm':
                        awg[chan] = {'linkList': [[create_padding_LL(SEQUENCE_PADDING//2), create_padding_LL(SEQUENCE_PADDING//2)]],
                                 'wfLib': markerWFLib}
                    else:
                        awg[chan] = {'linkList': [[create_padding_LL(SEQUENCE_PADDING//2), create_padding_LL(SEQUENCE_PADDING//2)]],
                                 'wfLib': {TAZKey:np.zeros(1, dtype=np.complex)}}

            # convert to hardware formats
            # create the target folder if it does not exist
            targetFolder = os.path.split(os.path.normpath(os.path.join(config.AWGDir, fileName)))[0]
            if not os.path.exists(targetFolder):
                os.mkdir(targetFolder)
            fullFileName = os.path.normpath(os.path.join(config.AWGDir, fileName + '-' + awgName + suffix + instrumentLib[awgName].seqFileExt))
            if isinstance(instrumentLib[awgName], APS):
                write_APS_file(awg, fullFileName, nbrRepeats)
            elif isinstance(instrumentLib[awgName], Tek5014):
                assert nbrRepeats == 1, 'nbrRepeats > 1 not implemented for the Tek'
                write_Tek_file(awg, fullFileName, fileName)
            else:
                raise NameError('Unknown AWG type')
            fileList.append(fullFileName)

    #Return the filenames we wrote
    return fileList
示例#14
0
def compile_to_hardware(seqs, fileName, suffix='', alignMode="right"):
    '''
    Compiles 'seqs' to a hardware description and saves it to 'fileName'. Other inputs:
        suffix : string to append to end of fileName (e.g. with fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5)
        alignMode : 'left' or 'right' (default 'left')
    '''

    #Add the digitizer trigger to measurements
    PatternUtils.add_digitizer_trigger(seqs, channelLib['digitizerTrig'])

    # Add gating/blanking pulses
    PatternUtils.add_gate_pulses(seqs)

    # Add the slave trigger
    PatternUtils.add_slave_trigger(seqs, channelLib['slaveTrig'])

    # find channel set at top level to account for individual sequence channel variability
    channels = set([])
    for seq in seqs:
        channels |= find_unique_channels(seq)

    #Compile all the pulses/pulseblocks to linklists and waveform libraries
    linkLists, wfLib = compile_sequences(seqs, channels)

    if not validate_linklist_channels(linkLists.keys()):
        print "Compile to hardware failed"
        return

    # apply gating constraints
    for chan, LL in linkLists.items():
        if isinstance(chan, Channels.LogicalMarkerChannel):
            linkLists[chan] = PatternUtils.apply_gating_constraints(
                chan.physChan, LL)
    # map logical to physical channels
    awgData = map_logical_to_physical(linkLists, wfLib)
    # construct channel delay map
    chanDelays = channel_delay_map(awgData)

    # for each physical channel need to:
    # 1) delay
    # 2) apply SSB if necessary
    # 3) mixer correct
    for awgName, data in awgData.items():
        for chanName, chanData in data.items():
            if chanData:
                # construct IQkey using existing convention
                IQkey = awgName + '-' + chanName[2:]
                chanObj = channelLib[IQkey]

                # apply channel delay
                PatternUtils.delay(chanData['linkList'], chanDelays[IQkey],
                                   chanObj.samplingRate)

                # For quadrature channels, apply SSB and mixer correction
                if isinstance(chanObj, Channels.PhysicalQuadratureChannel):

                    #At this point we finally have the timing of all the pulses so we can apply SSB
                    if hasattr(chanObj,
                               'SSBFreq') and abs(chanObj.SSBFreq) > 0:
                        PatternUtils.apply_SSB(chanData['linkList'],
                                               chanData['wfLib'],
                                               chanObj.SSBFreq,
                                               chanObj.samplingRate)

                    PatternUtils.correctMixer(chanData['wfLib'],
                                              chanObj.correctionT)

                #Remove unused waveforms
                compress_wfLib(chanData['linkList'], chanData['wfLib'])

    #Loop back through to fill empty channels and write to file
    fileList = []
    for awgName, data in awgData.items():
        #If all the channels are empty then do not bother writing the file
        if all([chan is None for chan in data.values()]):
            continue

        # convert to hardware formats
        # create the target folder if it does not exist
        targetFolder = os.path.split(
            os.path.normpath(os.path.join(config.AWGDir, fileName)))[0]
        if not os.path.exists(targetFolder):
            os.mkdir(targetFolder)
        fullFileName = os.path.normpath(
            os.path.join(
                config.AWGDir, fileName + '-' + awgName + suffix +
                instrumentLib[awgName].seqFileExt))
        write_sequence_file(instrumentLib[awgName], data, fullFileName)

        fileList.append(fullFileName)

    #Return the filenames we wrote
    return fileList
示例#15
0
def compile_sequence(seq, wfLib={}, channels=None):
    '''
    Converts a single sequence into a miniLL and waveform library.
    Returns a single-entry list of a miniLL and the updated wfLib
    '''

    #Find the set of logical channels used here and initialize them
    if not channels:
        channels = find_unique_channels(seq)

    logicalLLs = {}
    for chan in channels:
        logicalLLs[chan] = []
        if chan not in wfLib:
            if isinstance(chan, Channels.LogicalMarkerChannel):
                wfLib[chan] = markerWFLib
            else:
                wfLib[chan] = {
                    PatternUtils.TAZKey: np.zeros(1, dtype=np.complex)
                }
    carriedPhase = {ch: 0 for ch in channels}
    for block in normalize(flatten(seq), channels):
        # control flow instructions just need to broadcast to all channels
        if isinstance(block, ControlFlow.ControlInstruction):
            for chan in channels:
                logicalLLs[chan] += [copy(block)]
            continue
        # Align the block
        # drop length 0 blocks but push frame change onto next non-zero entry
        if len(block) == 0:
            carriedPhase = {
                ch: carriedPhase[ch] + block.pulses[ch].frameChange
                for ch in channels
            }
            continue
        for chan in channels:
            # add aligned LL entry(ies) (if the block contains a composite pulse, may get back multiple waveforms and LL entries)
            wfs, LLentries = align(block.label, block.pulses[chan], len(block),
                                   block.alignment)
            for wf in wfs:
                if isinstance(chan, Channels.LogicalMarkerChannel):
                    wf = wf.astype(np.bool)
                if PatternUtils.hash_pulse(wf) not in wfLib:
                    wfLib[chan][PatternUtils.hash_pulse(wf)] = wf
            # Frame changes are then propagated through
            logicalLLs[chan] += propagate_frame(LLentries, carriedPhase[chan])
        carriedPhase = {ch: 0 for ch in channels}

    # loop through again to find phases, frame changes, and SSB modulation for quadrature channels
    for chan in channels:
        if not isinstance(chan, (Channels.Qubit, Channels.Measurement)):
            continue
        curFrame = 0
        for entry in logicalLLs[chan]:
            if isinstance(entry, ControlFlow.ControlInstruction):
                continue
            # frame update
            shape = np.copy(wfLib[chan][entry.key])

            # See if we can turn into a TA pair
            # fragile: if you buffer a square pulse it will not be constant valued
            if np.all(shape == shape[0]):
                entry.isTimeAmp = True
                shape = shape[:1]
                # convert near zeros to PatternUtils.TAZKey
                if abs(shape[0]) < 1e-6:
                    entry.key = PatternUtils.TAZKey

            #Rotate for phase and frame change (don't rotate zeros...)
            if entry.key != PatternUtils.TAZKey:
                shape *= np.exp(1j * (entry.phase + curFrame))
                shapeHash = PatternUtils.hash_pulse(shape)
                if shapeHash not in wfLib[chan]:
                    wfLib[chan][shapeHash] = shape
                entry.key = shapeHash
            curFrame += entry.frameChange

    return logicalLLs, wfLib
示例#16
0
def compile_sequence(seq, wfLib={}, channels=None):
    '''
    Converts a single sequence into a miniLL and waveform library.
    Returns a single-entry list of a miniLL and the updated wfLib
    '''

    #Find the set of logical channels used here and initialize them
    if not channels:
        channels = find_unique_channels(seq)

    logicalLLs = {}
    for chan in channels:
        logicalLLs[chan] = []
        if chan not in wfLib:
            if isinstance(chan, Channels.LogicalMarkerChannel):
                wfLib[chan] = markerWFLib
            else:
                wfLib[chan] = {PatternUtils.TAZKey: np.zeros(1, dtype=np.complex)}
    carriedPhase = {ch: 0 for ch in channels}
    for block in normalize(flatten(seq), channels):
        # control flow instructions just need to broadcast to all channels
        if isinstance(block, ControlFlow.ControlInstruction):
            for chan in channels:
                logicalLLs[chan] += [copy(block)]
            continue
        # Align the block
        # drop length 0 blocks but push frame change onto next non-zero entry
        if len(block) == 0:
            carriedPhase = {ch: carriedPhase[ch]+block.pulses[ch].frameChange for ch in channels}
            continue
        for chan in channels:
            # add aligned LL entry(ies) (if the block contains a composite pulse, may get back multiple waveforms and LL entries)
            wfs, LLentries = align(block.label, block.pulses[chan], len(block), block.alignment)
            for wf in wfs:
                if isinstance(chan, Channels.LogicalMarkerChannel):
                    wf = wf.astype(np.bool)
                if PatternUtils.hash_pulse(wf) not in wfLib:
                    wfLib[chan][PatternUtils.hash_pulse(wf)] = wf
            # Frame changes are then propagated through
            logicalLLs[chan] += propagate_frame(LLentries, carriedPhase[chan])
        carriedPhase = {ch: 0 for ch in channels}

    # loop through again to find phases, frame changes, and SSB modulation for quadrature channels
    for chan in channels:
        if not isinstance(chan, (Channels.Qubit, Channels.Measurement)):
            continue
        curFrame = 0
        for entry in logicalLLs[chan]:
            if isinstance(entry, ControlFlow.ControlInstruction):
                continue
            # frame update
            shape = np.copy(wfLib[chan][entry.key])

            # See if we can turn into a TA pair
            # fragile: if you buffer a square pulse it will not be constant valued
            if np.all(shape == shape[0]):
                entry.isTimeAmp = True
                shape = shape[:1]
                # convert near zeros to PatternUtils.TAZKey
                if abs(shape[0]) < 1e-6:
                    entry.key = PatternUtils.TAZKey

            #Rotate for phase and frame change (don't rotate zeros...)
            if entry.key != PatternUtils.TAZKey:
                shape *= np.exp(1j*(entry.phase+curFrame))
                shapeHash = PatternUtils.hash_pulse(shape)
                if shapeHash not in wfLib[chan]:
                    wfLib[chan][shapeHash] = shape
                entry.key = shapeHash
            curFrame += entry.frameChange

    return logicalLLs, wfLib
示例#17
0
def compile_to_hardware(seqs, fileName, suffix='', alignMode="right"):
    '''
    Compiles 'seqs' to a hardware description and saves it to 'fileName'. Other inputs:
        suffix : string to append to end of fileName (e.g. with fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5)
        alignMode : 'left' or 'right' (default 'left')
    '''

    #Add the digitizer trigger to measurements
    PatternUtils.add_digitizer_trigger(seqs, channelLib['digitizerTrig'])

    # Add gating/blanking pulses
    PatternUtils.add_gate_pulses(seqs)

    # Add the slave trigger
    PatternUtils.add_slave_trigger(seqs, channelLib['slaveTrig'])

    # find channel set at top level to account for individual sequence channel variability
    channels = set([])
    for seq in seqs:
        channels |= find_unique_channels(seq)

    #Compile all the pulses/pulseblocks to linklists and waveform libraries
    linkLists, wfLib = compile_sequences(seqs, channels)

    if not validate_linklist_channels(linkLists.keys()):
        print "Compile to hardware failed"
        return        

    # apply gating constraints
    for chan, LL in linkLists.items():
        if isinstance(chan, Channels.LogicalMarkerChannel):
            linkLists[chan] = PatternUtils.apply_gating_constraints(chan.physChan, LL)
    # map logical to physical channels
    awgData = map_logical_to_physical(linkLists, wfLib)
    # construct channel delay map
    chanDelays = channel_delay_map(awgData)

    # for each physical channel need to:
    # 1) delay
    # 2) apply SSB if necessary
    # 3) mixer correct
    for awgName, data in awgData.items():
        for chanName, chanData in data.items():
            if chanData:
                # construct IQkey using existing convention
                IQkey = awgName + '-' + chanName[2:]
                chanObj = channelLib[IQkey]

                # apply channel delay
                PatternUtils.delay(chanData['linkList'], chanDelays[IQkey], chanObj.samplingRate)

                # For quadrature channels, apply SSB and mixer correction
                if isinstance(chanObj, Channels.PhysicalQuadratureChannel):

                    #At this point we finally have the timing of all the pulses so we can apply SSB
                    if hasattr(chanObj, 'SSBFreq') and abs(chanObj.SSBFreq) > 0:
                        PatternUtils.apply_SSB(chanData['linkList'], chanData['wfLib'], chanObj.SSBFreq, chanObj.samplingRate)

                    PatternUtils.correctMixer(chanData['wfLib'], chanObj.correctionT)

                #Remove unused waveforms
                compress_wfLib(chanData['linkList'], chanData['wfLib'])

    #Loop back through to fill empty channels and write to file
    fileList = []
    for awgName, data in awgData.items():
        #If all the channels are empty then do not bother writing the file
        if all([chan is None for chan in data.values()]):
            continue

        # convert to hardware formats
        # create the target folder if it does not exist
        targetFolder = os.path.split(os.path.normpath(os.path.join(config.AWGDir, fileName)))[0]
        if not os.path.exists(targetFolder):
            os.mkdir(targetFolder)
        fullFileName = os.path.normpath(os.path.join(config.AWGDir, fileName + '-' + awgName + suffix + instrumentLib[awgName].seqFileExt))
        write_sequence_file(instrumentLib[awgName], data, fullFileName)

        fileList.append(fullFileName)

    #Return the filenames we wrote
    return fileList
示例#18
0
def compile_to_hardware(seqs,
                        fileName=None,
                        suffix='',
                        alignMode="right",
                        nbrRepeats=1):
    #Add the digitizer trigger to each sequence
    #TODO: Make this more sophisticated.
    PatternUtils.add_digitizer_trigger(seqs, channelLib['digitizerTrig'])

    # normalize sequences
    channels = set([])
    for seq in seqs:
        channels |= find_unique_channels(seq)
    seqs = [normalize(seq, channels) for seq in seqs]

    #Compile all the pulses/pulseblocks to linklists and waveform libraries
    linkLists, wfLib = compile_sequences(seqs)

    # align channels
    # this horrible line finds the longest miniLL across all channels
    longestLL = max([
        sum([entry.totLength for entry in miniLL])
        for LL in linkLists.values() for miniLL in LL
    ])

    for chan, LL in linkLists.items():
        PatternUtils.align(LL, alignMode, longestLL + SEQUENCE_PADDING)

    #Add the slave trigger
    #TODO: only add to slave devices
    linkLists[channelLib['slaveTrig']], wfLib[
        channelLib['slaveTrig']] = PatternUtils.slave_trigger(len(seqs))

    # map logical to physical channels
    awgData = map_logical_to_physical(linkLists, wfLib)

    # for each physical channel need to:
    # 1) delay
    # 2) apply SSB if necessary
    # 3) mixer correct
    for awgName, awg in awgData.items():
        for chanName, chanData in awg.items():
            if chanData:
                # construct IQkey using existing convention
                IQkey = awgName + '-' + chanName[2:]
                chanObj = channelLib[IQkey]
                #We handle marker and quadrature channels differently
                #For now that is all we handle
                if isinstance(chanObj, Channels.PhysicalQuadratureChannel):
                    #Apply mixer corrections and channel delay
                    PatternUtils.delay(chanData['linkList'],
                                       chanObj.delay + chanObj.AWG.delay,
                                       chanObj.samplingRate)

                    #At this point we finally have the timing of all the pulses so we can apply SSB
                    if hasattr(chanObj,
                               'SSBFreq') and abs(chanObj.SSBFreq) > 0:
                        PatternUtils.apply_SSB(chanData['linkList'],
                                               chanData['wfLib'],
                                               chanObj.SSBFreq,
                                               chanObj.samplingRate)

                    PatternUtils.correctMixer(chanData['wfLib'],
                                              chanObj.correctionT)

                    # add gate pulses on the marker channel
                    # note that the marker may be on an entirely different AWG
                    markerAwgName, markerKey = chanObj.gateChan.name.split('-')
                    markerKey = 'ch' + markerKey
                    markerAwg = awgData[markerAwgName]
                    genObj = chanObj.generator
                    #TODO: check if this actually catches overwriting markers
                    if markerAwg[markerKey]:
                        warn('Reuse of marker gating channel: {0}'.format(
                            markerKey))
                    markerAwg[markerKey] = {
                        'linkList': None,
                        'wfLib': markerWFLib
                    }
                    markerAwg[markerKey][
                        'linkList'] = PatternUtils.create_gate_seqs(
                            chanData['linkList'], genObj.gateBuffer,
                            genObj.gateMinWidth, chanObj.samplingRate)
                    markerDelay = genObj.gateDelay + chanObj.gateChan.delay + (
                        chanObj.AWG.delay - chanObj.gateChan.AWG.delay)
                    PatternUtils.delay(markerAwg[markerKey]['linkList'],
                                       markerDelay,
                                       chanObj.gateChan.samplingRate)

                elif isinstance(chanObj, Channels.PhysicalMarkerChannel):
                    PatternUtils.delay(chanData['linkList'],
                                       chanObj.delay + chanObj.AWG.delay,
                                       chanObj.samplingRate)

                else:
                    raise NameError('Unable to handle channel type.')

    #Loop back through to fill empty channels and write to file
    fileList = []
    for awgName, awg in awgData.items():
        #If all the channels are empty then do not bother writing the file
        if not all([chan is None for chan in awg.values()]):
            for chan in awg.keys():
                if not awg[chan]:
                    #"Seems hackish but check for marker
                    if chan[-2] == 'm':
                        awg[chan] = {
                            'linkList': [[
                                create_padding_LL(SEQUENCE_PADDING // 2),
                                create_padding_LL(SEQUENCE_PADDING // 2)
                            ]],
                            'wfLib':
                            markerWFLib
                        }
                    else:
                        awg[chan] = {
                            'linkList': [[
                                create_padding_LL(SEQUENCE_PADDING // 2),
                                create_padding_LL(SEQUENCE_PADDING // 2)
                            ]],
                            'wfLib': {
                                TAZKey: np.zeros(1, dtype=np.complex)
                            }
                        }

            # convert to hardware formats
            # create the target folder if it does not exist
            targetFolder = os.path.split(config.AWGDir + fileName)[0]
            if not os.path.exists(targetFolder):
                os.mkdir(targetFolder)
            fullFileName = config.AWGDir + fileName + '-' + awgName + suffix + instrumentLib[
                awgName].seqFileExt
            if isinstance(instrumentLib[awgName], APS):
                write_APS_file(awg, fullFileName, nbrRepeats)
            elif isinstance(instrumentLib[awgName], Tek5014):
                assert nbrRepeats == 1, 'nbrRepeats > 1 not implemented for the Tek'
                write_Tek_file(awg, fullFileName, fileName)
            else:
                raise NameError('Unknown AWG type')
            fileList.append(fullFileName)

    #Return the filenames we wrote
    return fileList
示例#19
0
def compile_to_hardware(seqs, fileName, suffix=''):
    '''
    Compiles 'seqs' to a hardware description and saves it to 'fileName'. Other inputs:
        suffix : string to append to end of fileName (e.g. with fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5)
    '''

    # Add the digitizer trigger to measurements
    PatternUtils.add_digitizer_trigger(seqs, channelLib['digitizerTrig'])

    # Add gating/blanking pulses
    PatternUtils.add_gate_pulses(seqs)

    # Add the slave trigger
    PatternUtils.add_slave_trigger(seqs, channelLib['slaveTrig'])

    # find channel set at top level to account for individual sequence channel variability
    channels = set([])
    for seq in seqs:
        channels |= find_unique_channels(seq)

    # Compile all the pulses/pulseblocks to sequences of pulses and control flow
    wireSeqs = compile_sequences(seqs, channels)

    if not validate_linklist_channels(wireSeqs.keys()):
        print "Compile to hardware failed"
        return

    # apply gating constraints
    for chan, seq in wireSeqs.items():
        if isinstance(chan, Channels.LogicalMarkerChannel):
            wireSeqs[chan] = PatternUtils.apply_gating_constraints(chan.physChan, seq)
    # map logical to physical channels
    physWires = map_logical_to_physical(wireSeqs)

    # construct channel delay map
    delays = channel_delay_map(physWires)

    # apply delays
    for chan, wire in physWires.items():
        PatternUtils.delay(wire, delays[chan])

    # generate wf library (base shapes)
    wfs = generate_waveforms(physWires)

    # replace Pulse objects with Waveforms
    physWires = pulses_to_waveforms(physWires)

    # bundle wires on instruments
    awgData = bundle_wires(physWires, wfs)

    # convert to hardware formats
    fileList = []
    for awgName, data in awgData.items():
        # create the target folder if it does not exist
        targetFolder = os.path.split(os.path.normpath(os.path.join(config.AWGDir, fileName)))[0]
        if not os.path.exists(targetFolder):
            os.mkdir(targetFolder)
        fullFileName = os.path.normpath(os.path.join(config.AWGDir, fileName + '-' + awgName + suffix + instrumentLib[awgName].seqFileExt))
        instrumentLib[awgName].write_sequence_file(data, fullFileName)

        fileList.append(fullFileName)

    # Return the filenames we wrote
    return fileList
示例#20
0
def channel_delay_map(physicalWires):
    chanDelays = {chan : chan.delay + chan.AWG.delay for chan in physicalWires.keys()}
    return PatternUtils.normalize_delays(chanDelays)