Commit 64588684 authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

adding qsc demo

parent 8744218d
Loading
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
# MLIR+QIR as an IR for Quantum-Classical Computing
Here we demonstrate the utility of the QIR and MLIR for enabling the development of compilers for available quantum languages. 

Our motivation with this demonstration is to show how MLIR+QIR enable the main feature of a robust IR: mapping multiple languages or programming approaches to multiple backends.

Our examples will be simple GHZ and Bell circuits for NISQ and FTQC execution, respectively. 

## Goals

- Demonstrate the utility of the MLIR and QIR for creating compilers and executable code for available quantum languages.

- Demonstrate MLIR as language-level IR for quantum-classical computing (control flow from Standard/Affine, etc.).

- Demonstrate write-once, run-on-any available quantum backend and multiple languages to multiple backends.

- Demonstrate accessibility of MLIR and QIR for available Pythonic circuit construction frameworks. 

## NOTES
Poor man's way to count instructions in the LLVM output
```bash
qcor --emit-llvm bell.qasm -O3 &> bell_tmp.ll && cat bell_tmp.ll | grep '%* = \|call' | wc -l && rm bell_tmp.ll
```

## Outline

### Demo 1, Simple Language Lowering, Helpful Classical Passes

- Show off the code in `ghz.qasm`. Note the gate function. 
- Goal is to highlight the unique benefits of mutliple levels of IR abstraction
- Compile and run, note we want to run this in NISQ mode
```bash
qcor -qrt nisq -shots 1000 ghz.qasm -o ghz.x
./ghz.x
```
- Run the compile command with `-v` to show the steps in the workflow
```bash
qcor -v ghz.qasm 
```
- Show the MLIR and QIR Output, noting the Affine Loop (and that we benefit from leveraging classical IR Dialects)
```bash
qcor --emit-mlir ghz.qasm
qcor --emit-llvm ghz.qasm
```
- Show simple classical optimizations, here inlining and loop unrolling
```bash
qcor --emit-milr -O3 ghz.qasm
qcor --emit-llvm -O3 ghz.qasm
```

- Show off the FTQC mode code, noting that the for loop has a conditional statement
- Compile and run with -v 
```bash 
qcor -qrt ftqc -v bell.qasm -o bell.x
./bell.x
```
- Show the MLIR and QIR
```bash
qcor --emit-mlir bell.qasm
qcor --emit-llvm bell.qasm
```
- Note the need for multiple layers of IR, can't get opts from MLIR, but can from LLVM
```bash
qcor --emit-mlir -O3 bell.qasm
qcor --emit-llvm -O3 llvm.qasm
qcor --emit-llvm bell.qasm -O0 &> bell_tmp.ll && cat bell_tmp.ll | grep '%* = \|call' | wc -l && rm bell_tmp.ll
qcor --emit-llvm bell.qasm -O3 &> bell_tmp.ll && cat bell_tmp.ll | grep '%* = \|call' | wc -l && rm bell_tmp.ll
```

- Switch to the Python script to show the audience how all the MLIR/QIR infrastructure just shown can also be generated from Python. Moreover, that Qiskit and Pyquil can also generate MLIR/QIR. 
- Note how `qjit` is the QCOR quantum just-in-time compiler, produces executable functions that enable `mlir()` and `llvm()` methods. 

+33 −0
Original line number Diff line number Diff line
// qcor bell.qasm -o bell.x
// ./bell.x 
//
// qcor -v bell.qasm -o bell.x
// qcor --emit-mlir bell.qasm
// qcor --emit-llvm bell.qasm

OPENQASM 3;

qubit q[2];

const shots = 15;
int count = 0;

for i in [0:15] {
    
    // Create Bell state
    h q[0];
    cnot q[0], q[1];
    
    // Measure and assert both are equal
    bit c[2];
    c = measure q;
    if (c[0] == c[1]) {
        print("iter", i, ": measured =", c[0], c[1]);
        count += 1;
    }

    reset q;
}

