Esempio n. 1
0
 def bar(x):
     arg = anonymous_tuple.AnonymousTuple([
         (str(k), x[k]) if k % 2 == 0 else (None, x[k])
         for k in range(n)
     ])
     return tff.federated_zip(arg)
 async def create_value(self, value, type_spec=None):
     type_spec = computation_types.to_type(type_spec)
     py_typecheck.check_type(type_spec, computation_types.Type)
     if isinstance(value, intrinsic_defs.IntrinsicDef):
         if not type_analysis.is_concrete_instance_of(
                 type_spec, value.type_signature):  # pytype: disable=attribute-error
             raise TypeError(
                 'Incompatible type {} used with intrinsic {}.'.format(
                     type_spec, value.uri))  # pytype: disable=attribute-error
         else:
             return CompositeValue(value, type_spec)
     elif isinstance(value, pb.Computation):
         which_computation = value.WhichOneof('computation')
         if which_computation in ['tensorflow', 'lambda']:
             return CompositeValue(value, type_spec)
         elif which_computation == 'intrinsic':
             intr = intrinsic_defs.uri_to_intrinsic_def(value.intrinsic.uri)
             if intr is None:
                 raise ValueError(
                     'Encountered an unrecognized intrinsic "{}".'.format(
                         value.intrinsic.uri))
             py_typecheck.check_type(intr, intrinsic_defs.IntrinsicDef)
             return await self.create_value(intr, type_spec)
         else:
             raise NotImplementedError(
                 'Unimplemented computation type {}.'.format(
                     which_computation))
     elif isinstance(type_spec, computation_types.NamedTupleType):
         value_tuple = anonymous_tuple.from_container(value)
         items = await asyncio.gather(*[
             self.create_value(v, t)
             for v, t in zip(value_tuple, type_spec)
         ])
         type_elemnents_iter = anonymous_tuple.iter_elements(type_spec)
         return self.create_tuple(
             anonymous_tuple.AnonymousTuple(
                 (k, i) for (k, _), i in zip(type_elemnents_iter, items)))
     elif isinstance(type_spec, computation_types.FederatedType):
         if type_spec.placement == placement_literals.SERVER:
             if not type_spec.all_equal:
                 raise ValueError(
                     'Expected an all equal value at the `SERVER` placement, '
                     'found {}.'.format(type_spec))
             results = await self._parent_executor.create_value(
                 value, type_spec.member)
             return CompositeValue(results, type_spec)
         elif type_spec.placement == placement_literals.CLIENTS:
             if type_spec.all_equal:
                 results = await asyncio.gather(*[
                     c.create_value(value, type_spec)
                     for c in self._child_executors
                 ])
                 return CompositeValue(results, type_spec)
             else:
                 py_typecheck.check_type(value, list)
                 cardinalities = await self._get_cardinalities()
                 total_clients = sum(cardinalities)
                 py_typecheck.check_len(value, total_clients)
                 results = []
                 offset = 0
                 for child, num_clients in zip(self._child_executors,
                                               cardinalities):
                     new_offset = offset + num_clients
                     result = child.create_value(value[offset:new_offset],
                                                 type_spec)
                     results.append(result)
                     offset = new_offset
                 return CompositeValue(await asyncio.gather(*results),
                                       type_spec)
         else:
             raise ValueError('Unexpected placement {}.'.format(
                 type_spec.placement))
     else:
         return CompositeValue(
             await self._parent_executor.create_value(value, type_spec),
             type_spec)
Esempio n. 3
0
 def test_list_structures_from_element_type_spec_with_anonymous_tuples(self):
   self._test_list_structure(
       computation_types.NamedTupleType([('a', tf.int32)]), [
           anonymous_tuple.AnonymousTuple([('a', 1)]),
           anonymous_tuple.AnonymousTuple([('a', 2)])
       ], 'OrderedDict([(\'a\', array([1,2],dtype=int32))])')
Esempio n. 4
0
def stamp_parameter_in_graph(parameter_name, parameter_type, graph):
    """Stamps a parameter of a given type in the given tf.Graph instance.

  Tensors are stamped as placeholders, sequences are stamped as data sets
  constructed from string tensor handles, and named tuples are stamped by
  independently stamping their elements.

  Args:
    parameter_name: The suggested (string) name of the parameter to use in
      determining the names of the graph components to construct. The names that
      will actually appear in the graph are not guaranteed to be based on this
      suggested name, and may vary, e.g., due to existing naming conflicts, but
      a best-effort attempt will be made to make them similar for ease of
      debugging.
    parameter_type: The type of the parameter to stamp. Must be either an
      instance of computation_types.Type (or convertible to it), or None.
    graph: The instance of tf.Graph to stamp in.

  Returns:
    A tuple (val, binding), where 'val' is a Python object (such as a dataset,
    a placeholder, or a dictionary that represents a named tuple) that
    represents the stamped parameter for use in the body of a Python function
    that consumes this parameter, and the 'binding' is an instance of
    TensorFlow.Binding that indicates how parts of the type signature relate
    to the tensors and ops stamped into the graph.

  Raises:
    TypeError: If the arguments are of the wrong computation_types.
    ValueError: If the parameter type cannot be stamped in a TensorFlow graph.
  """
    py_typecheck.check_type(parameter_name, six.string_types)
    py_typecheck.check_type(graph, tf.Graph)
    if parameter_type is None:
        return (None, None)
    parameter_type = computation_types.to_type(parameter_type)
    if isinstance(parameter_type, computation_types.TensorType):
        with graph.as_default():
            placeholder = tf.placeholder(dtype=parameter_type.dtype,
                                         shape=parameter_type.shape,
                                         name=parameter_name)
            binding = pb.TensorFlow.Binding(tensor=pb.TensorFlow.TensorBinding(
                tensor_name=placeholder.name))
            return (placeholder, binding)
    elif isinstance(parameter_type, computation_types.NamedTupleType):
        element_name_value_pairs = []
        element_bindings = []
        for e in anonymous_tuple.to_elements(parameter_type):
            e_val, e_binding = stamp_parameter_in_graph(
                '{}_{}'.format(parameter_name, e[0]), e[1], graph)
            element_name_value_pairs.append((e[0], e_val))
            element_bindings.append(e_binding)
        return (anonymous_tuple.AnonymousTuple(element_name_value_pairs),
                pb.TensorFlow.Binding(tuple=pb.TensorFlow.NamedTupleBinding(
                    element=element_bindings)))
    elif isinstance(parameter_type, computation_types.SequenceType):
        with graph.as_default():
            handle = tf.placeholder(tf.string, shape=[])
        ds = make_dataset_from_string_handle(handle, parameter_type.element)
        return (ds,
                pb.TensorFlow.Binding(sequence=pb.TensorFlow.SequenceBinding(
                    iterator_string_handle_name=handle.name)))
    else:
        raise ValueError(
            'Parameter type component {} cannot be stamped into a TensorFlow '
            'graph.'.format(repr(parameter_type)))
