Repository: quantumlib/Cirq

Issue:
Add QASM importer
As the other leg of #44 and maybe a partial solution to #862 depending on the gate sets - as we discussed on today's sync meeting, a QASM importer would be useful. 

I'm happy to design and implement it.  


Discussion:
The issue of specifying a gate set shouldn't come up when importing. Just try to match the gate specified in the QASM as closely as possible, and for now fail if there's no equivalent in cirq.
@Strilanc  - this is my current understanding. Can you please comment? 
I'm leaning towards Option 2 in terms of lexer/parser infrastructure. 

Let me know what you think! 

---------

Design for QASM import for Cirq
=====

## Importing cirq.Circuit from a QASM file

The following call will create a circuit defined by the input file. Cirq takes care of opening, reading and closing the file.   

```python
c = cirq.Circuit.from_qasm_file('filename.qasm')

```

The following call will create a circuit defined by the input QASM string:

```python
c = cirq.Circuit.from_qasm_string("""OPENQASM 2.0;
include "qelib1.inc";


// Qubits: [q0]
qreg q[1];


rx(pi*0.123) q[0];
""")

```

## Supported Control statements 


|Cirq supports it?| Statement|Description|Example|
|--| -------- | -------- | -------- |
| supported | OPENQASM 2.0;                       | Denotes a file in Open QASM format                | OPENQASM 2.0;             |
 | supported (see mapping qubits) | qreg name[size];                    | Declare a named register of qubits                | qreg q[5];                |
 | supported (see mapping classical register to measurement keys) | creg name[size];                    | Declare a named register of bits                  | creg c[5];                |
 | supported ONLY to include the standard “qelib1.inc” lib for compatibility | include “filename”;                 | Open and parse another source file                | include “qelib1.inc”;     |
 | NOT supported | gate name(params) qargs             | Declare a unitary gate                            |               |
 | NOT supported | opaque name(params) qargs;          | Declare an opaque gate                            |               |
 | supported | // comment text                     | Comment a line of text                            | // oops!                  |
 | supported | U(theta,phi,lambda) qubit\|qreg;    | Apply built-in single qubit gate(s)               | U(pi/2,2\*pi/3,0) q[0];   |
 | supported | CX qubit\|qreg,qubit\|qreg;         | Apply built-in CNOT gate(s)                       | CX q[0],q[1];             |
 | supported | measure qubit\|qreg -> bit\|creg;   | Make measurement(s) in `Z` basis            | measure q -> c;           |
 | NOT supported | reset qubit\|qreg;                  | Prepare qubit(s) in`\|0>`             | `reset q[0]; ` |
 | supported for ONLY the supported subset of standard gates defined in "qelib1.inc" | gatename(params) qargs;             | Apply a user-defined unitary gate                 | crz(pi/2) q[1],q[0];      |
 | NOT supported | if(creg==int) qop;                  | Conditionally apply quantum operation             | if(c==5) CX q[0],q[1];    |
 |  NOT supported | barrier qargs;                      | Prevent transformations across this source line   | barrier q[0],q[1];        |


## Gate conversion rules 

Note: As the standard  Quantum Experience standard and user defined gates ([qelib.inc](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/qasm/libs/qelib1.inc)) are all defined based on `U` and `CX`, theoretically we could just use `cirq.QasmUGate`, `cirq.CX` based on their definitions, but that would require supporting the custom unitary gate control statement and also explicitly mapping to cirq gates is going to result in a cirq-native circuit. 

| QE gates | Cirq translation | Notes |
| -------- | -------- | -------- |
| u3(θ,φ,λ) | `QasmUGate (λ,θ,φ)`  | `QasmUGate` constructor takes angles reverse order, to cater for the rotation intuition RZ(λ) first, RY(θ), RZ(φ) last|
|u2(φ,λ) = u3(π/2,φ,λ) | `QasmUGate (λ,π/2,φ)` |  | 
|u1 (φ) = u3(0,0,λ) | `QasmUGate (λ,0,0)` | | 
| cx | `cirq.CNOT` | | 
| id | `cirq.Identity` | |  
| u0(γ) | NOT supported | this is the "WAIT gate" for length γ in QE| 
| x | `cirq.X` || 
| y | `cirq.Y` || 
| z | `cirq.Z` || 
| h | `cirq.H` || 
| s | `cirq.S` || 
| sdg | `cirq.S**-1` || 
| t | `cirq.T` || 
| tdg| `cirq.T**-1` ||
| rx(theta) | `cirq.RX` || 
| ry(theta) | `cirq.RY` || 
| rz(phi) |`cirq.RZ` || 
| cz | `cirq.CZ` || 
| swap| `cirq.SWAP` || 
| ch| `cirq.CH` || 
| ccx| `cirq.CCX` || 
| cswap| `cirq.CSWAP` || 
| crz(λ)| NOT supported || 
| cu1(λ)| NOT supported || 
| cu3(θ,φ,λ)| NOT supported || 
| rzz(θ) two-qubit ZZ rotation | NOT supported || 

## Mapping quantum registers to qubits 

For a quantum register `qreg qfoo[n];` the QASM importer will create `cirq.NamedQubit`s named `qfoo_0`..`qfoo_<n-1>`. 

## Mapping classical registers to measurement keys

For a classical register `creg cbar[n];` the QASM importer will create measurement keys named `cbar_0`..`cbar_<n-1>`. 

