def __init__(
        self,
        qubit_pairs: Iterable[tuple[cirq.GridQubit, cirq.GridQubit]],
        gateset: cirq.Gateset,
        gate_durations: Mapping[cirq.GateFamily, cirq.Duration] | None = None,
        all_qubits: Iterable[cirq.GridQubit] | None = None,
        compilation_target_gatesets: Iterable[cirq.CompilationTargetGateset] = (),
    ):
        """Create a GridDeviceMetadata object.

        Create a grid device which has a well defined set of couplable
        qubit pairs that have the same two qubit gates available in
        both coupling directions. Gate times (if provided) are expected
        to be uniform across all qubits on the device.

        Args:
            qubit_pairs: Iterable of pairs of `cirq.GridQubit`s representing
                bi-directional couplings.
            gateset: `cirq.Gateset` indicating gates supported
                everywhere on the device.
            gate_durations: Optional dictionary of `cirq.GateFamily`
                instances mapping to `cirq.Duration` instances for
                gate timing metadata information. If provided,
                all keys must exist in gateset.
            all_qubits: Optional iterable specifying all qubits
                found on the device. If None, all_qubits will
                be inferred from the entries in qubit_pairs.
            compilation_target_gatesets: A collection of valid
                `cirq.CompilationTargetGateset`s which can be used to
                transform circuits into ones that consist of only
                operations in `gateset`.

        Raises:
            ValueError: if some GateFamily keys in gate_durations are
                not in gateset.
            ValueError: If qubit_pairs contains a self loop.
            ValueError: if all_qubits is provided and is not a superset
                of all the qubits found in qubit_pairs.
        """
        sorted_pairs = sorted(list(qubit_pairs))
        for a, b in sorted_pairs:
            if a == b:
                raise ValueError(f"Self loop encountered in qubit {a}")

        # Keep lexicographically smaller tuples for undirected edges.
        edge_set = set()
        node_set = set()
        for a, b in sorted_pairs:
            node_set.add(a)
            node_set.add(b)
            if (b, a) not in edge_set:
                edge_set.add((a, b))

        if all_qubits is None:
            all_qubits = node_set

        all_qubits = frozenset(all_qubits)
        for q in node_set:
            if q not in all_qubits:
                raise ValueError(
                    f"Qubit {q} found in node_set and not in"
                    " all_qubits. all_qubits must contain at least"
                    " all the qubits found in all_qubits."
                )

        connectivity = nx.Graph()
        connectivity.add_nodes_from(sorted(all_qubits))
        connectivity.add_edges_from(sorted(edge_set), directed=False)
        super().__init__(all_qubits, connectivity)

        self._qubit_pairs = frozenset({frozenset(pair) for pair in edge_set})
        self._gateset = gateset
        self._isolated_qubits = all_qubits.difference(node_set)
        self._compilation_target_gatesets = tuple(compilation_target_gatesets)

        if gate_durations is not None:
            working_gatefamilies = frozenset(gate_durations.keys())
            if not working_gatefamilies.issubset(gateset.gates):
                raise ValueError(
                    "Some gate_durations keys are not found in gateset."
                    f" gate_durations={gate_durations}"
                    f" gateset.gates={gateset.gates}"
                )

        self._gate_durations = gate_durations
Create a GridDeviceMetadata object.

        Create a grid device which has a well defined set of couplable
        qubit pairs that have the same two qubit gates available in
        both coupling directions. Gate times (if provided) are expected
        to be uniform across all qubits on the device.

        Args:
            qubit_pairs: Iterable of pairs of `cirq.GridQubit`s representing
                bi-directional couplings.
            gateset: `cirq.Gateset` indicating gates supported
                everywhere on the device.
            gate_durations: Optional dictionary of `cirq.GateFamily`
                instances mapping to `cirq.Duration` instances for
                gate timing metadata information. If provided,
                all keys must exist in gateset.
            all_qubits: Optional iterable specifying all qubits
                found on the device. If None, all_qubits will
                be inferred from the entries in qubit_pairs.
            compilation_target_gatesets: A collection of valid
                `cirq.CompilationTargetGateset`s which can be used to
                transform circuits into ones that consist of only
                operations in `gateset`.

        Raises:
            ValueError: if some GateFamily keys in gate_durations are
                not in gateset.
            ValueError: If qubit_pairs contains a self loop.
            ValueError: if all_qubits is provided and is not a superset
                of all the qubits found in qubit_pairs.
        
cirq-core/cirq/devices/grid_device_metadata.py