<jupyter_start><jupyter_text>OPENQASM 3.0 interpreter to Qiskit QuantumCircuit() object  Import libraries<jupyter_code>import numpy as np
import struct
import re
import qiskit
from qiskit import *<jupyter_output><empty_output><jupyter_text>Regex  Checking the structure of each line of the code In this project, I will use regex to check the structure and syntax of each line of the QASM file. There are many kinds of structure for the QASM so I will identify it into 8 groups.    Variables identifier belong to From https://qiskit.github.io/openqasm/language/types.html, variable identifier can only belong to these groups below with underscore.    * Lu (letter uppercase)   * Ll (Letter lowercase)   * Lt (Letter titlecase)   * Lm (Letter modifier)   * Lo (Letter Other)   * Nl (Number Letter)<jupyter_code>declare_variable          = "^( *)(float)\[\d+\]( +)([A-Za-z0-9_])+( *);( *)$"
initiate_variable         = "^( *)((float)\[\d+\])*( +)*([A-Za-z0-9_])+( *)=( *)(([+-]?([0-9]*[.])?[0-9]+)|pi)( *);( *)$"
checking_declare_variable = "^( *)(qubit)( +)([A-Za-z0-9_])+( *);( *)$"
checking_declare_qreg     = "^( *)(qubit)\[(\d)+\]( +)([A-Za-z0-9_])+( *);( *)$"
checking_1qubit_gate      = "^( *)(x|y|z|h|s|sdg|t|tdg)( +)(([A-Za-z0-9_])+|([A-Za-z0-9_])+\[(\d)+\])( *);( *)$"
checking_1qubit_gate_para = "^( *)(rx|ry|rz)\(.+\)( +)(([A-Za-z0-9_])+|([A-Za-z0-9_])+\[(\d)+\])( *);( *)$"
checking_2qubits_gate     = "^( *)(cx|swap)( +)(([A-Za-z0-9_])+|([A-Za-z0-9_])+\[(\d)+\])( *),( *)(([A-Za-z0-9_])+|([A-Za-z0-9_])+\[(\d)+\])( *);( *)$"
checking_3qubits_gate     = "^( *)(ccx|cswap)( +)(([A-Za-z0-9_])+|([A-Za-z0-9_])+\[(\d)+\])( *),( *)(([A-Za-z0-9_])+|([A-Za-z0-9_])+\[(\d)+\])( *),( *)(([A-Za-z0-9_])+|([A-Za-z0-9_])+\[(\d)+\])( *);( *)$"
checking_float            = "^[+-]?([0-9]*[.])?[0-9]+$"
accepted_char ="[A-Za-z0-9]"
def syntax_checking(x):
    if(re.match(checking_declare_variable, x)):   return 0
    elif(re.match(checking_declare_qreg, x)):     return 1
    elif(re.match(checking_1qubit_gate, x)):      return 2
    elif(re.match(checking_1qubit_gate_para, x)): return 3
    elif(re.match(checking_2qubits_gate, x)):     return 4
    elif(re.match(checking_3qubits_gate, x)):     return 5
    elif(re.match(declare_variable, x)):          return 6
    elif(re.match(initiate_variable, x)):         return 7
    else: return 8
#From floating point number to binary -> checking for the size
def binary(num):
    num = float(num)
    return ''.join('{:0>8b}'.format(c) for c in struct.pack('!f', num))
len(binary(33333333))<jupyter_output><empty_output><jupyter_text>Errors  This Error class help us to return errors (Due to limited time, this errors class is not completed yet)<jupyter_code>#######################################
# ERRORS
#######################################

class Error:
    def __init__(self, ln, error_name):
        self.ln = ln
        self.error_name = error_name
    
    def as_string(self):
        result  = f'Error: {self.error_name}\n'
        return result

class IllegalCharError(Error):
    def __init__(self,detail):
        super().__init__('Wrong QASM ',detail)<jupyter_output><empty_output><jupyter_text>Lexer  Lexer class break a line of code into predefined token which can help ignoring space and filter out all unused components<jupyter_code>#######################################
# LEXER
#######################################

