Ejemplo n.º 1
0
    def __ccNot(self, gate_info):
        """
        Creates a Sparsematrix representing the controlled-controlled-not (ccnot or Tiffoli) 
        gate for the given qubits. Can be applied to any exisiting qubits.

        :param gate_info: (tuple(int, int, int)) first, second qubit controlling the gate and the qubit controlled by the other two
        :return: (SparseMatrix)  Matrix representation of the gate
        """
        control1, control2, qbit3 = gate_info
        # Reset the values to range from 0 to maxval
        minval = min(control1, control2, qbit3)
        maxval = max(control1, control2, qbit3) - minval
        control1 = control1 - minval
        control2 = control2 - minval
        qbit3 = qbit3 - minval

        # Create elements and calculate dimensions
        elements = [Sparse.MatrixElement(0, 0, 1)]
        dimension = 2**(maxval + 1)

        # For each possible bit check whether the control qubits are active
        for i in range(1, dimension):
            if self.__bitactive(i, control1) and self.__bitactive(i, control2):
                # if control qubits are active, calculate the new value and insert into matrix
                col = self.__toggle(i, qbit3)
                elements.append(Sparse.MatrixElement(i, col, 1))
            else:
                elements.append(Sparse.MatrixElement(i, i, 1))
        return Sparse.SparseMatrix(dimension, elements)
Ejemplo n.º 2
0
    def __cNot(self, gate_info):
        """
        Creates an arbitrary sized controlled not gate between two arbitrary qbits.

        :param gate_info: (tuple(int, int)) Position in the circuit of the control qubit and the controlled qubit
        :return: (SparseMatrix) The cnot gate entangling the two qubits given.
        """
        qbit1, qbit2 = gate_info

        if qbit1 > qbit2:
            control_bit = np.abs(qbit2 - qbit1)
            controlled_bit = 0
        else:
            control_bit = 0
            controlled_bit = qbit2 - qbit1

        elements = [Sparse.MatrixElement(0, 0, 1)]
        dimension = 2**(np.abs(qbit2 - qbit1) + 1)
        for i in range(1, dimension):
            if self.__bitactive(i, control_bit):
                col = self.__toggle(i, controlled_bit)
                elements.append(Sparse.MatrixElement(i, col, 1))
            else:
                elements.append(Sparse.MatrixElement(i, i, 1))
        return Sparse.SparseMatrix(dimension, elements)
Ejemplo n.º 3
0
def numpy_vs_lazy():
    min_qubits = 8
    max_qubits = 14
    had = Sparse.ColMatrix(2)
    had[0, 0] = 1 / np.sqrt(2)
    had[0, 1] = 1 / np.sqrt(2)
    had[1, 0] = 1 / np.sqrt(2)
    had[1, 1] = -1 / np.sqrt(2)
    np_had = np.array([[1, 1], [1, -1]]) / np.sqrt(2)

    times = np.zeros((2, max_qubits - min_qubits))
    memoryusage = np.zeros((2, max_qubits - min_qubits))

    for i in range(min_qubits, max_qubits):
        test_vector = np.ones(2**i) / np.sqrt(2**i)
        #test_vector[0] = 1
        t1 = time.time()
        for qubit in range(i):
            gate = Sparse.Gate(2**i, had, [qubit])
            test_vector = gate.apply(test_vector)
        t2 = time.time()
        memoryusage[0, i - min_qubits] = sys.getsizeof(gate)
        #print(test_vector)

        test_vector = np.ones(2**i) / np.sqrt(2**i)
        #test_vector[0] = 1
        t3 = time.time()
        gate = np.array([1])
        for qubit in range(i):
            gate = np.kron(gate, np_had)
        test_vector = gate.dot(test_vector)
        #print(test_vector)
        t4 = time.time()
        times[0, i - min_qubits] = t2 - t1
        times[1, i - min_qubits] = t4 - t3
        memoryusage[1, i - min_qubits] = sys.getsizeof(gate)

    print(
        'Time taken to superimpose all elements of an array using hadamard gates'
    )
    print(times)
    print(memoryusage)
    fig1 = plt.figure()
    ax1 = fig1.add_axes([0.1, 0.1, 0.8, 0.8])
    ax1.plot([i for i in range(min_qubits, max_qubits)],
             times[0],
             label='Lazy Implementation')
    ax1.plot([i for i in range(min_qubits, max_qubits)],
             times[1],
             label='Numpy Implementation')
    fig1.suptitle(
        "Runtime of Applying a Hadamard Gate to Every Qubit in a Register",
        fontsize='15')
    plt.xlabel("Number of Qubits", fontsize='14')
    plt.ylabel("Runtime (s)", fontsize='14')
    plt.yscale('log')
    plt.xlim((min_qubits, max_qubits - 1))
    ax1.legend()
    plt.show()
 def __cP(self, gate_info):
     mat = Sparse.ColMatrix(4)
     mat[0, 0] = 1
     mat[1, 1] = 1
     mat[2, 2] = 1
     mat[3, 3] = np.exp(1j * gate_info[-1])
     gate = Sparse.Gate(self.size, mat, list(gate_info)[:-1])
     self.register.Statevec = gate.apply(self.register.Statevec.Elements)
Ejemplo n.º 5
0
def calculateOptimalSparseFlops(matrices):
    sparsityPatterns = [
        matrix.getOriginalSparsityPattern() for matrix in matrices
    ]
    # Eliminate irrelevant entries in the matrix multiplication
    equivalentSparsityPatterns = Sparse.equivalentMultiplicationPatterns(
        sparsityPatterns)
    return Sparse.calculateOptimalSparseFlops(equivalentSparsityPatterns)
    def __Rt(self, theta, pos):
        """
        Creates and applies the matrix representing the rotation gate on one qubit.

        :param theta: (float) Rotation angle.
        :param pos: (int) position of the qubit.
        """
        mat = np.array([[1, 0], [0, np.exp(1j * theta)]])
        gate = Sparse.Gate(self.size, Sparse.toColMat(mat), [pos])
        self.register.Statevec = gate.apply(self.register.Statevec.Elements)
    def __NCP(self, gate_info):
        """
        Creates and applies the matrix representing the n controlled phase operation on n qubits.

        :param gate_info: (tuple(int,..., int, float)) The qubits the rotation applies to, Float for the rotation angle.
        """
        length = max(gate_info) - min(gate_info) + 1
        ncp = Sparse.ColMatrix(2**(length))
        for i in range(2**(length)):
            ncp[i, i] = 1
        ncp[2**length - 1, 2**length - 1] = np.exp(gate_info[-1])
        gate = Sparse.Gate(self.size, ncp, list(gate_info)[:-1])
        self.register.Statevec = gate.apply(self.register.Statevec.Elements)
    def __NCZ(self, gate_info):
        """
        Creates and applies the matrix representing the n controlled z operation on n qubits.

        :param gate_info: (tuple(int,..., int)) The qubits the z flip applies to.
        """
        length = max(gate_info) - min(gate_info) + 1
        ncz = Sparse.ColMatrix(2**(length))
        for i in range(2**(length)):
            ncz[i, i] = 1
        ncz[2**length - 1, 2**length - 1] = -1
        gate = Sparse.Gate(self.size, ncz, list(gate_info))
        self.register.Statevec = gate.apply(self.register.Statevec.Elements)
 def initialize(self):
     """
     Sets the statevector according to the current state of qubits
     """
     self.Statevec = Sparse.Vector(np.array([1], dtype=complex))
     for qbit in self.Qbits[::-1]:
         self.Statevec = self.Statevec.outer(qbit.vals)
Ejemplo n.º 10
0
    def __cP(self, gate_info):
        """
        Creates a controlled phase gate for the given qubits

        :param gate_info: (tuple(int, int, float)) The information supplied to the gate. Control qubits as ints, float as the phase.
        :return: (SparseMatrix) Representation of the cp gate
        """
        qbit1, qbit2, phi = gate_info

        elements = [Sparse.MatrixElement(0, 0, 1)]
        dimension = 2**(np.abs(qbit2 - qbit1) + 1)
        for i in range(1, dimension):
            if self.__bitactive(i, qbit1) and self.__bitactive(i, qbit2):
                elements.append(Sparse.MatrixElement(i, i, np.exp(1j * phi)))
            else:
                elements.append(Sparse.MatrixElement(i, i, 1))
        return Sparse.SparseMatrix(dimension, elements)
    def __cNot(self, gate_info):
        """
        Creates and applies the matrix representing the controlled x operation on 2 qubits.

        :param gate_info: (tuple(int, int, int)) First int is the control qubit, last int is the controlled qubit.
        """
        gate = Sparse.Gate(self.size, self.large_gates['cn'], list(gate_info))
        self.register.Statevec = gate.apply(self.register.Statevec.Elements)
    def __cZ(self, gate_info):
        """
        Creates and applies the matrix representing the controlled z operation on two qubits.

        :param gate_info: (tuple(int, int)) The qubits the controlled z flip applies to.
        """
        gate = Sparse.Gate(self.size, self.large_gates['cz'], list(gate_info))
        self.register.Statevec = gate.apply(self.register.Statevec.Elements)
    def __Swap(self, gate_info):
        """
        Creates and applies the matrix representing the swap operation between two qubits.

        :param gate_info: (tuple(int, int)) The two gates to be swapped.
        """
        gate = Sparse.Gate(self.size, self.large_gates['swap'],
                           list(gate_info))
        self.register.Statevec = gate.apply(self.register.Statevec.Elements)