## Implementing the parser 

### option 1: reuse qiskit infrastructure 

**1.a - Implement CirqBackend** 
Similar to [CircuitBackend](https://github.com/ajavadia/qiskit-terra/blob/stable/qiskit/unroll/_circuitbackend.py) we could implement a CirqBakcend and reuse the nice qiskit Lexer and Parser infrastructure. 

However this would introduce dependency on qiskit's internals that might break unexpectedly. Their master branch doesn't even have the Unroller infrastructure anymore. Convenient (if works!) but scary. 

Also, as we are scoping our QASM importer to not support custom gates, conditionals, etc. our parsing and execution logic could be significantly simpler. 

**1.b - Reuse qiskit's QASM AST** 
 [qiskit is using](https://github.com/ajavadia/qiskit-terra/tree/master/qiskit/qasm) PLY that is easy to use and relatively stable (at least they still have it in master!). We could just iterate through the AST, and error out on unsupported nodes. 
Benefit: 
* quick to get started
* QASM evolution will likely evolve this parse - lower maintenance overhead 
* handles a large part of the compilation for us  

Con: 
* introduces qiskit dependency 
* it would be nice to throw an error in the lexer when there is an unsupported token - instead of parsing the tree and throwing an error there - it's less user friendly

### option 2: write our own grammar, lexer, parser, execution

We have to evaluate some frameworks for this (e.g. [qiskit is using](https://github.com/ajavadia/qiskit-terra/tree/master/qiskit/qasm) PLY, that looks great but there is a ton of others, including ANTLR that can generate a python parser from a grammar). 

Benefit: 
- the Cirq supported grammar is explicit
- user-friendly errors out of the box when there is an issue in a larger QASM file 
- this design is more extensible in the future if we want to support more complex QASM features like custom gate creation.  

Con: 
- it's a bit involved: 
   - might complicate release process if the solution involves code generation
   - we have to evaluate our options

### option 3: just iterate on lines, parse with regex

This sub-grammar is simple enough that we can do something similar to how pyzx did it in their [QasmParser](https://github.com/Quantomatic/pyzx/blob/2e44693ce1ad70b3556b9abe82cf002307a5e83a/pyzx/circuit.py#L625-L777). 

Benefit: quick and easy 
Con: 
- more complex QASM features are going to be tricky to implement this way 
- the grammar is not explicitly stated

Resolving patch:
diff --git a/cirq/contrib/qasm_import/__init__.py b/cirq/contrib/qasm_import/__init__.py
--- a/cirq/contrib/qasm_import/__init__.py
+++ b/cirq/contrib/qasm_import/__init__.py
@@ -12,5 +12,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from cirq.contrib.qasm_import.exception import (QasmException)
-from cirq.contrib.qasm_import.qasm import (QasmCircuitParser)
+from cirq.contrib.qasm_import.exception import QasmException
+from cirq.contrib.qasm_import.qasm import circuit_from_qasm
diff --git a/cirq/contrib/qasm_import/qasm.py b/cirq/contrib/qasm_import/qasm.py
--- a/cirq/contrib/qasm_import/qasm.py
+++ b/cirq/contrib/qasm_import/qasm.py
@@ -16,14 +16,14 @@
 from cirq.contrib.qasm_import._parser import QasmParser
 
 
-class QasmCircuitParser:
-    """QasmCircuitParser is currently partially developed, not functional,
-    DO NOT USE.
-    TODO(https://github.com/quantumlib/Cirq/issues/1548)
-    It will serve as the entrypoint for parsing QASM files."""
+def circuit_from_qasm(qasm: str) -> circuits.Circuit:
+    """Parses an OpenQASM string to `cirq.Circuit`.
 
-    def __init__(self):
-        pass
+    Args:
+        qasm: The OpenQASM string
 
-    def parse(self, qasm: str) -> circuits.Circuit:
-        return QasmParser().parse(qasm).circuit
+    Returns:
+        The parsed circuit
+    """
+
+    return QasmParser().parse(qasm).circuit
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -118,7 +118,8 @@ def pandoc_process(app,
     'sphinx.ext.doctest',
     'sphinx.ext.mathjax',
     'sphinx.ext.napoleon',
-    'sphinx.ext.viewcode'
+    'sphinx.ext.viewcode',
+    'sphinx_markdown_tables',
 ]
 
 # Add any paths that contain templates here, relative to this directory.


Test patch:
diff --git a/cirq/contrib/qasm_import/qasm_test.py b/cirq/contrib/qasm_import/qasm_test.py
--- a/cirq/contrib/qasm_import/qasm_test.py
+++ b/cirq/contrib/qasm_import/qasm_test.py
@@ -16,6 +16,7 @@
 import cirq
 import cirq.testing as ct
 from cirq.testing import consistent_qasm as cq
+from cirq.contrib.qasm_import import circuit_from_qasm
 
 
 def test_consistency_with_qasm_output_and_qiskit():
@@ -47,7 +48,7 @@ def test_consistency_with_qasm_output_and_qiskit():
 
     qasm = cirq.qasm(circuit1)
 
-    circuit2 = cirq.contrib.qasm_import.qasm.QasmCircuitParser().parse(qasm)
+    circuit2 = circuit_from_qasm(qasm)
 
     cirq_unitary = cirq.unitary(circuit2)
     ct.assert_allclose_up_to_global_phase(cirq_unitary,