def test_infinite_bounds_are_valid_for_floats(self, minimum, maximum): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.DOUBLE tensor_spec.min.double = minimum tensor_spec.max.double = maximum tensor_spec.name = 'foo' tensor_spec_utils.bounds(tensor_spec)
def test_nonnumeric_type_raises_error(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.STRING tensor_spec.max.int32s.array[:] = [1] tensor_spec.name = 'foo' with self.assertRaisesRegex(ValueError, 'foo.*non-numeric.*string'): tensor_spec_utils.bounds(tensor_spec)
def test_invalid_min_shape(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.UINT32 tensor_spec.min.uint32s.array[:] = [1, 2] with self.assertRaisesRegex( ValueError, 'Scalar tensors must have exactly 1 element.*'): tensor_spec_utils.bounds(tensor_spec)
def test_max_mismatches_type_raises_error(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.UINT32 tensor_spec.max.int32s.array[:] = [1] tensor_spec.name = 'foo' with self.assertRaisesRegex(ValueError, 'foo.*uint32.*max.*int32'): tensor_spec_utils.bounds(tensor_spec)
def test_invalid_max_shape(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.UINT32 tensor_spec.max.uint32s.array[:] = [1, 2] tensor_spec.shape[:] = (2, 2) with self.assertRaisesRegex( ValueError, 'cannot reshape array of size .* into shape.*'): tensor_spec_utils.bounds(tensor_spec)
def test_max_less_than_min_raises_error(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.INT32 tensor_spec.max.int32s.array[:] = [-1] tensor_spec.min.int32s.array[:] = [1] tensor_spec.name = 'foo' with self.assertRaisesRegex(ValueError, 'foo.*min 1.*max -1'): tensor_spec_utils.bounds(tensor_spec)
def test_invalid_min_var_shape(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.INT32 tensor_spec.min.int32s.array[:] = [-1, -1] tensor_spec.max.int32s.array[:] = [1] tensor_spec.shape[:] = (-1, ) with self.assertRaisesRegex( ValueError, "TensorSpec's with variable length shapes " 'can only have scalar ranges.'): tensor_spec_utils.bounds(tensor_spec)
def test_min_and_max(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.INT32 tensor_spec.min.int32s.array[:] = [-1] tensor_spec.max.int32s.array[:] = [1] bounds = tensor_spec_utils.bounds(tensor_spec) self.assertEqual((-1, 1), bounds)
def test_min_and_max_legacy(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.INT32 tensor_spec.min.int32 = -1 tensor_spec.max.int32 = 1 bounds = tensor_spec_utils.bounds(tensor_spec) self.assertEqual((-1, 1), bounds)
def _create_test_value(spec): """Creates a scalar test value consistent with the TensorSpec `spec`.""" if _is_numeric_type(spec.dtype): return tensor_spec_utils.bounds(spec).min, else: np_type = tensor_utils.data_type_to_np_type(spec.dtype) return np_type()
def tensor_spec_to_dm_env_spec( tensor_spec: dm_env_rpc_pb2.TensorSpec) -> specs.Array: """Returns a dm_env spec given a dm_env_rpc TensorSpec. Args: tensor_spec: A dm_env_rpc TensorSpec protobuf. Returns: Either a DiscreteArray, BoundedArray, StringArray or Array, depending on the content of the TensorSpec. """ np_type = tensor_utils.data_type_to_np_type(tensor_spec.dtype) if tensor_spec.HasField('min') or tensor_spec.HasField('max'): bounds = tensor_spec_utils.bounds(tensor_spec) if (not tensor_spec.shape and np.issubdtype(np_type, np.integer) and bounds.min == 0 and tensor_spec.HasField('max')): return specs.DiscreteArray(num_values=bounds.max + 1, dtype=np_type, name=tensor_spec.name) else: return specs.BoundedArray(shape=tensor_spec.shape, dtype=np_type, name=tensor_spec.name, minimum=bounds.min, maximum=bounds.max) else: if tensor_spec.dtype == dm_env_rpc_pb2.DataType.STRING: return specs.StringArray(shape=tensor_spec.shape, name=tensor_spec.name) else: return specs.Array(shape=tensor_spec.shape, dtype=np_type, name=tensor_spec.name)
def test_max_broadcast(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.UINT32 tensor_spec.max.uint32s.array[:] = [1] tensor_spec.shape[:] = (2, 2) bounds = tensor_spec_utils.bounds(tensor_spec) np.testing.assert_array_equal(np.full(tensor_spec.shape, 0), bounds.min) np.testing.assert_array_equal(np.full(tensor_spec.shape, 1), bounds.max)
def test_broadcast_var_shape(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.INT32 tensor_spec.min.int32s.array[:] = [-1] tensor_spec.max.int32s.array[:] = [1] tensor_spec.shape[:] = (-1, ) bounds = tensor_spec_utils.bounds(tensor_spec) self.assertEqual((-1, 1), bounds)
def test_max_scalar_doesnt_broadcast(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.UINT32 tensor_spec.max.uint32s.array[:] = [1] tensor_spec.shape[:] = (2, 2) bounds = tensor_spec_utils.bounds(tensor_spec) self.assertEqual(0, bounds.min) self.assertEqual(1, bounds.max)
def test_error_if_min_or_max_cannot_be_safely_cast_to_dtype( self, minimum, maximum): name = 'foo' dtype = dm_env_rpc_pb2.DataType.INT8 tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dtype tensor_spec.min.int8 = minimum tensor_spec.max.int8 = maximum tensor_spec.name = name with self.assertRaisesWithLiteralMatch( ValueError, tensor_spec_utils._BOUNDS_CANNOT_BE_SAFELY_CAST_TO_DTYPE.format( name=name, minimum=minimum, maximum=maximum, dtype=dm_env_rpc_pb2.DataType.Name(dtype).lower(), )): tensor_spec_utils.bounds(tensor_spec)
def test_max_n_shape(self): maximum = np.array([[1, 2], [3, 4]]) tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.UINT32 tensor_spec.max.uint32s.array[:] = maximum.flatten().data.tolist() tensor_spec.shape[:] = maximum.shape bounds = tensor_spec_utils.bounds(tensor_spec) np.testing.assert_array_equal(np.full(maximum.shape, 0), bounds.min) np.testing.assert_array_equal(maximum, bounds.max)
def test_all_numerical_observations_in_range(self): numeric_uids = (uid for uid, spec in self.specs.observations.items() if _is_numeric_type(spec.dtype)) response = self.step(requested_observations=numeric_uids) for uid, observation in response.observations.items(): spec = self.specs.observations[uid] with self.subTest(uid=uid, name=spec.name): unpacked = tensor_utils.unpack_tensor(observation) bounds = tensor_spec_utils.bounds(spec) _assert_less_equal(unpacked, bounds.max) _assert_greater_equal(unpacked, bounds.min)
def _above_max(spec): """Returns a value above spec's max or None if none.""" if not spec.HasField('max'): return None np_type = tensor_utils.data_type_to_np_type(spec.dtype) max_type_value = _np_range_info(np_type).max if max_type_value > tensor_spec_utils.bounds(spec).max: return max_type_value else: return None
def _below_min(spec): """Returns a value below spec's min or None if none.""" if not spec.HasField('min'): return None np_type = tensor_utils.data_type_to_np_type(spec.dtype) min_type_value = _np_range_info(np_type).min if min_type_value < tensor_spec_utils.bounds(spec).min: return min_type_value else: return None
def test_min(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.UINT32 tensor_spec.min.uint32s.array[:] = [1] bounds = tensor_spec_utils.bounds(tensor_spec) self.assertEqual((1, 2**32 - 1), bounds)
def test_unbounded_signed(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.INT32 bounds = tensor_spec_utils.bounds(tensor_spec) self.assertEqual((-2**31, 2**31 - 1), bounds)
def test_min_0_stays_0(self): tensor_spec = dm_env_rpc_pb2.TensorSpec() tensor_spec.dtype = dm_env_rpc_pb2.DataType.INT8 tensor_spec.min.int8s.array = b'\x00' tensor_spec.name = 'foo' self.assertEqual((0, 127), tensor_spec_utils.bounds(tensor_spec))