예제 #1
0
    def test_diffs(self):
        x = tf.constant([1, 2, 3, 4, 5])
        dx = self.evaluate(diff_ops.diff(x, order=1, exclusive=False))
        np.testing.assert_array_equal(dx, [1, 1, 1, 1, 1])

        dx1 = self.evaluate(diff_ops.diff(x, order=1, exclusive=True))
        np.testing.assert_array_equal(dx1, [1, 1, 1, 1])

        dx2 = self.evaluate(diff_ops.diff(x, order=2, exclusive=False))
        np.testing.assert_array_equal(dx2, [1, 2, 2, 2, 2])
예제 #2
0
    def test_diffs_differentiable(self):
        """Tests that the diffs op is differentiable."""
        x = tf.constant(2.0)
        xv = tf.stack([x, x * x, x * x * x], axis=0)

        # Produces [x, x^2 - x, x^3 - x^2]
        dxv = self.evaluate(diff_ops.diff(xv))
        np.testing.assert_array_equal(dxv, [2., 2., 4.])

        grad = self.evaluate(tf.gradients(diff_ops.diff(xv), x)[0])
        # Note that TF gradients adds up the components of the jacobian.
        # The sum of [1, 2x-1, 3x^2-2x] at x = 2 is 12.
        self.assertEqual(grad, 12.0)
예제 #3
0
def _interpolate_adjacent(times, values, name=None):
    """Interpolates linearly between adjacent values.

  Suppose `times` are `[t_1, t_2, ..., t_n]` an array of length `n` and
  values are `[f_1, ... f_n]` of length `n`. This function associates
  each of the values to the midpoint of the interval i.e. `f_i` is associated
  to the midpoint of the interval `[t_i, t_{i+1}]`. Then it calculates the
  values at the interval boundaries by linearly interpolating between adjacent
  intervals. The first interval is considered to be `[0, t_1]`. The values at
  the endpoints (i.e. result[0] and result[n]) are computed as follows:
  `result[0] = values[0] - 0.5 * (result[1] - values[0])` and
  `result[n] = values[n-1] - 0.5 * (result[n-1] - values[n-1])`.
  The rationale for these specific values is discussed in Ref. [1].

  Args:
    times: A rank 1 `Tensor` of real dtype. The times at which the interpolated
      values are to be computed. The values in the array should be positive and
      monotonically increasing.
    values: A rank 1 `Tensor` of the same dtype and shape as `times`. The values
      assigned to the midpoints of the time intervals.
    name: Python `str` name prefixed to Ops created by this class.
      Default value: None which is mapped to the default name
        'interpolate_adjacent'.

  Returns:
    interval_values: The values interpolated from the supplied midpoint values
      as described above. A `Tensor` of the same dtype as `values` but shape
      `[n+1]` where `[n]` is the shape of `values`. The `i`th component of the
      is the value associated to the time point `t_{i+1}` with `t_0 = 0`.
  """
    with tf.compat.v1.name_scope(name,
                                 default_name='interpolate_adjacent',
                                 values=[times, values]):
        dt1 = diff_ops.diff(times, order=1, exclusive=False)
        dt2 = diff_ops.diff(times, order=2, exclusive=False)[1:]
        weight_right = dt1[:-1] / dt2
        weight_left = dt1[1:] / dt2
        interior_values = weight_right * values[1:] + weight_left * values[:-1]
        value_0 = values[0] - 0.5 * (interior_values[0] - values[0])
        value_n = values[-1] - 0.5 * (interior_values[-1] - values[-1])
        return tf.concat([[value_0], interior_values, [value_n]], axis=0)
예제 #4
0
def segment_diff(x,
                 segment_ids,
                 order=1,
                 exclusive=False,
                 dtype=None,
                 name=None):
  """Computes difference of successive elements in a segment.

  For a complete description of segment_* ops see documentation of
  `tf.segment_max`. This op extends the `diff` functionality to segmented
  inputs.

  The behaviour of this op is the same as that of the op `diff` within each
  segment. The result is effectively a concatenation of the results of `diff`
  applied to each segment.

  ## Example

  ```python
    x = tf.constant([2, 5, 1, 7, 9] + [32, 10, 12, 3] + [4, 8, 5])
    segments = tf.constant([0, 0, 0, 0, 0] + [1, 1, 1, 1] + [2, 2, 2])
    # First order diff. Expected result: [3, -4, 6, 2, -22, 2, -9, 4, -3]
    dx1 = segment_diff(
        x, segment_ids=segments, order=1, exclusive=True)
    # Non-exclusive, second order diff.
    # Expected result: [2, 5, -1, 2, 8, 32, 10, -20, -7, 4, 8, 1]
    dx2 = segment_diff(
        x, segment_ids=segments, order=2, exclusive=False)
  ```

  Args:
    x: A rank 1 `Tensor` of any dtype for which arithmetic operations are
      permitted.
    segment_ids: A `Tensor`. Must be one of the following types: int32, int64. A
      1-D tensor whose size is equal to the size of `x`. Values should be sorted
      and can be repeated.
    order: Positive Python int. The order of the difference to compute. `order =
      1` corresponds to the difference between successive elements.
      Default value: 1
    exclusive: Python bool. See description above.
      Default value: False
    dtype: Optional `tf.Dtype`. If supplied, the dtype for `x` to use when
      converting to `Tensor`.
      Default value: None which maps to the default dtype inferred by TF.
    name: Python `str` name prefixed to Ops created by this class.
      Default value: None which is mapped to the default name 'segment_diff'.

  Returns:
    diffs: A `Tensor` of the same dtype as `x`. Assuming that each segment is
      of length greater than or equal to order, if `exclusive` is True,
      then the size is `n-order*k` where `n` is the size of x,
      `k` is the number of different segment ids supplied if `segment_ids` is
      not None or 1 if `segment_ids` is None. If any of the segments is of
      length less than the order, then the size is:
      `n-sum(min(order, length(segment_j)), j)` where the sum is over segments.
      If `exclusive` is False, then the size is `n`.
  """
  with tf.compat.v1.name_scope(name, default_name='segment_diff', values=[x]):
    x = tf.convert_to_tensor(x, dtype=dtype)
    raw_diffs = diff_ops.diff(x, order=order, exclusive=exclusive)
    if segment_ids is None:
      return raw_diffs
    # If segment ids are supplied, raw_diffs are incorrect at locations:
    # p, p+1, ... min(p+order-1, m_p-1) where p is the index of the first
    # element of a segment other than the very first segment (which is
    # already correct). m_p is the segment length.
    # Find positions where the segments begin.
    has_segment_changed = tf.concat(
        [[False], tf.not_equal(segment_ids[1:] - segment_ids[:-1], 0)], axis=0)
    # Shape [k, 1]
    segment_start_index = tf.cast(tf.where(has_segment_changed), dtype=tf.int32)
    segment_end_index = tf.concat(
        [tf.reshape(segment_start_index, [-1])[1:], [tf.size(segment_ids)]],
        axis=0)
    segment_end_index = tf.reshape(segment_end_index, [-1, 1])
    # The indices of locations that need to be adjusted. This needs to be
    # constructed in steps. First we generate p, p+1, ... p+order-1.
    # Shape [num_segments-1, order]
    fix_indices = (
        segment_start_index + tf.range(order, dtype=segment_start_index.dtype))
    in_bounds = tf.where(fix_indices < segment_end_index)
    # Keep only the ones in bounds.
    fix_indices = tf.reshape(tf.gather_nd(fix_indices, in_bounds), [-1, 1])

    needs_fix = tf.scatter_nd(
        fix_indices,
        tf.reshape(tf.ones_like(fix_indices, dtype=tf.bool), [-1]),
        shape=tf.shape(x))
    # If exclusive is False, then needs_fix means we need to replace the values
    # in raw_diffs at those locations with the values in x.
    if not exclusive:
      return tf.where(needs_fix, x, raw_diffs)

    # If exclusive is True, we have to be more careful. The raw_diffs
    # computation has removed the first 'order' elements. After removing the
    # corresponding elements from needs_fix, we use it to remove the elements
    # from raw_diffs.
    return tf.boolean_mask(raw_diffs, tf.logical_not(needs_fix[order:]))