                   Int32(strides.4)],
         padding: padding.raw)
 }
+
+@usableFromInline
+func _vjpAvgPool3D<Scalar: TensorFlowFloatingPoint>(
+    _ x: Tensor<Scalar>,
+    filterSize: (Int, Int, Int, Int, Int),
+    strides: (Int, Int, Int, Int, Int),
+    padding: Padding
+) -> (Tensor<Scalar>, (Tensor<Scalar>) -> Tensor<Scalar>) {
+    // TODO: Currently this is not higher order differentiable. Redefine in
+    // closed form.
+    let value = avgPool3D(x, filterSize: filterSize, strides: strides, padding: padding)
+    return (value, { v in
+        return Raw.avgPool3DGrad(
+            origInputShape: x.shapeTensor,
+            grad: v,
+            ksize: [Int32(filterSize.0), Int32(filterSize.1), Int32(filterSize.2),
+                    Int32(filterSize.3), Int32(filterSize.4)],
+            strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2), Int32(strides.3),
+                      Int32(strides.4)],
+            padding: padding.raw
+        )
+    })
+}

    }
}


/// TensorFlow builtin conv2d gradient helper for the input.
@differentiable(wrt: (x, filter), vjp: _vjpConv2DBackpropInput)
@usableFromInline
func conv2DBackpropInput<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    shape: Tensor<Int32>,
    filter: Tensor<Scalar>,
    strides: (Int, Int, Int, Int),
    padding: Padding,
    dilations: (Int, Int, Int, Int) = (1, 1, 1, 1)
) -> Tensor<Scalar> {
    return Raw.conv2DBackpropInput(
        inputSizes: shape,
        filter: filter,
        outBackprop: x,
        strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2), Int32(strides.3)],
        padding: padding.raw2,
        explicitPaddings: [],
        dilations: [Int32(dilations.0), Int32(dilations.1), Int32(dilations.2), Int32(dilations.3)])
}

/// TensorFlow builtin conv2d gradient helper for the filter.
@differentiable(wrt: (x, input), vjp: _vjpConv2DBackpropFilter)
@usableFromInline
func conv2DBackpropFilter<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    input: Tensor<Scalar>,
    filterSizes: Tensor<Int32>,
    strides: (Int, Int, Int, Int),
    padding: Padding,
    dilations: (Int, Int, Int, Int)
) -> Tensor<Scalar> {
    return Raw.conv2DBackpropFilter(
        input,
        filterSizes: filterSizes,
        outBackprop: x,
        strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2), Int32(strides.3)],
        padding: padding.raw2,
        explicitPaddings: [],
        dilations: [Int32(dilations.0), Int32(dilations.1), Int32(dilations.2), Int32(dilations.3)])
}

@usableFromInline
func _vjpConv2DBackpropInput<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    _ shape: Tensor<Int32>,
    _ filter: Tensor<Scalar>,
    _ strides: (Int, Int, Int, Int),
    _ padding: Padding,
    _ dilations: (Int, Int, Int, Int)
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = conv2DBackpropInput(x, shape: shape, filter: filter,
                                    strides: strides, padding: padding, dilations: dilations)
    return (value, { v in
        (conv2DBackpropFilter(x, input: v, filterSizes: shape, strides: strides,
                              padding: padding, dilations: dilations),
         conv2D(v, filter: filter, strides: strides, padding: padding, dilations: dilations))
    })
}

@usableFromInline
func _vjpConv2DBackpropFilter<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    _ input: Tensor<Scalar>,
    _ filterSizes: Tensor<Int32>,
    _ strides: (Int, Int, Int, Int),
    _ padding: Padding,
    _ dilations: (Int, Int, Int, Int)
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = conv2DBackpropFilter(x, input: input, filterSizes: filterSizes,
                                     strides: strides, padding: padding, dilations: dilations)
    return (value, { v in
        (conv2DBackpropInput(x, shape: filterSizes, filter: v, strides: strides,
                             padding: padding, dilations: dilations),
         conv2D(input, filter: v, strides: strides, padding: padding, dilations: dilations))
    })
}

@usableFromInline
func _vjpConv2D<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    filter: Tensor<Scalar>,
    strides: (Int, Int, Int, Int),
    padding: Padding,
    dilations: (Int, Int, Int, Int)
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = conv2D(x, filter: filter, strides: strides, padding: padding, dilations: dilations)
    return (value, { v in
        (conv2DBackpropInput(v, shape: x.shapeTensor, filter: filter,
                             strides: strides, padding: padding, dilations: dilations),
         conv2DBackpropFilter(v, input: x, filterSizes: filter.shapeTensor,
                              strides: strides, padding: padding, dilations: dilations))
    })
}

/// TensorFlow builtin conv3d gradient helper for the input.
@differentiable(wrt: (x, filter), vjp: _vjpConv3DBackpropInput)
@usableFromInline
func conv3DBackpropInput<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    shape: Tensor<Int32>,
    filter: Tensor<Scalar>,
    strides: (Int, Int, Int, Int, Int),
    padding: Padding
) -> Tensor<Scalar> {
    return Raw.conv3DBackpropInputV2(
        inputSizes: shape,
        filter: filter,
        outBackprop: x,
        strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2),
                  Int32(strides.3), Int32(strides.4)],
        padding: padding.raw)
}