Esempio n. 5
0
class FunctionUtilsTest(test.TestCase, parameterized.TestCase):
    def test_get_defun_argspec_with_typed_non_eager_defun(self):
        # In a tf.function with a defined input signature, **kwargs or default
        # values are not allowed, but *args are, and the input signature may overlap
        # with *args.
        fn = tf.function(lambda x, y, *z: None, (
            tf.TensorSpec(None, tf.int32),
            tf.TensorSpec(None, tf.bool),
            tf.TensorSpec(None, tf.float32),
            tf.TensorSpec(None, tf.float32),
        ))
        self.assertEqual(
            collections.OrderedDict(
                function_utils.get_signature(fn).parameters),
            collections.OrderedDict(
                x=inspect.Parameter('x',
                                    inspect.Parameter.POSITIONAL_OR_KEYWORD),
                y=inspect.Parameter('y',
                                    inspect.Parameter.POSITIONAL_OR_KEYWORD),
                z=inspect.Parameter('z', inspect.Parameter.VAR_POSITIONAL),
            ))

    def test_get_defun_argspec_with_untyped_non_eager_defun(self):
        # In a tf.function with no input signature, the same restrictions as in a
        # typed eager function apply.
        fn = tf.function(lambda x, y, *z: None)
        self.assertEqual(
            collections.OrderedDict(
                function_utils.get_signature(fn).parameters),
            collections.OrderedDict(
                x=inspect.Parameter('x',
                                    inspect.Parameter.POSITIONAL_OR_KEYWORD),
                y=inspect.Parameter('y',
                                    inspect.Parameter.POSITIONAL_OR_KEYWORD),
                z=inspect.Parameter('z', inspect.Parameter.VAR_POSITIONAL),
            ))

    def test_get_signature_with_class_instance_method(self):
        class C:
            def __init__(self, x):
                self._x = x

            def foo(self, y):
                return self._x * y

        c = C(5)
        signature = function_utils.get_signature(c.foo)
        self.assertEqual(
            signature.parameters,
            collections.OrderedDict(y=inspect.Parameter(
                'y', inspect.Parameter.POSITIONAL_OR_KEYWORD)))

    def test_get_signature_with_class_property(self):
        class C:
            @property
            def x(self):
                return 99

        c = C()
        with self.assertRaises(TypeError):
            function_utils.get_signature(c.x)

    def test_as_wrapper_with_classmethod(self):
        class C:
            @classmethod
            def foo(cls, x):
                return x * 2

        signature = function_utils.get_signature(C.foo)
        self.assertEqual(
            signature.parameters,
            collections.OrderedDict(x=inspect.Parameter(
                'x', inspect.Parameter.POSITIONAL_OR_KEYWORD)))

    # pyformat: disable
    @parameterized.parameters(
        itertools.product(
            # Values of 'fn' to test.
            [
                lambda: None, lambda a: None, lambda a, b: None,
                lambda *a: None, lambda **a: None, lambda *a, **b: None,
                lambda a, *b: None, lambda a, **b: None,
                lambda a, b, **c: None, lambda a, b=10: None,
                lambda a, b=10, c=20: None, lambda a, b=10, *c: None,
                lambda a, b=10, **c: None, lambda a, b=10, *c, **d: None,
                lambda a, b, c=10, *d: None, lambda a=10, b=20, c=30, **d: None
            ],
            # Values of 'args' to test.
            [[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4]],
            # Values of 'kwargs' to test.
            [{}, {
                'b': 100
            }, {
                'name': 'foo'
            }, {
                'b': 100,
                'name': 'foo'
            }]))
    # pyformat: enable
    def test_get_callargs_for_signature(self, fn, args, kwargs):
        signature = function_utils.get_signature(fn)
        expected_error = None
        try:
            signature = inspect.signature(fn)
            bound_arguments = signature.bind(*args, **kwargs)
            expected_callargs = bound_arguments.arguments
        except TypeError as e:
            expected_error = e
            expected_callargs = None

        result_callargs = None
        if expected_error is None:
            try:
                bound_args = signature.bind(*args, **kwargs).arguments
                self.assertEqual(bound_args, expected_callargs)
            except (TypeError, AssertionError) as test_err:
                raise AssertionError(
                    'With signature `{!s}`, args {!s}, kwargs {!s}, expected bound '
                    'args {!s} and error {!s}, tested function returned {!s} and the '
                    'test has failed with message: {!s}'.format(
                        signature, args, kwargs, expected_callargs,
                        expected_error, result_callargs, test_err))
        else:
            with self.assertRaises(TypeError):
                _ = signature.bind(*args, **kwargs)

    # pyformat: disable
    # pylint: disable=g-complex-comprehension
    @parameterized.parameters(
        (function_utils.get_signature(params[0]), ) + params[1:]
        for params in [
            (lambda a: None, [tf.int32], {}),
            (lambda a, b=True: None, [tf.int32, tf.bool], {}),
            (lambda a, b=True: None, [tf.int32], {
                'b': tf.bool
            }),
            (lambda a, b=True: None, [tf.bool], {
                'b': tf.bool
            }),
            (lambda a=10, b=True: None, [tf.int32], {
                'b': tf.bool
            }),
        ])
    # pylint: enable=g-complex-comprehension
    # pyformat: enable
    def test_is_signature_compatible_with_types_true(self, signature, args,
                                                     kwargs):
        self.assertTrue(
            function_utils.is_signature_compatible_with_types(
                signature, *[computation_types.to_type(a) for a in args],
                **{k: computation_types.to_type(v)
                   for k, v in kwargs.items()}))

    # pyformat: disable
    # pylint: disable=g-complex-comprehension
    @parameterized.parameters(
        (function_utils.get_signature(params[0]), ) + params[1:]
        for params in [
            (lambda a=True: None, [tf.int32], {}),
            (lambda a=10, b=True: None, [tf.bool], {
                'b': tf.bool
            }),
        ])
    # pylint: enable=g-complex-comprehension
    # pyformat: enable
    def test_is_signature_compatible_with_types_false(self, signature, args,
                                                      kwargs):
        self.assertFalse(
            function_utils.is_signature_compatible_with_types(
                signature, *[computation_types.to_type(a) for a in args],
                **{k: computation_types.to_type(v)
                   for k, v in kwargs.items()}))

    # pyformat: disable
    @parameterized.parameters(
        (tf.int32, False), ([tf.int32, tf.int32], True),
        ([tf.int32, ('b', tf.int32)], True), ([('a', tf.int32),
                                               ('b', tf.int32)], True),
        ([('a', tf.int32), tf.int32], False),
        (anonymous_tuple.AnonymousTuple([(None, 1), ('a', 2)]), True),
        (anonymous_tuple.AnonymousTuple([('a', 1), (None, 2)]), False))
    # pyformat: enable
    def test_is_argument_tuple(self, arg, expected_result):
        self.assertEqual(function_utils.is_argument_tuple(arg),
                         expected_result)

    # pyformat: disable
    @parameterized.parameters(
        (anonymous_tuple.AnonymousTuple([(None, 1)]), [1], {}),
        (anonymous_tuple.AnonymousTuple([(None, 1), ('a', 2)]), [1], {
            'a': 2
        }))
    # pyformat: enable
    def test_unpack_args_from_anonymous_tuple(self, tuple_with_args,
                                              expected_args, expected_kwargs):
        self.assertEqual(
            function_utils.unpack_args_from_tuple(tuple_with_args),
            (expected_args, expected_kwargs))

    # pyformat: disable
    @parameterized.parameters(
        ([tf.int32], [tf.int32], {}), ([('a', tf.int32)], [], {
            'a': tf.int32
        }), ([tf.int32, tf.bool], [tf.int32, tf.bool], {}),
        ([tf.int32, ('b', tf.bool)], [tf.int32], {
            'b': tf.bool
        }), ([('a', tf.int32), ('b', tf.bool)], [], {
            'a': tf.int32,
            'b': tf.bool
        }))
    # pyformat: enable
    def test_unpack_args_from_tuple_type(self, tuple_with_args, expected_args,
                                         expected_kwargs):
        args, kwargs = function_utils.unpack_args_from_tuple(tuple_with_args)
        self.assertEqual(len(args), len(expected_args))
        for idx, arg in enumerate(args):
            self.assertTrue(
                type_analysis.are_equivalent_types(
                    arg, computation_types.to_type(expected_args[idx])))
        self.assertEqual(set(kwargs.keys()), set(expected_kwargs.keys()))
        for k, v in kwargs.items():
            self.assertTrue(
                type_analysis.are_equivalent_types(
                    computation_types.to_type(v), expected_kwargs[k]))

    def test_pack_args_into_anonymous_tuple_without_type_spec(self):
        self.assertEqual(
            function_utils.pack_args_into_anonymous_tuple([1], {'a': 10}),
            anonymous_tuple.AnonymousTuple([(None, 1), ('a', 10)]))
        self.assertIn(
            function_utils.pack_args_into_anonymous_tuple([1, 2], {
                'a': 10,
                'b': 20
            }), [
                anonymous_tuple.AnonymousTuple([
                    (None, 1),
                    (None, 2),
                    ('a', 10),
                    ('b', 20),
                ]),
                anonymous_tuple.AnonymousTuple([
                    (None, 1),
                    (None, 2),
                    ('b', 20),
                    ('a', 10),
                ])
            ])
        self.assertIn(
            function_utils.pack_args_into_anonymous_tuple([], {
                'a': 10,
                'b': 20
            }), [
                anonymous_tuple.AnonymousTuple([('a', 10), ('b', 20)]),
                anonymous_tuple.AnonymousTuple([('b', 20), ('a', 10)])
            ])
        self.assertEqual(
            function_utils.pack_args_into_anonymous_tuple([1], {}),
            anonymous_tuple.AnonymousTuple([(None, 1)]))

    # pyformat: disable
    @parameterized.parameters(
        ([1], {}, [tf.int32], [(None, 1)]),
        ([1, True], {}, [tf.int32, tf.bool], [(None, 1), (None, True)]),
        ([1, True], {}, [('x', tf.int32),
                         ('y', tf.bool)], [('x', 1), ('y', True)]), ([1], {
                             'y': True
                         }, [('x', tf.int32), ('y', tf.bool)], [('x', 1),
                                                                ('y', True)]),
        ([], {
            'x': 1,
            'y': True
        }, [('x', tf.int32), ('y', tf.bool)], [('x', 1), ('y', True)]),
        ([], collections.OrderedDict([('y', True), ('x', 1)]), [
            ('x', tf.int32), ('y', tf.bool)
        ], [('x', 1), ('y', True)]))
    # pyformat: enable
    def test_pack_args_into_anonymous_tuple_with_type_spec_expect_success(
            self, args, kwargs, type_spec, elements):
        self.assertEqual(
            function_utils.pack_args_into_anonymous_tuple(
                args, kwargs, type_spec, NoopIngestContextForTest()),
            anonymous_tuple.AnonymousTuple(elements))

    # pyformat: disable
    @parameterized.parameters(([1], {}, [(tf.bool)]), ([], {
        'x': 1,
        'y': True
    }, [(tf.int32), (tf.bool)]))
    # pyformat: enable
    def test_pack_args_into_anonymous_tuple_with_type_spec_expect_failure(
            self, args, kwargs, type_spec):
        with self.assertRaises(TypeError):
            function_utils.pack_args_into_anonymous_tuple(
                args, kwargs, type_spec, NoopIngestContextForTest())

    # pyformat: disable
    @parameterized.parameters(
        (None, [], {}, 'None'), (tf.int32, [1], {}, '1'),
        ([tf.int32, tf.bool], [1, True], {}, '<1,True>'),
        ([('x', tf.int32), ('y', tf.bool)], [1, True], {}, '<x=1,y=True>'),
        ([('x', tf.int32), ('y', tf.bool)], [1], {
            'y': True
        }, '<x=1,y=True>'),
        ([tf.int32, tf.bool],
         [anonymous_tuple.AnonymousTuple([(None, 1),
                                          (None, True)])], {}, '<1,True>'))
    # pyformat: enable
    def test_pack_args(self, parameter_type, args, kwargs,
                       expected_value_string):
        self.assertEqual(
            str(
                function_utils.pack_args(parameter_type, args, kwargs,
                                         NoopIngestContextForTest())),
            expected_value_string)

    # pyformat: disable
    @parameterized.parameters(
        (1, lambda: 10, None, None, None, 10),
        (2, lambda x=1: x + 10, None, None, None, 11),
        (3, lambda x=1: x + 10, tf.int32, None, 20, 30),
        (4, lambda x, y: x + y, [tf.int32, tf.int32], None,
         anonymous_tuple.AnonymousTuple([('x', 5), ('y', 6)]), 11),
        (5, lambda *args: str(args), [tf.int32, tf.int32], True,
         anonymous_tuple.AnonymousTuple([(None, 5), (None, 6)]), '(5, 6)'),
        (6, lambda *args: str(args), [
            ('x', tf.int32), ('y', tf.int32)
        ], False, anonymous_tuple.AnonymousTuple([
            ('x', 5), ('y', 6)
        ]), '(AnonymousTuple([(\'x\', 5), (\'y\', 6)]),)'),
        (
            7,
            lambda x: str(x),  # pylint: disable=unnecessary-lambda
            [tf.int32],
            None,
            anonymous_tuple.AnonymousTuple([(None, 10)]),
            '[10]'))
    # pyformat: enable
    def test_wrap_as_zero_or_one_arg_callable(self, unused_index, fn,
                                              parameter_type, unpack, arg,
                                              expected_result):
        wrapped_fn = function_utils.wrap_as_zero_or_one_arg_callable(
            fn, parameter_type, unpack)
        actual_result = wrapped_fn(arg) if parameter_type else wrapped_fn()
        self.assertEqual(actual_result, expected_result)
 def test_returns_false_with_named_tuple_type_spec(self):
     value = anonymous_tuple.AnonymousTuple([('a', 0.0)])
     type_spec = computation_types.NamedTupleType([('a', tf.float32)])
     self.assertFalse(
         type_analysis.is_anon_tuple_with_py_container(value, type_spec))
