| """ |
| |
| 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('"', '') |
| 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): |
| 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) |
|
|