Ejemplo n.º 14
0
def LazyGroverDemo(n_qubits, measured_bits, plot_results=True):
    grover_circuit = QuantumCircuit.QuantumCircuit('Grover', n_qubits)
    grover_circuit.addGate('h', [i for i in range(n_qubits)])
    repetitions = round(np.pi/4*np.sqrt(2**n_qubits)) - 1
    
    grover_circuit.addmeasure()
    # calculate oracle
    oracle = Sparse.ColMatrix(2**n_qubits)
    for i in range(2**n_qubits):
        if i in measured_bits:
            oracle[i,i] = -1
        else: oracle[i,i] = 1
    #print('oracle is:')
    #print(oracle)
    #Add Oracle
    grover_circuit.addCustom(0, n_qubits-1, oracle, 'oracle')
    
    #grover_circuit.addmeasure()
    #Add diffuser
    diffuser(grover_circuit)

    grover_circuit.addmeasure()
    # Repeat if necessary
    
    ""
    for i in range(int(repetitions)):
        # Add Oracle
        grover_circuit.addCustom(0, n_qubits-1, oracle, 'oracle')
        #Add diffuser
        diffuser(grover_circuit)
        grover_circuit.addmeasure()
    ""
    #print(np.array(grover_circuit.gates, dtype=object)[:,:6])

    #show results
    #print(grover_circuit.return_measurements())
    final_statevec, measurements = grover_circuit.lazysim()
    #for m in measurements[1]:
    #   print(m)
    
    if plot_results:
        # plots the results in a snazzy way
        figure, axis = plt.subplots(1, len(measurements[1]))
        for j, measurement in enumerate(measurements[1]):
            axis[j].bar([i for i in range(measurement.size)], measurement*np.conj(measurement))
            axis[j].set_ylim([0,1])
            axis[j].set_xlabel("State |N>", fontsize = '13')
            if j>0:
                axis[j].set_yticklabels("")
            #print((results[2][1][j]*np.conj(results[2][1][j])).sum())
        axis[0].set_ylabel("Probability", fontsize = '13')
        
        #figure.set_ylabel("Probability of Measuring State")
        figure.suptitle("Probability of measuring state N",fontweight='bold', fontsize='15')
        plt.show()
        
        print(grover_circuit)
Ejemplo n.º 15
0
 def __Rt(self, theta):
     """
     Creates an r gate with the given phase
 
     :param theta: (float) The angle in radians which the qubit should be rotated by.
     :return: (SparseMatrix) Matrix representation of the r gate.
     """
     return Sparse.makeSparse(
         np.array([[1, 0], [0, np.exp(1j * theta)]], dtype=complex))
 def setStateVec(self, newVec):
     """
     Allows the user to set the state vector to a new vector. Automatically normalises the vector.
     
     :param newVec: (list) The new vector to become the state vector
     """
     newVec = np.array(newVec, dtype=complex)
     assert self.Statevec.Dimension == newVec.size, 'Wrong dimensions for new statevector'
     normal_const = np.sqrt((newVec * newVec.conj()).sum())
     self.Statevec = Sparse.Vector(newVec / normal_const)
Ejemplo n.º 17
0
    def __cZ(self, gate_info):
        """
        Creates a controlled z gate given 2 qubits

        :param gate_info: (tuple(int, int)) The two gates to control the z
        :return: (SparseMatrix) Representation of the cz gate
        """
        qbit1, qbit2 = gate_info
        shift = min(qbit1, qbit2)
        qbit1 = qbit1 - shift
        qbit2 = qbit2 - shift

        elements = [Sparse.MatrixElement(0, 0, 1)]
        dimension = 2**(np.abs(qbit2 - qbit1) + 1)
        for i in range(1, dimension):
            if self.__bitactive(i, qbit1) and self.__bitactive(i, qbit2):
                elements.append(Sparse.MatrixElement(i, i, -1))
            else:
                elements.append(Sparse.MatrixElement(i, i, 1))
        return Sparse.SparseMatrix(dimension, elements)
Ejemplo n.º 18
0
    def __NCZ(self, gate_info):
        """
        Adds a z gate controlled by an arbitrary number of bits
    
        :param gate_info: (tuple(int, int, int...,)) Control qubits as ints, arbitrary number
        :return: (SparseMatrix) Matrix representatin of the gate
        """
        bits = np.array(gate_info)
        bits = bits - min(bits)

        elements = [Sparse.MatrixElement(0, 0, 1)]
        dimension = 2**(max(bits) - min(bits) + 1)
        for i in range(1, dimension):
            active = True
            for bit in bits:
                if not self.__bitactive(i, bit):
                    active = False
                    break
            if active: elements.append(Sparse.MatrixElement(i, i, -1))
            else: elements.append(Sparse.MatrixElement(i, i, 1))
        return Sparse.SparseMatrix(dimension, elements)
 def __custom(self, gate_info):
     """
     Creates and applies a custom, user defined matrix to the system
     
     :param gate_info: (tuple(int, int, str)) The ints represent the range of qubits the gate applies to, the string is the name of the gate.
     """
     bits = [
         i for i in range(min(list(gate_info)[:-1]),
                          max(gate_info[:-1]) + 1)
     ]
     gate = Sparse.Gate(self.size, self.customgates[gate_info[-1]], bits)
     self.register.Statevec = gate.apply(self.register.Statevec.Elements)
