There are a few things that we have been doing that have confused people for a while. We need to documeent this and make sure all the parts of qiskit obey our convention.
Lets start for a single register.
Tensor order
The tensor order goes
Qn x .. Q1 x Q0
This is what we have in qiskit now. This means that the a circuit that has an X on the q0 makes the state |01>
classical bits
The classical bits are ordered so that the MSB is to the left and the LSB is to the right. This is the standard binary sequence order. For the case 01 the MSB is 0 and the LSB is 1. This means that if we map the state |01> to a classical register then it is 01 and has a 1 to 1 relationship with the basis states of the quantum system. This is why in qiskit we use the non-standard tensor product order.
As a summary
binary | int | qubit
0..00 <-> 0 |0..00 >
0..01 <-> 1 |0..01 >
0..10 <-> 2 |0..10 >
0..11 <-> 3 |0..11 >
1..00
1..01
1..10
1..11
visualization
@ajavadia would like this to go from the highest qubit at the top and go to the lowest at the bottom (this is not what we have in qiskit now).
qn --
.. --
.. --
q1 --
q0 --
@ajavadia prefers this as if every qubit is maped to a classical register and you read the outcome it is more natural to read top to bottom and this is the binary number the register would be in.
Multiple registers
This is where qiskit is the most confused. Imagine we two two regsters Q and V and that Q is declared
first.
Ie
circ = QuantumCircuit()
q = QuantumRegister(5)
v = QuantumRegister(6)
circ.add_register(q)
circ.add_register(v)
this should be the same as
q = QuantumRegister(5)
v = QuantumRegister(6)
circ = QuantumCircuit(q,v)
Note the order in the list is the order they are declared (Not tensor product)
Tensor order
The tensor product order for these registers is
Vn x ... V1 x V0 x Qn x .. Q1 x Q0
classical bits
The classical bits are order with V being the most signifcant register and Q the least significant. That way the the mapping stays 1:1 between basis states and classical bits.
1...01 and for Q is 0..11 then the bit string returned is 1...010..11. Currently qiskit was suppost to return it 1...01 0..11 (issue #1243)visualization
@ajavadia would like this to go from the highest register to lowest register and if we add a new register it adds it to the top.
vn --
.. --
.. --
.. --
v1 --
v0 --
qn --
.. --
.. --
q1 --
q0 --
Reverse simply reverses everything (to the way i prefer)
q0 --
q1 --
.. --
.. --
qn --
v0 --
v1 --
.. --
.. --
.. --
vn --
@ajavadia and @chriseclectic does this represent the summary.
Yes this is what we discussed.
I think the main point that needs to be clearly documented is that QuantumCircuit(q, v) means "add q, then add v" (v is more significant, and the left tensor). It does NOT mean q otimes v.
One minor point: why don't you want a space between classical bits? I thought we would do this in the final step in Result (obviously not in the backends, who treat the whole thing as a flat register).
I donβt mind for the space. I thought you or Chris were against it.
If you dont agree with this convention, raise your concerns asap. Otherwise, tomorrow we will break this epic in individual issues to implement this approach around the code.
Suppose that a user defines a custom gate that implements CNOT. The canonical, textbook form of CNOT, when qubit 0 is the control and qubit 1 is the target, is:
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]
The user will be surprised that the CNOT gate that she's defined has qubit 1 as the control.
This is blocking #1187. Remove the tag from #1187 once this is agreed.
@yaelbh i agree that it is different but this is why we need good documentation.
So, let's considered this as "decided". I'm creating issues for each of the subtasks.
Recently Iβve been writing a simulator based on manipulations of the density matrix. In this context, Iβve gained some insights regarding the experience of working with Qiskit qubit ordering. I expect that developers of add-ons, which are external simulators wrapped by Qiskit interface, will have similar experience. I assume that most external simulators work with the standard qubit ordering, which is different from Qiskitβs. Iβm sorry for not sharing my insights earlier, this was impossible because this is really recent work. Iβm not trying to take back a decision that has already been made, and with good arguments. I simply find that I have relevant and useful information so I should not keep it to myself.
The bottom line is that, when wrapping a simulator with Qiskit interface, one needs to constantly cope with the different qubit ordering. This makes the wrapper more difficult to write, resulting in not-so-nice code, and sometimes also costs some runtime. Here are a couple of concrete examples.
def state_num2str(basis_state_as_num, nqubits):
return '{0:b}'.format(basis_state_as_num).zfill(nqubits)
def state_str2num(basis_state_as_str):
return int(basis_state_as_str, 2)
def state_reverse(basis_state_as_num, nqubits):
basis_state_as_str = state_num2str(basis_state_as_num, nqubits)
new_str = basis_state_as_str[::-1]
return state_str2num(new_str)
adjusted_state = np.zeros(2**nqubits, dtype=complex)
for basis_state in range(2**nqubits):
adjusted_state[state_reverse(basis_state, nqubits)] = state[basis_state]
At some point I considered getting rid of all the in-and-out conversions by writing the internals of my simulator using the Qiskit qubit ordering. I began to do it, but discovered that the conversions did not disappear but entered the simulator. For example, suppose that the simulator internally contains an array as a data structure, of length n, where each array entry corresponds to a qubit. Then entry i of the array corresponds to qubit (n-1-i). I therefore decided to keep the internals of the simulator nice and clean, and have the conversions externally, in the point where the simulator meets Qiskit.
Hi all, I'm just a qiskit beginner,
it is strange to me that the order of the qubits are reversed of the typical orders in the literature.
qr = QuantumRegister(2)
cr = ClassicalRegister(2)
circ = QuantumCircuit(qr,cr)
circ.x(qr[0])
cx_op = Operator([[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0]])
circ.unitary(cx_op, [qr[1],qr[0]], label='cx') # the order is reversed
One can possibly change the CX matrix as following:
cx_op = Operator([[1, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 1, 0, 0]])
But in the literature this matrix is used when you want to have CNOT with second qubit as the control one.
Anyhow I believe this is not the most straight forward way of ordering the qubits.
Google qiskit qubit order give this issue as the first result. We should add documentation to explain this convention. The best I've found in on the qiskit tutorials, terra/fundamentals/3_summary_of_quantum_operations.ipynb, Basis vector ordering in Qiskit section:
Just leaving my opinion about the ordering convention. I found it very unnatural that the classical output bits are ordered in reverse compared to the qubits.
In my opinion, particularly when writing QuantumCircuit.measure([0,1,2], [0,1,2]), the only natural way to represent the output bits is in the same order as the qubits.
I am writing some content aimed at high school students, and I suspect students will find this aspect confusing.
@ardroc92 If it helps, the right most bit is the least significant bit. This is the bit at is multiplied by 2^0 when converting a bitstring to integer. The next bit multiplied by 2^1 and so on. So the exponent corresponds to the qubit number in the circuit.
Thanks. I understand binary notation, but I don't see why it would be desirable here.
It seems very unnatural to specify the qubits to measure as [0,1,2] and representing the outcome bits in the order [2,1,0]. I doubt any quantum computing theorist would ever write outcomes strings on paper in this order. I'd be happy to be convinced that it helps in the programming enough to offset the inconvenience that most people will reverse the list of classical bits so that the outcome bits are displayed with the qubits ordering.
@ardroc92
I found it very unnatural that the classical output bits are ordered in reverse compared to the qubits.
Not sure what you mean by this. One of the nice things about this convention is precisely that qubits and classical bits have the same order (little endian). This means q1 is more significant than q0, and c1 is more significant than c0. So if you prepare a quantum state |10> and measure q1->c1 and q0->c0, you will read out 10. And this converts to decimal nicely too: |2> -> 2.
import qiskit as qk
qc = qk.QuantumCircuit(2,2)
qc.x(1) # excite the most significant qubit
qc.measure([0, 1], [0, 1]) # measure in the same order
sim = qk.providers.aer.QasmSimulator()
counts = qk.execute(qc, sim).result().get_counts()
print(counts) # the MSB is 1
{'10': 1024}
By the way it's awesome you are writing high school content using Qiskit.
@ajavadia Thanks for the detailed reply!
The part that I find unnatural is the fact that if you write qc.measure([0,1], [0,1]), then as in your example, the outcome is {'10': 1024}. I would have expected it to be '01'.
What I am not a fan of is that when I draw the circuit qc using qc.draw the qubits are represented with the |0> qubit at the top and |1> qubit at the bottom. The typical convention is that the state represented by this circuit is the state |01>. So, I would expect the outcome of measuring both qubits in the same order [0,1] to be '01', not '10'. Does that make sense? Thanks.
Ok I think in your case the confusion comes from this syntax qc.measure([0,1], [0,1]).
This just means qc.measure(qubits=[0,1], clbits=[0,1]). And it measures q0 to c0 and q1 to c1. It would be exactly the same if you wrote qc.measure([1,0], [1,0]).
If this shorthand one-liner is confusing, expand it to multiple lines:
qc.measure(0, 0) # measure q0 to c0
qc.measure(1, 1) # measure q1 to c1
As for drawing, yes as you see in the origin of this issue I was a fan of drawing the most-significant-bit at the top. However there is an option to do that in the drawers if you prefer:
qc.draw() (default drawing)
βββ
q_0: ββββββ€Mββββ
βββββββ₯ββββ
q_1: β€ X βββ«ββ€Mβ
βββββ β ββ₯β
c_0: βββββββ©βββ¬β
β
c_1: ββββββββββ©β
qc.draw(reverse_bits=True)
βββββ βββ
q_1: β€ X βββββ€Mβ
ββββββββββ₯β
q_0: ββββββ€Mβββ«β
ββ₯β β
c_1: βββββββ¬βββ©β
β
c_0: βββββββ©ββββ
Thanks for the patience. I was clear about the sintax of qc.measure, and I understand that everything can be fixed with a "reverse" somewhere. Maybe one way to precisely frame my "complaint" is:
the choice of default ordering for the representation of the outcome string is inconsistent with the choice of default ordering for qubits in a drawn circuit.
And, I have to admit, even if the "reverse_bits = True" were to be made default I would still be slightly unsatisfied, because I believe the prevalent convention throughout quantum information for ordering and referring to qubits in a circuit is to start from the top and proceed in ascending order (i.e. modulo the Pythonic zero, the "second qubit" or "second register" is the second one from the top - this is not the case when selecting reverse_bits=True).
To recap, call the prevalent convention for ordering and referring to qubits in a circuit "a". Then the prevalent convention for ordering outcome strings is also "a". So "a,a". What qiskit currently does as default is "a, b". What the reverse_bits=True implements is "b,b" (which is an improvement over "a,b"). What I suggest be the default in qiskit is: "a,a".
Most helpful comment
Suppose that a user defines a custom gate that implements CNOT. The canonical, textbook form of CNOT, when qubit 0 is the control and qubit 1 is the target, is:
The user will be surprised that the CNOT gate that she's defined has qubit 1 as the control.