Pysyft: Wrong behavior in comparison operator

Created on 15 Dec 2018  路  5Comments  路  Source: OpenMined/PySyft

Pls confirm the behavior below. The behavior seems wrong.

import syft as sy

hook = sy.TorchHook(verbose=False)
me = hook.local_worker
me.is_client_worker = False

bob = sy.VirtualWorker(id="bob", hook=hook, is_client_worker=False)
alice = sy.VirtualWorker(id="alice", hook=hook, is_client_worker=False)
workers = [bob, alice]

y = sy.FloatTensor([1.]).fix_precision().share(*workers)
x = sy.FloatTensor([-1.]).fix_precision().share(*workers)

print(any(y<0), all(y<0))
print(any(x<0), all(x<0))
# False True
# False True

All 5 comments

Hi @0shimax,

TL;DR: I think this is the current expected behaviour, not sure if intended or not, but it seems to be the way that "wrapper" tensors are implemented.

To be clear I'm definetly not the best person to say if this is expected behaviour or not, but I would say that if we consider that fix_precision creates a FixedPrecisionTensor_ parent to the FloatTensor which is basically a wrapper to the sy.FloatTensor, it is expected behavior.

The wrapper doesn't behave as a tensor. What I mean with this is that if you compare the tensor values to any number it will not have direct access to the values on the tensor, it will just return a Fixed precision tensor:

>>> sy.FloatTensor([-1.0, 1.0, 11]).fix_precision() < 0
[Fixed precision tensor]

In other words, if you try to iterate on this tensor it's empty.

>>> [tensor for tensor in sy.FloatTensor([-1.0, 1.0, 11])]
[-1.0, 1.0, 11.0]

>>> [tensor for tensor in sy.FloatTensor([-1.0, 1.0, 11]).fix_precision()]
[]

On the other hand if you run the same operation with a Float tensor you'll see:

>>> sy.FloatTensor([-1.0, 1.0, 11]) < 0
 1
 0
 0
[syft.core.frameworks.torch.tensor.ByteTensor of size 3]

This "wrapper behaviour" can be seen in other situations, per instance, if you send a tensor to another virtual worker:

>>> [tensor for tensor in sy.FloatTensor([-1.0, 1.0, 11]).send(bob)]
[]

>>> sy.FloatTensor([-1.0, 1.0, 11]).send(bob) < 0
FloatTensor[_PointerTensor - id:4734155120 owner:me loc:bob id@loc:23451730700]

So the wrapper tensor just return a tensor object, which is basically an empty iterator. If we check the documentation for any and all, we'll see that the default behaviour of the any function is to return False if the iterator is empty, and for the all function to return True.

Help on built-in function any in module builtins:

any(iterable, /)
    Return True if bool(x) is True for any x in the iterable.    
    If the iterable is empty, return False.

Help on built-in function all in module builtins:

all(iterable, /)
    Return True if bool(x) is True for all values x in the iterable.    
    If the iterable is empty, return True.

To have the expected output in the code snippet you just sent, a option would be to use the child tensors (I'm not sure if this is good practice, but probably not):

import syft as sy

hook = sy.TorchHook(verbose=False)
me = hook.local_worker
me.is_client_worker = False

bob = sy.VirtualWorker(id="bob", hook=hook, is_client_worker=False)
alice = sy.VirtualWorker(id="alice", hook=hook, is_client_worker=False)
workers = [bob, alice]

y = sy.FloatTensor([1., 2.0]).fix_precision().share(*workers)
x = sy.FloatTensor([-1., -2.0]).fix_precision().share(*workers)

def any_less_than_zero(wrapper_tensor):
    for element in wrapper_tensor.child.child:
        if element <= 0:
            return True
    return False
def all_less_than_zero(wrapper_tensor):
    for element in wrapper_tensor.child.child:
        if element >= 0:
            return False
    return True

print(any_less_than_zero(y), all_less_than_zero(y)) 
print(any_less_than_zero(x), all_less_than_zero(x))
# False False
# True True

Hi @mari-linhares,

Thank you for explaining in detail.
Understand about the behavior. I need to call "child" of chain.

Thank you again for describing in detail :)

@robert-wagner, @Ogofo any thoughts on this? Maybe close this issue if you think this is intended behaviour?

Thank you! Cheers!

In the original comment x and y are both mpc shared variables. We currently do not have the functions any and all implemented for mpc tensors as it leaks information most of the time. This behavior should return an error rather than a result that doesn't make sense

@robert-wagner
Thank you for your kind reply. I got it.

Was this page helpful?
0 / 5 - 0 ratings