Repository: openmined/pysyft
Issue: `np.ones_like` doesn't work on data pointers

## Description
`np.ones_like(d_ptr)` throws `ValueError: Invalid Numpy Operation: ones_like for DSA`.

## How to Reproduce

```py
import syft as sy
import numpy as np

domain_client = sy.login(email="info@openmined.org", password="changethis", port=8081)

arr = np.random.randint(0, 10000, (10000))

data = sy.Tensor(arr).annotate_with_dp_metadata(-1, 11000, data_subjects="Bob")
data_ptr = data.send(domain_client, tags=["my_data"])

domain_client.create_user(name="DS", email="test@email.com", password="password", budget=99999)
ds_client = sy.login(email="test@email.com", password="password", port=8081)

d_ptr = ds_client.store[data_ptr.id_at_location]

np.ones_like(d_ptr)
```

```py
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In [7], line 1
----> 1 np.ones_like(d_ptr)

File <__array_function__ internals>:180, in ones_like(*args, **kwargs)

File ~/pysyft/packages/syft/src/syft/core/tensor/passthrough.py:698, in PassthroughTensor.__array_function__(self, func, types, args, kwargs)
    696 implementation = query_implementation(self.__class__, func)
    697 if implementation:
--> 698     return implementation(*args, **kwargs)
    699 return self.__class__(func(*args, **kwargs))

File ~/pysyft/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py:1693, in ones_like(tensor, *args, **kwargs)
   1687 @implements(TensorWrappedPhiTensorPointer, np.ones_like)
   1688 def ones_like(
   1689     tensor: TensorWrappedPhiTensorPointer,
   1690     *args: Any,
   1691     **kwargs: Any,
   1692 ) -> TensorWrappedPhiTensorPointer:
-> 1693     return tensor.ones_like(*args, **kwargs)

File ~/pysyft/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py:962, in TensorWrappedPhiTensorPointer.ones_like(self, *args, **kwargs)
    949 def ones_like(
    950     self,
    951     *args: Any,
    952     **kwargs: Any,
    953 ) -> TensorWrappedPhiTensorPointer:
    954     """Apply the "ones_like" operation between "self" and "other"
    955 
    956     Args:
   (...)
    960         Union[TensorWrappedPhiTensorPointer,MPCTensor] : Result of the operation.
    961     """
--> 962     return self._apply_self_tensor_op("ones_like", *args, **kwargs)

File ~/pysyft/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py:318, in TensorWrappedPhiTensorPointer._apply_self_tensor_op(self, op_str, *args, **kwargs)
    316         data_subjects = self.data_subjects
    317 else:
--> 318     raise ValueError(f"Invalid Numpy Operation: {op_str} for DSA")
    320 result = TensorWrappedPhiTensorPointer(
    321     data_subjects=data_subjects,
    322     min_vals=min_vals,
    323     max_vals=max_vals,
    324     client=self.client,
    325 )
    327 # QUESTION can the id_at_location be None?

ValueError: Invalid Numpy Operation: ones_like for DSA
```

## Expected Behavior
A clear and concise description of what you expected to happen.

## Screenshots
If applicable, add screenshots to help explain your problem.

## System Information
 - OS: [e.g. iOS]
 - OS Version: [e.g. 22]
 - Language Version: [e.g. Python 3.7, Node 10.18.1]
 - Package Manager Version: [e.g. Conda 4.6.1, NPM 6.14.1]
 - Browser (if applicable): [e.g. Google Chrome]
 - Browser Version (if applicable): [e.g. 81.0.4044.138]

## Additional Context
Add any other context about the problem here.


Changed files:
packages/syft/src/syft/core/tensor/autodp/gamma_tensor.py, packages/syft/src/syft/core/tensor/lazy_repeat_array.py, packages/syft/src/syft/core/tensor/autodp/phi_tensor.py, packages/syft/tests/syft/core/tensor/adp/gamma_tensor_test.py, packages/syft/tests/syft/core/tensor/adp/phi_tensor_test.py

diff --git a/packages/syft/src/syft/core/tensor/autodp/gamma_tensor.py b/packages/syft/src/syft/core/tensor/autodp/gamma_tensor.py
index 2d085a484..f471db9bc 100644
--- a/packages/syft/src/syft/core/tensor/autodp/gamma_tensor.py
+++ b/packages/syft/src/syft/core/tensor/autodp/gamma_tensor.py
@@ -377,6 +377,8 @@ class TensorWrappedGammaTensorPointer(Pointer, PassthroughTensor):
                 data_subjects = getattr(self.data_subjects, op_str)(*args, **kwargs)
             if op_str in INPLACE_OPS:
                 data_subjects = self.data_subjects
+        elif op_str in ("ones_like", "zeros_like"):
+            data_subjects = self.data_subjects
         else:
             raise ValueError(f"Invalid Numpy Operation: {op_str} for DSA")
 
