Beispiel #1
0
 def __init__(self, num_rails, initial_state=None):
     self.num_rails = num_rails
     self.state = []
     self.gates = []
     if initial_state is not None:
         if initial_state.shape == [num_rails, 1]:
             # Qubit-wise definition.
             state = None
             for qubit in initial_state:
                 if qubit == 0:
                     if state is None:
                         state = zero
                     else:
                         state = tensorprod(state, zero)
                 elif qubit == 1:
                     if state is None:
                         state = one
                     else:
                         state = tensorprod(state, one)
                 else:
                     raise ValueError('Qubit can only be either 0 or 1')
         else:
             if initial_state.shape != (2 ** num_rails, 1):
                 # Not a full description.
                 raise ValueError('Incomplete or improper definition of'
                                  ' initial_state')
     else:
         initial_state = tensor(zero, num_rails)
     self.state.append(initial_state)
Beispiel #2
0
from tensorprod import tensor
from circuit import Circuit
from grover import iterator
from measurement import measure

if __name__ == '__main__':
    N = input('Enter the number of items in the search space: ')
    n = int(np.ceil(np.log2(N)) + 1)
    M = input('Enter the number of search targets: ')
    targets = []
    for i in range(M):
        target = raw_input('Target %d in binary: ' % (i + 1))
        targets.append(target)
    num_iter = input('Number of iterations: ')

    initial_state = tensor(plus, n)
    circuit = Circuit(n, initial_state)
    # Apply the Z gate to the ancilla bit to make it |-> for phase kickback
    # Multiply by root 2 to renormalize without considering the ancilla qubit
    state = np.sqrt(2) * circuit.add_gate(Z, [
        n,
    ])

    # Switch to interactive mode
    plt.ion()
    # Plot the initial probability amplitudes and angle
    amps_fig = plt.figure(0)
    amps = amps_fig.add_subplot(111)
    amps.bar(np.arange(state.size / 2) + 0.1, abs(state[::2])**2)
    amps_fig.show()
    angles_fig = plt.figure(1)
Beispiel #3
0
from tensorprod import tensor
from circuit import Circuit
from grover import iterator
from measurement import measure

if __name__ == "__main__":
    N = input("Enter the number of items in the search space: ")
    n = int(np.ceil(np.log2(N)) + 1)
    M = input("Enter the number of search targets: ")
    targets = []
    for i in range(M):
        target = raw_input("Target %d in binary: " % (i + 1))
        targets.append(target)
    num_iter = input("Number of iterations: ")

    initial_state = tensor(plus, n)
    circuit = Circuit(n, initial_state)
    # Apply the Z gate to the ancilla bit to make it |-> for phase kickback
    # Multiply by root 2 to renormalize without considering the ancilla qubit
    state = np.sqrt(2) * circuit.add_gate(Z, [n])

    # Switch to interactive mode
    plt.ion()
    # Plot the initial probability amplitudes and angle
    amps_fig = plt.figure(0)
    amps = amps_fig.add_subplot(111)
    amps.bar(np.arange(state.size / 2) + 0.1, abs(state[::2]) ** 2)
    amps_fig.show()
    angles_fig = plt.figure(1)
    angles = angles_fig.add_subplot(111, polar=True)
    theta = np.arccos(np.sqrt((2.0 ** (n - 1) - M) / (2 ** (n - 1))))
Beispiel #4
0
    def add_gate(self, gate, qubits, controls=[]):
        """
        Add a gate to the circuit.

        Can currently handle only single-qubit gates with one or more controls.

        Parameters
        ----------
        gate : np.ndarray
            2D matrix defining the transformation. Must be unitary. Size of the
            matrix must be (NxN) where N is 2^len(qubits)
        qubits : list
            List of rail or qubit numbers upon which this gate acts. Numbers
            must be between 1 and num_rails.
        controls : list
            Control qubits upon which this gate depends. Absolute value of
            qubit numbers must be between 1 and num_rails. Use a negative sign
            to indicate 0-control.

        Returns
        -------
        state : np.ndarray
            (num_rails x 1) dimensional state vector after successful
            application of the gate.
        """

        # Implementation notes:
        #
        # 1. Currently computing whole operator matrix for the entire state
        #    space from the single-qubit gate, the qubit being operated upon
        #    and the control qubits.
        # 2. Qubit-wise computations are not feasible, because we would then
        #    have to operate multi-qubit gates on some qubits *only*, which
        #    would be difficult because it is hard to extract qubits from the
        #    system state.
        # 3. Implementing control by looking at the probability of the control
        #    qubit being 0 or 1 and then applying the operation with that
        #    probability is harder than simply writing out the whole operation
        #    matrix.

        n = len(qubits)
        N = 2 ** n
        if gate.shape != (N, N):
            raise ValueError('Gate is of improper shape')
        if n != 1 or gate.shape != (2, 2):
            raise NotImplementedError('Cannot handle gates that are not single'
                                      '-qubit')
        controls = np.array(controls)

        # First, determine the x-operator, to invert zero-control rails.
        x_operator = np.array([[1,],])
        for i in xrange(1, self.num_rails+1):
            if i in abs(controls):
                if controls[np.where(abs(controls) == i)] < 0:
                    # Zero-control
                    x_operator = tensorprod(x_operator, X)
                else:
                    x_operator = tensorprod(x_operator, I)
            else:
                x_operator = tensorprod(x_operator, I)

        # Now, determine the actual operation of the gate itself.
        operator = np.array([[1,],])
        for i in xrange(1, self.num_rails+1):
            if i in qubits:
                if i in abs(controls):
                    raise ValueError('A qubit cannot be used for the gate and'
                                     ' also be a control')
                else:
                    operator = tensorprod(operator, gate)
            else:
                # Ignore control qubits for now. Treat them like identity.
                operator = tensorprod(operator, I)

        # Now go back and identify (pun intended) parts of the operator where
        # there is control.
        controls = abs(controls)
        controls.sort()
        # Identity is a big I block that will be used to wipe out those places
        # where there should have been a control, but we put a gate instead.
        identity = tensor(I, self.num_rails-1)
        for i in controls[::-1]:
            # Block size is the number of contiguous 0's that appear at the
            # i'th qubit when you write the binary numbers in order.
            block_size = 2 ** (self.num_rails - i)
            # We start with one square of dimension (block_size x block_size)
            # at the (0, 0) index and make it identity. We then skip one
            # block, go to the third block starting from (0, 0) and repeat.
            # We do this both sideways and downwards. That effectively adds
            # the requisite control to the entire operator.
            for j in xrange(0, 2**self.num_rails, 2*block_size):
                for k in xrange(0, 2**self.num_rails, 2*block_size):
                    # p and q are starting positions of blocks of dimension
                    # (block_size x block_size) in indentity. These are
                    # required because identity is only half as big in side
                    # length when compared with operator.
                    p = j / 2
                    q = k / 2
                    operator[j:j+block_size, k:k+block_size] = (
                        identity[p:p+block_size, q:q+block_size]
                    )

        # Compute the overall gate matrix
        gate_matrix = np.dot(x_operator, np.dot(operator, x_operator))
        # Compute and return the state after application of the gate
        state = np.dot(gate_matrix, self.state[-1])
        self.state.append(state)
        return state