Ejemplo n.º 20
0
    def __Swap(self, gate_info):
        """
        Creates the matrix representing the swap operation between two qubits.

        :param gate_info: (tuple(int, int)) The two gates to be swapped.
        :return: (SparseMatrix) Matrix representation of the swap gate.
        """
        qbit1, qbit2 = gate_info
        shift = min(qbit1, qbit2)
        qbit1 = qbit1 - shift
        qbit2 = qbit2 - shift
        elements = []
        dimension = 2**(np.abs(qbit2 - qbit1) + 1)
        for i in range(0, dimension):
            col = i
            if (self.__bitactive(i, qbit1) and not self.__bitactive(i, qbit2)
                ) or (not self.__bitactive(i, qbit1)
                      and self.__bitactive(i, qbit2)):
                col = self.__toggle(self.__toggle(i, qbit1), qbit2)
            elements.append(Sparse.MatrixElement(i, col, 1))

        return Sparse.SparseMatrix(dimension, elements)
Ejemplo n.º 21
0
    def makeMatrices(self):
        """
        Creates the matrices that will be applied to the wavevector
    
        :return: (array) list of np matrices that will be applied to the statevector
        """
        gates = np.array(self.gates, dtype=object).T

        bigmats = []
        for i, slot in enumerate(gates):
            bigmat = Sparse.SparseMatrix(1, [Sparse.MatrixElement(0, 0, 1)])
            for j in slot:
                if type(j) == tuple:
                    bigmat = self.__addLargeGate(j).tensorProduct(bigmat)
                elif j == 's':
                    continue
                else:
                    bigmat = Sparse.makeSparse(
                        self.singlegates[j]).tensorProduct(bigmat)
            bigmats.append(bigmat)

        return np.array(bigmats)
    def simulate(self):
        """
        Simulates the quantum circuit by iterating through all of the gates and applyig them one by one.
        Slower for smaller circuit, but better for larger ones, especially with dense matrices.

        :return self.register: (QuantumRegister) The quantum register representing the system
        :return self.measurements: (2xn list of lists) Any measurements taken during the simulation
        """
        self.gates = np.array(self.gates, dtype=object).T
        for i, row in enumerate(self.gates):
            for j, g in enumerate(row):
                if type(g) == tuple:
                    self.__addBigGate(g, j)
                elif g == 's' or g == 'i':
                    continue
                else:
                    gate = Sparse.Gate(self.size,
                                       Sparse.toColMat(self.singlegates[g]),
                                       [j])
                    self.register.Statevec = gate.apply(self.register.Statevec)
            if i in self.measurements[0]:
                self.measurements[1].append(self.register.Statevec.Elements)
        return self.register, self.measurements
def compareProbabilities():
    """
    Compares the two implementations to make sure that they give the same states for Grover's algorithm search.
    """
    lazy_max_measures = []
    numpy_max_measures = []
    # 2-9 qubits
    measured_bits = 1
    for n_qubits in range(2, 8):
        lazy_max_measures.append([])
        grover_circuit = QuantumCircuit('Grover', n_qubits)
        repetitions = int(np.pi / 4 * np.sqrt(2**n_qubits)) - 1
        grover_circuit.addGate('h', [i for i in range(n_qubits)])
        grover_circuit.addmeasure()
        # calculate oracle
        oracle = Sparse.ColMatrix(2**n_qubits)
        for i in range(2**n_qubits):
            if i in [measured_bits]:
                oracle[i, i] = -1
            else:
                oracle[i, i] = 1

        #Add Oracle
        grover_circuit.addCustom(0, n_qubits - 1, oracle, 'oracle')
        #Add diffuser
        Presentation.diffuser(grover_circuit)

        grover_circuit.addmeasure()
        # Repeat if necessary
        for i in range(repetitions):
            # Add Oracle
            grover_circuit.addCustom(0, n_qubits - 1, oracle, 'oracle')
            #Add diffuser
            Presentation.diffuser(grover_circuit)
            grover_circuit.addmeasure()

        #show results
        final_statevec, measurements = grover_circuit.lazysim()
        for m in measurements[1]:
            lazy_max_measures[n_qubits - 2].append(max(m * m.conj()))

        g = Grover()
        iter, success, desired_amps = g.run_circuit(n_qubits, 1, 'testing')
        numpy_max_measures.append(desired_amps)

    print("Checking if the two implementations produce the same results:")
    print(f"\nResult 1 :")
    print(np.array(lazy_max_measures, dtype=object))
    print(f"\nResult 2 :")
    print(np.array(numpy_max_measures, dtype=object))
    def setQbits(self, qbits, vals):
        """
        Sets the initial values of the qubits if required,
        although it is preferred to use gates for this step.
        Automatically normalizes the Qbit

        :param qbits: (list) qubts to be set
        :param vals: (list) The values that the qubits should be set to. Each entry containing two values
        """

        for qbit in qbits:
            self.Qbits[qbit].vals = Sparse.Vector(
                np.array(vals[qbit]) / np.linalg.norm(vals[qbit]))
        self.initialize()
