3
$\begingroup$

I've implemented a quantum adder and, when I build the results table, I'm having some trouble to understand what's going on. First, I'm using the adder quantum circuit proposed in this paper, which circuit is shown below:

Circuit from the quantum adder

This is my version, implemented in IBMQE, as shown below:

My adder circuit implemented in IBMQE

After building the following truth table, I've got the following results:

Truth table that was obtained by me.

One can verify that there are several wrong outputs from this circuit. My question is: What's wrong in this circuit? If there is nothing wrong, am I interpreting the outputs in a wrong way? How to interpret them correctly?

$\endgroup$
2
  • $\begingroup$ Did you run the circuit in a simulator or real device? $\endgroup$
    – epelaez
    Commented Jun 10, 2023 at 3:46
  • $\begingroup$ I ran the circuit in a simulator... $\endgroup$ Commented Jun 11, 2023 at 4:39

2 Answers 2

3
$\begingroup$

The figure you included in your question references the paper Addition on a Quantum Computer by Draper. Qiskit has implemented this circuit natively, which you can construct as follows.

from qiskit.circuit.library import DraperQFTAdder

adder = DraperQFTAdder(2).decompose()
adder.draw(output="text")
a_0: ─────────■──────■───────────────────────
              │      │                       
a_1: ─────────┼──────┼────────■──────────────
     ┌──────┐ │P(π)  │        │     ┌───────┐
b_0: ┤0     ├─■──────┼────────┼─────┤0      ├
     │  QFT │        │P(π/2)  │P(π) │  IQFT │
b_1: ┤1     ├────────■────────■─────┤1      ├
     └──────┘                       └───────┘

So, looking at your question, it seems like you misplaced the control qubit of a controlled-P qubit, as your second qubit is never acted on by any gate, but be careful to take into account Qiskit's endian convention when thinking about this.

Running this naively for all inputs, I got:

import itertools
from qiskit import QuantumCircuit, Aer, transpile
from qiskit.quantum_info import Statevector

backend = Aer.get_backend('qasm_simulator')

for l in list(itertools.product([0, 1], repeat=4)):
    qc = QuantumCircuit(4)
    
    for i, bit in enumerate(l):
        if bit == 1: 
            qc.x(i)
    qc.append(adder, [0, 1, 2, 3])
    qc.measure_all()
    qc = qc.reverse_bits()
    
    job = backend.run(transpile(qc, backend), shots=1024)
    print(f"Input: {l}. Result: {job.result().get_counts()}")
Input: (0, 0, 0, 0). Result: {'0000': 1024}
Input: (0, 0, 0, 1). Result: {'0001': 1024}
Input: (0, 0, 1, 0). Result: {'0010': 1024}
Input: (0, 0, 1, 1). Result: {'0011': 1024}
Input: (0, 1, 0, 0). Result: {'0101': 1024}
Input: (0, 1, 0, 1). Result: {'0100': 1024}
Input: (0, 1, 1, 0). Result: {'0111': 1024}
Input: (0, 1, 1, 1). Result: {'0110': 1024}
Input: (1, 0, 0, 0). Result: {'1010': 1024}
Input: (1, 0, 0, 1). Result: {'1011': 1024}
Input: (1, 0, 1, 0). Result: {'1001': 1024}
Input: (1, 0, 1, 1). Result: {'1000': 1024}
Input: (1, 1, 0, 0). Result: {'1111': 1024}
Input: (1, 1, 0, 1). Result: {'1110': 1024}
Input: (1, 1, 1, 0). Result: {'1100': 1024}
Input: (1, 1, 1, 1). Result: {'1101': 1024}

This, I think, is what you expected in your table if you take into account the endian notation for each register individually (so, e.g., the input (0, 1, 0, 1) is doing $(2 + 2) \mod 4 = 0$). But you can check the documentation for the adder in more detail to confirm.

$\endgroup$
3
$\begingroup$

The correct way to interpret the number is using a compiler that follows the qubit mapping.

You can see how it is done in Classiq, for example, by running examples from the arithmetic library

To your case, you can run:

from classiq import *

@qfunc
def main(a: Output[QNum[2, False,0]], b: Output[QNum[2,False,0]], res: Output[QNum]) -> None:
    allocate(2, a)
    allocate(2, b)
    hadamard_transform(a)
    hadamard_transform(b)

    res |= a+b

qmod = create_model(main)
qprog = synthesize(qmod)
show(qprog)

job = execute(qprog)
result = job.result()[0].value
print(result.parsed_counts)

job.open_in_ide()

For more complex case, where the register represents a fraaction or signed number, you can use this qfunc instead:

@qfunc
def main(a: Output[QNum[3, True,2]], b: Output[QNum[4,False,1]], res: Output[QNum]) -> None:
    allocate(3, a)
    allocate(4, b)
    hadamard_transform(a)
    hadamard_transform(b)

    res |= a+b

Will give you interpreted results:

     {'a': -0.25, 'b': 4.0, 'res': 3.75}: 23,
     {'a': 0.75, 'b': 0.0, 'res': 0.75}: 22,
 ...

     {'a': -0.75, 'b': 6.5, 'res': 5.75}: 13,
     {'a': 0.0, 'b': 1.5, 'res': 1.5}: 12,
...
     {'a': -0.25, 'b': 3.0, 'res': 2.75}: 11,
     {'a': -0.75, 'b': 7.5, 'res': 6.75}: 10,
    ...
     {'a': -1.0, 'b': 5.5, 'res': 4.5}: 5]

And in the IDE, you can hover on the bars and see that for every result, res=a+b enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.