Codi font per a qilisdk.digital.topology

# Copyright 2026 Qilimanjaro Quantum Tech
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations

from typing import TYPE_CHECKING, TypeGuard

from rustworkx import PyGraph

if TYPE_CHECKING:
    from .types import Topology


_TOPOLOGY_TUPLE_ARITY = 2


def _is_topology_graph(topology: list[tuple[int, int]] | PyGraph[int, None]) -> TypeGuard[PyGraph[int, None]]:
    return isinstance(topology, PyGraph) and all(isinstance(node, int) for node in topology.nodes())


def _is_topology_list(topology: list[tuple[int, int]] | PyGraph[int, None]) -> TypeGuard[list[tuple[int, int]]]:
    return isinstance(topology, list) and all(
        isinstance(element, tuple)
        and len(element) == _TOPOLOGY_TUPLE_ARITY
        and isinstance(element[0], int)
        and isinstance(element[1], int)
        for element in topology
    )


[documents] def build_topology_graph(topology: Topology) -> PyGraph[int, None]: """Return a topology as a ``rustworkx.PyGraph``. Args: topology (Topology): Coupling map provided either as a list of connected physical-qubit pairs or as an existing ``PyGraph``. Raises: ValueError: Returns: PyGraph[int, None]: Coupling map in ``PyGraph`` form. """ if _is_topology_graph(topology): return topology if _is_topology_list(topology): graph = PyGraph[int, None]() # Collect the physical qubit labels that actually appear in the coupling map. active_nodes = {int(qubit) for pair in topology for qubit in pair} if not active_nodes: raise ValueError("Topology edge list cannot be empty.") max_label = max(active_nodes) # Add a dense block of nodes so that node indices match physical labels. graph.add_nodes_from(range(max_label + 1)) for physical_qubit_a, physical_qubit_b in topology: graph.add_edge(int(physical_qubit_a), int(physical_qubit_b), None) # Remove any indices that are not populated in the topology. This keeps # rustworkx node indices aligned with the real physical labels. missing_nodes = {node for node in range(max_label + 1) if node not in active_nodes} for missing_node in sorted(missing_nodes, reverse=True): graph.remove_node(missing_node) return graph raise ValueError("topology has incorrect format.")