Exemplo n.º 1
0
 async def create_selection(self, source, index=None, name=None):
   py_typecheck.check_type(source, CachedValue)
   py_typecheck.check_type(source.type_signature,
                           computation_types.NamedTupleType)
   source_val = await source.target_future
   if index is not None:
     py_typecheck.check_none(name)
     identifier_str = '{}[{}]'.format(source.identifier, index)
     type_spec = source.type_signature[index]
   else:
     py_typecheck.check_not_none(name)
     identifier_str = '{}.{}'.format(source.identifier, name)
     type_spec = getattr(source.type_signature, name)
   identifier = CachedValueIdentifier(identifier_str)
   try:
     cached_value = self._cache[identifier]
   except KeyError:
     target_future = asyncio.ensure_future(
         self._target_executor.create_selection(
             source_val, index=index, name=name))
     cached_value = CachedValue(identifier, None, type_spec, target_future)
     self._cache[identifier] = cached_value
   try:
     target_value = await cached_value.target_future
   except Exception as e:
     # 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 e
   type_analysis.check_assignable_from(type_spec, target_value.type_signature)
   return cached_value
Exemplo n.º 2
0
 async def create_call(self, comp, arg=None):
   py_typecheck.check_type(comp, CachedValue)
   py_typecheck.check_type(comp.type_signature, computation_types.FunctionType)
   to_gather = [comp.target_future]
   if arg is not None:
     py_typecheck.check_type(arg, CachedValue)
     type_analysis.check_assignable_from(comp.type_signature.parameter,
                                         arg.type_signature)
     to_gather.append(arg.target_future)
     identifier_str = '{}({})'.format(comp.identifier, arg.identifier)
   else:
     identifier_str = '{}()'.format(comp.identifier)
   gathered = await asyncio.gather(*to_gather)
   type_spec = comp.type_signature.result
   identifier = CachedValueIdentifier(identifier_str)
   try:
     cached_value = self._cache[identifier]
   except KeyError:
     target_future = asyncio.ensure_future(
         self._target_executor.create_call(*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 as e:
     # 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 e
   type_analysis.check_assignable_from(type_spec, target_value.type_signature)
   return cached_value
Exemplo n.º 3
0
def deserialize_value(value_proto):
    """Deserializes a value (of any type) from `executor_pb2.Value`.

  Args:
    value_proto: An instance of `executor_pb2.Value`.

  Returns:
    A tuple `(value, type_spec)`, where `value` is a deserialized
    representation of the transmitted value (e.g., Numpy array, or a
    `pb.Computation` instance), and `type_spec` is an instance of
    `tff.TensorType` that represents its type.

  Raises:
    TypeError: If the arguments are of the wrong types.
    ValueError: If the value is malformed.
  """
    py_typecheck.check_type(value_proto, executor_pb2.Value)
    which_value = value_proto.WhichOneof('value')
    if which_value == 'tensor':
        return deserialize_tensor_value(value_proto)
    elif which_value == 'computation':
        return (value_proto.computation,
                type_serialization.deserialize_type(
                    value_proto.computation.type))
    elif which_value == 'tuple':
        val_elems = []
        type_elems = []
        for e in value_proto.tuple.element:
            name = e.name if e.name else None
            e_val, e_type = deserialize_value(e.value)
            val_elems.append((name, e_val))
            type_elems.append((name, e_type) if name else e_type)
        return (anonymous_tuple.AnonymousTuple(val_elems),
                computation_types.NamedTupleType(type_elems))
    elif which_value == 'sequence':
        return deserialize_sequence_value(value_proto.sequence)
    elif which_value == 'federated':
        type_spec = type_serialization.deserialize_type(
            computation_pb2.Type(federated=value_proto.federated.type))
        value = []
        for item in value_proto.federated.value:
            item_value, item_type = deserialize_value(item)
            type_analysis.check_assignable_from(type_spec.member, item_type)
            value.append(item_value)
        if type_spec.all_equal:
            if len(value) == 1:
                value = value[0]
            else:
                raise ValueError(
                    'Return an all_equal value with {} member consatituents.'.
                    format(len(value)))
        return value, type_spec
    else:
        raise ValueError(
            'Unable to deserialize a value of type {}.'.format(which_value))
Exemplo n.º 4
0
def serialize_tensor_value(value, type_spec=None):
    """Serializes a tensor value into `executor_pb2.Value`.

  Args:
    value: A Numpy array or other object understood by `tf.make_tensor_proto`.
    type_spec: An optional type spec, a `tff.TensorType` or something
      convertible to it.

  Returns:
    A tuple `(value_proto, ret_type_spec)` in which `value_proto` is an instance
    of `executor_pb2.Value` with the serialized content of `value`, and
    `ret_type_spec` is the type of the serialized value. The `ret_type_spec` is
    the same as the argument `type_spec` if that argument was not `None`. If
    the argument was `None`, `ret_type_spec` is a type determined from `value`.

  Raises:
    TypeError: If the arguments are of the wrong types.
    ValueError: If the value is malformed.
  """
    if isinstance(value, tf.Tensor):
        if type_spec is None:
            type_spec = computation_types.TensorType(
                dtype=tf.dtypes.as_dtype(value.dtype),
                shape=tf.TensorShape(value.shape))
        value = value.numpy()
    if type_spec is not None:
        type_spec = computation_types.to_type(type_spec)
        py_typecheck.check_type(type_spec, computation_types.TensorType)
        if isinstance(value, np.ndarray):
            tensor_proto = tf.make_tensor_proto(value,
                                                dtype=type_spec.dtype,
                                                verify_shape=False)
            type_analysis.check_assignable_from(
                type_spec,
                computation_types.TensorType(
                    dtype=tf.dtypes.as_dtype(tensor_proto.dtype),
                    shape=tf.TensorShape(tensor_proto.tensor_shape)))
        else:
            tensor_proto = tf.make_tensor_proto(value,
                                                dtype=type_spec.dtype,
                                                shape=type_spec.shape,
                                                verify_shape=True)
    else:
        tensor_proto = tf.make_tensor_proto(value)
        type_spec = computation_types.TensorType(
            dtype=tf.dtypes.as_dtype(tensor_proto.dtype),
            shape=tf.TensorShape(tensor_proto.tensor_shape))
    any_pb = any_pb2.Any()
    any_pb.Pack(tensor_proto)
    return executor_pb2.Value(tensor=any_pb), type_spec
Exemplo n.º 5
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 `anonymous_tuple.AnonymousTuple`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 isinstance(type_signature, computation_types.TensorType):
        if isinstance(value, np.ndarray):
            value_type = computation_types.TensorType(
                tf.dtypes.as_dtype(value.dtype), tf.TensorShape(value.shape))
            type_analysis.check_assignable_from(type_signature, 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 isinstance(type_signature, computation_types.NamedTupleType):
        if isinstance(value, (list, dict)):
            value = anonymous_tuple.from_container(value)
        stamped_elements = []
        named_type_signatures = anonymous_tuple.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 anonymous_tuple.AnonymousTuple(stamped_elements)
    elif isinstance(type_signature, computation_types.SequenceType):
        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))
 async def create_call(self, comp, arg=None):
     py_typecheck.check_type(comp, CompositeValue)
     if arg is not None:
         py_typecheck.check_type(arg, CompositeValue)
         py_typecheck.check_type(comp.type_signature,
                                 computation_types.FunctionType)
         param_type = comp.type_signature.parameter
         type_analysis.check_assignable_from(param_type, arg.type_signature)
         arg = CompositeValue(arg.internal_representation, param_type)
     if isinstance(comp.internal_representation, pb.Computation):
         which_computation = comp.internal_representation.WhichOneof(
             'computation')
         if which_computation == 'tensorflow':
             call_args = [
                 self._parent_executor.create_value(
                     comp.internal_representation, comp.type_signature)
             ]
             if arg is not None:
                 call_args.append(
                     executor_utils.delegate_entirely_to_executor(
                         arg.internal_representation, arg.type_signature,
                         self._parent_executor))
             result = await self._parent_executor.create_call(
                 *(await asyncio.gather(*call_args)))
             return CompositeValue(result, result.type_signature)
         else:
             raise ValueError(
                 'Directly calling computations of type {} is unsupported.'.
                 format(which_computation))
     elif isinstance(comp.internal_representation,
                     intrinsic_defs.IntrinsicDef):
         coro = getattr(
             self, '_compute_intrinsic_{}'.format(
                 comp.internal_representation.uri), None)
         if coro is not None:
             return await coro(arg)  # pylint: disable=not-callable
         else:
             raise NotImplementedError(
                 'Support for intrinsic "{}" has not been implemented yet.'.
                 format(comp.internal_representation.uri))
     else:
         raise ValueError(
             'Calling objects of type {} is unsupported.'.format(
                 py_typecheck.type_string(type(
                     comp.internal_representation))))
Exemplo n.º 7
0
 async def create_tuple(self, elements):
     if not isinstance(elements, anonymous_tuple.AnonymousTuple):
         elements = anonymous_tuple.from_container(elements)
     element_strings = []
     element_kv_pairs = anonymous_tuple.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.NamedTupleType(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_tuple(
                 anonymous_tuple.AnonymousTuple(
                     (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 as e:
         # 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 e
     type_analysis.check_assignable_from(type_spec,
                                         target_value.type_signature)
     return cached_value
Exemplo n.º 8
0
def serialize_value(value, type_spec=None):
    """Serializes a value into `executor_pb2.Value`.

  Args:
    value: A value to be serialized.
    type_spec: Optional type spec, a `tff.Type` or something convertible to it.

  Returns:
    A tuple `(value_proto, ret_type_spec)` where `value_proto` is an instance
    of `executor_pb2.Value` with the serialized content of `value`, and the
    returned `ret_type_spec` is an instance of `tff.Type` that represents the
    TFF type of the serialized value.

  Raises:
    TypeError: If the arguments are of the wrong types.
    ValueError: If the value is malformed.
  """
    type_spec = computation_types.to_type(type_spec)
    if isinstance(value, computation_pb2.Computation):
        type_spec = type_utils.reconcile_value_type_with_type_spec(
            type_serialization.deserialize_type(value.type), type_spec)
        return executor_pb2.Value(computation=value), type_spec
    elif isinstance(value, computation_impl.ComputationImpl):
        return serialize_value(
            computation_impl.ComputationImpl.get_proto(value),
            type_utils.reconcile_value_with_type_spec(value, type_spec))
    elif isinstance(type_spec, computation_types.TensorType):
        return serialize_tensor_value(value, type_spec)
    elif isinstance(type_spec, computation_types.NamedTupleType):
        type_elements = anonymous_tuple.to_elements(type_spec)
        val_elements = anonymous_tuple.to_elements(
            anonymous_tuple.from_container(value))
        tup_elems = []
        for (e_name, e_type), (_, e_val) in zip(type_elements, val_elements):
            e_proto, _ = serialize_value(e_val, e_type)
            tup_elems.append(
                executor_pb2.Value.Tuple.Element(
                    name=e_name if e_name else None, value=e_proto))
        result_proto = (executor_pb2.Value(tuple=executor_pb2.Value.Tuple(
            element=tup_elems)))
        return result_proto, type_spec
    elif isinstance(type_spec, computation_types.SequenceType):
        if not isinstance(value,
                          type_conversions.TF_DATASET_REPRESENTATION_TYPES):
            raise TypeError(
                'Cannot serialize Python type {!s} as TFF type {!s}.'.format(
                    py_typecheck.type_string(type(value)),
                    type_spec if type_spec is not None else 'unknown'))

        value_type = computation_types.SequenceType(
            computation_types.to_type(value.element_spec))
        if not type_analysis.is_assignable_from(type_spec, value_type):
            raise TypeError(
                'Cannot serialize dataset with elements of type {!s} as TFF type {!s}.'
                .format(value_type,
                        type_spec if type_spec is not None else 'unknown'))

        return serialize_sequence_value(value), type_spec
    elif isinstance(type_spec, computation_types.FederatedType):
        if type_spec.all_equal:
            value = [value]
        else:
            py_typecheck.check_type(value, list)
        items = []
        for v in value:
            it, it_type = serialize_value(v, type_spec.member)
            type_analysis.check_assignable_from(type_spec.member, it_type)
            items.append(it)
        result_proto = executor_pb2.Value(
            federated=executor_pb2.Value.Federated(
                type=type_serialization.serialize_type(type_spec).federated,
                value=items))
        return result_proto, type_spec
    else:
        raise ValueError(
            'Unable to serialize value with Python type {} and {} TFF type.'.
            format(str(py_typecheck.type_string(type(value))),
                   str(type_spec) if type_spec is not None else 'unknown'))
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))
Exemplo n.º 10
0
    def __init__(self, initialize, prepare, work, zero, accumulate, merge,
                 report, bitwidth, update):
        """Constructs a representation of a MapReduce-like iterative process.

    Note: All the computations supplied here as arguments must be TensorFlow
    computations, i.e., instances of `tff.Computation` constructed by the
    `tff.tf_computation` decorator/wrapper.

    Args:
      initialize: The computation that produces the initial server state.
      prepare: The computation that prepares the input for the clients.
      work: The client-side work computation.
      zero: The computation that produces the initial state for accumulators.
      accumulate: The computation that adds a client update to an accumulator.
      merge: The computation to use for merging pairs of accumulators.
      report: The computation that produces the final server-side aggregate for
        the top level accumulator (the global update).
      bitwidth: The computation that produces the bitwidth for secure sum.
      update: The computation that takes the global update and the server state
        and produces the new server state, as well as server-side output.

    Raises:
      TypeError: If the Python or TFF types of the arguments are invalid or not
        compatible with each other.
      AssertionError: If the manner in which the given TensorFlow computations
        are represented by TFF does not match what this code is expecting (this
        is an internal error that requires code update).
    """
        for label, comp in [
            ('initialize', initialize),
            ('prepare', prepare),
            ('work', work),
            ('zero', zero),
            ('accumulate', accumulate),
            ('merge', merge),
            ('report', report),
            ('bitwidth', bitwidth),
            ('update', update),
        ]:
            py_typecheck.check_type(comp, computation_base.Computation, label)

            # TODO(b/130633916): Remove private access once an appropriate API for it
            # becomes available.
            comp_proto = comp._computation_proto  # pylint: disable=protected-access

            if not isinstance(comp_proto, computation_pb2.Computation):
                # Explicitly raised to force it to be done in non-debug mode as well.
                raise AssertionError(
                    'Cannot find the embedded computation definition.')
            which_comp = comp_proto.WhichOneof('computation')
            if which_comp != 'tensorflow':
                raise TypeError(
                    'Expected all computations supplied as arguments to '
                    'be plain TensorFlow, found {}.'.format(which_comp))

        if prepare.type_signature.parameter != initialize.type_signature.result:
            raise TypeError(
                'The `prepare` computation expects an argument of type {}, '
                'which does not match the result type {} of `initialize`.'.
                format(prepare.type_signature.parameter,
                       initialize.type_signature.result))

        if (not isinstance(work.type_signature.parameter,
                           computation_types.NamedTupleType)
                or len(work.type_signature.parameter) != 2):
            raise TypeError(
                'The `work` computation expects an argument of type {} that is not '
                'a two-tuple.'.format(work.type_signature.parameter))

        if work.type_signature.parameter[1] != prepare.type_signature.result:
            raise TypeError(
                'The `work` computation expects an argument tuple with type {} as '
                'the second element (the initial client state from the server), '
                'which does not match the result type {} of `prepare`.'.format(
                    work.type_signature.parameter[1],
                    prepare.type_signature.result))

        if (not isinstance(work.type_signature.result,
                           computation_types.NamedTupleType)
                or len(work.type_signature.result) != 2):
            raise TypeError(
                'The `work` computation returns a result  of type {} that is not a '
                'two-tuple.'.format(work.type_signature.result))

        py_typecheck.check_type(zero.type_signature,
                                computation_types.FunctionType)

        py_typecheck.check_type(accumulate.type_signature,
                                computation_types.FunctionType)
        py_typecheck.check_len(accumulate.type_signature.parameter, 2)
        type_analysis.check_assignable_from(
            accumulate.type_signature.parameter[0], zero.type_signature.result)
        if (accumulate.type_signature.parameter[1] !=
                work.type_signature.result[0][0]):

            raise TypeError(
                'The `accumulate` computation expects a second argument of type {}, '
                'which does not match the expected {} as implied by the type '
                'signature of `work`.'.format(
                    accumulate.type_signature.parameter[1],
                    work.type_signature.result[0][0]))
        type_analysis.check_assignable_from(
            accumulate.type_signature.parameter[0],
            accumulate.type_signature.result)

        py_typecheck.check_type(merge.type_signature,
                                computation_types.FunctionType)
        py_typecheck.check_len(merge.type_signature.parameter, 2)
        type_analysis.check_assignable_from(merge.type_signature.parameter[0],
                                            accumulate.type_signature.result)
        type_analysis.check_assignable_from(merge.type_signature.parameter[1],
                                            accumulate.type_signature.result)
        type_analysis.check_assignable_from(merge.type_signature.parameter[0],
                                            merge.type_signature.result)

        py_typecheck.check_type(report.type_signature,
                                computation_types.FunctionType)
        type_analysis.check_assignable_from(report.type_signature.parameter,
                                            merge.type_signature.result)

        py_typecheck.check_type(bitwidth.type_signature,
                                computation_types.FunctionType)

        expected_update_parameter_type = computation_types.to_type([
            initialize.type_signature.result,
            [report.type_signature.result, work.type_signature.result[0][1]],
        ])
        if update.type_signature.parameter != expected_update_parameter_type:
            raise TypeError(
                'The `update` computation expects an argument of type {}, '
                'which does not match the expected {} as implied by the type '
                'signatures of `initialize`, `report`, and `work`.'.format(
                    update.type_signature.parameter,
                    expected_update_parameter_type))

        if (not isinstance(update.type_signature.result,
                           computation_types.NamedTupleType)
                or len(update.type_signature.result) != 2):
            raise TypeError(
                'The `update` computation returns a result  of type {} that is not '
                'a two-tuple.'.format(update.type_signature.result))

        if update.type_signature.result[0] != initialize.type_signature.result:
            raise TypeError(
                'The `update` computation returns a result tuple with type {} as '
                'the first element (the updated state of the server), which does '
                'not match the result type {} of `initialize`.'.format(
                    update.type_signature.result[0],
                    initialize.type_signature.result))

        self._initialize = initialize
        self._prepare = prepare
        self._work = work
        self._zero = zero
        self._accumulate = accumulate
        self._merge = merge
        self._report = report
        self._bitwidth = bitwidth
        self._update = update
Exemplo n.º 11
0
    async def create_call(self, comp, arg=None):
        """A coroutine that creates a call to `comp` with optional argument `arg`.

    Args:
      comp: The computation to invoke.
      arg: An optional argument of the call, or `None` if no argument was
        supplied.

    Returns:
      An instance of `FederatingExecutorValue` that represents the constructed
      call.

    Raises:
      TypeError: If `comp` or `arg` are not embedded in the executor, before
        calling `create_call` or if the `type_signature` of `arg` do not match
        the expected `type_signature` of the parameter to `comp`.
      ValueError: If `comp` is not a functional kind recognized by
        `FederatingExecutor` or if `comp` is a lambda with an argument.
      NotImplementedError: If `comp` is an intrinsic and it has not been
        implemented by the `FederatingExecutor`.
    """
        py_typecheck.check_type(comp, FederatingExecutorValue)
        if arg is not None:
            py_typecheck.check_type(arg, FederatingExecutorValue)
            py_typecheck.check_type(comp.type_signature,
                                    computation_types.FunctionType)
            param_type = comp.type_signature.parameter
            type_analysis.check_assignable_from(param_type, arg.type_signature)
            arg = FederatingExecutorValue(arg.internal_representation,
                                          param_type)
        if isinstance(comp.internal_representation, pb.Computation):
            which_computation = comp.internal_representation.WhichOneof(
                'computation')
            if which_computation == 'tensorflow':
                child = self._target_executors[None][0]
                embedded_comp = await child.create_value(
                    comp.internal_representation, comp.type_signature)
                if arg is not None:
                    embedded_arg = await executor_utils.delegate_entirely_to_executor(
                        arg.internal_representation, arg.type_signature, child)
                else:
                    embedded_arg = None
                result = await child.create_call(embedded_comp, embedded_arg)
                return FederatingExecutorValue(result, result.type_signature)
            else:
                raise ValueError(
                    'Directly calling computations of type {} is unsupported.'.
                    format(which_computation))
        elif isinstance(comp.internal_representation,
                        intrinsic_defs.IntrinsicDef):
            coro = getattr(
                self, '_compute_intrinsic_{}'.format(
                    comp.internal_representation.uri), None)
            if coro is not None:
                return await coro(arg)  # pylint: disable=not-callable
            else:
                raise NotImplementedError(
                    'Support for intrinsic "{}" has not been implemented yet.'.
                    format(comp.internal_representation.uri))
        else:
            raise ValueError(
                'Calling objects of type {} is unsupported.'.format(
                    py_typecheck.type_string(type(
                        comp.internal_representation))))
Exemplo n.º 12
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_analysis.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):
            deserialized_type = type_serialization.deserialize_type(value.type)
            if type_spec is None:
                type_spec = deserialized_type
            else:
                type_analysis.check_assignable_from(type_spec,
                                                    deserialized_type)
            which_computation = value.WhichOneof('computation')
            if which_computation in ['lambda', 'tensorflow']:
                return FederatingExecutorValue(value, type_spec)
            elif which_computation == 'intrinsic':
                intrinsic_def = intrinsic_defs.uri_to_intrinsic_def(
                    value.intrinsic.uri)
                if intrinsic_def is None:
                    raise ValueError(
                        'Encountered an unrecognized intrinsic "{}".'.format(
                            value.intrinsic.uri))
                return await self.create_value(intrinsic_def, type_spec)
            else:
                raise ValueError(
                    'Unsupported computation building block of type "{}".'.
                    format(which_computation))
        elif isinstance(type_spec, computation_types.FederatedType):
            self._check_value_compatible_with_placement(
                value, type_spec.placement, type_spec.all_equal)
            children = self._target_executors[type_spec.placement]
            if type_spec.all_equal:
                value = [value for _ in children]
            results = await asyncio.gather(*[
                c.create_value(v, type_spec.member)
                for v, c in zip(value, children)
            ])
            return FederatingExecutorValue(results, type_spec)
        else:
            child = self._target_executors[None][0]
            return FederatingExecutorValue(
                await child.create_value(value, type_spec), type_spec)
    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_analysis.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):
            deserialized_type = type_serialization.deserialize_type(value.type)
            if type_spec is None:
                type_spec = deserialized_type
            else:
                type_analysis.check_assignable_from(type_spec,
                                                    deserialized_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)