Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend QuSimPy with rotgates #4

Open
BramDo opened this issue Apr 19, 2019 · 3 comments
Open

Extend QuSimPy with rotgates #4

BramDo opened this issue Apr 19, 2019 · 3 comments

Comments

@BramDo
Copy link

BramDo commented Apr 19, 2019

The simulator works great, however, i would like to extend with rotation gates and U1, U2 and U3 gates. I have defined for U3

@staticmethod
def U3(theta,phi,lambd):
theta_mat=np.matrix([[np.cos(theta/2),np.sin(theta/2)],[-np.sin(theta/2),np.cos(theta/2)]])
phi_mat=np.matrix([[1,0],[0, e**(i phi)]])
lambd_mat=np.matrix([[1,0],[0,e
*(i lambd)]])
return phi_mat
theta_mat*lambd_mat

So now i can do the following
gateMatrix = gates.U3(pi/2,pi/3,0)
print(gateMatrix)

Result
[[ 0.70710678+0.j 0.70710678+0.j ]
[-0.35355339-0.61237244j 0.35355339+0.61237244j]]

How todo insert this in to the applygate ?

@adamisntdead
Copy link
Owner

adamisntdead commented Apr 19, 2019

Hello, I actually have these implemented already in a different simulator.
I will transfer them across.

@adamisntdead
Copy link
Owner

Actually, I think that adding those to the main repository would complicate the code.
However, I will show you how you could rewrite it.

What I would do is add U3, U2, and U1 to return the gate matrices. Then I would add another function called applyGateMatrix.

The finished new file would be like

from functools import reduce
import numpy as np

class gates:
    i = np.complex(0, 1)

    singleQubitGates = {
        # Pauli-X / Not Gate
        'X': np.matrix([
            [0, 1],
            [1, 0]
        ]),
        # Pauli-Y Gate
        'Y': np.matrix([
            [0, -i],
            [i, 0]
        ]),
        # Pauli-Z Gate
        'Z': np.matrix([
            [1, 0],
            [0, -1]
        ]),
        # Hadamard Gate
        'H': np.multiply(1. / np.sqrt(2), np.matrix([
            [1, 1],
            [1, -1]
        ])),
        # Identity Gate
        'Id': np.eye(2),
        # S & S Dagger Gate
        'S': np.matrix([
            [1, 0],
            [0, i]
        ]),
        'SDagger': np.matrix([
            [1, 0],
            [0, i]
        ]).conjugate().transpose(),
        # T & T Dagger / Pi over 8 Gate
        'T': np.matrix([
            [1, 0],
            [0, np.e**(i * np.pi / 4.)]
        ]),
        'TDagger': np.matrix([
            [1, 0],
            [0, np.e**(i * np.pi / 4.)]
        ]).conjugate().transpose()
    }

    @staticmethod
    def U3(theta, phi, lda):
        a = np.exp(-1j * (phi + lda) / 2) * np.cos(theta / 2)
        b = - np.exp(-1j * (phi - lda) / 2) * np.sin(theta / 2)
        c = np.exp(1j * (phi - lda) / 2) * np.sin(theta / 2)    
        d = np.exp(1j * (phi + lda) / 2) * np.cos(theta / 2)
    
        return np.matrix([
            [a, b],
            [c, d]
        ])
    
    @staticmethod
    def U2(phi, lda):
        return gates.U3(np.pi / 2, phi, lda)
    
    @staticmethod
    def U1(lda):
        return gates.U3(0, 0, lda) 

    @staticmethod
    def generateGate(gate, numQubits, qubit1, qubit2=1):
        if (gate == 'CNOT'):
            control = qubit1
            target = qubit2

            identity = np.eye(2)
            X = gates.singleQubitGates['X']
            # NaN is our 'C' from the multi qubit gate generation formula
            C = np.mat([
                [float('nan'), 0],
                [0, 1]
            ])

            # Set the gate order
            gateOrder = []
            for i in range(1, numQubits + 1):
                if (i == control):
                    gateOrder.append(C)
                elif (i == target):
                    gateOrder.append(X)
                else:
                    gateOrder.append(identity)

            # Generate the gate and then replace the NaNs to Id gates
            newGate = reduce(np.kron, gateOrder)

            n = newGate.shape[0]
            return np.mat([[newGate[i, j] if not np.isnan(newGate[i, j]) else 1 if i == j else 0 for j in range(n)] for i in range(n)])

        else:
            # Put these here for handyness
            identity = gates.singleQubitGates['Id']
            mainGate = gates.singleQubitGates[gate]
            gateOrder = (mainGate if i == qubit1 else identity
                         for i in range(1, numQubits + 1))
            return reduce(np.kron, gateOrder)


class QuantumRegister:

    def __init__(self, numQubits):
        self.numQubits = numQubits

        # The number of amplitudes needed is 2^n, where N is the 
        # number of qubits, So start with a vector of zeros.
        self.amplitudes = np.zeros(2**numQubits)
        # Set the probabilit of getting 0 when measured to 1
        self.amplitudes[0] = 1

        self.value = False

    def applyGate(self, gate, qubit1, qubit2=-1):
        if self.value:
            raise ValueError('Cannot Apply Gate to Measured Register')
        else:
            # Generate the gate matrix
            gateMatrix = gates.generateGate(
                gate, self.numQubits, qubit1, qubit2)
            # Calculate the new state vector by multiplying by the gate
            self.amplitudes = np.dot(self.amplitudes, gateMatrix)

    def applyGateMatrix(self, gate, target):
        if self.value:
            raise ValueError('Cannot Apply Gate to Measured Register')
        else:
            identity = gates.singleQubitGates['Id']
            gateOrder = (gate if i == target else identity
                         for i in range(1, self.numQubits + 1))
            full_gate = reduce(np.kron, gateOrder)

            self.amplitudes = np.dot(self.amplitudes, full_gate)

    def measure(self):
        if self.value:
            return self.value
        else:
            # Get this list of probabilities, by squaring the absolute
            # value of the amplitudes
            self.probabilities = []
            for amp in np.nditer(self.amplitudes):
                probability = np.absolute(amp)**2
                self.probabilities.append(probability)

            # Now, we need to make a weighted random choice of all of the possible
            # output states (done with the range function)

            results = list(range(len(self.probabilities)))
            self.value = np.binary_repr(
                np.random.choice(results, p=self.probabilities),
                self.numQubits
            )
            return self.value

# And thats it!

and we could use it like:

from QuSim import QuantumRegister, gates

reg = QuantumRegister(3)
gateMatrix = gates.U3(pi/2,pi/3,0)

reg.applyGateMatrix(gateMatrix, 1)

@BramDo
Copy link
Author

BramDo commented Apr 22, 2019

Thanks. QuSimPy does what it's name implies and can easily extended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants