def testUpsampleAndDownsampleAreTransposes(self):
        """Tests that _downsample() is the transpose of _upsample()."""
        n = 8
        x = np.random.uniform(size=(1, n, 1))
        for f_len in range(1, 5):
            f = np.random.uniform(size=f_len)
            for shift in [0, 1]:

                # We're only testing the resampling operators away from the boundaries,
                # as this test appears to fail in the presences of boundary conditions.
                # TODO(barron): Figure out what's happening and make this test more
                # thorough, and then set range1 = range(d), range2 = range(d//2) and
                # have this code depend on util.compute_jacobian().
                range1 = np.arange(f_len // 2 + 1, n - (f_len // 2 + 1))
                range2 = np.arange(f_len // 4, n // 2 - (f_len // 4))

                y = wavelet._downsample(x, f, 0, shift)

                # Construct the jacobian of _downsample().
                x_ph = tf.placeholder(tf.float32, x.shape)
                jacobian_down = []
                vec = lambda x: tf.reshape(x, [-1])
                for d in range2:
                    jacobian_down.append(
                        vec(
                            tf.gradients(
                                vec(wavelet._downsample(x_ph, f, 0, shift))[d],
                                x_ph)[0]))
                jacobian_down = tf.stack(jacobian_down, 1)
                with tf.Session() as sess:
                    jacobian_down, y = sess.run([jacobian_down, y], {x_ph: x})

                # Construct the jacobian of _upsample().
                y_ph = tf.placeholder(tf.float32, y.shape)
                jacobian_up = []
                vec = lambda x: tf.reshape(x, [-1])
                for d in range1:
                    jacobian_up.append(
                        vec(
                            tf.gradients(
                                vec(
                                    wavelet._upsample(y_ph, x.shape[1:], f, 0,
                                                      shift))[d], y_ph)[0]))
                jacobian_up = tf.stack(jacobian_up, 1)
                with tf.Session() as sess:
                    jacobian_up = sess.run(jacobian_up, {y_ph: y})

                # Test that the jacobian of _downsample() is close to the transpose of
                # the jacobian of _upsample().
                self.assertAllClose(jacobian_down[range1, :],
                                    np.transpose(jacobian_up[range2, :]),
                                    atol=1e-6,
                                    rtol=1e-6)
  def testUpsampleAndDownsampleAreTransposes(self):
    """Tests that _downsample() is the transpose of _upsample()."""
    n = 8
    x = tf.convert_to_tensor(np.random.uniform(size=(1, n, 1)))

    for f_len in range(1, 5):
      f = np.random.uniform(size=f_len)
      for shift in [0, 1]:

        # We're only testing the resampling operators away from the boundaries,
        # as this test appears to fail in the presences of boundary conditions.
        # TODO(barron): Figure out what's happening and make this test more
        # thorough, and then set range1 = range(d), range2 = range(d//2) and
        # have this code depend on util.compute_jacobian().
        range1 = np.arange(f_len // 2 + 1, n - (f_len // 2 + 1))
        range2 = np.arange(f_len // 4, n // 2 - (f_len // 4))

        y = wavelet._downsample(x, f, 0, shift)
        vec = lambda z: tf.reshape(z, [-1])

        jacobian_down = []
        with tf.GradientTape(persistent=True) as tape:
          tape.watch(x)
          for d in range2:
            yd = vec(wavelet._downsample(x, f, 0, shift))[d]
            jacobian_down.append(vec(tape.gradient(yd, x)))
          jacobian_down = tf.stack(jacobian_down, 1).numpy()

        jacobian_up = []
        with tf.GradientTape(persistent=True) as tape:
          tape.watch(y)
          for d in range1:
            xd = vec(wavelet._upsample(y, x.shape[1:], f, 0, shift))[d]
            jacobian_up.append(vec(tape.gradient(xd, y)))
          jacobian_up = tf.stack(jacobian_up, 1).numpy()

        # Test that the jacobian of _downsample() is close to the transpose of
        # the jacobian of _upsample().
        self.assertAllClose(
            jacobian_down[range1, :],
            np.transpose(jacobian_up[range2, :]),
            atol=1e-6,
            rtol=1e-6)