コード例 #1
0
def transpile_circuit(circuit, transpile_config):
    """Select a PassManager and run a single circuit through it.

    Args:
        circuit (QuantumCircuit): circuit to transpile
        transpile_config (TranspileConfig): configuration dictating how to transpile

    Returns:
        QuantumCircuit: transpiled circuit

    Raises:
        TranspilerError: if transpile_config is not valid or transpilation incurs error
    """
    # if the pass manager is not already selected, choose an appropriate one.
    if transpile_config.pass_manager:
        pass_manager = transpile_config.pass_manager

    elif transpile_config.optimization_level is not None:
        level = transpile_config.optimization_level
        if level == 0:
            pass_manager = level_0_pass_manager(transpile_config)
        elif level == 1:
            pass_manager = level_1_pass_manager(transpile_config)
        elif level == 2:
            pass_manager = level_2_pass_manager(transpile_config)
        elif level == 3:
            pass_manager = level_3_pass_manager(transpile_config)
        else:
            raise TranspilerError("optimization_level can range from 0 to 3.")

    # legacy behavior
    elif transpile_config.coupling_map:
        pass_manager = level_1_pass_manager(transpile_config)
    else:
        pass_manager = default_pass_manager_simulator(transpile_config)

    return pass_manager.run(circuit)
コード例 #2
0
    def run(self, dag):
        try:
            from constraint import Problem, RecursiveBacktrackingSolver, AllDifferentConstraint
        except ImportError:
            raise TranspilerError('CSPLayout requires python-constraint to run. '
                                  'Run pip install python-constraint')
        qubits = dag.qubits()
        cxs = set()

        for gate in dag.twoQ_gates():
            cxs.add((qubits.index(gate.qargs[0]),
                     qubits.index(gate.qargs[1])))
        edges = self.coupling_map.get_edges()

        problem = Problem(RecursiveBacktrackingSolver())

        problem.addVariables(list(range(len(qubits))), self.coupling_map.physical_qubits)

        problem.addConstraint(AllDifferentConstraint())  # each wire is map to a single qbit

        if self.strict_direction:
            def constraint(control, target):
                return (control, target) in edges
        else:
            def constraint(control, target):
                return (control, target) in edges or (target, control) in edges

        for pair in cxs:
            problem.addConstraint(constraint, [pair[0], pair[1]])

        random.seed(self.seed)
        solution = problem.getSolution()

        if solution is None:
            return

        self.property_set['layout'] = Layout({v: qubits[k] for k, v in solution.items()})
コード例 #3
0
    def _replace_node(self, dag, node, instr_map):
        target_params, target_dag = instr_map[node.op.name, node.op.num_qubits]
        if len(node.op.params) != len(target_params):
            raise TranspilerError(
                "Translation num_params not equal to op num_params."
                "Op: {} {} Translation: {}\n{}".format(
                    node.op.params, node.op.name, target_params, target_dag
                )
            )

        if node.op.params:
            # Convert target to circ and back to assign_parameters, since
            # DAGCircuits won't have a ParameterTable.
            target_circuit = dag_to_circuit(target_dag)

            target_circuit.assign_parameters(
                dict(zip_longest(target_params, node.op.params)), inplace=True
            )

            bound_target_dag = circuit_to_dag(target_circuit)
        else:
            bound_target_dag = target_dag

        if len(bound_target_dag.op_nodes()) == 1 and len(
            bound_target_dag.op_nodes()[0].qargs
        ) == len(node.qargs):
            dag_op = bound_target_dag.op_nodes()[0].op
            # dag_op may be the same instance as other ops in the dag,
            # so if there is a condition, need to copy
            if getattr(node.op, "condition", None):
                dag_op = dag_op.copy()
            dag.substitute_node(node, dag_op, inplace=True)

            if bound_target_dag.global_phase:
                dag.global_phase += bound_target_dag.global_phase
        else:
            dag.substitute_node_with_dag(node, bound_target_dag)
コード例 #4
0
    def yzy_to_zyz(xi, theta1, theta2, eps=1e-9):  # pylint: disable=invalid-name
        """Express a Y.Z.Y single qubit gate as a Z.Y.Z gate.

        Solve the equation

        .. math::

        Ry(theta1).Rz(xi).Ry(theta2) = Rz(phi).Ry(theta).Rz(lambda)

        for theta, phi, and lambda.

        Return a solution theta, phi, and lambda.
        """
        quaternion_yzy = Quaternion.from_euler([theta1, xi, theta2], 'yzy')
        euler = quaternion_yzy.to_zyz()
        quaternion_zyz = Quaternion.from_euler(euler, 'zyz')
        # output order different than rotation order
        out_angles = (euler[1], euler[0], euler[2])
        abs_inner = abs(quaternion_zyz.data.dot(quaternion_yzy.data))
        if not np.allclose(abs_inner, 1, eps):
            raise TranspilerError('YZY and ZYZ angles do not give same rotation matrix.')
        out_angles = tuple(0 if np.abs(angle) < _CHOP_THRESHOLD else angle
                           for angle in out_angles)
        return out_angles
コード例 #5
0
    def run(self, dag):
        """
        Main scheduling function
        """
        if not HAS_Z3:
            raise TranspilerError(
                'z3-solver is required to use CrosstalkAdaptiveSchedule. '
                'To install, run "pip install z3-solver".')

        self.dag = dag

        # process input program
        self.qubit_indices = {bit: idx for idx, bit in enumerate(dag.qubits)}
        self.assign_gate_id(self.dag)
        self.extract_dag_overlap_sets(self.dag)
        self.extract_crosstalk_relevant_sets()

        # setup and solve a Z3 optimization
        z3_result = self.solve_optimization()

        # post-process to insert barriers
        new_dag = self.enforce_schedule_on_dag(z3_result)
        self.reset()
        return new_dag
コード例 #6
0
    def run(self, dag):
        """Translate an input DAGCircuit to the target basis.

        Args:
            dag (DAGCircuit): input dag

        Raises:
            TranspilerError: if the target basis cannot be reached

        Returns:
            DAGCircuit: translated circuit.
        """

        if self._target_basis is None:
            return dag

        # Names of instructions assumed to supported by any backend.
        basic_instrs = ['measure', 'reset', 'barrier', 'snapshot']

        target_basis = set(self._target_basis).union(basic_instrs)

        source_basis = set()
        for node in dag.op_nodes():
            name = node.op.name
            qubit_params = (tuple([node.qargs[0].index]), tuple(node.op.params))
            if (dag.calibrations and name in dag.calibrations
                    and qubit_params in dag.calibrations[name]):
                pass
            else:
                source_basis.add((name, node.op.num_qubits))

        logger.info('Begin BasisTranslator from source basis %s to target '
                    'basis %s.', source_basis, target_basis)

        # Search for a path from source to target basis.

        search_start_time = time.time()
        basis_transforms = _basis_search(self._equiv_lib, source_basis,
                                         target_basis, _basis_heuristic)
        search_end_time = time.time()
        logger.info('Basis translation path search completed in %.3fs.',
                    search_end_time - search_start_time)

        if basis_transforms is None:
            raise TranspilerError(
                'Unable to map source basis {} to target basis {} '
                'over library {}.'.format(
                    source_basis, target_basis, self._equiv_lib))

        # Compose found path into a set of instruction substitution rules.

        compose_start_time = time.time()
        instr_map = _compose_transforms(basis_transforms, source_basis, dag)

        compose_end_time = time.time()
        logger.info('Basis translation paths composed in %.3fs.',
                    compose_end_time - compose_start_time)

        # Replace source instructions with target translations.

        replace_start_time = time.time()
        for node in dag.op_nodes():
            if node.name in target_basis:
                continue

            if dag.calibrations and node.name in dag.calibrations:
                qubit = tuple([node.qargs[0].index])
                params = tuple(node.op.params)
                if (qubit, params) in dag.calibrations[node.name]:
                    continue

            if (node.op.name, node.op.num_qubits) in instr_map:
                target_params, target_dag = instr_map[node.op.name, node.op.num_qubits]

                if len(node.op.params) != len(target_params):
                    raise TranspilerError(
                        'Translation num_params not equal to op num_params.'
                        'Op: {} {} Translation: {}\n{}'.format(
                            node.op.params, node.op.name,
                            target_params, target_dag))

                if node.op.params:
                    # Convert target to circ and back to assign_parameters, since
                    # DAGCircuits won't have a ParameterTable.
                    from qiskit.converters import dag_to_circuit, circuit_to_dag
                    target_circuit = dag_to_circuit(target_dag)

                    target_circuit.assign_parameters(
                        dict(zip_longest(target_params, node.op.params)),
                        inplace=True)

                    bound_target_dag = circuit_to_dag(target_circuit)
                else:
                    bound_target_dag = target_dag

                if (len(bound_target_dag.op_nodes()) == 1
                        and len(bound_target_dag.op_nodes()[0].qargs) == len(node.qargs)):
                    dag.substitute_node(node, bound_target_dag.op_nodes()[0].op, inplace=True)
                else:
                    dag.substitute_node_with_dag(node, bound_target_dag)
            else:
                raise TranspilerError('BasisTranslator did not map {}.'.format(node.name))

        replace_end_time = time.time()
        logger.info('Basis translation instructions replaced in %.3fs.',
                    replace_end_time - replace_start_time)

        return dag
コード例 #7
0
    def run(self, dag):
        """Run the BIPMapping pass on `dag`, assuming the number of virtual qubits (defined in
        `dag`) and the number of physical qubits (defined in `coupling_map`) are the same.

        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG. If there is no 2q-gate in DAG or it fails to map,
                returns the original dag.

        Raises:
            TranspilerError: if the number of virtual and physical qubits are not the same.
            AssertionError: if the final layout is not valid.
        """
        if self.coupling_map is None:
            return dag

        if len(dag.qubits) > len(self.qubit_subset):
            raise TranspilerError("More virtual qubits exist than physical qubits.")

        if len(dag.qubits) != len(self.qubit_subset):
            raise TranspilerError(
                "BIPMapping requires the number of virtual and physical qubits to be the same. "
                "Supply 'qubit_subset' to specify physical qubits to use."
            )

        original_dag = dag

        dummy_steps = math.ceil(math.sqrt(dag.num_qubits()))
        if self.max_swaps_inbetween_layers is not None:
            dummy_steps = max(0, self.max_swaps_inbetween_layers - 1)

        model = BIPMappingModel(
            dag=dag,
            coupling_map=self.coupling_map,
            qubit_subset=self.qubit_subset,
            dummy_timesteps=dummy_steps,
        )

        if len(model.su4layers) == 0:
            logger.info("BIPMapping is skipped due to no 2q-gates.")
            return original_dag

        model.create_cpx_problem(
            objective=self.objective,
            backend_prop=self.backend_prop,
            depth_obj_weight=self.depth_obj_weight,
            default_cx_error_rate=self.default_cx_error_rate,
        )

        status = model.solve_cpx_problem(time_limit=self.time_limit, threads=self.threads)
        if model.solution is None:
            logger.warning("Failed to solve a BIP problem. Status: %s", status)
            return original_dag

        # Get the optimized initial layout
        optimized_layout = model.get_layout(0)

        # Create a layout to track changes in layout for each layer
        layout = copy.deepcopy(optimized_layout)

        # Construct the mapped circuit
        canonical_qreg = QuantumRegister(self.coupling_map.size(), "q")
        mapped_dag = self._create_empty_dagcircuit(dag, canonical_qreg)
        interval = dummy_steps + 1
        for k, layer in enumerate(dag.layers()):
            if model.is_su4layer(k):
                su4dep = model.to_su4layer_depth(k)
                # add swaps between (su4dep-1)-th and su4dep-th su4layer
                from_steps = max(interval * (su4dep - 1), 0)
                to_steps = min(interval * su4dep, model.depth - 1)
                for t in range(from_steps, to_steps):  # pylint: disable=invalid-name
                    for (i, j) in model.get_swaps(t):
                        mapped_dag.apply_operation_back(
                            op=SwapGate(),
                            qargs=[canonical_qreg[i], canonical_qreg[j]],
                        )
                        # update layout, swapping physical qubits (i, j)
                        layout.swap(i, j)

            # map gates in k-th layer
            for node in layer["graph"].nodes():
                if isinstance(node, DAGOpNode):
                    mapped_dag.apply_operation_back(
                        op=copy.deepcopy(node.op),
                        qargs=[canonical_qreg[layout[q]] for q in node.qargs],
                        cargs=node.cargs,
                    )
                # TODO: double check with y values?

        # Check final layout
        final_layout = model.get_layout(model.depth - 1)
        if layout != final_layout:
            raise AssertionError(
                f"Bug: final layout {final_layout} != the layout computed from swaps {layout}"
            )

        self.property_set["layout"] = self._to_full_layout(optimized_layout)
        self.property_set["final_layout"] = self._to_full_layout(final_layout)

        return mapped_dag
コード例 #8
0
    def _mapper(self, circuit_graph, coupling_graph, trials=20):
        """Map a DAGCircuit onto a CouplingMap using swap gates.

        Use self.trivial_layout for the initial layout.

        Args:
            circuit_graph (DAGCircuit): input DAG circuit
            coupling_graph (CouplingMap): coupling graph to map onto
            trials (int): number of trials.

        Returns:
            DAGCircuit: object containing a circuit equivalent to
                circuit_graph that respects couplings in coupling_graph

        Raises:
            TranspilerError: if there was any error during the mapping
                or with the parameters.
        """
        # Schedule the input circuit by calling layers()
        layerlist = list(circuit_graph.layers())
        logger.debug("schedule:")
        for i, v in enumerate(layerlist):
            logger.debug("    %d: %s", i, v["partition"])

        qubit_subset = self.trivial_layout.get_virtual_bits().keys()

        # Find swap circuit to precede each layer of input circuit
        layout = self.trivial_layout.copy()

        # Construct an empty DAGCircuit with the same set of
        # qregs and cregs as the input circuit
        dagcircuit_output = circuit_graph._copy_circuit_metadata()

        logger.debug("trivial_layout = %s", layout)

        # Iterate over layers
        for i, layer in enumerate(layerlist):

            # Attempt to find a permutation for this layer
            success_flag, best_circuit, best_depth, best_layout \
                = self._layer_permutation(layer["partition"], layout,
                                          qubit_subset, coupling_graph,
                                          trials)
            logger.debug("mapper: layer %d", i)
            logger.debug("mapper: success_flag=%s,best_depth=%s", success_flag,
                         str(best_depth))

            # If this fails, try one gate at a time in this layer
            if not success_flag:
                logger.debug(
                    "mapper: failed, layer %d, "
                    "retrying sequentially", i)
                serial_layerlist = list(layer["graph"].serial_layers())

                # Go through each gate in the layer
                for j, serial_layer in enumerate(serial_layerlist):

                    success_flag, best_circuit, best_depth, best_layout = \
                        self._layer_permutation(
                            serial_layer["partition"],
                            layout, qubit_subset,
                            coupling_graph,
                            trials)
                    logger.debug("mapper: layer %d, sublayer %d", i, j)
                    logger.debug("mapper: success_flag=%s,best_depth=%s,",
                                 success_flag, str(best_depth))

                    # Give up if we fail again
                    if not success_flag:
                        raise TranspilerError("swap mapper failed: " +
                                              "layer %d, sublayer %d" % (i, j))

                    # Update the record of qubit positions
                    # for each inner iteration
                    layout = best_layout
                    # Update the DAG
                    dagcircuit_output.compose(
                        self._layer_update(j, best_layout, best_depth,
                                           best_circuit, serial_layerlist))

            else:
                # Update the record of qubit positions for each iteration
                layout = best_layout

                # Update the DAG
                dagcircuit_output.compose(
                    self._layer_update(i, best_layout, best_depth,
                                       best_circuit, layerlist))

        # This is the final edgemap. We might use it to correctly replace
        # any measurements that needed to be removed earlier.
        logger.debug("mapper: self.trivial_layout = %s", self.trivial_layout)
        logger.debug("mapper: layout = %s", layout)

        return dagcircuit_output
コード例 #9
0
def _layer_permutation(layer_partition, initial_layout, layout, qubit_subset,
                       coupling, trials, qregs, rng):
    """Find a swap circuit that implements a permutation for this layer.

    Args:
        layer_partition (list): The layer_partition is a list of (qu)bit
            lists and each qubit is a tuple (qreg, index).
        initial_layout (Layout): The initial layout passed.
        layout (Layout): The layout is a Layout object mapping virtual
            qubits in the input circuit to physical qubits in the coupling
            graph. It reflects the current positions of the data.
        qubit_subset (list): The qubit_subset is the set of qubits in
            the coupling graph that we have chosen to map into, as tuples
            (Register, index).
        coupling (CouplingMap): Directed graph representing a coupling map.
            This coupling map should be one that was provided to the
            stochastic mapper.
        trials (int): Number of attempts the randomized algorithm makes.
        qregs (OrderedDict): Ordered dict of registers from input DAG.
        rng (RandomState): Random number generator.

    Returns:
        Tuple: success_flag, best_circuit, best_depth, best_layout, trivial_flag

    Raises:
        TranspilerError: if anything went wrong.
     """
    logger.debug("layer_permutation: layer_partition = %s",
                 pformat(layer_partition))
    logger.debug("layer_permutation: layout = %s",
                 pformat(layout.get_virtual_bits()))
    logger.debug("layer_permutation: qubit_subset = %s", pformat(qubit_subset))
    logger.debug("layer_permutation: trials = %s", trials)

    gates = []  # list of lists of tuples [[(register, index), ...], ...]
    for gate_args in layer_partition:
        if len(gate_args) > 2:
            raise TranspilerError("Layer contains > 2-qubit gates")
        elif len(gate_args) == 2:
            gates.append(tuple(gate_args))
    logger.debug("layer_permutation: gates = %s", pformat(gates))

    # Can we already apply the gates? If so, there is no work to do.
    dist = sum([coupling.distance(layout[g[0]], layout[g[1]]) for g in gates])
    logger.debug("layer_permutation: distance = %s", dist)
    if dist == len(gates):
        logger.debug("layer_permutation: nothing to do")
        circ = DAGCircuit()
        for register in layout.get_virtual_bits().keys():
            if register[0] not in circ.qregs.values():
                circ.add_qreg(register[0])
        return True, circ, 0, layout, (not bool(gates))

    # Begin loop over trials of randomized algorithm
    num_qubits = len(layout)
    best_depth = inf  # initialize best depth
    best_edges = None  # best edges found
    best_circuit = None  # initialize best swap circuit
    best_layout = None  # initialize best final layout

    cdist2 = coupling._dist_matrix**2
    # Scaling matrix
    scale = np.zeros((num_qubits, num_qubits))

    int_qubit_subset = regtuple_to_numeric(qubit_subset, qregs)
    int_gates = gates_to_idx(gates, qregs)
    int_layout = nlayout_from_layout(layout, qregs, coupling.size())

    trial_circuit = DAGCircuit()  # SWAP circuit for this trial
    for register in layout.get_virtual_bits().keys():
        if register[0] not in trial_circuit.qregs.values():
            trial_circuit.add_qreg(register[0])

    slice_circuit = DAGCircuit()  # circuit for this swap slice
    for register in layout.get_virtual_bits().keys():
        if register[0] not in slice_circuit.qregs.values():
            slice_circuit.add_qreg(register[0])
    edges = np.asarray(coupling.get_edges(), dtype=np.int32).ravel()
    cdist = coupling._dist_matrix
    for trial in range(trials):
        logger.debug("layer_permutation: trial %s", trial)
        # This is one Trial --------------------------------------
        dist, optim_edges, trial_layout, depth_step = swap_trial(
            num_qubits, int_layout, int_qubit_subset, int_gates, cdist2, cdist,
            edges, scale, rng)

        logger.debug("layer_permutation: final distance for this trial = %s",
                     dist)
        if dist == len(gates) and depth_step < best_depth:
            logger.debug(
                "layer_permutation: got circuit with improved depth %s",
                depth_step)
            best_edges = optim_edges
            best_layout = trial_layout
            best_depth = min(best_depth, depth_step)

        # Break out of trial loop if we found a depth 1 circuit
        # since we can't improve it further
        if best_depth == 1:
            break

    # If we have no best circuit for this layer, all of the
    # trials have failed
    if best_layout is None:
        logger.debug("layer_permutation: failed!")
        return False, None, None, None, False

    edgs = best_edges.edges()
    for idx in range(best_edges.size // 2):
        slice_circuit.apply_operation_back(
            SwapGate(),
            [initial_layout[edgs[2 * idx]], initial_layout[edgs[2 * idx + 1]]],
            [])
    trial_circuit.extend_back(slice_circuit)
    best_circuit = trial_circuit

    # Otherwise, we return our result for this layer
    logger.debug("layer_permutation: success!")
    best_lay = best_layout.to_layout(qregs)
    return True, best_circuit, best_depth, best_lay, False
コード例 #10
0
    def _pad(
        self,
        dag: DAGCircuit,
        qubit: Qubit,
        t_start: int,
        t_end: int,
        next_node: DAGNode,
        prev_node: DAGNode,
    ):
        # This routine takes care of the pulse alignment constraint for the DD sequence.
        # Note that the alignment constraint acts on the t0 of the DAGOpNode.
        # Now this constrained scheduling problem is simplified to the problem of
        # finding a delay amount which is a multiple of the constraint value by assuming
        # that the duration of every DAGOpNode is also a multiple of the constraint value.
        #
        # For example, given the constraint value of 16 and XY4 with 160 dt gates.
        # Here we assume current interval is 992 dt.
        #
        # relative spacing := [0.125, 0.25, 0.25, 0.25, 0.125]
        # slack = 992 dt - 4 x 160 dt = 352 dt
        #
        # unconstraind sequence: 44dt-X1-88dt-Y2-88dt-X3-88dt-Y4-44dt
        # constraind sequence  : 32dt-X1-80dt-Y2-80dt-X3-80dt-Y4-32dt + extra slack 48 dt
        #
        # Now we evenly split extra slack into start and end of the sequence.
        # The distributed slack should be multiple of 16.
        # Start = +16, End += 32
        #
        # final sequence       : 48dt-X1-80dt-Y2-80dt-X3-80dt-Y4-64dt / in total 992 dt
        #
        # Now we verify t0 of every node starts from multiple of 16 dt.
        #
        # X1:  48 dt (3 x 16 dt)
        # Y2:  48 dt + 160 dt + 80 dt = 288 dt (18 x 16 dt)
        # Y3: 288 dt + 160 dt + 80 dt = 528 dt (33 x 16 dt)
        # Y4: 368 dt + 160 dt + 80 dt = 768 dt (48 x 16 dt)
        #
        # As you can see, constraints on t0 are all satified without explicit scheduling.
        time_interval = t_end - t_start

        if self._qubits and dag.qubits.index(qubit) not in self._qubits:
            # Target physical qubit is not the target of this DD sequence.
            self._apply_scheduled_op(dag, t_start,
                                     Delay(time_interval, dag.unit), qubit)
            return

        if self._skip_reset_qubits and (isinstance(prev_node, DAGInNode)
                                        or isinstance(prev_node.op, Reset)):
            # Previous node is the start edge or reset, i.e. qubit is ground state.
            self._apply_scheduled_op(dag, t_start,
                                     Delay(time_interval, dag.unit), qubit)
            return

        slack = time_interval - np.sum(self._dd_sequence_lengths[qubit])
        sequence_gphase = self._sequence_phase

        if slack <= 0:
            # Interval too short.
            self._apply_scheduled_op(dag, t_start,
                                     Delay(time_interval, dag.unit), qubit)
            return

        if len(self._dd_sequence) == 1:
            # Special case of using a single gate for DD
            u_inv = self._dd_sequence[0].inverse().to_matrix()
            theta, phi, lam, phase = OneQubitEulerDecomposer(
            ).angles_and_phase(u_inv)
            if isinstance(next_node, DAGOpNode) and isinstance(
                    next_node.op, (UGate, U3Gate)):
                # Absorb the inverse into the successor (from left in circuit)
                theta_r, phi_r, lam_r = next_node.op.params
                next_node.op.params = Optimize1qGates.compose_u3(
                    theta_r, phi_r, lam_r, theta, phi, lam)
                sequence_gphase += phase
            elif isinstance(prev_node, DAGOpNode) and isinstance(
                    prev_node.op, (UGate, U3Gate)):
                # Absorb the inverse into the predecessor (from right in circuit)
                theta_l, phi_l, lam_l = prev_node.op.params
                prev_node.op.params = Optimize1qGates.compose_u3(
                    theta, phi, lam, theta_l, phi_l, lam_l)
                sequence_gphase += phase
            else:
                # Don't do anything if there's no single-qubit gate to absorb the inverse
                self._apply_scheduled_op(dag, t_start,
                                         Delay(time_interval, dag.unit), qubit)
                return

        def _constrained_length(values):
            return self._alignment * np.floor(values / self._alignment)

        # (1) Compute DD intervals satisfying the constraint
        taus = _constrained_length(slack * np.asarray(self._spacing))
        extra_slack = slack - np.sum(taus)

        # (2) Distribute extra slack
        if self._extra_slack_distribution == "middle":
            mid_ind = int((len(taus) - 1) / 2)
            to_middle = _constrained_length(extra_slack)
            taus[mid_ind] += to_middle
            if extra_slack - to_middle:
                # If to_middle is not a multiple value of the pulse alignment,
                # it is truncated to the nearlest multiple value and
                # the rest of slack is added to the end.
                taus[-1] += extra_slack - to_middle
        elif self._extra_slack_distribution == "edges":
            to_begin_edge = _constrained_length(extra_slack / 2)
            taus[0] += to_begin_edge
            taus[-1] += extra_slack - to_begin_edge
        else:
            raise TranspilerError(
                f"Option extra_slack_distribution = {self._extra_slack_distribution} is invalid."
            )

        # (3) Construct DD sequence with delays
        num_elements = max(len(self._dd_sequence), len(taus))
        idle_after = t_start
        for dd_ind in range(num_elements):
            if dd_ind < len(taus):
                tau = taus[dd_ind]
                if tau > 0:
                    self._apply_scheduled_op(dag, idle_after,
                                             Delay(tau, dag.unit), qubit)
                    idle_after += tau
            if dd_ind < len(self._dd_sequence):
                gate = self._dd_sequence[dd_ind]
                gate_length = self._dd_sequence_lengths[qubit][dd_ind]
                self._apply_scheduled_op(dag, idle_after, gate, qubit)
                idle_after += gate_length

        dag.global_phase = self._mod_2pi(dag.global_phase + sequence_gphase)
コード例 #11
0
def _parse_transpile_args(circuits, backend,
                          basis_gates, coupling_map, backend_properties,
                          initial_layout, layout_method, routing_method, translation_method,
                          scheduling_method, instruction_durations, dt,
                          seed_transpiler, optimization_level,
                          callback, output_name) -> List[Dict]:
    """Resolve the various types of args allowed to the transpile() function through
    duck typing, overriding args, etc. Refer to the transpile() docstring for details on
    what types of inputs are allowed.

    Here the args are resolved by converting them to standard instances, and prioritizing
    them in case a transpile option is passed through multiple args (explicitly setting an
    arg has more priority than the arg set by backend).

    Returns:
        list[dicts]: a list of transpile parameters.

    Raises:
        TranspilerError: If instruction_durations are required but not supplied or found.
    """
    if initial_layout is not None and layout_method is not None:
        warnings.warn("initial_layout provided; layout_method is ignored.",
                      UserWarning)
    # Each arg could be single or a list. If list, it must be the same size as
    # number of circuits. If single, duplicate to create a list of that size.
    num_circuits = len(circuits)

    basis_gates = _parse_basis_gates(basis_gates, backend, circuits)
    faulty_qubits_map = _parse_faulty_qubits_map(backend, num_circuits)
    coupling_map = _parse_coupling_map(coupling_map, backend, num_circuits)
    backend_properties = _parse_backend_properties(backend_properties, backend, num_circuits)
    backend_num_qubits = _parse_backend_num_qubits(backend, num_circuits)
    initial_layout = _parse_initial_layout(initial_layout, circuits)
    layout_method = _parse_layout_method(layout_method, num_circuits)
    routing_method = _parse_routing_method(routing_method, num_circuits)
    translation_method = _parse_translation_method(translation_method, num_circuits)
    seed_transpiler = _parse_seed_transpiler(seed_transpiler, num_circuits)
    optimization_level = _parse_optimization_level(optimization_level, num_circuits)
    output_name = _parse_output_name(output_name, circuits)
    callback = _parse_callback(callback, num_circuits)

    durations = _parse_instruction_durations(backend, instruction_durations, dt, circuits)
    scheduling_method = _parse_scheduling_method(scheduling_method, circuits)
    if scheduling_method and not durations:
        raise TranspilerError("Transpiling a circuit with a scheduling method or with delay "
                              "instructions requires a backend or instruction_durations.")

    list_transpile_args = []
    for args in zip(basis_gates, coupling_map, backend_properties, initial_layout,
                    layout_method, routing_method, translation_method, scheduling_method,
                    durations, seed_transpiler, optimization_level,
                    output_name, callback, backend_num_qubits, faulty_qubits_map):
        transpile_args = {'pass_manager_config': PassManagerConfig(basis_gates=args[0],
                                                                   coupling_map=args[1],
                                                                   backend_properties=args[2],
                                                                   initial_layout=args[3],
                                                                   layout_method=args[4],
                                                                   routing_method=args[5],
                                                                   translation_method=args[6],
                                                                   scheduling_method=args[7],
                                                                   instruction_durations=args[8],
                                                                   seed_transpiler=args[9]),
                          'optimization_level': args[10],
                          'output_name': args[11],
                          'callback': args[12],
                          'backend_num_qubits': args[13],
                          'faulty_qubits_map': args[14]}
        list_transpile_args.append(transpile_args)

    return list_transpile_args
コード例 #12
0
ファイル: alap.py プロジェクト: yaelbh/qiskit-terra
    def run(self, dag):
        """Run the ALAPSchedule pass on `dag`.

        Args:
            dag (DAGCircuit): DAG to schedule.

        Returns:
            DAGCircuit: A scheduled DAG.

        Raises:
            TranspilerError: if the circuit is not mapped on physical qubits.
        """
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError(
                "ALAP schedule runs on physical circuits only")

        time_unit = self.property_set["time_unit"]
        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        qubit_time_available = defaultdict(int)

        def pad_with_delays(qubits: List[int], until, unit) -> None:
            """Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``."""
            for q in qubits:
                if qubit_time_available[q] < until:
                    idle_duration = until - qubit_time_available[q]
                    new_dag.apply_operation_front(Delay(idle_duration, unit),
                                                  [q], [])

        bit_indices = {bit: index for index, bit in enumerate(dag.qubits)}
        for node in reversed(list(dag.topological_op_nodes())):
            start_time = max(qubit_time_available[q] for q in node.qargs)
            pad_with_delays(node.qargs, until=start_time, unit=time_unit)

            new_dag.apply_operation_front(node.op, node.qargs, node.cargs)

            # validate node.op.duration
            if node.op.duration is None:
                indices = [bit_indices[qarg] for qarg in node.qargs]
                raise TranspilerError(f"Duration of {node.op.name} on qubits "
                                      f"{indices} is not found.")
            if isinstance(node.op.duration, ParameterExpression):
                indices = [bit_indices[qarg] for qarg in node.qargs]
                raise TranspilerError(
                    f"Parameterized duration ({node.op.duration}) "
                    f"of {node.op.name} on qubits {indices} is not bounded.")

            stop_time = start_time + node.op.duration
            # update time table
            for q in node.qargs:
                qubit_time_available[q] = stop_time

        working_qubits = qubit_time_available.keys()
        circuit_duration = max(qubit_time_available[q] for q in working_qubits)
        pad_with_delays(new_dag.qubits, until=circuit_duration, unit=time_unit)

        new_dag.name = dag.name
        new_dag.metadata = dag.metadata
        # set circuit duration and unit to indicate it is scheduled
        new_dag.duration = circuit_duration
        new_dag.unit = time_unit
        return new_dag
コード例 #13
0
ファイル: asap.py プロジェクト: Zoufalc/qiskit-terra
    def run(self, dag):
        """Run the ASAPSchedule pass on `dag`.

        Args:
            dag (DAGCircuit): DAG to schedule.

        Returns:
            DAGCircuit: A scheduled DAG.

        Raises:
            TranspilerError: if the circuit is not mapped on physical qubits.
        """
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError(
                "ASAP schedule runs on physical circuits only")

        time_unit = self.property_set["time_unit"]

        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        qubit_time_available = defaultdict(int)
        clbit_readable = defaultdict(int)
        clbit_writeable = defaultdict(int)

        def pad_with_delays(qubits: List[int], until, unit) -> None:
            """Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``."""
            for q in qubits:
                if qubit_time_available[q] < until:
                    idle_duration = until - qubit_time_available[q]
                    new_dag.apply_operation_back(Delay(idle_duration, unit),
                                                 [q])

        bit_indices = {q: index for index, q in enumerate(dag.qubits)}
        for node in dag.topological_op_nodes():
            # validate node.op.duration
            if node.op.duration is None:
                indices = [bit_indices[qarg] for qarg in node.qargs]
                if dag.has_calibration_for(node):
                    node.op.duration = dag.calibrations[node.op.name][(
                        tuple(indices),
                        tuple(float(p) for p in node.op.params))].duration

                if node.op.duration is None:
                    raise TranspilerError(
                        f"Duration of {node.op.name} on qubits {indices} is not found."
                    )
            if isinstance(node.op.duration, ParameterExpression):
                indices = [bit_indices[qarg] for qarg in node.qargs]
                raise TranspilerError(
                    f"Parameterized duration ({node.op.duration}) "
                    f"of {node.op.name} on qubits {indices} is not bounded.")
            # choose appropriate clbit available time depending on op
            clbit_time_available = (clbit_writeable if isinstance(
                node.op, Measure) else clbit_readable)
            # correction to change clbit start time to qubit start time
            delta = node.op.duration if isinstance(node.op, Measure) else 0
            # must wait for op.condition_bits as well as node.cargs
            start_time = max(
                itertools.chain(
                    (qubit_time_available[q] for q in node.qargs),
                    (clbit_time_available[c] - delta
                     for c in node.cargs + node.op.condition_bits),
                ))

            pad_with_delays(node.qargs, until=start_time, unit=time_unit)

            new_dag.apply_operation_back(node.op, node.qargs, node.cargs)

            stop_time = start_time + node.op.duration
            # update time table
            for q in node.qargs:
                qubit_time_available[q] = stop_time
            for c in node.cargs:  # measure
                clbit_writeable[c] = clbit_readable[c] = stop_time
            for c in node.op.condition_bits:  # conditional op
                clbit_writeable[c] = max(start_time, clbit_writeable[c])

        working_qubits = qubit_time_available.keys()
        circuit_duration = max(qubit_time_available[q] for q in working_qubits)
        pad_with_delays(new_dag.qubits, until=circuit_duration, unit=time_unit)

        new_dag.name = dag.name
        new_dag.metadata = dag.metadata
        # set circuit duration and unit to indicate it is scheduled
        new_dag.duration = circuit_duration
        new_dag.unit = time_unit
        return new_dag
コード例 #14
0
    def run(self, dag):
        """Run the DenseLayout pass on `dag`.

        Pick a convenient layout depending on the best matching
        qubit connectivity, and set the property `layout`.

        Args:
            dag (DAGCircuit): DAG to find layout for.

        Raises:
            TranspilerError: if dag wider than self.coupling_map
        """
        num_dag_qubits = sum([qreg.size for qreg in dag.qregs.values()])
        if num_dag_qubits > self.coupling_map.size():
            raise TranspilerError("Number of qubits greater than device.")

        # Get avg number of cx and meas per qubit
        ops = dag.count_ops()
        if "cx" in ops.keys():
            self.num_cx = ops["cx"]
        if "measure" in ops.keys():
            self.num_meas = ops["measure"]

        # Compute the sparse cx_err matrix and meas array
        device_qubits = self.coupling_map.size()
        if self.backend_prop:
            rows = []
            cols = []
            cx_err = []

            for edge in self.coupling_map.get_edges():
                for gate in self.backend_prop.gates:
                    if gate.qubits == edge:
                        rows.append(edge[0])
                        cols.append(edge[1])
                        cx_err.append(gate.parameters[0].value)
                        break
                else:
                    continue

            self.cx_mat = sp.coo_matrix(
                (cx_err, (rows, cols)), shape=(device_qubits, device_qubits)
            ).tocsr()

            # Set measurement array
            meas_err = []
            for qubit_data in self.backend_prop.qubits:
                for item in qubit_data:
                    if item.name == "readout_error":
                        meas_err.append(item.value)
                        break
                else:
                    continue
            self.meas_arr = np.asarray(meas_err)

        best_sub = self._best_subset(num_dag_qubits)
        layout = Layout()
        map_iter = 0
        for qreg in dag.qregs.values():
            for i in range(qreg.size):
                layout[qreg[i]] = int(best_sub[map_iter])
                map_iter += 1
            layout.add_register(qreg)
        self.property_set["layout"] = layout
コード例 #15
0
ファイル: asap.py プロジェクト: merav-aharoni/qiskit-terra
    def run(self, dag):
        """Run the ASAPSchedule pass on `dag`.

        Args:
            dag (DAGCircuit): DAG to schedule.

        Returns:
            DAGCircuit: A scheduled DAG.

        Raises:
            TranspilerError: if the circuit is not mapped on physical qubits.
            TranspilerError: if conditional bit is added to non-supported instruction.
        """
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError(
                "ASAP schedule runs on physical circuits only")

        time_unit = self.property_set["time_unit"]

        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        idle_after = {q: 0 for q in dag.qubits + dag.clbits}
        bit_indices = {q: index for index, q in enumerate(dag.qubits)}
        for node in dag.topological_op_nodes():
            op_duration = self._get_node_duration(node, bit_indices, dag)

            # compute t0, t1: instruction interval, note that
            # t0: start time of instruction
            # t1: end time of instruction
            if isinstance(node.op, self.CONDITIONAL_SUPPORTED):
                t0q = max(idle_after[q] for q in node.qargs)
                if node.op.condition_bits:
                    # conditional is bit tricky due to conditional_latency
                    t0c = max(idle_after[bit]
                              for bit in node.op.condition_bits)
                    if t0q > t0c:
                        # This is situation something like below
                        #
                        #           |t0q
                        # Q ▒▒▒▒▒▒▒▒▒░░
                        # C ▒▒▒░░░░░░░░
                        #     |t0c
                        #
                        # In this case, you can insert readout access before tq0
                        #
                        #           |t0q
                        # Q ▒▒▒▒▒▒▒▒▒▒▒
                        # C ▒▒▒░░░▒▒░░░
                        #         |t0q - conditional_latency
                        #
                        t0c = max(t0q - self.conditional_latency, t0c)
                    t1c = t0c + self.conditional_latency
                    for bit in node.op.condition_bits:
                        # Lock clbit until state is read
                        idle_after[bit] = t1c
                    # It starts after register read access
                    t0 = max(t0q, t1c)
                else:
                    t0 = t0q
                t1 = t0 + op_duration
            else:
                if node.op.condition_bits:
                    raise TranspilerError(
                        f"Conditional instruction {node.op.name} is not supported in ASAP scheduler."
                    )

                if isinstance(node.op, Measure):
                    # measure instruction handling is bit tricky due to clbit_write_latency
                    t0q = max(idle_after[q] for q in node.qargs)
                    t0c = max(idle_after[c] for c in node.cargs)
                    # Assume following case (t0c > t0q)
                    #
                    #       |t0q
                    # Q ▒▒▒▒░░░░░░░░░░░░
                    # C ▒▒▒▒▒▒▒▒░░░░░░░░
                    #           |t0c
                    #
                    # In this case, there is no actual clbit access until clbit_write_latency.
                    # The node t0 can be push backward by this amount.
                    #
                    #         |t0q' = t0c - clbit_write_latency
                    # Q ▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒
                    # C ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
                    #           |t0c' = t0c
                    #
                    # rather than naively doing
                    #
                    #           |t0q' = t0c
                    # Q ▒▒▒▒░░░░▒▒▒▒▒▒▒▒
                    # C ▒▒▒▒▒▒▒▒░░░▒▒▒▒▒
                    #              |t0c' = t0c + clbit_write_latency
                    #
                    t0 = max(t0q, t0c - self.clbit_write_latency)
                    t1 = t0 + op_duration
                    for clbit in node.cargs:
                        idle_after[clbit] = t1
                else:
                    # It happens to be directives such as barrier
                    t0 = max(idle_after[bit]
                             for bit in node.qargs + node.cargs)
                    t1 = t0 + op_duration

            # Add delay to qubit wire
            for bit in node.qargs:
                delta = t0 - idle_after[bit]
                if delta > 0 and isinstance(bit, Qubit):
                    new_dag.apply_operation_back(Delay(delta, time_unit),
                                                 [bit], [])
                idle_after[bit] = t1

            new_dag.apply_operation_back(node.op, node.qargs, node.cargs)

        circuit_duration = max(idle_after.values())
        for bit, after in idle_after.items():
            delta = circuit_duration - after
            if not (delta > 0 and isinstance(bit, Qubit)):
                continue
            new_dag.apply_operation_back(Delay(delta, time_unit), [bit], [])

        new_dag.name = dag.name
        new_dag.metadata = dag.metadata
        new_dag.calibrations = dag.calibrations

        # set circuit duration and unit to indicate it is scheduled
        new_dag.duration = circuit_duration
        new_dag.unit = time_unit
        return new_dag
コード例 #16
0
    def run(self, dag):
        try:
            from constraint import Problem, RecursiveBacktrackingSolver, AllDifferentConstraint
        except ImportError:
            raise TranspilerError(
                'CSPLayout requires python-constraint to run. '
                'Run pip install python-constraint')
        qubits = dag.qubits()
        cxs = set()

        for gate in dag.twoQ_gates():
            cxs.add((qubits.index(gate.qargs[0]), qubits.index(gate.qargs[1])))
        edges = self.coupling_map.get_edges()

        class CustomSolver(RecursiveBacktrackingSolver):
            """A wrap to RecursiveBacktrackingSolver to support ``call_limit``"""
            def __init__(self, call_limit=None, time_limit=None):
                self.call_limit = call_limit
                self.time_limit = time_limit
                self.call_current = None
                self.time_start = None
                self.time_current = None
                super().__init__()

            def limit_reached(self):
                """Checks if a limit is reached."""
                if self.call_current is not None:
                    self.call_current += 1
                    if self.call_current > self.call_limit:
                        return True
                if self.time_start is not None:
                    self.time_current = time() - self.time_start
                    if self.time_current > self.time_limit:
                        return True
                return False

            def getSolution(
                    self,  # pylint: disable=invalid-name
                    domains,
                    constraints,
                    vconstraints):
                """Wrap RecursiveBacktrackingSolver.getSolution to add the limits."""
                if self.call_limit is not None:
                    self.call_current = 0
                if self.time_limit is not None:
                    self.time_start = time()
                return super().getSolution(domains, constraints, vconstraints)

            def recursiveBacktracking(
                    self,  # pylint: disable=invalid-name
                    solutions,
                    domains,
                    vconstraints,
                    assignments,
                    single):
                """Like ``constraint.RecursiveBacktrackingSolver.recursiveBacktracking`` but
                limited in the amount of calls by ``self.call_limit`` """
                if self.limit_reached():
                    return None
                return super().recursiveBacktracking(solutions, domains,
                                                     vconstraints, assignments,
                                                     single)

        if self.time_limit is None and self.call_limit is None:
            solver = RecursiveBacktrackingSolver()
        else:
            solver = CustomSolver(call_limit=self.call_limit,
                                  time_limit=self.time_limit)

        problem = Problem(solver)
        problem.addVariables(list(range(len(qubits))),
                             self.coupling_map.physical_qubits)
        problem.addConstraint(
            AllDifferentConstraint())  # each wire is map to a single qbit

        if self.strict_direction:

            def constraint(control, target):
                return (control, target) in edges
        else:

            def constraint(control, target):
                return (control, target) in edges or (target, control) in edges

        for pair in cxs:
            problem.addConstraint(constraint, [pair[0], pair[1]])

        random.seed(self.seed)
        solution = problem.getSolution()

        if solution is None:
            stop_reason = 'nonexistent solution'
            if isinstance(solver, CustomSolver):
                if solver.time_limit is not None and solver.time_current >= self.time_limit:
                    stop_reason = 'time limit reached'
                elif solver.call_limit is not None and solver.call_current >= self.call_limit:
                    stop_reason = 'call limit reached'
        else:
            stop_reason = 'solution found'
            self.property_set['layout'] = Layout(
                {v: qubits[k]
                 for k, v in solution.items()})

        self.property_set['CSPLayout_stop_reason'] = stop_reason
コード例 #17
0
ファイル: transpiler.py プロジェクト: ecpeterson/qiskit-terra
def _parse_approximation_degree(approximation_degree, num_circuits):
    if not isinstance(approximation_degree, list):
        approximation_degree = [approximation_degree] * num_circuits
    if not all(0.0 <= d <= 1.0 for d in approximation_degree if d):
        raise TranspilerError("Approximation degree must be in [0.0, 1.0]")
    return approximation_degree
コード例 #18
0
    def run(self, dag):
        """Run the ApplyLayout pass on `dag`.

        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG (with physical qubits).

        Raises:
            TranspilerError: if no layout is found in `property_set` or no full physical qubits.
        """
        layout = self.property_set["layout"]
        if not layout:
            raise TranspilerError(
                "No 'layout' is found in property_set. Please run a Layout pass in advance."
            )
        if len(layout) != (1 + max(layout.get_physical_bits())):
            raise TranspilerError("The 'layout' must be full (with ancilla).")

        post_layout = self.property_set["post_layout"]

        q = QuantumRegister(len(layout), "q")

        new_dag = DAGCircuit()
        new_dag.add_qreg(q)
        new_dag.metadata = dag.metadata
        new_dag.add_clbits(dag.clbits)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)
        if post_layout is None:
            for qreg in dag.qregs.values():
                self.property_set["layout"].add_register(qreg)
            virtual_phsyical_map = layout.get_virtual_bits()
            for node in dag.topological_op_nodes():
                qargs = [q[virtual_phsyical_map[qarg]] for qarg in node.qargs]
                new_dag.apply_operation_back(node.op, qargs, node.cargs)
        else:
            # First build a new layout object going from:
            # old virtual -> old phsyical -> new virtual -> new physical
            # to:
            # old virtual -> new physical
            full_layout = Layout()
            old_phys_to_virtual = layout.get_physical_bits()
            new_virtual_to_physical = post_layout.get_virtual_bits()
            qubit_index_map = {
                bit: index
                for index, bit in enumerate(dag.qubits)
            }
            for new_virt, new_phys in new_virtual_to_physical.items():
                old_phys = qubit_index_map[new_virt]
                old_virt = old_phys_to_virtual[old_phys]
                full_layout.add(old_virt, new_phys)
            for reg in layout.get_registers():
                full_layout.add_register(reg)
            # Apply new layout to the circuit
            for node in dag.topological_op_nodes():
                qargs = [
                    q[new_virtual_to_physical[qarg]] for qarg in node.qargs
                ]
                new_dag.apply_operation_back(node.op, qargs, node.cargs)
            self.property_set["layout"] = full_layout
        new_dag._global_phase = dag._global_phase

        return new_dag
コード例 #19
0
ファイル: sabre_swap.py プロジェクト: septembrr/qiskit-terra
    def run(self, dag):
        """Run the SabreSwap pass on `dag`.

        Args:
            dag (DAGCircuit): the directed acyclic graph to be mapped.
        Returns:
            DAGCircuit: A dag mapped to be compatible with the coupling_map.
        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG
        """
        if len(dag.qregs) != 1 or dag.qregs.get('q', None) is None:
            raise TranspilerError('Sabre swap runs on physical circuits only.')

        if len(dag.qubits) > self.coupling_map.size():
            raise TranspilerError('More virtual qubits exist than physical.')

        rng = np.random.default_rng(self.seed)

        # Preserve input DAG's name, regs, wire_map, etc. but replace the graph.
        mapped_dag = dag._copy_circuit_metadata()

        # Assume bidirectional couplings, fixing gate direction is easy later.
        self.coupling_map.make_symmetric()

        canonical_register = dag.qregs['q']
        current_layout = Layout.generate_trivial_layout(canonical_register)

        # A decay factor for each qubit used to heuristically penalize recently
        # used qubits (to encourage parallelism).
        self.qubits_decay = {qubit: 1 for qubit in dag.qubits}

        # Start algorithm from the front layer and iterate until all gates done.
        num_search_steps = 0
        front_layer = dag.front_layer()
        self.applied_gates = set()
        while front_layer:
            execute_gate_list = []

            # Remove as many immediately applicable gates as possible
            for node in front_layer:
                if len(node.qargs) == 2:
                    v0, v1 = node.qargs
                    physical_qubits = (current_layout[v0], current_layout[v1])
                    if physical_qubits in self.coupling_map.get_edges():
                        execute_gate_list.append(node)
                else:  # Single-qubit gates as well as barriers are free
                    execute_gate_list.append(node)

            if execute_gate_list:
                for node in execute_gate_list:
                    new_node = _transform_gate_for_layout(node, current_layout)
                    mapped_dag.apply_operation_back(new_node.op,
                                                    new_node.qargs,
                                                    new_node.cargs,
                                                    new_node.condition)
                    front_layer.remove(node)
                    self.applied_gates.add(node)
                    for successor in dag.quantum_successors(node):
                        if successor.type != 'op':
                            continue
                        if self._is_resolved(successor, dag):
                            front_layer.append(successor)

                    if node.qargs:
                        self._reset_qubits_decay()

                # Diagnostics
                logger.debug('free! %s',
                             [(n.name, n.qargs) for n in execute_gate_list])
                logger.debug('front_layer: %s',
                             [(n.name, n.qargs) for n in front_layer])

                continue

            # After all free gates are exhausted, heuristically find
            # the best swap and insert it. When two or more swaps tie
            # for best score, pick one randomly.
            extended_set = self._obtain_extended_set(dag, front_layer)
            swap_candidates = self._obtain_swaps(front_layer, current_layout)
            swap_scores = dict.fromkeys(swap_candidates, 0)
            for swap_qubits in swap_scores:
                trial_layout = current_layout.copy()
                trial_layout.swap(*swap_qubits)
                score = self._score_heuristic(self.heuristic, front_layer,
                                              extended_set, trial_layout,
                                              swap_qubits)
                swap_scores[swap_qubits] = score
            min_score = min(swap_scores.values())
            best_swaps = [k for k, v in swap_scores.items() if v == min_score]
            best_swaps.sort(key=lambda x: (x[0].index, x[1].index))
            best_swap = rng.choice(best_swaps)
            swap_node = DAGNode(op=SwapGate(), qargs=best_swap, type='op')
            swap_node = _transform_gate_for_layout(swap_node, current_layout)
            mapped_dag.apply_operation_back(swap_node.op, swap_node.qargs)
            current_layout.swap(*best_swap)

            num_search_steps += 1
            if num_search_steps % DECAY_RESET_INTERVAL == 0:
                self._reset_qubits_decay()
            else:
                self.qubits_decay[best_swap[0]] += DECAY_RATE
                self.qubits_decay[best_swap[1]] += DECAY_RATE

            # Diagnostics
            logger.debug('SWAP Selection...')
            logger.debug('extended_set: %s',
                         [(n.name, n.qargs) for n in extended_set])
            logger.debug('swap scores: %s', swap_scores)
            logger.debug('best swap: %s', best_swap)
            logger.debug('qubits decay: %s', self.qubits_decay)

        self.property_set['final_layout'] = current_layout

        return mapped_dag
