|
| 1 | +''' |
| 2 | +Teague Tomesh - 2/10/2020 |
| 3 | + |
| 4 | +Implementation of an n-bit ripple-carry adder. |
| 5 | + |
| 6 | +Based on the specification given in Cuccaro, Draper, Kutin, Moulton. |
| 7 | +(https://arxiv.org/abs/quant-ph/0410184v1) |
| 8 | +''' |
| 9 | + |
| 10 | +from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister |
| 11 | +import tensorcircuit as tc |
| 12 | +from .randinit import qc_randinit |
| 13 | + |
| 14 | + |
| 15 | +class RCAdder: |
| 16 | + """ |
| 17 | + An n-bit ripple-carry adder can be generated using an instance of the |
| 18 | + RCAdder class by calling the gen_circuit() method. |
| 19 | + |
| 20 | + This adder circuit uses 1 ancilla qubit to add together two values |
| 21 | + a = a_(n-1)...a_0 and b = b_(n-1)...a_0 |
| 22 | + and store their sum |
| 23 | + s = s_n...s_0 |
| 24 | + in the registers which initially held the b value. |
| 25 | + |
| 26 | + The adder circuit uses 2 + binary_len(a) + binary_len(b) qubits. |
| 27 | + The initial carry value is stored in the qubit at index = 0. |
| 28 | + The binary value of a_i is stored in the qubit at index = 2*i + 2 |
| 29 | + The binary value of b_i is stored in the qubit at index = 2*i + 1 |
| 30 | + The high bit, s_n, is stored in the last qubit at index = num_qubits - 1 |
| 31 | + |
| 32 | + Attributes |
| 33 | + ---------- |
| 34 | + nbits : int |
| 35 | + size, in bits, of the numbers the adder can handle |
| 36 | + nq : int |
| 37 | + number of qubits needed to construct the adder circuit |
| 38 | + a, b : int |
| 39 | + optional parameters to specify the numbers the adder should add. |
| 40 | + Will throw an exception if the length of the bitstring representations |
| 41 | + of a or b are greater than nbits. |
| 42 | + use_toffoli : bool |
| 43 | + Should the toffoli gate be used in the generated circuit or should it |
| 44 | + first be decomposed |
| 45 | + barriers : bool |
| 46 | + should barriers be included in the generated circuit |
| 47 | + regname : str |
| 48 | + optional string to name the quantum and classical registers. This |
| 49 | + allows for the easy concatenation of multiple QuantumCircuits. |
| 50 | + qr : QuantumRegister |
| 51 | + Qiskit QuantumRegister holding all of the quantum bits |
| 52 | + circ : QuantumCircuit |
| 53 | + Qiskit QuantumCircuit that represents the uccsd circuit |
| 54 | + """ |
| 55 | + |
| 56 | + def __init__(self, nbits=None, a=0, b=0, use_toffoli=False, barriers=False, |
| 57 | + measure=False, regname=None): |
| 58 | + |
| 59 | + # number of bits the adder can handle |
| 60 | + if nbits is None: |
| 61 | + raise Exception('Number of bits must be specified') |
| 62 | + else: |
| 63 | + self.nbits = nbits |
| 64 | + |
| 65 | + # given nbits, compute the number of qubits the adder will need |
| 66 | + # number of qubits = 1 ancilla for the initial carry value |
| 67 | + # + 2*nbits to hold both a and b |
| 68 | + # + 1 more qubit to hold the high bit, s_n |
| 69 | + self.nq = 1 + 2*nbits + 1 |
| 70 | + |
| 71 | + # set flags for circuit generation |
| 72 | + if len('{0:b}'.format(a)) > nbits or len('{0:b}'.format(b)) > nbits: |
| 73 | + raise Exception('Binary representations of a and b must be less than or equal to nbits') |
| 74 | + |
| 75 | + self.a = a |
| 76 | + self.b = b |
| 77 | + self.use_toffoli = use_toffoli |
| 78 | + self.barriers = barriers |
| 79 | + self.measure = measure |
| 80 | + |
| 81 | + # create a QuantumCircuit object |
| 82 | + if regname is None: |
| 83 | + self.qr = QuantumRegister(self.nq) |
| 84 | + else: |
| 85 | + self.qr = QuantumRegister(self.nq, name=regname) |
| 86 | + self.circ = QuantumCircuit(self.qr) |
| 87 | + qc_randinit(self.circ) |
| 88 | + |
| 89 | + # add ClassicalRegister if measure is True |
| 90 | + if self.measure: |
| 91 | + self.cr = ClassicalRegister(self.nq) |
| 92 | + self.circ.add_register(self.cr) |
| 93 | + |
| 94 | + |
| 95 | + def _initialize_value(self, indices, value): |
| 96 | + """ |
| 97 | + Initialize the qubits at indices to the given value |
| 98 | + |
| 99 | + Parameters |
| 100 | + ---------- |
| 101 | + indices : List[int] |
| 102 | + List of qubit indices |
| 103 | + value : int |
| 104 | + The desired initial value |
| 105 | + """ |
| 106 | + binstr = '{0:b}'.format(value) |
| 107 | + for index, val in enumerate(reversed(binstr)): |
| 108 | + if val is '1': |
| 109 | + self.circ.x(indices[index]) |
| 110 | + |
| 111 | + |
| 112 | + def _toffoli(self, x, y, z): |
| 113 | + """ |
| 114 | + Implement the toffoli gate using 1 and 2 qubit gates |
| 115 | + """ |
| 116 | + self.circ.h(z) |
| 117 | + self.circ.cx(y,z) |
| 118 | + self.circ.tdg(z) |
| 119 | + self.circ.cx(x,z) |
| 120 | + self.circ.t(z) |
| 121 | + self.circ.cx(y,z) |
| 122 | + self.circ.t(y) |
| 123 | + self.circ.tdg(z) |
| 124 | + self.circ.cx(x,z) |
| 125 | + self.circ.cx(x,y) |
| 126 | + self.circ.t(z) |
| 127 | + self.circ.h(z) |
| 128 | + self.circ.t(x) |
| 129 | + self.circ.tdg(y) |
| 130 | + self.circ.cx(x,y) |
| 131 | + |
| 132 | + |
| 133 | + def _MAJ(self, x, y, z): |
| 134 | + """ |
| 135 | + Implement the MAJ (Majority) gate described in Cuccaro, Draper, Kutin, |
| 136 | + Moulton. |
| 137 | + """ |
| 138 | + self.circ.cx(z,y) |
| 139 | + self.circ.cx(z,x) |
| 140 | + |
| 141 | + if self.use_toffoli: |
| 142 | + self.circ.ccx(x,y,z) |
| 143 | + else: |
| 144 | + # use a decomposed version of toffoli |
| 145 | + self._toffoli(x,y,z) |
| 146 | + |
| 147 | + |
| 148 | + def _UMA(self, x, y, z): |
| 149 | + """ |
| 150 | + Implement the UMA (UnMajority and Add) gate described in Cuccaro, |
| 151 | + Draper, Kutin, Moulton. |
| 152 | + """ |
| 153 | + self.circ.x(y) |
| 154 | + self.circ.cx(x,y) |
| 155 | + if self.use_toffoli: |
| 156 | + self.circ.ccx(x,y,z) |
| 157 | + else: |
| 158 | + # use a decomposed version of toffoli |
| 159 | + self._toffoli(x,y,z) |
| 160 | + self.circ.x(y) |
| 161 | + self.circ.cx(z,x) |
| 162 | + self.circ.cx(z,y) |
| 163 | + |
| 164 | + |
| 165 | + def gen_circuit(self): |
| 166 | + """ |
| 167 | + Create a circuit implementing the ripple-carry adder |
| 168 | + |
| 169 | + Returns |
| 170 | + ------- |
| 171 | + QuantumCircuit |
| 172 | + QuantumCircuit object of size self.nq |
| 173 | + """ |
| 174 | + high_bit_index = self.nq-1 |
| 175 | + |
| 176 | + # initialize the a and b registers |
| 177 | + a_indices = [2*i+2 for i in range(self.nbits)] |
| 178 | + b_indices = [2*i+1 for i in range(self.nbits)] |
| 179 | + for index_list, value in zip([a_indices, b_indices], [self.a, self.b]): |
| 180 | + self._initialize_value(index_list, value) |
| 181 | + |
| 182 | + # compute the carry bits, c_i, in order using the MAJ ladder |
| 183 | + for a_i in a_indices: |
| 184 | + self._MAJ(a_i-2, a_i-1, a_i) |
| 185 | + |
| 186 | + # write the final carry bit value to the high bit register |
| 187 | + self.circ.cx(a_indices[-1], high_bit_index) |
| 188 | + |
| 189 | + # erase the carry bits in reverse order using the UMA ladder |
| 190 | + for a_i in reversed(a_indices): |
| 191 | + self._UMA(a_i-2, a_i-1, a_i) |
| 192 | + |
| 193 | + return self.circ |
| 194 | + |
| 195 | + |
| 196 | +def gen_adder_circ(nbits): |
| 197 | + adder = RCAdder(nbits, a=0, b=0) |
| 198 | + circ = adder.gen_circuit() |
| 199 | + return tc.Circuit.from_qiskit(circ) |
| 200 | + |
0 commit comments