Esempio n. 7
0
 async def create_value(self, value, type_spec=None):
     type_spec = computation_types.to_type(type_spec)
     py_typecheck.check_type(type_spec, computation_types.Type)
     if isinstance(value, intrinsic_defs.IntrinsicDef):
         if not type_utils.is_concrete_instance_of(type_spec,
                                                   value.type_signature):  # pytype: disable=attribute-error
             raise TypeError(
                 'Incompatible type {} used with intrinsic {}.'.format(
                     type_spec, value.uri))  # pytype: disable=attribute-error
         else:
             return CompositeValue(value, type_spec)
     elif isinstance(value, pb.Computation):
         which_computation = value.WhichOneof('computation')
         if which_computation in ['tensorflow', 'lambda']:
             return CompositeValue(value, type_spec)
         elif which_computation == 'intrinsic':
             intr = intrinsic_defs.uri_to_intrinsic_def(value.intrinsic.uri)
             if intr is None:
                 raise ValueError(
                     'Encountered an unrecognized intrinsic "{}".'.format(
                         value.intrinsic.uri))
             py_typecheck.check_type(intr, intrinsic_defs.IntrinsicDef)
             return await self.create_value(intr, type_spec)
         else:
             raise NotImplementedError(
                 'Unimplemented computation type {}.'.format(
                     which_computation))
     elif isinstance(type_spec, computation_types.NamedTupleType):
         v_el = anonymous_tuple.to_elements(
             anonymous_tuple.from_container(value))
         t_el = anonymous_tuple.to_elements(type_spec)
         items = await asyncio.gather(*[
             self.create_value(v, t) for (_, v), (_, t) in zip(v_el, t_el)
         ])
         return self.create_tuple(
             anonymous_tuple.AnonymousTuple([
                 (k, i) for (k, _), i in zip(t_el, items)
             ]))
     elif isinstance(type_spec, computation_types.FederatedType):
         if type_spec.placement == placement_literals.SERVER:
             if type_spec.all_equal:
                 return CompositeValue(
                     await self._parent_executor.create_value(
                         value, type_spec.member), type_spec)
             else:
                 raise ValueError(
                     'A non-all_equal value on the server is unexpected.')
         elif type_spec.placement == placement_literals.CLIENTS:
             if type_spec.all_equal:
                 return CompositeValue(
                     await asyncio.gather(*[
                         c.create_value(value, type_spec)
                         for c in self._child_executors
                     ]), type_spec)
             else:
                 py_typecheck.check_type(value, list)
                 if self._cardinalities is None:
                     self._cardinalities = asyncio.ensure_future(
                         self._get_cardinalities())
                 cardinalities = await self._cardinalities
                 py_typecheck.check_len(cardinalities,
                                        len(self._child_executors))
                 count = sum(cardinalities)
                 py_typecheck.check_len(value, count)
                 result = []
                 offset = 0
                 for c, n in zip(self._child_executors, cardinalities):
                     new_offset = offset + n
                     # The slice opporator is not supported on all the types `value`
                     # supports.
                     # pytype: disable=unsupported-operands
                     result.append(
                         c.create_value(value[offset:new_offset],
                                        type_spec))
                     # pytype: enable=unsupported-operands
                     offset = new_offset
                 return CompositeValue(await asyncio.gather(*result),
                                       type_spec)
         else:
             raise ValueError('Unexpected placement {}.'.format(
                 type_spec.placement))
     else:
         return CompositeValue(
             await self._parent_executor.create_value(value, type_spec),
             type_spec)
Esempio n. 8
0
 def test_getitem_key_builtin_attribute_raises(self):
     v = [('foo', 10), ('bar', 20)]
     x = anonymous_tuple.AnonymousTuple(v)
     with self.assertRaises(AttributeError):
         _ = x['__getattr__']
Esempio n. 9
0
 def test_getitem_bad_bounds(self):
     v = [(None, i) for i in range(0, 50, 10)]
     x = anonymous_tuple.AnonymousTuple(v)
     with self.assertRaises(IndexError):
         _ = x[10]
Esempio n. 10
0
 async def _child_fn(ex, x, y):
   py_typecheck.check_type(x, executor_value_base.ExecutorValue)
   py_typecheck.check_type(y, executor_value_base.ExecutorValue)
   return await ex.create_call(
       await ex.create_value(zip_comp, zip_type), await ex.create_tuple(
           anonymous_tuple.AnonymousTuple([(keys[0], x), (keys[1], y)])))
Esempio n. 11
0
 def test_construction_from_tuple(self):
     v = (('a', 1), ('b', 2), (None, 3))
     x = anonymous_tuple.AnonymousTuple(v)
     self.assertSequenceEqual(anonymous_tuple.to_elements(x), v)
 def comp():
   return anonymous_tuple.AnonymousTuple([('a', 1), ('b', 2)])
Esempio n. 13
0
    async def _evaluate(self, comp, scope=None):
        """Evaluates or partially evaluates `comp` in `scope`.

    Args:
      comp: An instance of `pb.Computation` to process.
      scope: An optional `LambdaExecutorScope` to process it in, or `None` if
        there's no surrounding scope (the computation is self-contained).

    Returns:
      An instance of `LambdaExecutorValue` that isn't unprocessed (i.e., the
      internal representation directly in it isn't simply `comp` or any
      other instance of`pb.Computation`). The result, however, does not have,
      and often won't be processed completely; it suffices for this function
      to make only partial progress.
    """
        py_typecheck.check_type(comp, pb.Computation)
        if scope is not None:
            py_typecheck.check_type(scope, LambdaExecutorScope)
        which_computation = comp.WhichOneof('computation')
        if which_computation in [
                'tensorflow', 'intrinsic', 'data', 'placement'
        ]:
            return LambdaExecutorValue(
                await self._target_executor.create_value(
                    comp,
                    type_utils.get_function_type(
                        type_serialization.deserialize_type(comp.type))))
        elif which_computation == 'lambda':

            def _make_comp_fn(scope, result, name, type_spec):
                async def _comp_fn(arg):
                    return await self._evaluate(
                        result, LambdaExecutorScope({name: arg}, scope))

                return LambdaExecutorValue(_comp_fn, type_spec=type_spec)

            comp_lambda = getattr(comp, 'lambda')
            type_spec = type_utils.get_function_type(
                type_serialization.deserialize_type(comp.type))
            return _make_comp_fn(scope, comp_lambda.result,
                                 comp_lambda.parameter_name, type_spec)
        elif which_computation == 'reference':
            return scope.resolve_reference(comp.reference.name)
        elif which_computation == 'call':
            if comp.call.argument.WhichOneof('computation') is not None:
                arg = LambdaExecutorValue(comp.call.argument, scope=scope)
            else:
                arg = None
            return await self.create_call(LambdaExecutorValue(
                comp.call.function, scope=scope),
                                          arg=arg)
        elif which_computation == 'selection':
            which_selection = comp.selection.WhichOneof('selection')
            return await self.create_selection(
                await self.create_call(
                    LambdaExecutorValue(comp.selection.source, scope=scope)),
                **{which_selection: getattr(comp.selection, which_selection)})
        elif which_computation == 'tuple':
            names = [
                str(e.name) if e.name else None for e in comp.tuple.element
            ]
            values = []
            for e in comp.tuple.element:
                val = LambdaExecutorValue(e.value, scope=scope)
                if (isinstance(val.type_signature,
                               computation_types.FunctionType)
                        and val.type_signature.parameter is None):
                    val = self.create_call(val)
                else:

                    async def _async_identity(x):
                        return x

                    val = _async_identity(val)
                values.append(val)
            values = await asyncio.gather(*values)
            return await self.create_tuple(
                anonymous_tuple.AnonymousTuple(list(zip(names, values))))
        elif which_computation == 'block':
            for loc in comp.block.local:
                value = LambdaExecutorValue(loc.value, scope)
                scope = LambdaExecutorScope({loc.name: value}, scope)
            return await self._evaluate(comp.block.result, scope)
        else:
            raise NotImplementedError(
                'Unsupported computation type "{}".'.format(which_computation))
