Exemplo n.º 1
0
    def run(self, dag):
        """Run the RemoveFinalMeasurements pass on `dag`.

        Args:
            dag (DAGCircuit): the DAG to be optimized.

        Returns:
            DAGCircuit: the optimized DAG.
        """
        final_op_types = ['measure', 'barrier']
        final_ops = []
        cregs_to_remove = dict()
        clbits_with_final_measures = set()

        for candidate_node in dag.named_nodes(*final_op_types):
            is_final_op = True

            for _, child_successors in dag.bfs_successors(candidate_node):
                if any(suc.type == 'op' and suc.name not in final_op_types
                       for suc in child_successors):
                    is_final_op = False
                    break

            if is_final_op:
                final_ops.append(candidate_node)

        if not final_ops:
            return dag

        new_dag = DAGCircuit()

        for node in final_ops:
            for carg in node.cargs:
                # Add the clbit that was attached to the measure we are removing
                clbits_with_final_measures.add(carg)
            dag.remove_op_node(node)

        # If the clbit is idle, add its register to list of registers we may remove
        for clbit in clbits_with_final_measures:
            if clbit in dag.idle_wires():
                if clbit.register in cregs_to_remove:
                    cregs_to_remove[clbit.register] += 1
                else:
                    cregs_to_remove[clbit.register] = 1

        # Remove creg if all of its clbits were added above
        for key, val in zip(list(dag.cregs.keys()), list(dag.cregs.values())):
            if val in cregs_to_remove and cregs_to_remove[val] == val.size:
                del dag.cregs[key]

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

        for node in dag.topological_op_nodes():
            # copy the condition over too
            if node.condition:
                new_dag.apply_operation_back(node.op,
                                             qargs=node.qargs,
                                             cargs=node.cargs,
                                             condition=node.condition)
            else:
                new_dag.apply_operation_back(node.op,
                                             qargs=node.qargs,
                                             cargs=node.cargs)

        return new_dag
Exemplo n.º 2
0
def swap_mapper(circuit_graph,
                coupling_graph,
                initial_layout=None,
                trials=20,
                seed=None):
    """Map a DAGCircuit onto a CouplingGraph using swap gates.

    Args:
        circuit_graph (DAGCircuit): input DAG circuit
        coupling_graph (CouplingGraph): coupling graph to map onto
        initial_layout (Layout): dict {(str, int): (str, int)}
            from qubits of circuit_graph to qubits of coupling_graph (optional)
        trials (int): number of trials.
        seed (int): initial seed.

    Returns:
        DAGCircuit: object containing a circuit equivalent to
        circuit_graph that respects couplings in coupling_graph, and
        a layout dict 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.

    Raises:
        MapperError: if there was any error during the mapping or with the
            parameters.
    """
    if circuit_graph.width() > coupling_graph.size():
        raise MapperError("Not enough qubits in CouplingGraph")

    # Schedule the input circuit
    layerlist = list(circuit_graph.layers())
    logger.debug("schedule:")
    for i, v in enumerate(layerlist):
        logger.debug("    %d: %s", i, v["partition"])

    if initial_layout is not None:
        # update initial_layout from a user given dict{(regname,idx): (regname,idx)}
        # to an expected dict{(reg,idx): (reg,idx)}
        device_register = QuantumRegister(coupling_graph.size(), 'q')
        initial_layout = {(circuit_graph.qregs[k[0]], k[1]):
                          (device_register, v[1])
                          for k, v in initial_layout.items()}
        # Check the input layout
        circ_qubits = circuit_graph.get_qubits()
        coup_qubits = [(QuantumRegister(coupling_graph.size(), 'q'), wire)
                       for wire in coupling_graph.physical_qubits]
        qubit_subset = []
        for k, v in initial_layout.items():
            qubit_subset.append(v)
            if k not in circ_qubits:
                raise MapperError("initial_layout qubit %s[%d] not in input "
                                  "DAGCircuit" % (k[0].name, k[1]))
            if v not in coup_qubits:
                raise MapperError("initial_layout qubit %s[%d] not in input "
                                  "CouplingGraph" % (v[0].name, v[1]))
    else:
        # Supply a default layout
        qubit_subset = [(QuantumRegister(coupling_graph.size(), 'q'), wire)
                        for wire in coupling_graph.physical_qubits]
        qubit_subset = qubit_subset[0:circuit_graph.width()]
        initial_layout = {
            a: b
            for a, b in zip(circuit_graph.get_qubits(), qubit_subset)
        }

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

    # Construct an empty DAGCircuit with one qreg "q"
    # and the same set of cregs as the input circuit
    dagcircuit_output = DAGCircuit()
    dagcircuit_output.name = circuit_graph.name
    dagcircuit_output.add_qreg(QuantumRegister(coupling_graph.size(), "q"))
    for creg in circuit_graph.cregs.values():
        dagcircuit_output.add_creg(creg)

    # Make a trivial wire mapping between the subcircuits
    # returned by swap_mapper_layer_update and the circuit
    # we are building
    identity_wire_map = {}
    q = QuantumRegister(coupling_graph.size(), 'q')
    for j in range(coupling_graph.size()):
        identity_wire_map[(q, j)] = (q, j)
    for creg in circuit_graph.cregs.values():
        for j in range(creg.size):
            identity_wire_map[(creg, j)] = (creg, j)

    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_circ, best_d, best_layout, trivial_flag \
            = layer_permutation(layer["partition"], layout,
                                qubit_subset, coupling_graph, trials, seed)
        logger.debug("swap_mapper: layer %d", i)
        logger.debug("swap_mapper: success_flag=%s,best_d=%s,trivial_flag=%s",
                     success_flag, str(best_d), trivial_flag)

        # If this fails, try one gate at a time in this layer
        if not success_flag:
            logger.debug(
                "swap_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_circ, best_d, best_layout, trivial_flag \
                    = layer_permutation(serial_layer["partition"],
                                        layout, qubit_subset, coupling_graph,
                                        trials, seed)
                logger.debug("swap_mapper: layer %d, sublayer %d", i, j)
                logger.debug(
                    "swap_mapper: success_flag=%s,best_d=%s,"
                    "trivial_flag=%s", success_flag, str(best_d), trivial_flag)

                # Give up if we fail again
                if not success_flag:
                    raise MapperError("swap_mapper failed: " +
                                      "layer %d, sublayer %d" % (i, j) +
                                      ", \"%s\"" % serial_layer["graph"].qasm(
                                          no_decls=True, aliases=layout))

                # 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("swap_mapper: skip to next sublayer")
                    continue

                # Update the record of qubit positions for each inner iteration
                layout = best_layout
                # Update the QASM
                dagcircuit_output.compose_back(
                    swap_mapper_layer_update(j, first_layer, best_layout,
                                             best_d, best_circ,
                                             serial_layerlist, coupling_graph),
                    identity_wire_map)
                # Update initial layout
                if first_layer:
                    initial_layout = layout
                    first_layer = False

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

            # Update the QASM
            dagcircuit_output.compose_back(
                swap_mapper_layer_update(i, first_layer, best_layout, best_d,
                                         best_circ, layerlist, coupling_graph),
                identity_wire_map)
            # Update initial layout
            if first_layer:
                initial_layout = layout
                first_layer = False

    # 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
    if first_layer:
        layout = initial_layout
        for i, layer in enumerate(layerlist):
            dagcircuit_output.compose_back(layer["graph"], layout)

    return dagcircuit_output, initial_layout
Exemplo n.º 3
0
    def run(self, dag):
        """Return a new circuit that has been optimized."""
        runs = dag.collect_runs(["u1", "u2", "u3", "id"])
        for run in runs:
            right_name = "u1"
            right_parameters = (0, 0, 0)  # (theta, phi, lambda)

            for current_node in run:
                left_name = current_node.name
                if (current_node.condition is not None
                        or len(current_node.qargs) != 1
                        or left_name not in ["u1", "u2", "u3", "id"]):
                    raise TranspilerError("internal error")
                if left_name == "u1":
                    left_parameters = (0, 0, current_node.op.params[0])
                elif left_name == "u2":
                    left_parameters = (np.pi / 2, current_node.op.params[0],
                                       current_node.op.params[1])
                elif left_name == "u3":
                    left_parameters = tuple(current_node.op.params)
                else:
                    left_name = "u1"  # replace id with u1
                    left_parameters = (0, 0, 0)
                # If there are any sympy objects coming from the gate convert
                # to numpy.
                left_parameters = tuple([float(x) for x in left_parameters])
                # Compose gates
                name_tuple = (left_name, right_name)
                if name_tuple == ("u1", "u1"):
                    # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2)
                    right_parameters = (0, 0, right_parameters[2] +
                                        left_parameters[2])
                elif name_tuple == ("u1", "u2"):
                    # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2)
                    right_parameters = (np.pi / 2, right_parameters[1] +
                                        left_parameters[2],
                                        right_parameters[2])
                elif name_tuple == ("u2", "u1"):
                    # u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2)
                    right_name = "u2"
                    right_parameters = (np.pi / 2, left_parameters[1],
                                        right_parameters[2] +
                                        left_parameters[2])
                elif name_tuple == ("u1", "u3"):
                    # u1(lambda1) * u3(theta2, phi2, lambda2) =
                    #     u3(theta2, phi2 + lambda1, lambda2)
                    right_parameters = (right_parameters[0],
                                        right_parameters[1] +
                                        left_parameters[2],
                                        right_parameters[2])
                elif name_tuple == ("u3", "u1"):
                    # u3(theta1, phi1, lambda1) * u1(lambda2) =
                    #     u3(theta1, phi1, lambda1 + lambda2)
                    right_name = "u3"
                    right_parameters = (left_parameters[0], left_parameters[1],
                                        right_parameters[2] +
                                        left_parameters[2])
                elif name_tuple == ("u2", "u2"):
                    # Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) =
                    #    Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2),
                    # u2(phi1, lambda1) * u2(phi2, lambda2) =
                    #    u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2)
                    right_name = "u3"
                    right_parameters = (np.pi - left_parameters[2] -
                                        right_parameters[1],
                                        left_parameters[1] + np.pi / 2,
                                        right_parameters[2] + np.pi / 2)
                elif name_tuple[1] == "nop":
                    right_name = left_name
                    right_parameters = left_parameters
                else:
                    # For composing u3's or u2's with u3's, use
                    # u2(phi, lambda) = u3(pi/2, phi, lambda)
                    # together with the qiskit.mapper.compose_u3 method.
                    right_name = "u3"
                    # Evaluate the symbolic expressions for efficiency
                    right_parameters = Optimize1qGates.compose_u3(
                        left_parameters[0], left_parameters[1],
                        left_parameters[2], right_parameters[0],
                        right_parameters[1], right_parameters[2])
                    # Why evalf()? This program:
                    #   OPENQASM 2.0;
                    #   include "qelib1.inc";
                    #   qreg q[2];
                    #   creg c[2];
                    #   u3(0.518016983430947*pi,1.37051598592907*pi,1.36816383603222*pi) q[0];
                    #   u3(1.69867232277986*pi,0.371448347747471*pi,0.461117217930936*pi) q[0];
                    #   u3(0.294319836336836*pi,0.450325871124225*pi,1.46804720442555*pi) q[0];
                    #   measure q -> c;
                    # took >630 seconds (did not complete) to optimize without
                    # calling evalf() at all, 19 seconds to optimize calling
                    # evalf() AFTER compose_u3, and 1 second to optimize
                    # calling evalf() BEFORE compose_u3.
                # 1. Here down, when we simplify, we add f(theta) to lambda to
                # correct the global phase when f(theta) is 2*pi. This isn't
                # necessary but the other steps preserve the global phase, so
                # we continue in that manner.
                # 2. The final step will remove Z rotations by 2*pi.
                # 3. Note that is_zero is true only if the expression is exactly
                # zero. If the input expressions have already been evaluated
                # then these final simplifications will not occur.
                # TODO After we refactor, we should have separate passes for
                # exact and approximate rewriting.

                # Y rotation is 0 mod 2*pi, so the gate is a u1
                if np.mod(right_parameters[0], (2 * np.pi)) == 0 \
                        and right_name != "u1":
                    right_name = "u1"
                    right_parameters = (0, 0, right_parameters[1] +
                                        right_parameters[2] +
                                        right_parameters[0])
                # Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2
                if right_name == "u3":
                    # theta = pi/2 + 2*k*pi
                    if np.mod((right_parameters[0] - np.pi / 2),
                              (2 * np.pi)) == 0:
                        right_name = "u2"
                        right_parameters = (np.pi / 2, right_parameters[1],
                                            right_parameters[2] +
                                            (right_parameters[0] - np.pi / 2))
                    # theta = -pi/2 + 2*k*pi
                    if np.mod((right_parameters[0] + np.pi / 2),
                              (2 * np.pi)) == 0:
                        right_name = "u2"
                        right_parameters = (np.pi / 2,
                                            right_parameters[1] + np.pi,
                                            right_parameters[2] - np.pi +
                                            (right_parameters[0] + np.pi / 2))
                # u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase)
                if right_name == "u1" and np.mod(right_parameters[2],
                                                 (2 * np.pi)) == 0:
                    right_name = "nop"

            # Replace the the first node in the run with a dummy DAG which contains a dummy
            # qubit. The name is irrelevant, because substitute_node_with_dag will take care of
            # putting it in the right place.
            run_qarg = (QuantumRegister(1, 'q'), 0)
            new_op = Gate(name="", num_qubits=1, params=[])
            if right_name == "u1":
                new_op = U1Gate(right_parameters[2])
            if right_name == "u2":
                new_op = U2Gate(right_parameters[1], right_parameters[2])
            if right_name == "u3":
                new_op = U3Gate(*right_parameters)

            if right_name != 'nop':
                new_dag = DAGCircuit()
                new_dag.add_qreg(run_qarg[0])
                new_dag.apply_operation_back(new_op, [run_qarg], [])
                dag.substitute_node_with_dag(run[0], new_dag)

            # Delete the other nodes in the run
            for current_node in run[1:]:
                dag.remove_op_node(current_node)
            if right_name == "nop":
                dag.remove_op_node(run[0])

        return dag
