def __init__(self, num_qubits, coupling_graph=None): """ Constructs an all-to-all topology by default. Args: num_qubits (int): The total number of qubits in the topology. coupling_graph (List[Tuple[int]]): List of connected qubit pairs. Raises: TypeError: If coupling_graph is invalid. """ if coupling_graph is None: coupling_graph = [] for l in it.combinations(range(num_qubits), 2): coupling_graph.append(tuple(l)) if not utils.is_valid_coupling_graph(coupling_graph, num_qubits): raise TypeError("Invalid coupling graph.") self.coupling_graph = coupling_graph self.num_qubits = num_qubits self.cache = {} self.adjlist = [[] for i in range(self.num_qubits)] for q0, q1 in coupling_graph: self.adjlist[q0].append(q1) self.adjlist[q1].append(q0)
def test_is_valid_coupling_graph_invalid3 ( self ): self.assertFalse( is_valid_coupling_graph( [ "a" ] ) ) self.assertFalse( is_valid_coupling_graph( [ [0, 1] ] ) ) self.assertFalse( is_valid_coupling_graph( [ [ [ 0 ], 1] ] ) ) self.assertFalse( is_valid_coupling_graph( [ "a" ], 2 ) ) self.assertFalse( is_valid_coupling_graph( [ [0, 1] ], 2 ) ) self.assertFalse( is_valid_coupling_graph( [ [ [ 0 ], 1] ], 2 ) )
def test_is_valid_coupling_graph_empty ( self ): self.assertTrue( is_valid_coupling_graph( [] ) ) self.assertTrue( is_valid_coupling_graph( [], 0 ) ) self.assertTrue( is_valid_coupling_graph( [], 5 ) )
def test_is_valid_coupling_graph_invalid7 ( self ): self.assertFalse( is_valid_coupling_graph( [ (0, 0), (1, 1) ] ) ) self.assertFalse( is_valid_coupling_graph( [ (0, 0), (1, 1) ], 5 ) )
def test_is_valid_coupling_graph_invalid5 ( self ): self.assertFalse( is_valid_coupling_graph( [ (0, 1), (1, 2) ], 1 ) )
def test_is_valid_coupling_graph_invalid4 ( self ): self.assertFalse( is_valid_coupling_graph( [ (0, 1, 2) ] ) ) self.assertFalse( is_valid_coupling_graph( [ (0, 1, 2) ], 4 ) )
def test_is_valid_coupling_graph_invalid2 ( self ): self.assertFalse( is_valid_coupling_graph( "a" ) ) self.assertFalse( is_valid_coupling_graph( "a", 0 ) )
def test_is_valid_coupling_graph_valid ( self ): cgraph = [ (0, 1), (1, 2), (2, 3), (4, 3), (0, 4) ] self.assertTrue( is_valid_coupling_graph( cgraph ) ) self.assertTrue( is_valid_coupling_graph( cgraph, 5 ) )
def synthesize ( utry, model = "PermModel", optimizer = "LBFGSOptimizer", tool = "QSearchTool", combiner = "NaiveCombiner", hierarchy_fn = lambda x : x // 3 if x > 5 else 2, coupling_graph = None, basis_gates = None, intermediate_solution_callback = None, model_options = {} ): """ Synthesize a unitary matrix and return qasm code using QFAST. Args: utry (np.ndarray): The unitary matrix to synthesize. model (str): The model to use during decomposition. optimizer (str): The optimizer to use during decomposition. tool (str): The native tool to use during instantiation. combiner (str): The combiner to use during recombination. hierarchy_fn (callable): This function determines the decomposition hierarchy. coupling_graph (None or list[tuple[int]]): Determines the connection of qubits. If none, will be set to all-to-all. basis_gates (None or list[str]): Determines the gate set for the final circuit. Only works with tools that implement this feature. 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. model_options (Dict): kwargs for model Returns: (str): Qasm code implementing utry. Raises: TypeError: If the coupling_graph is invalid. RuntimeError: If the native tool cannot be found. """ if coupling_graph is not None: if not utils.is_valid_coupling_graph( coupling_graph ): raise TypeError( "The specified coupling graph is invalid." ) if combiner not in plugins.get_combiners(): raise RuntimeError( "Cannot find combiner." ) # Get target_gate_size for decomposition if tool not in plugins.get_native_tools(): raise RuntimeError( "Cannot find native tool." ) target_gate_size = plugins.get_native_tool( tool )().get_maximum_size() num_qubits = utils.get_num_qubits( utry ) topology = Topology( num_qubits, coupling_graph ) # Decompose the big input unitary into smaller unitary gates. decomposer = Decomposer( utry, target_gate_size = target_gate_size, model = model, optimizer = optimizer, topology = topology, hierarchy_fn = hierarchy_fn, intermediate_solution_callback = intermediate_solution_callback, model_options = model_options ) gate_list = decomposer.decompose() # Instantiate the small unitary gates into native code instantiater = Instantiater( tool, topology, basis_gates = basis_gates ) qasm_list = instantiater.instantiate( gate_list ) # Recombine all small circuits into one large output combiner = plugins.get_combiner( combiner )() qasm_out = combiner.combine( qasm_list ) return qasm_out