Repository: OpenMined/PySyft

Issue:
Data subject checks fail when using the result of an operation like sum
In several places in the codebase, we do the following check:
`if (self.data_subjects == other.data_subjects).all():`

If `self` and `other` is the result of an operation like sum, then this `self.data_subjects == other.data_subjects` returns a single Bool (since we're comparing two singletons) instead of an array of booleans (which has the attribute `all()`). This causes the check above to fail: 


![Image](https://user-images.githubusercontent.com/32711264/199333586-613306b6-92d1-48f8-93c2-7087366438b3.png)




Discussion:
This will also cause failures for methods like __mod__, __truediv__, __eq__, etc
Agreed. Since we are trying to support Tensors and Scalars in the same data types we should be more careful and perhaps introduce an `equality` check function we can call which checks the sizes and does the correct comparison in the event of a Scalar.

Resolving patch:
diff --git a/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py b/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py
--- a/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py
+++ b/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py
@@ -425,7 +425,7 @@ def _apply_op(
             Tuple[MPCTensor,Union[MPCTensor,int,float,np.ndarray]] : Result of the operation
         """
         if isinstance(other, TensorWrappedPhiTensorPointer):
-            if (self.data_subjects != other.data_subjects).all():  # type: ignore
+            if np.array(self.data_subjects != other.data_subjects).all():  # type: ignore
                 return getattr(self.gamma, op_str)(other.gamma)
         elif isinstance(other, TensorWrappedGammaTensorPointer):
             return getattr(self.gamma, op_str)(other)
@@ -2744,7 +2744,7 @@ def __sub__(self, other: SupportedChainType) -> Union[PhiTensor, GammaTensor]:
     def __mul__(self, other: SupportedChainType) -> Union[PhiTensor, GammaTensor]:
 
         if isinstance(other, PhiTensor):
-            if (self.data_subjects == other.data_subjects).all():
+            if np.array(self.data_subjects == other.data_subjects).all():
                 min_min = self.min_vals.data * other.min_vals.data
                 min_max = self.min_vals.data * other.max_vals.data
                 max_min = self.max_vals.data * other.min_vals.data
@@ -2794,7 +2794,7 @@ def __mul__(self, other: SupportedChainType) -> Union[PhiTensor, GammaTensor]:
 
     def __truediv__(self, other: Any) -> Union[PhiTensor, GammaTensor]:
         if isinstance(other, PhiTensor):
-            if (self.data_subjects != other.data_subjects).all():
+            if np.array(self.data_subjects != other.data_subjects).all():
                 return self.gamma / other.gamma
             else:
                 min_min = self.min_vals.data / other.min_vals.data
@@ -3250,7 +3250,7 @@ def concatenate(
 
     def __lt__(self, other: SupportedChainType) -> Union[PhiTensor, GammaTensor]:
         if isinstance(other, PhiTensor):
-            if (self.data_subjects == other.data_subjects).all():
+            if np.array(self.data_subjects == other.data_subjects).all():
                 return PhiTensor(
                     child=(self.child < other.child) * 1,
                     data_subjects=self.data_subjects,
@@ -3284,7 +3284,7 @@ def __le__(self, other: SupportedChainType) -> Union[PhiTensor, GammaTensor]:
 
         # if the tensor being compared is also private
         if isinstance(other, PhiTensor):
-            if (self.data_subjects == other.data_subjects).all():
+            if np.array(self.data_subjects == other.data_subjects).all():
                 return PhiTensor(
                     child=(self.child <= other.child) * 1,
                     data_subjects=self.data_subjects,
@@ -3318,7 +3318,7 @@ def __gt__(self, other: SupportedChainType) -> Union[PhiTensor, GammaTensor]:
 
         # if the tensor being compared is also private
         if isinstance(other, PhiTensor):
-            if (self.data_subjects == other.data_subjects).all():
+            if np.array(self.data_subjects == other.data_subjects).all():
                 return PhiTensor(
                     child=(self.child > other.child) * 1,
                     data_subjects=self.data_subjects,
@@ -3351,7 +3351,7 @@ def __ge__(self, other: SupportedChainType) -> Union[PhiTensor, GammaTensor]:
 
         # if the tensor being compared is also private
         if isinstance(other, PhiTensor):
-            if (self.data_subjects == other.data_subjects).all():
+            if np.array(self.data_subjects == other.data_subjects).all():
                 return PhiTensor(
                     child=(self.child >= other.child) * 1,
                     data_subjects=self.data_subjects,
@@ -3809,7 +3809,7 @@ def __floordiv__(self, other: Any) -> Union[PhiTensor, GammaTensor]:
         return self // value.
         """
         if isinstance(other, PhiTensor):
-            if (self.data_subjects != other.data_subjects).all():
+            if np.array(self.data_subjects != other.data_subjects).all():
                 return self.gamma // other.gamma
             else:
                 min_min = self.min_vals.data // other.min_vals.data


Test patch: