Repository: quantumlib/Cirq

Issue:
Fix types / docstrings on `DeviceMetadata` 
**Description of the issue**
1. Args say `qubits` and `nx_graph` are optional, but their type is not `Optional[]`. 
https://github.com/quantumlib/Cirq/blob/6937e417e372e4157ed70a6a38ec1d38964201ab/cirq-core/cirq/devices/device.py#L224-L231

2. Args say the returned qubit set can be `None` but return type is not `Optional[FrozenSet['cirq.Qid']]`.
https://github.com/quantumlib/Cirq/blob/6937e417e372e4157ed70a6a38ec1d38964201ab/cirq-core/cirq/devices/device.py#L240-L245

3. Args say the returned graph can be `None` but return type is not `Optional[nx.Graph]`.
https://github.com/quantumlib/Cirq/blob/6937e417e372e4157ed70a6a38ec1d38964201ab/cirq-core/cirq/devices/device.py#L249-L253

4. `nx.graph` is used as a type instead of `nx.Graph` -- the former is a module and latter is a type. 
https://github.com/quantumlib/Cirq/blob/6937e417e372e4157ed70a6a38ec1d38964201ab/cirq-core/cirq/devices/device.py#L225



**Cirq version**
```
0.14dev
```

cc @MichaelBroughton 



Discussion:

Resolving patch:
diff --git a/cirq-core/cirq/devices/device.py b/cirq-core/cirq/devices/device.py
--- a/cirq-core/cirq/devices/device.py
+++ b/cirq-core/cirq/devices/device.py
@@ -222,13 +222,13 @@ class DeviceMetadata:
     def __init__(
         self,
         qubits: Iterable['cirq.Qid'],
-        nx_graph: 'nx.graph',
+        nx_graph: 'nx.Graph',
     ):
         """Construct a DeviceMetadata object.
 
         Args:
-            qubits: Optional iterable of `cirq.Qid`s that exist on the device.
-            nx_graph: Optional `nx.Graph` describing qubit connectivity
+            qubits: Iterable of `cirq.Qid`s that exist on the device.
+            nx_graph: `nx.Graph` describing qubit connectivity
                 on a device. Nodes represent qubits, directed edges indicate
                 directional coupling, undirected edges indicate bi-directional
                 coupling.
@@ -238,10 +238,10 @@ def __init__(
 
     @property
     def qubit_set(self) -> FrozenSet['cirq.Qid']:
-        """Returns a set of qubits on the device, if possible.
+        """Returns the set of qubits on the device.
 
         Returns:
-            Frozenset of qubits on device if specified, otherwise None.
+            Frozenset of qubits on device.
         """
         return self._qubits_set
 
@@ -250,7 +250,7 @@ def nx_graph(self) -> 'nx.Graph':
         """Returns a nx.Graph where nodes are qubits and edges are couple-able qubits.
 
         Returns:
-            `nx.Graph` of device connectivity if specified, otherwise None.
+            `nx.Graph` of device connectivity.
         """
         return self._nx_graph
 
@@ -269,6 +269,6 @@ def _json_dict_(self):
         return {'qubits': qubits_payload, 'nx_graph': graph_payload}
 
     @classmethod
-    def _from_json_dict_(cls, qubits, nx_graph, **kwargs):
+    def _from_json_dict_(cls, qubits: Iterable['cirq.Qid'], nx_graph: 'nx.Graph', **kwargs):
         graph_obj = nx.readwrite.json_graph.node_link_graph(nx_graph)
         return cls(qubits, graph_obj)
