Coder Social home page Coder Social logo

bbn-q / qgl Goto Github PK

View Code? Open in Web Editor NEW
29.0 15.0 12.0 9.21 MB

Quantum Gate Language (QGL) is a domain specific language embedded in python for specifying quantum gate sequences.

License: Apache License 2.0

Python 99.26% Shell 0.74%
quantum-computing

qgl's Introduction

Quantum Gate Language (QGL)

Build Status

QGL

Overview

Quantum Gate Language (QGL) is a domain specific language embedded in python for specifying pulse sequences.

Our "getting started" documentation is published online from the local file. This documentation includes dependency, installation and basic programming information. The documentation is organized into the following sections:

  1. What is QGL
  2. Dependencies
  3. Installation
  4. Examples
  5. Channels and Qubits
  6. Gate Primitives
  7. Sequences and Concurrent Operations
  8. Pulse Shapes and Waveforms
  9. Compiling and Plotting
  10. Built-in Basic Sequences

Usage

There are a number of QGL example Jupyer notebooks in the QGL/doc folder:

  1. ex1_basic_QGL.ipynb: Basic setup of 'qubit' objects, defining sequences of pulses on qubits, and visualizing these pulse sequences.
  2. ex2_single_qubit_sequences.ipynb: Simple spectroscopy and coherence experiments on a single qubit.
  3. ex3_two_qubit_sequences.ipynb: Examples of two-qubit sequences, including CR gates.

Obviously, we suggest that you start with ex1_basic_QGL.

QGL requires the installation and use of bbndb. bbndb is a shared, versioned, means of storing instrument, qubit, and other configuration information. It is based on the SQLAlchemy framework.

QGL is typically used with Auspex -- an experiment management framework. More sophisticated uses of bbndb, especially usage of a channel library, can be found in the Auspex documentation

Dependencies

Note additional setup information in setup.py. This file is typically used by pip and other package managers.

qgl's People

Contributors

ahelsing avatar bcdonovan avatar billykalfus avatar blakejohnson avatar caryan avatar dellard avatar dieris avatar grahamrow avatar gribeill avatar matthewware avatar ranzani avatar rblazarus avatar rmcgurrin avatar sfallek1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qgl's Issues

Compiler slows down with repeated calls from iPython

In trying to do some benchmarking/profiling I noticed the compiler gets progressively slower.

In [2]: %%timeit
seqs = create_seqs(lines[:500])
compile_to_hardware(seqs, "Test/Test")
   ...: 
Compiled 500 sequences.
Compiled 500 sequences.
Compiled 500 sequences.
Compiled 500 sequences.
1 loop, best of 3: 2.58 s per loop

In [3]: %%timeit
seqs = create_seqs(lines[:500])
compile_to_hardware(seqs, "Test/Test")
   ...: 
Compiled 500 sequences.
Compiled 500 sequences.
Compiled 500 sequences.
Compiled 500 sequences.
1 loop, best of 3: 4.45 s per loop

In [4]: %%timeit
seqs = create_seqs(lines[:500])
compile_to_hardware(seqs, "Test/Test")
   ...: 
Compiled 500 sequences.
Compiled 500 sequences.
Compiled 500 sequences.
Compiled 500 sequences.
1 loop, best of 3: 6.46 s per loop

Presumably some cache is getting larger with each call.

APS2 unrolling/inlining heuristics

We should have an inlining pass that could catch e.g.

  1. repeats of a single instruction 3 or fewer timing takes more instructions that just repeating the pulse. The actual unrolling threshold should probably be higher to account for the jump cost of 7 clocks.
  2. function calls to a one or two pulses should be inlined

Should we stop filewatching?

Now that we let users muck with the YAML directly, those with a penchant for saving invalid intermediate versions (yours truly) may confuse the automatic reloader. @blakejohnson @caryan I wasn't privy to the original design decisions, but were there reasons to not simply check if we need to reparse the ChannelLibrary when a sequence is processed?

A side effect of our current system means that QGL throws loading errors into whichever notebook cell was executed most recently, which eliminates any causal relationship between compiling sequences and generating errors.

RFC: Pulse.hashshape() unstable between runs

When working on #35 ran into the surprising behaviour that Pulse.hashshape() is random between python instances. Probably related to hash being randomized between run in Python 3.3 release notes. I'm not sure whether we should care but it is unexpected.

Pulse caching