Ejemplo n.º 25
0
    def simulate2(self):
        """
        Applies the circuit to the initialized statevector without storing the operations

        :return: The register and any measurements made
        """
        gates = np.array(self.gates, dtype=object).T

        for i, slot in enumerate(gates):
            bigmat = Sparse.SparseMatrix(1, [Sparse.MatrixElement(0, 0, 1)])
            for j in slot:
                if type(j) == tuple:
                    bigmat = self.__addLargeGate(j).tensorProduct(bigmat)
                elif j == 's':
                    continue
                else:
                    bigmat = Sparse.makeSparse(
                        self.singlegates[j]).tensorProduct(bigmat)
            self.register.Statevec = bigmat.apply(self.register.Statevec)
            if i in self.measurements[0]:
                self.measurements[1].append(self.register.Statevec.Elements)

        return self.register, self.measurements
Ejemplo n.º 26
0
    def preload_source(t_query):
        """load info from example tile (image)

        Arguments
        -----------
        t_query: :class:`TileQuery`
            Only the file path is needed

        Returns
        --------
        dict
            * :class:`RUNTIME` ``.IMAGE.BLOCK.NAME``
                (numpy.ndarray) -- 3x1 for any given tile shape
            * :class:`OUTPUT` ``.INFO.TYPE.NAME``
                (str) -- numpy dtype of any given tile
            * :class:`OUTPUT` ``.INFO.SIZE.NAME``
                (numpy.ndarray) -- 3x1 for full volume shape
            * :class:`OUTPUT` ``.IMAGE.MERGE.NAME``
                (lil_matrix) -- A matrix of merged ids
            * :class:`OUTPUT` ``.IMAGE.SPLIT.NAME``
                (lil_matrix) -- A matrix of plit regions
            * :class:`OUTPUT` ``.IMAGE.ERROR.NAME``
                dict -- All error messages
        """
        # take named keywords
        RUNTIME = t_query.RUNTIME
        k_merge = RUNTIME.IMAGE.MERGE.NAME
        k_split = RUNTIME.IMAGE.SPLIT.NAME
        k_error = RUNTIME.IMAGE.ERROR.NAME
        # Create the edit path
        edit_path = t_query.edit_path
        # Return merges and error message
        return {
            k_merge : Sparse.load_mt(edit_path),
            k_split : Sparse.load_st(edit_path),
            k_error : '',
        }
Ejemplo n.º 27
0
    def __NCP(self, gate_info):
        """
        Adds a phase gate controlled by an arbitrary number of bits
    
        :param gate_info: (tuple(int, int, int..., float)) Control qubits as ints, phase as a float.
        :return: (SparseMatrix) Matrix representation of gate.
        """
        bits = np.array(gate_info[:-1])
        bits = bits - min(bits)
        phi = gate_info[-1]

        elements = [Sparse.MatrixElement(0, 0, 1)]
        dimension = 2**(max(bits) - min(bits) + 1)
        for i in range(1, dimension):
            active = True
            for bit in bits:
                if not self.__bitactive(i, bit):
                    active = False
                    break
            if active:
                elements.append(Sparse.MatrixElement(i, i, np.exp(1j * phi)))
            else:
                elements.append(Sparse.MatrixElement(i, i, 1))
        return Sparse.SparseMatrix(dimension, elements)
    def __initialise_big_gates(self):
        """
        Creates the ColMatrix representation of the larger quantum gates
        can be optimised in the future by only calling it once required
        """
        cnot = Sparse.ColMatrix(4)
        cnot[0, 0] = 1
        cnot[1, 3] = 1
        cnot[3, 1] = 1
        cnot[2, 2] = 1

        cz = Sparse.ColMatrix(4)
        cz[0, 0] = 1
        cz[1, 1] = 1
        cz[2, 2] = 1
        cz[3, 3] = -1

        cy = Sparse.ColMatrix(4)
        cy[0, 0] = 1
        cy[1, 1] = 1
        cy[2, 3] = 1j
        cy[3, 1] = -1j

        ccx = Sparse.ColMatrix(8)
        for i in range(6):
            ccx[i, i] = 1
        ccx[6, 7] = 1
        ccx[7, 6] = 1

        swap = Sparse.ColMatrix(4)
        swap[0, 0] = 1
        swap[2, 1] = 1
        swap[1, 2] = 1
        swap[3, 3] = 1

        self.large_gates = {'cn': cnot, 'cz': cz, 'ccx': ccx, 'swap': swap}
