matMulTransposeGradient :: (Bool, Bool) -> Test
matMulTransposeGradient txw = testCase ("matMulTransposeGradients " ++ show txw) $ do
  let (transposeX, transposeW) = txw

  let dfBuild = do
        let xShape = TF.Shape [3, 1 :: Int64]
        let xZeros = TF.zeros xShape
        x <- TF.render $ if transposeX then TF.matTranspose xZeros else xZeros
        variable <- TF.zeroInitializedVariable $ TF.Shape [1, 2 :: Int64]
        let wv = if transposeW then TF.matTranspose (TF.readValue variable) else TF.readValue variable
        let f = TF.matMul' (transAttrs transposeX transposeW) x wv :: TF.Tensor TF.Build Float
        w <- TF.render wv
        ds <- TF.gradients f [x, w]
        return (x, w, ds)

  TF.runSession $ do
    (x, w, d) <- TF.build dfBuild
    (dx, dw) <-
      case d of
        [dx, dw] -> pure (dx, dw)
        _ -> liftIO $ assertFailure "pattern-match failure in matMulTransposeGradient"
    xShape <- TF.run $ TF.shape x
    dxShape <- TF.run $ TF.shape dx
    liftIO $ assertEqual "xShape must match dxShape" xShape (dxShape :: V.Vector Int32)

    wShape <- TF.run $ TF.shape w
    dwShape <- TF.run $ TF.shape dw
    liftIO $ assertEqual "wShape must match dwShape" wShape (dwShape :: V.Vector Int32)
matMulTransposeGradient :: (Bool, Bool) -> Test
matMulTransposeGradient txw = testCase ("matMulTransposeGradients " ++ show txw) $ do
  let (transposeX, transposeW) = txw

  let dfBuild = do
        let xShape = TF.Shape [3, 1 :: Int64]
        let xZeros = TF.zeros xShape
        x <- TF.render $ if transposeX then TF.matTranspose xZeros else xZeros
        variable <- TF.zeroInitializedVariable $ TF.Shape [1, 2 :: Int64]
        let wv = if transposeW then TF.matTranspose (TF.readValue variable) else TF.readValue variable
        let f = TF.matMul' (transAttrs transposeX transposeW) x wv :: TF.Tensor TF.Build Float
        w <- TF.render wv
        ds <- TF.gradients f [x, w]
        return (x, w, ds)

  TF.runSession $ do
    (x, w, d) <- TF.build dfBuild
    (dx, dw) <-
      case d of
        [dx, dw] -> pure (dx, dw)
        _ -> liftIO $ assertFailure "pattern-match failure in matMulTransposeGradient"
    xShape <- TF.run $ TF.shape x
    dxShape <- TF.run $ TF.shape dx
    liftIO $ assertEqual "xShape must match dxShape" xShape (dxShape :: V.Vector Int32)

    wShape <- TF.run $ TF.shape w
    dwShape <- TF.run $ TF.shape dw
    liftIO $ assertEqual "wShape must match dwShape" wShape (dwShape :: V.Vector Int32)
matMulTransposeGradient txw = testCase ("matMulTransposeGradients " ++ show txw) $ do
  let (transposeX, transposeW) = txw

  let dfBuild = do
        let xShape = TF.Shape [3, 1 :: Int64]
        let xZeros = TF.zeros xShape
        x <- TF.render $ if transposeX then TF.matTranspose xZeros else xZeros
        variable <- TF.zeroInitializedVariable $ TF.Shape [1, 2 :: Int64]
        let wv = if transposeW then TF.matTranspose (TF.readValue variable) else TF.readValue variable
        let f = TF.matMul' (transAttrs transposeX transposeW) x wv :: TF.Tensor TF.Build Float
        w <- TF.render wv
        ds <- TF.gradients f [x, w]
        return (x, w, ds)

  TF.runSession $ do
    (x, w, d) <- TF.build dfBuild
    (dx, dw) <-
      case d of
        [dx, dw] -> pure (dx, dw)
        _ -> liftIO $ assertFailure "pattern-match failure in matMulTransposeGradient"
    xShape <- TF.run $ TF.shape x
    dxShape <- TF.run $ TF.shape dx
    liftIO $ assertEqual "xShape must match dxShape" xShape (dxShape :: V.Vector Int32)

    wShape <- TF.run $ TF.shape w
    dwShape <- TF.run $ TF.shape dw
    liftIO $ assertEqual "wShape must match dwShape" wShape (dwShape :: V.Vector Int32)