Exemplo n.º 1
0
async def _ingest(executor, val, type_spec):
    """A coroutine that handles ingestion.

  Args:
    executor: An instance of `executor_base.Executor`.
    val: The first argument to `context_base.Context.ingest()`.
    type_spec: The second argument to `context_base.Context.ingest()`.

  Returns:
    The result of the ingestion.

  Raises:
    TypeError: If the `val` is not a value of type `type_spec`.
  """
    if isinstance(val, executor_value_base.ExecutorValue):
        return val
    elif (isinstance(val, structure.Struct) and not type_spec.is_federated()):
        type_spec.check_struct()
        v_elem = structure.to_elements(val)
        t_elem = structure.to_elements(type_spec)
        if ([k for k, _ in v_elem] != [k for k, _ in t_elem]):
            raise TypeError('Value {} does not match type {}.'.format(
                val, type_spec))
        ingested = []
        for (_, v), (_, t) in zip(v_elem, t_elem):
            ingested.append(_ingest(executor, v, t))
        ingested = await asyncio.gather(*ingested)
        return await executor.create_struct(
            structure.Struct(
                (name, val) for (name, _), val in zip(t_elem, ingested)))
    else:
        return await executor.create_value(val, type_spec)
Exemplo n.º 2
0
def is_argument_struct(arg) -> bool:
    """Determines if 'arg' is interpretable as an argument struct.

  Args:
    arg: A value or type to test.

  Returns:
    True iff 'arg' is either a `Struct` in which all unnamed elements
    precede named ones, or a `StructType` with this property, or something
    that can be converted into the latter by computation_types.to_type().

  Raises:
    TypeError: If the argument is neither an `structure.Struct`,
      nor a type spec.
  """
    if isinstance(arg, structure.Struct):
        elements = structure.to_elements(arg)
    elif isinstance(arg, typed_object.TypedObject):
        return is_argument_struct(arg.type_signature)
    else:
        arg = computation_types.to_type(arg)
        if arg.is_struct():
            elements = structure.to_elements(arg)
        else:
            return False
    max_unnamed = -1
    min_named = len(elements)
    for idx, element in enumerate(elements):
        if element[0]:
            min_named = min(min_named, idx)
        else:
            max_unnamed = idx
    return max_unnamed < min_named
Exemplo n.º 3
0
 def is_assignable_from(self, source_type: 'Type') -> bool:
     if not isinstance(source_type, StructType):
         return False
     target_elements = structure.to_elements(self)
     source_elements = structure.to_elements(source_type)
     return ((len(target_elements) == len(source_elements)) and all(
         ((source_elements[k][0] in [target_elements[k][0], None]) and
          target_elements[k][1].is_assignable_from(source_elements[k][1]))
         for k in range(len(target_elements))))
Exemplo n.º 4
0
def get_curried(fn):
    """Returns a curried version of function `fn` that takes a parameter tuple.

  For functions `fn` of types <T1,T2,....,Tn> -> U, the result is a function
  of the form T1 -> (T2 -> (T3 -> .... (Tn -> U) ... )).

  Note: No attempt is made at avoiding naming conflicts in cases where `fn`
  contains references. The arguments of the curriend function are named `argN`
  with `N` starting at 0.

  Args:
    fn: A value of a functional TFF type.

  Returns:
    A value that represents the curried form of `fn`.
  """
    py_typecheck.check_type(fn, value_base.Value)
    py_typecheck.check_type(fn.type_signature, computation_types.FunctionType)
    py_typecheck.check_type(fn.type_signature.parameter,
                            computation_types.StructType)
    param_elements = structure.to_elements(fn.type_signature.parameter)
    references = []
    for idx, (_, elem_type) in enumerate(param_elements):
        references.append(
            building_blocks.Reference('arg{}'.format(idx), elem_type))
    result = building_blocks.Call(value_impl.ValueImpl.get_comp(fn),
                                  building_blocks.Struct(references))
    for ref in references[::-1]:
        result = building_blocks.Lambda(ref.name, ref.type_signature, result)
    return value_impl.ValueImpl(result,
                                value_impl.ValueImpl.get_context_stack(fn))
Exemplo n.º 5
0
 async def _zip(self, arg, placement, all_equal):
     self._check_arg_is_structure(arg)
     py_typecheck.check_type(placement, placement_literals.PlacementLiteral)
     self._check_strategy_compatible_with_placement(placement)
     children = self._target_executors[placement]
     cardinality = len(children)
     elements = structure.to_elements(arg.internal_representation)
     for _, v in elements:
         py_typecheck.check_type(v, list)
         if len(v) != cardinality:
             raise RuntimeError('Expected {} items, found {}.'.format(
                 cardinality, len(v)))
     new_vals = []
     for idx in range(cardinality):
         new_vals.append(
             structure.Struct([(k, v[idx]) for k, v in elements]))
     new_vals = await asyncio.gather(
         *[c.create_struct(x) for c, x in zip(children, new_vals)])
     return FederatedResolvingStrategyValue(
         new_vals,
         computation_types.FederatedType(computation_types.StructType(
             ((k, v.member) if k else v.member
              for k, v in structure.iter_elements(arg.type_signature))),
                                         placement,
                                         all_equal=all_equal))