Ejemplo n.º 29
0
  def gemms(self, matrices):
    nameToIndex = dict()
    for i,matrix in enumerate(matrices):
      nameToIndex[matrix.name] = i

    sparsityPatterns = [matrix.spp for matrix in matrices]
    # Eliminate irrelevant entries in the matrix multiplication
    equivalentSparsityPatterns = Sparse.equivalentMultiplicationPatterns(sparsityPatterns)
    # Fit the equivalent sparsity pattern tightly for each memory block
    fittedBlocks = [DB.patternFittedBlocks(matrix.blocks, equivalentSparsityPatterns[i]) for i, matrix in enumerate(matrices)]
    # Find the actual implementation pattern, i.e. dense matrix -> dense pattern
    implementationPatterns = [matrix.getImplementationPattern(fittedBlocks[i], equivalentSparsityPatterns[i]) for i, matrix in enumerate(matrices)]
    # Determine the matrix multiplication order based on the implementation pattern
    chainOrder, dummy = Sparse.sparseMatrixChainOrder(implementationPatterns)
    
    self.nonZeroFlops += Sparse.calculateOptimalSparseFlops(equivalentSparsityPatterns)

    # convert matrix chain order to postfix
    stack = list()
    output = list()
    stack.append((0, len(matrices)-1))
    while len(stack) > 0:
      current = stack[-1]
      i = current[0]
      j = current[1]
      stack.pop()
      if (i == j): # matrix chain is a single matrix
        output.append(matrices[i]) # post the matrix A_i
      else: # subproblem A_i * ... * A_j
        output.append('*') # post a multiplication
        k = chainOrder[current[0], current[1]] # split position A_i..k * A_(k+1)j
        stack.append((current[0], k))   # post A_i..k
        stack.append((k+1, current[1])) # post A_(k+1)j

    # parse postfix
    operands = list()
    tempCounter = len(self.temps)
    while len(output) > 0:
      top = output.pop()
      if top != '*':
        operands.append(top)
      else:
        # In the following we generate instructions for op1 * op2.
        op2 = operands.pop()
        op1 = operands.pop()
        
        blocks1 = fittedBlocks[nameToIndex[op1.name]] if nameToIndex.has_key(op1.name) else op1.blocks
        blocks2 = fittedBlocks[nameToIndex[op2.name]] if nameToIndex.has_key(op2.name) else op2.blocks
        
        # Manage temporary variables
        if len(output) > 0:
          if len(self.temps) == 0:
            tempCounter += 1
            self.temps.append(DB.MatrixInfo(self.tempBaseName + str(tempCounter)))
          result = self.temps.pop()
          resultRequiredReals = result.requiredReals
          spp1 = implementationPatterns[nameToIndex[op1.name]] if nameToIndex.has_key(op1.name) else op1.spp
          spp2 = implementationPatterns[nameToIndex[op2.name]] if nameToIndex.has_key(op2.name) else op2.spp
          result = DB.MatrixInfo(result.name, op1.rows, op2.cols, sparsityPattern = spp1 * spp2)
          result.fitBlocksToSparsityPattern()
          result.generateMemoryLayout(self.arch, alignStartrow=True)
          resultName = result.name
          operands.append(result)
          beta = 0
          result.requiredReals = max(resultRequiredReals, result.requiredReals)
        else:
          beta = 1
          result = self.kernel
          resultName = self.resultName
        
        ops = []
        writes = []
        # op1 and op2 may be partitioned in several blocks.
        # Here we split the blocks of op1 and op2 in sums, i.e.
        # op1 * op2 = (op11 + op12 + ... + op1m) * (op21 + op22 + ... + op2n)
        #           = op11*op21 + op11*op22 + ...
        # opij is a matrix where the j-th block of opi is nonzero.
        # E.g. the first block of op1 (e.g. a 3x3 matrix) is given by (1, 2, 0, 2), then
        #        0 0 0
        # op11 = x x 0
        #        0 0 0
        for i1, block1 in enumerate(blocks1):
          for i2, block2 in enumerate(blocks2):
            # op1k * op2l is only nonzero if the columns of op1k and
            # the rows of op2l intersect.
            self.__gemm(op1.name, block1, op1.blocks[i1], op2.name, block2, op2.blocks[i2], resultName, result.blocks[0], beta, ops, writes)

        # Reorder ops in order to find betas
        if len(writes) > 0 and beta == 0:
          targetCard = result.blocks[0].ld * result.blocks[0].cols()
          mdsIn = MDS.maxDisjointSet(writes, targetCard)
          mdsOut = list( set(range(len(writes))).difference(set(mdsIn)) )
          order = mdsIn + mdsOut          
          memsetInterval = set(range(targetCard))
          for m in mdsIn:
            memsetInterval.difference_update(set( [i + j*result.blocks[0].ld for j in range(writes[m].startcol, writes[m].stopcol) for i in range(writes[m].startrow, writes[m].stoprow)] ))
          memsetInterval = list(memsetInterval)
          ranges = []
          for key, group in itertools.groupby(enumerate(memsetInterval), lambda(index, value): value - index):
            group = map(operator.itemgetter(1), group)
            start = group[0]
            end = group[-1] if len(group) > 1 else start
            self.operations.append(dict(
              type=Operation.MEMSET,
              pointer=resultName,
              offset=start,
              numberOfReals=1+end-start,
              dataType=self.arch.typename
            ))
          for m in mdsOut:
            ops[m]['gemm']['beta'] = 1
          ops = [ops[o] for o in order]

        for op in ops:
          self.gemmlist.append(op['gemm'])
          self.operations.append(op)
          if op['gemm']['spp'] is not None:
            NNZ = int(numpy.sum(op['gemm']['spp']))
            if op['gemm']['LDA'] < 1:
              self.hardwareFlops += 2 * NNZ * op['gemm']['N']
            else:
              self.hardwareFlops += 2 * op['gemm']['M'] * NNZ
          else:
            self.hardwareFlops += 2 * op['gemm']['M'] * op['gemm']['N'] * op['gemm']['K']
            
        # if op is temporary
        if not nameToIndex.has_key(op1.name):
          self.temps.append(op1)
        if not nameToIndex.has_key(op2.name):
          self.temps.append(op2)
