<commit_before># Getting Started with QGL

## Table of Contents

1. [What is QGL](#What-is-QGL)
1. [Dependencies](#Dependencies)
1. [Installation](#Installation)
1. [Examples](#Examples)
1. [Channels and Qubits](#Channels-and-Qubits)
1. [Gate Primitives](#Gate-Primitives)
1. [Sequences and Concurrent Operations](#Sequences-and-Concurrent-Operations)
1. [Pulse Shapes and Waveforms](#Pulse-Shapes-and-Waveforms)
1. [Compiling and Plotting](#Compiling-and-Plotting)
1. [Built-in Basic Sequences](#Built-in-Basic-Sequences)


## What is QGL <a name="What-is-QGL"></a>

Quantum Gate Language (QGL) is a domain specific programming language embedded in python for
specifying gate sequences on quantum processors. It is a low-level language in
the sense that users write programs at the level of gates on physical qubits.
While the QGL developers pay particular attention to the requirements of
superconducting qubit systems, its structure is generic to any qubit system with
dipole-coupled qubits in a rotating frame. In such systems, the rotation axis in
the X-Y plane is determined by the pulse phase, and Z-axis rotations may be
achieved through *frame updates*.


## Dependencies <a name="Dependencies"></a>

QGL is embedded in the python programming language. Currently, it is dependent on python versions 3.6+. QGL programming constructs look very similar to python programming constructs.    

QGL relies on the concept of a "channel" to represent a qubit. The channel embodies the physical realization of the qubit in an experimental configuration or a simulation configuration. These characteristic can include physical qubit attributes, simulation or equipment mappings, and physical couplings between qubits. This configuration information is specified and maintained in an associated database, [bbndb](https://github.com/BBN-Q/bbndb), which is necessary for compiling the QGL program into inputs for physical systems (such as realizable signals/pulses) or simulation inputs (native instruction set). 

While QGL is *not* dependent on [Auspex](https://github.com/BBN-Q/auspex), Auspex is an experiment control framework which greatly facilitates executing QGL programs on laboratory hardware. Auspex provides constructs for abstracting (defining) instruments, connectivity, and post processing to enable "hands off" experimental control of sophiscated experiments on a variety of laboratory equipment including AWGs, digitizers, current sources, etc.    



## Installation <a name="Installation"></a>

QGL can be downloaded from GitHub:

```https://github.com/BBN-Q/QGL/archive/master.zip```   

Or QGL can be cloned from GitHub to participate in QGL development:

```git clone https://github.com/BBN-Q/qgl.git```   

And subsequently installed using pip:

```
cd QGL    
pip install -e .       
```

which will automatically fetch and install all of the requirement packages. This process
will execute the package requirements enumerated in setup.py.  
   
## Examples: where to start <a name="Examples"></a>

QGL comes with a number of example jupyter notebook files that illustrate most of the basic
QGL programming concepts (in the QGL/doc directory):

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

In addition to these example notebooks for manipulating qubits, `channel_libriary.ipynb` provides a 
basic Channel Library that is used by `ex2_single_qubit_sequences` and `ex3_two_qubit_sequences` 
example notebooks. 


## Channels and Qubits <a name="Channels-and-Qubits"></a>

Many quantum processors require non-uniform control parameters to achieve
high-fidelity gates across all qubits in the device. To support this need, QGL
provides a number of *channel* objects to store the *individual* parameters
needed to control or measure particular qubits.

Gates in QGL operate on resources known as `logical channels`. For control, these
channels are either `Qubits` or `Edges`. The `qubit` channels encode properties
specific to manipulating individual qubits in quantum processor, while `Edges`
encode the connectivity of the device. Since some 2-qubit gates have a preferred
directionality due to the physical parameters of the device, `edges` correspond
to a *directed* edge in the qubit connectivity graph. Qubit measurements instead
act upon a third `logical channel` type which is the `measurement` channel. A
final logical resource type is the `logical marker channel` which is used to carry
ancillary pulse information for things such as event triggers.

All `logical channels` are associated with `physical channels` through a Channel 
Library database in order for the QGL
compiler to produce pulse files for the target hardware. The setup of this
mapping is briefly described in the QGL example notebooks in this folder 
and a more detailed description is provide with the Auspex tools. 

While setup of these channels is important for final sequence compilation, QGL
programs typically refer only to `Qubit` channels. Actions on other channel
types may be implied by the operation. For example, to create a `Qubit` object
in QGL, one can write:
```
cl = ChannelLibrary(db_resource_name=":memory:")
q1 = cl.new_qubit("q1")
```
where the Channel Library contains the "physical" information for the logical "qubit" 
channel. 

The `new_qubit` method returns a `Qubit` object with the properties defined
by the name `q1` if found in the channel library. If the name is not found, then
the users gets a `Qubit` object with the default properties.

## Gate Primitives <a name="Gate-Primitives"></a>

The underlying representation of all QGL operations is a `Pulse` object.
However, users are not expected to create `Pulses` directly, but instead
interact with various pre-defined one- and two-qubit primitives.



### Single-Qubit Operations

QGL provides the following single-qubit gates:
```
# generic rotation angle and phase
Utheta(q, angle, phase)

# generic rotations about a specific axis (phase)
Xtheta(q, angle)
Ytheta(q, angle)
Ztheta(q, angle)

# generic rotations of a specific angle
U(q, phase)
U90(q, phase)

# rotations of particular angle and phase
# X (phase = 0)
X(q)    # rotates by pi (180 degrees)
X90(q)  # rotates by +pi/2
X90m(q) # rotates by -pi/2

# Y (phase = pi/2)
Y(q)
Y90(q)
Y90m(q)

# just frame-updates
Z(q)
Z90(q)
Z90m(q)

# identity (delay or no-op)
Id(q, length) # length parameter is optional

# measurement
MEAS(q)
```

Due to the utility of Clifford-group operations in characterizing gate
performance, QGL also directly provides a primitive to implement the 24-element
single-qubit Clifford group:

```
# atomic Clifford operation on 1-qubit
AC(q, n)
```

This method is "atomic" because it implements the full 1-qubit Clifford group
with one pulse per element, as opposed to requiring a sequence of the primitives
already given above. We known of no canonical way to specify the elements of the
Clifford group; consequently, `AC` identifies which Clifford by a numerical
index (0-23). See the definition of `AC` in `PulsePrimitives.py` or the
definition of `C1` in `Cliffords.py` to find our enumeration of the group.

### Two-qubit Operations

QGL provides only one high-level two-qubit primitive, `CNOT`. The implementation
of CNOT may be chosen by specifying the `cnot_implementation` key in QGL's
[config.py](./QGL/config.py) file.

```python
# high-level primitives
CNOT(q1, q2)

# mid-level primitives
CNOT_simple(q1, q2) # a single-pulse gate on Edge(q1, q2)
CNOT_CR(q1, q2)     # an "echoed" cross-resonance CNOT gate on Edge(q1, q2)
ZX90_CR(q1, q2)     # a ZX90 on Edge(q1, q2) implemented with "echoed"
                    # cross-resonance pulses

# lowest-level primitives
echoCR(q1, q2)  # A "echoed" cross-resonance pulse
```

### Composite Pulses 

Occasionally one wants to construct a sequence of pulses and treat them as if
the entire sequence were a single pulse. For this, QGL allows pulses to be
joined with the `+` operator. This allows, for example, us to define
```
def hadamard(q):
    return Y90(q) + X(q)
```
and then use `hadamard(q)` just like any other pulse primitive, even though it is
composed of a sequence of two pulses.

### Additional Pulse Parameters
<commit_msg>fixed link to config.py<commit_after># Getting Started with QGL

## Table of Contents

1. [What is QGL](#What-is-QGL)
1. [Dependencies](#Dependencies)
1. [Installation](#Installation)
1. [Examples](#Examples)
1. [Channels and Qubits](#Channels-and-Qubits)
1. [Gate Primitives](#Gate-Primitives)
1. [Sequences and Concurrent Operations](#Sequences-and-Concurrent-Operations)
1. [Pulse Shapes and Waveforms](#Pulse-Shapes-and-Waveforms)
1. [Compiling and Plotting](#Compiling-and-Plotting)
1. [Built-in Basic Sequences](#Built-in-Basic-Sequences)


## What is QGL <a name="What-is-QGL"></a>

Quantum Gate Language (QGL) is a domain specific programming language embedded in python for
specifying gate sequences on quantum processors. It is a low-level language in
the sense that users write programs at the level of gates on physical qubits.
While the QGL developers pay particular attention to the requirements of
superconducting qubit systems, its structure is generic to any qubit system with
dipole-coupled qubits in a rotating frame. In such systems, the rotation axis in
the X-Y plane is determined by the pulse phase, and Z-axis rotations may be
achieved through *frame updates*.


## Dependencies <a name="Dependencies"></a>

QGL is embedded in the python programming language. Currently, it is dependent on python versions 3.6+. QGL programming constructs look very similar to python programming constructs.    

QGL relies on the concept of a "channel" to represent a qubit. The channel embodies the physical realization of the qubit in an experimental configuration or a simulation configuration. These characteristic can include physical qubit attributes, simulation or equipment mappings, and physical couplings between qubits. This configuration information is specified and maintained in an associated database, [bbndb](https://github.com/BBN-Q/bbndb), which is necessary for compiling the QGL program into inputs for physical systems (such as realizable signals/pulses) or simulation inputs (native instruction set). 

While QGL is *not* dependent on [Auspex](https://github.com/BBN-Q/auspex), Auspex is an experiment control framework which greatly facilitates executing QGL programs on laboratory hardware. Auspex provides constructs for abstracting (defining) instruments, connectivity, and post processing to enable "hands off" experimental control of sophiscated experiments on a variety of laboratory equipment including AWGs, digitizers, current sources, etc.    



## Installation <a name="Installation"></a>

QGL can be downloaded from GitHub:

```https://github.com/BBN-Q/QGL/archive/master.zip```   

Or QGL can be cloned from GitHub to participate in QGL development:

```git clone https://github.com/BBN-Q/qgl.git```   

And subsequently installed using pip:

```
cd QGL    
pip install -e .       
```

which will automatically fetch and install all of the requirement packages. This process
will execute the package requirements enumerated in setup.py.  
   
## Examples: where to start <a name="Examples"></a>

QGL comes with a number of example jupyter notebook files that illustrate most of the basic
QGL programming concepts (in the QGL/doc directory):

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

In addition to these example notebooks for manipulating qubits, `channel_libriary.ipynb` provides a 
basic Channel Library that is used by `ex2_single_qubit_sequences` and `ex3_two_qubit_sequences` 
example notebooks. 


## Channels and Qubits <a name="Channels-and-Qubits"></a>

Many quantum processors require non-uniform control parameters to achieve
high-fidelity gates across all qubits in the device. To support this need, QGL
provides a number of *channel* objects to store the *individual* parameters
needed to control or measure particular qubits.

Gates in QGL operate on resources known as `logical channels`. For control, these
channels are either `Qubits` or `Edges`. The `qubit` channels encode properties
specific to manipulating individual qubits in quantum processor, while `Edges`
encode the connectivity of the device. Since some 2-qubit gates have a preferred
directionality due to the physical parameters of the device, `edges` correspond
to a *directed* edge in the qubit connectivity graph. Qubit measurements instead
act upon a third `logical channel` type which is the `measurement` channel. A
final logical resource type is the `logical marker channel` which is used to carry
ancillary pulse information for things such as event triggers.

All `logical channels` are associated with `physical channels` through a Channel 
Library database in order for the QGL
compiler to produce pulse files for the target hardware. The setup of this
mapping is briefly described in the QGL example notebooks in this folder 
and a more detailed description is provide with the Auspex tools. 

While setup of these channels is important for final sequence compilation, QGL
programs typically refer only to `Qubit` channels. Actions on other channel
types may be implied by the operation. For example, to create a `Qubit` object
in QGL, one can write:
```
cl = ChannelLibrary(db_resource_name=":memory:")
q1 = cl.new_qubit("q1")
```
where the Channel Library contains the "physical" information for the logical "qubit" 
channel. 

The `new_qubit` method returns a `Qubit` object with the properties defined
by the name `q1` if found in the channel library. If the name is not found, then
the users gets a `Qubit` object with the default properties.

## Gate Primitives <a name="Gate-Primitives"></a>

The underlying representation of all QGL operations is a `Pulse` object.
However, users are not expected to create `Pulses` directly, but instead
interact with various pre-defined one- and two-qubit primitives.



### Single-Qubit Operations

QGL provides the following single-qubit gates:
```
# generic rotation angle and phase
Utheta(q, angle, phase)

# generic rotations about a specific axis (phase)
Xtheta(q, angle)
Ytheta(q, angle)
Ztheta(q, angle)

# generic rotations of a specific angle
U(q, phase)
U90(q, phase)

# rotations of particular angle and phase
# X (phase = 0)
X(q)    # rotates by pi (180 degrees)
X90(q)  # rotates by +pi/2
X90m(q) # rotates by -pi/2

# Y (phase = pi/2)
Y(q)
Y90(q)
Y90m(q)

# just frame-updates
Z(q)
Z90(q)
Z90m(q)

# identity (delay or no-op)
Id(q, length) # length parameter is optional

# measurement
MEAS(q)
```

Due to the utility of Clifford-group operations in characterizing gate
performance, QGL also directly provides a primitive to implement the 24-element
single-qubit Clifford group:

```
# atomic Clifford operation on 1-qubit
AC(q, n)
```

This method is "atomic" because it implements the full 1-qubit Clifford group
with one pulse per element, as opposed to requiring a sequence of the primitives
already given above. We known of no canonical way to specify the elements of the
Clifford group; consequently, `AC` identifies which Clifford by a numerical
index (0-23). See the definition of `AC` in `PulsePrimitives.py` or the
definition of `C1` in `Cliffords.py` to find our enumeration of the group.

### Two-qubit Operations

QGL provides only one high-level two-qubit primitive, `CNOT`. The implementation
of CNOT may be chosen by specifying the `cnot_implementation` key in QGL's
[config.py](../QGL/config.py) file.

```python
# high-level primitives
CNOT(q1, q2)

# mid-level primitives
CNOT_simple(q1, q2) # a single-pulse gate on Edge(q1, q2)
CNOT_CR(q1, q2)     # an "echoed" cross-resonance CNOT gate on Edge(q1, q2)
ZX90_CR(q1, q2)     # a ZX90 on Edge(q1, q2) implemented with "echoed"
                    # cross-resonance pulses

# lowest-level primitives
echoCR(q1, q2)  # A "echoed" cross-resonance pulse
```

### Composite Pulses 

Occasionally one wants to construct a sequence of pulses and treat them as if
the entire sequence were a single pulse. For this, QGL allows pulses to be
joined with the `+` operator. This allows, for example, us to define
```
def hadamard(q):
    return Y90(q) + X(q)
```
and then use `hadamard(q)` just like any other pulse primitive, even though it is
composed of a sequence of two pulses.

### Additional Pulse Parameters

{'fraction_non_alphanumeric': 0.07496003315767659, 'fraction_numerical': 0.008526259695659897, 'mean_word_length': 4.748808713410483, 'pattern_counts': {'":': 2, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4350100', 'n_tokens_mistral': 4992, 'n_tokens_neox': 4609, 'n_words': 2235}