qiboteam / qibo

A framework for quantum computing
https://qibo.science
Apache License 2.0
284 stars 55 forks source link

Add general matrix to a circuit #554

Closed juliabarbera closed 1 year ago

juliabarbera commented 2 years ago

I was looking for the way to add a diagonal matrix in Qibo language which doesn't correspond to any quantum logic gate to a circuit. How can I do that?

stavros11 commented 2 years ago

The Unitary gate allows you to add any matrix as a gate to a circuit, for example

import numpy as np
from qibo import models, gates

c = models.Circuit(3)
matrix = np.random.random((4, 4)) # random 4x4 matrix
c.add(gates.Unitary(matrix, 0, 1)) # act with the matrix on qubits 0 and 1

This will apply the matrix by multiplying it to the state vector with proper indexing according to the target qubits.

Is your diagonal matrix very large? There may be a decrease in performance if the matrix (or number of target qubits) is large because this method will not exploit the fact that it is diagonal, as it works for an arbitrary matrix too.

juliabarbera commented 2 years ago

Thank you for your answer. I want to perform something like this: Captura de Pantalla 2022-02-21 a les 15 01 45

Where state is an integer.

stavros11 commented 2 years ago

It seems that the number of targets is the total number of qubits in the circuit. The gates.Unitary approach will work, but you will probably notice some deterioration if nqubits > 10 and eventually run out of memory, because it will be creating a dense matrix of size (2^nqubits, 2^nqubits).

If I understand correctly, the matrix you are applying is the identity, with a single 1 flipped to -1 in the position defined by state. If state == nqubits - 1 this is a Z gate controlled on all qubits, which you can add as:

c.add(gates.Z(nqubits - 1).controlled_by(*range(nqubits - 1)))

If state != nqubits - 1, I believe you can still do this with the same gate if you apply X gates to move the -1 to the appropriate place:

c.add(gates.X(q))
c.add(gates.Z(nqubits - 1).controlled_by(*range(nqubits - 1)))
c.add(gates.X(q))

where q depends on state. This approach is slightly more complicated to code but depending on your total circuit, may be faster, because controlled gates are applied with indexing which is more efficient than creating a big matrix.

EDIT (more details): The X gate should be applied in all qubits that correspond to 0 when you represent state in binary. So if state == 0 you'll have to apply X to all qubits, etc., that's why how efficient this is depends on the value of state.

igres26 commented 2 years ago

Concerning the implementation of these types of gates, it might be valuable to implement an "Oracle" gate in a more native way in Qibo in order to accommodate fast simulation of Grover (and amplitude amplification-like) algorithms.

Something like:

c.add(gates.Oracle(*q, [states]))

where q is the qubit register where the oracle is applied, and the states are the target states that are to be flipped.

From there, the decomposition to gates, and then to two-qubit gates for implementation, can be automatized.

juliabarbera commented 2 years ago

Thank you for the review. I have used gates.Unitary and it worked. I will implement quantum gates but first I wanted to calculate the matrix classically.