Exemplo n.º 6
0
 def _to_representative_value(type_spec, elements):
   """Convert to a container to a type understood by TF and TFF."""
   if type_spec.is_tensor():
     return elements
   elif type_spec.is_struct():
     field_types = structure.to_elements(type_spec)
     is_all_named = all([name is not None for name, _ in field_types])
     if is_all_named:
       if py_typecheck.is_named_tuple(elements):
         values = collections.OrderedDict(
             (name, _to_representative_value(field_type, e))
             for (name, field_type), e in zip(field_types, elements))
         return type(elements)(**values)
       else:
         values = [(name, _to_representative_value(field_type, elements[name]))
                   for name, field_type in field_types]
         return collections.OrderedDict(values)
     else:
       return tuple(
           _to_representative_value(t, e) for t, e in zip(type_spec, elements))
   else:
     raise ValueError(
         'Coercing a dataset with elements of expected type {!s}, '
         'produced a value with incompatible type `{!s}. Value: '
         '{!s}'.format(type_spec, type(elements), elements))
Exemplo n.º 7
0
 def test_multiple_named_and_unnamed(self):
   v = [(None, 10), ('foo', 20), ('bar', 30)]
   x = structure.Struct(v)
   self.assertLen(x, 3)
   self.assertEqual(x[0], 10)
   self.assertEqual(x[1], 20)
   self.assertEqual(x[2], 30)
   self.assertRaises(IndexError, lambda _: x[3], None)
   self.assertEqual(list(iter(x)), [10, 20, 30])
   self.assertEqual(dir(x), ['bar', 'foo'])
   self.assertEqual(structure.name_list(x), ['foo', 'bar'])
   self.assertEqual(x.foo, 20)
   self.assertEqual(x.bar, 30)
   self.assertRaises(AttributeError, lambda _: x.baz, None)
   self.assertEqual(x, structure.Struct([(None, 10), ('foo', 20),
                                         ('bar', 30)]))
   self.assertNotEqual(
       x, structure.Struct([('foo', 10), ('bar', 20), (None, 30)]))
   self.assertEqual(structure.to_elements(x), v)
   self.assertEqual(
       repr(x), 'Struct([(None, 10), (\'foo\', 20), (\'bar\', 30)])')
   self.assertEqual(str(x), '<10,foo=20,bar=30>')
   with self.assertRaisesRegex(ValueError, 'unnamed'):
     structure.to_odict(x)
   with self.assertRaisesRegex(ValueError, 'named and unnamed'):
     structure.to_odict_or_tuple(x)
Exemplo n.º 8
0
    def _lines_for_type(type_spec, formatted):
        """Returns a `list` of strings representing the given `type_spec`.

    Args:
      type_spec: An instance of a TFF `Type`.
      formatted: A boolean indicating if the returned string should be
        formatted.
    """
        if type_spec.is_abstract():
            return [type_spec.label]
        elif type_spec.is_federated():
            member_lines = _lines_for_type(type_spec.member, formatted)
            placement_line = '@{}'.format(type_spec.placement)
            if type_spec.all_equal:
                return _combine([member_lines, [placement_line]])
            else:
                return _combine([['{'], member_lines, ['}'], [placement_line]])
        elif type_spec.is_function():
            if type_spec.parameter is not None:
                parameter_lines = _lines_for_type(type_spec.parameter,
                                                  formatted)
            else:
                parameter_lines = ['']
            result_lines = _lines_for_type(type_spec.result, formatted)
            return _combine([['('], parameter_lines, [' -> '], result_lines,
                             [')']])
        elif type_spec.is_struct():
            if len(type_spec) == 0:  # pylint: disable=g-explicit-length-test
                return ['<>']
            elements = structure.to_elements(type_spec)
            elements_lines = _lines_for_named_types(elements, formatted)
            if formatted:
                elements_lines = _indent(elements_lines)
                lines = [['<', ''], elements_lines, ['', '>']]
            else:
                lines = [['<'], elements_lines, ['>']]
            return _combine(lines)
        elif type_spec.is_placement():
            return ['placement']
        elif type_spec.is_sequence():
            element_lines = _lines_for_type(type_spec.element, formatted)
            return _combine([element_lines, ['*']])
        elif type_spec.is_tensor():
            if type_spec.shape.ndims is None:
                return ['{!r}[{}]'.format(type_spec.dtype, None)]
            elif type_spec.shape.ndims > 0:

                def _value_string(value):
                    return str(value) if value is not None else '?'

                value_strings = [
                    _value_string(e.value) for e in type_spec.shape.dims
                ]
                values_strings = ','.join(value_strings)
                return ['{}[{}]'.format(type_spec.dtype.name, values_strings)]
            else:
                return [type_spec.dtype.name]
        else:
            raise NotImplementedError('Unexpected type found: {}.'.format(
                type(type_spec)))