Exemplo n.º 4
0
 def test_add_reg_bad_type(self):
     """add_qreg with a classical register is not allowed."""
     dag = DAGCircuit()
     cr = ClassicalRegister(2)
     self.assertRaises(DAGCircuitError, dag.add_qreg, cr)
Exemplo n.º 5
0
    def run(self, dag):
        """
        Runs the BasicSwap pass on `dag`.
        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG.

        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG
        """
        new_dag = DAGCircuit()

        if self.initial_layout is None:
            if self.property_set["layout"]:
                self.initial_layout = self.property_set["layout"]
            else:
                self.initial_layout = Layout.generate_trivial_layout(
                    *dag.qregs.values())

        if len(dag.qubits()) != len(self.initial_layout):
            raise TranspilerError(
                'The layout does not match the amount of qubits in the DAG')

        if len(self.coupling_map.physical_qubits) != len(self.initial_layout):
            raise TranspilerError(
                "Mappers require to have the layout to be the same size as the coupling map"
            )

        current_layout = self.initial_layout.copy()

        for layer in dag.serial_layers():
            subdag = layer['graph']

            for gate in subdag.twoQ_gates():
                physical_q0 = current_layout[gate.qargs[0]]
                physical_q1 = current_layout[gate.qargs[1]]
                if self.coupling_map.distance(physical_q0, physical_q1) != 1:
                    # Insert a new layer with the SWAP(s).
                    swap_layer = DAGCircuit()

                    path = self.coupling_map.shortest_undirected_path(
                        physical_q0, physical_q1)
                    for swap in range(len(path) - 2):
                        connected_wire_1 = path[swap]
                        connected_wire_2 = path[swap + 1]

                        qubit_1 = current_layout[connected_wire_1]
                        qubit_2 = current_layout[connected_wire_2]

                        # create qregs
                        for qreg in current_layout.get_registers():
                            if qreg not in swap_layer.qregs.values():
                                swap_layer.add_qreg(qreg)

                        # create the swap operation
                        swap_layer.apply_operation_back(
                            SwapGate(), qargs=[qubit_1, qubit_2], cargs=[])

                    # layer insertion
                    edge_map = current_layout.combine_into_edge_map(
                        self.initial_layout)
                    new_dag.compose_back(swap_layer, edge_map)

                    # update current_layout
                    for swap in range(len(path) - 2):
                        current_layout.swap(path[swap], path[swap + 1])

            edge_map = current_layout.combine_into_edge_map(
                self.initial_layout)
            new_dag.extend_back(subdag, edge_map)

        return new_dag
Exemplo n.º 6
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 = 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

        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, 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:
                        logger.debug("mapper: skip to next sublayer")
                        continue

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

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

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

        # 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",
                     pformat(self.trivial_layout))
        logger.debug("mapper: layout = %s", pformat(layout))
        last_edgemap = layout.combine_into_edge_map(self.trivial_layout)
        logger.debug("mapper: last_edgemap = %s", pformat(last_edgemap))

        return dagcircuit_output
Exemplo n.º 7
0
def swap_mapper(circuit_graph,
                coupling_graph,
                initial_layout=None,
                basis="cx,u1,u2,u3,id",
                trials=20,
                seed=None):
    """Map a DAGCircuit onto a CouplingGraph using swap gates.

    Args:
        circuit_graph (DAGCircuit): input DAG circuit
        coupling_graph (CouplingGraph): coupling graph to map onto
        initial_layout (dict): dict from qubits of circuit_graph to qubits
            of coupling_graph (optional)
        basis (str): basis string specifying basis of output DAGCircuit
        trials (int): number of trials.
        seed (int): initial seed.

    Returns:
        DAGCircuit: object containing a circuit equivalent to
        circuit_graph that respects couplings in coupling_graph, and
        a layout dict 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. Finally, returned is the final layer qubit
        permutation that is needed to add measurements back in.

    Raises:
        MapperError: if there was any error during the mapping or with the
            parameters.
    """
    if circuit_graph.width() > coupling_graph.size():
        raise MapperError("Not enough qubits in CouplingGraph")

    # Schedule the input circuit
    layerlist = list(circuit_graph.layers())
    logger.debug("schedule:")
    for i, v in enumerate(layerlist):
        logger.debug("    %d: %s", i, v["partition"])

    if initial_layout is not None:
        # Check the input layout
        circ_qubits = circuit_graph.get_qubits()
        coup_qubits = coupling_graph.get_qubits()
        qubit_subset = []
        for k, v in initial_layout.items():
            qubit_subset.append(v)
            if k not in circ_qubits:
                raise MapperError("initial_layout qubit %s[%d] not in input "
                                  "DAGCircuit" % (k[0], k[1]))
            if v not in coup_qubits:
                raise MapperError("initial_layout qubit %s[%d] not in input "
                                  "CouplingGraph" % (v[0], v[1]))
    else:
        # Supply a default layout
        qubit_subset = coupling_graph.get_qubits()
        qubit_subset = qubit_subset[0:circuit_graph.width()]
        initial_layout = {
            a: b
            for a, b in zip(circuit_graph.get_qubits(), qubit_subset)
        }

    # Find swap circuit to preceed to each layer of input circuit
    layout = initial_layout.copy()
    layout_max_index = max(map(lambda x: x[1] + 1, layout.values()))

    # Construct an empty DAGCircuit with one qreg "q"
    # and the same set of cregs as the input circuit
    dagcircuit_output = DAGCircuit()
    dagcircuit_output.name = circuit_graph.name
    dagcircuit_output.add_qreg("q", layout_max_index)
    for name, size in circuit_graph.cregs.items():
        dagcircuit_output.add_creg(name, size)

    # Make a trivial wire mapping between the subcircuits
    # returned by swap_mapper_layer_update and the circuit
    # we are building
    identity_wire_map = {}
    for j in range(layout_max_index):
        identity_wire_map[("q", j)] = ("q", j)
    for name, size in circuit_graph.cregs.items():
        for j in range(size):
            identity_wire_map[(name, j)] = (name, j)

    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_circ, best_d, best_layout, trivial_flag \
            = layer_permutation(layer["partition"], layout,
                                qubit_subset, coupling_graph, trials, seed)
        logger.debug("swap_mapper: layer %d", i)
        logger.debug("swap_mapper: success_flag=%s,best_d=%s,trivial_flag=%s",
                     success_flag, str(best_d), trivial_flag)

        # If this fails, try one gate at a time in this layer
        if not success_flag:
            logger.debug(
                "swap_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_circ, best_d, best_layout, trivial_flag \
                    = layer_permutation(serial_layer["partition"],
                                        layout, qubit_subset, coupling_graph,
                                        trials, seed)
                logger.debug("swap_mapper: layer %d, sublayer %d", i, j)
                logger.debug(
                    "swap_mapper: success_flag=%s,best_d=%s,"
                    "trivial_flag=%s", success_flag, str(best_d), trivial_flag)

                # Give up if we fail again
                if not success_flag:
                    raise MapperError("swap_mapper failed: " +
                                      "layer %d, sublayer %d" % (i, j) +
                                      ", \"%s\"" % serial_layer["graph"].qasm(
                                          no_decls=True, aliases=layout))

                # 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("swap_mapper: skip to next sublayer")
                    continue

                # Update the record of qubit positions for each inner iteration
                layout = best_layout
                # Update the QASM
                dagcircuit_output.compose_back(
                    swap_mapper_layer_update(j, first_layer, best_layout,
                                             best_d, best_circ,
                                             serial_layerlist),
                    identity_wire_map)
                # Update initial layout
                if first_layer:
                    initial_layout = layout
                    first_layer = False

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

            # Update the QASM
            dagcircuit_output.compose_back(
                swap_mapper_layer_update(i, first_layer, best_layout, best_d,
                                         best_circ, layerlist),
                identity_wire_map)
            # Update initial layout
            if first_layer:
                initial_layout = layout
                first_layer = False

    # This is the final layout that we need to correctly replace
    # any measurements that needed to be removed before the swap
    last_layout = layout

    # 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
    if first_layer:
        layout = initial_layout
        for i, layer in enumerate(layerlist):
            dagcircuit_output.compose_back(layer["graph"], layout)

    # Parse openqasm_output into DAGCircuit object
    dag_unrolled = DagUnroller(dagcircuit_output, DAGBackend(basis.split(",")))
    dagcircuit_output = dag_unrolled.expand_gates()
    return dagcircuit_output, initial_layout, last_layout