Esempio n. 14
0
class FuncUtilsTest(test.TestCase, parameterized.TestCase):
    def test_is_defun(self):
        self.assertTrue(
            function_utils.is_defun(function.Defun()(lambda x: None)))
        self.assertTrue(
            function_utils.is_defun(function.Defun(tf.int32)(lambda x: None)))
        self.assertFalse(function_utils.is_defun(function.Defun))
        self.assertFalse(function_utils.is_defun(lambda x: None))
        self.assertFalse(function_utils.is_defun(None))

    def test_get_defun_argspec_with_typed_non_eager_defun(self):
        # In a non-eager defun with a defined input signature, **kwargs or default
        # values are not allowed, but *args are, and the input signature may
        # overlap with *args.
        self.assertEqual(
            function_utils.get_argspec(
                function.Defun(tf.int32, tf.bool, tf.float32,
                               tf.float32)(lambda x, y, *z: None)),
            inspect.ArgSpec(args=['x', 'y'],
                            varargs='z',
                            keywords=None,
                            defaults=None))

    def test_get_defun_argspec_with_untyped_non_eager_defun(self):
        # In a non-eager defun with no input signature, the same restrictions as in
        # a typed defun apply.
        self.assertEqual(
            function_utils.get_argspec(
                function.Defun()(lambda x, y, *z: None)),
            inspect.ArgSpec(args=['x', 'y'],
                            varargs='z',
                            keywords=None,
                            defaults=None))

    # pyformat: disable
    @parameterized.parameters(
        itertools.product(
            # Values of 'fn' to test.
            [
                lambda: None, lambda a: None, lambda a, b: None,
                lambda *a: None, lambda **a: None, lambda *a, **b: None,
                lambda a, *b: None, lambda a, **b: None,
                lambda a, b, **c: None, lambda a, b=10: None,
                lambda a, b=10, c=20: None, lambda a, b=10, *c: None,
                lambda a, b=10, **c: None, lambda a, b=10, *c, **d: None,
                lambda a, b, c=10, *d: None, lambda a=10, b=20, c=30, **d: None
            ],
            # Values of 'args' to test.
            [[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4]],
            # Values of 'kwargs' to test.
            [{}, {
                'b': 100
            }, {
                'name': 'foo'
            }, {
                'b': 100,
                'name': 'foo'
            }]))
    # pyformat: enable
    def test_get_callargs_for_argspec(self, fn, args, kwargs):
        argspec = inspect.getargspec(fn)
        expected_error = None
        try:
            expected_callargs = inspect.getcallargs(fn, *args, **kwargs)
        except TypeError as e:
            expected_error = e
            expected_callargs = None
        try:
            if expected_error is None:
                result_callargs = function_utils.get_callargs_for_argspec(
                    argspec, *args, **kwargs)
                self.assertEqual(result_callargs, expected_callargs)
            else:
                with self.assertRaises(TypeError):
                    result_callargs = function_utils.get_callargs_for_argspec(
                        argspec, *args, **kwargs)
        except (TypeError, AssertionError) as test_err:
            raise AssertionError(
                'With argspec {}, args {}, kwargs {}, expected callargs {} and '
                'error {}, tested function returned {} and the test has failed '
                'with message: {}'.format(str(argspec), str(args), str(kwargs),
                                          str(expected_callargs),
                                          str(expected_error),
                                          str(result_callargs), str(test_err)))

    # pyformat: disable
    @parameterized.parameters(
        (inspect.getargspec(params[0]), ) + params[1:] for params in
        [(lambda a: None, [tf.int32], {},
          True), (
              lambda a=True: None,
              [tf.int32], {},
              False), (lambda a, b=True: None, [tf.int32, tf.bool], {}, True),
         (lambda a, b=True: None, [tf.int32], {
             'b': tf.bool
         }, True), (lambda a, b=True: None, [tf.bool], {
             'b': tf.bool
         }, True), (lambda a=10, b=True: None, [tf.int32], {
             'b': tf.bool
         }, True), (lambda a=10, b=True: None, [tf.bool], {
             'b': tf.bool
         }, False)])
    # pyformat: enable
    def test_is_argspec_compatible_with_types(self, argspec, args, kwargs,
                                              expected_result):
        self.assertEqual(
            function_utils.is_argspec_compatible_with_types(
                argspec, *[computation_types.to_type(a) for a in args], **{
                    k: computation_types.to_type(v)
                    for k, v in six.iteritems(kwargs)
                }), expected_result)

    # pyformat: disable
    @parameterized.parameters(
        (tf.int32, False), ([tf.int32, tf.int32], True),
        ([tf.int32, ('b', tf.int32)], True), ([('a', tf.int32),
                                               ('b', tf.int32)], True),
        ([('a', tf.int32), tf.int32], False),
        (anonymous_tuple.AnonymousTuple([(None, 1), ('a', 2)]), True),
        (anonymous_tuple.AnonymousTuple([('a', 1), (None, 2)]), False))
    # pyformat: enable
    def test_is_argument_tuple(self, arg, expected_result):
        self.assertEqual(function_utils.is_argument_tuple(arg),
                         expected_result)

    # pyformat: disable
    @parameterized.parameters(
        (anonymous_tuple.AnonymousTuple([(None, 1)]), [1], {}),
        (anonymous_tuple.AnonymousTuple([(None, 1), ('a', 2)]), [1], {
            'a': 2
        }))
    # pyformat: enable
    def test_unpack_args_from_anonymous_tuple(self, tuple_with_args,
                                              expected_args, expected_kwargs):
        self.assertEqual(
            function_utils.unpack_args_from_tuple(tuple_with_args),
            (expected_args, expected_kwargs))

    # pyformat: disable
    @parameterized.parameters(
        ([tf.int32], [tf.int32], {}), ([('a', tf.int32)], [], {
            'a': tf.int32
        }), ([tf.int32, tf.bool], [tf.int32, tf.bool], {}),
        ([tf.int32, ('b', tf.bool)], [tf.int32], {
            'b': tf.bool
        }), ([('a', tf.int32), ('b', tf.bool)], [], {
            'a': tf.int32,
            'b': tf.bool
        }))
    # pyformat: enable
    def test_unpack_args_from_tuple_type(self, tuple_with_args, expected_args,
                                         expected_kwargs):
        args, kwargs = function_utils.unpack_args_from_tuple(tuple_with_args)
        self.assertEqual(len(args), len(expected_args))
        for idx, arg in enumerate(args):
            self.assertTrue(
                type_utils.are_equivalent_types(
                    arg, computation_types.to_type(expected_args[idx])))
        self.assertEqual(set(kwargs.keys()), set(expected_kwargs.keys()))
        for k, v in six.iteritems(kwargs):
            self.assertTrue(
                type_utils.are_equivalent_types(computation_types.to_type(v),
                                                expected_kwargs[k]))

    def test_pack_args_into_anonymous_tuple_without_type_spec(self):
        self.assertEqual(
            function_utils.pack_args_into_anonymous_tuple([1], {'a': 10}),
            anonymous_tuple.AnonymousTuple([(None, 1), ('a', 10)]))
        self.assertIn(
            function_utils.pack_args_into_anonymous_tuple([1, 2], {
                'a': 10,
                'b': 20
            }), [
                anonymous_tuple.AnonymousTuple([
                    (None, 1),
                    (None, 2),
                    ('a', 10),
                    ('b', 20),
                ]),
                anonymous_tuple.AnonymousTuple([
                    (None, 1),
                    (None, 2),
                    ('b', 20),
                    ('a', 10),
                ])
            ])
        self.assertIn(
            function_utils.pack_args_into_anonymous_tuple([], {
                'a': 10,
                'b': 20
            }), [
                anonymous_tuple.AnonymousTuple([('a', 10), ('b', 20)]),
                anonymous_tuple.AnonymousTuple([('b', 20), ('a', 10)])
            ])
        self.assertEqual(
            function_utils.pack_args_into_anonymous_tuple([1], {}),
            anonymous_tuple.AnonymousTuple([(None, 1)]))

    # pyformat: disable
    @parameterized.parameters(
        ([1], {}, [tf.int32], [(None, 1)]),
        ([1, True], {}, [tf.int32, tf.bool], [(None, 1), (None, True)]),
        ([1, True], {}, [('x', tf.int32),
                         ('y', tf.bool)], [('x', 1), ('y', True)]), ([1], {
                             'y': True
                         }, [('x', tf.int32), ('y', tf.bool)], [('x', 1),
                                                                ('y', True)]),
        ([], {
            'x': 1,
            'y': True
        }, [('x', tf.int32), ('y', tf.bool)], [('x', 1), ('y', True)]),
        ([], collections.OrderedDict([('y', True), ('x', 1)]), [
            ('x', tf.int32), ('y', tf.bool)
        ], [('x', 1), ('y', True)]))
    # pyformat: enable
    def test_pack_args_into_anonymous_tuple_with_type_spec_expect_success(
            self, args, kwargs, type_spec, elements):
        self.assertEqual(
            function_utils.pack_args_into_anonymous_tuple(
                args, kwargs, type_spec, NoopIngestContextForTest()),
            anonymous_tuple.AnonymousTuple(elements))

    # pyformat: disable
    @parameterized.parameters(([1], {}, [(tf.bool)]), ([], {
        'x': 1,
        'y': True
    }, [(tf.int32), (tf.bool)]))
    # pyformat: enable
    def test_pack_args_into_anonymous_tuple_with_type_spec_expect_failure(
            self, args, kwargs, type_spec):
        with self.assertRaises(TypeError):
            function_utils.pack_args_into_anonymous_tuple(
                args, kwargs, type_spec, NoopIngestContextForTest())

    # pyformat: disable
    @parameterized.parameters(
        (None, [], {}, 'None'), (tf.int32, [1], {}, '1'),
        ([tf.int32, tf.bool], [1, True], {}, '<1,True>'),
        ([('x', tf.int32), ('y', tf.bool)], [1, True], {}, '<x=1,y=True>'),
        ([('x', tf.int32), ('y', tf.bool)], [1], {
            'y': True
        }, '<x=1,y=True>'),
        ([tf.int32, tf.bool],
         [anonymous_tuple.AnonymousTuple([(None, 1),
                                          (None, True)])], {}, '<1,True>'))
    # pyformat: enable
    def test_pack_args(self, parameter_type, args, kwargs,
                       expected_value_string):
        self.assertEqual(
            str(
                function_utils.pack_args(parameter_type, args, kwargs,
                                         NoopIngestContextForTest())),
            expected_value_string)

    # pyformat: disable
    @parameterized.parameters(
        (1, lambda: 10, None, None, None, 10),
        (2, lambda x=1: x + 10, None, None, None, 11),
        (3, lambda x=1: x + 10, tf.int32, None, 20, 30),
        (4, lambda x, y: x + y, [tf.int32, tf.int32], None,
         anonymous_tuple.AnonymousTuple([('x', 5), ('y', 6)]), 11),
        (5, lambda *args: str(args), [tf.int32, tf.int32], True,
         anonymous_tuple.AnonymousTuple([(None, 5), (None, 6)]), '(5, 6)'),
        (6, lambda *args: str(args), [
            ('x', tf.int32), ('y', tf.int32)
        ], False, anonymous_tuple.AnonymousTuple(
            [('x', 5), ('y', 6)]), '(AnonymousTuple([(x, 5), (y, 6)]),)'),
        (
            7,
            lambda x: str(x),  # pylint: disable=unnecessary-lambda
            [tf.int32],
            None,
            anonymous_tuple.AnonymousTuple([(None, 10)]),
            '<10>'))
    # pyformat: enable
    def test_wrap_as_zero_or_one_arg_callable(self, unused_index, fn,
                                              parameter_type, unpack, arg,
                                              expected_result):
        wrapped_fn = function_utils.wrap_as_zero_or_one_arg_callable(
            fn, parameter_type, unpack)
        actual_result = wrapped_fn(arg) if parameter_type else wrapped_fn()
        self.assertEqual(actual_result, expected_result)

    def test_polymorphic_function(self):
        class ContextForTest(context_base.Context):
            def ingest(self, val, type_spec):
                return val

            def invoke(self, comp, arg):
                return 'name={},type={},arg={}'.format(
                    comp.name, str(comp.type_signature.parameter), str(arg))

        class TestFunction(function_utils.ConcreteFunction):
            def __init__(self, name, parameter_type):
                self._name = name
                super(TestFunction, self).__init__(
                    computation_types.FunctionType(parameter_type, tf.string),
                    context_stack_impl.context_stack)

            @property
            def name(self):
                return self._name

        class TestFunctionFactory(object):
            def __init__(self):
                self._count = 0

            def __call__(self, parameter_type):
                self._count = self._count + 1
                return TestFunction(str(self._count), parameter_type)

        with context_stack_impl.context_stack.install(ContextForTest()):
            fn = function_utils.PolymorphicFunction(TestFunctionFactory())
            self.assertEqual(fn(10), 'name=1,type=<int32>,arg=<10>')
            self.assertEqual(fn(20, x=True),
                             'name=2,type=<int32,x=bool>,arg=<20,x=True>')
            self.assertEqual(fn(True), 'name=3,type=<bool>,arg=<True>')
            self.assertEqual(fn(30, x=40),
                             'name=4,type=<int32,x=int32>,arg=<30,x=40>')
            self.assertEqual(fn(50), 'name=1,type=<int32>,arg=<50>')
            self.assertEqual(fn(0, x=False),
                             'name=2,type=<int32,x=bool>,arg=<0,x=False>')
            self.assertEqual(fn(False), 'name=3,type=<bool>,arg=<False>')
            self.assertEqual(fn(60, x=70),
                             'name=4,type=<int32,x=int32>,arg=<60,x=70>')

    def test_concrete_function(self):
        class ContextForTest(context_base.Context):
            def ingest(self, val, type_spec):
                return val

            def invoke(self, comp, arg):
                return comp.invoke_fn(arg)

        class TestFunction(function_utils.ConcreteFunction):
            def __init__(self, type_signature, invoke_fn):
                super(TestFunction,
                      self).__init__(type_signature,
                                     context_stack_impl.context_stack)
                self._invoke_fn = invoke_fn

            def invoke_fn(self, arg):
                return self._invoke_fn(arg)

        with context_stack_impl.context_stack.install(ContextForTest()):
            fn = TestFunction(
                computation_types.FunctionType(tf.int32, tf.bool),
                lambda x: x > 10)
            self.assertEqual(fn(5), False)
            self.assertEqual(fn(15), True)

            fn = TestFunction(
                computation_types.FunctionType([('x', tf.int32),
                                                ('y', tf.int32)], tf.bool),
                lambda arg: arg.x > arg.y)
            self.assertEqual(fn(5, 10), False)
            self.assertEqual(fn(10, 5), True)
            self.assertEqual(fn(y=10, x=5), False)
            self.assertEqual(fn(y=5, x=10), True)
            self.assertEqual(fn(10, y=5), True)