Exemplo n.º 9
0
def make_dummy_element_for_type_spec(type_spec, none_dim_replacement=0):
    """Creates ndarray of zeros corresponding to `type_spec`.

  Returns a list containing this ndarray, whose type is *compatible* with, not
  necessarily equal to, `type_spec`. This is due to the fact that some
  dimensions of `type_spec` may be indeterminate, representing compatibility
  of `type_spec` with any number (e.g. leaving a batch dimension indeterminate
  to signify compatibility with batches of any size). However a concrete
  structure (like the ndarray) must have specified sizes for its dimensions.
  So we construct a dummy element where any `None` dimensions of the shape
  of `type_spec` are replaced with the value `none_dim_replacement`.The
  default value of 0 therefore returns a dummy element of minimal size which
  matches `type_spec`.

  Args:
    type_spec: Instance of `computation_types.Type`, or something convertible to
      one by `computation_types.to_type`.
    none_dim_replacement: `int` with which to replace any unspecified tensor
      dimensions.

  Returns:
    Returns possibly nested `numpy ndarray`s containing all zeros: a single
    `ndarray` if `type_spec` is a `computation_types.TensorType` and a list
    of such arrays if  `type_spec` is `computation_types.StructType`.
    This data structure is of the minimal size necessary in order to be
    compatible with `type_spec`.
  """
    type_spec = computation_types.to_type(type_spec)
    if not type_analysis.contains_only(
            type_spec, lambda t: t.is_struct() or t.is_tensor()):
        raise ValueError(
            'Cannot construct array for TFF type containing anything '
            'other than `computation_types.TensorType` or '
            '`computation_types.StructType`; you have passed the '
            'type {}'.format(type_spec))
    py_typecheck.check_type(none_dim_replacement, int)
    if none_dim_replacement < 0:
        raise ValueError('Please pass nonnegative integer argument as '
                         '`none_dim_replacement`.')

    def _handle_none_dimension(x):
        if x is None or (isinstance(x, tf.compat.v1.Dimension)
                         and x.value is None):
            return none_dim_replacement
        return x

    if type_spec.is_tensor():
        dummy_shape = [_handle_none_dimension(x) for x in type_spec.shape]
        if type_spec.dtype == tf.string:
            return np.empty(dummy_shape, dtype=str)
        return np.zeros(dummy_shape, type_spec.dtype.as_numpy_dtype)
    elif type_spec.is_struct():
        elements = structure.to_elements(type_spec)
        elem_list = []
        for _, elem_type in elements:
            elem_list.append(make_dummy_element_for_type_spec(elem_type))
        return elem_list
Exemplo n.º 10
0
async def _ingest(executor, val, type_spec):
    """A coroutine that handles ingestion.

  Args:
    executor: An instance of `executor_base.Executor`.
    val: The first argument to `context_base.Context.ingest()`.
    type_spec: The second argument to `context_base.Context.ingest()`.

  Returns:
    The result of the ingestion.

  Raises:
    TypeError: If the `val` is not a value of type `type_spec`.
  """
    if isinstance(val, executor_value_base.ExecutorValue):
        return val
    elif isinstance(val, ingestable_base.Ingestable):
        val_type = val.type_signature
        py_typecheck.check_type(val_type, computation_types.Type)
        type_spec.check_assignable_from(val_type)
        return await val.ingest(executor)
    elif (isinstance(val, structure.Struct) and not type_spec.is_federated()):
        type_spec.check_struct()
        v_elem = structure.to_elements(val)
        t_elem = structure.to_elements(type_spec)
        if len(v_elem) != len(t_elem):
            raise TypeError(
                'Value {} does not match type {}: mismatching tuple length.'.
                format(val, type_spec))
        for ((vk, _), (tk, _)) in zip(v_elem, t_elem):
            if vk not in [tk, None]:
                raise TypeError(
                    'Value {} does not match type {}: mismatching tuple element '
                    'names {} vs. {}.'.format(val, type_spec, vk, tk))
        ingested = []
        for (_, v), (_, t) in zip(v_elem, t_elem):
            ingested.append(_ingest(executor, v, t))
        ingested = await asyncio.gather(*ingested)
        return await executor.create_struct(
            structure.Struct(
                (name, val) for (name, _), val in zip(t_elem, ingested)))
    else:
        return await executor.create_value(val, type_spec)
Exemplo n.º 11
0
def _parameter_type(
    parameters, parameter_types: Tuple[computation_types.Type, ...]
) -> Optional[computation_types.Type]:
    """Bundle any user-provided parameter types into a single argument type."""
    parameter_names = [parameter.name for parameter in parameters]
    if not parameter_types and not parameters:
        return None
    if len(parameter_types) == 1:
        parameter_type = parameter_types[0]
        if parameter_type is None and not parameters:
            return None
        if len(parameters) == 1:
            return parameter_type
        # There is a single parameter type but multiple parameters.
        if not parameter_type.is_struct() or len(parameter_type) != len(
                parameters):
            raise TypeError(
                f'Function with {len(parameters)} parameters must have a parameter '
                f'type with the same number of parameters. Found parameter type '
                f'{parameter_type}.')
        name_list_from_types = structure.name_list(parameter_type)
        if name_list_from_types:
            if len(name_list_from_types) != len(parameter_type):
                raise TypeError(
                    'Types with both named and unnamed fields cannot be unpacked into '
                    f'argument lists. Found parameter type {parameter_type}.')
            if set(name_list_from_types) != set(parameter_names):
                raise TypeError(
                    'Function argument names must match field names of parameter type. '
                    f'Found argument names {parameter_names}, which do not match '
                    f'{name_list_from_types}, the top-level fields of the parameter '
                    f'type {parameter_type}.')
            # The provided parameter type has all named fields which exactly match
            # the names of the function's parameters.
            return parameter_type
        else:
            # The provided parameter type has no named fields. Apply the names from
            # the function parameters.
            parameter_types = (
                v for (_, v) in structure.to_elements(parameter_type))
            return computation_types.StructWithPythonType(
                list(zip(parameter_names, parameter_types)),
                collections.OrderedDict)
    elif len(parameters) == 1:
        # If there are multiple provided argument types but the function being
        # decorated only accepts a single argument, tuple the arguments together.
        return computation_types.to_type(parameter_types)
    if len(parameters) != len(parameter_types):
        raise TypeError(
            f'Function with {len(parameters)} parameters is '
            f'incompatible with provided argument types {parameter_types}.')
    # The function has `n` parameters and `n` parameter types.
    # Zip them up into a structure using the names from the function as keys.
    return computation_types.StructWithPythonType(
        list(zip(parameter_names, parameter_types)), collections.OrderedDict)
