ProjectQ-Framework / ProjectQ

ProjectQ: An open source software framework for quantum computing
https://projectq.ch
Apache License 2.0
878 stars 271 forks source link

Suggestion: Meta gate for concatenation of gates #268

Open cgogolin opened 6 years ago

cgogolin commented 6 years ago

It could be nice to have a meta gate (similar to how Tensor and ControledGate work) that represents a concatenation of two or more gates. As a concrete use case, consider that a user might want to define a gate that does a three axis rotation. It would be nice if one could do something like

R = Concatenate(Rx, Ry, Rz)

and then

R(x,y,z) | q[0]

would do the same as

Rz(z) | q[0]
Ry(y) | q[0]
Rx(x) | q[0]
damiansteiger commented 6 years ago

Thanks for the suggestion.

A single qubit rotation around a general axis might indeed be useful. Can you think of more examples?

A new gate would do the trick even better as the compiler could have a specialized rules for that one general rotation gate but of course if it happens frequently, then such a meta gate is worth thinking about.

By the way, a similar feature of concatenating gates was discussed in #116 and I think some (but not all!) arguments apply here as well.

Concatenate as a meta gate solves the problem of @ not being available in python2.7 (though maybe soon one does not need to support python2.7 anymore ;-)

Would you suggest to limit Concatenate to single qubit gates?

A similar idea which was once floating around is an easier way to define new gates on the fly, which would also allow the use the | syntax as Concatenate...

cgogolin commented 6 years ago

Use cases will depend on the algorithms people write, but I like the idea of Concatenate because, as algorithms get more complex one can encapsulate a small sub-routine in such a custom gate.

I would restrict Concatenate to gates that act on the same number of qubits, otherwise it will be difficult to specify with the | syntax which systems they are supposed to act on. Maybe an additional At meta-gate that applies a gate to a subsystem could be useful as well, then one could do something like

Example(x,y) = Concatenate(At(0,2,Rx),At(1,2,Ry),CNOT)

to get a gate on two qubits that would apply a CNOT followed by a x rotation on the first qubit (position 0 of 2) and and y rotation on the second qubit (position 1 of 2).

The problem with this is again the incompatible syntax (also noted in #116) for how to specify the right hand side of | for controlled and non-controlled gates. Why can tuples of qubits not be treated on a equal footing to quregs?

thomashaener commented 6 years ago

I think adding a new gate for rotations around arbitrary axes would indeed be useful. However, I wouldn't introduce another way to write functions via Concatenate or At; writing it as a function is much easier (also makes it easier to read). What we can do is try to make registering decomposition rules a shorter process. I.e., provide a shortcut which turns a given function to a gate.

It's not that tuples of qubits are different from quregs. The correct syntax of any gate is

Gate | (qureg1, qureg2, ...)

so the argument is a tuple of quantum registers. We also allow incorrect syntax as long as we can infer what the correct syntax would be. E.g., ProjectQ automatically turns a tuple of qubits into a tuple of quantum registers of length 1... just to make things a bit easier for users and the code nicer to look at.

cgogolin commented 6 years ago

I think adding a new gate for rotations around arbitrary axes would indeed be useful.

That would be nice!

Going a bit beyond the original scope of this issue (maybe the title should be changed if we go further in this direction):

An arbitrary single qubit unitary and/or Hermitian gate would also be nice. Or would it be too complicated to define gate decompositions for these?

damiansteiger commented 6 years ago

An arbitrary single qubit unitary and/or Hermitian gate would also be nice. Or would it be too complicated to define gate decompositions for these?

So far arbitrary single qubit gates (must of course be unitary) can be defined by matrices (and the compiler decomposes them into our standard gates):

my_gate = BasicGate()
my_gate.matrix = numpy.matrix(...)
my_gate | qubit

Do you have something else in mind?

Hermitian gates are impossible, but one can represent hermitian operators using QubitOperator which can be used to construct the unitary TimeEvolution gate or for emulation functions of the simulator such as get_expectation_value

cgogolin commented 6 years ago

Nice! I wasn't aware of that! Is that somewhere explained in the documentation? I thought the BasicGate was really just a parent class for the other gates, and nothing I should be using as a user. I really need to give the decomposer/compiler code a closer look, to understand what it can do automatically and what needs special decomposition rules.

There remains the suggestion to have an arbitrary single qubit rotation gate. Everything else looks perfect.

damiansteiger commented 6 years ago

It was rather silently introduced in the last release as we used it previously from time to time for testing https://github.com/ProjectQ-Framework/ProjectQ/releases/tag/v0.4.1, see section on improvements. An upcoming feature will be better syntax checking in which case one can derive from a new class SingleQubitGate which makes things nicer. In the meantime to trick with BasicGate works.

It is suggested to use the native gates as they have specialized decompositions, whereas for a gate defined by a matrix the decomposition might not be close to optimal. For single qubit gates, there is a fall back implemented if we don't have a special decomposition but the gate has a matrix in https://github.com/ProjectQ-Framework/ProjectQ/blob/develop/projectq/setups/decompositions/arb1qubit2rzandry.py

There is another user currently implementing a fallback for larger gate matrices.