I went for another QOSF task (Oct. 2022), there I tackled the seemingly easy task of implementing a Draper adder, as an intermediate step towards a multiplier. However, I got lost in index engineering. At this point, I am not satisfied with the solution, but the tests check out, so it is usable.
I added two quantum registers; instead of the much easier multiplication of a quantum with a classical register.
import pennylane as qml
from pennylane import numpy as np
reg_size = 4
n_wires = 2*reg_size
dev = qml.device("default.qubit", wires=n_wires)
def base_encode(a, wires_offset=0):
a_bin = np.binary_repr(a, reg_size)
a_bin = a_bin if wires_offset else reversed(a_bin)
for i, ai in enumerate(a_bin):
if ai == "1":
qml.PauliX(i + wires_offset)
@qml.qnode(dev)
def circuit(a, b):
# encode basis state
base_encode(a)
base_encode(b, wires_offset=reg_size)
# apply QFT to one register
qml.QFT(wires=range(reg_size, n_wires))
# in this implementation, I pretend that I do not know the value of b,
# this could be the case, if this addition was amidst a longer calculation
for i in range(reg_size): # ith significant bit (target)
for j in range(reg_size-i): # control bits
trgt_idx = n_wires - 1 - i
ctrl_idx = trgt_idx - reg_size - j
angle = np.pi/(2**j)
qml.ControlledPhaseShift(angle, wires=[ctrl_idx, trgt_idx])
# invert QFT
qml.adjoint(qml.QFT)(wires=range(reg_size, n_wires))
return qml.probs(wires=range(reg_size, n_wires))# [qml.expval(qml.PauliZ(i)) for i in range(reg_size, n_wires)]
As already mentioned, I wouldn’t trust the implementation, if the tests didn’t check out, but they do.
circ_draw = qml.draw(circuit)
res = np.array([])
for a in range(reg_size**2 - 1):
for b in range(reg_size**2 - 1 -a):
sample = circuit(a, b)
expected_result = a+b
if np.argmax(sample)!=expected_result:
print(f"a: {a}, b: {b}, sample: {np.argmax(sample)}")
print(circ_draw(a, b))
break
res = np.append(res, [np.argmax(sample)==a+b])
print(f"The algorithm is correct! {len(res)} tests successful!" if np.all(res) else "There is an error")