def safe_unsigned_div(a, b, eps=None, name='safe_unsigned_div'): """Calculates a/b with b >= 0 safely. If the tfg debug flag TFG_ADD_ASSERTS_TO_GRAPH defined in tfg_flags.py is set to True, this function adds assertions to the graph that check whether b + eps is greather than zero, and the division has no NaN or Inf values. Args: a: A `float` or a tensor of shape `[A1, ..., An]`, which is the nominator. b: A `float` or a tensor of shape `[A1, ..., An]`, which is the denominator. eps: A small `float`, to be added to the denominator. If left as `None`, its value is automatically selected using `b.dtype`. name: A name for this op. Defaults to 'safe_unsigned_div'. Raises: InvalidArgumentError: If tf-graphics debug flag is set and the division causes `NaN` or `Inf` values. Returns: A tensor of shape `[A1, ..., An]` containing the results of division. """ with tf.name_scope(name): a = tf.convert_to_tensor(value=a) b = tf.convert_to_tensor(value=b) if eps is None: eps = asserts.select_eps_for_division(b.dtype) eps = tf.convert_to_tensor(value=eps, dtype=b.dtype) return asserts.assert_no_infs_or_nans(a / (b + eps))
def safe_signed_div(a, b, eps=None, name='safe_signed_div'): """Calculates a/b safely. If the tf-graphics debug flag is set to `True`, this function adds assertions to the graph that check whether `abs(b + eps)` is greather than zero, and the division has no `NaN` or `Inf` values. Note: In the following, A1 to An are optional batch dimensions, which must be broadcast compatible. Args: a: A `float` or a tensor of shape `[A1, ..., An]`, which is the nominator. b: A `float` or a tensor of shape `[A1, ..., An]`, which is the denominator with non-negative values. eps: A small `float`, to be added to the denominator. If left `None`, its value is automatically selected using `b.dtype`. name: A name for this op. Defaults to 'safe_signed_div'. Raises: InvalidArgumentError: If tf-graphics debug flag is set and the division causes `NaN` or `Inf` values. Returns: A tensor of shape `[A1, ..., An]` containing the results of division. """ with tf.name_scope(name): a = tf.convert_to_tensor(value=a) b = tf.convert_to_tensor(value=b) if eps is None: eps = asserts.select_eps_for_division(b.dtype) eps = tf.convert_to_tensor(value=eps, dtype=b.dtype) return asserts.assert_no_infs_or_nans(a / (b + nonzero_sign(b) * eps))
def test_select_eps_for_division(self, dtype): """Checks that select_eps_for_division does not cause Inf values.""" a = tf.constant(1.0, dtype=dtype) eps = asserts.select_eps_for_division(dtype) self.assert_exception_is_not_raised( asserts.assert_no_infs_or_nans, shapes=[], tensor=a / eps)
def generate_random_face_indices( num_samples: int, face_weights: type_alias.TensorLike, seed: Optional[type_alias.TensorLike] = None, stateless: bool = False, name: str = "generate_random_face_indices") -> type_alias.TensorLike: """Generate a sample of face ids given per face probability. Note: In the following, A1 to An are optional batch dimensions. Args: num_samples: An `int32` scalar denoting the number of samples to generate per mesh. face_weights: A `float` tensor of shape `[A1, ..., An, F]` where F is number of faces. All weights must be > 0. seed: Optional seed for the random number generator. stateless: Optional flag to use stateless random sampler. If stateless=True, then `seed` must be provided as shape `[2]` int tensor. Stateless random sampling is useful for testing to generate the same reproducible sequence across calls. If stateless=False, then a stateful random number generator is used (default behavior). name: Name for op. Defaults to "generate_random_face_indices". Returns: An `int32` tensor of shape `[A1, ..., An, num_samples]` denoting sampled face indices. """ with tf.name_scope(name): num_samples = tf.convert_to_tensor(value=num_samples) face_weights = tf.convert_to_tensor(value=face_weights) shape.check_static(tensor=face_weights, tensor_name="face_weights", has_rank_greater_than=0) shape.check_static(tensor=num_samples, tensor_name="num_samples", has_rank=0) face_weights = asserts.assert_all_above(face_weights, minval=0.0) eps = asserts.select_eps_for_division(face_weights.dtype) face_weights = face_weights + eps sampled_face_indices = _random_categorical_sample( num_samples=num_samples, weights=face_weights, seed=seed, stateless=stateless) return sampled_face_indices
def safe_cospx_div_cosx(theta: type_alias.TensorLike, factor: type_alias.TensorLike, eps: Optional[type_alias.Float] = None, name: str = 'safe_cospx_div_cosx') -> tf.Tensor: """Calculates cos(factor * theta)/cos(theta) safely. The term `cos(factor * theta)/cos(theta)` has periodic edge cases with division by zero problems, and also zero / zero, e.g. when factor is equal to 1.0 and `theta` is `(n + 1/2)pi`. This function adds signed eps to the angles in both the nominator and the denominator to ensure safety, and returns the correct values in all edge cases. Note: In the following, A1 to An are optional batch dimensions, which must be broadcast compatible. Args: theta: A tensor of shape `[A1, ..., An]`, representing angles in radians. factor: A `float` or a tensor of shape `[A1, ..., An]`. eps: A `float`, used to perturb the angle. If left as `None`, its value is automatically determined from the `dtype` of `theta`. name: A name for this op. Defaults to 'safe_cospx_div_cosx'. Raises: InvalidArgumentError: If tf-graphics debug flag is set and division returns `NaN` or `Inf` values. Returns: A tensor of shape `[A1, ..., An]` containing the resulting values. """ with tf.name_scope(name): theta = tf.convert_to_tensor(value=theta) factor = tf.convert_to_tensor(value=factor, dtype=theta.dtype) if eps is None: eps = asserts.select_eps_for_division(theta.dtype) eps = tf.convert_to_tensor(value=eps, dtype=theta.dtype) # eps will be multiplied with factor next, which can make it zero. # Therefore we multiply eps with min(1/factor, 1e10), which can handle # factors as small as 1e-10 correctly, while preventing a division by zero. eps *= tf.clip_by_value(1.0 / factor, 1.0, 1e10) sign = nonzero_sign(0.5 * np.pi - (theta - 0.5 * np.pi) % np.pi) theta += sign * eps div = tf.cos(factor * theta) / tf.cos(theta) return asserts.assert_no_infs_or_nans(div)
def safe_sinpx_div_sinx(theta, factor, eps=None, name='safe_sinpx_div_sinx'): """Calculates sin(factor * theta)/sin(theta) safely. The term `sin(factor * theta)/sin(theta)` appears when calculating spherical interpolation weights, and it has periodic edge cases causing both zero / zero and division by zero problems. This function adds signed eps to the angles in both the nominator and the denominator to ensure safety, and returns the correct values estimated by l'Hopital rule in the case of zero / zero. Note: In the following, A1 to An are optional batch dimensions, which must be broadcast compatible. Args: theta: A tensor of shape `[A1, ..., An]` representing angles in radians. factor: A `float` or a tensor of shape `[A1, ..., An]`. eps: A `float`, used to perturb the angle. If left as `None`, its value is automatically determined from the `dtype` of `theta`. name: A name for this op. Defaults to 'safe_sinpx_div_sinx'. Raises: InvalidArgumentError: If tf-graphics debug flag is set and the division returns `NaN` or `Inf` values. Returns: A tensor of shape `[A1, ..., An]` containing the resulting values. """ with tf.name_scope(name): theta = tf.convert_to_tensor(value=theta) factor = tf.convert_to_tensor(value=factor, dtype=theta.dtype) if eps is None: eps = asserts.select_eps_for_division(theta.dtype) eps = tf.convert_to_tensor(value=eps, dtype=theta.dtype) # eps will be multiplied with factor next, which can make it zero. # Therefore we multiply eps with min(1/factor, 1e10), which can handle # factors as small as 1e-10 correctly, while preventing a division by zero. eps *= tf.clip_by_value(1.0 / factor, 1.0, 1e10) sign = nonzero_sign(0.5 * np.pi - theta % np.pi) theta += sign * eps div = tf.sin(factor * theta) / tf.sin(theta) return asserts.assert_no_infs_or_nans(div)