class Lexer:
    def __init__(self, text, mode):
        self.text = text
        self.mode = mode
        self.pos = -1
        self.current_char = None
        self.advance()
    def advance(self):
        self.pos += 1
        self.current_char = self.text[self.pos] if self.pos < len(self.text) else None
    def backward(self):
        self.pos -= 1
        self.current_char = self.text[self.pos] if self.pos >= 0 else None
    
    # Create the list of tokens by going thourgh each char, when a digit or normal char is met, create_token is called.
    def make_tokens(self):
        tokens = []
        while self.current_char != None:
            if self.current_char in ' \t':
                self.advance() 
            elif ((self.current_char).isalnum() == False):
                x = self.current_char
                if(x == '_'):
                    token = self.create_token()
                    if(token != ''):
                        if(token == 'pi'):
                            token = str(np.pi)
                            tokens.append(token)
                            self.advance()
                        else:
                            tokens.append(token)
                            self.advance()
                    else:
                        self.advance()                    
                else:
                    tokens.append(x)
                    self.advance()
            else:
                token = self.create_token()
                #print(x)
                if(token != ''):
                    if(token == 'pi'):
                        token = str(np.pi)
                        tokens.append(token)
                        self.advance()
                    else:
                        tokens.append(token)
                        self.advance()
                else:
                    self.advance()
        return tokens, None
    
    #This function is used to create a token then return it to make_tokens, it will go thourgh a cluster of chars until meeting invalid char or space.
    def create_token(self):
        string =''
        while self.current_char != None:
            if self.current_char in' \t': return string
            elif ((self.current_char).isalnum() == False):
                if(self.current_char == '_'):
                    string += self.current_char
                    self.advance()
                else:
                    self.backward()
                    return string
            else:
                if(self.current_char == "π"):
                    string += str(np.pi)
                else:
                    string += self.current_char
                self.advance()
        return string
        
#######################################
# RUN LESSER
#######################################

# Help translating Rx(pi/2) into Rx(1.570796327)
def calculate_param(tokens):
    start_index = 1
    while(1):
        if((tokens[start_index] == '(') & (tokens[start_index+2] != ')') & (tokens[start_index+1] != '-')):
            if(tokens[start_index+2] == '+'):
                tokens[start_index+1] = float(tokens[start_index+1]) +  float(tokens[start_index+3])
            elif(tokens[start_index+2] == '-'):
                tokens[start_index+1] = float(tokens[start_index+1]) -  float(tokens[start_index+3])
            elif(tokens[start_index+2] == '*'):
                tokens[start_index+1] = float(tokens[start_index+1]) *  float(tokens[start_index+3])
            elif(tokens[start_index+2] == '/'):
                if(float(tokens[start_index+3]) == 0):
                    return None
                tokens[start_index+1] = float(tokens[start_index+1]) /  float(tokens[start_index+3])
            tokens.pop(3)
            tokens.pop(3)
            break
        else:
            break
    return tokens

#This function run the whole Lesser class
def run_lexer( text, mode):
    if(mode == 8):
        return [],IllegalCharError('syntax')
    lexer = Lexer(text, mode)
    tokens, error = lexer.make_tokens()
    if(mode == 3):
        tokens = calculate_param(tokens)
    return tokens, error
# Example using Lesser

#text = "rx(-10) input;"
text = "qubit bit_1;"
mode = syntax_checking(text)
tokens, error = run_lexer(text,mode)
tokens<jupyter_output><empty_output><jupyter_text>Interpreter Interpreter is use to process the tokens which are created from the leser above.* In this project, I only handle two kinds of variable which is qubit and float types. I create a global variable which has the same name of the variable declare in the code using ***globals()['variable_name']***.* For the inverse quantum circuit, when creating the quantum ciruit and using gate_sequence to create a list of quantum instruction then I will create inverse quantum circuit by going backward the list and apply the gate but change only $rx/ry/rz(theta)$ to $rx/ry/rz(-theta)$ as others gates has the transpose conjugate is itself.<jupyter_code>#######################################
# INTERPRETER
#######################################

