<jupyter_start><jupyter_text>Task-3To design a OpenQASM3 interpreter which does the following:1. Convert the OpenQASM3 code to a Quantum Circuit2. Calculates the inverse of the circuit For this task, I first start with writing out a basic tokenizer and line-by-line interpreter. The code for the tokenizer and interpreter is in the `qasm_to_qiskit_circuit_converter.py` file in the root folder. Then I use this Jupyter notebook to run and analyse a few code samples for OpenQASM3.<jupyter_code># Import the converter class from `qasm_to_qiskit_circuit_converter` file
from qasm_to_qiskit_circuit_converter import QasmToQuantumCircuitConverter<jupyter_output><empty_output><jupyter_text>The next step is to write an OpenQASM3 code which defines a random quantum circuit which can be consumed by the converter to generate a Qiskit QuantumCircuit object. Below is a sample OpenQASM3 code.<jupyter_code># Define the QASM input
qasm_input_string = '''

OPENQASM 3.0;
include "stdgates.inc";

qubit[3] q1, q2;
bit[3] c1, c2;

const theta = pi/2;
float lambda = pi/4;

x q1[0];
rz(theta) q1[1];
ry(lambda) q2[2];
h q2[1];
cx q1[1], q2[2];
sdg q1[2];
swap q1[1], q2[2];
ctrl @ swap q1[2], q1[0], q2[1];
barrier q1;

measure q1 -> c1;
barrier q2;

reset q2;
U(0, theta, 0) q2[2];
ctrl @ U(2*lambda, 3*theta/2, pi/2) q1[2], q2[1];
inv @ U(0, 2*theta, 0) q1[0];

c2 = measure q2;

'''.strip()<jupyter_output><empty_output><jupyter_text>The above code has the following features:1. Uses `qubit[.]` and `bit[.]` keywords instead of the old (OpenQASM2) `qreg` and `creg` keywords.2. Has ability to define variables for parameterized quantum gates (eg: `theta` and `lambda`)3. Uses gate modifiers like `ctrl` and `inv` which are a new addition to OpenQASM3.4. Uses two different syntax for `measure` instruction as defined in the old (OpenQASM2) and the new (OpenQASM3) languages.5. Implements conversion for all the specified gates in the problem, plus a few exttras ;)These features are crucial to differentiate between OpenQASM3 and OpenQASM2 standards. This is achieved by having the ability to storre the context of the program using a Python dictionary (being used as a Hash-Map here), to store the quantum and classical register objects as well as the variables that are defined during the lifetime of the program.  Initializing the `QasmToQuantumCircuitConverter`Below is an example of initializing the converter class with the OpenQASM3 code as an input. The `to_qiskit_circuit()` method is used to convert the OpenQASM3 code into a Qiskit QuantumCircuit object.<jupyter_code>converter = QasmToQuantumCircuitConverter(qasm_input_string)
qc = converter.to_qiskit_circuit()
qc.draw('mpl')<jupyter_output><empty_output><jupyter_text>However, the above circuit is not reversible as there are certain non-unitary operations which are a part of it. These non-unitary operations include the `measure` and `reset` instructions. Hence to make this circuit unitary and reversible, we can remove these operations and define a new OpenQASM3 code. Making the quantum code unitary and reversibleBelow is an OpenQASM3 code which is both unitary and reversible.<jupyter_code>qasm_input_string = '''

OPENQASM 3.0;
include "stdgates.inc";

qubit[3] q1, q2;
bit[3] c1, c2;

const theta = pi/2;
float lambda = pi/4;

x q1[0];
rz(theta) q1[1];
ry(lambda) q2[2];
h q2[1];
cx q1[1], q2[2];
sdg q1[2];
swap q1[1], q2[2];
ctrl @ swap q1[2], q1[0], q2[1];
barrier q1;

U(0, theta, 0) q2[2];
ctrl @ U(2*lambda, 3*theta/2, pi/2) q1[2], q2[1];
inv @ U(0, 2*theta, 0) q1[0];

'''.strip()<jupyter_output><empty_output><jupyter_text>Following the same approach, we can convert the code into a Qiskit QuantumCircuit object as follows,<jupyter_code>converter2 = QasmToQuantumCircuitConverter(qasm_input_string)
qc = converter2.to_qiskit_circuit()
qc.draw('mpl')<jupyter_output><empty_output><jupyter_text>As we can observe, the converter generates correct QuantumCircuit object and draws the circuit properly.Now, to invert this circuit, we'll make use of the following property of unitary operations. If we have a unitary operation $U$ which is of the form,$$U = U_1 \cdot U_2 \cdot U_3 \cdot \dots \cdot U_{m-1} \cdot U_m$$We can write the inverse of $U$ i.e. $U^\dagger$ by reversing the orger of operations and taking inverse of each individual $U_i$. This can be represented mathematically as,$$U^\dagger = U^\dagger _m \cdot U^\dagger _{m-1} \cdot \dots \cdot U^\dagger _3 \cdot U^\dagger _2 \cdot U^\dagger _1$$The code written for the method `to_inverse_qiskit_circuit()` follows a similar approach. First I traverse through the code and store all the quantum and classical registers in the program context (the Python dictionary) and then traverse in the opposite direction to achieve the following:-1. Reverse the direction of individual unitary operations.2. Invert each individual unitary operation to get $U^\dagger _i$. Inverting the circuit and converting to `QuantumCircuit` object<jupyter_code>converter3 = QasmToQuantumCircuitConverter(qasm_input_string)
qc = converter3.to_inverse_qiskit_circuit()
qc.draw('mpl')<jupyter_output><empty_output><jupyter_text>Sample for the program context storageBelow is the output for how the converter stores the quantum and classical registers, and variables for the program context. This is very useful as it makes look-up very efficient as Python dictionary is implemented as Hash-Map.<jupyter_code>converter.get_variables()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08901480533723267, 'fraction_numerical': 0.0268689453481996, 'mean_word_length': 5.600723763570567, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '72008', 'license': 'Apache-2.0', 'n_tokens_mistral': 1848, 'n_tokens_neox': 1732, 'n_words': 699, 'path': 'task3.ipynb', 'repo_name': 'AbeerVaishnav13/qosf2021', 'size': '5471'}