Skip to content

Commit

Permalink
Merge ce63dd3 into 146a533
Browse files Browse the repository at this point in the history
  • Loading branch information
grahamrow committed Oct 4, 2019
2 parents 146a533 + ce63dd3 commit 409a8a6
Show file tree
Hide file tree
Showing 22 changed files with 2,533 additions and 2,689 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
*.h5
*.py~
*.py#
*.json~
*.coverage
*.ipynb_checkpoints
*.egg-info
4 changes: 3 additions & 1 deletion QGL/BasicSequences/Decoupling.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ def CPMG(qubit, numPulses, pulseSpacing, calRepeats=2, showPlot=False):

metafile = compile_to_hardware(seqs, 'CPMG/CPMG',
axis_descriptor=[
delay_descriptor(pulseSpacing * numPulses),
# NOTE: numPulses is often not a numpy array, so cannot multiply by a float.
# But thankfully, np.array(np.array) = np.array so this is always a good move here.
delay_descriptor(pulseSpacing * np.array(numPulses)),
cal_descriptor((qubit,), calRepeats)
])

Expand Down
7 changes: 5 additions & 2 deletions QGL/BasicSequences/helpers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# coding=utf-8

from functools import reduce
from itertools import product
import numpy as np
import operator

from ..PulsePrimitives import Id, X, MEAS
from ..ControlFlow import qwait
from functools import reduce