class interpreter:
    def __init__(self, total_tokens):
        self.total_tokens = total_tokens
        self.number_of_var = 0
        self.VARIABLE = {}
        self.NORMAL_VARIABLE = {}
        self.quantum_circuit = None
        self.inverse_quantum_circuit = None
        self.error = None
        self.gate_sequence = []
        
    def create_quantum_circuit(self):
        name_qreg = []
        x = self.VARIABLE.keys()
        for i in range (len(x)):
            name_qreg += (globals()[list(x)[i]])
        self.quantum_circuit = QuantumCircuit(name_qreg)
        
    def create_inverse_quantum_circuit(self):
        name_qreg = []
        x = self.VARIABLE.keys()
        for i in range (len(x)):
            name_qreg += (globals()[list(x)[i]])
        self.inverse_quantum_circuit = QuantumCircuit(name_qreg)
        
    def implement_onequibt_gate(self, gate, qreg, index):
        if(gate == 'x'):
            self.quantum_circuit.x(qreg[index])
        elif(gate == 'y'):
            self.quantum_circuit.y(qreg[index])
        elif(gate == 'z'):
            self.quantum_circuit.z(qreg[index])
        elif(gate == 'h'):
            self.quantum_circuit.h(qreg[index])
        elif(gate == 's'):
            self.quantum_circuit.s(qreg[index])
        elif(gate == 'sdg'):
            self.quantum_circuit.sdg(qreg[index])
        elif(gate == 't'):
            self.quantum_circuit.t(qreg[index])
        elif(gate == 'tdg'):
            self.quantum_circuit.tdg(qreg[index])
            
    def implement_onequibt_paramgate(self, gate, param, qreg, index):
        #print(param)
        if(gate == 'rx'):
            self.quantum_circuit.rx(param,qreg[index]) 
        elif(gate == 'ry'): 
            self.quantum_circuit.ry(param,qreg[index])
        elif(gate == 'rz'):
            self.quantum_circuit.rz(param,qreg[index])
            
    def implement_twoquibt_gate(self, gate, qreg1, index1, qreg2, index2):
        if(gate == 'cx'):
            self.quantum_circuit.cx(qreg1[index1],qreg2[index2]) 
        elif(gate == 'swap'):
            self.quantum_circuit.swap(qreg1[index1],qreg2[index2]) 
        
    def implement_threequibt_gate(self, gate, qreg1, index1, qreg2, index2, qreg3, index3):
        if(gate == 'ccx'):
            self.quantum_circuit.ccx(qreg1[index1],qreg2[index2],qreg3[index3]) 
        elif(gate == 'cswap'):
            self.quantum_circuit.cswap(qreg1[index1],qreg2[index2],qreg3[index3])
    
    # Create inverse_quantum_circuit
    def implement_inverse_quantum_circuit(self):
        if(self.quantum_circuit != None):
            self.create_inverse_quantum_circuit()
            i = len(self.gate_sequence)-1
            while(i >= 0):
                gate = self.gate_sequence[i][0]
                if(gate == 'x'): self.inverse_quantum_circuit.x(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])])
                elif(gate == 'y'): self.inverse_quantum_circuit.y(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])])
                elif(gate == 'z'): self.inverse_quantum_circuit.z(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])])
                elif(gate == 'h'): self.inverse_quantum_circuit.h(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])])
                elif(gate == 's'): self.inverse_quantum_circuit.sdg(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])])
                elif(gate == 'sdg'): self.inverse_quantum_circuit.s(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])])
                elif(gate == 't'): self.inverse_quantum_circuit.tdg(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])])
                elif(gate == 'tdg'): self.inverse_quantum_circuit.t(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])])
                elif(gate == 'rx'): self.inverse_quantum_circuit.rx(float(self.gate_sequence[i][1])*(-1),globals()[self.gate_sequence[i][2]][int(self.gate_sequence[i][3])])
                elif(gate == 'ry'): self.inverse_quantum_circuit.ry(float(self.gate_sequence[i][1])*(-1),globals()[self.gate_sequence[i][2]][int(self.gate_sequence[i][3])])
                elif(gate == 'rz'): self.inverse_quantum_circuit.rz(float(self.gate_sequence[i][1])*(-1),globals()[self.gate_sequence[i][2]][int(self.gate_sequence[i][3])])
                elif(gate == 'cx'): self.inverse_quantum_circuit.cx(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])],globals()[self.gate_sequence[i][3]][int(self.gate_sequence[i][4])])
                elif(gate == 'swap'): self.inverse_quantum_circuit.swap(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])],globals()[self.gate_sequence[i][3]][int(self.gate_sequence[i][4])])
                elif(gate == 'ccx'): self.inverse_quantum_circuit.ccx(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])],globals()[self.gate_sequence[i][3]][int(self.gate_sequence[i][4])],globals()[self.gate_sequence[i][5]][int(self.gate_sequence[i][6])])
                elif(gate == 'cswap'): self.inverse_quantum_circuit.cswap(globals()[self.gate_sequence[i][1]][int(self.gate_sequence[i][2])],globals()[self.gate_sequence[i][3]][int(self.gate_sequence[i][4])],globals()[self.gate_sequence[i][5]][int(self.gate_sequence[i][6])])
                i -=1
    