Ejemplo n.º 30
0
def Grover_Circuit(n_qubits, measured_bits, plot_results=True):
    """
    Constructs a circuit representing Grover's algorithm for a given number of qubits and
    bits that we are interested in. Plots measurements after each iteration of the algorithm.

    :param n_qubits: (int) Number of qubits in the circuit.
    :param measured_bits: (list) list of bits that we are interested in and want to increase the amplitude of.

    """
    grover_circuit = QuantumCircuit.QuantumCircuit('Grover', n_qubits)
    grover_circuit.addGate('h', [i for i in range(n_qubits)])
    repetitions = int(np.pi/4*np.sqrt(2**n_qubits)) - 1
    
    grover_circuit.addmeasure()
    # calculate oracle
    elements = []
    for i in range(2**n_qubits):
        if i in measured_bits:
            elements.append(Sparse.MatrixElement(i,i,-1))
        else: elements.append(Sparse.MatrixElement(i,i,1))
    oracle_gate = Sparse.SparseMatrix(2**n_qubits, elements) # Creates a sparseMatrix representation of the oracle
    #print(oracle_gate.makedense())
    
    #Add Oracle
    grover_circuit.addCustom(0, n_qubits-1, oracle_gate, 'oracle')
    
    #grover_circuit.addmeasure()
    #Add diffuser
    diffuser(grover_circuit)

    grover_circuit.addmeasure()
    # Repeat if necessary
    for i in range(repetitions):
        # Add Oracle
        grover_circuit.addCustom(0, n_qubits-1, oracle_gate, 'oracle')
        #Add diffuser
        diffuser(grover_circuit)
        grover_circuit.addmeasure()

    #print(np.array(grover_circuit.gates, dtype=object)[:,:6])

    #show results
    #print(grover_circuit.return_measurements())
    final_statevec, measurements = grover_circuit.simulate2()
    #for m in measurements[1]:
    #   print(m)
    
    if plot_results:
        # plots the results in a snazzy way
        figure, axis = plt.subplots(1, len(measurements[1]))
        for j, measurement in enumerate(measurements[1]):
            axis[j].bar([i for i in range(measurement.size)], measurement*np.conj(measurement))
            axis[j].set_ylim([0,1])
            axis[j].set_xlabel("State |N>", fontsize = '13')
            if j>0:
                axis[j].set_yticklabels("")
            #print((results[2][1][j]*np.conj(results[2][1][j])).sum())
        axis[0].set_ylabel("Probability", fontsize = '13')
        
        #figure.set_ylabel("Probability of Measuring State")
        figure.suptitle("Probability of measuring state N",fontweight='bold', fontsize='15')
        plt.show()
        
        print(grover_circuit)
