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.") num_qubits = utils.get_num_qubits(utry) basis_gates = ['u1', 'u2', 'u3', 'cx', 'id'] circ = qiskit.QuantumCircuit(num_qubits) circ.iso(utry, list(reversed(range(num_qubits))), []) circ = qiskit.transpile(circ, optimization_level=3, basis_gates=basis_gates) return circ.qasm()
def test_is_unitary1(self): paulis = get_norder_paulis(3) for i in range(10): alpha = np.random.random(4**3) U = sp.linalg.expm(1j * dot_product(alpha, paulis)) self.assertTrue(is_unitary(U, tol=1e-14))
def synthesize(self, utry): """ 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.") solver = qsearch.solvers.LeastSquares_Jac_SolverNative() assembler_style = qsearch.assembler.ASSEMBLY_IBMOPENQASM options = qsearch.options.Options() options.target = utry options.verbosity = 0 compiler = qsearch.leap_compiler.LeapCompiler(solver=solver) output = compiler.compile(options) output = qsearch.assembler.assemble(output["structure"], output["vector"], assembler_style) return output
def __init__(self, utry, location): """ Gate Class Constructor Args: utry (np.ndarray): The gate's unitary operation. location (tuple[int]): The set of qubits the gate acts on. Raises: TypeError: If unitary or location are invalid. """ if not utils.is_unitary(utry, tol=1e-14): raise TypeError("Invalid unitary.") self.utry = utry self.num_qubits = utils.get_num_qubits(self.utry) if not utils.is_valid_location(location): raise TypeError("Invalid location.") if len(location) != self.num_qubits: raise ValueError("Invalid size of location.") self.location = location
def synthesize(self, utry, **kwargs): """ Synthesis function with QISKit's KAK implementation. 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.") if utry.shape[0] == 4: circ = qiskit.QuantumCircuit(2) circ.unitary(utry, [1, 0]) else: circ = qiskit.QuantumCircuit(1) circ.unitary(utry) circ = qiskit.compiler.transpile(circ, basis_gates=['u3', 'cx'], optimization_level=3) return circ.qasm()
def test_closest_unitary_valid_nonunitary1(self): valid_matrix = np.identity(4) + (1e-3 * np.ones((4, 4))) out_matrix = closest_unitary(valid_matrix) self.assertTrue(valid_matrix.shape == out_matrix.shape) self.assertTrue(is_unitary(out_matrix)) self.assertTrue(np.linalg.norm(out_matrix - valid_matrix) <= 4 * 1e-3)
def test_closest_unitary_valid_unitary2(self): valid_matrix = np.identity(4) out_matrix = closest_unitary(valid_matrix) self.assertTrue(valid_matrix.shape == out_matrix.shape) self.assertTrue(np.linalg.norm(out_matrix - valid_matrix) <= 1e-16) self.assertTrue(is_unitary(out_matrix))
def test_closest_unitary_valid_unitary1(self): valid_matrix = np.array([[0, 1], [1, 0]]) out_matrix = closest_unitary(valid_matrix) self.assertTrue(valid_matrix.shape == out_matrix.shape) self.assertTrue(np.linalg.norm(out_matrix - valid_matrix) <= 1e-16) self.assertTrue(is_unitary(out_matrix))
def unitary_log_no_i(U, tol=1e-15): """ Solves for H in U = e^{iH} Args: U (np.ndarray): The unitary to decompose Returns: H (np.ndarray): e^{iH} = U """ if not utils.is_unitary(U, tol): raise TypeError("Input is not unitary.") T, Z = scipy.linalg.schur(U) T = np.diag(T) D = T / np.abs(T) D = np.diag(np.log(D)) H0 = -1j * (Z @ D @ Z.conj().T) return 0.5 * H0 + 0.5 * H0.conj().T
def test_is_unitary_invalid(self): self.assertFalse(is_unitary(1j * np.ones((4, 4)))) self.assertFalse(is_unitary(np.ones((4, 3)))) self.assertFalse(is_unitary(np.ones((4, )))) self.assertFalse(is_unitary(1)) self.assertFalse(is_unitary("a"))
def __init__ ( self, utry, target_gate_size = 2, model = "PermModel", optimizer = "LBFGSOptimizer", hierarchy_fn = lambda x : x // 3 if x > 5 else 2, topology = None, intermediate_solution_callback = None, model_options = {} ): """ Initializes a decomposer. Args: utry (np.ndarray): A unitary matrix to decompose target_gate_size (int): After decomposition, this will be the largest size of any gate in the returned list. model (str): The circuit model to use during decomposition. optimizer (str): The optimizer to use during decomposition. hierarchy_fn (callable): This function determines the decomposition hierarchy. topoology (Topology): Determines the connection of qubits. If none, will be set to all-to-all. intermediate_solution_callback (None or callable): Callback function for intermediate solutions. If not None, then a function that takes in a list[Gates] and returns nothing. Raises: ValueError: If the target_gate_size is nonpositive or too large. RuntimeError: If the model or optimizer cannot be found. """ if not utils.is_unitary( utry, tol = 1e-14 ): logger.warning( "Unitary is not doubly-precise." ) logger.warning( "Proceeding with closest unitary to input." ) self.utry = utils.closest_unitary( utry ) else: self.utry = utry self.num_qubits = utils.get_num_qubits( utry ) if target_gate_size <= 0 or target_gate_size > self.num_qubits: raise ValueError( "Invalid target gate size." ) self.target_gate_size = target_gate_size if not callable( hierarchy_fn ): raise TypeError( "Invalid hierarchy function." ) if intermediate_solution_callback is not None: if not callable( intermediate_solution_callback ): raise TypeError( "Invalid intermediate solution callback." ) self.hierarchy_fn = hierarchy_fn self.intermediate_solution_callback = intermediate_solution_callback if topology is not None and not isinstance( topology, Topology ): raise TypeError( "Invalid topology." ) self.topology = topology or Topology( self.num_qubits ) if model not in plugins.get_models(): raise RuntimeError( f"Cannot find decomposition model: {model}" ) self.model = plugins.get_model( model ) if optimizer not in plugins.get_optimizers(): raise RuntimeError( f"Cannot find optimizer: {optimizer}" ) self.model_options = model_options self.optimizer = plugins.get_optimizer( optimizer ) logger.debug( "Created decomposer with %s and %s." % ( model, optimizer ) )
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