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)
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)
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 __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)
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 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 __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)
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
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 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)