Exemplo n.º 12
0
 def _make(type_spec, next_unused_tensor_index):
     if isinstance(type_spec, computation_types.TensorType):
         obj = _XlaSerializerTensorArg(type_spec, next_unused_tensor_index)
         next_unused_tensor_index = next_unused_tensor_index + 1
         return obj, next_unused_tensor_index
     py_typecheck.check_type(type_spec, computation_types.StructType)
     elements = []
     for k, v in structure.to_elements(type_spec):
         obj, next_unused_tensor_index = _make(v, next_unused_tensor_index)
         elements.append((k, obj))
     obj = _XlaSerializerStructArg(type_spec, elements)
     return obj, next_unused_tensor_index
Exemplo n.º 13
0
 def _to_representative_value(type_spec, elements):
     """Convert to a container to a type understood by TF and TFF."""
     if type_spec.is_tensor():
         return elements
     elif type_spec.is_struct_with_python():
         if tf.is_tensor(elements):
             # In this case we have a singleton tuple tensor that may have been
             # unwrapped by tf.data.
             elements = [elements]
         py_type = computation_types.StructWithPythonType.get_container_type(
             type_spec)
         field_types = structure.iter_elements(type_spec)
         if (issubclass(py_type, collections.abc.Mapping)
                 or py_typecheck.is_attrs(py_type)):
             values = collections.OrderedDict(
                 (name,
                  _to_representative_value(field_type, elements[name]))
                 for name, field_type in field_types)
             return py_type(**values)
         else:
             values = [
                 _to_representative_value(field_type, e)
                 for (_, field_type), e in zip(field_types, elements)
             ]
             if py_typecheck.is_named_tuple(py_type):
                 return py_type(*values)
             return py_type(values)
     elif type_spec.is_struct():
         field_types = structure.to_elements(type_spec)
         is_all_named = all([name is not None for name, _ in field_types])
         if is_all_named:
             if py_typecheck.is_named_tuple(elements):
                 values = collections.OrderedDict(
                     (name, _to_representative_value(field_type, e))
                     for (name,
                          field_type), e in zip(field_types, elements))
                 return type(elements)(**values)
             else:
                 values = [
                     (name,
                      _to_representative_value(field_type, elements[name]))
                     for name, field_type in field_types
                 ]
                 return collections.OrderedDict(values)
         else:
             return tuple(
                 _to_representative_value(t, e)
                 for t, e in zip(type_spec, elements))
     else:
         raise ValueError(
             'Coercing a dataset with elements of expected type {!s}, '
             'produced a value with incompatible type `{!s}. Value: '
             '{!s}'.format(type_spec, type(elements), elements))
