示例#1
0
    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()
示例#2
0
    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
示例#3
0
    def __init__(self,
                 utry,
                 gate_size,
                 locations,
                 optimizer,
                 success_threshold=1e-3,
                 partial_solution_callback=None):
        """
        Default constructor for CircuitModels.

        Args:
            utry (np.ndarray): The unitary to model.

            gate_size (int): The size of the model's gates.

            locations (List[Tuple[int]]): The valid locations for gates.

            optimizer (Optimizer): The optimizer available for use.

            success_threshold (float): The distance criteria for success.

            partial_solution_callback (None or callable): callback for
                partial solutions. If not None, then callable that takes
                a list[gate.Gate] and returns nothing.

        Raises:
            ValueError: If the gate_size or locations are invalid.
        """

        if partial_solution_callback is not None:
            if not callable(partial_solution_callback):
                raise TypeError("Invalid partial_solution_callback.")

        self.num_qubits = utils.get_num_qubits(utry)

        if gate_size >= self.num_qubits:
            raise ValueError("Invalid gate_size")

        self.gate_size = gate_size

        if not utils.is_valid_locations(locations, self.num_qubits,
                                        self.gate_size):
            raise TypeError("Invalid locations")

        self.partial_solution_callback = partial_solution_callback
        self.locations = locations
        self.optimizer = optimizer
        self.utry = utry
        self.utry_dag = utry.conj().T
        self.success_threshold = success_threshold
        self.gates = []
        self.param_ranges = [0]
        self.x = self.get_initial_input()
示例#4
0
def run_tests():
    # Register Signal Handlers
    signal.signal(signal.SIGALRM, term_trial)
    signal.signal(signal.SIGINT, term_trial)

    # Initialize Data Folders
    if not os.path.isdir(".checkpoints"):
        os.makedirs(".checkpoints")

    start_date = str(date.today())
    exp_folder = ".checkpoints/" + start_date + "/"

    if not os.path.isdir(exp_folder):
        os.makedirs(exp_folder)

    data = {}

    hierarchy_fn = lambda x: 3 if x > 6 else 2

    for file in os.listdir():
        if os.path.isfile(file) and file[-8:] == ".unitary":
            name = file[:-8]
            utry = np.loadtxt(file, dtype=np.complex128)
            num_qubits = utils.get_num_qubits(utry)

            # Record QFAST's logger
            stream = StringIO()
            handler = logging.StreamHandler(stream)
            handler.setLevel(logging.DEBUG)
            logger.setLevel(logging.DEBUG)
            logger.addHandler(handler)

            logger.info("-" * 40)
            logger.info(get_exp_header())
            logger.info(start_date)
            logger.info(name)
            logger.info("-" * 40)

            soltree = SolutionTree(utry)

            timeout = False
            timeouts = {
                3: 10 * 60,
                4: 20 * 60,
                5: 45 * 60,
                6: 90 * 60,
                7: 360 * 60
            }
            signal.alarm(timeouts[num_qubits])

            # Set Random Seed
            np.random.seed(21211411)

            # Run Benchmark
            start = timer()

            try:
                qasm = synthesize(
                    utry,
                    model="PermModel",
                    hierarchy_fn=hierarchy_fn,
                    intermediate_solution_callback=soltree.add_intermediate,
                    model_options={
                        "partial_solution_callback": soltree.add_partial,
                        "success_threshold": 1e-3
                    })

            except TrialTerminatedException:
                timeout = True
            except:
                timeout = True
                logger.error(
                    "Benchmark %s encountered error during execution." % name)
                logger.error(traceback.format_exc())
                traceback.print_exc()

            end = timer()

            handler.flush()
            logger.removeHandler(handler)
            handler.close()
            log_out = stream.getvalue()

            if not timeout:
                data[name] = (num_qubits, qasm.count("cx"), end - start,
                              get_error(utry, qasm), qasm, log_out, soltree)
            else:
                data[name] = (num_qubits, log_out, soltree)

            # Save data
            filenum = 0
            filename = exp_folder + name + str(filenum) + ".dat"
            while os.path.isfile(filename):
                filenum += 1
                filename = exp_folder + name + str(filenum) + ".dat"
            file = open(filename, 'wb')
            pickle.dump(data[name], file)

    # Print Summary
    for test in data:
        if len(data[test]) < 6:
            continue
        print("-" * 40)
        print(test)
        print("CX count:", data[test][1])
        print("Time (s):", data[test][2])
        print("Error:", data[test][3])
    print("-" * 40)
示例#5
0
    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 ) )
示例#6
0
 def test_num_qubits(self):
     for i in range(4):
         M = np.identity(2**i)
         self.assertTrue(get_num_qubits(M) == i)
示例#7
0
文件: qs.py 项目: BQSKit/qfast
    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
示例#8
0
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