コード例 #20
0
    def run(self, dag, time_unit=None):  # pylint: disable=arguments-differ
        """Run the ALAPSchedule pass on `dag`.

        Args:
            dag (DAGCircuit): DAG to schedule.
            time_unit (str): Time unit to be used in scheduling: 'dt' or 's'.

        Returns:
            DAGCircuit: A scheduled DAG.

        Raises:
            TranspilerError: if the circuit is not mapped on physical qubits.
        """
        if len(dag.qregs) != 1 or dag.qregs.get('q', None) is None:
            raise TranspilerError(
                'ALAP schedule runs on physical circuits only')

        if not time_unit:
            time_unit = self.property_set['time_unit']

        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        qubit_time_available = defaultdict(int)

        def pad_with_delays(qubits: List[int], until, unit) -> None:
            """Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``."""
            for q in qubits:
                if qubit_time_available[q] < until:
                    idle_duration = until - qubit_time_available[q]
                    new_dag.apply_operation_front(Delay(idle_duration, unit),
                                                  [q], [])

        for node in reversed(list(dag.topological_op_nodes())):
            start_time = max(qubit_time_available[q] for q in node.qargs)
            pad_with_delays(node.qargs, until=start_time, unit=time_unit)

            duration = self.durations.get(node.op, node.qargs, unit=time_unit)

            # set duration for each instruction (tricky but necessary)
            new_op = node.op.copy(
            )  # need different op instance to store duration
            new_op.duration = duration
            new_op.unit = time_unit

            new_dag.apply_operation_front(new_op, node.qargs, node.cargs,
                                          node.condition)

            stop_time = start_time + duration
            # update time table
            for q in node.qargs:
                qubit_time_available[q] = stop_time

        working_qubits = qubit_time_available.keys()
        circuit_duration = max(qubit_time_available[q] for q in working_qubits)
        pad_with_delays(new_dag.qubits, until=circuit_duration, unit=time_unit)

        new_dag.name = dag.name
        new_dag.metadata = dag.metadata
        new_dag.duration = circuit_duration
        new_dag.unit = time_unit
        return new_dag
コード例 #21
0
def _check_conflicting_argument(**kargs):
    conflicting_args = [arg for arg, value in kargs.items() if value]
    if conflicting_args:
        raise TranspilerError("The parameters pass_manager conflicts with the following "
                              "parameter(s): {}.".format(', '.join(conflicting_args)))
コード例 #22
0
    def run(self, dag):
        """Run the LookaheadSwap pass on `dag`.

        Args:
            dag (DAGCircuit): the directed acyclic graph to be mapped
        Returns:
            DAGCircuit: A dag mapped to be compatible with the coupling_map in
                the property_set.
        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG
        """
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError("Lookahead swap runs on physical circuits only")

        number_of_available_qubits = len(self.coupling_map.physical_qubits)
        if len(dag.qubits) > number_of_available_qubits:
            raise TranspilerError(
                f"The number of DAG qubits ({len(dag.qubits)}) is greater than the number of "
                f"available device qubits ({number_of_available_qubits})."
            )

        canonical_register = dag.qregs["q"]
        trivial_layout = Layout.generate_trivial_layout(canonical_register)
        current_layout = trivial_layout.copy()

        mapped_gates = []
        ordered_virtual_gates = list(dag.serial_layers())
        gates_remaining = ordered_virtual_gates.copy()

        while gates_remaining:
            logger.debug("Top-level routing step: %d gates remaining.", len(gates_remaining))

            best_step = _search_forward_n_swaps(
                current_layout,
                gates_remaining,
                self.coupling_map,
                self.search_depth,
                self.search_width,
            )

            if best_step is None:
                raise TranspilerError(
                    "Lookahead failed to find a swap which mapped "
                    "gates or improved layout score."
                )

            logger.debug(
                "Found best step: mapped %d gates. Added swaps: %s.",
                len(best_step["gates_mapped"]),
                best_step["swaps_added"],
            )

            current_layout = best_step["layout"]
            gates_mapped = best_step["gates_mapped"]
            gates_remaining = best_step["gates_remaining"]

            mapped_gates.extend(gates_mapped)

        if self.fake_run:
            self.property_set["final_layout"] = current_layout
            return dag

        # Preserve input DAG's name, regs, wire_map, etc. but replace the graph.
        mapped_dag = dag._copy_circuit_metadata()

        for node in mapped_gates:
            mapped_dag.apply_operation_back(op=node.op, qargs=node.qargs, cargs=node.cargs)

        return mapped_dag
