Repository: quantumlib/Cirq

Issue:
equivalent `MSGate` and `XXPowGate` instances don't evaluate as such
**Description of the issue**

`cirq.XXPowGate(exponent=0.5, global_shift=-0.5)` and `cirq.ms(x*π/4)` are equivalent, but evaluate as unequal (even up to global phase)

**How to reproduce the issue**

```python
gate1 = cirq.ms(np.pi / 4)
gate2 = cirq.XXPowGate(exponent=0.5, global_shift=-0.5)

assert np.array_equal(cirq.unitary(gate1), cirq.unitary(gate2))  # ok

assert gate1 == gate2  # fails
assert cirq.approx_eq(gate1, gate2)  # fails
assert cirq.equal_up_to_global_phase(gate1, gate2)  # fails
assert gate1 in cirq.GateFamily(gate2)  # fails
```


i would expect all of these to pass, given that the same thing works correctly for Pauli gate subclasses e.g.,
```python
assert cirq.rx(np.pi / 2) == cirq.XPowGate(exponent=0.5, global_shift=-0.5)  # ok
assert cirq.approx_eq(cirq.rx(np.pi / 2), cirq.XPowGate(exponent=0.5, global_shift=-0.5))  # ok
assert cirq.equal_up_to_global_phase(cirq.rx(np.pi / 2), cirq.X**0.5)  # ok
assert cirq.rx(np.pi / 2) in cirq.GateFamily(cirq.X**0.5)  # ok
```


**Cirq version**

```
1.3.0.dev20230801170401
```


Discussion:

Resolving patch:
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
@@ -28,6 +28,7 @@
     import cirq
 
 
+@value.value_equality
 class XXPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
     r"""The X-parity gate, possibly raised to a power.
 
@@ -133,6 +134,7 @@ def __repr__(self) -> str:
         )
 
 
+@value.value_equality
 class YYPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
     r"""The Y-parity gate, possibly raised to a power.
 
@@ -237,6 +239,7 @@ def __repr__(self) -> str:
         )
 
 
+@value.value_equality
 class ZZPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
     r"""The Z-parity gate, possibly raised to a power.
 


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
@@ -257,8 +257,24 @@ 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))
-    eq_tester.add_equality_group(cirq.XXPowGate(global_shift=-0.5))
+    eq_tester.add_equality_group(
+        cirq.ms(np.pi / 2), cirq.ops.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)
+    )
+    eq_tester.add_equality_group(cirq.XX)
+    eq_tester.add_equality_group(cirq.XX**0.5)
+
+
+def test_ms_equal_up_to_global_phase():
+    assert cirq.equal_up_to_global_phase(cirq.ms(np.pi / 2), cirq.XX)
+    assert cirq.equal_up_to_global_phase(cirq.ms(np.pi / 4), cirq.XX**0.5)
+    assert not cirq.equal_up_to_global_phase(cirq.ms(np.pi / 4), cirq.XX)
+
+    assert cirq.ms(np.pi / 2) in cirq.GateFamily(cirq.XX)
+    assert cirq.ms(np.pi / 4) in cirq.GateFamily(cirq.XX**0.5)
+    assert cirq.ms(np.pi / 4) not in cirq.GateFamily(cirq.XX)
 
 
 def test_ms_str():