Esempio n. 15
0
    async def create_value(self, value, type_spec=None):
        """A coroutine that creates embedded value from `value` of type `type_spec`.

    See the `FederatingExecutorValue` for detailed information about the
    `value`s and `type_spec`s that can be embedded using `create_value`.

    Args:
      value: An object that represents the value to embed within the executor.
      type_spec: An optional `tff.Type` of the value represented by this object,
        or something convertible to it.

    Returns:
      An instance of `FederatingExecutorValue` that represents the embedded
      value.

    Raises:
      TypeError: If the `value` and `type_spec` do not match.
      ValueError: If `value` is not a kind recognized by the
        `FederatingExecutor`.
    """
        type_spec = computation_types.to_type(type_spec)
        if isinstance(type_spec, computation_types.FederatedType):
            self._check_executor_compatible_with_placement(type_spec.placement)
        elif (isinstance(type_spec, computation_types.FunctionType) and
              isinstance(type_spec.result, computation_types.FederatedType)):
            self._check_executor_compatible_with_placement(
                type_spec.result.placement)
        if isinstance(value, intrinsic_defs.IntrinsicDef):
            if not type_utils.is_concrete_instance_of(type_spec,
                                                      value.type_signature):
                raise TypeError(
                    'Incompatible type {} used with intrinsic {}.'.format(
                        type_spec, value.uri))
            return FederatingExecutorValue(value, type_spec)
        elif isinstance(value, placement_literals.PlacementLiteral):
            if type_spec is None:
                type_spec = computation_types.PlacementType()
            else:
                py_typecheck.check_type(type_spec,
                                        computation_types.PlacementType)
            return FederatingExecutorValue(value, type_spec)
        elif isinstance(value, computation_impl.ComputationImpl):
            return await self.create_value(
                computation_impl.ComputationImpl.get_proto(value),
                type_utils.reconcile_value_with_type_spec(value, type_spec))
        elif isinstance(value, pb.Computation):
            if type_spec is None:
                type_spec = type_serialization.deserialize_type(value.type)
            which_computation = value.WhichOneof('computation')
            if which_computation in ['lambda', 'tensorflow']:
                return FederatingExecutorValue(value, type_spec)
            elif which_computation == 'reference':
                raise ValueError(
                    'Encountered an unexpected unbound references "{}".'.
                    format(value.reference.name))
            elif which_computation == 'intrinsic':
                intr = intrinsic_defs.uri_to_intrinsic_def(value.intrinsic.uri)
                if intr is None:
                    raise ValueError(
                        'Encountered an unrecognized intrinsic "{}".'.format(
                            value.intrinsic.uri))
                py_typecheck.check_type(intr, intrinsic_defs.IntrinsicDef)
                return await self.create_value(intr, type_spec)
            elif which_computation == 'placement':
                return await self.create_value(
                    placement_literals.uri_to_placement_literal(
                        value.placement.uri), type_spec)
            elif which_computation == 'call':
                parts = [value.call.function]
                if value.call.argument.WhichOneof('computation'):
                    parts.append(value.call.argument)
                parts = await asyncio.gather(
                    *[self.create_value(x) for x in parts])
                return await self.create_call(
                    parts[0], parts[1] if len(parts) > 1 else None)
            elif which_computation == 'tuple':
                element_values = await asyncio.gather(
                    *[self.create_value(x.value) for x in value.tuple.element])
                return await self.create_tuple(
                    anonymous_tuple.AnonymousTuple(
                        (e.name if e.name else None, v)
                        for e, v in zip(value.tuple.element, element_values)))
            elif which_computation == 'selection':
                which_selection = value.selection.WhichOneof('selection')
                if which_selection == 'name':
                    name = value.selection.name
                    index = None
                elif which_selection != 'index':
                    raise ValueError(
                        'Unrecognized selection type: "{}".'.format(
                            which_selection))
                else:
                    index = value.selection.index
                    name = None
                return await self.create_selection(await self.create_value(
                    value.selection.source),
                                                   index=index,
                                                   name=name)
            else:
                raise ValueError(
                    'Unsupported computation building block of type "{}".'.
                    format(which_computation))
        else:
            py_typecheck.check_type(type_spec, computation_types.Type)
            if isinstance(type_spec, computation_types.FunctionType):
                raise ValueError(
                    'Encountered a value of a functional TFF type {} and Python type '
                    '{} that is not of one of the recognized representations.'.
                    format(type_spec, py_typecheck.type_string(type(value))))
            elif isinstance(type_spec, computation_types.FederatedType):
                children = self._target_executors.get(type_spec.placement)
                self._check_value_compatible_with_placement(
                    value, type_spec.placement, type_spec.all_equal)
                if type_spec.all_equal:
                    value = [value for _ in children]
                child_vals = await asyncio.gather(*[
                    c.create_value(v, type_spec.member)
                    for v, c in zip(value, children)
                ])
                return FederatingExecutorValue(child_vals, type_spec)
            else:
                child = self._target_executors.get(None)
                if not child or len(child) > 1:
                    raise ValueError(
                        'Executor is not configured for unplaced values.')
                else:
                    return FederatingExecutorValue(
                        await child[0].create_value(value, type_spec),
                        type_spec)
Esempio n. 16
0
 def test_construction_from_ordereddict(self):
     v = collections.OrderedDict(a=1, b=2, c=3)
     x = anonymous_tuple.AnonymousTuple(v.items())
     self.assertSequenceEqual(anonymous_tuple.to_elements(x),
                              list(v.items()))
 def test_returns_true(self):
     value = anonymous_tuple.AnonymousTuple([('a', 0.0)])
     type_spec = computation_types.NamedTupleTypeWithPyContainerType(
         [('a', tf.float32)], dict)
     self.assertTrue(
         type_analysis.is_anon_tuple_with_py_container(value, type_spec))
Esempio n. 18
0
 def test_is_same_structure_check_types(self):
     self.assertTrue(
         anonymous_tuple.is_same_structure(
             anonymous_tuple.AnonymousTuple([('a', 10)]),
             anonymous_tuple.AnonymousTuple([('a', 20)])))
     self.assertTrue(
         anonymous_tuple.is_same_structure(
             anonymous_tuple.AnonymousTuple([
                 ('a', 10),
                 ('b', anonymous_tuple.AnonymousTuple([('z', 5)])),
             ]),
             anonymous_tuple.AnonymousTuple([
                 ('a', 20),
                 ('b', anonymous_tuple.AnonymousTuple([('z', 50)])),
             ])))
     self.assertFalse(
         anonymous_tuple.is_same_structure(
             anonymous_tuple.AnonymousTuple([('x', {
                 'y': 4
             })]), anonymous_tuple.AnonymousTuple([('x', {
                 'y': 5,
                 'z': 6
             })])))
     self.assertTrue(
         anonymous_tuple.is_same_structure(
             anonymous_tuple.AnonymousTuple([('x', {
                 'y': 5
             })]), anonymous_tuple.AnonymousTuple([('x', {
                 'y': 6
             })])))
     with self.assertRaises(TypeError):
         anonymous_tuple.is_same_structure(
             {'x': 5.0},  # not an AnonymousTuple
             anonymous_tuple.AnonymousTuple([('x', 5.0)]))
