Repository: OpenMined/PySyft

Issue:
Comparison between FPT and AST doesn't always works
## Description
Some cases of handling comparison between FixedPrecision and AdditiveSharingTensor are supported, but some are not. We should systematize this.

## How to Reproduce
```python
t1 = torch.tensor([1.2, 1]).fix_precision().share(*workers, crypto_provider=crypto_provider, protocol="fss")
t2 = torch.tensor([1.2, 1]).fix_precision()

t1 > t2 # FAILS but t1 < t2 works
```

## Stacktrace
```
AttributeError                            Traceback (most recent call last)
<ipython-input-10-c55d3fcd7179> in <module>
      2 t2 = torch.tensor([1.2, 1]).fix_precision()#.share(*workers, crypto_provider=crypto_provider, protocol="fss", requires_grad=True)
      3 
----> 4 t1 > t2

~/code/PySyft/syft/generic/frameworks/hook/hook.py in overloaded_native_method(self, *args, **kwargs)
    218                 # Send the new command to the appropriate class and get the response
    219                 method = getattr(new_self, method_name)
--> 220                 response = method(*new_args, **new_kwargs)
    221 
    222                 # For inplace methods, just directly return self

~/code/PySyft/syft/generic/frameworks/overload.py in _hook_method_args(self, *args, **kwargs)
     25 
     26             # Send it to the appropriate class and get the response
---> 27             response = attr(self, new_self, *new_args, **new_kwargs)
     28 
     29             # Put back SyftTensor on the tensors found in the response

~/code/PySyft/syft/frameworks/torch/tensors/interpreters/precision.py in __gt__(self, _self, other)
    821     def __gt__(self, _self, other):
    822         print("FPT gt", _self, other)
--> 823         result = _self.__gt__(other)
    824         return result.type(self.torch_dtype) * self.base ** self.precision_fractional
    825 

~/code/PySyft/syft/frameworks/torch/mpc/__init__.py in method(self, *args, **kwargs)
     33         def method(self, *args, **kwargs):
     34             f = protocol_store[(name, self.protocol)]
---> 35             return f(self, *args, **kwargs)
     36 
     37         return method

~/code/PySyft/syft/frameworks/torch/tensors/interpreters/additive_shared.py in __gt__(self, other)
    938     @crypto_protocol("fss")
    939     def __gt__(self, other):
--> 940         return (other + 1) <= self
    941 
    942     def ge(self, other):

~/code/PySyft/syft/generic/frameworks/hook/hook.py in overloaded_native_method(self, *args, **kwargs)
    156                     # arguments
    157                     if not isinstance(args[0].child, PointerTensor):
--> 158                         self = type(args[0].child)().on(self, wrap=True)
    159                         args = [args[0]]
    160                         return overloaded_native_method(self, *args, **kwargs)

AttributeError: 'dict' object has no attribute 'on'

```



Discussion:

Resolving patch:
diff --git a/syft/frameworks/torch/tensors/interpreters/additive_shared.py b/syft/frameworks/torch/tensors/interpreters/additive_shared.py
--- a/syft/frameworks/torch/tensors/interpreters/additive_shared.py
+++ b/syft/frameworks/torch/tensors/interpreters/additive_shared.py
@@ -929,7 +929,7 @@ def __gt__(self, other):
 
     @crypto_protocol("fss")
     def __gt__(self, other):
-        return (other + 1) <= self
+        return -self <= -(other + 1)
 
     def ge(self, other):
         return (self - other).positive()


Test patch:
diff --git a/test/torch/tensors/test_additive_shared.py b/test/torch/tensors/test_additive_shared.py
--- a/test/torch/tensors/test_additive_shared.py
+++ b/test/torch/tensors/test_additive_shared.py
@@ -1325,3 +1325,62 @@ def test_garbage_collect_mul(workers):
 
     assert len(alice.object_store._objects) == 3
     assert len(bob.object_store._objects) == 3
+
+
+@pytest.mark.parametrize("protocol", ["fss"])
+@pytest.mark.parametrize("force_preprocessing", [True, False])
+def test_comp_ast_fpt(workers, protocol, force_preprocessing):
+    me, alice, bob, crypto_provider = (
+        workers["me"],
+        workers["alice"],
+        workers["bob"],
+        workers["james"],
+    )
+
+    if force_preprocessing:
+        me.crypto_store.provide_primitives("fss_comp", [alice, bob], n_instances=50)
+
+    args = (alice, bob)
+    kwargs = {"protocol": protocol, "crypto_provider": crypto_provider}
+
+    # for x as AST and  y as FPT
+    # we currently support this set of operation only for fss protocol
+    t1 = torch.tensor([-2.1, 1.8])
+    t2 = torch.tensor([-3.1, 0.3])
+    x = t1.fix_prec().share(*args, **kwargs)
+    y = t2.fix_prec()
+
+    assert ((x >= y).get().float_prec() == (t1 >= t2)).all()
+    assert ((x <= y).get().float_prec() == (t1 <= t2)).all()
+    assert ((x > y).get().float_prec() == (t1 > t2)).all()
+    assert ((x < y).get().float_prec() == (t1 < t2)).all()
+
+    t1 = torch.tensor([[-2.1, 1.8], [-1.1, -0.7]])
+    t2 = torch.tensor([[-3.1, 0.3], [-1.1, 0.3]])
+    x = t1.fix_prec().share(*args, **kwargs)
+    y = t2.fix_prec()
+
+    assert ((x >= y).get().float_prec() == (t1 >= t2)).all()
+    assert ((x <= y).get().float_prec() == (t1 <= t2)).all()
+    assert ((x > y).get().float_prec() == (t1 > t2)).all()
+    assert ((x < y).get().float_prec() == (t1 < t2)).all()
+
+
+@pytest.mark.parametrize("protocol", ["fss"])
+def test_eq_ast_fpt(workers, protocol):
+    me, alice, bob, crypto_provider = (
+        workers["me"],
+        workers["alice"],
+        workers["bob"],
+        workers["james"],
+    )
+
+    args = (alice, bob)
+    kwargs = {"protocol": protocol, "crypto_provider": crypto_provider}
+
+    # for x as AST and  y as FPT
+    # we currently support this set of operation only for fss protocol
+    x = torch.tensor([-3.1]).fix_prec().share(*args, **kwargs)
+    y = torch.tensor([-3.1]).fix_prec()
+
+    assert (x == y).get().float_prec()