def create_cal_seqs(qubits, numRepeats, measChans=None, waitcmp=False, delay=None):
"""
Expand Down Expand Up @@ -59,7 +61,8 @@ def delay_descriptor(delays, desired_units="us"):
axis_descriptor = {
'name': 'delay',
'unit': desired_units,
'points': list(scale * delays),
# Make sure delays is a numpy array so can multiply it by a float safely
'points': list(scale * np.array(delays)),
'partition': 1
}
return axis_descriptor
86 changes: 66 additions & 20 deletions QGL/ChannelLibraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,14 @@ def update_channelDict(self):

def ls(self):
cdb = Channels.ChannelDatabase
q = self.session.query(cdb.label, cdb.time, cdb.id).\
order_by(Channels.ChannelDatabase.id, Channels.ChannelDatabase.label).all()
q = self.session.query(cdb.label, cdb.time, cdb.id, cdb.notes).\
order_by(Channels.ChannelDatabase.id, Channels.ChannelDatabase.label, Channels.ChannelDatabase.notes).all()
table_code = ""
for i, (label, time, id) in enumerate(q):
for i, (label, time, id, notes) in enumerate(q):
y, d, t = map(time.strftime, ["%Y", "%b. %d", "%I:%M:%S %p"])
# t = time.strftime("(%Y) %b. %d @ %I:%M:%S %p")
table_code += f"<tr><td>{id}</td><td>{y}</td><td>{d}</td><td>{t}</td><td>{label}</td></tr>"
display(HTML(f"<table><tr><th>id</th><th>Year</th><th>Date</th><th>Time</th><th>Name</th></tr><tr>{table_code}</tr></table>"))
table_code += f"<tr><td>{id}</td><td>{y}</td><td>{d}</td><td>{t}</td><td>{label}</td><td>{notes}</td></tr>"
display(HTML(f"<table><tr><th>id</th><th>Year</th><th>Date</th><th>Time</th><th>Name</th><th>Notes</th></tr><tr>{table_code}</tr></table>"))

def ent_by_type(self, obj_type, show=False):
q = self.session.query(obj_type).filter(obj_type.channel_db.has(label="working")).order_by(obj_type.label).all()
Expand Down Expand Up @@ -183,7 +183,7 @@ def show(self, qubits=[]):
indices = {n: i for i, n in enumerate(graph.nodes())}
node_data = [{'label': str(n).replace('(','\r\n(')} for n in graph.nodes()]
link_data = [{'source': indices[s], 'target': indices[t]} for s, t in graph.edges()]

qub_objs.sort(key=lambda x: x.label)
qubit_names = [q.label for q in qub_objs]

Expand Down Expand Up @@ -225,7 +225,7 @@ def next_level(nodes, iteration=0, offset=0, accum=[]):
end = widest[0]-0.6
elif i == len(qub_objs):
start = sum(widest)-0.4
end = max(x)+0.4
end = max(x)+0.4
else:
start = sum(widest[:i])-0.4
end = sum(widest[:i+1])-0.6
Expand All @@ -240,7 +240,7 @@ def show_frequency_plan(self):
c_freqs[qubit.label] = qubit.frequency*1e-9
if qubit.phys_chan.generator:
c_freqs[qubit.label] += qubit.phys_chan.generator.frequency*1e-9

m_freqs[qubit.label] = qubit.measure_chan.frequency*1e-9
if qubit.measure_chan.phys_chan.generator:
m_freqs[qubit.label] += qubit.measure_chan.phys_chan.generator.frequency*1e-9
Expand Down Expand Up @@ -333,13 +333,14 @@ def revert(self):
self.session.rollback()

@check_session_dirty
def save_as(self, name):
def save_as(self, name, notes = ''):
if name == "working":
raise ValueError("Cannot save as `working` since that is the default working environment name...")
self.commit()
new_channelDatabase = bbndb.deepcopy_sqla_object(self.channelDatabase, self.session)
new_channelDatabase.label = name
new_channelDatabase.time = datetime.datetime.now()
new_channelDatabase.notes = notes
self.commit()

def add_and_update_dict(self, el):
Expand Down Expand Up @@ -376,6 +377,19 @@ def build_connectivity_graph(self):
self.connectivityG.add_edge(chan.source, chan.target)
self.connectivityG[chan.source][chan.target]['channel'] = chan

@check_for_duplicates
def new_APS3(self, label, address, serial_port, **kwargs):
chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-1", channel=0, instrument=label, translator="APS3Pattern", sampling_rate=2.5e9, channel_db=self.channelDatabase)
chan2 = Channels.PhysicalQuadratureChannel(label=f"{label}-2", channel=1, instrument=label, translator="APS3Pattern", sampling_rate=2.5e9, channel_db=self.channelDatabase)
m1 = Channels.PhysicalMarkerChannel(label=f"{label}-m1", channel=0, instrument=label, translator="APS3Pattern", sampling_rate=2.5e9, channel_db=self.channelDatabase)

this_transmitter = Channels.Transmitter(label=label, model="APS3", address=address, serial_port=serial_port, channels=[chan1, chan2, m1], channel_db=self.channelDatabase, **kwargs)
this_transmitter.trigger_source = 'external' if 'trigger_source' not in kwargs else kwargs['trigger_source']

self.add_and_update_dict(this_transmitter)
return this_transmitter


@check_for_duplicates
def new_APS2(self, label, address, **kwargs):
chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-1", channel=0, instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase)
Expand Down Expand Up @@ -409,7 +423,12 @@ def new_APS(self, label, address, **kwargs):

@check_for_duplicates
def new_TDM(self, label, address, **kwargs):
return Channels.Processor(label=label, model="TDM", address=address, trigger_interval=250e-6)
chans = []
for k in range(7): # TDM has 7 digital inputs
chans.append(Channels.DigitalInput(label=f"DigitalInput-{label}-{k}", channel=k, channel_db=self.channelDatabase))
tdm = Channels.Processor(label=label, model="TDM", address=address, trigger_interval=250e-6, channels=chans, channel_db=self.channelDatabase)
self.add_and_update_dict(tdm)
return tdm

@check_for_duplicates
def new_spectrum_analzyer(self, label, address, source, **kwargs):
Expand Down Expand Up @@ -541,7 +560,7 @@ def set_qubit_connectivity(self, graph):
self.add_and_update_dict(new_edges)
return new_edges

def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7):
def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7, tdm_chan=None):

if isinstance(transmitter, Channels.Transmitter):
quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)]
Expand Down Expand Up @@ -600,6 +619,20 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe
meas.gate_chan = gate_chan
self.add_and_update_dict([gate_chan])

if tdm_chan:
if isinstance(tdm_chan, Channels.DigitalInput):
phys_tdm_channel = tdm_chan
else:
if not hasattr(self.channelDatabase, 'processors') or not self.channelDatabase.processors:
raise ValueError(f"No processor is defined")
elif len(self.channelDatabase.processors) > 1:
raise ValueError(f"Multiple processors are defined. Please specify digital input channel.")
else:
tdm = self.channelDatabase.processors[0]
phys_tdm_channel = tdm.get_chan(tdm_chan)
meas.processor_chan = phys_tdm_channel
self.add_and_update_dict([meas, phys_tdm_channel])

def set_master(self, master_instrument, trig_channel=None, pulse_length=1e-7):

if isinstance(master_instrument, Channels.Processor):
Expand All @@ -622,39 +655,52 @@ def set_master(self, master_instrument, trig_channel=None, pulse_length=1e-7):
self.add_and_update_dict([st])

else:
raise ValueError(f"Could not determine which transmitter to set as master for {transmitter}:{trig_channel}")
raise ValueError(f"Could not determine which transmitter to set as master for {master_instrument}:{trig_channel}")

# Used by QGL2, which needs a non-class member function to
# retrieve a Qubit from the CL without accessing the CL directly
def QubitFactory(label):
''' Return a saved qubit channel'''
if channelLib is None:
raise Exception("No channel library initialized")
channelLib.update_channelDict()
cs = [c for c in channelLib.channelDatabase.channels if c.label==label]
# cs = [c for c in channelLib.channelDatabase.channels if c.label==label]
cs = [c for c in channelLib.channelDatabase.channels if c.label==label and isinstance(c, Channels.Qubit)]
# q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all()
if len(cs) == 1:
return cs[0]
else:
raise Exception(f"Expected to find a single qubit {label} but found {len(cs)} qubits with the same label instead.")
raise Exception(f"Expected to find a single qubit '{label}' but found {len(cs)} qubits with the same label instead.")

def MeasFactory(label):
''' Return a saved measurement channel or create a new one. '''
''' Return a saved measurement channel.'''
if channelLib is None:
raise Exception("No channel library initialized")
channelLib.update_channelDict()
cs = [c for c in channelLib.channelDatabase.channels if c.label==label]
# cs = [c for c in channelLib.channelDatabase.channels if c.label==label]
cs = [c for c in channelLib.channelDatabase.channels if c.label==label and isinstance(c, Channels.Measurement)]
# q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all()
if len(cs) == 1:
return cs[0]
else:
raise Exception(f"Expected to find a single measurement {label} but found {len(cs)} measurements with the same label instead.")
raise Exception(f"Expected to find a single measurement '{label}' but found {len(cs)} measurements with the same label instead.")

def MarkerFactory(label):
''' Return a saved Marker channel or create a new one. '''
cs = [c for c in channelLib.channelDatabase.channels if c.label==label]
''' Return a saved Marker channel with this label. '''
if channelLib is None:
raise Exception("No channel library initialized")
# cs = [c for c in channelLib.channelDatabase.channels if c.label==label]
cs = [c for c in channelLib.channelDatabase.channels if c.label==label and isinstance(c, Channels.LogicalMarkerChannel)]
channelLib.update_channelDict()
# q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all()
if len(cs) == 1:
return cs[0]
else:
raise Exception(f"Expected to find a single marker {label} but found {len(cs)} markers with the same label instead.")
raise Exception(f"Expected to find a single marker '{label}' but found {len(cs)} markers with the same label instead.")

def EdgeFactory(source, target):
if channelLib is None:
raise Exception("No channel library initialized")
channelLib.update_channelDict()
if channelLib.connectivityG.has_edge(source, target):
return channelLib.connectivityG[source][target]['channel']
Expand Down
8 changes: 4 additions & 4 deletions QGL/GSTTools.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

PYGSTI_PRESENT = False
try:
from pygsti.objects import GateString
from pygsti.objects.circuit import Circuit
PYGSTI_PRESENT = True
except:
pass
Expand All @@ -51,11 +51,11 @@ def gst_map_1Q(gst_list, qubit, qgl_map=gst_gate_map, append_meas=True):
Returns:
QGL sequences, preserving the input list nesting (as a generator)
"""
if isinstance(gst_list, GateString):
if isinstance(gst_list, Circuit):
gst_list = [gst_list]
for item in gst_list:
if isinstance(item, GateString):
mapped = map(lambda x: qgl_map[x](qubit), item.tup)
if isinstance(item, Circuit):
mapped = map(lambda x: qgl_map[str(x)](qubit), item.tup)
if append_meas:
yield list(chain(mapped, [MEAS(qubit)]))
else:
Expand Down
1 change: 1 addition & 0 deletions QGL/PatternUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ def convert_lengths_to_samples(instructions, sampling_rate, quantization=1, wf_t
entry.length = int(round(entry.length * sampling_rate))
# TODO: warn when truncating?
entry.length -= entry.length % quantization

return instructions

def convert_length_to_samples(wf_length, sampling_rate, quantization=1):
Expand Down
1 change: 0 additions & 1 deletion QGL/PulseSequencePlotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ def extract_waveforms(fileNames, nameDecorator='', time=False):
translator = resolve_translator(fileName, translators)
wfs = translator.read_sequence_file(fileName)
sample_time = 1.0/translator.SAMPLING_RATE if time else 1

for (k, seqs) in sorted(wfs.items()):
if all_zero_seqs(seqs):
continue
Expand Down
6 changes: 5 additions & 1 deletion QGL/PulseSequencer.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ def __str__(self):
if (n not in self.ignoredStrParams and
n in self.channel.pulse_params and
self.channel.pulse_params[n] != v):
kwvals.append("{0}={1}".format(n, v))
# Hack to make certain printouts prettier...
if callable(v) and v.__module__ == 'QGL.PulseShapes':
kwvals.append("{0}=<{1}>".format(n, v.__name__))
else:
kwvals.append("{0}={1}".format(n, v))
if kwvals:
kwstr = ", " + ", ".join(kwvals)
else:
Expand Down
23 changes: 17 additions & 6 deletions QGL/PulseShapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ def gaussian(amp=1, length=0, cutoff=2, sampling_rate=1e9, **params):
A simple gaussian shaped pulse.
cutoff is how many sigma the pulse goes out
'''
if length == 0:
raise ValueError("gaussian() got 0 length")
if sampling_rate == 0:
raise ValueError("gaussian() got 0 sampling_rate")
#Round to how many points we need
numPts = int(np.round(length * sampling_rate))
xPts = np.linspace(-cutoff, cutoff, numPts)
Expand Down Expand Up @@ -135,12 +139,19 @@ def tanh(amp=1, length=0, sigma=0, cutoff=2, sampling_rate=1e9, **params):
'''
A rounded constant shape from the sum of two tanh shapes.
'''
numPts = int(np.round(length * sampling_rate))
xPts = np.linspace(-length / 2, length / 2, numPts)
x1 = -length / 2 + cutoff * sigma
x2 = +length / 2 - cutoff * sigma
return amp * 0.5 * (np.tanh((xPts - x1) / sigma) + np.tanh(
(x2 - xPts) / sigma)).astype(np.complex)
if length == 0.0:
return np.empty(shape=(0,)).astype(np.complex)
else:
numPts = int(np.round(length * sampling_rate))
xPts = np.linspace(-length / 2, length / 2, numPts)
x1 = -length / 2 + cutoff * sigma
x2 = +length / 2 - cutoff * sigma
assert x1 < 0 and x2 > 0, (f'Pulse length must be greater than'
f'2 * cutoff (={cutoff}) * sigma '
f'(={sigma}s). Consider '
f'using a Gaussian pulse instead.')
return amp * 0.5 * (np.tanh((xPts - x1) / sigma) + np.tanh(
(x2 - xPts) / sigma)).astype(np.complex)


def exp_decay(amp=1, length=0, sigma=0, sampling_rate=1e9, steady_state=0.4, **params):
Expand Down
20 changes: 19 additions & 1 deletion QGL/Scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def schedule(seq):
continue

channels = get_channels(instr, channel_set)
if channels is None:
raise Exception("No channels found from 'get_channels(%s, channel_set)'" % instr)
# find the most advanced counter in the channel set
idx = max(counters.get(ch, 0) for ch in channels)

Expand All @@ -54,8 +56,24 @@ def get_channels(instr, channel_set=None):
return channel_set
elif isinstance(instr, Barrier):
return chanlist
elif isinstance(instr, list):
# This is a bug I think
warn("Supposed instruction is actually a %d item list" % len(instr))

# For debugging, could print these, but this can be verbose
# if len(instr) < 3:
# outstr = ""
# for i in instr:
# outstr += " " + str(i) + "\n"
# if len(outstr) > 160:
# outstr += "...\n"
# break
# print(outstr, end='')

return find_all_channels(instr)
# return None
elif not hasattr(instr, 'channel'):
warn("instruction %s does not have a 'channel' property", instr)
warn("instruction '%s' does not have a 'channel' property" % instr)
return None
elif isinstance(instr.channel, Edge):
return (instr.channel.source, instr.channel.target)
Expand Down
Loading

0 comments on commit 409a8a6

Please sign in to comment.