def test_raises_with_conflicting_names(self): t1 = computation_types.NamedTupleType( [computation_types.AbstractType('T1')] * 2) t2 = computation_types.NamedTupleType([('a', tf.int32), ('b', tf.int32)]) with self.assertRaises(TypeError): type_analysis.is_concrete_instance_of(t2, t1)
def test_succeeds_under_tuple(self): t1 = computation_types.NamedTupleType( [computation_types.AbstractType('T1')] * 2) t2 = computation_types.NamedTupleType([ computation_types.TensorType(tf.int32), computation_types.TensorType(tf.int32) ]) self.assertTrue(type_analysis.is_concrete_instance_of(t2, t1))
def test_abstract_federated_types_succeeds(self): t1 = computation_types.FederatedType( [computation_types.AbstractType('T1')] * 2, placement_literals.CLIENTS, all_equal=True) t2 = computation_types.FederatedType( [tf.int32] * 2, placement_literals.CLIENTS, all_equal=True) self.assertTrue(type_analysis.is_concrete_instance_of(t2, t1))
def test_abstract_fails_on_different_federated_placements(self): t1 = computation_types.FederatedType( [computation_types.AbstractType('T1')] * 2, placement_literals.CLIENTS, all_equal=True) t2 = computation_types.FederatedType( [tf.int32] * 2, placement_literals.SERVER, all_equal=True) self.assertFalse(type_analysis.is_concrete_instance_of(t2, t1))
def test_fails_under_tuple_conflicting_concrete_types(self): t1 = computation_types.NamedTupleType( [computation_types.AbstractType('T1')] * 2) t2 = computation_types.NamedTupleType([ computation_types.TensorType(tf.int32), computation_types.TensorType(tf.float32) ]) self.assertFalse(type_analysis.is_concrete_instance_of(t2, t1))
def test_raises_with_different_lengths(self): t1 = computation_types.NamedTupleType( [computation_types.AbstractType('T1')] * 2) t2 = computation_types.NamedTupleType([tf.int32]) with self.assertRaises(TypeError): type_analysis.is_concrete_instance_of(t2, t1)
def test_with_single_abstract_type_and_tuple_type(self): t1 = computation_types.AbstractType('T1') t2 = computation_types.NamedTupleType([tf.int32]) self.assertTrue(type_analysis.is_concrete_instance_of(t2, t1))
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)
def test_succeeds_function_different_parameter_and_return_types(self): t1 = computation_types.FunctionType( computation_types.AbstractType('T'), computation_types.AbstractType('U')) t2 = computation_types.FunctionType(tf.int32, tf.float32) self.assertTrue(type_analysis.is_concrete_instance_of(t2, t1))
def test_fails_conflicting_concrete_types_under_sequence(self): t1 = computation_types.SequenceType( [computation_types.AbstractType('T')] * 2) t2 = computation_types.SequenceType([tf.int32, tf.float32]) self.assertFalse(type_analysis.is_concrete_instance_of(t2, t1))
def test_raises_different_structures(self): with self.assertRaises(TypeError): type_analysis.is_concrete_instance_of( computation_types.to_type(tf.int32), computation_types.to_type([tf.int32]))
def test_raises_with_int_second_argument(self): with self.assertRaises(TypeError): type_analysis.is_concrete_instance_of( computation_types.to_type(tf.int32), 1)
def test_raises_with_int_first_argument(self): with self.assertRaises(TypeError): type_analysis.is_concrete_instance_of( 1, computation_types.TensorType(tf.int32))
async def create_value(self, value, type_spec=None): type_spec = computation_types.to_type(type_spec) if isinstance(value, intrinsic_defs.IntrinsicDef): if not type_analysis.is_concrete_instance_of( type_spec, value.type_signature): # pytype: disable=attribute-error raise TypeError( 'Incompatible type {} used with intrinsic {}.'.format( type_spec, value.uri)) return CompositeValue(value, type_spec) elif isinstance(value, pb.Computation): which_computation = value.WhichOneof('computation') if which_computation in ['lambda', 'tensorflow']: return CompositeValue(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 NotImplementedError( 'Unimplemented computation type {}.'.format( which_computation)) elif isinstance(type_spec, computation_types.FederatedType): if type_spec.placement == placement_literals.SERVER: if not type_spec.all_equal: raise ValueError( 'Expected an all equal value at the `SERVER` placement, ' 'found {}.'.format(type_spec)) results = await self._parent_executor.create_value( value, type_spec.member) return CompositeValue(results, type_spec) elif type_spec.placement == placement_literals.CLIENTS: if type_spec.all_equal: results = await asyncio.gather(*[ c.create_value(value, type_spec) for c in self._child_executors ]) return CompositeValue(results, type_spec) else: py_typecheck.check_type(value, list) cardinalities = await self._get_cardinalities() total_clients = sum(cardinalities) py_typecheck.check_len(value, total_clients) results = [] offset = 0 for child, num_clients in zip(self._child_executors, cardinalities): new_offset = offset + num_clients result = child.create_value(value[offset:new_offset], type_spec) results.append(result) offset = new_offset return CompositeValue(await asyncio.gather(*results), type_spec) else: raise ValueError('Unexpected placement {}.'.format( type_spec.placement)) else: return CompositeValue( await self._parent_executor.create_value(value, type_spec), type_spec)
async def create_value( self, value: Any, type_spec: Any = None) -> executor_value_base.ExecutorValue: """Creates an embedded value from the given `value` and `type_spec`. The kinds of supported `value`s are: * An instance of `intrinsic_defs.IntrinsicDef`. * An instance of `placement_literals.PlacementLiteral`. * An instance of `pb.Computation` if of one of the following kinds: intrinsic, lambda, and tensorflow. * A Python `list` if `type_spec` is a federated type. Note: The `value` must be a list even if it is of an `all_equal` type or if there is only a single participant associated with the given placement. * A Python value if `type_spec` is a non-functional, non-federated type. Args: value: An object to embed in the executor, one of the supported types defined by above. type_spec: An optional type convertible to instance of `tff.Type` via `tff.to_type`, the type of `value`. Returns: An instance of `executor_value_base.ExecutorValue` representing a value embedded in the `FederatingExecutor` using a particular `FederatingStrategy`. Raises: TypeError: If the `value` and `type_spec` do not match. ValueError: If `value` is not a kind supported by the `FederatingExecutor`. """ type_spec = computation_types.to_type(type_spec) 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 self._strategy.ingest_value(value, type_spec) elif isinstance(value, placement_literals.PlacementLiteral): if type_spec is None: type_spec = computation_types.PlacementType() type_spec.check_placement() return self._strategy.ingest_value(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_spec.check_assignable_from(deserialized_type) which_computation = value.WhichOneof('computation') if which_computation in ['lambda', 'tensorflow']: return self._strategy.ingest_value(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 type_spec is not None and type_spec.is_federated(): return await self._strategy.compute_federated_value(value, type_spec) else: result = await self._unplaced_executor.create_value(value, type_spec) return self._strategy.ingest_value(result, type_spec)
def test_raises_with_abstract_type_as_first_arg(self): t1 = computation_types.AbstractType('T1') t2 = computation_types.TensorType(tf.int32) with self.assertRaises(TypeError): type_analysis.is_concrete_instance_of(t1, t2)
def test_succeeds_abstract_type_under_sequence_type(self): t1 = computation_types.SequenceType( computation_types.AbstractType('T')) t2 = computation_types.SequenceType(tf.int32) self.assertTrue(type_analysis.is_concrete_instance_of(t2, t1))
def test_with_single_abstract_type_and_tensor_type(self): t1 = computation_types.AbstractType('T1') t2 = computation_types.TensorType(tf.int32) self.assertTrue(type_analysis.is_concrete_instance_of(t2, t1))
def test_succeeds_single_function_type(self): t1 = computation_types.FunctionType( *[computation_types.AbstractType('T')] * 2) t2 = computation_types.FunctionType(tf.int32, tf.int32) self.assertTrue(type_analysis.is_concrete_instance_of(t2, t1))
def test_raises_with_abstract_type_in_second_argument(self): t1 = computation_types.AbstractType('T1') t2 = computation_types.AbstractType('T2') with self.assertRaises(TypeError): type_analysis.is_concrete_instance_of(t2, t1)
def test_fails_conflicting_binding_in_parameter_and_result(self): t1 = computation_types.FunctionType( *[computation_types.AbstractType('T')] * 2) t2 = computation_types.FunctionType(tf.int32, tf.float32) self.assertFalse(type_analysis.is_concrete_instance_of(t2, t1))
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)