コード例 #23
0
    def _pre_runhook(self, dag: DAGCircuit):
        super()._pre_runhook(dag)

        num_pulses = len(self._dd_sequence)

        # Check if physical circuit is given
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError("DD runs on physical circuits only.")

        # Set default spacing otherwise validate user input
        if self._spacing is None:
            mid = 1 / num_pulses
            end = mid / 2
            self._spacing = [end] + [mid] * (num_pulses - 1) + [end]
        else:
            if sum(self._spacing) != 1 or any(a < 0 for a in self._spacing):
                raise TranspilerError(
                    "The spacings must be given in terms of fractions "
                    "of the slack period and sum to 1.")

        # Check if DD sequence is identity
        if num_pulses != 1:
            if num_pulses % 2 != 0:
                raise TranspilerError(
                    "DD sequence must contain an even number of gates (or 1).")
            noop = np.eye(2)
            for gate in self._dd_sequence:
                noop = noop.dot(gate.to_matrix())
            if not matrix_equal(noop, IGate().to_matrix(), ignore_phase=True):
                raise TranspilerError(
                    "The DD sequence does not make an identity operation.")
            self._sequence_phase = np.angle(noop[0][0])

        # Precompute qubit-wise DD sequence length for performance
        for qubit in dag.qubits:
            physical_index = dag.qubits.index(qubit)
            if self._qubits and physical_index not in self._qubits:
                continue

            sequence_lengths = []
            for gate in self._dd_sequence:
                try:
                    # Check calibration.
                    gate_length = dag.calibrations[gate.name][(physical_index,
                                                               gate.params)]
                    if gate_length % self._alignment != 0:
                        # This is necessary to implement lightweight scheduling logic for this pass.
                        # Usually the pulse alignment constraint and pulse data chunk size take
                        # the same value, however, we can intentionally violate this pattern
                        # at the gate level. For example, we can create a schedule consisting of
                        # a pi-pulse of 32 dt followed by a post buffer, i.e. delay, of 4 dt
                        # on the device with 16 dt constraint. Note that the pi-pulse length
                        # is multiple of 16 dt but the gate length of 36 is not multiple of it.
                        # Such pulse gate should be excluded.
                        raise TranspilerError(
                            f"Pulse gate {gate.name} with length non-multiple of {self._alignment} "
                            f"is not acceptable in {self.__class__.__name__} pass."
                        )
                except KeyError:
                    gate_length = self._durations.get(gate, physical_index)
                sequence_lengths.append(gate_length)
                # Update gate duration. This is necessary for current timeline drawer, i.e. scheduled.
                gate.duration = gate_length
            self._dd_sequence_lengths[qubit] = sequence_lengths
コード例 #24
0
    def run(self, dag):
        """Run the DynamicalDecoupling pass on dag.

        Args:
            dag (DAGCircuit): a scheduled DAG.

        Returns:
            DAGCircuit: equivalent circuit with delays interrupted by DD,
                where possible.

        Raises:
            TranspilerError: if the circuit is not mapped on physical qubits.
        """
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError("DD runs on physical circuits only.")

        if dag.duration is None:
            raise TranspilerError("DD runs after circuit is scheduled.")

        num_pulses = len(self._dd_sequence)
        sequence_gphase = 0
        if num_pulses != 1:
            if num_pulses % 2 != 0:
                raise TranspilerError(
                    "DD sequence must contain an even number of gates (or 1).")
            noop = np.eye(2)
            for gate in self._dd_sequence:
                noop = noop.dot(gate.to_matrix())
            if not matrix_equal(noop, IGate().to_matrix(), ignore_phase=True):
                raise TranspilerError(
                    "The DD sequence does not make an identity operation.")
            sequence_gphase = np.angle(noop[0][0])

        if self._qubits is None:
            self._qubits = set(range(dag.num_qubits()))
        else:
            self._qubits = set(self._qubits)

        if self._spacing:
            if sum(self._spacing) != 1 or any(a < 0 for a in self._spacing):
                raise TranspilerError(
                    "The spacings must be given in terms of fractions "
                    "of the slack period and sum to 1.")
        else:  # default to balanced spacing
            mid = 1 / num_pulses
            end = mid / 2
            self._spacing = [end] + [mid] * (num_pulses - 1) + [end]

        new_dag = dag._copy_circuit_metadata()

        qubit_index_map = {
            qubit: index
            for index, qubit in enumerate(new_dag.qubits)
        }
        index_sequence_duration_map = {}
        for qubit in new_dag.qubits:
            physical_qubit = qubit_index_map[qubit]
            dd_sequence_duration = 0
            for gate in self._dd_sequence:
                gate.duration = self._durations.get(gate, physical_qubit)
                dd_sequence_duration += gate.duration
            index_sequence_duration_map[physical_qubit] = dd_sequence_duration

        for nd in dag.topological_op_nodes():
            if not isinstance(nd.op, Delay):
                new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                continue

            dag_qubit = nd.qargs[0]
            physical_qubit = qubit_index_map[dag_qubit]
            if physical_qubit not in self._qubits:  # skip unwanted qubits
                new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                continue

            pred = next(dag.predecessors(nd))
            succ = next(dag.successors(nd))
            if self._skip_reset_qubits:  # discount initial delays
                if pred.type == "in" or isinstance(pred.op, Reset):
                    new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                    continue

            dd_sequence_duration = index_sequence_duration_map[physical_qubit]
            slack = nd.op.duration - dd_sequence_duration
            if slack <= 0:  # dd doesn't fit
                new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                continue

            if num_pulses == 1:  # special case of using a single gate for DD
                u_inv = self._dd_sequence[0].inverse().to_matrix()
                theta, phi, lam, phase = OneQubitEulerDecomposer(
                ).angles_and_phase(u_inv)
                # absorb the inverse into the successor (from left in circuit)
                if succ.type == "op" and isinstance(succ.op, (UGate, U3Gate)):
                    theta_r, phi_r, lam_r = succ.op.params
                    succ.op.params = Optimize1qGates.compose_u3(
                        theta_r, phi_r, lam_r, theta, phi, lam)
                    sequence_gphase += phase
                # absorb the inverse into the predecessor (from right in circuit)
                elif pred.type == "op" and isinstance(pred.op,
                                                      (UGate, U3Gate)):
                    theta_l, phi_l, lam_l = pred.op.params
                    pred.op.params = Optimize1qGates.compose_u3(
                        theta, phi, lam, theta_l, phi_l, lam_l)
                    sequence_gphase += phase
                # don't do anything if there's no single-qubit gate to absorb the inverse
                else:
                    new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                    continue

            # insert the actual DD sequence
            taus = [int(slack * a) for a in self._spacing]
            unused_slack = slack - sum(
                taus)  # unused, due to rounding to int multiples of dt
            middle_index = int(
                (len(taus) - 1) / 2)  # arbitrary: redistribute to middle
            taus[
                middle_index] += unused_slack  # now we add up to original delay duration

            for tau, gate in itertools.zip_longest(taus, self._dd_sequence):
                if tau > 0:
                    new_dag.apply_operation_back(Delay(tau), [dag_qubit])
                if gate is not None:
                    new_dag.apply_operation_back(gate, [dag_qubit])

            new_dag.global_phase = _mod_2pi(new_dag.global_phase +
                                            sequence_gphase)

        return new_dag
コード例 #25
0
    def _mapper(self, circuit_graph, coupling_graph, trials=20):
        """Map a DAGCircuit onto a CouplingMap using swap gates.

        Use self.initial_layout for the initial layout.

        Args:
            circuit_graph (DAGCircuit): input DAG circuit
            coupling_graph (CouplingMap): coupling graph to map onto
            trials (int): number of trials.

        Returns:
            DAGCircuit: object containing a circuit equivalent to
                circuit_graph that respects couplings in coupling_graph
            Layout: a layout object mapping qubits of circuit_graph into
                qubits of coupling_graph. The layout may differ from the
                initial_layout if the first layer of gates cannot be
                executed on the initial_layout, since in this case
                it is more efficient to modify the layout instead of swapping
            Dict: a final-layer qubit permutation

        Raises:
            TranspilerError: if there was any error during the mapping
                or with the parameters.
        """
        # Schedule the input circuit by calling layers()
        layerlist = list(circuit_graph.layers())
        logger.debug("schedule:")
        for i, v in enumerate(layerlist):
            logger.debug("    %d: %s", i, v["partition"])

        if self.initial_layout is not None:
            qubit_subset = self.initial_layout.get_virtual_bits().keys()
        else:
            # Supply a default layout for this dag
            self.initial_layout = Layout()
            physical_qubit = 0
            for qreg in circuit_graph.qregs.values():
                for index in range(qreg.size):
                    self.initial_layout[(qreg, index)] = physical_qubit
                    physical_qubit += 1
            qubit_subset = self.initial_layout.get_virtual_bits().keys()
            # Restrict the coupling map to the image of the layout
            coupling_graph = coupling_graph.subgraph(
                self.initial_layout.get_physical_bits().keys())
            if coupling_graph.size() < len(self.initial_layout):
                raise TranspilerError(
                    "Coupling map too small for default layout")
            self.input_layout = self.initial_layout.copy()

        # Find swap circuit to preceed to each layer of input circuit
        layout = self.initial_layout.copy()

        # Construct an empty DAGCircuit with the same set of
        # qregs and cregs as the input circuit
        dagcircuit_output = DAGCircuit()
        dagcircuit_output.name = circuit_graph.name
        for qreg in circuit_graph.qregs.values():
            dagcircuit_output.add_qreg(qreg)
        for creg in circuit_graph.cregs.values():
            dagcircuit_output.add_creg(creg)

        # Make a trivial wire mapping between the subcircuits
        # returned by _layer_update and the circuit we build
        identity_wire_map = {}
        for qubit in circuit_graph.qubits():
            identity_wire_map[qubit] = qubit
        for bit in circuit_graph.clbits():
            identity_wire_map[bit] = bit

        first_layer = True  # True until first layer is output
        logger.debug("initial_layout = %s", layout)

        # Iterate over layers
        for i, layer in enumerate(layerlist):

            # Attempt to find a permutation for this layer
            success_flag, best_circuit, best_depth, best_layout, trivial_flag \
                = self._layer_permutation(layer["partition"], layout,
                                          qubit_subset, coupling_graph,
                                          trials)
            logger.debug("mapper: layer %d", i)
            logger.debug(
                "mapper: success_flag=%s,best_depth=%s,trivial_flag=%s",
                success_flag, str(best_depth), trivial_flag)

            # If this fails, try one gate at a time in this layer
            if not success_flag:
                logger.debug(
                    "mapper: failed, layer %d, "
                    "retrying sequentially", i)
                serial_layerlist = list(layer["graph"].serial_layers())

                # Go through each gate in the layer
                for j, serial_layer in enumerate(serial_layerlist):

                    success_flag, best_circuit, best_depth, best_layout, trivial_flag = \
                        self._layer_permutation(
                            serial_layer["partition"],
                            layout, qubit_subset,
                            coupling_graph,
                            trials)
                    logger.debug("mapper: layer %d, sublayer %d", i, j)
                    logger.debug(
                        "mapper: success_flag=%s,best_depth=%s,"
                        "trivial_flag=%s", success_flag, str(best_depth),
                        trivial_flag)

                    # Give up if we fail again
                    if not success_flag:
                        raise TranspilerError("swap mapper failed: " +
                                              "layer %d, sublayer %d" % (i, j))

                    # If this layer is only single-qubit gates,
                    # and we have yet to see multi-qubit gates,
                    # continue to the next inner iteration
                    if trivial_flag and first_layer:
                        logger.debug("mapper: skip to next sublayer")
                        continue

                    if first_layer:
                        self.initial_layout = layout

                    # Update the record of qubit positions
                    # for each inner iteration
                    layout = best_layout
                    # Update the DAG
                    dagcircuit_output.extend_back(
                        self._layer_update(j, first_layer, best_layout,
                                           best_depth, best_circuit,
                                           serial_layerlist),
                        identity_wire_map)
                    if first_layer:
                        first_layer = False

            else:
                # Update the record of qubit positions for each iteration
                layout = best_layout

                if first_layer:
                    self.initial_layout = layout

                # Update the DAG
                dagcircuit_output.extend_back(
                    self._layer_update(i, first_layer, best_layout, best_depth,
                                       best_circuit, layerlist),
                    identity_wire_map)

                if first_layer:
                    first_layer = False

        # This is the final edgemap. We might use it to correctly replace
        # any measurements that needed to be removed earlier.
        logger.debug("mapper: self.initial_layout = %s",
                     pformat(self.initial_layout))
        logger.debug("mapper: layout = %s", pformat(layout))
        last_edgemap = layout.combine_into_edge_map(self.initial_layout)
        logger.debug("mapper: last_edgemap = %s", pformat(last_edgemap))

        # If first_layer is still set, the circuit only has single-qubit gates
        # so we can use the initial layout to output the entire circuit
        # This code is dead due to changes to first_layer above.
        if first_layer:
            logger.debug("mapper: first_layer flag still set")
            layout = self.initial_layout
            for i, layer in enumerate(layerlist):
                edge_map = layout.combine_into_edge_map(self.initial_layout)
                dagcircuit_output.compose_back(layer["graph"], edge_map)

        return dagcircuit_output
