qasm.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. """
  2. qasm.py - Functions to parse a set of qasm commands into a SymPy Circuit.
  3. Examples taken from Chuang's page: https://web.archive.org/web/20220120121541/https://www.media.mit.edu/quanta/qasm2circ/
  4. The code returns a circuit and an associated list of labels.
  5. >>> from sympy.physics.quantum.qasm import Qasm
  6. >>> q = Qasm('qubit q0', 'qubit q1', 'h q0', 'cnot q0,q1')
  7. >>> q.get_circuit()
  8. CNOT(1,0)*H(1)
  9. >>> q = Qasm('qubit q0', 'qubit q1', 'cnot q0,q1', 'cnot q1,q0', 'cnot q0,q1')
  10. >>> q.get_circuit()
  11. CNOT(1,0)*CNOT(0,1)*CNOT(1,0)
  12. """
  13. __all__ = [
  14. 'Qasm',
  15. ]
  16. from math import prod
  17. from sympy.physics.quantum.gate import H, CNOT, X, Z, CGate, CGateS, SWAP, S, T,CPHASE
  18. from sympy.physics.quantum.circuitplot import Mz
  19. def read_qasm(lines):
  20. return Qasm(*lines.splitlines())
  21. def read_qasm_file(filename):
  22. return Qasm(*open(filename).readlines())
  23. def flip_index(i, n):
  24. """Reorder qubit indices from largest to smallest.
  25. >>> from sympy.physics.quantum.qasm import flip_index
  26. >>> flip_index(0, 2)
  27. 1
  28. >>> flip_index(1, 2)
  29. 0
  30. """
  31. return n-i-1
  32. def trim(line):
  33. """Remove everything following comment # characters in line.
  34. >>> from sympy.physics.quantum.qasm import trim
  35. >>> trim('nothing happens here')
  36. 'nothing happens here'
  37. >>> trim('something #happens here')
  38. 'something '
  39. """
  40. if '#' not in line:
  41. return line
  42. return line.split('#')[0]
  43. def get_index(target, labels):
  44. """Get qubit labels from the rest of the line,and return indices
  45. >>> from sympy.physics.quantum.qasm import get_index
  46. >>> get_index('q0', ['q0', 'q1'])
  47. 1
  48. >>> get_index('q1', ['q0', 'q1'])
  49. 0
  50. """
  51. nq = len(labels)
  52. return flip_index(labels.index(target), nq)
  53. def get_indices(targets, labels):
  54. return [get_index(t, labels) for t in targets]
  55. def nonblank(args):
  56. for line in args:
  57. line = trim(line)
  58. if line.isspace():
  59. continue
  60. yield line
  61. return
  62. def fullsplit(line):
  63. words = line.split()
  64. rest = ' '.join(words[1:])
  65. return fixcommand(words[0]), [s.strip() for s in rest.split(',')]
  66. def fixcommand(c):
  67. """Fix Qasm command names.
  68. Remove all of forbidden characters from command c, and
  69. replace 'def' with 'qdef'.
  70. """
  71. forbidden_characters = ['-']
  72. c = c.lower()
  73. for char in forbidden_characters:
  74. c = c.replace(char, '')
  75. if c == 'def':
  76. return 'qdef'
  77. return c
  78. def stripquotes(s):
  79. """Replace explicit quotes in a string.
  80. >>> from sympy.physics.quantum.qasm import stripquotes
  81. >>> stripquotes("'S'") == 'S'
  82. True
  83. >>> stripquotes('"S"') == 'S'
  84. True
  85. >>> stripquotes('S') == 'S'
  86. True
  87. """
  88. s = s.replace('"', '') # Remove second set of quotes?
  89. s = s.replace("'", '')
  90. return s
  91. class Qasm:
  92. """Class to form objects from Qasm lines
  93. >>> from sympy.physics.quantum.qasm import Qasm
  94. >>> q = Qasm('qubit q0', 'qubit q1', 'h q0', 'cnot q0,q1')
  95. >>> q.get_circuit()
  96. CNOT(1,0)*H(1)
  97. >>> q = Qasm('qubit q0', 'qubit q1', 'cnot q0,q1', 'cnot q1,q0', 'cnot q0,q1')
  98. >>> q.get_circuit()
  99. CNOT(1,0)*CNOT(0,1)*CNOT(1,0)
  100. """
  101. def __init__(self, *args, **kwargs):
  102. self.defs = {}
  103. self.circuit = []
  104. self.labels = []
  105. self.inits = {}
  106. self.add(*args)
  107. self.kwargs = kwargs
  108. def add(self, *lines):
  109. for line in nonblank(lines):
  110. command, rest = fullsplit(line)
  111. if self.defs.get(command): #defs come first, since you can override built-in
  112. function = self.defs.get(command)
  113. indices = self.indices(rest)
  114. if len(indices) == 1:
  115. self.circuit.append(function(indices[0]))
  116. else:
  117. self.circuit.append(function(indices[:-1], indices[-1]))
  118. elif hasattr(self, command):
  119. function = getattr(self, command)
  120. function(*rest)
  121. else:
  122. print("Function %s not defined. Skipping" % command)
  123. def get_circuit(self):
  124. return prod(reversed(self.circuit))
  125. def get_labels(self):
  126. return list(reversed(self.labels))
  127. def plot(self):
  128. from sympy.physics.quantum.circuitplot import CircuitPlot
  129. circuit, labels = self.get_circuit(), self.get_labels()
  130. CircuitPlot(circuit, len(labels), labels=labels, inits=self.inits)
  131. def qubit(self, arg, init=None):
  132. self.labels.append(arg)
  133. if init: self.inits[arg] = init
  134. def indices(self, args):
  135. return get_indices(args, self.labels)
  136. def index(self, arg):
  137. return get_index(arg, self.labels)
  138. def nop(self, *args):
  139. pass
  140. def x(self, arg):
  141. self.circuit.append(X(self.index(arg)))
  142. def z(self, arg):
  143. self.circuit.append(Z(self.index(arg)))
  144. def h(self, arg):
  145. self.circuit.append(H(self.index(arg)))
  146. def s(self, arg):
  147. self.circuit.append(S(self.index(arg)))
  148. def t(self, arg):
  149. self.circuit.append(T(self.index(arg)))
  150. def measure(self, arg):
  151. self.circuit.append(Mz(self.index(arg)))
  152. def cnot(self, a1, a2):
  153. self.circuit.append(CNOT(*self.indices([a1, a2])))
  154. def swap(self, a1, a2):
  155. self.circuit.append(SWAP(*self.indices([a1, a2])))
  156. def cphase(self, a1, a2):
  157. self.circuit.append(CPHASE(*self.indices([a1, a2])))
  158. def toffoli(self, a1, a2, a3):
  159. i1, i2, i3 = self.indices([a1, a2, a3])
  160. self.circuit.append(CGateS((i1, i2), X(i3)))
  161. def cx(self, a1, a2):
  162. fi, fj = self.indices([a1, a2])
  163. self.circuit.append(CGate(fi, X(fj)))
  164. def cz(self, a1, a2):
  165. fi, fj = self.indices([a1, a2])
  166. self.circuit.append(CGate(fi, Z(fj)))
  167. def defbox(self, *args):
  168. print("defbox not supported yet. Skipping: ", args)
  169. def qdef(self, name, ncontrols, symbol):
  170. from sympy.physics.quantum.circuitplot import CreateOneQubitGate, CreateCGate
  171. ncontrols = int(ncontrols)
  172. command = fixcommand(name)
  173. symbol = stripquotes(symbol)
  174. if ncontrols > 0:
  175. self.defs[command] = CreateCGate(symbol)
  176. else:
  177. self.defs[command] = CreateOneQubitGate(symbol)