Esempio n. 19
0
 async def create_value(self, value, type_spec=None):
     type_spec = computation_types.to_type(type_spec)
     if isinstance(value, intrinsic_defs.IntrinsicDef):
         if not type_utils.is_concrete_instance_of(type_spec,
                                                   value.type_signature):
             raise TypeError(
                 'Incompatible type {} used with intrinsic {}.'.format(
                     type_spec, value.uri))
         else:
             return FederatedExecutorValue(value, type_spec)
     if isinstance(value, placement_literals.PlacementLiteral):
         if type_spec is not None:
             py_typecheck.check_type(type_spec,
                                     computation_types.PlacementType)
         return FederatedExecutorValue(value,
                                       computation_types.PlacementType())
     elif isinstance(value, computation_impl.ComputationImpl):
         return await self.create_value(
             computation_impl.ComputationImpl.get_proto(value),
             type_utils.reconcile_value_with_type_spec(value, type_spec))
     elif isinstance(value, pb.Computation):
         if type_spec is None:
             type_spec = type_serialization.deserialize_type(value.type)
         which_computation = value.WhichOneof('computation')
         if which_computation in ['tensorflow', 'lambda']:
             return FederatedExecutorValue(value, type_spec)
         elif which_computation == 'reference':
             raise ValueError(
                 'Encountered an unexpected unbound references "{}".'.
                 format(value.reference.name))
         elif which_computation == 'intrinsic':
             intr = intrinsic_defs.uri_to_intrinsic_def(value.intrinsic.uri)
             if intr is None:
                 raise ValueError(
                     'Encountered an unrecognized intrinsic "{}".'.format(
                         value.intrinsic.uri))
             py_typecheck.check_type(intr, intrinsic_defs.IntrinsicDef)
             return await self.create_value(intr, type_spec)
         elif which_computation == 'placement':
             return await self.create_value(
                 placement_literals.uri_to_placement_literal(
                     value.placement.uri), type_spec)
         elif which_computation == 'call':
             parts = [value.call.function]
             if value.call.argument.WhichOneof('computation'):
                 parts.append(value.call.argument)
             parts = await asyncio.gather(
                 *[self.create_value(x) for x in parts])
             return await self.create_call(
                 parts[0], parts[1] if len(parts) > 1 else None)
         elif which_computation == 'tuple':
             element_values = await asyncio.gather(
                 *[self.create_value(x.value) for x in value.tuple.element])
             return await self.create_tuple(
                 anonymous_tuple.AnonymousTuple([
                     (e.name if e.name else None, v)
                     for e, v in zip(value.tuple.element, element_values)
                 ]))
         elif which_computation == 'selection':
             which_selection = value.selection.WhichOneof('selection')
             if which_selection == 'name':
                 name = value.selection.name
                 index = None
             elif which_selection != 'index':
                 raise ValueError(
                     'Unrecognized selection type: "{}".'.format(
                         which_selection))
             else:
                 index = value.selection.index
                 name = None
             return await self.create_selection(await self.create_value(
                 value.selection.source),
                                                index=index,
                                                name=name)
         else:
             raise ValueError(
                 'Unsupported computation building block of type "{}".'.
                 format(which_computation))
     else:
         py_typecheck.check_type(type_spec, computation_types.Type)
         if isinstance(type_spec, computation_types.FunctionType):
             raise ValueError(
                 'Encountered a value of a functional TFF type {} and Python type '
                 '{} that is not of one of the recognized representations.'.
                 format(type_spec, py_typecheck.type_string(type(value))))
         elif isinstance(type_spec, computation_types.FederatedType):
             children = self._target_executors.get(type_spec.placement)
             if not children:
                 raise ValueError(
                     'Placement "{}" is not configured in this executor.'.
                     format(type_spec.placement))
             py_typecheck.check_type(children, list)
             if not type_spec.all_equal:
                 py_typecheck.check_type(value,
                                         (list, tuple, set, frozenset))
                 if not isinstance(value, list):
                     value = list(value)
             elif isinstance(value, list):
                 raise ValueError(
                     'An all_equal value should be passed directly, not as a list.'
                 )
             else:
                 value = [value for _ in children]
             if len(value) != len(children):
                 raise ValueError(
                     'Federated value contains {} items, but the placement {} in this '
                     'executor is configured with {} participants.'.format(
                         len(value), type_spec.placement, len(children)))
             child_vals = await asyncio.gather(*[
                 c.create_value(v, type_spec.member)
                 for v, c in zip(value, children)
             ])
             return FederatedExecutorValue(child_vals, type_spec)
         else:
             child = self._target_executors.get(None)
             if not child or len(child) > 1:
                 raise RuntimeError(
                     'Executor is not configured for unplaced values.')
             else:
                 return FederatedExecutorValue(
                     await child[0].create_value(value, type_spec),
                     type_spec)
Esempio n. 20
0
    def test_map_structure(self):
        x = anonymous_tuple.AnonymousTuple([
            ('a', 10),
            ('b',
             anonymous_tuple.AnonymousTuple([
                 ('x', anonymous_tuple.AnonymousTuple([('p', 40)])),
                 ('y', 30),
                 ('z', anonymous_tuple.AnonymousTuple([('q', 50), ('r', 60)])),
             ])),
            ('c', 20),
        ])
        y = anonymous_tuple.AnonymousTuple([
            ('a', 1),
            ('b',
             anonymous_tuple.AnonymousTuple([
                 ('x', anonymous_tuple.AnonymousTuple([('p', 4)])),
                 ('y', 3),
                 ('z', anonymous_tuple.AnonymousTuple([('q', 5), ('r', 6)])),
             ])),
            ('c', 2),
        ])

        self.assertEqual(
            anonymous_tuple.map_structure(lambda x, y: x + y, x, y),
            anonymous_tuple.AnonymousTuple([
                ('a', 11),
                ('b',
                 anonymous_tuple.AnonymousTuple([
                     ('x', anonymous_tuple.AnonymousTuple([('p', 44)])),
                     ('y', 33),
                     ('z',
                      anonymous_tuple.AnonymousTuple([('q', 55), ('r', 66)])),
                 ])),
                ('c', 22),
            ]))
Esempio n. 21
0
def assemble_result_from_graph(type_spec, binding, output_map):
    """Assembles a result stamped into a `tf.Graph` given type signature/binding.

  This method does roughly the opposite of `capture_result_from_graph`, in that
  whereas `capture_result_from_graph` starts with a single structured object
  made up of tensors and computes its type and bindings, this method starts
  with the type/bindings and constructs a structured object made up of tensors.

  Args:
    type_spec: The type signature of the result to assemble, an instance of
      `types.Type` or something convertible to it.
    binding: The binding that relates the type signature to names of tensors in
      the graph, an instance of `pb.TensorFlow.Binding`.
    output_map: The mapping from tensor names that appear in the binding to
      actual stamped tensors (possibly renamed during import).

  Returns:
    The assembled result, a Python object that is composed of tensors, possibly
    nested within Python structures such as anonymous tuples.

  Raises:
    TypeError: If the argument or any of its parts are of an uexpected type.
    ValueError: If the arguments are invalid or inconsistent witch other, e.g.,
      the type and binding don't match, or the tensor is not found in the map.
  """
    type_spec = computation_types.to_type(type_spec)
    py_typecheck.check_type(type_spec, computation_types.Type)
    py_typecheck.check_type(binding, pb.TensorFlow.Binding)
    py_typecheck.check_type(output_map, dict)
    for k, v in six.iteritems(output_map):
        py_typecheck.check_type(k, six.string_types)
        if not tf.contrib.framework.is_tensor(v):
            raise TypeError(
                'Element with key {} in the output map is {}, not a tensor.'.
                format(k, py_typecheck.type_string(type(v))))

    binding_oneof = binding.WhichOneof('binding')
    if isinstance(type_spec, computation_types.TensorType):
        if binding_oneof != 'tensor':
            raise ValueError(
                'Expected a tensor binding, found {}.'.format(binding_oneof))
        elif binding.tensor.tensor_name not in output_map:
            raise ValueError(
                'Tensor named {} not found in the output map.'.format(
                    binding.tensor.tensor_name))
        else:
            return output_map[binding.tensor.tensor_name]
    elif isinstance(type_spec, computation_types.NamedTupleType):
        if binding_oneof != 'tuple':
            raise ValueError(
                'Expected a tuple binding, found {}.'.format(binding_oneof))
        else:
            type_elements = anonymous_tuple.to_elements(type_spec)
            if len(binding.tuple.element) != len(type_elements):
                raise ValueError(
                    'Mismatching tuple sizes in type ({}) and binding ({}).'.
                    format(len(type_elements), len(binding.tuple.element)))
            result_elements = []
            for (element_name,
                 element_type), element_binding in zip(type_elements,
                                                       binding.tuple.element):
                element_object = assemble_result_from_graph(
                    element_type, element_binding, output_map)
                result_elements.append((element_name, element_object))
            return anonymous_tuple.AnonymousTuple(result_elements)
    elif isinstance(type_spec, computation_types.SequenceType):
        if binding_oneof != 'sequence':
            raise ValueError(
                'Expected a sequence binding, found {}.'.format(binding_oneof))
        else:
            handle = output_map[binding.sequence.iterator_string_handle_name]
            return make_dataset_from_string_handle(handle, type_spec.element)
    else:
        raise ValueError('Unsupported type \'{}\'.'.format(str(type_spec)))
Esempio n. 22
0
 def test_construction_from_generator_expression(self):
     x = anonymous_tuple.AnonymousTuple(
         (name, i) for i, name in enumerate(('a', 'b', None)))
     self.assertSequenceEqual(anonymous_tuple.to_elements(x), [('a', 0),
                                                               ('b', 1),
                                                               (None, 2)])
Esempio n. 23
0
 def test_pack_args_into_anonymous_tuple_with_type_spec_expect_success(
         self, args, kwargs, type_spec, elements):
     self.assertEqual(
         function_utils.pack_args_into_anonymous_tuple(
             args, kwargs, type_spec, NoopIngestContextForTest()),
         anonymous_tuple.AnonymousTuple(elements))
