Repository: quantumlib/Cirq

Issue:
MSGate not in top level 
**Description of the issue**

`cirq.MSGate` does not exist, but `cirq.ops.MSGate` does.  Think it should be at the top level too?




Discussion:
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days
Adding triage discuss to determine whether to expose at the top level.
Ill take this issue

Resolving patch:
diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py
--- a/cirq-core/cirq/__init__.py
+++ b/cirq-core/cirq/__init__.py
@@ -240,6 +240,7 @@
     MatrixGate,
     MixedUnitaryChannel,
     M,
+    MSGate,
     measure,
     measure_each,
     measure_paulistring_terms,
diff --git a/cirq-core/cirq/json_resolver_cache.py b/cirq-core/cirq/json_resolver_cache.py
--- a/cirq-core/cirq/json_resolver_cache.py
+++ b/cirq-core/cirq/json_resolver_cache.py
@@ -157,6 +157,7 @@ def _symmetricalqidpair(qids):
         'LineTopology': cirq.LineTopology,
         'Linspace': cirq.Linspace,
         'ListSweep': cirq.ListSweep,
+        'cirq.MSGate': cirq.MSGate,
         'MatrixGate': cirq.MatrixGate,
         'MixedUnitaryChannel': cirq.MixedUnitaryChannel,
         'MeasurementKey': cirq.MeasurementKey,
diff --git a/cirq-core/cirq/ops/parity_gates.py b/cirq-core/cirq/ops/parity_gates.py
--- a/cirq-core/cirq/ops/parity_gates.py
+++ b/cirq-core/cirq/ops/parity_gates.py
@@ -399,6 +399,11 @@ def __repr__(self) -> str:
             return 'cirq.ms(np.pi/2)'
         return f'cirq.ms({self._exponent!r}*np.pi/2)'
 
+    # the default namespace is already occupied by cirq_ionq.MSGate
+    @classmethod
+    def _json_namespace_(cls) -> str:
+        return 'cirq'
+
     def _json_dict_(self) -> Dict[str, Any]:
         return protocols.obj_to_dict_helper(self, ["rads"])
 


Test patch:
diff --git a/cirq-core/cirq/ops/parity_gates_test.py b/cirq-core/cirq/ops/parity_gates_test.py
--- a/cirq-core/cirq/ops/parity_gates_test.py
+++ b/cirq-core/cirq/ops/parity_gates_test.py
@@ -258,7 +258,7 @@ def test_trace_distance():
 def test_ms_arguments():
     eq_tester = cirq.testing.EqualsTester()
     eq_tester.add_equality_group(
-        cirq.ms(np.pi / 2), cirq.ops.MSGate(rads=np.pi / 2), cirq.XXPowGate(global_shift=-0.5)
+        cirq.ms(np.pi / 2), cirq.MSGate(rads=np.pi / 2), cirq.XXPowGate(global_shift=-0.5)
     )
     eq_tester.add_equality_group(
         cirq.ms(np.pi / 4), cirq.XXPowGate(exponent=0.5, global_shift=-0.5)
@@ -323,15 +323,7 @@ def test_ms_diagrams():
 
 
 def test_json_serialization():
-    def custom_resolver(cirq_type: str):
-        if cirq_type == "MSGate":
-            return cirq.ops.MSGate
-        return None
-
-    assert cirq.read_json(
-        json_text=cirq.to_json(cirq.ms(np.pi / 2)), resolvers=[custom_resolver]
-    ) == cirq.ms(np.pi / 2)
-    assert custom_resolver('X') is None
+    assert cirq.read_json(json_text=cirq.to_json(cirq.ms(np.pi / 2))) == cirq.ms(np.pi / 2)
 
 
 @pytest.mark.parametrize('gate_cls', (cirq.XXPowGate, cirq.YYPowGate, cirq.ZZPowGate))
diff --git a/cirq-core/cirq/protocols/json_test_data/cirq.MSGate.json b/cirq-core/cirq/protocols/json_test_data/cirq.MSGate.json
new file mode 100644
--- /dev/null
+++ b/cirq-core/cirq/protocols/json_test_data/cirq.MSGate.json
@@ -0,0 +1,4 @@
+{
+  "cirq_type": "cirq.MSGate",
+  "rads": 1.5707963267948966
+}
diff --git a/cirq-core/cirq/protocols/json_test_data/cirq.MSGate.repr b/cirq-core/cirq/protocols/json_test_data/cirq.MSGate.repr
new file mode 100644
--- /dev/null
+++ b/cirq-core/cirq/protocols/json_test_data/cirq.MSGate.repr
@@ -0,0 +1 @@
+cirq.ms(np.pi/2)
\ No newline at end of file
diff --git a/cirq-core/cirq/protocols/json_test_data/spec.py b/cirq-core/cirq/protocols/json_test_data/spec.py
--- a/cirq-core/cirq/protocols/json_test_data/spec.py
+++ b/cirq-core/cirq/protocols/json_test_data/spec.py
@@ -22,6 +22,7 @@
     name="cirq",
     packages=[cirq, cirq.work],
     test_data_path=pathlib.Path(__file__).parent,
+    custom_class_name_to_cirq_type={"MSGate": "cirq.MSGate"},
     resolver_cache=_class_resolver_dictionary(),
     not_yet_serializable=[
         'Alignment',