diff --git a/cirq-core/cirq/devices/grid_device_metadata.py b/cirq-core/cirq/devices/grid_device_metadata.py
--- a/cirq-core/cirq/devices/grid_device_metadata.py
+++ b/cirq-core/cirq/devices/grid_device_metadata.py
@@ -37,8 +37,8 @@ class GridDeviceMetadata(device.DeviceMetadata):
     def __init__(
         self,
         qubit_pairs: Iterable[Tuple['cirq.Qid', 'cirq.Qid']],
-        supported_gates: 'cirq.Gateset',
-        gate_durations: Optional[Dict['cirq.Gateset', 'cirq.Duration']] = None,
+        gateset: 'cirq.Gateset',
+        gate_durations: Optional[Dict['cirq.GateFamily', 'cirq.Duration']] = None,
     ):
         """Create a GridDeviceMetadata object.
 
@@ -49,16 +49,16 @@ def __init__(
         Args:
             qubit_pairs: Iterable of pairs of `cirq.Qid`s representing
                 bi-directional couplings.
-            supported_gates: `cirq.Gateset` indicating gates supported
+            gateset: `cirq.Gateset` indicating gates supported
                 everywhere on the device.
-            gate_durations: Optional dictionary of `cirq.Gateset`
+            gate_durations: Optional dictionary of `cirq.GateFamily`
                 instances mapping to `cirq.Duration` instances for
                 gate timing metadata information. If provided,
-                must match all entries in supported_gates.
+                must match all entries in gateset.
 
         Raises:
-            ValueError: if the union of gateset keys in gate_durations,
-                do not represent an identical gateset to supported_gates.
+            ValueError: if the union of GateFamily keys in gate_durations
+                is not identical to set of gate families in gateset.
         """
         qubit_pairs = list(qubit_pairs)
         flat_pairs = [q for pair in qubit_pairs for q in pair]
