Beispiel #1
0
def _swap_ops_from_edge(edge, layout):
    """Generate list of ops to implement a SWAP gate along a coupling edge."""
    device_qreg = QuantumRegister(len(layout.get_physical_bits()), 'q')
    qreg_edge = [device_qreg[i] for i in edge]

    # TODO shouldn't be making other nodes not by the DAG!!
    return [DAGNode(op=SwapGate(), qargs=qreg_edge, cargs=[], type='op')]
Beispiel #2
0
 def test_definition_specification(self):
     """Test instantiation with explicit definition"""
     swap = SwapGate()
     cswap = ControlledGate('cswap',
                            3, [],
                            num_ctrl_qubits=1,
                            definition=swap.definition)
     self.assertEqual(swap.definition, cswap.definition)
def _swap_ops_from_edge(edge, layout):
    """Generate list of ops to implement a SWAP gate along a coupling edge."""

    device_qreg = QuantumRegister(len(layout.get_physical_bits()), 'q')
    qreg_edge = [(device_qreg, i) for i in edge]
    return [
        {'op': SwapGate(*qreg_edge), 'qargs': qreg_edge},
    ]
Beispiel #4
0
def _swap_ops_from_edge(edge, layout):
    """Generate list of ops to implement a SWAP gate along a coupling edge."""

    device_qreg = QuantumRegister(len(layout.get_physical_bits()), 'q')
    qreg_edge = [(device_qreg, i) for i in edge]

    # TODO shouldn't be making other nodes not by the DAG!!
    return [
        DAGNode({'op': SwapGate(*qreg_edge), 'qargs': qreg_edge, 'type': 'op'})
    ]
class TestParameterCtrlState(QiskitTestCase):
    """Test gate equality with ctrl_state parameter."""
    @data((RXGate(0.5), CRXGate(0.5)), (RYGate(0.5), CRYGate(0.5)),
          (RZGate(0.5), CRZGate(0.5)), (XGate(), CXGate()),
          (YGate(), CYGate()), (ZGate(), CZGate()),
          (U1Gate(0.5), CU1Gate(0.5)), (SwapGate(), CSwapGate()),
          (HGate(), CHGate()), (U3Gate(0.1, 0.2, 0.3), CU3Gate(0.1, 0.2, 0.3)))
    @unpack
    def test_ctrl_state_one(self, gate, controlled_gate):
        """Test controlled gates with ctrl_state
        See https://github.com/Qiskit/qiskit-terra/pull/4025
        """
        self.assertEqual(gate.control(1, ctrl_state='1'), controlled_gate)
Beispiel #6
0
    def test_is_identity(self):
        """ The is_identity function determines whether a pair of gates
            forms the identity, when ignoring control qubits.
        """
        seq = [
            DAGNode({
                'type': 'op',
                'op': XGate().control()
            }),
            DAGNode({
                'type': 'op',
                'op': XGate().control(2)
            })
        ]
        self.assertTrue(HoareOptimizer()._is_identity(seq))

        seq = [
            DAGNode({
                'type': 'op',
                'op': RZGate(-pi / 2).control()
            }),
            DAGNode({
                'type': 'op',
                'op': RZGate(pi / 2).control(2)
            })
        ]
        self.assertTrue(HoareOptimizer()._is_identity(seq))

        seq = [
            DAGNode({
                'type': 'op',
                'op': CSwapGate()
            }),
            DAGNode({
                'type': 'op',
                'op': SwapGate()
            })
        ]
        self.assertTrue(HoareOptimizer()._is_identity(seq))

        seq = [
            DAGNode({
                'type': 'op',
                'op': UnitaryGate([[1, 0], [0, 1j]]).control()
            }),
            DAGNode({
                'type': 'op',
                'op': UnitaryGate([[1, 0], [0, -1j]])
            })
        ]
def random_clifford_circuit(num_qubits, num_gates, gates='all', seed=None):
    """Generate a pseudo random Clifford circuit."""

    if gates == 'all':
        if num_qubits == 1:
            gates = ['i', 'x', 'y', 'z', 'h', 's', 'sdg', 'v', 'w']
        else:
            gates = [
                'i', 'x', 'y', 'z', 'h', 's', 'sdg', 'v', 'w', 'cx', 'cz',
                'swap'
            ]

    instructions = {
        'i': (IGate(), 1),
        'x': (XGate(), 1),
        'y': (YGate(), 1),
        'z': (ZGate(), 1),
        'h': (HGate(), 1),
        's': (SGate(), 1),
        'sdg': (SdgGate(), 1),
        'v': (VGate(), 1),
        'w': (WGate(), 1),
        'cx': (CXGate(), 2),
        'cz': (CZGate(), 2),
        'swap': (SwapGate(), 2)
    }

    if isinstance(seed, np.random.RandomState):
        rng = seed
    else:
        rng = np.random.RandomState(seed=seed)

    samples = rng.choice(gates, num_gates)

    circ = QuantumCircuit(num_qubits)

    for name in samples:
        gate, nqargs = instructions[name]
        qargs = rng.choice(range(num_qubits), nqargs, replace=False).tolist()
        circ.append(gate, qargs)

    return circ
Beispiel #8
0
def _layer_permutation(layer_partition, initial_layout, layout, qubit_subset,
                       coupling, trials, qregs, rng):
    """Find a swap circuit that implements a permutation for this layer.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    # Otherwise, we return our result for this layer
    logger.debug("layer_permutation: success!")
    best_lay = best_layout.to_layout(qregs)
    return True, best_circuit, best_depth, best_lay, False
Beispiel #9
0
 def test_controlled_swap(self):
     """Test creation of controlled swap gate"""
     self.assertEqual(SwapGate().control(), FredkinGate())
Beispiel #10
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
Beispiel #11
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
Beispiel #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])
            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

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

        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
            data = 1 + np.random.normal(0, 1/num_qubits,
                                        size=num_qubits*(num_qubits+1)//2)
            scale[utri_idx] = data
            xi = (scale+scale.T)*cdist2  # pylint: disable=invalid-name

            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])

            # 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
                    need_copy = True
                    for edge in coupling.get_edges():
                        qubits = (trial_layout[edge[0]], trial_layout[edge[1]])
                        # Are the qubits available?
                        if qubits[0] in qubit_set and qubits[1] in qubit_set:
                            # Try this edge to reduce the cost
                            if need_copy:
                                new_layout = trial_layout.copy()
                                need_copy = False
                            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 = (self.initial_layout[edge[0]],
                                                self.initial_layout[edge[1]])
                                optimal_qubits = qubits
                                need_copy = True
                            else:
                                new_layout.swap(edge[0], edge[1])

                    # Were there any good swap choices?
                    if cost_reduced:
                        qubit_set.remove(optimal_qubits[0])
                        qubit_set.remove(optimal_qubits[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
Beispiel #13
0
    def _layer_permutation(self, layer_partition, layout, qubit_subset,
                           coupling, trials):
        """Find a swap circuit that implements a permutation for this layer.

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

        Based on S. Bravyi's algorithm.

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

        Returns:
            Tuple: success_flag, best_circuit, best_depth, best_layout

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

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

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

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

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

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

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

        int_qubit_subset = _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 slice of swaps in 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)

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

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

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

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

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

        # Otherwise, we return our result for this layer
        logger.debug("layer_permutation: success!")
        best_lay = best_layout.to_layout(qregs)
        return True, best_circuit, best_depth, best_lay
Beispiel #14
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")
            elif 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 succceed
                            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