@@ -1382,12 +1384,27 @@ class TensorWrappedGammaTensorPointer(Pointer, PassthroughTensor):
         """
         return self._apply_self_tensor_op("__getitem__", key)
 
+    def zeros_like(
+        self,
+        *args: Any,
+        **kwargs: Any,
+    ) -> Union[TensorWrappedGammaTensorPointer, MPCTensor]:
+        """Apply the "zeros_like" operation on "self"
+
+        Args:
+            y (Union[TensorWrappedGammaTensorPointer,MPCTensor,int,float,np.ndarray]) : second operand.
+
+        Returns:
+            Union[TensorWrappedGammaTensorPointer,MPCTensor] : Result of the operation.
+        """
+        return self._apply_self_tensor_op("zeros_like", *args, **kwargs)
+
     def ones_like(
         self,
         *args: Any,
         **kwargs: Any,
     ) -> Union[TensorWrappedGammaTensorPointer, MPCTensor]:
-        """Apply the "ones like" operation on self"
+        """Apply the "ones_like" operation on "self"
 
         Args:
             y (Union[TensorWrappedGammaTensorPointer,MPCTensor,int,float,np.ndarray]) : second operand.
@@ -1765,6 +1782,15 @@ class TensorWrappedGammaTensorPointer(Pointer, PassthroughTensor):
         )
 
 
+@implements(TensorWrappedGammaTensorPointer, np.zeros_like)
+def zeros_like(
+    tensor: TensorWrappedGammaTensorPointer,
+    *args: Any,
+    **kwargs: Any,
+) -> TensorWrappedGammaTensorPointer:
+    return tensor.zeros_like(*args, **kwargs)
+
+
 @implements(TensorWrappedGammaTensorPointer, np.ones_like)
 def ones_like(
     tensor: TensorWrappedGammaTensorPointer,
diff --git a/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py b/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py
index e1086f792..48b717237 100644
--- a/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py
+++ b/packages/syft/src/syft/core/tensor/autodp/phi_tensor.py
@@ -322,6 +322,8 @@ class TensorWrappedPhiTensorPointer(Pointer, PassthroughTensor):
                 data_subjects = getattr(self.data_subjects, op_str)(*args, **kwargs)
             if op_str in INPLACE_OPS:
                 data_subjects = self.data_subjects
+        elif op_str in ("ones_like", "zeros_like"):
+            data_subjects = self.data_subjects
         else:
             raise ValueError(f"Invalid Numpy Operation: {op_str} for DSA")
 
@@ -954,12 +956,27 @@ class TensorWrappedPhiTensorPointer(Pointer, PassthroughTensor):
         """
         return self._apply_self_tensor_op("__getitem__", key)
 
+    def zeros_like(
+        self,
+        *args: Any,
+        **kwargs: Any,
+    ) -> TensorWrappedPhiTensorPointer:
+        """Apply the "zeros_like" operation on "self"
+
+        Args:
+            y (Union[TensorWrappedPhiTensorPointer,MPCTensor,int,float,np.ndarray]) : second operand.
+
+        Returns:
+            Union[TensorWrappedPhiTensorPointer,MPCTensor] : Result of the operation.
+        """
+        return self._apply_self_tensor_op("zeros_like", *args, **kwargs)
+
     def ones_like(
         self,
         *args: Any,
         **kwargs: Any,
     ) -> TensorWrappedPhiTensorPointer:
-        """Apply the "ones_like" operation between "self" and "other"
+        """Apply the "ones_like" operation on "self"
 
         Args:
             y (Union[TensorWrappedPhiTensorPointer,MPCTensor,int,float,np.ndarray]) : second operand.
@@ -1692,6 +1709,15 @@ class TensorWrappedPhiTensorPointer(Pointer, PassthroughTensor):
         return self._apply_self_tensor_op("reshape", *args, **kwargs)
 
 
