Skip to content

Commit

Permalink
Merge 13de257 into 32e2146
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewware authored May 27, 2020
2 parents 32e2146 + 13de257 commit ebf0502
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 10 deletions.
3 changes: 2 additions & 1 deletion QGL/PatternUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import hashlib, collections
import pickle
from copy import copy
from collections.abc import Iterable

from .PulseSequencer import Pulse, TAPulse, PulseBlock, CompositePulse, CompoundGate, align
from .PulsePrimitives import BLANK, X
Expand Down Expand Up @@ -312,7 +313,7 @@ def convert_length_to_samples(wf_length, sampling_rate, quantization=1):
# from Stack Overflow: http://stackoverflow.com/questions/2158395/flatten-an-irregular-list-of-lists-in-python/2158532#2158532
def flatten(l):
for el in l:
if isinstance(el, collections.Iterable) and not isinstance(el, (str, Pulse, CompositePulse)) :
if isinstance(el, Iterable) and not isinstance(el, (str, Pulse, CompositePulse)) :
for sub in flatten(el):
yield sub
else:
Expand Down
81 changes: 74 additions & 7 deletions QGL/drivers/APS2Pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,7 @@ def compress_marker(markerLL):

def write_sequence_file(awgData, fileName):
'''
Main function to pack channel sequences into an APS2 h5 file.
Main function to pack channel sequences into an APS2 file.
'''
# Convert QGL IR into a representation that is closer to the hardware.
awgData['ch1']['linkList'], wfLib = preprocess(
Expand Down Expand Up @@ -1115,7 +1115,7 @@ def write_sequence_file(awgData, fileName):

def read_sequence_file(fileName):
"""
Reads a HDF5 sequence file and returns a dictionary of lists.
Reads a .aps2 sequence file and returns a dictionary of lists.
Dictionary keys are channel strings such as ch1, m1
Lists are or tuples of time-amplitude pairs (time, output)
"""
Expand Down Expand Up @@ -1525,17 +1525,78 @@ def read_waveforms(filename):
wf_dat.append(dat)
return wf_dat

def replace_instructions(filename, instructions, channel = 1):
channelStr = get_channel_instructions_string(channel)
with h5py.File(filename, 'r+') as fid:
del fid[channelStr]
fid.create_dataset(channelStr, data=instructions)
def replace_instructions(filename, instructions):
"""
Update an aps2 instruction set in place give an iterable of
properly encoded instructions.
Parameters
----------
filename : string
path to the .aps2 file to update
instructions : list of raw aps2 instructions
list of raw, uint64 instructions to replace the current ones in file
Examples
--------
>>> APS2Pattern.replace_instructions('path/to/.aps2', instructions)
"""

with open(filename, 'rb+') as FID:
# read all the existing file information so we can faithfully
# reconstruct it.

target_hw = FID.read(4).decode('utf-8')
file_version = struct.unpack('<f', FID.read(4))[0]
min_fw = struct.unpack('<f', FID.read(4))[0]
num_chans = struct.unpack('<H', FID.read(2))[0]

old_inst_len = struct.unpack('<Q', FID.read(8))[0]
old_instructions = np.frombuffer(FID.read(8*old_inst_len), dtype=np.uint64)

wf_dat = []
for i in range(num_chans):
wf_len = struct.unpack('<Q', FID.read(8))[0]
dat = np.frombuffer(FID.read(2*wf_len), dtype=np.int16).flatten()
wf_dat.append(dat)

# Write new data

# skip over the header
FID.seek(0) # return to the start of the file
FID.seek(4) # target_hw
FID.seek(4, 1) # file version
FID.seek(4, 1) # minimum firmware version
FID.seek(2, 1) # number of channels

FID.write(np.uint64(instructions.size).tobytes()) # instruction length
FID.write(instructions.tobytes()) # instructions in uint64 form

# if the instruction length has changed we need to rewrite the
# waveform data
for i in range(num_chans):
data = wf_dat[i]
FID.write(np.uint64(data.size).tobytes()) # waveform data length for channel
print(data.astype(np.int16))
FID.write(data.astype(np.int16).tobytes())

# chop off any remaining old data
FID.truncate()


def display_decompiled_file(filename, tdm = False):
"""
Display all the decomplied instructions from a .aps2 file as
{instructions #}{instruction value in 16 bit hex}{decompiled instruction}
"""
raw = raw_instructions(filename)
display_decompiled_instructions(raw, tdm)

def display_decompiled_instructions(raw, tdm = False, display_op_codes = True):
"""
Display the decomplied instructions from their packed, 16 bit values
{instructions #}{instruction value in 16 bit hex}{decompiled instruction}
"""
cmds = decompile_instructions(raw, tdm)
opcodeStr = ''
for i,a in enumerate(zip(raw,cmds)):
Expand All @@ -1545,10 +1606,16 @@ def display_decompiled_instructions(raw, tdm = False, display_op_codes = True):
print("{:5}: {}{}".format(i, opcodeStr,y))

def display_raw_instructions(raw):
"""
Format a raw number as a 16 bit hex instruction
"""
for x in raw:
print("0x{:016x}".format(x))

def display_raw_file(filename):
"""
Display all the raw hex instructions from a .aps2 file as
"""
raw = raw_instructions(filename)
display_raw_instructions(raw)

Expand Down
121 changes: 119 additions & 2 deletions tests/test_APS2Pattern.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import unittest
import os
import pickle
import numpy as np
from copy import copy

Expand All @@ -9,10 +11,57 @@
class APSPatternUtils(unittest.TestCase):
def setUp(self):
self.cl = ChannelLibrary(db_resource_name=":memory:")
self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate', channel_db=self.cl.channelDatabase)
#self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate',
# channel_db=self.cl.channelDatabase)
self.q1 = self.cl.new_qubit(label='q1')
self.q1.gate_chan = self.q1gate
#self.q1.gate_chan = self.q1gate
self.q1.pulse_params['length'] = 30e-9

ip_addresses = [f"192.168.1.{i}" for i in [23, 24, 25, 28]]
aps2 = self.cl.new_APS2_rack("Maxwell",
ip_addresses,
tdm_ip="192.168.1.11")
aps2.px("TDM").trigger_interval = 500e-6
self.cl.set_master(aps2.px("TDM"))

# initialize all four APS2 to linear regime
for i in range(1,4):
aps2.tx(i).ch(1).I_channel_amp_factor = 0.5
aps2.tx(i).ch(1).Q_channel_amp_factor = 0.5
aps2.tx(i).ch(1).amp_factor = 1

dig_1 = self.cl.new_X6("MyX6", address=0)

dig_1.record_length = 1024 + 256

#### QUBIT 1 ######################################################
#### Qubit 1 Instruments ##########################################
AM1 = self.cl.new_source("AutodyneM1",
"HolzworthHS9000",
"HS9004A-492-1",
power=16.0,
frequency= 6.74621e9, reference="10MHz")

q1src = self.cl.new_source("q1source",
"HolzworthHS9000",
"HS9004A-492-2",
power=16.0,
frequency=5.0122e9, reference="10MHz")

self.cl.set_measure(self.q1, aps2.tx(2), dig_1.channels[1],
gate=False,
trig_channel=aps2.tx(2).ch("m2"),
generator=AM1)
self.cl.set_control(self.q1, aps2.tx(4), generator=q1src)

#### Qubit 2 Measure Chan ########################################

self.cl["q1"].measure_chan.autodyne_freq = 11e6
self.cl["q1"].measure_chan.pulse_params = {"length": 1.0e-6,
"amp": 0.5,
"sigma": 1.0e-8,
"shape_fun": "tanh"}

self.cl.update_channelDict()

def test_synchronize_control_flow(self):
Expand Down Expand Up @@ -43,6 +92,74 @@ def test_synchronize_control_flow(self):
instrOpCode = (actual.header >> 4) & 0xf
assert (instrOpCode == expected)

def test_inplace_updates(self):
q1 = self.q1
APS2Pattern.SAVE_WF_OFFSETS = True
#print(q1)
#print(q1.chan)

# amps = np.linspace(-1, 1, 11)
# seqs = [[Utheta(q1, amp=amp), MEAS(q1)] for amp in amps]
#
# axis_descriptor = [{
# 'name': 'amplitude',
# 'unit': None,
# 'points': list(amps),
# 'partition': 1
# }]
#
# mf = compile_to_hardware(seqs, 'Rabi/Rabi',
# axis_descriptor=axis_descriptor)

mf = RabiAmp(q1, np.linspace(-1, 1, 11))
aps2_f = os.path.join(os.path.dirname(mf), "Rabi-Maxwell_U4.aps2")

# instructions = APS2Pattern.read_instructions(aps2_f)
# waveforms = APS2Pattern.read_waveforms(aps2_f)
#
# t_instructions =
# t_waveforms =
#
# # Assert that we can read the correct information from file
# for actual, expected in zip(instructions, t_instructions):
# instrOpCode = (actual.header >> 4) & 0xf
# assert (instrOpCode == expected)
#
# for actual, expected in zip(waveforms, t_waveforms):
# instrOpCode = (actual.header >> 4) & 0xf
# assert (instrOpCode == expected)

# overwrite the instructions and waveforms
# here we completely change the experiment from Rabi to
# SPAM characterization
spam_mf = SPAM(q1, np.linspace(-1.0, 1.0, 11));

offset_f = os.path.join(os.path.dirname(spam_mf), "SPAM-Maxwell_U4.offsets")
with open(offset_f, "rb") as FID:
offsets = pickle.load(FID)

#pulses = {l: Utheta(q1, amp=0.5, phase=0) for l in offsets}
aps2_f = os.path.join(os.path.dirname(mf), "Rabi-Maxwell_U4.aps2")
wfm_f = os.path.join(os.path.dirname(spam_mf), "SPAM-Maxwell_U4.aps2")
spam_waveforms = APS2Pattern.read_waveforms(wfm_f)
print(spam_waveforms)
# The spam_waveforms are raw. Is this what we want?
APS2Pattern.update_wf_library(aps2_f, spam_waveforms, offsets)

spam_instrs = APS2Pattern.read_instructions(wfm_f)
APS2Pattern.replace_instructions(aps2_f, spam_instrs)

# assert the data now in the file is what we wrote above
instructions = APS2Pattern.read_instructions(wfm_f)
waveforms = APS2Pattern.read_waveforms(wfm_f)
for actual, expected in zip(instructions, spam_instrs):
instrOpCode = (actual.header >> 4) & 0xf
assert (instrOpCode == expected)

for actual, expected in zip(waveforms, spam_waveforms):
instrOpCode = (actual.header >> 4) & 0xf
assert (instrOpCode == expected)


if __name__ == "__main__":
unittest.main()

0 comments on commit ebf0502

Please sign in to comment.