@@ -73,18 +73,17 @@ def __init__(
         connectivity.add_edges_from(sorted(pair_set), directed=False)
         super().__init__(flat_pairs, connectivity)
         self._qubit_pairs = frozenset(pair_set)
-        self._supported_gates = supported_gates
+        self._gateset = gateset
 
         if gate_durations is not None:
-            working_gatefamilies = frozenset(
-                g for gset in gate_durations.keys() for g in gset.gates
-            )
-            if working_gatefamilies != supported_gates.gates:
-                missing_items = working_gatefamilies.difference(supported_gates.gates)
+            working_gatefamilies = frozenset(gate_durations.keys())
+            if working_gatefamilies != gateset.gates:
                 raise ValueError(
-                    "Supplied gate_durations contains gates not present"
-                    f" in supported_gates. {missing_items} in supported_gates"
-                    " is False."
+                    "set of gate_durations keys are not equal to the "
+                    "GateFamily instances found in gateset. Some gates "
+                    "will be missing duration information."
+                    f" gate_durations={gate_durations}"
+                    f" gateset.gates={gateset.gates}"
                 )
 
         self._gate_durations = gate_durations
@@ -97,10 +96,10 @@ def qubit_pairs(self) -> FrozenSet[Tuple['cirq.Qid', 'cirq.Qid']]:
     @property
     def gateset(self) -> 'cirq.Gateset':
         """Returns the `cirq.Gateset` of supported gates on this device."""
-        return self._supported_gates
+        return self._gateset
 
     @property
-    def gate_durations(self) -> Optional[Dict['cirq.Gateset', 'cirq.Duration']]:
+    def gate_durations(self) -> Optional[Dict['cirq.GateFamily', 'cirq.Duration']]:
         """Get a dictionary mapping from gateset to duration for gates."""
         return self._gate_durations
 
@@ -111,14 +110,14 @@ def _value_equality_values_(self):
 
         return (
             tuple(sorted(self._qubit_pairs)),
-            self._supported_gates,
+            self._gateset,
             tuple(duration_equality),
         )
 
     def __repr__(self) -> str:
         return (
             f'cirq.GridDeviceMetadata({repr(self._qubit_pairs)},'
-            f' {repr(self._supported_gates)}, {repr(self._gate_durations)})'
+            f' {repr(self._gateset)}, {repr(self._gate_durations)})'
         )
 
     def _json_dict_(self):
@@ -128,10 +127,10 @@ def _json_dict_(self):
 
         return {
             'qubit_pairs': sorted(list(self._qubit_pairs)),
-            'supported_gates': self._supported_gates,
+            'gateset': self._gateset,
             'gate_durations': duration_payload,
         }
 
     @classmethod
-    def _from_json_dict_(cls, qubit_pairs, supported_gates, gate_durations, **kwargs):
-        return cls(qubit_pairs, supported_gates, dict(gate_durations))
+    def _from_json_dict_(cls, qubit_pairs, gateset, gate_durations, **kwargs):
+        return cls(qubit_pairs, gateset, dict(gate_durations))
diff --git a/cirq-google/cirq_google/devices/serializable_device.py b/cirq-google/cirq_google/devices/serializable_device.py
--- a/cirq-google/cirq_google/devices/serializable_device.py
+++ b/cirq-google/cirq_google/devices/serializable_device.py
@@ -117,7 +117,7 @@ def __init__(
                 for pair in gate_def.target_set
                 if len(pair) == 2 and pair[0] < pair[1]
             ],
-            supported_gates=cirq.Gateset(
+            gateset=cirq.Gateset(
                 *[g for g in gate_definitions.keys() if isinstance(g, (cirq.Gate, type(cirq.Gate)))]
             ),
             gate_durations=None,


Test patch:
diff --git a/cirq-core/cirq/devices/grid_device_metadata_test.py b/cirq-core/cirq/devices/grid_device_metadata_test.py
--- a/cirq-core/cirq/devices/grid_device_metadata_test.py
+++ b/cirq-core/cirq/devices/grid_device_metadata_test.py
@@ -51,8 +51,8 @@ def test_griddevice_metadata_bad_durations():
 
     gateset = cirq.Gateset(cirq.XPowGate, cirq.YPowGate)
     invalid_duration = {
-        cirq.Gateset(cirq.XPowGate): cirq.Duration(nanos=1),
-        cirq.Gateset(cirq.ZPowGate): cirq.Duration(picos=1),
+        cirq.GateFamily(cirq.XPowGate): cirq.Duration(nanos=1),
+        cirq.GateFamily(cirq.ZPowGate): cirq.Duration(picos=1),
     }
     with pytest.raises(ValueError, match="ZPowGate"):
         cirq.GridDeviceMetadata([qubits], gateset, gate_durations=invalid_duration)
@@ -63,9 +63,9 @@ def test_griddevice_json_load():
     qubit_pairs = [(a, b) for a in qubits for b in qubits if a != b and a.is_adjacent(b)]
     gateset = cirq.Gateset(cirq.XPowGate, cirq.YPowGate, cirq.ZPowGate)
     duration = {
-        cirq.Gateset(cirq.XPowGate): cirq.Duration(nanos=1),
-        cirq.Gateset(cirq.YPowGate): cirq.Duration(picos=2),
-        cirq.Gateset(cirq.ZPowGate): cirq.Duration(picos=3),
+        cirq.GateFamily(cirq.XPowGate): cirq.Duration(nanos=1),
+        cirq.GateFamily(cirq.YPowGate): cirq.Duration(picos=2),
+        cirq.GateFamily(cirq.ZPowGate): cirq.Duration(picos=3),
     }
     metadata = cirq.GridDeviceMetadata(qubit_pairs, gateset, gate_durations=duration)
     rep_str = cirq.to_json(metadata)
@@ -77,14 +77,14 @@ def test_griddevice_metadata_equality():
     qubit_pairs = [(a, b) for a in qubits for b in qubits if a != b and a.is_adjacent(b)]
     gateset = cirq.Gateset(cirq.XPowGate, cirq.YPowGate, cirq.ZPowGate)
     duration = {
-        cirq.Gateset(cirq.XPowGate): cirq.Duration(nanos=1),
-        cirq.Gateset(cirq.YPowGate): cirq.Duration(picos=3),
-        cirq.Gateset(cirq.ZPowGate): cirq.Duration(picos=2),
+        cirq.GateFamily(cirq.XPowGate): cirq.Duration(nanos=1),
+        cirq.GateFamily(cirq.YPowGate): cirq.Duration(picos=3),
+        cirq.GateFamily(cirq.ZPowGate): cirq.Duration(picos=2),
     }
     duration2 = {
-        cirq.Gateset(cirq.XPowGate): cirq.Duration(nanos=10),
-        cirq.Gateset(cirq.YPowGate): cirq.Duration(picos=13),
-        cirq.Gateset(cirq.ZPowGate): cirq.Duration(picos=12),
+        cirq.GateFamily(cirq.XPowGate): cirq.Duration(nanos=10),
+        cirq.GateFamily(cirq.YPowGate): cirq.Duration(picos=13),
+        cirq.GateFamily(cirq.ZPowGate): cirq.Duration(picos=12),
     }
     metadata = cirq.GridDeviceMetadata(qubit_pairs, gateset, gate_durations=duration)
     metadata2 = cirq.GridDeviceMetadata(qubit_pairs[:2], gateset, gate_durations=duration)
@@ -106,9 +106,9 @@ def test_repr():
     qubit_pairs = [(a, b) for a in qubits for b in qubits if a != b and a.is_adjacent(b)]
     gateset = cirq.Gateset(cirq.XPowGate, cirq.YPowGate, cirq.ZPowGate)
     duration = {
-        cirq.Gateset(cirq.XPowGate): cirq.Duration(nanos=1),
-        cirq.Gateset(cirq.YPowGate): cirq.Duration(picos=3),
-        cirq.Gateset(cirq.ZPowGate): cirq.Duration(picos=2),
+        cirq.GateFamily(cirq.XPowGate): cirq.Duration(nanos=1),
+        cirq.GateFamily(cirq.YPowGate): cirq.Duration(picos=3),
+        cirq.GateFamily(cirq.ZPowGate): cirq.Duration(picos=2),
     }
     metadata = cirq.GridDeviceMetadata(qubit_pairs, gateset, gate_durations=duration)
     cirq.testing.assert_equivalent_repr(metadata)
diff --git a/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.json b/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.json
--- a/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.json
+++ b/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.json
@@ -86,7 +86,7 @@
       }
     ]
   ],