/// TensorFlow builtin conv3d gradient helper for the filter.
@differentiable(wrt: (x, input), vjp: _vjpConv3DBackpropFilter)
@usableFromInline
func conv3DBackpropFilter<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    input: Tensor<Scalar>,
    filterSizes: Tensor<Int32>,
    strides: (Int, Int, Int, Int, Int),
    padding: Padding
) -> Tensor<Scalar> {
    return Raw.conv3DBackpropFilterV2(
        x,
        filterSizes: filterSizes,
        outBackprop: x,
        strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2),
                  Int32(strides.3), Int32(strides.4)],
        padding: padding.raw)
}

@usableFromInline
func _vjpConv3DBackpropInput<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    _ shape: Tensor<Int32>,
    _ filter: Tensor<Scalar>,
    _ strides: (Int, Int, Int, Int, Int),
    _ padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = conv3DBackpropInput(x, shape: shape, filter: filter, strides: strides,
                                    padding: padding)
    return (value, { v in
        return (
            conv3DBackpropFilter(x, input: v, filterSizes: shape, strides: strides,
                                 padding: padding),
            conv3D(v, filter: filter, strides: strides, padding: padding)
        )
    })
}

@usableFromInline
func _vjpConv3DBackpropFilter<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    _ input: Tensor<Scalar>,
    _ filterSizes: Tensor<Int32>,
    _ strides: (Int, Int, Int, Int, Int),
    _ padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = conv3DBackpropFilter(x, input: input, filterSizes: filterSizes,
                                     strides: strides, padding: padding)
    return (value, { v in
        return (
            conv3DBackpropInput(x, shape: filterSizes, filter: v, strides: strides,
                                  padding: padding),
            conv3D(input, filter: v, strides: strides, padding: padding)
        )
    })
}

@usableFromInline
func _vjpConv3D<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    filter: Tensor<Scalar>,
    strides: (Int, Int, Int, Int, Int),
    padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = conv3D(x, filter: filter, strides: strides,
                       padding: padding)
    return (value, { v in
        return (
            conv3DBackpropInput(v, shape: x.shapeTensor, filter: filter,
                                strides: strides, padding: padding),
            conv3DBackpropFilter(v, input: x, filterSizes: filter.shapeTensor,
                                 strides: strides, padding: padding)
        )
    })
}

/// TensorFlow builtin depthwiseConv2D gradient helper for the input.
@differentiable(wrt: (x, filter), vjp: _vjpdepthwiseConv2dBackpropInput)
@usableFromInline
func depthwiseConv2dBackpropInput<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    shape: Tensor<Int32>,
    filter: Tensor<Scalar>,
    strides: (Int, Int, Int, Int),
    padding: Padding
) -> Tensor<Scalar> {
    return Raw.depthwiseConv2dNativeBackpropInput(
        inputSizes: shape,
        filter: filter,
        outBackprop: x,
        strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2), Int32(strides.3)],
        padding: padding.raw)
}

/// TensorFlow builtin depthwiseConv2D gradient helper for the filter.
@differentiable(wrt: (x, input), vjp: _vjpdepthwiseConv2dBackpropFilter)
@usableFromInline
func depthwiseConv2dBackpropFilter<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    input: Tensor<Scalar>,
    filterSizes: Tensor<Int32>,
    strides: (Int, Int, Int, Int),
    padding: Padding
) -> Tensor<Scalar> {
    return Raw.depthwiseConv2dNativeBackpropFilter(
        x,
        filterSizes: filterSizes,
        outBackprop: x,
        strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2), Int32(strides.3)],
        padding: padding.raw)
}

@usableFromInline
func _vjpdepthwiseConv2dBackpropInput<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    _ shape: Tensor<Int32>,
    _ filter: Tensor<Scalar>,
    _ strides: (Int, Int, Int, Int),
    _ padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = depthwiseConv2dBackpropInput(x, shape: shape, filter: filter, strides: strides,
                                             padding: padding)
    return (value, { v in
        return (
            depthwiseConv2dBackpropFilter(x, input: v, filterSizes: shape, strides: strides,
                                          padding: padding),
            depthwiseConv2D(v, filter: filter, strides: strides, padding: padding)
        )
    })
}

@usableFromInline
func _vjpdepthwiseConv2dBackpropFilter<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    _ input: Tensor<Scalar>,
    _ filterSizes: Tensor<Int32>,
    _ strides: (Int, Int, Int, Int),
    _ padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = depthwiseConv2dBackpropFilter(x, input: input, filterSizes: filterSizes,
                                              strides: strides, padding: padding)
    return (value, { v in
        return (
            depthwiseConv2dBackpropInput(x, shape: filterSizes, filter: v, strides: strides,
                                         padding: padding),
            depthwiseConv2D(input, filter: v, strides: strides, padding: padding)
        )
    })
}