Esempio n. 24
0
 def test_from_container_with_anonymous_tuple(self):
     x = anonymous_tuple.from_container(
         anonymous_tuple.AnonymousTuple([('a', 10), ('b', 20)]))
     self.assertIs(x, x)
 async def _compute_tuple(anon_tuple):
     elements = anonymous_tuple.to_elements(anon_tuple)
     keys = [k for k, _ in elements]
     vals = await asyncio.gather(
         *[_compute_element(v) for _, v in elements])
     return anonymous_tuple.AnonymousTuple(zip(keys, vals))
Esempio n. 26
0
 def test_construction_from_iter_elements(self):
     x = anonymous_tuple.AnonymousTuple((('a', 1), ('b', 2), (None, 3)))
     self.assertSequenceEqual(
         anonymous_tuple.AnonymousTuple(anonymous_tuple.iter_elements(x)),
         x)
Esempio n. 27
0
def pack_args_into_anonymous_tuple(args, kwargs, type_spec=None, context=None):
  """Packs positional and keyword arguments into an anonymous tuple.

  If 'type_spec' is not None, it must be a tuple type or something that's
  convertible to it by computation_types.to_type(). The assignment of arguments
  to fields of the tuple follows the same rule as during function calls. If
  'type_spec' is None, the positional arguments precede any of the keyword
  arguments, and the ordering of the keyword arguments matches the ordering in
  which they appear in kwargs. If the latter is an OrderedDict, the ordering
  will be preserved. On the other hand, if the latter is an ordinary unordered
  dict, the ordering is arbitrary.

  Args:
    args: Positional arguments.
    kwargs: Keyword arguments.
    type_spec: The optional type specification (either an instance of
      computation_types.NamedTupleType or something convertible to it), or None
      if there's no type. Used to drive the arrangements of args into fields of
      the constructed anonymous tuple, as noted in the description.
    context: The optional context (an instance of `context_base.Context`) in
      which the arguments are being packed. Required if and only if the
      `type_spec` is not `None`.

  Returns:
    An anoymous tuple containing all the arguments.

  Raises:
    TypeError: if the arguments are of the wrong computation_types.
  """
  type_spec = computation_types.to_type(type_spec)
  if not type_spec:
    return anonymous_tuple.AnonymousTuple([(None, arg) for arg in args] +
                                          list(six.iteritems(kwargs)))
  else:
    py_typecheck.check_type(type_spec, computation_types.NamedTupleType)
    py_typecheck.check_type(context, context_base.Context)
    context = context  # type: context_base.Context
    if not is_argument_tuple(type_spec):  # pylint: disable=attribute-error
      raise TypeError(
          'Parameter type {} does not have a structure of an argument tuple, '
          'and cannot be populated from multiple positional and keyword '
          'arguments'.format(type_spec))
    else:
      result_elements = []
      positions_used = set()
      keywords_used = set()
      for index, (name, elem_type) in enumerate(
          anonymous_tuple.to_elements(type_spec)):
        if index < len(args):
          if name is not None and name in kwargs:
            raise TypeError('Argument {} specified twice.'.format(name))
          else:
            arg_value = args[index]
            result_elements.append((name, context.ingest(arg_value, elem_type)))
            positions_used.add(index)
        elif name is not None and name in kwargs:
          arg_value = kwargs[name]
          result_elements.append((name, context.ingest(arg_value, elem_type)))
          keywords_used.add(name)
        elif name:
          raise TypeError('Argument named {} is missing.'.format(name))
        else:
          raise TypeError('Argument at position {} is missing.'.format(index))
      positions_missing = set(range(len(args))).difference(positions_used)
      if positions_missing:
        raise TypeError(
            'Positional arguments at {} not used.'.format(positions_missing))
      keywords_missing = set(kwargs.keys()).difference(keywords_used)
      if keywords_missing:
        raise TypeError(
            'Keyword arguments at {} not used.'.format(keywords_missing))
      return anonymous_tuple.AnonymousTuple(result_elements)
Esempio n. 28
0
def to_representation_for_type(value,
                               tf_function_cache,
                               type_spec=None,
                               device=None):
    """Verifies or converts the `value` to an eager object matching `type_spec`.

  WARNING: This function is only partially implemented. It does not support
  data sets at this point.

  The output of this function is always an eager tensor, eager dataset, a
  representation of a TensorFlow computation, or a nested structure of those
  that matches `type_spec`, and when `device` has been specified, everything
  is placed on that device on a best-effort basis.

  TensorFlow computations are represented here as zero- or one-argument Python
  callables that accept their entire argument bundle as a single Python object.

  Args:
    value: The raw representation of a value to compare against `type_spec` and
      potentially to be converted.
    tf_function_cache: A cache obeying `dict` semantics that can be used to look
      up previously embedded TensorFlow functions.
    type_spec: An instance of `tff.Type`, can be `None` for values that derive
      from `typed_object.TypedObject`.
    device: The optional device to place the value on (for tensor-level values).

  Returns:
    Either `value` itself, or a modified version of it.

  Raises:
    TypeError: If the `value` is not compatible with `type_spec`.
  """
    type_spec = type_utils.reconcile_value_with_type_spec(value, type_spec)
    if isinstance(value, computation_base.Computation):
        return to_representation_for_type(
            computation_impl.ComputationImpl.get_proto(value),
            tf_function_cache, type_spec, device)
    elif isinstance(value, pb.Computation):
        key = (value.SerializeToString(), str(type_spec), device)
        cached_fn = tf_function_cache.get(key)
        if cached_fn:
            return cached_fn
        embedded_fn = embed_tensorflow_computation(value, type_spec, device)
        tf_function_cache[key] = embedded_fn
        return embedded_fn
    elif isinstance(type_spec, computation_types.NamedTupleType):
        type_elem = anonymous_tuple.to_elements(type_spec)
        value_elem = (anonymous_tuple.to_elements(
            anonymous_tuple.from_container(value)))
        result_elem = []
        if len(type_elem) != len(value_elem):
            raise TypeError(
                'Expected a {}-element tuple, found {} elements.'.format(
                    len(type_elem), len(value_elem)))
        for (t_name, el_type), (v_name, el_val) in zip(type_elem, value_elem):
            if t_name != v_name:
                raise TypeError(
                    'Mismatching element names in type vs. value: {} vs. {}.'.
                    format(t_name, v_name))
            el_repr = to_representation_for_type(el_val, tf_function_cache,
                                                 el_type, device)
            result_elem.append((t_name, el_repr))
        return anonymous_tuple.AnonymousTuple(result_elem)
    elif device is not None:
        py_typecheck.check_type(device, str)
        with tf.device(device):
            return to_representation_for_type(value,
                                              tf_function_cache,
                                              type_spec=type_spec,
                                              device=None)
    elif isinstance(value, EagerValue):
        return value.internal_representation
    elif isinstance(value, executor_value_base.ExecutorValue):
        raise TypeError(
            'Cannot accept a value embedded within a non-eager executor.')
    elif isinstance(type_spec, computation_types.TensorType):
        if not tf.is_tensor(value):
            value = tf.convert_to_tensor(value, dtype=type_spec.dtype)
        elif hasattr(value, 'read_value'):
            # a tf.Variable-like result, get a proper tensor.
            value = value.read_value()
        value_type = (computation_types.TensorType(value.dtype.base_dtype,
                                                   value.shape))
        if not type_analysis.is_assignable_from(type_spec, value_type):
            raise TypeError(
                'The apparent type {} of a tensor {} does not match the expected '
                'type {}.'.format(value_type, value, type_spec))
        return value
    elif isinstance(type_spec, computation_types.SequenceType):
        if isinstance(value, list):
            value = tensorflow_utils.make_data_set_from_elements(
                None, value, type_spec.element)
        py_typecheck.check_type(
            value, type_conversions.TF_DATASET_REPRESENTATION_TYPES)
        element_type = computation_types.to_type(value.element_spec)
        value_type = computation_types.SequenceType(element_type)
        type_analysis.check_assignable_from(type_spec, value_type)
        return value
    else:
        raise TypeError('Unexpected type {}.'.format(type_spec))
Esempio n. 29
0
 def test_list_structures_from_element_type_spec_with_empty_anon_tuples(self):
   self._test_list_structure(
       computation_types.NamedTupleType([]), [
           anonymous_tuple.AnonymousTuple([]),
           anonymous_tuple.AnonymousTuple([])
       ], 'OrderedDict()')
Esempio n. 30
0
 def bar(x):
     arg = anonymous_tuple.AnonymousTuple(
         _make_test_tuple(x, k) for k in range(n))
     val = intrinsics.federated_zip(arg)
     self.assertIsInstance(val, value_base.Value)
     return val