Exemplo n.º 8
0
    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.
            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(
                "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)

        idle_before = {q: 0 for q in dag.qubits + dag.clbits}
        bit_indices = {bit: index for index, bit in enumerate(dag.qubits)}
        for node in reversed(list(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

            # since this is alap scheduling, node is scheduled in reversed topological ordering
            # and nodes are packed from the very end of the circuit.
            # the physical meaning of t0 and t1 is flipped here.
            if isinstance(node.op, self.CONDITIONAL_SUPPORTED):
                t0q = max(idle_before[q] for q in node.qargs)
                if node.op.condition_bits:
                    # conditional is bit tricky due to conditional_latency
                    t0c = max(idle_before[c] for c in node.op.condition_bits)
                    # Assume following case (t0c > t0q):
                    #
                    #                |t0q
                    # Q ░░░░░░░░░░░░░▒▒▒
                    # C ░░░░░░░░▒▒▒▒▒▒▒▒
                    #           |t0c
                    #
                    # In this case, there is no actual clbit read before gate.
                    #
                    #             |t0q' = t0c - conditional_latency
                    # Q ░░░░░░░░▒▒▒░░▒▒▒
                    # C ░░░░░░▒▒▒▒▒▒▒▒▒▒
                    #         |t1c' = t0c + conditional_latency
                    #
                    # rather than naively doing
                    #
                    #        |t1q' = t0c + duration
                    # Q ░░░░░▒▒▒░░░░░▒▒▒
                    # C ░░▒▒░░░░▒▒▒▒▒▒▒▒
                    #     |t1c' = t0c + duration + conditional_latency
                    #
                    t0 = max(t0q, t0c - op_duration)
                    t1 = t0 + op_duration
                    for clbit in node.op.condition_bits:
                        idle_before[clbit] = t1 + self.conditional_latency
                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 ALAP scheduler."
                    )

                if isinstance(node.op, Measure):
                    # clbit time is always right (alap) justified
                    t0 = max(idle_before[bit]
                             for bit in node.qargs + node.cargs)
                    t1 = t0 + op_duration
                    #
                    #        |t1 = t0 + duration
                    # Q ░░░░░▒▒▒▒▒▒▒▒▒▒▒
                    # C ░░░░░░░░░▒▒▒▒▒▒▒
                    #            |t0 + (duration - clbit_write_latency)
                    #
                    for clbit in node.cargs:
                        idle_before[clbit] = t0 + (op_duration -
                                                   self.clbit_write_latency)
                else:
                    # It happens to be directives such as barrier
                    t0 = max(idle_before[bit]
                             for bit in node.qargs + node.cargs)
                    t1 = t0 + op_duration

            for bit in node.qargs:
                delta = t0 - idle_before[bit]
                if delta > 0:
                    new_dag.apply_operation_front(Delay(delta, time_unit),
                                                  [bit], [])
                idle_before[bit] = t1

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

        circuit_duration = max(idle_before.values())
        for bit, before in idle_before.items():
            delta = circuit_duration - before
            if not (delta > 0 and isinstance(bit, Qubit)):
                continue
            new_dag.apply_operation_front(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
Exemplo n.º 9
0
    def run(self, dag):
        """Run the BasicSwap pass on `dag`.

        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG.

        Raises:
            TranspilerError: if the coupling map or the layout are not
            compatible with the DAG.
        """

        new_dag = DAGCircuit()

        if len(dag.qregs) != 1 or dag.qregs.get('q', None) is None:
            raise TranspilerError('Basic swap runs on physical circuits only')

        if len(dag.qubits()) > len(self.coupling_map.physical_qubits):
            raise TranspilerError('The layout does not match the amount of qubits in the DAG')

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

        for layer in dag.serial_layers():
            subdag = layer['graph']

            for gate in subdag.twoQ_gates():
                physical_q0 = current_layout[gate.qargs[0]]
                physical_q1 = current_layout[gate.qargs[1]]
                if self.coupling_map.distance(physical_q0, physical_q1) != 1:
                    # Insert a new layer with the SWAP(s).
                    swap_layer = DAGCircuit()
                    swap_layer.add_qreg(canonical_register)

                    path = self.coupling_map.shortest_undirected_path(physical_q0, physical_q1)
                    for swap in range(len(path) - 2):
                        connected_wire_1 = path[swap]
                        connected_wire_2 = path[swap + 1]

                        qubit_1 = current_layout[connected_wire_1]
                        qubit_2 = current_layout[connected_wire_2]

                        # create the swap operation
                        swap_layer.apply_operation_back(SwapGate(),
                                                        qargs=[qubit_1, qubit_2],
                                                        cargs=[])

                    # layer insertion
                    edge_map = current_layout.combine_into_edge_map(trivial_layout)
                    new_dag.compose_back(swap_layer, edge_map)

                    # update current_layout
                    for swap in range(len(path) - 2):
                        current_layout.swap(path[swap], path[swap + 1])

            edge_map = current_layout.combine_into_edge_map(trivial_layout)
            new_dag.extend_back(subdag, edge_map)

        return new_dag
Exemplo n.º 10
0
    def run(self, dag):
        """
        Runs the BasicMapper pass on `dag`.
        Args:
            dag (DAGCircuit): DAG to map.

        Returns:
            DAGCircuit: A mapped DAG.
        """
        new_dag = DAGCircuit()

        if self.initial_layout is None:
            # create a one-to-one layout
            self.initial_layout = Layout()
            physical_qubit = 0
            for qreg in dag.qregs.values():
                for index in range(qreg.size):
                    self.initial_layout[(qreg, index)] = physical_qubit
                    physical_qubit += 1
        current_layout = copy(self.initial_layout)

        for layer in dag.serial_layers():
            subdag = layer['graph']

            for a_cx in subdag.get_cnot_nodes():
                physical_q0 = current_layout[a_cx['qargs'][0]]
                physical_q1 = current_layout[a_cx['qargs'][1]]
                if self.coupling_map.distance(physical_q0, physical_q1) != 1:
                    # Insert a new layer with the SWAP(s).
                    swap_layer = DAGCircuit()

                    path = self.coupling_map.shortest_undirected_path(
                        physical_q0, physical_q1)
                    for swap in range(len(path) - 2):
                        connected_wire_1 = path[swap]
                        connected_wire_2 = path[swap + 1]

                        qubit_1 = current_layout[connected_wire_1]
                        qubit_2 = current_layout[connected_wire_2]

                        # create the involved registers
                        if qubit_1[0] not in swap_layer.qregs.values():
                            swap_layer.add_qreg(qubit_1[0])
                        if qubit_2[0] not in swap_layer.qregs.values():
                            swap_layer.add_qreg(qubit_2[0])

                        # create the swap operation
                        swap_layer.add_basis_element('swap', 2, 0, 0)
                        swap_layer.apply_operation_back(
                            self.swap_gate(qubit_1, qubit_2),
                            qargs=[qubit_1, qubit_2])

                    # layer insertion
                    edge_map = current_layout.combine_into_edge_map(
                        self.initial_layout)
                    new_dag.compose_back(swap_layer, edge_map)

                    # update current_layout
                    for swap in range(len(path) - 2):
                        current_layout.swap(path[swap], path[swap + 1])

            edge_map = current_layout.combine_into_edge_map(
                self.initial_layout)
            new_dag.extend_back(subdag, edge_map)

        return new_dag
Exemplo n.º 11
0
    def run(self, dag):
        """Run the Unroller pass on `dag`.

        Args:
            dag (DAGCircuit): input dag

        Raises:
            QiskitError: if unable to unroll given the basis due to undefined
            decomposition rules (such as a bad basis) or excessive recursion.

        Returns:
            DAGCircuit: output unrolled dag
        """
        if self.basis is None:
            return dag

        # Walk through the DAG and expand each non-basis node
        for node in dag.op_nodes():
            basic_insts = ['measure', 'reset', 'barrier', 'snapshot']
            if node.name in basic_insts:
                # TODO: this is legacy behavior.Basis_insts should be removed that these
                #  instructions should be part of the device-reported basis. Currently, no
                #  backend reports "measure", for example.
                continue
            if node.name in self.basis:  # If already a base, ignore.
                if isinstance(node.op, ControlledGate) and node.op._open_ctrl:
                    pass
                else:
                    continue

            # TODO: allow choosing other possible decompositions
            try:
                rule = node.op.definition
            except TypeError as err:
                raise QiskitError('Error decomposing node {}: {}'.format(
                    node.name, err))

            # Isometry gates definitions can have widths smaller than that of the
            # original gate, in which case substitute_node will raise. Fall back
            # to substitute_node_with_dag if an the width of the definition is
            # different that the width of the node.
            while rule and len(rule) == 1 and len(node.qargs) == len(
                    rule[0][1]):
                if rule[0][0].name in self.basis:
                    dag.substitute_node(node, rule[0][0], inplace=True)
                    break
                try:
                    rule = rule[0][0].definition
                except TypeError as err:
                    raise QiskitError('Error decomposing node {}: {}'.format(
                        node.name, err))

            else:
                if not rule:
                    if rule == []:  # empty node
                        dag.remove_op_node(node)
                        continue
                    # opaque node
                    raise QiskitError(
                        "Cannot unroll the circuit to the given basis, %s. "
                        "No rule to expand instruction %s." %
                        (str(self.basis), node.op.name))

                # hacky way to build a dag on the same register as the rule is defined
                # TODO: need anonymous rules to address wires by index
                decomposition = DAGCircuit()
                qregs = {qb.register for inst in rule for qb in inst[1]}
                cregs = {cb.register for inst in rule for cb in inst[2]}
                for qreg in qregs:
                    decomposition.add_qreg(qreg)
                for creg in cregs:
                    decomposition.add_creg(creg)
                for inst in rule:
                    decomposition.apply_operation_back(*inst)

                unrolled_dag = self.run(
                    decomposition)  # recursively unroll ops
                dag.substitute_node_with_dag(node, unrolled_dag)

        return dag
Exemplo n.º 12
0
    def _layer_permutation(self, layer_partition, layout, qubit_subset,
                           coupling, trials, seed=None):
        """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.

        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.
        seed (int): Optional seed for the random number generator. If it is
            None we do not reseed.

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

        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. The trivial_flag is set if the layer
        has no multi-qubit gates.

        Raises:
            TranspilerError: if anything went wrong.
        """
        if seed is not None:
            np.random.seed(seed)

        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])
            circ.add_basis_element("swap", 2)
            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_circuit = None  # initialize best swap circuit
        best_layout = None  # initialize best final layout
        for trial in range(trials):
            logger.debug("layer_permutation: trial %s", trial)
            trial_layout = layout.copy()
            trial_circuit = DAGCircuit()  # SWAP circuit for this trial
            for register in trial_layout.get_virtual_bits().keys():
                if register[0] not in trial_circuit.qregs.values():
                    trial_circuit.add_qreg(register[0])

            # Compute randomized distance
            xi = {}  # pylint: disable=invalid-name
            for i in range(num_qubits):
                xi[i] = {}
            for i in range(num_qubits):
                for j in range(i, num_qubits):
                    scale = 1 + np.random.normal(0, 1 / num_qubits)
                    xi[i][j] = scale * coupling.distance(i, j) ** 2
                    xi[j][i] = xi[i][j]

            slice_circuit = DAGCircuit()  # circuit for this swap slice
            for register in trial_layout.get_virtual_bits().keys():
                if register[0] not in slice_circuit.qregs.values():
                    slice_circuit.add_qreg(register[0])
            slice_circuit.add_basis_element("swap", 2)

            # Loop over depths from 1 up to a maximum depth
            depth_step = 1
            depth_max = 2 * num_qubits + 1
            while depth_step < depth_max:
                qubit_set = set(qubit_subset)
                # While there are still qubits available
                while qubit_set:
                    # Compute the objective function
                    min_cost = sum([xi[trial_layout[g[0]]][trial_layout[g[1]]] for g in gates])
                    # Try to decrease objective function
                    cost_reduced = False

                    # Loop over edges of coupling graph
                    for edge in coupling.get_edges():
                        qubits = [trial_layout[e] for e in edge]
                        # Are the qubits available?
                        if qubits[0] in qubit_set and qubits[1] in qubit_set:
                            # Try this edge to reduce the cost
                            new_layout = trial_layout.copy()
                            new_layout.swap(edge[0], edge[1])

                            # Compute the objective function
                            new_cost = sum([xi[new_layout[g[0]]][new_layout[g[1]]] for g in gates])
                            # Record progress if we succceed
                            if new_cost < min_cost:
                                logger.debug("layer_permutation: min_cost "
                                             "improved to %s", min_cost)
                                cost_reduced = True
                                min_cost = new_cost
                                optimal_layout = new_layout
                                optimal_edge = qubits

                    # Were there any good swap choices?
                    if cost_reduced:
                        qubit_set.remove(optimal_edge[0])
                        qubit_set.remove(optimal_edge[1])
                        trial_layout = optimal_layout
                        slice_circuit.apply_operation_back(
                            SwapGate(optimal_edge[0],
                                     optimal_edge[1]))
                        logger.debug("layer_permutation: swap the pair %s",
                                     pformat(optimal_edge))
                    else:
                        break

                # We have either run out of swap pairs to try or
                # failed to improve the cost.

                # Compute the coupling graph distance
                dist = sum([coupling.distance(trial_layout[g[0]],
                                              trial_layout[g[1]])
                            for g in gates])
                logger.debug("layer_permutation: new swap distance = %s", dist)
                # If all gates can be applied now, we are finished.
                # Otherwise we need to consider a deeper swap circuit
                if dist == len(gates):
                    logger.debug("layer_permutation: all gates can be "
                                 "applied now in this layer")
                    trial_circuit.extend_back(slice_circuit)
                    break

                # Increment the depth
                depth_step += 1
                logger.debug("layer_permutation: increment depth to %s", depth_step)

            # Either we have succeeded at some depth d < dmax or failed
            dist = sum([coupling.distance(trial_layout[g[0]],
                                          trial_layout[g[1]])
                        for g in gates])
            logger.debug("layer_permutation: final distance for this trial = %s", dist)
            if dist == len(gates):
                if depth_step < best_depth:
                    logger.debug("layer_permutation: got circuit with improved depth %s",
                                 depth_step)
                    best_circuit = trial_circuit
                    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_circuit is None:
            logger.debug("layer_permutation: failed!")
            return False, None, None, None, False

        # Otherwise, we return our result for this layer
        logger.debug("layer_permutation: success!")
        return True, best_circuit, best_depth, best_layout, False
Exemplo n.º 13
0
    def run(self, dag):
        """
        Apply this transpiler pass to a circuit in directed acyclic graph representation.

        :param qiskit.dagcircuit.DAGCircuit dag: The circuit to transpile.
        :returns: The transpiled circuit.
        :rtype: qiskit.dagcircuit.DAGCircuit
        :raises qiskit.exceptions.QiskitError: If the circuit contains a parametrized non-basis gate, or contains a gate that cannot be unrolled.
        """
        # Walk through the DAG and expand each non-basis node
        for node in dag.op_nodes():
            basic_insts = ["measure", "reset", "barrier", "snapshot"]
            if node.name in basic_insts:
                # TODO: this is legacy behavior.Basis_insts should be removed that these
                #  instructions should be part of the device-reported basis. Currently, no
                #  backend reports "measure", for example.
                continue
            if node.name in [
                    "id",
                    "r",
                    "sx",
                    "sy",
                    "x",
                    "y",
                    "rz",
                    "ms2",
            ]:  # If already a base, ignore.
                continue

            try:
                rule = self._get_rule(node)
            except TypeError as err:
                if any(
                        isinstance(p, ParameterExpression)
                        for p in node.op.params):
                    raise QiskitError(
                        "Unrolling gates parameterized by expressions "
                        "is currently unsupported.")
                raise QiskitError("Error decomposing node {}: {}".format(
                    node.name, err))

            if not rule:
                raise QiskitError(
                    "Cannot unroll the circuit to trapped ion gates. "
                    "No rule to expand instruction %s." % node.op.name)

            # hacky way to build a dag on the same register as the rule is defined
            # TODO: need anonymous rules to address wires by index
            decomposition = DAGCircuit()
            qregs = {qb.register for inst in rule for qb in inst[1]}
            cregs = {cb.register for inst in rule for cb in inst[2]}
            for qreg in qregs:
                decomposition.add_qreg(qreg)
            for creg in cregs:
                decomposition.add_creg(creg)
            for inst in rule:
                decomposition.apply_operation_back(*inst)

            unrolled_dag = self.run(decomposition)  # recursively unroll ops
            dag.substitute_node_with_dag(node, unrolled_dag)
        return dag
    def run(self, dag):
        """Return a circuit with a barrier before last measurments."""

        # Collect DAG nodes which are followed only by barriers or other measures.
        final_op_types = ['measure', 'barrier']
        final_ops = []
        for candidate_op in dag.named_nodes(*final_op_types):
            is_final_op = True
            for _, child_successors in dag.bfs_successors(candidate_op):
                if any(dag.multi_graph.node[suc]['type'] == 'op' and
                       dag.multi_graph.node[suc]['op'].name not in final_op_types
                       for suc in child_successors):
                    is_final_op = False
                    break

            if is_final_op:
                final_ops.append(candidate_op)

        if not final_ops:
            return dag

        # Create a layer with the barrier and add registers from the original dag.
        barrier_layer = DAGCircuit()
        for qreg in dag.qregs.values():
            barrier_layer.add_qreg(qreg)
        for creg in dag.cregs.values():
            barrier_layer.add_creg(creg)

        final_qubits = set(dag.multi_graph.node[final_op]['qargs'][0]
                           for final_op in final_ops)

        new_barrier_id = barrier_layer.apply_operation_back(Barrier(qubits=final_qubits))

        # Preserve order of final ops collected earlier from the original DAG.
        ordered_node_ids = [node_id for node_id in dag.node_nums_in_topological_order()
                            if node_id in set(final_ops)]
        ordered_final_nodes = [dag.multi_graph.node[node] for node in ordered_node_ids]

        # Move final ops to the new layer and append the new layer to the DAG.
        for final_node in ordered_final_nodes:
            barrier_layer.apply_operation_back(final_node['op'])

        for final_op in final_ops:
            dag._remove_op_node(final_op)

        # Check to see if the new barrier added to the DAG is equivalent to any
        # existing barriers, and if so, consolidate the two.
        our_ancestors = barrier_layer.ancestors(new_barrier_id)
        our_descendants = barrier_layer.descendants(new_barrier_id)
        our_qubits = final_qubits

        existing_barriers = barrier_layer.named_nodes('barrier')
        existing_barriers.remove(new_barrier_id)

        for candidate_barrier in existing_barriers:
            their_ancestors = barrier_layer.ancestors(candidate_barrier)
            their_descendants = barrier_layer.descendants(candidate_barrier)
            their_qubits = set(barrier_layer.multi_graph.nodes[candidate_barrier]['op'].qargs)

            if (
                    not our_qubits.isdisjoint(their_qubits)
                    and our_ancestors.isdisjoint(their_descendants)
                    and our_descendants.isdisjoint(their_ancestors)
            ):
                merge_barrier = Barrier(qubits=(our_qubits | their_qubits))
                merge_barrier_id = barrier_layer.apply_operation_front(merge_barrier)

                our_ancestors = our_ancestors | their_ancestors
                our_descendants = our_descendants | their_descendants

                barrier_layer._remove_op_node(candidate_barrier)
                barrier_layer._remove_op_node(new_barrier_id)

                new_barrier_id = merge_barrier_id

        dag.extend_back(barrier_layer)

        return dag
Exemplo n.º 15
0
    def run(self, dag):
        """iterate over each block and replace it with an equivalent Unitary
        on the same wires.
        """
        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)

        # compute ordered indices for the global circuit wires
        global_index_map = {wire: idx for idx, wire in enumerate(dag.qubits())}

        blocks = self.property_set['block_list']
        # just to make checking if a node is in any block easier
        all_block_nodes = {nd for bl in blocks for nd in bl}

        for node in dag.topological_op_nodes():
            if node not in all_block_nodes:
                # need to add this node to find out where in the list it goes
                preds = [
                    nd for nd in dag.predecessors(node) if nd.type == 'op'
                ]

                block_count = 0
                while preds:
                    if block_count < len(blocks):
                        block = blocks[block_count]

                        # if any of the predecessors are in the block, remove them
                        preds = [p for p in preds if p not in block]
                    else:
                        # should never occur as this would mean not all
                        # nodes before this one topologically had been added
                        # so not all predecessors were removed
                        raise TranspilerError(
                            "Not all predecessors removed due to error"
                            " in topological order")

                    block_count += 1

                # we have now seen all predecessors
                # so update the blocks list to include this block
                blocks = blocks[:block_count] + [[node]] + blocks[block_count:]

        # create the dag from the updated list of blocks
        basis_gate_name = self.decomposer.gate.name
        for block in blocks:

            if len(block) == 1 and block[0].name != 'cx':
                # an intermediate node that was added into the overall list
                new_dag.apply_operation_back(block[0].op, block[0].qargs,
                                             block[0].cargs,
                                             block[0].condition)
            else:
                # find the qubits involved in this block
                block_qargs = set()
                for nd in block:
                    block_qargs |= set(nd.qargs)
                # convert block to a sub-circuit, then simulate unitary and add
                block_width = len(block_qargs)
                q = QuantumRegister(block_width)
                subcirc = QuantumCircuit(q)
                block_index_map = self._block_qargs_to_indices(
                    block_qargs, global_index_map)
                basis_count = 0
                for nd in block:
                    if nd.op.name == basis_gate_name:
                        basis_count += 1
                    subcirc.append(nd.op,
                                   [q[block_index_map[i]] for i in nd.qargs])
                unitary = UnitaryGate(
                    Operator(subcirc))  # simulates the circuit
                if self.force_consolidate or unitary.num_qubits > 2 or \
                        self.decomposer.num_basis_gates(unitary) != basis_count:

                    new_dag.apply_operation_back(
                        unitary,
                        sorted(block_qargs, key=lambda x: block_index_map[x]))
                else:
                    for nd in block:
                        new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs,
                                                     nd.condition)

        return new_dag
    def run(self, dag):
        """Run the CommutativeCancellation pass on `dag`.

        Args:
            dag (DAGCircuit): the DAG to be optimized.

        Returns:
            DAGCircuit: the optimized DAG.

        Raises:
            TranspilerError: when the 1-qubit rotation gates are not found
        """
        var_z_gate = None
        z_var_gates = [
            gate for gate in dag.count_ops().keys() if gate in self._var_z_map
        ]
        if z_var_gates:
            # priortize z gates in circuit
            var_z_gate = self._var_z_map[next(iter(z_var_gates))]
        else:
            z_var_gates = [
                gate for gate in self.basis if gate in self._var_z_map
            ]
            if z_var_gates:
                var_z_gate = self._var_z_map[next(iter(z_var_gates))]

        # Now the gates supported are hard-coded
        q_gate_list = ['cx', 'cy', 'cz', 'h', 'y']

        # Gate sets to be cancelled
        cancellation_sets = defaultdict(lambda: [])

        # Traverse each qubit to generate the cancel dictionaries
        # Cancel dictionaries:
        #  - For 1-qubit gates the key is (gate_type, qubit_id, commutation_set_id),
        #    the value is the list of gates that share the same gate type, qubit, commutation set.
        #  - For 2qbit gates the key: (gate_type, first_qbit, sec_qbit, first commutation_set_id,
        #    sec_commutation_set_id), the value is the list gates that share the same gate type,
        #    qubits and commutation sets.

        for wire in dag.wires:
            wire_commutation_set = self.property_set['commutation_set'][wire]

            for com_set_idx, com_set in enumerate(wire_commutation_set):
                if com_set[0].type in ['in', 'out']:
                    continue
                for node in com_set:
                    num_qargs = len(node.qargs)
                    if num_qargs == 1 and node.name in q_gate_list:
                        cancellation_sets[(node.name, wire,
                                           com_set_idx)].append(node)
                    if num_qargs == 1 and node.name in [
                            'p', 'z', 'u1', 'rz', 't', 's'
                    ]:
                        cancellation_sets[('z_rotation', wire,
                                           com_set_idx)].append(node)
                    if num_qargs == 1 and node.name in ['rx', 'x']:
                        cancellation_sets[('x_rotation', wire,
                                           com_set_idx)].append(node)
                    # Don't deal with Y rotation, because Y rotation doesn't commute with CNOT, so
                    # it should be dealt with by optimized1qgate pass
                    elif num_qargs == 2 and node.qargs[0] == wire:
                        second_qarg = node.qargs[1]
                        q2_key = (node.name, wire, second_qarg, com_set_idx,
                                  self.property_set['commutation_set'][(
                                      node, second_qarg)])
                        cancellation_sets[q2_key].append(node)

        for cancel_set_key in cancellation_sets:
            if cancel_set_key[0] == 'z_rotation' and var_z_gate is None:
                continue
            set_len = len(cancellation_sets[cancel_set_key])
            if set_len > 1 and cancel_set_key[0] in q_gate_list:
                gates_to_cancel = cancellation_sets[cancel_set_key]
                for c_node in gates_to_cancel[:(set_len // 2) * 2]:
                    dag.remove_op_node(c_node)

            elif set_len > 1 and cancel_set_key[0] in [
                    'z_rotation', 'x_rotation'
            ]:
                run = cancellation_sets[cancel_set_key]
                run_qarg = run[0].qargs[0]
                total_angle = 0.0  # lambda
                total_phase = 0.0
                for current_node in run:
                    if (current_node.condition is not None
                            or len(current_node.qargs) != 1
                            or current_node.qargs[0] != run_qarg):
                        raise TranspilerError("internal error")

                    if current_node.name in ['p', 'u1', 'rz', 'rx']:
                        current_angle = float(current_node.op.params[0])
                    elif current_node.name in ['z', 'x']:
                        current_angle = np.pi
                    elif current_node.name == 't':
                        current_angle = np.pi / 4
                    elif current_node.name == 's':
                        current_angle = np.pi / 2

                    # Compose gates
                    total_angle = current_angle + total_angle
                    if current_node.op.definition:
                        total_phase += current_node.op.definition.global_phase

                # Replace the data of the first node in the run
                if cancel_set_key[0] == 'z_rotation':
                    new_op = var_z_gate(total_angle)
                elif cancel_set_key[0] == 'x_rotation':
                    new_op = RXGate(total_angle)

                new_op_phase = 0
                if np.mod(total_angle, (2 * np.pi)) > _CUTOFF_PRECISION:
                    new_qarg = QuantumRegister(1, 'q')
                    new_dag = DAGCircuit()
                    new_dag.add_qreg(new_qarg)
                    new_dag.apply_operation_back(new_op, [new_qarg[0]])
                    dag.substitute_node_with_dag(run[0], new_dag)
                    if new_op.definition:
                        new_op_phase = new_op.definition.global_phase

                dag.global_phase = total_phase - new_op_phase

                # Delete the other nodes in the run
                for current_node in run[1:]:
                    dag.remove_op_node(current_node)

                if np.mod(total_angle, (2 * np.pi)) < _CUTOFF_PRECISION:
                    dag.remove_op_node(run[0])

        return dag
Exemplo n.º 17
0
    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,
                                          node.condition)

            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.")

            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
Exemplo n.º 18
0
    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)
        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_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())):
            # 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 {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 = 0 if isinstance(node.op, Measure) else node.op.duration
            # 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_front(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] = start_time
            for c in node.op.condition_bits:  # conditional op
                clbit_writeable[c] = max(stop_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
Exemplo n.º 19
0
def _layer_permutation(layer_partition, layout, qubit_subset, coupling, trials,
                       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).
        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.
        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)

    # 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", 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()
        circ.add_qreg(canonical_register)
        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 qubit in layout.get_virtual_bits().keys():
        if qubit.register not in trial_circuit.qregs.values():
            trial_circuit.add_qreg(qubit.register)

    slice_circuit = DAGCircuit()  # circuit for this swap slice
    for qubit in layout.get_virtual_bits().keys():
        if qubit.register not in slice_circuit.qregs.values():
            slice_circuit.add_qreg(qubit.register)
    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()
    trivial_layout = Layout.generate_trivial_layout(canonical_register)
    for idx in range(best_edges.size // 2):
        slice_circuit.apply_operation_back(
            SwapGate(),
            [trivial_layout[edgs[2 * idx]], trivial_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
Exemplo n.º 20
0
def _compose_transforms(basis_transforms, source_basis, source_dag):
    """Compose a set of basis transforms into a set of replacements.

    Args:
        basis_transforms (List[Tuple[gate_name, params, equiv]]): List of
            transforms to compose.
        source_basis (Set[Tuple[gate_name: str, gate_num_qubits: int]]): Names
            of gates which need to be translated.
        source_dag (DAGCircuit): DAG with example gates from source_basis.
            (Used to determine num_params for gate in source_basis.)

    Returns:
        Dict[gate_name, Tuple(params, dag)]: Dictionary mapping between each gate
            in source_basis and a DAGCircuit instance to replace it. Gates in
            source_basis but not affected by basis_transforms will be included
            as a key mapping to itself.
    """

    example_gates = {(node.op.name, node.op.num_qubits): node.op
                     for node in source_dag.op_nodes()}
    mapped_instrs = {}

    for gate_name, gate_num_qubits in source_basis:
        # Need to grab a gate instance to find num_qubits and num_params.
        # Can be removed following https://github.com/Qiskit/qiskit-terra/pull/3947 .
        example_gate = example_gates[gate_name, gate_num_qubits]
        num_params = len(example_gate.params)

        placeholder_params = ParameterVector(gate_name, num_params)
        placeholder_gate = Gate(gate_name, gate_num_qubits,
                                list(placeholder_params))
        placeholder_gate.params = list(placeholder_params)

        dag = DAGCircuit()
        qr = QuantumRegister(gate_num_qubits)
        dag.add_qreg(qr)
        dag.apply_operation_back(placeholder_gate, qr[:], [])
        mapped_instrs[gate_name, gate_num_qubits] = placeholder_params, dag

    for gate_name, gate_num_qubits, equiv_params, equiv in basis_transforms:
        logger.debug('Composing transform step: %s/%s %s =>\n%s', gate_name,
                     gate_num_qubits, equiv_params, equiv)

        for mapped_instr_name, (dag_params, dag) in mapped_instrs.items():
            doomed_nodes = [
                node for node in dag.op_nodes()
                if (node.op.name, node.op.num_qubits) == (gate_name,
                                                          gate_num_qubits)
            ]

            if doomed_nodes and logger.isEnabledFor(logging.DEBUG):
                from qiskit.converters import dag_to_circuit
                logger.debug(
                    'Updating transform for mapped instr %s %s from \n%s',
                    mapped_instr_name, dag_params, dag_to_circuit(dag))

            for node in doomed_nodes:
                from qiskit.converters import circuit_to_dag

                replacement = equiv.assign_parameters(
                    dict(zip_longest(equiv_params, node.op.params)))

                replacement_dag = circuit_to_dag(replacement)

                dag.substitute_node_with_dag(node, replacement_dag)

            if doomed_nodes and logger.isEnabledFor(logging.DEBUG):
                from qiskit.converters import dag_to_circuit
                logger.debug('Updated transform for mapped instr %s %s to\n%s',
                             mapped_instr_name, dag_params,
                             dag_to_circuit(dag))

    return mapped_instrs
Exemplo n.º 21
0
    def run(self, dag):
        """iterate over each block and replace it with an equivalent Unitary
        on the same wires.
        """
        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)

        # compute ordered indices for the global circuit wires
        global_index_map = {}
        for wire in dag.wires:
            if not isinstance(wire[0], QuantumRegister):
                continue
            global_qregs = list(dag.qregs.values())
            global_index_map[wire] = global_qregs.index(wire[0]) + wire[1]

        blocks = self.property_set['block_list']
        nodes_seen = set()

        for node in dag.topological_op_nodes():
            # skip already-visited nodes or input/output nodes
            if node in nodes_seen or node.type == 'in' or node.type == 'out':
                continue
            # check if the node belongs to the next block
            if blocks and node in blocks[0]:
                block = blocks[0]
                # find the qubits involved in this block
                block_qargs = set()
                for nd in block:
                    block_qargs |= set(nd.qargs)
                # convert block to a sub-circuit, then simulate unitary and add
                block_width = len(block_qargs)
                q = QuantumRegister(block_width)
                subcirc = QuantumCircuit(q)
                block_index_map = self._block_qargs_to_indices(
                    block_qargs, global_index_map)
                for nd in block:
                    nodes_seen.add(nd)
                    subcirc.append(nd.op,
                                   [q[block_index_map[i]] for i in nd.qargs])
                unitary = UnitaryGate(
                    Operator(subcirc))  # simulates the circuit
                new_dag.apply_operation_back(
                    unitary,
                    sorted(block_qargs, key=lambda x: block_index_map[x]))
                del blocks[0]
            else:
                # the node could belong to some future block, but in that case
                # we simply skip it. It is guaranteed that we will revisit that
                # future block, via its other nodes
                for block in blocks[1:]:
                    if node in block:
                        break
                # freestanding nodes can just be added
                else:
                    nodes_seen.add(node)
                    new_dag.apply_operation_back(node.op, node.qargs,
                                                 node.cargs)

        return new_dag
Exemplo n.º 22
0
    def layer_permutation(self, layer_partition, layout, qubit_subset):
        """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 Sergey Bravyi's algorithm.

        The layer_partition is a list of (qu)bit lists and each qubit is a
        tuple (qreg, index).
        The layout is a dict mapping qubits in the circuit to qubits in the
        coupling graph and represents the current positions of the data.
        The qubit_subset is the subset of qubits in the coupling graph that
        we have chosen to map into.
        The coupling is a CouplingGraph.
        TRIALS is the number of attempts the randomized algorithm makes.

        Returns: success_flag, best_circ, best_d, best_layout, trivial_flag

        If success_flag is True, then best_circ contains a DAGCircuit with
        the swap circuit, best_d 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. The trivial_flag is set if the layer
        has no multi-qubit gates.
        """
        if self.seed is None:
            self.seed = np.random.randint(0, np.iinfo(np.int32).max)
        rng = np.random.RandomState(self.seed)
        rev_layout = {b: a for a, b in layout.items()}
        gates = []
        for layer in layer_partition:
            if len(layer) > 2:
                raise TranspilerError("Layer contains >2 qubit gates")
            if len(layer) == 2:
                gates.append(tuple(layer))

        # Can we already apply the gates?
        dist = sum([
            self.coupling_map.distance(layout[g[0]][1], layout[g[1]][1])
            for g in gates
        ])
        if dist == len(gates):
            circ = DAGCircuit()
            circ.add_qreg(QuantumRegister(self.coupling_map.size(), "q"))
            return True, circ, 0, layout, bool(gates)

        # Begin loop over trials of randomized algorithm
        n = self.coupling_map.size()
        best_d = sys.maxsize  # initialize best depth
        best_circ = None  # initialize best swap circuit
        best_layout = None  # initialize best final layout
        QR = QuantumRegister(self.coupling_map.size(), "q")
        for _ in range(self.trials):

            trial_layout = layout.copy()
            rev_trial_layout = rev_layout.copy()
            # SWAP circuit constructed this trial
            trial_circ = DAGCircuit()
            trial_circ.add_qreg(QR)

            # Compute Sergey's randomized distance
            xi = {}
            for i in self.coupling_map.physical_qubits:
                xi[(QR, i)] = {}
            for i in self.coupling_map.physical_qubits:
                i = (QR, i)
                for j in self.coupling_map.physical_qubits:
                    j = (QR, j)
                    scale = 1 + rng.normal(0, 1 / n)
                    xi[i][j] = scale * self.coupling_map.distance(i[1],
                                                                  j[1])**2
                    xi[j][i] = xi[i][j]

            # Loop over depths d up to a max depth of 2n+1
            d = 1
            # Circuit for this swap slice
            circ = DAGCircuit()
            circ.add_qreg(QR)

            # Identity wire-map for composing the circuits
            identity_wire_map = {(QR, j): (QR, j) for j in range(n)}

            while d < 2 * n + 1:
                # Set of available qubits
                qubit_set = set(qubit_subset)
                # While there are still qubits available
                while qubit_set:
                    # Compute the objective function
                    min_cost = sum([
                        xi[trial_layout[g[0]]][trial_layout[g[1]]]
                        for g in gates
                    ])
                    # Try to decrease objective function
                    progress_made = False
                    # Loop over edges of coupling graph
                    for e in self.coupling_map.get_edges():
                        e = [(QR, edge) for edge in e]
                        # Are the qubits available?
                        if e[0] in qubit_set and e[1] in qubit_set:
                            # Try this edge to reduce the cost
                            new_layout = trial_layout.copy()
                            new_layout[rev_trial_layout[e[0]]] = e[1]
                            new_layout[rev_trial_layout[e[1]]] = e[0]
                            rev_new_layout = rev_trial_layout.copy()
                            rev_new_layout[e[0]] = rev_trial_layout[e[1]]
                            rev_new_layout[e[1]] = rev_trial_layout[e[0]]
                            # Compute the objective function
                            new_cost = sum([
                                xi[new_layout[g[0]]][new_layout[g[1]]]
                                for g in gates
                            ])
                            # Record progress if we succeed
                            if new_cost < min_cost:
                                progress_made = True
                                min_cost = new_cost
                                opt_layout = new_layout
                                rev_opt_layout = rev_new_layout
                                opt_edge = e

                    # Were there any good choices?
                    if progress_made:
                        qubit_set.remove(opt_edge[0])
                        qubit_set.remove(opt_edge[1])
                        trial_layout = opt_layout
                        rev_trial_layout = rev_opt_layout
                        circ.apply_operation_back(
                            SwapGate(), [(opt_edge[0][0], opt_edge[0][1]),
                                         (opt_edge[1][0], opt_edge[1][1])], [])
                    else:
                        break

                # We have either run out of qubits or failed to improve
                # Compute the coupling graph distance_qubits
                dist = sum([
                    self.coupling_map.distance(trial_layout[g[0]][1],
                                               trial_layout[g[1]][1])
                    for g in gates
                ])
                # If all gates can be applied now, we are finished
                # Otherwise we need to consider a deeper swap circuit
                if dist == len(gates):
                    trial_circ.compose_back(circ, identity_wire_map)
                    break

                # Increment the depth
                d += 1

            # Either we have succeeded at some depth d < dmax or failed
            dist = sum([
                self.coupling_map.distance(trial_layout[g[0]][1],
                                           trial_layout[g[1]][1])
                for g in gates
            ])
            if dist == len(gates):
                if d < best_d:
                    best_circ = trial_circ
                    best_layout = trial_layout
                best_d = min(best_d, d)

        if best_circ is None:
            return False, None, None, None, False

        return True, best_circ, best_d, best_layout, False
    def run(self, dag):
        """
        Run the CNOTCascadesTransform pass over a dag circuit.

        After the transformation, proceeds to check for possible one-qubit gates optimizations and
        CNOT cancellations, as subsequent CNOT nearest-neighbor sequences could create
        the opportunity for useful circuit simplifications.

        Args:
            dag (DAGCircuit): the dag circuit to be searched for CNOT cascades.

        Returns:
            new_dag (DAGCircuit): a new dag where all CNOT cascades have been transformed.
        """
        # prepare new dag
        new_dag = DAGCircuit()

        new_dag.name = dag.name
        self._num_qubits = dag.num_qubits()
        for q_reg in dag.qregs.values():
            new_dag.add_qreg(q_reg)
        for c_reg in dag.cregs.values():
            new_dag.add_creg(c_reg)

        i = 0
        for q_reg in dag.qregs.values():
            for q in q_reg:
                self._wires_to_id[q] = i
                self._id_to_wires[i] = q
                i += 1

        depth = new_dag.depth()
        while True:
            new_dag = Optimize1qGates().run(new_dag)
            new_dag = CXCancellation().run(new_dag)
            new_depth = new_dag.depth()
            if new_depth < depth:
                depth = new_depth
            else:
                break

        # get dag layers
        self._layers = [layer['graph'] for layer in dag.layers()]
        # this is the list of new layers for the nearest-neighbor CNOT sequences
        self._extra_layers = {l: [] for l in range(len(self._layers))}
        # loop through all layers
        for i, layer in enumerate(self._layers):
            if i != 0:
                # add nearest-neighbor CNOT sequences in the right layer
                for gate in self._extra_layers[i - 1]:
                    new_dag.apply_operation_back(*gate)

            # check all gates in the layer
            for gate in layer.op_nodes():
                temp = None
                # do not add gates that have been used in the transformation process
                if gate in self._skip:
                    continue
                # every cnot could be the starting point for a CNOT cascade
                elif gate.name == 'cx':
                    logger.debug('Check Cascade %s with qargs: %s\n' % (gate.name, gate.qargs))
                    # check for a CNOT cascade
                    temp = self.check_cascade(gate, i)
                    if temp is not None:
                        logger.info('Cascade Starts at %s with qargs: %s\n' % (gate.name, gate.qargs))
                        self._skip.extend(temp)
                    else:
                        logger.debug(
                            'Check Inverse Cascade at %s with qargs: %s\n' % (gate.name, gate.qargs))
                        # check for an inverted CNOT cascade
                        temp = self.check_inverse_cascade(gate, i)
                        if temp is not None:
                            logger.info(
                                'Inverse Cascade Starts at %s with qargs: %s\n' % (gate.name, gate.qargs))
                            self._skip.extend(temp)
                        else:
                            # apply the CNOT if no cascade was found
                            self._skip.append(gate)
                            logger.debug(
                                'Found Nothing at %s with qargs: %s\n' % (gate.name, gate.qargs))
                            new_dag.apply_operation_back(gate.op, gate.qargs, gate.cargs, gate.condition)
                else:
                    self._skip.append(gate)
                    new_dag.apply_operation_back(gate.op, gate.qargs, gate.cargs, gate.condition)
        logger.debug('Cascades found: %s' % str(self._extra_layers))

        # optimize dag after transformation
        depth = new_dag.depth()
        while True:
            new_dag = Optimize1qGates().run(new_dag)
            new_dag = CXCancellation().run(new_dag)
            new_depth = new_dag.depth()
            if new_depth < depth:
                depth = new_depth
            else:
                break
        return new_dag
Exemplo n.º 24
0
    def run(self, dag):
        """Map a DAGCircuit onto a CouplingGraph using swap gates.

        Args:
            dag (DAGCircuit): input DAG circuit

        Returns:
            DAGCircuit: object containing a circuit equivalent to
            circuit_graph that respects couplings in coupling_map, and
            a layout dict mapping qubits of circuit_graph into qubits
            of coupling_map. The layout may differ from the initial_layout
            if the first layer of gates cannot be executed on the
            initial_layout.

        Raises:
            TranspilerError: if there was any error during the mapping or with the
                parameters.
        """
        if dag.width() > self.coupling_map.size():
            raise TranspilerError("Not enough qubits in CouplingGraph")

        # Schedule the input circuit
        layerlist = list(dag.layers())

        if self.initial_layout is None and self.property_set["layout"]:
            self.initial_layout = self.property_set["layout"]

        if self.initial_layout is not None:
            # update initial_layout from a user given dict{(regname,idx): (regname,idx)}
            # to an expected dict{(reg,idx): (reg,idx)}

            virtual_qubits = self.initial_layout.get_virtual_bits()
            self.initial_layout = {(v[0].name, v[1]):
                                   ('q', self.initial_layout[v])
                                   for v in virtual_qubits}

            device_register = QuantumRegister(self.coupling_map.size(), 'q')
            initial_layout = {(dag.qregs[k[0]], k[1]): (device_register, v[1])
                              for k, v in self.initial_layout.items()}
            # Check the input layout
            circ_qubits = dag.qubits()
            coup_qubits = [(QuantumRegister(self.coupling_map.size(),
                                            'q'), wire)
                           for wire in self.coupling_map.physical_qubits]
            qubit_subset = []
            for k, v in initial_layout.items():
                qubit_subset.append(v)
                if k not in circ_qubits:
                    raise TranspilerError(
                        "initial_layout qubit %s[%d] not in input "
                        "DAGCircuit" % (k[0].name, k[1]))
                if v not in coup_qubits:
                    raise TranspilerError(
                        "initial_layout qubit %s[%d] not in input "
                        "CouplingGraph" % (v[0].name, v[1]))
        else:
            # Supply a default layout
            qubit_subset = [(QuantumRegister(self.coupling_map.size(),
                                             'q'), wire)
                            for wire in self.coupling_map.physical_qubits]
            qubit_subset = qubit_subset[0:dag.width()]
            initial_layout = {a: b for a, b in zip(dag.qubits(), qubit_subset)}

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

        # Construct an empty DAGCircuit with one qreg "q"
        # and the same set of cregs as the input circuit
        dagcircuit_output = DAGCircuit()
        dagcircuit_output.name = dag.name
        dagcircuit_output.add_qreg(
            QuantumRegister(self.coupling_map.size(), "q"))
        for creg in dag.cregs.values():
            dagcircuit_output.add_creg(creg)

        # Make a trivial wire mapping between the subcircuits
        # returned by swap_mapper_layer_update and the circuit
        # we are building
        identity_wire_map = {}
        q = QuantumRegister(self.coupling_map.size(), 'q')
        for j in range(self.coupling_map.size()):
            identity_wire_map[(q, j)] = (q, j)
        for creg in dag.cregs.values():
            for j in range(creg.size):
                identity_wire_map[(creg, j)] = (creg, j)

        first_layer = True  # True until first layer is output

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

            # Attempt to find a permutation for this layer
            success_flag, best_circ, best_d, best_layout, trivial_flag \
                = self.layer_permutation(layer["partition"], layout, qubit_subset)

            # If this fails, try one gate at a time in this layer
            if not success_flag:
                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_circ, best_d, best_layout, trivial_flag \
                        = self.layer_permutation(serial_layer["partition"], layout, qubit_subset)

                    # 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:
                        continue

                    # Update the record of qubit positions for each inner iteration
                    layout = best_layout
                    # Update the QASM
                    dagcircuit_output.compose_back(
                        self.swap_mapper_layer_update(j, first_layer,
                                                      best_layout, best_d,
                                                      best_circ,
                                                      serial_layerlist),
                        identity_wire_map)
                    # Update initial layout
                    if first_layer:
                        initial_layout = layout
                        first_layer = False

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

                # Update the QASM
                dagcircuit_output.compose_back(
                    self.swap_mapper_layer_update(i, first_layer, best_layout,
                                                  best_d, best_circ,
                                                  layerlist),
                    identity_wire_map)
                # Update initial layout
                if first_layer:
                    initial_layout = layout
                    first_layer = False

        # 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
        if first_layer:
            layout = initial_layout
            for i, layer in enumerate(layerlist):
                dagcircuit_output.compose_back(layer["graph"], layout)

        return dagcircuit_output
Exemplo n.º 25
0
def layer_permutation(layer_partition,
                      layout,
                      qubit_subset,
                      coupling,
                      trials,
                      seed=None):
    """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 Sergey Bravyi's algorithm.

    The layer_partition is a list of (qu)bit lists and each qubit is a
    tuple (qreg, index).
    The layout is a dict mapping qubits in the circuit to qubits in the
    coupling graph and represents the current positions of the data.
    The qubit_subset is the subset of qubits in the coupling graph that
    we have chosen to map into.
    The coupling is a CouplingGraph.
    TRIALS is the number of attempts the randomized algorithm makes.

    Returns: success_flag, best_circ, best_d, best_layout, trivial_flag

    If success_flag is True, then best_circ contains a DAGCircuit with
    the swap circuit, best_d 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. The trivial_flag is set if the layer
    has no multi-qubit gates.
    """
    if seed is not None:
        np.random.seed(seed)
    logger.debug("layer_permutation: ----- enter -----")
    logger.debug("layer_permutation: layer_partition = %s",
                 pprint.pformat(layer_partition))
    logger.debug("layer_permutation: layout = %s", pprint.pformat(layout))
    logger.debug("layer_permutation: qubit_subset = %s",
                 pprint.pformat(qubit_subset))
    logger.debug("layer_permutation: trials = %s", trials)
    rev_layout = {b: a for a, b in layout.items()}
    gates = []
    for layer in layer_partition:
        if len(layer) > 2:
            raise MapperError("Layer contains >2 qubit gates")
        elif len(layer) == 2:
            gates.append(tuple(layer))

    logger.debug("layer_permutation: gates = %s", pprint.pformat(gates))

    # Can we already apply the gates?
    dist = sum(
        [coupling.distance(layout[g[0]][1], layout[g[1]][1]) for g in gates])
    logger.debug("layer_permutation: dist = %s", dist)
    if dist == len(gates):
        logger.debug("layer_permutation: done already")
        logger.debug("layer_permutation: ----- exit -----")
        circ = DAGCircuit()
        circ.add_qreg(QuantumRegister(coupling.size(), "q"))
        circ.add_basis_element("CX", 2)
        circ.add_basis_element("cx", 2)
        circ.add_basis_element("swap", 2)
        circ.add_gate_data("cx", cx_data)
        circ.add_gate_data("swap", swap_data)
        return True, circ, 0, layout, bool(gates)

    # Begin loop over trials of randomized algorithm
    n = coupling.size()
    best_d = sys.maxsize  # initialize best depth
    best_circ = None  # initialize best swap circuit
    best_layout = None  # initialize best final layout
    for trial in range(trials):

        logger.debug("layer_permutation: trial %s", trial)
        trial_layout = layout.copy()
        rev_trial_layout = rev_layout.copy()
        # SWAP circuit constructed this trial
        trial_circ = DAGCircuit()
        trial_circ.add_qreg(QuantumRegister(coupling.size(), "q"))

        # Compute Sergey's randomized distance
        xi = {}
        for i in coupling.physical_qubits:
            xi[(QuantumRegister(coupling.size(), 'q'), i)] = {}
        for i in coupling.physical_qubits:
            i = (QuantumRegister(coupling.size(), 'q'), i)
            for j in coupling.physical_qubits:
                j = (QuantumRegister(coupling.size(), 'q'), j)
                scale = 1 + np.random.normal(0, 1 / n)
                xi[i][j] = scale * coupling.distance(i[1], j[1])**2
                xi[j][i] = xi[i][j]

        # Loop over depths d up to a max depth of 2n+1
        d = 1
        # Circuit for this swap slice
        circ = DAGCircuit()
        circ.add_qreg(QuantumRegister(coupling.size(), "q"))
        circ.add_basis_element("CX", 2)
        circ.add_basis_element("cx", 2)
        circ.add_basis_element("swap", 2)
        circ.add_gate_data("cx", cx_data)
        circ.add_gate_data("swap", swap_data)

        # Identity wire-map for composing the circuits
        q = QuantumRegister(coupling.size(), 'q')
        identity_wire_map = {(q, j): (q, j) for j in range(coupling.size())}

        while d < 2 * n + 1:
            # Set of available qubits
            qubit_set = set(qubit_subset)
            # While there are still qubits available
            while qubit_set:
                # Compute the objective function
                min_cost = sum([
                    xi[trial_layout[g[0]]][trial_layout[g[1]]] for g in gates
                ])
                # Try to decrease objective function
                progress_made = False
                # Loop over edges of coupling graph
                for e in coupling.get_edges():
                    e = [(QuantumRegister(coupling.size(), 'q'), edge)
                         for edge in e]
                    # Are the qubits available?
                    if e[0] in qubit_set and e[1] in qubit_set:
                        # Try this edge to reduce the cost
                        new_layout = trial_layout.copy()
                        new_layout[rev_trial_layout[e[0]]] = e[1]
                        new_layout[rev_trial_layout[e[1]]] = e[0]
                        rev_new_layout = rev_trial_layout.copy()
                        rev_new_layout[e[0]] = rev_trial_layout[e[1]]
                        rev_new_layout[e[1]] = rev_trial_layout[e[0]]
                        # Compute the objective function
                        new_cost = sum([
                            xi[new_layout[g[0]]][new_layout[g[1]]]
                            for g in gates
                        ])
                        # Record progress if we succceed
                        if new_cost < min_cost:
                            logger.debug(
                                "layer_permutation: progress! "
                                "min_cost = %s", min_cost)
                            progress_made = True
                            min_cost = new_cost
                            opt_layout = new_layout
                            rev_opt_layout = rev_new_layout
                            opt_edge = e

                # Were there any good choices?
                if progress_made:
                    qubit_set.remove(opt_edge[0])
                    qubit_set.remove(opt_edge[1])
                    trial_layout = opt_layout
                    rev_trial_layout = rev_opt_layout
                    circ.apply_operation_back(
                        SwapGate((opt_edge[0][0], opt_edge[0][1]),
                                 (opt_edge[1][0], opt_edge[1][1])))
                    logger.debug("layer_permutation: chose pair %s",
                                 pprint.pformat(opt_edge))
                else:
                    break

            # We have either run out of qubits or failed to improve
            # Compute the coupling graph distance_qubits
            dist = sum([
                coupling.distance(trial_layout[g[0]][1], trial_layout[g[1]][1])
                for g in gates
            ])
            logger.debug("layer_permutation: dist = %s", dist)
            # If all gates can be applied now, we are finished
            # Otherwise we need to consider a deeper swap circuit
            if dist == len(gates):
                logger.debug("layer_permutation: all can be applied now")
                trial_circ.compose_back(circ, identity_wire_map)
                break

            # Increment the depth
            d += 1
            logger.debug("layer_permutation: increment depth to %s", d)

        # Either we have succeeded at some depth d < dmax or failed
        dist = sum([
            coupling.distance(trial_layout[g[0]][1], trial_layout[g[1]][1])
            for g in gates
        ])
        logger.debug("layer_permutation: dist = %s", dist)
        if dist == len(gates):
            if d < best_d:
                logger.debug("layer_permutation: got circuit with depth %s", d)
                best_circ = trial_circ
                best_layout = trial_layout
            best_d = min(best_d, d)

    if best_circ is None:
        logger.debug("layer_permutation: failed!")
        logger.debug("layer_permutation: ----- exit -----")
        return False, None, None, None, False

    logger.debug("layer_permutation: done")
    logger.debug("layer_permutation: ----- exit -----")
    return True, best_circ, best_d, best_layout, False
Exemplo n.º 26
0
    def run(self, dag):
        """Run the CommutativeCancellation pass on a dag

        Args:
            dag (DAGCircuit): the DAG to be optimized.

        Returns:
            DAGCircuit: the optimized DAG.

        Raises:
            TranspilerError: when the 1 qubit rotation gates are not found
        """

        q_gate_list = ['cx', 'cy', 'cz', 'h', 'x', 'y', 'z']

        # Gate sets to be cancelled
        cancellation_sets = defaultdict(lambda: [])

        for wire in dag.wires:
            wire_name = "{0}[{1}]".format(str(wire[0].name), str(wire[1]))
            wire_commutation_set = self.property_set['commutation_set'][wire_name]

            for com_set_idx, com_set in enumerate(wire_commutation_set):
                if com_set[0].type in ['in', 'out']:
                    continue
                for node in com_set:
                    num_qargs = len(node.qargs)
                    if num_qargs == 1 and node.name in q_gate_list:
                        cancellation_sets[(node.name, wire_name, com_set_idx)].append(node)
                    if num_qargs == 1 and node.name in ['u1', 'rz', 't', 's']:
                        cancellation_sets[('z_rotation', wire_name, com_set_idx)].append(node)
                    elif num_qargs == 2 and node.qargs[0] == wire:
                        second_op_name = "{0}[{1}]".format(str(node.qargs[1][0].name),
                                                           str(node.qargs[1][1]))
                        q2_key = (node.name, wire_name, second_op_name,
                                  self.property_set['commutation_set'][(node, second_op_name)])
                        cancellation_sets[q2_key].append(node)

        for cancel_set_key in cancellation_sets:
            set_len = len(cancellation_sets[cancel_set_key])
            if ((set_len) > 1 and cancel_set_key[0] in q_gate_list):
                gates_to_cancel = cancellation_sets[cancel_set_key]
                for c_node in gates_to_cancel[:(set_len // 2) * 2]:
                    dag.remove_op_node(c_node)

            elif((set_len) > 1 and cancel_set_key[0] == 'z_rotation'):
                run = cancellation_sets[cancel_set_key]
                run_qarg = run[0].qargs[0]
                total_angle = 0.0  # lambda
                for current_node in run:
                    if (current_node.condition is not None
                            or len(current_node.qargs) != 1
                            or current_node.qargs[0] != run_qarg):
                        raise TranspilerError("internal error")

                    if current_node.name in ['u1', 'rz']:
                        current_angle = float(current_node.op.params[0])
                    elif current_node.name == 't':
                        current_angle = sympy.pi / 4
                    elif current_node.name == 's':
                        current_angle = sympy.pi / 2

                    # Compose gates
                    total_angle = current_angle + total_angle

                # Replace the data of the first node in the run
                new_op = U1Gate(total_angle)
                new_qarg = (QuantumRegister(1, 'q'), 0)
                new_dag = DAGCircuit()
                new_dag.add_qreg(new_qarg[0])
                new_dag.apply_operation_back(new_op, [new_qarg])
                dag.substitute_node_with_dag(run[0], new_dag)

                # Delete the other nodes in the run
                for current_node in run[1:]:
                    dag.remove_op_node(current_node)

        return dag
Exemplo n.º 27
0
    def depGraphToDagWithLinking(self, dep_graph, registers_in_order):
        # insert deallocation nodes (to avoid linking a_1 to a_2 if there is a dependency edge between the 2)
        for var in dep_graph.nodes:
            list_nodes = dep_graph.nodes[var]
            if list_nodes and list_nodes[0]:
                cur_node = list_nodes[0][-1]
                if isinstance(cur_node.register, AncillaRegister):
                    if cur_node.consume_edge_out is None:
                        de_node = Node(cur_node.register, cur_node.index,
                                       DeallocateGate(), -1, 0)
                        dep_graph.addNode(de_node)
                        dep_graph.connectConsumedNodes(cur_node, de_node)

        # now the topo order and add registers to dag as they come:
        # we take nodes that have no parents (and remove them from the graph, giving new orphan nodes)
        # + we try to avoid taking ancillasFirstNodes as much as possible, and when we have to, we pick the ones with oldest date of birth
        not_ancilla_alloc = [
        ]  # parentless nodes that are not an ancilla alloc, simple list
        ancilla_alloc = PriorityQueue(
        )  #when pop: element with least prio comes

        # we first fill up both of those lists
        for var in dep_graph.nodes:
            # no need to look into the deallocate nodes, they all have a parent for now
            list_nodes = dep_graph.nodes[var]
            if list_nodes and list_nodes[0]:
                node = list_nodes[0][0]
                if node.consume_edge_in is None and not node.ctrl_edges_in:
                    if isinstance(node.register, AncillaRegister):
                        ancilla_alloc.put(
                            (node.register._allocation_date, node))
                    else:
                        not_ancilla_alloc.append(node)
        # now we go through the nodes in order, creating the dag and when possible reusing ancilla spots
        dag = DAGCircuit()
        already_added = {
        }  # registers may yield mutliple wires, we don't want to add them multiple times

        # we add non ancilla registers already to have them in the right order
        for qubit in registers_in_order:
            if not isinstance(qubit.register, AncillaRegister):
                if already_added.get(qubit.register) is None:
                    already_added[qubit.register] = True
                    dag.add_qreg(qubit.register)

        available_ancilla_slots = [
        ]  # ancillas that have already been deallocated
        ancilla_correspondance = {
        }  # a_c[a] = b if ancilla b can reuse ancilla a wire
        removed_edges = {
        }  # for each node: remove_edges[node] = [nb_removed_consume_edges, nb_removed_ctrl_edges, nb_removed_non_Ctrl_edges]
        const_consume = 0
        const_ctrls = 1
        const_non_ctrls = 2
        # because we can't remove them really because they're needed to build the dag

        while not_ancilla_alloc or not ancilla_alloc.empty():
            cur_node = None
            # get the node, and deal with ancilla spots
            if not_ancilla_alloc:
                cur_node = not_ancilla_alloc.pop()
                if cur_node.gate.name == 'deallocate':
                    available_ancilla_slots.append(
                        ancilla_correspondance[(cur_node.register,
                                                cur_node.index)])
            else:
                (allocation_date, cur_node) = ancilla_alloc.get()
                if available_ancilla_slots:
                    ancilla_correspondance[(
                        cur_node.register,
                        cur_node.index)] = available_ancilla_slots.pop()
                else:
                    # we don't want to keep ancilla registers as is, because they might have way too many qubits => new single qubits for them
                    anc_reg = QuantumRegister(1,
                                              name="anc" +
                                              cur_node.register.name +
                                              str(cur_node.index))
                    already_added[anc_reg] = True
                    dag.add_qreg(anc_reg)
                    ancilla_correspondance[(cur_node.register,
                                            cur_node.index)] = (anc_reg, 0)

            # add node to dag
            if cur_node.gate.name == 'init':
                register = cur_node.register
                if ancilla_correspondance.get(
                    (cur_node.register, cur_node.index)) is not None:
                    (register,
                     ind) = ancilla_correspondance[(cur_node.register,
                                                    cur_node.index)]
                if already_added.get(register) is None:
                    already_added[register] = True
                    dag.add_qreg(register)
            elif cur_node.gate.name != 'deallocate':
                #get the arguments of the gate: again assuming ctrls first, target last
                # we have to replace ancilla registers
                ctrl_reg = [
                    self._getQubit(ctrl_edge.node_from, ancilla_correspondance)
                    for ctrl_edge in cur_node.ctrl_edges_in
                ]
                target_reg = self._getQubit(cur_node.consume_edge_in.node_from,
                                            ancilla_correspondance)
                ctrl_reg.append(target_reg)
                # if gate is on an ancilla, all CCXs can be replaced by RCCXs, so we go recursively into the gate definition and replace all CCXs there
                if isinstance(cur_node.register, AncillaRegister):
                    cur_node.gate = _replaceCCXs(cur_node.gate)
                dag.apply_operation_back(cur_node.gate, ctrl_reg)

            # remove all outgoing edges from this node form dep_graph, and add new orphan nodes to lists
            if cur_node.consume_edge_out is not None:
                consuming_node = cur_node.consume_edge_out.node_to
                if removed_edges.get(consuming_node) is None:
                    removed_edges[consuming_node] = [0, 0, 0]
                removed_edges[consuming_node][const_consume] += 1
                if removed_edges[consuming_node][const_ctrls] == len(
                        consuming_node.ctrl_edges_in
                ) and removed_edges[consuming_node][const_non_ctrls] == len(
                        consuming_node.non_ctrl_edges_in):
                    not_ancilla_alloc.append(
                        consuming_node
                    )  # it consumes some node, so not ancilla alloc
            for e in cur_node.edges_out:
                node_to = e.node_to
                if removed_edges.get(node_to) is None:
                    removed_edges[node_to] = [0, 0, 0]
                if e.type == 'd':
                    removed_edges[node_to][const_ctrls] += 1
                else:
                    removed_edges[node_to][const_non_ctrls] += 1
                if removed_edges[node_to][const_ctrls] == len(
                        node_to.ctrl_edges_in
                ) and removed_edges[node_to][const_non_ctrls] == len(
                        node_to.non_ctrl_edges_in):
                    if removed_edges[node_to][const_consume] == 1:
                        not_ancilla_alloc.append(
                            node_to
                        )  # it consumes some node, so not ancilla alloc
                    elif node_to.consume_edge_in is None:
                        if isinstance(node_to.register, AncillaRegister):
                            ancilla_alloc.put(
                                (node_to.register._allocation_date, node_to))
                        else:
                            not_ancilla_alloc.append(node_to)
        return dag
Exemplo n.º 28
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
Exemplo n.º 29
0
 def test_add_reg_duplicate(self):
     """ add_qreg with the same register twice is not allowed."""
     dag = DAGCircuit()
     qr = QuantumRegister(2)
     dag.add_qreg(qr)
     self.assertRaises(DAGCircuitError, dag.add_qreg, qr)
Exemplo n.º 30
0
    def run(self, dag, time_unit=None):  # pylint: disable=arguments-differ
        """Run the ASAPSchedule 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(
                'ASAP 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_back(Delay(idle_duration, unit),
                                                 [q])

        for node in 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_back(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.duration = circuit_duration
        new_dag.unit = time_unit
        return new_dag