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
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
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)
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
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
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)
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
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
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
def channel_delay_map(physicalWires): chanDelays = { chan: chan.delay + chan.AWG.delay for chan in physicalWires.keys() } return PatternUtils.normalize_delays(chanDelays)
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
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
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
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
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
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
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
def channel_delay_map(physicalWires): chanDelays = {chan : chan.delay + chan.AWG.delay for chan in physicalWires.keys()} return PatternUtils.normalize_delays(chanDelays)