""" qasm.py - Functions to parse a set of qasm commands into a SymPy Circuit. Examples taken from Chuang's page: https://web.archive.org/web/20220120121541/https://www.media.mit.edu/quanta/qasm2circ/ The code returns a circuit and an associated list of labels. >>> from sympy.physics.quantum.qasm import Qasm >>> q = Qasm('qubit q0', 'qubit q1', 'h q0', 'cnot q0,q1') >>> q.get_circuit() CNOT(1,0)*H(1) >>> q = Qasm('qubit q0', 'qubit q1', 'cnot q0,q1', 'cnot q1,q0', 'cnot q0,q1') >>> q.get_circuit() CNOT(1,0)*CNOT(0,1)*CNOT(1,0) """ __all__ = [ 'Qasm', ] from math import prod from sympy.physics.quantum.gate import H, CNOT, X, Z, CGate, CGateS, SWAP, S, T,CPHASE from sympy.physics.quantum.circuitplot import Mz def read_qasm(lines): return Qasm(*lines.splitlines()) def read_qasm_file(filename): return Qasm(*open(filename).readlines()) def flip_index(i, n): """Reorder qubit indices from largest to smallest. >>> from sympy.physics.quantum.qasm import flip_index >>> flip_index(0, 2) 1 >>> flip_index(1, 2) 0 """ return n-i-1 def trim(line): """Remove everything following comment # characters in line. >>> from sympy.physics.quantum.qasm import trim >>> trim('nothing happens here') 'nothing happens here' >>> trim('something #happens here') 'something ' """ if '#' not in line: return line return line.split('#')[0] def get_index(target, labels): """Get qubit labels from the rest of the line,and return indices >>> from sympy.physics.quantum.qasm import get_index >>> get_index('q0', ['q0', 'q1']) 1 >>> get_index('q1', ['q0', 'q1']) 0 """ nq = len(labels) return flip_index(labels.index(target), nq) def get_indices(targets, labels): return [get_index(t, labels) for t in targets] def nonblank(args): for line in args: line = trim(line) if line.isspace(): continue yield line return def fullsplit(line): words = line.split() rest = ' '.join(words[1:]) return fixcommand(words[0]), [s.strip() for s in rest.split(',')] def fixcommand(c): """Fix Qasm command names. Remove all of forbidden characters from command c, and replace 'def' with 'qdef'. """ forbidden_characters = ['-'] c = c.lower() for char in forbidden_characters: c = c.replace(char, '') if c == 'def': return 'qdef' return c def stripquotes(s): """Replace explicit quotes in a string. >>> from sympy.physics.quantum.qasm import stripquotes >>> stripquotes("'S'") == 'S' True >>> stripquotes('"S"') == 'S' True >>> stripquotes('S') == 'S' True """ s = s.replace('"', '') # Remove second set of quotes? s = s.replace("'", '') return s class Qasm: """Class to form objects from Qasm lines >>> from sympy.physics.quantum.qasm import Qasm >>> q = Qasm('qubit q0', 'qubit q1', 'h q0', 'cnot q0,q1') >>> q.get_circuit() CNOT(1,0)*H(1) >>> q = Qasm('qubit q0', 'qubit q1', 'cnot q0,q1', 'cnot q1,q0', 'cnot q0,q1') >>> q.get_circuit() CNOT(1,0)*CNOT(0,1)*CNOT(1,0) """ def __init__(self, *args, **kwargs): self.defs = {} self.circuit = [] self.labels = [] self.inits = {} self.add(*args) self.kwargs = kwargs def add(self, *lines): for line in nonblank(lines): command, rest = fullsplit(line) if self.defs.get(command): #defs come first, since you can override built-in function = self.defs.get(command) indices = self.indices(rest) if len(indices) == 1: self.circuit.append(function(indices[0])) else: self.circuit.append(function(indices[:-1], indices[-1])) elif hasattr(self, command): function = getattr(self, command) function(*rest) else: print("Function %s not defined. Skipping" % command) def get_circuit(self): return prod(reversed(self.circuit)) def get_labels(self): return list(reversed(self.labels)) def plot(self): from sympy.physics.quantum.circuitplot import CircuitPlot circuit, labels = self.get_circuit(), self.get_labels() CircuitPlot(circuit, len(labels), labels=labels, inits=self.inits) def qubit(self, arg, init=None): self.labels.append(arg) if init: self.inits[arg] = init def indices(self, args): return get_indices(args, self.labels) def index(self, arg): return get_index(arg, self.labels) def nop(self, *args): pass def x(self, arg): self.circuit.append(X(self.index(arg))) def z(self, arg): self.circuit.append(Z(self.index(arg))) def h(self, arg): self.circuit.append(H(self.index(arg))) def s(self, arg): self.circuit.append(S(self.index(arg))) def t(self, arg): self.circuit.append(T(self.index(arg))) def measure(self, arg): self.circuit.append(Mz(self.index(arg))) def cnot(self, a1, a2): self.circuit.append(CNOT(*self.indices([a1, a2]))) def swap(self, a1, a2): self.circuit.append(SWAP(*self.indices([a1, a2]))) def cphase(self, a1, a2): self.circuit.append(CPHASE(*self.indices([a1, a2]))) def toffoli(self, a1, a2, a3): i1, i2, i3 = self.indices([a1, a2, a3]) self.circuit.append(CGateS((i1, i2), X(i3))) def cx(self, a1, a2): fi, fj = self.indices([a1, a2]) self.circuit.append(CGate(fi, X(fj))) def cz(self, a1, a2): fi, fj = self.indices([a1, a2]) self.circuit.append(CGate(fi, Z(fj))) def defbox(self, *args): print("defbox not supported yet. Skipping: ", args) def qdef(self, name, ncontrols, symbol): from sympy.physics.quantum.circuitplot import CreateOneQubitGate, CreateCGate ncontrols = int(ncontrols) command = fixcommand(name) symbol = stripquotes(symbol) if ncontrols > 0: self.defs[command] = CreateCGate(symbol) else: self.defs[command] = CreateOneQubitGate(symbol)