Now that we translate to Waveforms early on we should be able to reactivate the memoization we've had commented out for 4! years. We could also make Pulses explicitly immutable perhaps using a namedtuple. The only issue I see is we need some hook to invalidate the cache when pulse parameters are changed by the file watcher.

APS2 firmware v4 support

APS2 v4 firmware has new features we should support in the translator

  • deep waveform memory with prefetching
  • SSB modulation (need to invert sign)
  • frame changes
  • waveform engine select (broadcasting only for now)
  • instruction prefetching

Copy all sequence entries before compiling

Spent some time debugging strange behaviour with @dieris and it once again came down to sequences with multiple references to the same pulse block object getting modified in place by the compiler. In this case the flow was.

  1. Create a sequence with multiple references to the same pulse block. E.g. using itertools.product:
    python tomoblocks = QGL.Tomography.create_tomo_blocks((q1,q2), 4) seqs = [[prep, Id(q1)*Id(q2), meas] for prep,meas in product(tomoblocks, repeat=2)]
  2. compile_to_hardware will then tensor on the slave trigger. This inplace modifies the pulse blocks to add another pulse.
  3. PatternUtils.apply_gating_constraints then globs together adjacent TAZ entries by inplace modifiying the length of the first one. As it does this to every seqeuence the copied reference accumulates all the lengths of all the sequences!

One hammer solution is to implement __deepcopy__ for Pulses and PulseBlocks and the first thing we do in compile is copy everything.

Other trigger pulses.

Would be nice to be able to specify trigger pulses that are not necessarily associated with a qubit measure or control channel.

Plotting performance

44f4c37 was nice because the slider worked even outside the notebook. However, the performance is so abysmal that no one plots sequences any more. Most likely need a bokeh server to store the data and do the updating.

permit 'third-party' device drivers in other modules

The drivers for individual devices are assumed, by the current QGL compiler, to be installed as a module that is part of QGL itself. In theory, we could have third-party drivers at some point, and it would be somewhat awkward if the only way that the compiler can use a driver is if it is copied into the QGL/drivers directory. It would be nicer if it could be loaded from an arbitrary location, under the direction of the user.

Reconsider delay accumulation convention

Right now QGL automatically offsets pulses by the minimum delay to avoid negative delays. This is awkward across multiple APS slices if you want to delay a slave trigger, since it automatically pushes quadrature channel pulses off into the future, thereby canceling the intention. What should we do?

Propagate frame change before first edge pulse

[X90(q5), Z(q5)] + ZX90_CR(q3,q5)

is not supported. The frame change Z(q5) can't find a non-Id Edge pulse here and is never applied. I think that in this case an UPDATE_FRAME to the Edge should be inserted right after RESET_PHASE

unit tests use 'case insensitive / non-preserving' paths

Many unit tests are currently failing due to what appears to be broken paths, where the paths that are failing are all converted to lowercase (while the paths in the git repo are rooted at 'QGL', so basically nothing works at all).

Maybe this was a step taken to get things to working on Windows, but the result is that it doesn't work on Linux.

YAML config CI build

Travis needs updating so that the tests stop failing on the load-from-yaml branch.

single flatten pass

the flatten helper is convenient and so it gets called mulitiple times. However, it adds up to 16% of the total time in the profiling done in #69. APS2Pattern still relies of the list of list for some heuristics but otherwise once we inject WAIT at the beginning of each sequence we should be able to flatten once

Don't plot all points

Since we're plotting lines we don't need to store/display all the waveform points: Bokeh will interpolate for us. This has become more accute since #15 where we store all the waveform data.

Reset sequence improvement

The Reset sequence is a bit cryptic to follow. A few issues

  1. docstring update
  2. more liberal comments required
  3. update for fixed count vs fbcount
  4. don't need count anymore

Waveform repeats unhandled

When dispatched on a single pulse the repeat multimethod just sets the repeat field of the pulse. Aside from another annoying case of Python's mutable class dictionary, this is not an expected part of a Pulse and this is currently unhandled in the APS2 translator. Until we have better unrolling heuristics it seems we should just always use the repeat instruction and let the APS1 unroll_loops handle this for single pulses.

Pinging @blakejohnson because you were most involved in this.

Edge not found in graph

In load-from-yaml branch, occasionally need to create QubitFactory objects again before an EdgeFactory can find the relevant edge. The edge is in the graph, but is not properly matched to source and target. In other words,

channelLib.connectivityG.edges contains the edge of interest, but both conditions

QGL/QGL/ChannelLibrary.py