Ejemplo n.º 31
0
    def gemms(self, matrices, firstProduct):
        nameToIndex = dict()
        for i, matrix in enumerate(matrices):
            nameToIndex[matrix.name] = i

        sparsityPatterns = [matrix.spp for matrix in matrices]
        # Eliminate irrelevant entries in the matrix multiplication
        equivalentSparsityPatterns = Sparse.equivalentMultiplicationPatterns(
            sparsityPatterns)
        # Fit the equivalent sparsity pattern tightly for each memory block
        fittedBlocks = [
            DB.patternFittedBlocks(matrix.blocks,
                                   equivalentSparsityPatterns[i])
            for i, matrix in enumerate(matrices)
        ]
        # Find the actual implementation pattern, i.e. dense matrix -> dense pattern
        implementationPatterns = [
            matrix.getImplementationPattern(fittedBlocks[i],
                                            equivalentSparsityPatterns[i])
            for i, matrix in enumerate(matrices)
        ]
        # Determine the matrix multiplication order based on the implementation pattern
        chainOrder, dummy = Sparse.sparseMatrixChainOrder(
            implementationPatterns)

        self.nonZeroFlops += calculateOptimalSparseFlops(matrices)

        # convert matrix chain order to postfix
        stack = list()
        output = list()
        stack.append((0, len(matrices) - 1))
        while len(stack) > 0:
            current = stack[-1]
            i = current[0]
            j = current[1]
            stack.pop()
            if (i == j):  # matrix chain is a single matrix
                output.append(matrices[i])  # post the matrix A_i
            else:  # subproblem A_i * ... * A_j
                output.append('*')  # post a multiplication
                k = chainOrder[current[0],
                               current[1]]  # split position A_i..k * A_(k+1)j
                stack.append((current[0], k))  # post A_i..k
                stack.append((k + 1, current[1]))  # post A_(k+1)j

        # parse postfix
        operands = list()
        tempCounter = len(self.temps)
        while len(output) > 0:
            top = output.pop()
            if top != '*':
                operands.append(top)
            else:
                # In the following we generate instructions for op1 * op2.
                op2 = operands.pop()
                op1 = operands.pop()

                blocks1 = fittedBlocks[nameToIndex[
                    op1.name]] if nameToIndex.has_key(op1.name) else op1.blocks
                blocks2 = fittedBlocks[nameToIndex[
                    op2.name]] if nameToIndex.has_key(op2.name) else op2.blocks

                # Manage temporary variables
                if len(output) > 0:
                    if len(self.temps) == 0:
                        tempCounter += 1
                        self.temps.append(
                            DB.MatrixInfo(self.tempBaseName + str(tempCounter),
                                          1, 1))
                    result = self.temps.pop()
                    resultRequiredReals = result.requiredReals
                    spp1 = implementationPatterns[nameToIndex[
                        op1.name]] if nameToIndex.has_key(
                            op1.name) else op1.spp
                    spp2 = implementationPatterns[nameToIndex[
                        op2.name]] if nameToIndex.has_key(
                            op2.name) else op2.spp
                    result = DB.MatrixInfo(result.name,
                                           op1.rows,
                                           op2.cols,
                                           matrix=spp1 * spp2)
                    result.fitBlocksToSparsityPattern()
                    result.generateMemoryLayout(self.arch, alignStartrow=True)
                    resultName = result.name
                    operands.append(result)
                    beta = 0
                    result.requiredReals = max(resultRequiredReals,
                                               result.requiredReals)
                else:
                    beta = 1 if not firstProduct else self.prototype.beta
                    result = self.prototype.kernel
                    resultName = Kernel.ResultName

                ops = []
                # op1 and op2 may be partitioned in several blocks.
                # Here we split the blocks of op1 and op2 in sums, i.e.
                # op1 * op2 = (op11 + op12 + ... + op1m) * (op21 + op22 + ... + op2n)
                #           = op11*op21 + op11*op22 + ...
                # opij is a matrix where the j-th block of opi is nonzero.
                # E.g. the first block of op1 (e.g. a 3x3 matrix) is given by (1, 2, 0, 2), then
                #        0 0 0
                # op11 = x x 0
                #        0 0 0
                for i1, block1 in enumerate(blocks1):
                    for i2, block2 in enumerate(blocks2):
                        # op1k * op2l is only nonzero if the columns of op1k and
                        # the rows of op2l intersect.
                        self.__gemm(op1.name, block1, op1.blocks[i1], op2.name,
                                    block2, op2.blocks[i2], resultName,
                                    result.blocks[0], beta, ops)

                writes = [op['modifiedBlockC'] for op in ops]
                # Reorder ops in order to find betas
                if len(writes) > 0 and beta == 0:
                    targetCard = result.blocks[0].ld * result.blocks[0].cols()
                    mdsIn = MDS.maxDisjointSet(writes, targetCard)
                    mdsOut = list(
                        set(range(len(writes))).difference(set(mdsIn)))
                    order = mdsIn + mdsOut
                    memsetInterval = set(range(targetCard))
                    for m in mdsIn:
                        memsetInterval.difference_update(
                            set([
                                i + j * result.blocks[0].ld for j in range(
                                    writes[m].startcol, writes[m].stopcol)
                                for i in range(writes[m].startrow,
                                               writes[m].stoprow)
                            ]))
                    memsetInterval = list(memsetInterval)
                    ranges = []
                    for key, group in itertools.groupby(
                            enumerate(memsetInterval), lambda
                        (index, value): value - index):
                        group = map(operator.itemgetter(1), group)
                        start = group[0]
                        end = group[-1] if len(group) > 1 else start
                        self.operations.append(
                            dict(type=Operation.MEMSET,
                                 pointer=resultName,
                                 offset=start,
                                 numberOfReals=1 + end - start,
                                 dataType=self.arch.typename))
                    for m in mdsOut:
                        ops[m]['gemm']['beta'] = 1
                    ops = [ops[o] for o in order]

                for op in ops:
                    self.operations.append(op)
                    if op['gemm']['spp'] is not None:
                        NNZ = int(numpy.sum(op['gemm']['spp']))
                        if op['gemm']['LDA'] < 1:
                            self.hardwareFlops += 2 * NNZ * op['gemm']['N']
                        else:
                            self.hardwareFlops += 2 * op['gemm']['M'] * NNZ
                    else:
                        self.hardwareFlops += 2 * op['gemm']['M'] * op['gemm'][
                            'N'] * op['gemm']['K']

                # if op is temporary
                if not nameToIndex.has_key(op1.name):
                    self.temps.append(op1)
                if not nameToIndex.has_key(op2.name):
                    self.temps.append(op2)