@usableFromInline
func _vjpDepthwiseConv2D<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    filter: Tensor<Scalar>,
    strides: (Int, Int, Int, Int),
    padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> (Tensor<Scalar>, Tensor<Scalar>)) {
    let value = depthwiseConv2D(x, filter: filter, strides: strides,
                                padding: padding)
    return (value, { v in
        return (
            depthwiseConv2dBackpropInput(v, shape: x.shapeTensor, filter: filter,
                                         strides: strides, padding: padding
            ),
            depthwiseConv2dBackpropFilter(v, input: x, filterSizes: filter.shapeTensor,
                                          strides: strides, padding: padding
            )
        )
    })
}

@usableFromInline
func _vjpMaxPool2D<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    filterSize: (Int, Int, Int, Int),
    strides: (Int, Int, Int, Int),
    padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> Tensor<Scalar>) {
    // TODO: Currently this is not higher order differentiable. Redefine in
    // closed form.
    let value = maxPool2D(x, filterSize: filterSize, strides: strides, padding: padding)
    return (value, { v in
        Raw.maxPoolGradV2(
            origInput: x,
            origOutput: value,
            grad: v,
            ksize: Tensor<Int32>([Int32(filterSize.0), Int32(filterSize.1),
                                  Int32(filterSize.2), Int32(filterSize.3)]),
            strides: Tensor<Int32>([Int32(strides.0), Int32(strides.1),
                                    Int32(strides.2), Int32(strides.3)]),
            padding: padding.raw)
    })
}

@usableFromInline
func _vjpMaxPool3D<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    filterSize: (Int, Int, Int, Int, Int),
    strides: (Int, Int, Int, Int, Int),
    padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> Tensor<Scalar>) {
    // TODO: Currently this is not higher order differentiable. Redefine in
    // closed form.
    let value = maxPool3D(x, filterSize: filterSize, strides: strides, padding: padding)
    return (value, { v in
        return Raw.maxPool3DGrad(
            origInput: x,
            origOutput: value,
            grad: v,
            ksize: [Int32(filterSize.0), Int32(filterSize.1), Int32(filterSize.2),
                    Int32(filterSize.3), Int32(filterSize.4)],
            strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2), Int32(strides.3),
                      Int32(strides.4)],
            padding: padding.raw
        )
    })
}

@usableFromInline
func _vjpAvgPool2D<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    filterSize: (Int, Int, Int, Int),
    strides: (Int, Int, Int, Int),
    padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> Tensor<Scalar>) {
    // TODO: Currently this is not higher order differentiable. Redefine in
    // closed form.
    let value = avgPool2D(x, filterSize: filterSize, strides: strides, padding: padding)
    return (value, { v in
        Raw.avgPoolGrad(
            origInputShape: x.shapeTensor,
            grad: v,
            ksize: [Int32(filterSize.0), Int32(filterSize.1),
                    Int32(filterSize.2), Int32(filterSize.3)],
            strides: [Int32(strides.0), Int32(strides.1),
                      Int32(strides.2), Int32(strides.3)],
            padding: padding.raw
        )
    })
}

@usableFromInline
func _vjpAvgPool3D<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    filterSize: (Int, Int, Int, Int, Int),
    strides: (Int, Int, Int, Int, Int),
    padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> Tensor<Scalar>) {
    // TODO: Currently this is not higher order differentiable. Redefine in
    // closed form.
    let value = avgPool3D(x, filterSize: filterSize, strides: strides, padding: padding)
    return (value, { v in
        return Raw.avgPool3DGrad(
            origInputShape: x.shapeTensor,
            grad: v,
            ksize: [Int32(filterSize.0), Int32(filterSize.1), Int32(filterSize.2),
                    Int32(filterSize.3), Int32(filterSize.4)],
            strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2), Int32(strides.3),
                      Int32(strides.4)],
            padding: padding.raw
        )
    })
}

/// Returns a 2-D convolution with the specified input, filter, strides, and padding.
///
/// - Parameters:

        padding: padding.raw)
}

@usableFromInline
func _vjpMaxPool3D<Scalar: TensorFlowFloatingPoint>(
    _ x: Tensor<Scalar>,
    filterSize: (Int, Int, Int, Int, Int),
    strides: (Int, Int, Int, Int, Int),
    padding: Padding
) -> (Tensor<Scalar>, (Tensor<Scalar>) -> Tensor<Scalar>) {
    // TODO: Currently this is not higher order differentiable. Redefine in
    // closed form.
    let value = maxPool3D(x, filterSize: filterSize, strides: strides, padding: padding)
    return (value, { v in
        return Raw.maxPool3DGrad(
            origInput: x,
            origOutput: value,
            grad: v,
            ksize: [Int32(filterSize.0), Int32(filterSize.1), Int32(filterSize.2),
                    Int32(filterSize.3), Int32(filterSize.4)],
            strides: [Int32(strides.0), Int32(strides.1), Int32(strides.2), Int32(strides.3),
                      Int32(strides.4)],
            padding: padding.raw
        )
    })
}

/// Returns a 2-D average pooling, with the specified filter sizes, strides,
/// and padding.
///