コード例 #26
0
    def run(self, dag):
        """Run the CrosstalkAdaptiveLayout pass on `list of dag`."""
        self._correct_xtalk_prop_keys()
        self._initialize_backend_prop()
        num_qubits = self._create_program_graphs(dag=dag)

        if num_qubits > len(self.available_hw_qubits):
            raise TranspilerError("Number of qubits greater than device.")

        for hwid, q in enumerate(dag.qubits):
            self.qarg_to_id[q.register.name + str(q.index)] = hwid

        for prog_graph in self.prog_graphs:
            # sort by weight, then edge name for determinism (since networkx on python 3.5 returns
            # different order of edges)
            """NEXT STEP!
            ここに、Multi-programmingするかどうかの判定関数を噛ませる
            """
            self.pending_program_edges = sorted(
                prog_graph.edges(data=True),
                key=lambda x: [x[2]["weight"], -x[0], -x[1]],
                reverse=True,
            )

            while self.pending_program_edges:

                edge = self._select_next_edge()
                q1_mapped = edge[0] in self.prog2hw
                q2_mapped = edge[1] in self.prog2hw
                if (not q1_mapped) and (not q2_mapped):
                    best_hw_edge = self._select_best_remaining_cx()
                    if best_hw_edge is None:
                        raise TranspilerError(
                            "CNOT({}, {}) could not be placed "
                            "in selected device.".format(edge[0], edge[1]))
                    self.prog2hw[edge[0]] = best_hw_edge[0]
                    self.prog2hw[edge[1]] = best_hw_edge[1]
                    self.available_hw_qubits.remove(best_hw_edge[0])
                    self.available_hw_qubits.remove(best_hw_edge[1])

                    self._crosstalk_backend_prop(edge=best_hw_edge)
                elif not q1_mapped:
                    best_hw_qubit = self._select_best_remaining_qubit(
                        edge[0], prog_graph)
                    if best_hw_qubit is None:
                        raise TranspilerError(
                            "CNOT({}, {}) could not be placed in selected device. "
                            "No qubit near qr[{}] available".format(
                                edge[0], edge[1], edge[0]))
                    self.prog2hw[edge[0]] = best_hw_qubit
                    self.available_hw_qubits.remove(best_hw_qubit)
                    self._crosstalk_backend_prop(edge=(self.prog2hw[edge[1]],
                                                       best_hw_qubit))
                else:
                    best_hw_qubit = self._select_best_remaining_qubit(
                        edge[1], prog_graph)
                    if best_hw_qubit is None:
                        raise TranspilerError(
                            "CNOT({}, {}) could not be placed in selected device. "
                            "No qubit near qr[{}] available".format(
                                edge[0], edge[1], edge[1]))
                    self.prog2hw[edge[1]] = best_hw_qubit
                    self.available_hw_qubits.remove(best_hw_qubit)
                    self._crosstalk_backend_prop(edge=(self.prog2hw[edge[0]],
                                                       best_hw_qubit))
                new_edges = [
                    x for x in self.pending_program_edges
                    if not (x[0] in self.prog2hw and x[1] in self.prog2hw)
                ]

                self.pending_program_edges = new_edges

        for qid in self.qarg_to_id.values():
            if qid not in self.prog2hw:
                self.prog2hw[qid] = self.available_hw_qubits[0]
                self.available_hw_qubits.remove(self.prog2hw[qid])

        layout_dict = {}
        for q in dag.qubits:
            pid = self._qarg_to_id(q)
            hwid = self.prog2hw[pid]
            # layout[q] = hwid
            layout_dict[q] = hwid
        self.property_set["layout"] = Layout(input_dict=layout_dict)