-  "supported_gates": {
+  "gateset": {
     "cirq_type": "Gateset",
     "gates": [
       {
@@ -118,19 +118,11 @@
   "gate_durations": [
     [
       {
-        "cirq_type": "Gateset",
-        "gates": [
-          {
-            "cirq_type": "GateFamily",
-            "gate": "XPowGate",
-            "name": "Type GateFamily: cirq.ops.common_gates.XPowGate",
-            "description": "Accepts `cirq.Gate` instances `g` s.t. `isinstance(g, cirq.ops.common_gates.XPowGate)`",
-            "ignore_global_phase": true
-          }
-        ],
-        "name": null,
-        "unroll_circuit_op": true,
-        "accept_global_phase_op": true
+        "cirq_type": "GateFamily",
+        "gate": "XPowGate",
+        "name": "Type GateFamily: cirq.ops.common_gates.XPowGate",
+        "description": "Accepts `cirq.Gate` instances `g` s.t. `isinstance(g, cirq.ops.common_gates.XPowGate)`",
+        "ignore_global_phase": true
       },
       {
         "cirq_type": "Duration",
@@ -139,19 +131,11 @@
     ],
     [
       {
-        "cirq_type": "Gateset",
-        "gates": [
-          {
-            "cirq_type": "GateFamily",
-            "gate": "YPowGate",
-            "name": "Type GateFamily: cirq.ops.common_gates.YPowGate",
-            "description": "Accepts `cirq.Gate` instances `g` s.t. `isinstance(g, cirq.ops.common_gates.YPowGate)`",
-            "ignore_global_phase": true
-          }
-        ],
-        "name": null,
-        "unroll_circuit_op": true,
-        "accept_global_phase_op": true
+        "cirq_type": "GateFamily",
+        "gate": "YPowGate",
+        "name": "Type GateFamily: cirq.ops.common_gates.YPowGate",
+        "description": "Accepts `cirq.Gate` instances `g` s.t. `isinstance(g, cirq.ops.common_gates.YPowGate)`",
+        "ignore_global_phase": true
       },
       {
         "cirq_type": "Duration",
@@ -160,19 +144,11 @@
     ],
     [
       {
-        "cirq_type": "Gateset",
-        "gates": [
-          {
-            "cirq_type": "GateFamily",
-            "gate": "ZPowGate",
-            "name": "Type GateFamily: cirq.ops.common_gates.ZPowGate",
-            "description": "Accepts `cirq.Gate` instances `g` s.t. `isinstance(g, cirq.ops.common_gates.ZPowGate)`",
-            "ignore_global_phase": true
-          }
-        ],
-        "name": null,
-        "unroll_circuit_op": true,
-        "accept_global_phase_op": true
+        "cirq_type": "GateFamily",
+        "gate": "ZPowGate",
+        "name": "Type GateFamily: cirq.ops.common_gates.ZPowGate",
+        "description": "Accepts `cirq.Gate` instances `g` s.t. `isinstance(g, cirq.ops.common_gates.ZPowGate)`",
+        "ignore_global_phase": true
       },
       {
         "cirq_type": "Duration",
@@ -180,4 +156,4 @@
       }
     ]
   ]
-}
+}
\ No newline at end of file
diff --git a/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.repr b/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.repr
--- a/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.repr
+++ b/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.repr
@@ -1 +1 @@
-cirq.GridDeviceMetadata(frozenset({(cirq.GridQubit(0, 2), cirq.GridQubit(1, 2)), (cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)), (cirq.GridQubit(0, 1), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), (cirq.GridQubit(0, 1), cirq.GridQubit(0, 2)), (cirq.GridQubit(1, 1), cirq.GridQubit(1, 2)), (cirq.GridQubit(1, 0), cirq.GridQubit(1, 1))}), cirq.Gateset(cirq.ops.common_gates.XPowGate, cirq.ops.common_gates.YPowGate, cirq.ops.common_gates.ZPowGate, unroll_circuit_op = True,accept_global_phase_op = True), {cirq.Gateset(cirq.ops.common_gates.XPowGate, unroll_circuit_op = True,accept_global_phase_op = True): cirq.Duration(nanos=1), cirq.Gateset(cirq.ops.common_gates.YPowGate, unroll_circuit_op = True,accept_global_phase_op = True): cirq.Duration(picos=1), cirq.Gateset(cirq.ops.common_gates.ZPowGate, unroll_circuit_op = True,accept_global_phase_op = True): cirq.Duration(picos=1)})
\ No newline at end of file
+cirq.GridDeviceMetadata(frozenset({(cirq.GridQubit(0, 2), cirq.GridQubit(1, 2)), (cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)), (cirq.GridQubit(0, 1), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), (cirq.GridQubit(0, 1), cirq.GridQubit(0, 2)), (cirq.GridQubit(1, 1), cirq.GridQubit(1, 2)), (cirq.GridQubit(1, 0), cirq.GridQubit(1, 1))}), cirq.Gateset(cirq.ops.common_gates.XPowGate, cirq.ops.common_gates.YPowGate, cirq.ops.common_gates.ZPowGate), {cirq.GateFamily(cirq.ops.common_gates.XPowGate): cirq.Duration(nanos=1), cirq.GateFamily(cirq.ops.common_gates.YPowGate): cirq.Duration(picos=1), cirq.GateFamily(cirq.ops.common_gates.ZPowGate): cirq.Duration(picos=1)})
\ No newline at end of file