Exemplo n.º 14
0
def _to_struct_internal_rep(
    *, value: Any, tf_function_cache: MutableMapping[str, Any],
    type_spec: computation_types.StructType,
    device: tf.config.LogicalDevice) -> structure.Struct:
  """Converts a python container to internal representation for TF executor."""
  type_elem = structure.to_elements(type_spec)
  value_elem = (structure.to_elements(structure.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 (type_name, elem_type), (val_name,
                               elem_val) in zip(type_elem, value_elem):
    if type_name != val_name:
      raise TypeError(
          'Mismatching element names in type vs. value: {} vs. {}.'.format(
              type_name, val_name))
    elem_repr = to_representation_for_type(elem_val, tf_function_cache,
                                           elem_type, device)
    result_elem.append((type_name, elem_repr))
  return structure.Struct(result_elem)
Exemplo n.º 15
0
def unpack_args_from_struct(
        struct_with_args) -> Tuple[List[Any], Dict[str, Any]]:
    """Extracts argument types from a struct.

  Args:
    struct_with_args: An instance of either an `struct.Struct` or
      computation_types.StructType` (or something convertible to it by
      computation_types.to_type()), on which is_argument_struct() is True.

  Returns:
    A pair (args, kwargs) containing tuple elements from 'struct_with_args'.

  Raises:
    TypeError: if 'struct_with_args' is of a wrong type.
  """
    if not is_argument_struct(struct_with_args):
        raise TypeError('Not an argument struct: {}.'.format(struct_with_args))
    if isinstance(struct_with_args, structure.Struct):
        elements = structure.to_elements(struct_with_args)
    elif isinstance(struct_with_args, typed_object.TypedObject):
        elements = []
        for index, (name, _) in enumerate(
                structure.to_elements(struct_with_args.type_signature)):
            if name is not None:
                elements.append((name, getattr(struct_with_args, name)))
            else:
                elements.append((None, struct_with_args[index]))
    else:
        struct_with_args = computation_types.to_type(struct_with_args)
        struct_with_args.check_struct()
        elements = structure.to_elements(struct_with_args)
    args = []
    kwargs = {}
    for name, value in elements:
        if name is not None:
            kwargs[name] = value
        else:
            args.append(value)
    return args, kwargs
Exemplo n.º 16
0
def replace_empty_leaf_lists_with_numpy_arrays(lists, type_spec):
  """Replaces empty leaf lists in `lists` with numpy arrays.

  This function is primarily used to ensure that an appropriate TF dtype is
  inferrable for a structure, even if no elements are actually present.

  Args:
    lists: Output of `make_empty_list_structure_for_element_type_spec`.
    type_spec: An instance of `tff.Type` or something convertible to it, as in
      `make_empty_list_structure_for_element_type_spec`.

  Returns:
    The transformed version of `structure`.

  Raises:
    TypeError: If the `type_spec` is not of a form described above, or if
      `lists` is not of a type compatible with `type_spec`.
  """
  type_spec = computation_types.to_type(type_spec)
  py_typecheck.check_type(type_spec, computation_types.Type)
  if type_spec.is_tensor():
    py_typecheck.check_type(lists, list)
    if len(lists) > 0:  # pylint: disable=g-explicit-length-test
      return lists
    else:
      return np.array([], dtype=type_spec.dtype.as_numpy_dtype)
  elif type_spec.is_struct():
    elements = structure.to_elements(type_spec)
    if isinstance(lists, collections.OrderedDict):
      to_return = []
      for elem_name, elem_type in elements:
        elem_val = replace_empty_leaf_lists_with_numpy_arrays(
            lists[elem_name], elem_type)
        to_return.append((elem_name, elem_val))
      return collections.OrderedDict(to_return)
    elif isinstance(lists, tuple):
      to_return = []
      for idx, (_, elem_type) in enumerate(elements):
        elem_val = replace_empty_leaf_lists_with_numpy_arrays(
            lists[idx], elem_type)
        to_return.append(elem_val)
      return tuple(to_return)
    else:
      raise TypeError(
          'Invalid nested structure, unexpected container type {}.'.format(
              py_typecheck.type_string(type(lists))))
  else:
    raise TypeError(
        'Expected a tensor or struct type, found {}.'.format(type_spec))
Exemplo n.º 17
0
 def test_empty(self):
   v = []
   x = structure.Struct(v)
   # Explicitly test the implementation of __len__() here so use, assertLen()
   # instead of assertEmpty().
   self.assertLen(x, 0)  # pylint: disable=g-generic-assert
   self.assertRaises(IndexError, lambda _: x[0], None)
   self.assertEqual(list(iter(x)), [])
   self.assertEqual(dir(x), [])
   self.assertRaises(AttributeError, lambda _: x.foo, None)
   self.assertEqual(x, structure.Struct([]))
   self.assertNotEqual(x, structure.Struct([('foo', 10)]))
   self.assertEqual(structure.to_elements(x), v)
   self.assertEqual(structure.to_odict(x), collections.OrderedDict())
   self.assertEqual(repr(x), 'Struct([])')
   self.assertEqual(str(x), '<>')
 async def compute(self):
   if self._type_signature.is_function():
     raise TypeError(
         'Materializing a computed value of a functional TFF type {} is not '
         'possible; only non-functional values can be materialized. Did you '
         'perhaps forget to __call__() a function you declared?'.format(
             str(self._type_signature)))
   elif isinstance(self._value, executor_value_base.ExecutorValue):
     return await self._value.compute()
   else:
     # `ScopedLambda` would have had to declare a functional type, so this is
     # the only case left to handle.
     py_typecheck.check_type(self._value, structure.Struct)
     elem = structure.to_elements(self._value)
     vals = await asyncio.gather(*[v.compute() for _, v in elem])
     return structure.Struct(zip([k for k, _ in elem], vals))
Exemplo n.º 19
0
def _stamp_value_into_graph(value, type_signature, graph):
    """Stamps `value` in `graph` as an object of type `type_signature`.

  Args:
    value: An value to stamp.
    type_signature: An instance of `computation_types.Type`.
    graph: The graph to stamp in.

  Returns:
    A Python object made of tensors stamped into `graph`, `tf.data.Dataset`s,
    and `structure.Struct`s that structurally corresponds to the
    value passed at input.
  """
    py_typecheck.check_type(type_signature, computation_types.Type)
    py_typecheck.check_type(graph, tf.Graph)
    if value is None:
        return None
    if type_signature.is_tensor():
        if isinstance(value, np.ndarray):
            value_type = computation_types.TensorType(
                tf.dtypes.as_dtype(value.dtype), tf.TensorShape(value.shape))
            type_signature.is_assignable_from(value_type)
            with graph.as_default():
                return tf.constant(value)
        else:
            with graph.as_default():
                return tf.constant(value,
                                   dtype=type_signature.dtype,
                                   shape=type_signature.shape)
    elif type_signature.is_struct():
        if isinstance(value, (list, dict)):
            value = structure.from_container(value)
        stamped_elements = []
        named_type_signatures = structure.to_elements(type_signature)
        for (name, type_signature), element in zip(named_type_signatures,
                                                   value):
            stamped_element = _stamp_value_into_graph(element, type_signature,
                                                      graph)
            stamped_elements.append((name, stamped_element))
        return structure.Struct(stamped_elements)
    elif type_signature.is_sequence():
        return tensorflow_utils.make_data_set_from_elements(
            graph, value, type_signature.element)
    else:
        raise NotImplementedError(
            'Unable to stamp a value of type {} in graph.'.format(
                type_signature))
Exemplo n.º 20
0
def type_to_tf_structure(type_spec: computation_types.Type):
    """Returns nested `tf.data.experimental.Structure` for a given TFF type.

  Args:
    type_spec: A `computation_types.Type`, the type specification must be
      composed of only named tuples and tensors. In all named tuples that appear
      in the type spec, all the elements must be named.

  Returns:
    An instance of `tf.data.experimental.Structure`, possibly nested, that
    corresponds to `type_spec`.

  Raises:
    ValueError: if the `type_spec` is composed of something other than named
      tuples and tensors, or if any of the elements in named tuples are unnamed.
  """
    py_typecheck.check_type(type_spec, computation_types.Type)
    if type_spec.is_tensor():
        return tf.TensorSpec(type_spec.shape, type_spec.dtype)
    elif type_spec.is_struct():
        elements = structure.to_elements(type_spec)
        if not elements:
            raise ValueError('Empty tuples are unsupported.')
        element_outputs = [(k, type_to_tf_structure(v)) for k, v in elements]
        named = element_outputs[0][0] is not None
        if not all((e[0] is not None) == named for e in element_outputs):
            raise ValueError('Tuple elements inconsistently named.')
        if type_spec.python_container is None:
            if named:
                output = collections.OrderedDict(element_outputs)
            else:
                output = tuple(v for _, v in element_outputs)
        else:
            container_type = type_spec.python_container
            if (py_typecheck.is_named_tuple(container_type)
                    or py_typecheck.is_attrs(container_type)):
                output = container_type(**dict(element_outputs))
            elif named:
                output = container_type(element_outputs)
            else:
                output = container_type(e if e[0] is not None else e[1]
                                        for e in element_outputs)
        return output
    else:
        raise ValueError('Unsupported type {}.'.format(
            py_typecheck.type_string(type(type_spec))))
Exemplo n.º 21
0
 def test_single_unnamed(self):
   v = [(None, 10)]
   x = structure.Struct(v)
   self.assertLen(x, 1)
   self.assertRaises(IndexError, lambda _: x[1], None)
   self.assertEqual(x[0], 10)
   self.assertEqual(list(iter(x)), [10])
   self.assertEqual(dir(x), [])
   self.assertRaises(AttributeError, lambda _: x.foo, None)
   self.assertNotEqual(x, structure.Struct([]))
   self.assertNotEqual(x, structure.Struct([('foo', 10)]))
   self.assertEqual(x, structure.Struct([(None, 10)]))
   self.assertNotEqual(x, structure.Struct([(None, 10), ('foo', 20)]))
   self.assertEqual(structure.to_elements(x), v)
   self.assertEqual(repr(x), 'Struct([(None, 10)])')
   self.assertEqual(str(x), '<10>')
   with self.assertRaisesRegex(ValueError, 'unnamed'):
     structure.to_odict(x)
 async def create_value(self, value, type_spec=None):
   type_spec = computation_types.to_type(type_spec)
   if isinstance(value, computation_impl.ComputationImpl):
     return await self.create_value(
         computation_impl.ComputationImpl.get_proto(value),
         executor_utils.reconcile_value_with_type_spec(value, type_spec))
   elif isinstance(value, pb.Computation):
     return await self._evaluate(value)
   elif type_spec is not None and type_spec.is_struct():
     v_el = structure.to_elements(structure.from_container(value))
     vals = await asyncio.gather(
         *[self.create_value(val, t) for (_, val), t in zip(v_el, type_spec)])
     return ReferenceResolvingExecutorValue(
         structure.Struct((name, val) for (name, _), val in zip(v_el, vals)))
   else:
     return ReferenceResolvingExecutorValue(await
                                            self._target_executor.create_value(
                                                value, type_spec))
Exemplo n.º 23
0
def make_empty_list_structure_for_element_type_spec(type_spec):
    """Creates a nested structure of empty Python lists for `type_spec`.

  This function prepares a nested structure made of `collections.OrderedDict`s
  and Python `tuple`s at the intermediate (non-leaf) levels, and that has empty
  Python `list`s at the leaf level, with the shape of the structure matching
  that of `type_spec`. This structure is used to accumulate elemnts of a data
  set for ingestion by `tf.data.Dataset.from_tensor_slices`.

  Args:
    type_spec: An instance of `tff.Type` or something convertible to it that
      consists of only tensor and named tuple types, and in which rach of the
      named tuples either have all or none of their elements named.

  Returns:
    The nested structure, as described above.

  Raises:
    TypeError: If the `type_spec` is not of a form described above.
  """
    type_spec = computation_types.to_type(type_spec)
    py_typecheck.check_type(type_spec, computation_types.Type)
    if type_spec.is_tensor():
        return []
    elif type_spec.is_struct():
        elements = structure.to_elements(type_spec)
        if all(k is not None for k, _ in elements):
            return collections.OrderedDict([
                (k, make_empty_list_structure_for_element_type_spec(v))
                for k, v in elements
            ])
        elif all(k is None for k, _ in elements):
            return tuple([
                make_empty_list_structure_for_element_type_spec(v)
                for _, v in elements
            ])
        else:
            raise TypeError(
                'Expected a named tuple type with either all elements named or all '
                'unnamed, got {}.'.format(type_spec))
    else:
        raise TypeError(
            'Expected a tensor or named tuple type, found {}.'.format(
                type_spec))
Exemplo n.º 24
0
 def test_single_named(self):
   v = [('foo', 20)]
   x = structure.Struct(v)
   self.assertLen(x, 1)
   self.assertEqual(x[0], 20)
   self.assertRaises(IndexError, lambda _: x[1], None)
   self.assertEqual(list(iter(x)), [20])
   self.assertEqual(dir(x), ['foo'])
   self.assertEqual(x.foo, 20)
   self.assertRaises(AttributeError, lambda _: x.bar, None)
   self.assertNotEqual(x, structure.Struct([]))
   self.assertNotEqual(x, structure.Struct([('foo', 10)]))
   self.assertNotEqual(x, structure.Struct([(None, 20)]))
   self.assertEqual(x, structure.Struct([('foo', 20)]))
   self.assertNotEqual(x, structure.Struct([('foo', 20), ('bar', 30)]))
   self.assertEqual(structure.to_elements(x), v)
   self.assertEqual(repr(x), 'Struct([(\'foo\', 20)])')
   self.assertEqual(str(x), '<foo=20>')
   self.assertEqual(structure.to_odict(x), collections.OrderedDict(v))
Exemplo n.º 25
0
 async def create_struct(self, elements):
     if not isinstance(elements, structure.Struct):
         elements = structure.from_container(elements)
     element_strings = []
     element_kv_pairs = structure.to_elements(elements)
     to_gather = []
     type_elements = []
     for k, v in element_kv_pairs:
         py_typecheck.check_type(v, CachedValue)
         to_gather.append(v.target_future)
         if k is not None:
             py_typecheck.check_type(k, str)
             element_strings.append('{}={}'.format(k, v.identifier))
             type_elements.append((k, v.type_signature))
         else:
             element_strings.append(str(v.identifier))
             type_elements.append(v.type_signature)
     type_spec = computation_types.StructType(type_elements)
     gathered = await asyncio.gather(*to_gather)
     identifier = CachedValueIdentifier('<{}>'.format(
         ','.join(element_strings)))
     try:
         cached_value = self._cache[identifier]
     except KeyError:
         target_future = asyncio.ensure_future(
             self._target_executor.create_struct(
                 structure.Struct(
                     (k, v)
                     for (k, _), v in zip(element_kv_pairs, gathered))))
         cached_value = CachedValue(identifier, None, type_spec,
                                    target_future)
         self._cache[identifier] = cached_value
     try:
         target_value = await cached_value.target_future
     except Exception:
         # TODO(b/145514490): This is a bit heavy handed, there maybe caches where
         # only the current cache item needs to be invalidated; however this
         # currently only occurs when an inner RemoteExecutor has the backend go
         # down.
         self._cache = {}
         raise
     type_spec.check_assignable_from(target_value.type_signature)
     return cached_value
def infer_cardinalities(value, type_spec):
  """Infers cardinalities from Python `value`.

  Allows for any Python object to represent a federated value; enforcing
  particular representations is not the job of this inference function, but
  rather ingestion functions lower in the stack.

  Args:
    value: Python object from which to infer TFF placement cardinalities.
    type_spec: The TFF type spec for `value`, determining the semantics for
      inferring cardinalities. That is, we only pull the cardinality off of
      federated types.

  Returns:
    Dict of cardinalities.

  Raises:
    ValueError: If conflicting cardinalities are inferred from `value`.
    TypeError: If the arguments are of the wrong types, or if `type_spec` is
      a federated type which is not `all_equal` but the yet-to-be-embedded
      `value` is not represented as a Python `list`.
  """
  if value is None:
    return {}
  py_typecheck.check_type(type_spec, computation_types.Type)
  if isinstance(value, cardinality_carrying_base.CardinalityCarrying):
    return value.cardinality
  if type_spec.is_federated():
    if type_spec.all_equal:
      return {}
    if not isinstance(value, (list, tuple)):
      raise InvalidNonAllEqualValueError(value, type_spec)
    return {type_spec.placement: len(value)}
  elif type_spec.is_struct():
    structure_value = structure.from_container(value, recursive=False)
    cardinality_dict = {}
    for idx, (_, elem_type) in enumerate(structure.to_elements(type_spec)):
      cardinality_dict = merge_cardinalities(
          cardinality_dict, infer_cardinalities(structure_value[idx],
                                                elem_type))
    return cardinality_dict
  else:
    return {}
Exemplo n.º 27
0
    async def compute_federated_zip_at_clients(
        self, arg: FederatedComposingStrategyValue
    ) -> FederatedComposingStrategyValue:
        py_typecheck.check_type(arg.type_signature,
                                computation_types.StructType)
        py_typecheck.check_len(arg.type_signature, 2)
        py_typecheck.check_type(arg.internal_representation, structure.Struct)
        py_typecheck.check_len(arg.internal_representation, 2)
        keys = [k for k, _ in structure.to_elements(arg.type_signature)]
        vals = [arg.internal_representation[n] for n in [0, 1]]
        types = [arg.type_signature[n] for n in [0, 1]]
        for n in [0, 1]:
            type_analysis.check_federated_type(
                types[n], placement=placement_literals.CLIENTS)
            types[n] = computation_types.at_clients(types[n].member)
            py_typecheck.check_type(vals[n], list)
            py_typecheck.check_len(vals[n], len(self._target_executors))
        item_type = computation_types.StructType([
            ((keys[n], types[n].member) if keys[n] else types[n].member)
            for n in [0, 1]
        ])
        result_type = computation_types.at_clients(item_type)
        zip_type = computation_types.FunctionType(
            computation_types.StructType([
                ((keys[n], types[n]) if keys[n] else types[n]) for n in [0, 1]
            ]), result_type)
        zip_comp = executor_utils.create_intrinsic_comp(
            intrinsic_defs.FEDERATED_ZIP_AT_CLIENTS, zip_type)

        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_struct(structure.Struct([(keys[0], x),
                                                   (keys[1], y)])))

        result = await asyncio.gather(*[
            _child_fn(c, x, y)
            for c, x, y in zip(self._target_executors, vals[0], vals[1])
        ])
        return FederatedComposingStrategyValue(result, result_type)
Exemplo n.º 28
0
    async def create_struct(self, elements):
        """Creates a tuple of `elements`.

    Args:
      elements: As documented in `executor_base.Executor`.

    Returns:
      An instance of `EagerValue` that represents the constructed tuple.
    """
        elements = structure.to_elements(structure.from_container(elements))
        val_elements = []
        type_elements = []
        for k, v in elements:
            py_typecheck.check_type(v, EagerValue)
            val_elements.append((k, v.internal_representation))
            type_elements.append((k, v.type_signature))
        return EagerValue(
            structure.Struct(val_elements), self._tf_function_cache,
            computation_types.StructType([(k, v) if k is not None else v
                                          for k, v in type_elements]))
Exemplo n.º 29
0
 def _assert_binding_matches_type_and_value(self, binding, type_spec, val,
                                            graph):
   """Asserts that 'bindings' matches the given type, value, and graph."""
   self.assertIsInstance(binding, pb.TensorFlow.Binding)
   self.assertIsInstance(type_spec, computation_types.Type)
   binding_oneof = binding.WhichOneof('binding')
   if binding_oneof == 'tensor':
     self.assertTrue(tf.is_tensor(val))
     if not isinstance(val, tf.Variable):
       # We insert a read_value() op for Variables, which produces
       # a name we don't control. Otherwise, names should match:
       self.assertEqual(binding.tensor.tensor_name, val.name)
     self.assertIsInstance(type_spec, computation_types.TensorType)
     self.assertEqual(type_spec.dtype, val.dtype.base_dtype)
     self.assertEqual(repr(type_spec.shape), repr(val.shape))
   elif binding_oneof == 'sequence':
     self.assertIsInstance(val,
                           type_conversions.TF_DATASET_REPRESENTATION_TYPES)
     sequence_oneof = binding.sequence.WhichOneof('binding')
     self.assertEqual(sequence_oneof, 'variant_tensor_name')
     variant_tensor = graph.get_tensor_by_name(
         binding.sequence.variant_tensor_name)
     op = str(variant_tensor.op.type)
     self.assertTrue((op == 'Placeholder') or ('Dataset' in op))
     self.assertEqual(variant_tensor.dtype, tf.variant)
     self.assertIsInstance(type_spec, computation_types.SequenceType)
     self.assertEqual(
         computation_types.to_type(val.element_spec), type_spec.element)
   elif binding_oneof == 'struct':
     self.assertIsInstance(type_spec, computation_types.StructType)
     if not isinstance(val, (list, tuple, structure.Struct)):
       self.assertIsInstance(val, dict)
       if isinstance(val, collections.OrderedDict):
         val = list(val.values())
       else:
         val = [v for _, v in sorted(val.items())]
     for idx, e in enumerate(structure.to_elements(type_spec)):
       self._assert_binding_matches_type_and_value(binding.struct.element[idx],
                                                   e[1], val[idx], graph)
   else:
     self.fail('Unknown binding.')
Exemplo n.º 30
0
def type_to_tf_dtypes_and_shapes(type_spec: computation_types.Type):
    """Returns nested structures of tensor dtypes and shapes for a given TFF type.

  The returned dtypes and shapes match those used by `tf.data.Dataset`s to
  indicate the type and shape of their elements. They can be used, e.g., as
  arguments in constructing an iterator over a string handle.

  Args:
    type_spec: A `computation_types.Type`, the type specification must be
      composed of only named tuples and tensors. In all named tuples that appear
      in the type spec, all the elements must be named.

  Returns:
    A pair of parallel nested structures with the dtypes and shapes of tensors
    defined in `type_spec`. The layout of the two structures returned is the
    same as the layout of the nested type defined by `type_spec`. Named tuples
    are represented as dictionaries.

  Raises:
    ValueError: if the `type_spec` is composed of something other than named
      tuples and tensors, or if any of the elements in named tuples are unnamed.
  """
    py_typecheck.check_type(type_spec, computation_types.Type)
    if type_spec.is_tensor():
        return (type_spec.dtype, type_spec.shape)
    elif type_spec.is_struct():
        elements = structure.to_elements(type_spec)
        if not elements:
            output_dtypes = []
            output_shapes = []
        elif elements[0][0] is not None:
            output_dtypes = collections.OrderedDict()
            output_shapes = collections.OrderedDict()
            for e in elements:
                element_name = e[0]
                element_spec = e[1]
                if element_name is None:
                    raise ValueError(
                        'When a sequence appears as a part of a parameter to a section '
                        'of TensorFlow code, in the type signature of elements of that '
                        'sequence all named tuples must have their elements explicitly '
                        'named, and this does not appear to be the case in {}.'
                        .format(type_spec))
                element_output = type_to_tf_dtypes_and_shapes(element_spec)
                output_dtypes[element_name] = element_output[0]
                output_shapes[element_name] = element_output[1]
        else:
            output_dtypes = []
            output_shapes = []
            for e in elements:
                element_name = e[0]
                element_spec = e[1]
                if element_name is not None:
                    raise ValueError(
                        'When a sequence appears as a part of a parameter to a section '
                        'of TensorFlow code, in the type signature of elements of that '
                        'sequence all named tuples must have their elements explicitly '
                        'named, and this does not appear to be the case in {}.'
                        .format(type_spec))
                element_output = type_to_tf_dtypes_and_shapes(element_spec)
                output_dtypes.append(element_output[0])
                output_shapes.append(element_output[1])
        if type_spec.python_container is not None:
            container_type = type_spec.python_container

            def build_py_container(elements):
                if (py_typecheck.is_named_tuple(container_type)
                        or py_typecheck.is_attrs(container_type)):
                    return container_type(**dict(elements))
                else:
                    return container_type(elements)

            output_dtypes = build_py_container(output_dtypes)
            output_shapes = build_py_container(output_shapes)
        else:
            output_dtypes = tuple(output_dtypes)
            output_shapes = tuple(output_shapes)
        return (output_dtypes, output_shapes)
    else:
        raise ValueError('Unsupported type {}.'.format(
            py_typecheck.type_string(type(type_spec))))