def test_calc_permutation_matrix_big(self): I = np.identity(2, dtype=np.complex128) II = np.kron(I, I) IIII = np.kron(II, II) X = np.array([[0, 1], [1, 0]], dtype=np.complex128) XX = np.kron(X, X) XXI = np.kron(XX, I) IXX = np.kron(I, XX) IIXX = np.kron(I, IXX) IX = np.kron(I, X) IXIX = np.kron(IX, IX) XXXX = np.kron(XX, XX) IXIXIXIX = np.kron(IXIX, IXIX) U0 = sp.linalg.expm(-1j * IXX) U1 = sp.linalg.expm(-1j * XXI) P = calc_permutation_matrix(3, (1, 2)) self.assertTrue(np.allclose(U0, P @ U1 @ P.T)) U0 = sp.linalg.expm(-1j * IIXX) U1 = sp.linalg.expm(-1j * IXIX) P = calc_permutation_matrix(4, (0, 2)) self.assertTrue(np.allclose(U0, P @ U1 @ P.T)) U0 = sp.linalg.expm(-1j * IXIXIXIX) U1 = sp.linalg.expm(-1j * np.kron(XXXX, IIII)) P = calc_permutation_matrix(8, (1, 3, 5, 7)) self.assertTrue(np.allclose(U0, P @ U1 @ P.T))
def test_toffoli_tensor ( self ): toffoli = np.array( [ [ 1, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 1 ], [ 0, 0, 0, 0, 0, 0, 1, 0 ] ] ) p12 = calc_permutation_matrix( 3, (1, 2) ) p02 = calc_permutation_matrix( 3, (0, 2) ) cnot = np.array( [ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 0, 1 ], [ 0, 0, 1, 0 ] ] ) H = (np.sqrt(2)/2) * np.array( [ [ 1, 1 ], [ 1, -1 ] ] ) T = np.array( [ [ 1, 0 ], [ 0, np.exp( 1j * np.pi/4 ) ] ] ) I = np.identity( 2 ) u1 = np.kron( I, T.conj().T ) @ cnot @ np.kron( I, H ) u2 = np.kron( I, T ) @ cnot u3 = np.kron( I, T.conj().T ) @ cnot u4 = np.kron( I, H @ T ) @ cnot u5 = cnot @ np.kron( T, T.conj().T ) @ cnot @ np.kron( I, T ) circuit = [ Gate( u1, (1, 2) ), Gate( u2, (0, 2) ), Gate( u3, (1, 2) ), Gate( u4, (0, 2) ), Gate( u5, (0, 1) ) ] c1 = p12 @ np.kron( u1, I ) @ p12.T c2 = p02 @ np.kron( u2, I ) @ p02.T c3 = p12 @ np.kron( u3, I ) @ p12.T c4 = p02 @ np.kron( u4, I ) @ p02.T c5 = np.kron( u5, I ) self.assertTrue( np.allclose( toffoli, c5 @ c4 @ c3 @ c2 @ c1 ) ) ct = CircuitTensor( toffoli, [] ) self.assertTrue( np.allclose( ct.utry, toffoli.conj().T ) ) ct.apply_right( circuit[0] ) self.assertTrue( np.allclose( ct.utry, c1 @ toffoli.conj().T ) ) ct.apply_right( circuit[1] ) self.assertTrue( np.allclose( ct.utry, c2 @ c1 @ toffoli.conj().T ) ) ct.apply_right( circuit[2] ) self.assertTrue( np.allclose( ct.utry, c3 @ c2 @ c1 @ toffoli.conj().T ) ) ct.apply_right( circuit[3] ) self.assertTrue( np.allclose( ct.utry, c4 @ c3 @ c2 @ c1 @ toffoli.conj().T ) ) ct.apply_right( circuit[4] ) self.assertTrue( np.allclose( ct.utry, c5 @ c4 @ c3 @ c2 @ c1 @ toffoli.conj().T ) ) self.assertTrue( np.allclose( ct.utry, np.identity( 8 ) ) ) ct = CircuitTensor( toffoli, circuit ) self.assertTrue( np.allclose( ct.utry, np.identity( 8 ) ) )
def test_calc_permutation_matrix(self): swap_012 = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) perm = calc_permutation_matrix(2, (1, 0)) self.assertTrue(np.allclose(perm, swap_012)) perm = calc_permutation_matrix(2, (1, )) self.assertTrue(np.allclose(perm, swap_012)) perm = calc_permutation_matrix(2, (0, 1)) self.assertTrue(np.allclose(perm, np.identity(4)))
def __init__ ( self, num_qubits, gate_size, location ): """ FixedGate Constructor Args: num_qubits (int): The number of qubits in the entire circuit gate_size (int): The number of qubits this gate acts on location (tuple[int]): The qubits this gate acts on """ super().__init__( num_qubits, gate_size ) if not utils.is_valid_location( location, num_qubits ): raise TypeError( "Specified location is invalid." ) if len( location ) != gate_size: raise ValueError( "Location does not match gate size." ) self.location = location self.Hcoef = -1j / ( 2 ** self.num_qubits ) self.paulis = pauli.get_norder_paulis( self.gate_size ) self.sigmav = self.Hcoef * np.array( self.paulis ) self.I = np.identity( 2 ** ( num_qubits - gate_size ) ) self.perm_matrix = perm.calc_permutation_matrix( num_qubits, location )
def __init__ ( self, num_qubits, gate_size, locations ): """ GenericGate Constructor Args: num_qubits (int): The number of qubits in the entire circuit gate_size (int): The number of qubits this gate acts on locations (list[tuple[int]]): The potential locations of this gate """ super().__init__( num_qubits, gate_size ) if not utils.is_valid_locations( locations, num_qubits, gate_size ): raise TypeError( "Specified locations is invalid." ) self.locations = locations self.Hcoef = -1j / ( 2 ** self.num_qubits ) self.paulis = pauli.get_norder_paulis( self.gate_size ) self.sigmav = self.Hcoef * np.array( self.paulis ) self.I = np.identity( 2 ** ( num_qubits - gate_size ) ) self.perms = np.array( [ perm.calc_permutation_matrix( num_qubits, l ) for l in self.locations ] ) self.working_locations = deepcopy( locations ) self.working_perms = np.copy( self.perms )
def get_utry(circ): """Converts a qiskit circuit into a numpy unitary.""" backend = qiskit.BasicAer.get_backend('unitary_simulator') utry = qiskit.execute(circ, backend).result().get_unitary() num_qubits = int(np.log2(len(utry))) qubit_order = tuple(reversed(range(num_qubits))) P = perm.calc_permutation_matrix(num_qubits, qubit_order) return P @ utry @ P.T
def synthesize(self, utry, **kwargs): """ Synthesis function with this tool. Args: utry (np.ndarray): The unitary to synthesize. Returns qasm (str): The synthesized QASM output. Raises: TypeError: If utry is not a valid unitary. ValueError: If the utry has invalid dimensions. """ if not utils.is_unitary(utry, tol=1e-14): raise TypeError("utry must be a valid unitary.") if utry.shape[0] > 2**self.get_maximum_size(): raise ValueError("utry has incorrect dimensions.") # Parse kwargs basis_gates = ["cx"] coupling_graph = [(0, 1), (1, 2)] if "basis_gates" in kwargs: basis_gates = kwargs["basis_gates"] or basis_gates if "coupling_graph" in kwargs: coupling_graph = kwargs["coupling_graph"] or coupling_graph # Prepermute unitary to line up coupling_graph # This is done because qsearch handles pure linear topologies best if utils.get_num_qubits(utry) == 3: a = (0, 1) in coupling_graph b = (1, 2) in coupling_graph c = (0, 2) in coupling_graph if not (a and b): if (a and c): # Permute 0 and 1 P = perm.calc_permutation_matrix(3, (1, 0, 2)) utry = P @ utry @ P.T elif (b and c): # Permute 1 and 2 P = perm.calc_permutation_matrix(3, (0, 2, 1)) utry = P @ utry @ P.T else: raise ValueError("Invalid coupling graph.") # Pass options into qsearch, being maximally quiet, # and set the target to utry opts = options.Options() opts.target = utry opts.gateset = self.map_basis_str_to_gateset(basis_gates) opts.verbosity = 0 opts.write_to_stdout = False opts.reoptimize_size = 7 # use the LEAP compiler, which scales better than normal qsearch compiler = leap_compiler.LeapCompiler() output = compiler.compile(opts) # LEAP requires some post-processing pp = post_processing.LEAPReoptimizing_PostProcessor() output = pp.post_process_circuit(output, opts) output = assemblers.ASSEMBLER_IBMOPENQASM.assemble(output) # Renumber qubits in circuit if we flipped the unitary if utils.get_num_qubits(utry) == 3: a = (0, 1) in coupling_graph b = (1, 2) in coupling_graph c = (0, 2) in coupling_graph if not (a and b): if (a and c): # Permute 0 and 1 str0 = "[0]" str1 = "[1]" elif (b and c): # Permute 1 and 2 str0 = "[1]" str1 = "[2]" output = output.replace(str0, "[tmp]") output = output.replace(str1, str0) output = output.replace("[tmp]", str1) return output