print("count is", count);
+34 −0
Original line number Diff line number Diff line
// NISQ Mode Execution
//
// Compile and run on qpp 
// qcor ghz.qasm -o ghz.x -qrt nisq -shots 1000
// ./ghz.x 
//
// Show on local aer with noisy backend
// qcor ghz.qasm -o ghz.x -qrt nisq -shots 100 -qpu aer:ibmq_sydney
// ./ghz.x 
//
// (Now Show off how this works, MLIR + QIR)
//
// qcor -v ghz.qasm -o ghz.x
// qcor --emit-mlir ghz.qasm
// qcor --emit-llvm ghz.qasm


OPENQASM 3;

const n_qubits = 3;

qubit q[n_qubits];

gate ctrl_x a, b {
    ctrl @ x a, b;
}

h q[0];
for i in [0:n_qubits-1] {
    ctrl_x q[i], q[i+1];
}

bit c[n_qubits];
c = measure q;
 No newline at end of file
+45 −0
Original line number Diff line number Diff line
from qcor import qjit, qalloc
import qiskit

# Generate 3-qubit GHZ state with Qiskit
circ = qiskit.QuantumCircuit(3)
circ.h(0)
circ.cx(0, 1)
circ.cx(1, 2)
circ.measure_all()

# Creates a kernel parameterized on a qreg
qcor_kernel = qjit(circ)

# Allocate the qreg
q = qalloc(3)

# Convert to MLIR and print
mlir = qcor_kernel.mlir(q)
print(mlir)

# Convert to QIR and print
qir = qcor_kernel.qir(q, opt=3)
print(qir)

from pyquil import Program
from pyquil.gates import CNOT, H, MEASURE
  
p = Program()
p += H(0)
p += CNOT(0, 1)
ro = p.declare('ro', 'BIT', 2)
p += MEASURE(0, ro[0])
p += MEASURE(1, ro[1])

# This requires rigetti/quilc docker image
qcor_kernel_pyquil = qjit(p, opt=3)
r = qalloc(2)

# Convert to MLIR and print
mlir = qcor_kernel_pyquil.mlir(r)
print(mlir)

# Convert to QIR and print
qir = qcor_kernel_pyquil.qir(r)
print(qir)
 No newline at end of file
+23 −0
Original line number Diff line number Diff line
# MLIR as an IR enabling Quantum Optimizations
Here we demonstrate the utility of the MLIR for quantum compiler optimization passes. The specific goal is to highlight the need for an SSA-based IR for quantum computing. We want to demonstrate select common quantum optimizations and how the MLIR enables their implementation. 

## Goals

- Demonstrate the benefits of an SSA-based IR for quantum computing.
- Demonstrate common quantum optimizations (ID pairs, rotation merging, single-qubit gate merge, inlining, permute and cancel)
- Demonstrate classical optimizations that enable new quantum optimizations (inlining and loop unrolling)

## Outline
1. Start with `simple_opts.qasm`, walk through the various sections showing different code patterns that could be optimized (removed fully, or just reduced). Note what should remain after full optimization
2. Emit the unoptimized MLIR, note that all the quantum instructions are there (when they really do nothing).
3. Show the optimized MLIR, note how it was able to reduce to a couple instructions. 
4. Show the unoptimized LLVM/QIR
5. Show the optimized LLVM/QIR. 
6. Use `--pass-timing` to show exactly what was run.  
7. Move on to `trotter_unroll_simple.qasm` and note the `for` loop will naively place ID pairs next to each other. Show the unoptimized MLIR, noting the `affine.for` loop. 
8. Show the optimized MLIR, noting the `affine.for` is gone, and the compiler has optimized the code to the `for` loop body. 
9. Show the same for unoptimized and optimized LLVM. 
10. Open `trotter_unroll_with_inlining.qasm` to demonstrate a bit more complexity, and how inlining+ loop unrolling can work together to further optimized the code. Highlight the necessity of any Quantum IR to support both classical and quantum optimizations. 
11. Go through the same workflow, unoptimized/optimized MLIR, unoptimized/optimized QIR.
12. Show `-print-final-submission` for unoptimized and optimized.
## Notes:
Loading