Qiskit / qiskit

Qiskit is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
https://www.ibm.com/quantum/qiskit
Apache License 2.0
5.03k stars 2.32k forks source link

Support alternative tensor product ordering #7628

Open kevinsung opened 2 years ago

kevinsung commented 2 years ago

What should we add?

Qiskit uses the little endian convention for ordering the factors in a tensor product. For example, the statevector for a 3-qubit state would use the ordering Q2 ⊗ Q1 ⊗ Q0. It would be useful to add support for specifying the tensor product order. This could be used, for example, to request a state vector using the common big endian ordering.

This can be implemented by adding a qubit_order argument to all functions that implicitly rely on a choice of tensor product ordering. This argument would be a list of the qubit indices in the desired tensor product order. So for the example above, it would be [2, 1, 0] to get the state in big-endian order.

The following is a non-exhaustive list of functions that would be affected:

jakelishman commented 2 years ago

I think the right way to go about this is to add a permute method to the operator/statevector/pauli-op/etc classes in qiskit.quantum_info and leave other things alone. A lot of combinatorial operations on quantum_info classes already support this via the qargs keyword argument (technically permute can already be achieved inefficiently by composing Operator with the identity operator, with a suitable qargs, but obviously that's not ideal). We are missing a single permute, though. Operator, DensityMatrix and Statevector already have reverse_qargs, which might even be enough for your use cases.

Qiskit (Terra) internally reliably uses the little-endian convention across the board when crossing API boundaries (like IQX -> Terra, etc), and I don't think it's a good idea to blur that (e.g. I'm not 100% convinced that it's a good idea to add arguments to any Result functions). Consistency is key there. But permutation of our quantum_info objects is good, and definitely a nice feature we could/should support.

kevinsung commented 2 years ago

@jakelishman Permuting quantum objects is a good feature, but it is a separate issue. Permuting a quantum object is a physical operation (i.e. permuting qubits is equivalent to performing a SWAP gate on them). This issue is about requesting information about an object, but in a different convention than the one Qiskit uses by default. Of course, this can be simulated by permuting the objects, but that is only a workaround/hack.

nonhermitian commented 2 years ago

I see this issue is raising its head again.

It was decided several years ago that Qiskit would use the little-endian convention everywhere as it makes quite a bit of sense. I am not sure that this issue will gain much traction given that all agreed that this was the way forward many years ago, and mixing conventions is never a good thing.

kevinsung commented 2 years ago

I understand the concern about mixing conventions, but I don't think it's such a big deal. I'm not suggesting mixing conventions anywhere within the library, but only providing the option to the end user. Besides, the Qiskit "ecosystem" has already started mixing conventions, considering that both Qiskit Nature and mthree use big-endian.

nonhermitian commented 2 years ago

M3 uses the standard Qiskit convention. Qiskit Nature should as well, save for perhaps some unintentional usage

kevinsung commented 2 years ago

@nonhermitian

import mthree
from qiskit import QuantumCircuit
from qiskit.providers.aer import AerSimulator

backend = AerSimulator(method='statevector')
mit = mthree.M3Mitigation(backend)
mit.cals_from_system(range(2))

circuit = QuantumCircuit(2)
circuit.x(0)
circuit.measure_all()
counts = backend.run(circuit, shots=100).result().get_counts()

quasis = mit.apply_correction(counts, range(2))
print(f"Expectation of 01: {quasis.expval('01')}")
print(f"Expectation of 10: {quasis.expval('10')}")

prints

Expectation of 01: 0.0
Expectation of 10: 1.0

The developers of Qiskit Nature deliberately chose to use big-endian instead of little-endian throughout the library (as @mrossinek can confirm). When they convert Nature operators to Terra operators, a silent switch to little-endian is performed.

jakelishman commented 2 years ago

I'm happy to accept a small, self-contained and composable function permute that is the more general case of reverse_qargs, but Terra already has a convention for API boundaries (requesting objects), and I don't want to accept a PR muddying those waters because of the associated maintenance cost, in software, documentation and mental model for users.

If you want the returned results to be in a different order when you retrieve them, you can already build the initial quantum circuit using your preferred convention, which doesn't involve any physical operations. You can easily do this without any reference to bit numbers by using the exact Qubit instances you want as the "indices" when calling circuit methods: it's totally fine to do

from qiskit.circuit import QuantumCircuit, Qubit, Clbit

my_qubits = [Qubit() for _ in [None]*10]
my_clbits = [Clbit() for _ in [None]*10]
my_qlayout = [my_qubits[x] for x in <whatever layout>]
my_clayout = [my_clbits[x] for x in <whatever layout>]
circuit = QuantumCircuit(my_qlayout, my_clayout)

circuit.h(my_qubits[0])
circuit.cx(my_qubits[0], my_qubits[1])
circuit.measure(my_qubits[1], my_clbits[1])

(In fact, internally this is actually the most efficient way to construct a circuit, though the difference is marginal.)

The hypothetical Operator.permute isn't a physical operation - it'd be implemented just as a Numpy transposition on the matrix representation of an operator, and the quantum_info module has nothing to do with hardware at all. To be clear, this is a nice feature, and I'd be happy to accept it as a helper method for the quantum_info classes. All I'm opposed to is adding extra keyword arguments while objects are being construction - the assumption should always be that everything internally was constructed in a little-endian order, and if a user wants to do post-processing on objects after the fact, it's a small, composable function.

For those reasons, I'm actually not a fan of QuantumCircuit.reverse_qubits because QuantumCircuit is always under construction, but the ship has sailed on that. Unfortunately, its existence has produced a fairly constant stream of bugs (for example #7415 and #6731, but related issues cause issues in visualisation too), which is where I'm coming from on the maintenance burden side.

kevinsung commented 2 years ago

@jakelishman I tried to simulate the circuit in your code using AerSimulator but got

Simulation failed and returned the following error message:
ERROR: Failed to load qobj: Invalid Qobj experiment: not enough memory slots.

The hypothetical Operator.permute isn't a physical operation - it'd be implemented just as a Numpy transposition on the matrix representation of an operator, and the quantum_info module has nothing to do with hardware at all.

By "physical operation" I did not mean that it is related to hardware. I meant that it is a physically significant operation. For example, suppose the operator represents the Hamiltonian of a particular physical system being modeled. Enacting a non-trivial permutation on the operator would result in a different operator that does not necessarily model the same physical system any more.

jakelishman commented 2 years ago

Oh, that's pretty weird about the AerSimulator error - it should work just fine. Certainly QuantumCircuit should have absolutely no problems representing that, so it'll be a bug in serialisation to or deserialisation from Qobj (possibly irredeemably within in the current spec, which requires registers, but that's already known and will be taken care of).

At any rate, if you put the bits in the circuit with

my_qlayout = QuantumRegister(bits=[my_qubits[x] for x in ...])
my_clayout = ClassicalRegister(bits=[my_clbits[x] for x in ...])
circuit = QuantumCircuit(my_qlayout, my_clayout)

it should be fine again (you can still use my_qubits[x] in the gate calls). My bad - I thought registerless bits weren't a problem for Aer, so I was just lazy typing out my example.


Sure, but you can do all your calculations in a "wrong" convention as long as you're consistent, then just permute them into the "right" convention whenever you want to look at the results. Nothing in there changes the physical meaning. In circuits where the layout demands physical swap gates be inserted, Terra's transpiler will actually do this logical permutation already by mutating any final measure instructions instead of applying noisy gates. I'm happy to accept a PR that adds this sort of post-processing permutation in the quantum_info classes, but not one that muddies the waters of Terra's internal convention.

jakelishman commented 2 years ago

Just to mention it again in case you missed it: it's actually already possible in quantum_info to compose operators in unusual patterns of the tensor arrangement - that's what the qargs parameter is for in compose - and you can swap between big-endian and little-endian conventions in post with the reverse_qargs method on some (but not all - happy for a PR) quantum_info classes.

kevinsung commented 2 years ago

Thanks. As I said above, I am indeed aware that permuting objects can be used to achieve the same effect as reordering labels. But again, this is just a workaround/hack.

jakelishman commented 2 years ago

If the existing forms can be used for the post-processing you'd like, I'll close this issue, but if you'd like to mentor a project to expand permute in quantum_info, I'll happily leave it open.