#######################################
# Running INTERPRETER
#######################################
    def process(self):
        for counter in range (len(total_tokens)):
            if(self.error != None):
                break
            elif(total_tokens[counter][0] == 'float'):
                self.NORMAL_VARIABLE[str(total_tokens[counter][4])] = total_tokens[counter][2]
                if(total_tokens[counter][5] == '='):
                    if(total_tokens[counter][6] == '-'):
                        total_tokens[counter][6] = str(float(total_tokens[counter][6])*(-1))
                        total_tokens[counter].pop(7)
                    if(int(total_tokens[counter][2]) >= len(binary(total_tokens[counter][6]))): #Check number of bit of the number with the declared size
                        globals()[str(total_tokens[counter][4])] = float(total_tokens[counter][6])
                        
            elif(total_tokens[counter][0] in self.NORMAL_VARIABLE.keys()):
                if(total_tokens[counter][1] == '='):
                    if(total_tokens[counter][2] == '-'):
                        total_tokens[counter][2] = str(float(total_tokens[counter][3])*(-1))
                        total_tokens[counter].pop(3)
                    if(int(self.NORMAL_VARIABLE[total_tokens[counter][0]]) >= len(binary(total_tokens[counter][2]))): #Check number of bit of the number with the declared size
                         globals()[str(total_tokens[counter][0])] = float(total_tokens[counter][2])
                        
            elif(total_tokens[counter][0] == 'qubit'): # Declare variable
                if(total_tokens[counter][1] =='['):
                    self.VARIABLE[str(total_tokens[counter][4])] = total_tokens[counter][2]
                    globals()[str(total_tokens[counter][4])] = QuantumRegister(int(total_tokens[counter][2]),str(total_tokens[counter][4]))
                    self.number_of_var += 1
                else:
                    self.VARIABLE[str(total_tokens[counter][1])] = 1 
                    globals()[str(total_tokens[counter][1])] = QuantumRegister(1,str(total_tokens[counter][1]))
                    self.number_of_var += 1
                    
            elif((total_tokens[counter][0] == 'x') | (total_tokens[counter][0] == 'y') | (total_tokens[counter][0] == 'z') | (total_tokens[counter][0] == 'h') |( total_tokens[counter][0] == 's') | (total_tokens[counter][0] == 'sdg') | (total_tokens[counter][0] == 't') | (total_tokens[counter][0] == 'tdg')): # X gate
                if(self.quantum_circuit == None):
                    self.create_quantum_circuit()
                if(total_tokens[counter][2] == '['):
                    index = int(total_tokens[counter][3])
                    if(index >= int(self.VARIABLE[total_tokens[counter][1]])):
                        self.error = IllegalCharError('index out of bound')
                    else:
                        self.implement_onequibt_gate(total_tokens[counter][0],(globals()[total_tokens[counter][1]]),index)
                        self.gate_sequence.append([total_tokens[counter][0], total_tokens[counter][1], index])
                else:
                    index = 0
                    self.implement_onequibt_gate(total_tokens[counter][0],(globals()[total_tokens[counter][1]]),index)
                    self.gate_sequence.append([total_tokens[counter][0], total_tokens[counter][1], index])
                    
            elif((total_tokens[counter][0] == 'rx') | (total_tokens[counter][0] == 'ry') | (total_tokens[counter][0] == 'rz')):
                if(self.quantum_circuit == None):
                    self.create_quantum_circuit()
                    
                if(total_tokens[counter][2] != '-'):
                    if(re.match(checking_float, total_tokens[counter][2])):
                        param = float(total_tokens[counter][2])
                    elif(total_tokens[counter][2] in self.NORMAL_VARIABLE.keys()):
                        param = float(globals()[total_tokens[counter][2]])
                else:
                    if(re.match(checking_float, total_tokens[counter][3])):
                        total_tokens[counter][3] = str(float(total_tokens[counter][3])*(-1.0))
                        total_tokens[counter].pop(2)
                        param = float(total_tokens[counter][2])
                    elif(total_tokens[counter][3] in self.NORMAL_VARIABLE.keys()):
                        total_tokens[counter][3] = str(float(globals()[total_tokens[counter][3]])*(-1.0))
                        total_tokens[counter].pop(2)
                        param = float(total_tokens[counter][2])        
                if(total_tokens[counter][5] == '['):
                    index = int(total_tokens[counter][6])
                    if(index >= int(self.VARIABLE[total_tokens[counter][4]])):
                        self.error = IllegalCharError('index out of bound')
                    else:
                        self.implement_onequibt_paramgate(total_tokens[counter][0], param,(globals()[total_tokens[counter][4]]),index)
                        self.gate_sequence.append([total_tokens[counter][0], param, total_tokens[counter][4], index])
                else:
                    index = 0
                    self.implement_onequibt_paramgate(total_tokens[counter][0], param,(globals()[total_tokens[counter][4]]),index)
                    self.gate_sequence.append([total_tokens[counter][0], param, total_tokens[counter][4], index])
                    
            elif((total_tokens[counter][0] == 'cx') | (total_tokens[counter][0] == 'swap')):
                if(self.quantum_circuit == None):
                    self.create_quantum_circuit()
                    
                qreg1 = total_tokens[counter][1]
                if(total_tokens[counter][2] == '['):
                    index_qreg1 = int(total_tokens[counter][3])
                    if(index_qreg1 >= int(self.VARIABLE[qreg1])):
                        self.error = IllegalCharError('index out of bound')
                else:
                    index_qreg1 = 0 
                
                if(total_tokens[counter][2] == '['): 
                    index_vareg2 = 6
                else: 
                    index_vareg2 = 3
                    
                qreg2 = total_tokens[counter][index_vareg2]
                if(total_tokens[counter][index_vareg2+1] == '['):
                    index_qreg2 = int(total_tokens[counter][index_vareg2+2])
                    if(index_qreg2 >= int(self.VARIABLE[qreg2])):
                        self.error = IllegalCharError('index out of bound')
                else:
                    index_qreg2 = 0
                self.implement_twoquibt_gate((total_tokens[counter][0]),(globals()[qreg1]),index_qreg1,(globals()[qreg2]),index_qreg2)
                self.gate_sequence.append([total_tokens[counter][0], qreg1 ,index_qreg1, qreg2, index_qreg2])
                
            elif((total_tokens[counter][0] == 'ccx') | (total_tokens[counter][0] == 'cswap')):
                if(self.quantum_circuit == None):
                    self.create_quantum_circuit()
                ##qreg1_index1
                qreg1 = total_tokens[counter][1]
                if(total_tokens[counter][2] == '['):
                    index_qreg1 = int(total_tokens[counter][3])
                    if(index_qreg1 >= int(self.VARIABLE[qreg1])):
                        self.error = IllegalCharError('index out of bound')
                else:
                    index_qreg1 = 0 
                
                ##qreg2_index2
                if(total_tokens[counter][2] == '['): 
                    index_vareg2 = 6
                else: 
                    index_vareg2 = 3
                    
                qreg2 = total_tokens[counter][index_vareg2]
                if(total_tokens[counter][index_vareg2+1] == '['):
                    index_qreg2 = int(total_tokens[counter][index_vareg2+2])
                    if(index_qreg2 >= int(self.VARIABLE[qreg2])):
                        self.error = IllegalCharError('index out of bound')
                else:
                    index_qreg2 = 0
                
                ##qreg3_index3
                if((total_tokens[counter][2] != '[') & (total_tokens[counter][index_vareg2 +1] != '[')):
                    index_vareg3 = 5
                elif((total_tokens[counter][2] == '[') & (total_tokens[counter][index_vareg2 +1] == '[')):
                    index_vareg3 = 11
                else:
                    index_vareg3 = 8
                qreg3 = total_tokens[counter][index_vareg3]
                if(total_tokens[counter][index_vareg3+1] == '['):
                    index_qreg3 = int(total_tokens[counter][index_vareg3+2])
                    if(index_qreg3 >= int(self.VARIABLE[qreg3])):
                        self.error = IllegalCharError('index out of bound')
                else:
                    index_qreg3 = 0                
                self.implement_threequibt_gate((total_tokens[counter][0]),(globals()[qreg1]),index_qreg1,(globals()[qreg2]),index_qreg2,(globals()[qreg3]),index_qreg3)
                self.gate_sequence.append([total_tokens[counter][0], qreg1 ,index_qreg1, qreg2, index_qreg2, qreg3, index_qreg3])
# Read the file
f = open("test\\input.txt", "r")
for x in f:
    print(x)
# An example of the interpreter
f = open("test\\input.txt", "r")
total_tokens = []
number_of_var = 0
for x in f:
    mode = syntax_checking(x)
    tokens, error = run_lexer(x, mode)
    if(error): 
        print(error.as_string())
        break
    print(tokens)
    total_tokens.append(tokens)
inte = interpreter(total_tokens)
inte.process() 
if(inte.error != None):
    print(inte.error.as_string())
inte.NORMAL_VARIABLE
inte.VARIABLE
inte.gate_sequence
inte.quantum_circuit.draw()
# inverse quantum circuit
inte.implement_inverse_quantum_circuit()
inte.inverse_quantum_circuit.draw()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.16646338898935947, 'fraction_numerical': 0.016991209992850233, 'mean_word_length': 2.3167805830659787, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 34, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '552764', 'license': 'MIT', 'n_tokens_mistral': 7649, 'n_tokens_neox': 7159, 'n_words': 1160, 'path': 'src/.ipynb_checkpoints/Open QASM interpreter-checkpoint.ipynb', 'repo_name': 'bachbao/QASM3.0-Interpreter', 'size': '23777'}