コード例 #27
0
    def _layer_permutation(self, layer_partition, layout, qubit_subset,
                           coupling, trials):
        """Find a swap circuit that implements a permutation for this layer.

        The goal is to swap qubits such that qubits in the same two-qubit gates
        are adjacent.

        Based on S. Bravyi's algorithm.

        Args:
            layer_partition (list): The layer_partition is a list of (qu)bit
                lists and each qubit is a tuple (qreg, index).
            layout (Layout): The layout is a Layout object mapping virtual
                qubits in the input circuit to physical qubits in the coupling
                graph. It reflects the current positions of the data.
            qubit_subset (list): The qubit_subset is the set of qubits in
                the coupling graph that we have chosen to map into, as tuples
                (Register, index).
            coupling (CouplingMap): Directed graph representing a coupling map.
                This coupling map should be one that was provided to the
                stochastic mapper.
            trials (int): Number of attempts the randomized algorithm makes.

        Returns:
            Tuple: success_flag, best_circuit, best_depth, best_layout

        If success_flag is True, then best_circuit contains a DAGCircuit with
        the swap circuit, best_depth contains the depth of the swap circuit,
        and best_layout contains the new positions of the data qubits after the
        swap circuit has been applied.

        Raises:
            TranspilerError: if anything went wrong.
        """
        logger.debug("layer_permutation: layer_partition = %s",
                     layer_partition)
        logger.debug("layer_permutation: layout = %s",
                     layout.get_virtual_bits())
        logger.debug("layer_permutation: qubit_subset = %s", qubit_subset)
        logger.debug("layer_permutation: trials = %s", trials)

        # The input dag is on a flat canonical register
        # TODO: cleanup the code that is general for multiple qregs below
        canonical_register = QuantumRegister(len(layout), 'q')
        qregs = OrderedDict({canonical_register.name: canonical_register})

        gates = []  # list of lists of tuples [[(register, index), ...], ...]
        for gate_args in layer_partition:
            if len(gate_args) > 2:
                raise TranspilerError("Layer contains > 2-qubit gates")
            if len(gate_args) == 2:
                gates.append(tuple(gate_args))
        logger.debug("layer_permutation: gates = %s", gates)

        # Can we already apply the gates? If so, there is no work to do.
        dist = sum(
            [coupling.distance(layout[g[0]], layout[g[1]]) for g in gates])
        logger.debug("layer_permutation: distance = %s", dist)
        if dist == len(gates):
            logger.debug("layer_permutation: nothing to do")
            circ = DAGCircuit()
            circ.add_qreg(canonical_register)
            return True, circ, 0, layout

        # Begin loop over trials of randomized algorithm
        num_qubits = len(layout)
        best_depth = inf  # initialize best depth
        best_edges = None  # best edges found
        best_circuit = None  # initialize best swap circuit
        best_layout = None  # initialize best final layout

        cdist2 = coupling._dist_matrix**2
        # Scaling matrix
        scale = np.zeros((num_qubits, num_qubits))

        int_qubit_subset = np.fromiter(
            (self._qubit_indices[bit] for bit in qubit_subset),
            dtype=np.int32,
            count=len(qubit_subset))

        int_gates = np.fromiter(
            (self._qubit_indices[bit] for gate in gates for bit in gate),
            dtype=np.int32,
            count=2 * len(gates))

        int_layout = nlayout_from_layout(layout, self._qubit_indices,
                                         num_qubits, coupling.size())

        trial_circuit = DAGCircuit(
        )  # SWAP circuit for slice of swaps in this trial
        trial_circuit.add_qubits(layout.get_virtual_bits())

        edges = np.asarray(coupling.get_edges(), dtype=np.int32).ravel()
        cdist = coupling._dist_matrix
        for trial in range(trials):
            logger.debug("layer_permutation: trial %s", trial)
            # This is one Trial --------------------------------------
            dist, optim_edges, trial_layout, depth_step = swap_trial(
                num_qubits, int_layout, int_qubit_subset, int_gates, cdist2,
                cdist, edges, scale, self.rng)

            logger.debug(
                "layer_permutation: final distance for this trial = %s", dist)
            if dist == len(gates) and depth_step < best_depth:
                logger.debug(
                    "layer_permutation: got circuit with improved depth %s",
                    depth_step)
                best_edges = optim_edges
                best_layout = trial_layout
                best_depth = min(best_depth, depth_step)

            # Break out of trial loop if we found a depth 1 circuit
            # since we can't improve it further
            if best_depth == 1:
                break

        # If we have no best circuit for this layer, all of the
        # trials have failed
        if best_layout is None:
            logger.debug("layer_permutation: failed!")
            return False, None, None, None

        edges = best_edges.edges()
        for idx in range(best_edges.size // 2):
            swap_src = self.trivial_layout[edges[2 * idx]]
            swap_tgt = self.trivial_layout[edges[2 * idx + 1]]
            trial_circuit.apply_operation_back(SwapGate(),
                                               [swap_src, swap_tgt], [])
        best_circuit = trial_circuit

        # Otherwise, we return our result for this layer
        logger.debug("layer_permutation: success!")
        best_lay = best_layout.to_layout(qregs)
        return True, best_circuit, best_depth, best_lay
コード例 #28
0
    def run(self, dag):
        """
        Args:
            dag(DAGCircuit): DAG circuit.
        Returns:
            DAGCircuit: optimized DAG circuit.
        Raises:
            TranspilerError: If the template has not the right form or
             if the output circuit acts differently as the input circuit.
        """
        circuit_dag = dag
        circuit_dag_dep = dag_to_dagdependency(circuit_dag)

        for template in self.template_list:
            if not isinstance(template, (QuantumCircuit, DAGDependency)):
                raise TranspilerError(
                    "A template is a Quantumciruit or a DAGDependency.")

            if len(template.qubits) > len(circuit_dag_dep.qubits):
                continue

            identity = np.identity(2**len(template.qubits), dtype=complex)
            try:
                if isinstance(template, DAGDependency):
                    data = Operator(dagdependency_to_circuit(template)).data
                else:
                    data = Operator(template).data

                comparison = np.allclose(data, identity)

                if not comparison:
                    raise TranspilerError(
                        "A template is a Quantumciruit() that "
                        "performs the identity.")
            except TypeError:
                pass

            if isinstance(template, QuantumCircuit):
                template_dag_dep = circuit_to_dagdependency(template)
            else:
                template_dag_dep = template

            template_m = TemplateMatching(
                circuit_dag_dep,
                template_dag_dep,
                self.heuristics_qubits_param,
                self.heuristics_backward_param,
            )

            template_m.run_template_matching()

            matches = template_m.match_list

            if matches:
                maximal = MaximalMatches(matches)
                maximal.run_maximal_matches()
                max_matches = maximal.max_match_list

                substitution = TemplateSubstitution(
                    max_matches,
                    template_m.circuit_dag_dep,
                    template_m.template_dag_dep,
                    self.user_cost_dict,
                )
                substitution.run_dag_opt()

                circuit_dag_dep = substitution.dag_dep_optimized
            else:
                continue
        circuit_dag = dagdependency_to_dag(circuit_dag_dep)
        return circuit_dag
コード例 #29
0
def transpile(circuits,
              backend=None,
              basis_gates=None,
              coupling_map=None,
              backend_properties=None,
              initial_layout=None,
              seed_transpiler=None,
              optimization_level=None,
              pass_manager=None):
    """transpile one or more circuits, according to some desired
    transpilation targets.

    All arguments may be given as either singleton or list. In case of list,
    the length must be equal to the number of circuits being transpiled.

    Transpilation is done in parallel using multiprocessing.

    Args:
        circuits (QuantumCircuit or list[QuantumCircuit]):
            Circuit(s) to transpile

        backend (BaseBackend):
            If set, transpiler options are automatically grabbed from
            backend.configuration() and backend.properties().
            If any other option is explicitly set (e.g. coupling_map), it
            will override the backend's.
            Note: the backend arg is purely for convenience. The resulting
                circuit may be run on any backend as long as it is compatible.

        basis_gates (list[str]):
            List of basis gate names to unroll to.
            e.g:
                ['u1', 'u2', 'u3', 'cx']
            If None, do not unroll.

        coupling_map (CouplingMap or list):
            Coupling map (perhaps custom) to target in mapping.
            Multiple formats are supported:
            a. CouplingMap instance

            b. list
                Must be given as an adjacency matrix, where each entry
                specifies all two-qubit interactions supported by backend
                e.g:
                    [[0, 1], [0, 3], [1, 2], [1, 5], [2, 5], [4, 1], [5, 3]]

        backend_properties (BackendProperties):
            properties returned by a backend, including information on gate
            errors, readout errors, qubit coherence times, etc. For a backend
            that provides this information, it can be obtained with:
            ``backend.properties()``

        initial_layout (Layout or dict or list):
            Initial position of virtual qubits on physical qubits.
            If this layout makes the circuit compatible with the coupling_map
            constraints, it will be used.
            The final layout is not guaranteed to be the same, as the transpiler
            may permute qubits through swaps or other means.

            Multiple formats are supported:
            a. Layout instance

            b. dict
                virtual to physical:
                    {qr[0]: 0,
                     qr[1]: 3,
                     qr[2]: 5}

                physical to virtual:
                    {0: qr[0],
                     3: qr[1],
                     5: qr[2]}

            c. list
                virtual to physical:
                    [0, 3, 5]  # virtual qubits are ordered (in addition to named)

                physical to virtual:
                    [qr[0], None, None, qr[1], None, qr[2]]

        seed_transpiler (int):
            sets random seed for the stochastic parts of the transpiler

        optimization_level (int):
            How much optimization to perform on the circuits.
            Higher levels generate more optimized circuits,
            at the expense of longer transpilation time.
                0: no optimization
                1: light optimization
                2: heavy optimization
                3: even heavier optimization

        pass_manager (PassManager):
            The pass manager to use for a custom pipeline of transpiler passes.
            If this arg is present, all other args will be ignored and the
            pass manager will be used directly (Qiskit will not attempt to
            auto-select a pass manager based on transpile options).


    Returns:
        QuantumCircuit or list[QuantumCircuit]: transpiled circuit(s).

    Raises:
        TranspilerError: in case of bad inputs to transpiler or errors in passes
    """

    # transpiling schedules is not supported yet.
    if isinstance(circuits, Schedule) or \
       (isinstance(circuits, list) and all(isinstance(c, Schedule) for c in circuits)):
        return circuits

    if optimization_level is None:
        config = user_config.get_config()
        optimization_level = config.get('transpile_optimization_level', None)

    # Get TranspileConfig(s) to configure the circuit transpilation job(s)
    circuits = circuits if isinstance(circuits, list) else [circuits]
    transpile_configs = _parse_transpile_args(circuits, backend, basis_gates,
                                              coupling_map, backend_properties,
                                              initial_layout, seed_transpiler,
                                              optimization_level, pass_manager)
    # Check circuit width against number of qubits in coupling_map(s)
    coupling_maps_list = list(config.coupling_map
                              for config in transpile_configs)
    for circuit, parsed_coupling_map in zip(circuits, coupling_maps_list):
        # If coupling_map is not None
        if isinstance(parsed_coupling_map, CouplingMap):
            n_qubits = len(circuit.qubits)
            max_qubits = parsed_coupling_map.size()
            if n_qubits > max_qubits:
                raise TranspilerError(
                    'Number of qubits ({}) '.format(n_qubits) +
                    'in {} '.format(circuit.name) +
                    'is greater than maximum ({}) '.format(max_qubits) +
                    'in the coupling_map')
    # Transpile circuits in parallel
    circuits = parallel_map(_transpile_circuit,
                            list(zip(circuits, transpile_configs)))

    if len(circuits) == 1:
        return circuits[0]
    return circuits
コード例 #30
0
    def run(self, dag):
        """Translate an input DAGCircuit to the target basis.

        Args:
            dag (DAGCircuit): input dag

        Raises:
            TranspilerError: if the target basis cannot be reached

        Returns:
            DAGCircuit: translated circuit.
        """
        if self._target_basis is None and self._target is None:
            return dag

        qarg_indices = {qubit: index for index, qubit in enumerate(dag.qubits)}
        # Names of instructions assumed to supported by any backend.
        if self._target is None:
            basic_instrs = ["measure", "reset", "barrier", "snapshot", "delay"]
            target_basis = set(self._target_basis)
            source_basis = set()
            for node in dag.op_nodes():
                if not dag.has_calibration_for(node):
                    source_basis.add((node.name, node.op.num_qubits))
            qargs_local_source_basis = {}
        else:
            basic_instrs = ["barrier", "snapshot"]
            source_basis = set()
            target_basis = self._target.keys() - set(self._non_global_operations)
            qargs_local_source_basis = defaultdict(set)
            for node in dag.op_nodes():
                qargs = tuple(qarg_indices[bit] for bit in node.qargs)
                if dag.has_calibration_for(node):
                    continue
                # Treat the instruction as on an incomplete basis if the qargs are in the
                # qargs_with_non_global_operation dictionary or if any of the qubits in qargs
                # are a superset for a non-local operation. For example, if the qargs
                # are (0, 1) and that's a global (ie no non-local operations on (0, 1)
                # operation but there is a non-local operation on (1,) we need to
                # do an extra non-local search for this op to ensure we include any
                # single qubit operation for (1,) as valid. This pattern also holds
                # true for > 2q ops too (so for 4q operations we need to check for 3q, 2q,
                # and 1q opertaions in the same manner)
                if qargs in self._qargs_with_non_global_operation or any(
                    frozenset(qargs).issuperset(incomplete_qargs)
                    for incomplete_qargs in self._qargs_with_non_global_operation
                ):
                    qargs_local_source_basis[frozenset(qargs)].add((node.name, node.op.num_qubits))
                else:
                    source_basis.add((node.name, node.op.num_qubits))

        target_basis = set(target_basis).union(basic_instrs)

        logger.info(
            "Begin BasisTranslator from source basis %s to target basis %s.",
            source_basis,
            target_basis,
        )

        # Search for a path from source to target basis.
        search_start_time = time.time()
        basis_transforms = _basis_search(
            self._equiv_lib, source_basis, target_basis, _basis_heuristic
        )

        qarg_local_basis_transforms = {}
        for qarg, local_source_basis in qargs_local_source_basis.items():
            expanded_target = target_basis | self._qargs_with_non_global_operation[qarg]
            # For any multiqubit operation that contains a subset of qubits that
            # has a non-local operation, include that non-local operation in the
            # search. This matches with the check we did above to include those
            # subset non-local operations in the check here.
            if len(qarg) > 1:
                for non_local_qarg, local_basis in self._qargs_with_non_global_operation.items():
                    if qarg.issuperset(non_local_qarg):
                        expanded_target |= local_basis

            logger.info(
                "Performing BasisTranslator search from source basis %s to target "
                "basis %s on qarg %s.",
                local_source_basis,
                expanded_target,
                qarg,
            )
            qarg_local_basis_transforms[qarg] = _basis_search(
                self._equiv_lib, local_source_basis, expanded_target, _basis_heuristic
            )

        search_end_time = time.time()
        logger.info(
            "Basis translation path search completed in %.3fs.", search_end_time - search_start_time
        )

        if basis_transforms is None:
            raise TranspilerError(
                "Unable to map source basis {} to target basis {} "
                "over library {}.".format(source_basis, target_basis, self._equiv_lib)
            )

        # Compose found path into a set of instruction substitution rules.

        compose_start_time = time.time()
        instr_map = _compose_transforms(basis_transforms, source_basis, dag)
        extra_instr_map = {
            qarg: _compose_transforms(transforms, qargs_local_source_basis[qarg], dag)
            for qarg, transforms in qarg_local_basis_transforms.items()
        }

        compose_end_time = time.time()
        logger.info(
            "Basis translation paths composed in %.3fs.", compose_end_time - compose_start_time
        )

        # Replace source instructions with target translations.

        replace_start_time = time.time()
        for node in dag.op_nodes():
            node_qargs = tuple(qarg_indices[bit] for bit in node.qargs)
            qubit_set = frozenset(node_qargs)

            if node.name in target_basis:
                continue
            if (
                node_qargs in self._qargs_with_non_global_operation
                and node.name in self._qargs_with_non_global_operation[node_qargs]
            ):
                continue

            if dag.has_calibration_for(node):
                continue

            def replace_node(node, instr_map):
                target_params, target_dag = instr_map[node.op.name, node.op.num_qubits]
                if len(node.op.params) != len(target_params):
                    raise TranspilerError(
                        "Translation num_params not equal to op num_params."
                        "Op: {} {} Translation: {}\n{}".format(
                            node.op.params, node.op.name, target_params, target_dag
                        )
                    )

                if node.op.params:
                    # Convert target to circ and back to assign_parameters, since
                    # DAGCircuits won't have a ParameterTable.
                    from qiskit.converters import dag_to_circuit, circuit_to_dag

                    target_circuit = dag_to_circuit(target_dag)

                    target_circuit.assign_parameters(
                        dict(zip_longest(target_params, node.op.params)), inplace=True
                    )

                    bound_target_dag = circuit_to_dag(target_circuit)
                else:
                    bound_target_dag = target_dag

                if len(bound_target_dag.op_nodes()) == 1 and len(
                    bound_target_dag.op_nodes()[0].qargs
                ) == len(node.qargs):
                    dag_op = bound_target_dag.op_nodes()[0].op
                    # dag_op may be the same instance as other ops in the dag,
                    # so if there is a condition, need to copy
                    if node.op.condition:
                        dag_op = dag_op.copy()
                    dag.substitute_node(node, dag_op, inplace=True)

                    if bound_target_dag.global_phase:
                        dag.global_phase += bound_target_dag.global_phase
                else:
                    dag.substitute_node_with_dag(node, bound_target_dag)

            if qubit_set in extra_instr_map:
                replace_node(node, extra_instr_map[qubit_set])
            elif (node.op.name, node.op.num_qubits) in instr_map:
                replace_node(node, instr_map)
            else:
                raise TranspilerError(f"BasisTranslator did not map {node.name}.")

        replace_end_time = time.time()
        logger.info(
            "Basis translation instructions replaced in %.3fs.",
            replace_end_time - replace_start_time,
        )

        return dag