Lines 263 to 266 in 18179da

if channelLib.connectivityG.has_edge(source, target):
return channelLib.connectivityG[source][target]['channel']
elif channelLib.connectivityG.has_edge(target, source):
return channelLib.connectivityG[target][source]['channel']

are false.

Produce a meta output file with the complete program description

i.e. we want to have:

  • a dictionary from instruments to their compiled sequence files
  • some equivalent information for receivers/digitizers (#91)
    • including some representation of the difference between a segment and a capture event (i.e. if there are multiple MEASs in a sequence
  • label information for swept parameters. Done in #65

Compiling >20k long sequence saturates RAM (>12 GB)

Compiling the following sequences takes ~10 minutes and in Perseus, using ~12 GB RAM and severely slowing it down until seqs is deleted.

import csv  
gatedic = {}  
gatedic['1'] = (X90,a)
gatedic['2'] = (X90,q)
gatedic['3'] = (Y90,a)
gatedic['4'] = (Y90,q)
seqs=[]
with open('gst16-2q.csv', 'rb') as csvfile:
    gatemat = csv.reader(csvfile, delimiter=',', quotechar='|')
    for ind,row in enumerate(gatemat):
        seq = []
        for key in row:
            if key=='5':
                seq+= ZX90_CR(a,q)
            else:
                if key!='0':
                    seq+=[gatedic[key][0](gatedic[key][1])]
        seq+=[MEAS(a)*MEAS(q)]
        seqs+=[seq]
    seqs+=create_cal_seqs((a,q),250)

filenames = compile_to_hardware(seqs,'Test\Test')

gst16-2q.zip

Non-unique pulse key?

This doesn't make any sense to me, but dragScaling = -1 and -2 have the same key (though different shape in Q as expected)

X(q3, dragScaling = -1).hashshape() == X(q3, dragScaling = -2).hashshape()
True

Unicode character 'circled times'

Getting this error in save_code for any sequence with a tensor product.

UnicodeEncodeError: 'charmap' codec can't encode character u'\u2297' in position 7: character maps to <undefined>

QGL/QGL/awg doesn't exist by default; causes errors

If QGL/QGL/awg doesn't already exist, then creating the subdirectory for the results fails, halting compile_to_hardware:

  File "../../src/python/pyqgl2/main.py", line 401, in <module>
    opts.suffix)
  File "../../src/python/pyqgl2/main.py", line 342, in qgl2_compile_to_hardware
    return compile_to_hardware([scheduled_seq], filename, suffix)
  File "/home/dellard/github/QGL/QGL/Compiler.py", line 316, in compile_to_hardware
    save_code(seqs, fileName + suffix)
  File "/home/dellard/github/QGL/QGL/Compiler.py", line 754, in save_code
    os.mkdir(targetFolder)

This might be my fault (maybe there are instructions I overlooked, that said to create that directory).

error in unittest: test_RB_SimultaneousRB_AC (APS2)

Observed on the HEAD of master, with the default config.json file.

(it's possible my other config files are out of sync, but this looks like a code problem, not a config problem)

ERROR: test_RB_SimultaneousRB_AC (tests.test_Sequences.TestAPS2)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/dellard/RF/QGL/tests/test_Sequences.py", line 374, in test_RB_SimultaneousRB_AC
    SimultaneousRB_AC((self.q1, self.q2), (seqs1, seqs2))
  File "/home/dellard/RF/QGL/QGL/BasicSequences/RB.py", line 294, in SimultaneousRB_AC
    metafile = compile_to_hardware(seqsBis, 'RB/RB')
  File "/home/dellard/RF/QGL/QGL/Compiler.py", line 349, in compile_to_hardware
    wireSeqs = compile_sequences(seqs, channels)
  File "/home/dellard/RF/QGL/QGL/Compiler.py", line 486, in compile_sequences
    wires = compile_sequence(seqs[0], channels)
  File "/home/dellard/RF/QGL/QGL/Compiler.py", line 551, in compile_sequence
    wires, chan, block.pulses[chan].frameChange)
  File "/home/dellard/RF/QGL/QGL/Compiler.py", line 585, in propagate_node_frame_to_edges
    edge = ChannelLibraries.channelLib.connectivityG.edges[predecessor, chan]['channel']
TypeError: 'method' object is not subscriptable

make location of 'config.json' configurable

Reported by Robert McGurrin:

When QGL is imported, config.json is read from the QGL package's directory (see QGL/config.py). I'd like to change this so that the config.json file can be read from alternate locations.

I'd like to add the capability of loading the config.json file from the user's local directory if it exists: otherwise the default config.json file should be loaded from the QGL/config.py when the package is loaded.

I'd also like to add the capability for the User to specify the config file using a function like:
config('/somepath/config.json')

The purpose of these changes is to make it easier for the user to change configurations.

immutable cached Waveforms and APS2 instructions

Compiling longish sequences spends ~10% of them time creating APS2 instructions and converting them to 8 byte machine code and another 5% of the time creating Compiler.Waveform, most of which is in hash_shape. It also uses a non-trivial amount of memory. It seems both of these could be solved with conversion to immutable named tuples and caching.

from QGL import *

import psutil
process = psutil.Process(os.getpid())
print("Memory usage: {} MB".format(process.memory_info().rss // (1 << 20)))

q = QubitFactory("q1")
pulseLib = [Cliffords.AC(q, cliffNum) for cliffNum in range(24)]
# over write AC Id to have finite length
pulseLib[0] = Id(q, 2e-8)

def create_seqs(file):
    seqs = []
    with open(file, 'r') as FID:
        lines = FID.readlines()

    for line in lines:
        seq = [pulseLib[int(pulseNum)] for pulseNum in line.split(',')]
        seq.append(MEAS(q))
        seqs.append(seq)

    seqs += create_cal_seqs((q,), 500)

    return seqs

file = "sequence_numbers.csv"
seqs = create_seqs(file)
filenames = compile_to_hardware(seqs, "test/test" )
print("Memory usage: {} MB".format(process.memory_info().rss // (1 << 20)))
Compiled 4505 sequences.
Memory usage: 659 MB

%%timeit
seqs = create_seqs(file)
filenames = compile_to_hardware(seqs, "test/test" )
Compiled 4505 sequences.
Compiled 4505 sequences.
Compiled 4505 sequences.
Compiled 4505 sequences.
1 loop, best of 3: 57.9 s per loop

%load_ext snakeviz
%%snakeviz
seqs = create_seqs(file)
compile_to_hardware(seqs, "test/test" )

sequence_numbers.csv.zip

num_measurements calculation is wrong when measurements are not simultaneous

For example:

[MEAS(q1)*MEAS(q2)]

we count as 1 measurement, but

[MEAS(q1), MEAS(q2)]

we count as 2 measurements. The num_measurements calculation should probably be done on a per channel basis. Then we have the new problem of choosing a value when the number of measurements is not uniform over the set of channels, but requires more infrastructure to solve.

cc @dieris

QGL IR

This issue is to track discussions about supporting a simple, pure text IR for QGL. Some initial notion of what that might look like from this morning's discussion:

QBIT_q1 = Qubit('q1')
QBIT_q2 = Qubit('q2')
OPAQUE VOTE

MEAS(QBIT_q1) -> 0x0010
MEAS(QBIT_q2) -> 0x0011
VOTE(0x0010, 0x0011) -> 0x0013
LoadCmp(0x0013)
CmpEq(0)
Goto(BlockLabel('if_1'))
Id(QBIT_q1)
Goto(BlockLabel('if_end_1'))
BlockLabel('if_1')
X(QBIT_q1)
BlockLabel('if_end_1')
X90(QBIT_q1)

update waveform library post compiling

For sequences where are a burden to recompile and we have some small set of cannonical pulses it would be nice to be able to update the waveform library in place after recalibrating pulse parameters. We'd need something like:

  1. pickle the offset dictionary with usable keys like "X90(q1)"
  2. a helper function to update a h5 file given this dictionary

suggestion from @marcusps and @dieris

Either document how to setup git-lfs, or get rid of it

The example test output files are represented in the git repo by references to git lfs objects. It doesn't look like this dependency is mentioned anywhere (or at least not blatantly enough to catch my eye), so my builds were failing.

I don't like adding dependencies (especially ones that aren't compatible with some platforms), and I don't know that we're benefitting from using git-lfs here anyway. I would recommend either getting rid of it (if that's possible), or at minimum adding documentation to the README.md explaining how to acquire, install, and use git-lfs correctly.

Event type

We need some better way to represent zero time "events" such as control-flow. Using simply zero time is not enough because by the time we get to creating the instruction stream we have simultaneous events for which the instruction ordering may be ambiguous. E.g. is a WAIT part of a repeat loop so does the LOAD_REAPEAT go before or after the WAIT?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.