+@implements(TensorWrappedPhiTensorPointer, np.zeros_like)
+def zeros_like(
+    tensor: TensorWrappedPhiTensorPointer,
+    *args: Any,
+    **kwargs: Any,
+) -> TensorWrappedPhiTensorPointer:
+    return tensor.zeros_like(*args, **kwargs)
+
+
 @implements(TensorWrappedPhiTensorPointer, np.ones_like)
 def ones_like(
     tensor: TensorWrappedPhiTensorPointer,
diff --git a/packages/syft/src/syft/core/tensor/lazy_repeat_array.py b/packages/syft/src/syft/core/tensor/lazy_repeat_array.py
index 5499f2494..13e770f00 100644
--- a/packages/syft/src/syft/core/tensor/lazy_repeat_array.py
+++ b/packages/syft/src/syft/core/tensor/lazy_repeat_array.py
@@ -536,6 +536,9 @@ def compute_min_max(
         dummy_res = np.empty(x_min_vals.shape).max(*args, **kwargs)
         min_vals = lazyrepeatarray(data=x_min_vals.data, shape=dummy_res.shape)
         max_vals = lazyrepeatarray(data=x_max_vals.data, shape=dummy_res.shape)
+    elif op_str == "zeros_like":
+        min_vals = x_min_vals.zeros_like(*args, **kwargs)
+        max_vals = x_max_vals.zeros_like(*args, **kwargs)
     elif op_str == "ones_like":
         min_vals = x_min_vals.ones_like(*args, **kwargs)
         max_vals = x_max_vals.ones_like(*args, **kwargs)
diff --git a/packages/syft/tests/syft/core/tensor/adp/gamma_tensor_test.py b/packages/syft/tests/syft/core/tensor/adp/gamma_tensor_test.py
index 5dffb5413..16f7100d3 100644
--- a/packages/syft/tests/syft/core/tensor/adp/gamma_tensor_test.py
+++ b/packages/syft/tests/syft/core/tensor/adp/gamma_tensor_test.py
@@ -157,6 +157,50 @@ def test_gamma_publish(
     print(ledger_store.kv_store)
 
 
+def test_zeros_like(
+    reference_data: np.ndarray,
+    upper_bound: np.ndarray,
+    lower_bound: np.ndarray,
+    ishan: DataSubjectArray,
+) -> None:
+    data_subjects = np.broadcast_to(
+        np.array(DataSubjectArray(["eagle", "potato"])), reference_data.shape
+    )
+    reference_tensor = GammaTensor(
+        child=reference_data,
+        data_subjects=data_subjects,
+        max_vals=upper_bound,
+        min_vals=lower_bound,
+    )
+    output = reference_tensor.zeros_like()
+    assert np.all(output.child == 0)
+    assert output.min_vals.shape == reference_tensor.min_vals.shape
+    assert output.max_vals.shape == reference_tensor.max_vals.shape
+    assert (output.data_subjects == reference_tensor.data_subjects).all()
+
+
+def test_ones_like(
+    reference_data: np.ndarray,
+    upper_bound: np.ndarray,
+    lower_bound: np.ndarray,
+    ishan: DataSubjectArray,
+) -> None:
+    data_subjects = np.broadcast_to(
+        np.array(DataSubjectArray(["eagle", "potato"])), reference_data.shape
+    )
+    reference_tensor = GammaTensor(
+        child=reference_data,
+        data_subjects=data_subjects,
+        max_vals=upper_bound,
+        min_vals=lower_bound,
+    )
+    output = reference_tensor.ones_like()
+    assert np.all(output.child == 1)
+    assert output.min_vals.shape == reference_tensor.min_vals.shape
+    assert output.max_vals.shape == reference_tensor.max_vals.shape
+    assert (output.data_subjects == reference_tensor.data_subjects).all()
+
+
 def test_sum(
     reference_data: np.ndarray,
     upper_bound: np.ndarray,
diff --git a/packages/syft/tests/syft/core/tensor/adp/phi_tensor_test.py b/packages/syft/tests/syft/core/tensor/adp/phi_tensor_test.py
index 9b4af1c6d..5ae4a0ba0 100644
--- a/packages/syft/tests/syft/core/tensor/adp/phi_tensor_test.py
+++ b/packages/syft/tests/syft/core/tensor/adp/phi_tensor_test.py
@@ -131,6 +131,46 @@ def test_eq(
     ).all(), "Equality between identical PTs fails"
 
 
+def test_zeros_like(
+    reference_data: np.ndarray,
+    upper_bound: np.ndarray,
+    lower_bound: np.ndarray,
+    ishan: DataSubjectArray,
+) -> None:
+    ishan = np.broadcast_to(ishan, reference_data.shape)
+    reference_tensor = PT(
+        child=reference_data,
+        data_subjects=ishan,
+        max_vals=upper_bound,
+        min_vals=lower_bound,
+    )
+    output = reference_tensor.zeros_like()
+    assert np.all(output.child == 0)
+    assert output.min_vals.shape == reference_tensor.min_vals.shape
+    assert output.max_vals.shape == reference_tensor.max_vals.shape
+    assert (output.data_subjects == reference_tensor.data_subjects).all()
+
+
+def test_ones_like(
+    reference_data: np.ndarray,
+    upper_bound: np.ndarray,
+    lower_bound: np.ndarray,
+    ishan: DataSubjectArray,
+) -> None:
+    ishan = np.broadcast_to(ishan, reference_data.shape)
+    reference_tensor = PT(
+        child=reference_data,
+        data_subjects=ishan,
+        max_vals=upper_bound,
+        min_vals=lower_bound,
+    )
+    output = reference_tensor.ones_like()
+    assert np.all(output.child == 1)
+    assert output.min_vals.shape == reference_tensor.min_vals.shape
+    assert output.max_vals.shape == reference_tensor.max_vals.shape
+    assert (output.data_subjects == reference_tensor.data_subjects).all()
+
+
 def test_add_wrong_types(
     reference_data: np.ndarray,
     upper_bound: np.ndarray,