packages feed

futhark 0.25.28 → 0.25.29

raw patch · 15 files changed

+251/−96 lines, 15 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Futhark.IR.Prop.Rearrange: isIdentityPerm :: [Int] -> Bool
- Futhark.Optimise.Fusion.RulesWithAccs: tryFuseWithAccs :: (HasScope SOACS m, MonadFreshNames m) => [VName] -> Stm SOACS -> Stm SOACS -> m (Maybe (Stm SOACS))
+ Futhark.Optimise.Fusion.RulesWithAccs: tryFuseWithAccs :: (HasScope SOACS m, MonadFreshNames m) => [VName] -> Stm SOACS -> Stm SOACS -> Maybe (m (Stm SOACS))
- Language.Futhark.Interpreter.AD: TapeID :: Int -> ADValue -> Tape
+ Language.Futhark.Interpreter.AD: TapeID :: Depth -> ADValue -> Tape
- Language.Futhark.Interpreter.AD: Variable :: Int -> ADVariable -> ADValue
+ Language.Futhark.Interpreter.AD: Variable :: Depth -> ADVariable -> ADValue
- Language.Futhark.Interpreter.AD: doOp :: Op -> [ADValue] -> Maybe ADValue
+ Language.Futhark.Interpreter.AD: doOp :: Op -> [ADValue] -> Either String ADValue

Files

CHANGELOG.md view
@@ -5,13 +5,32 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [0.25.28]+## [0.25.29] -### Added+### Fixed -### Removed+* AD would in some cases produce code that would cause a compiler+  crash (#2228). -### Changed+* Slight error in the definition of the partial derivatives for the+  `**` operator could cause NaNs in the interpreter when using+  forward-mode AD (#2229).++* The magical machinery for inferring external API types did not+  handle arrays with uniqueness annotations consistently, resulting in+  incompatible entry point types being generated, leading to a+  compiler crash. (#2231)++* A simplification rule for array slices would in some cases produce+  type-incorrect code. (#2232)++* A bug in the defunctionaliser could cause a compiler crash in code+  that used both higher order functions and size expressions in clever+  ways (#2234).++* Fusion could crash after AD in some circumstances (#2236).++## [0.25.28]  ### Fixed 
futhark.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:           futhark-version:        0.25.28+version:        0.25.29 synopsis:       An optimising compiler for a functional, array-oriented language.  description:    Futhark is a small programming language designed to be compiled to
src/Futhark/AD/Derivatives.hs view
@@ -136,7 +136,7 @@   where     derivs x y =       ( y * (x ** (y - 1)),-        condExp (x .<=. 0) 0 ((x ** y) * log x)+        (x ** y) * condExp (x .<=. 0) 0 (log x)       ) pdBinOp (FMax ft) a b =   floatBinOp derivs derivs derivs ft a b
src/Futhark/AD/Rev.hs view
@@ -28,6 +28,15 @@ patName (Pat [pe]) = pure $ patElemName pe patName pat = error $ "Expected single-element pattern: " ++ prettyString pat +copyIfArray :: VName -> ADM VName+copyIfArray v = do+  v_t <- lookupType v+  case v_t of+    Array {} ->+      letExp (baseString v <> "_copy") . BasicOp $+        Replicate mempty (Var v)+    _ -> pure v+ -- The vast majority of BasicOps require no special treatment in the -- forward pass and produce one value (and hence one adjoint).  We -- deal with that case here.@@ -202,13 +211,7 @@       (_pat_v, pat_adj) <- commonBasicOp pat aux e m       returnSweepCode $ do         v_adj <- letExp "update_val_adj" $ BasicOp $ Index pat_adj slice-        t <- lookupType v_adj-        v_adj_copy <--          case t of-            Array {} ->-              letExp "update_val_adj_copy" . BasicOp $-                Replicate mempty (Var v_adj)-            _ -> pure v_adj+        v_adj_copy <- copyIfArray v_adj         updateSubExpAdj v v_adj_copy         zeroes <- letSubExp "update_zero" . zeroExp =<< subExpType v         void $@@ -278,7 +281,9 @@           ses           (map (fmap $ diffBody adjs branches_free) cases)           (diffBody adjs branches_free defbody)-    zipWithM_ insAdj branches_free branches_free_adj+    -- See Note [Array Adjoints of Match]+    forM_ (zip branches_free branches_free_adj) $ \(v, v_adj) ->+      insAdj v =<< copyIfArray v_adj diffStm (Let pat aux (Op soac)) m =   vjpSOAC vjpOps pat aux soac m diffStm (Let pat aux loop@Loop {}) m =@@ -439,3 +444,43 @@ -- our current translation rules, they will be dead code.  As long as -- we are careful to run dead code elimination after revVJP, we should -- be good.++-- Note [Array Adjoints of Match]+--+-- Some unusual, but sadly not completely contrived, contain Match+-- expressions that return multiple arrays, and there the arrays+-- returned by one branch have overlapping aliases with another+-- branch, although in different places. As an example consider this:+--+--   let (X,Y) = if c+--               then (A, B)+--               else (B, A)+--+-- Because our aliasing representation cannot express mutually+-- exclusive aliases, we will consider X and Y to be aliased to each+-- other. In practice, this means it is unlikely for X or Y to be+-- consumed, because it would also consume the other (although it's+-- possible for carefully written code).+--+-- When producing adjoints for this, it will be something like+--+--   let (X_adj,Y_adj) = if c+--                       then (A_adj, B_adj)+--                       else (B_adj, A_adj)+--+-- which completely reflects the primal code. However, while it is+-- unlikely that any consumption takes place for the original primal+-- variables, it is almost guaranteed that X_adj and Y_adj will be+-- consumed (that is the main way we use adjoints after all), and due+-- to the conservative aliasing, when one is consumed, so is the+-- other! To avoid this tragic fate, we are forced to copy any+-- array-typed adjoints returned by a Match. This can be quite costly.+-- However:+--+-- 1) Futhark has pretty OK copy removal, so maybe it can get rid of+--    these by using information not available to the AD pass.+--+-- 2) In many cases, arrays will have accumulator adjoints, which are+--    not subject to this problem.+--+-- Issue #2228 was caused by neglecting to do this.
src/Futhark/IR/Prop/Rearrange.hs view
@@ -6,6 +6,7 @@     rearrangeReach,     rearrangeCompose,     isPermutationOf,+    isIdentityPerm,     transposeIndex,     isMapTranspose,   )@@ -61,6 +62,11 @@       | otherwise = do           (xs', v) <- pick (i + 1) xs y           pure (x : xs', v)++-- | Is this an identify permutation? An identity permutation is of+-- the form @[0, 1, ..., k]@.+isIdentityPerm :: [Int] -> Bool+isIdentityPerm perm = perm == [0 .. length perm - 1]  -- | If @l@ is an index into the array @a@, then @transposeIndex k n -- l@ is an index to the same element in the array @transposeArray k n
src/Futhark/Internalise/Defunctionalise.hs view
@@ -310,8 +310,11 @@     f _ = Nothing  sizesToRename :: StaticVal -> S.Set VName-sizesToRename (DynamicFun (_, sv1) sv2) =-  sizesToRename sv1 <> sizesToRename sv2+sizesToRename (DynamicFun (_, sv1) _sv2) =+  -- It is intentional that we do not look at sv2 here, as some names+  -- that are free in sv2 are actually bound by the parameters in sv1.+  -- See #2234.+  sizesToRename sv1 sizesToRename IntrinsicSV =   mempty sizesToRename HoleSV {} =@@ -882,8 +885,9 @@           globals <- asks fst           let bound_sizes = S.fromList dims' <> globals           pats' <- instAnySizes pats+          let dims'' = dims' ++ unboundSizes bound_sizes pats' -          liftValDec fname (RetType [] rettype') (dims' ++ unboundSizes bound_sizes pats') pats' e0+          liftValDec fname (RetType [] rettype') dims'' pats' e0           pure             ( Var                 (qualName fname)
src/Futhark/Internalise/Entry.hs view
@@ -185,6 +185,12 @@ entryPointTypeName (I.TypeOpaque v) = v entryPointTypeName (I.TypeTransparent {}) = error "entryPointTypeName: TypeTransparent" +elemTypeExp :: E.TypeExp E.Exp VName -> Maybe (E.TypeExp E.Exp VName)+elemTypeExp (E.TEArray _ te _) = Just te+elemTypeExp (E.TEUnique te _) = elemTypeExp te+elemTypeExp (E.TEParens te _) = elemTypeExp te+elemTypeExp _ = Nothing+ entryPointType ::   VisibleTypes ->   E.EntryType ->@@ -221,17 +227,13 @@                   rank = E.shapeRank shape                   ts' = map (strip rank) ts                   record_t = E.Scalar (E.Record fs)-                  record_te = case E.entryAscribed t of-                    Just (E.TEArray _ te _) -> Just te-                    _ -> Nothing+                  record_te = elemTypeExp =<< E.entryAscribed t               ept <- snd <$> entryPointType types (E.EntryType record_t record_te) ts'               addType desc . I.OpaqueRecordArray rank (entryPointTypeName ept)                 =<< opaqueRecordArray types rank fs' ts         E.Array _ shape et -> do           let ts' = map (strip (E.shapeRank shape)) ts-              elem_te = case E.entryAscribed t of-                Just (E.TEArray _ te _) -> Just te-                _ -> Nothing+              elem_te = elemTypeExp =<< E.entryAscribed t           ept <- snd <$> entryPointType types (E.EntryType (E.Scalar et) elem_te) ts'           addType desc . I.OpaqueArray (E.shapeRank shape) (entryPointTypeName ept) $             map valueType ts@@ -265,9 +267,15 @@                          E.entryAscribed eret                        ) of                 (Just ts, Just (E.TETuple e_ts _)) ->-                  zipWithM (entryPointType types) (zipWith E.EntryType ts (map Just e_ts)) crets+                  zipWithM+                    (entryPointType types)+                    (zipWith E.EntryType ts (map Just e_ts))+                    crets                 (Just ts, Nothing) ->-                  zipWithM (entryPointType types) (map (`E.EntryType` Nothing) ts) crets+                  zipWithM+                    (entryPointType types)+                    (map (`E.EntryType` Nothing) ts)+                    crets                 _ ->                   pure <$> entryPointType types eret (concat crets)           )
src/Futhark/Internalise/Monomorphise.hs view
@@ -841,12 +841,12 @@     -- the thing that should be fixed, but it requires fiddling with     -- the defunctorisation of size-lifted types. -    onExps bound (Var v _ _) e-      | Just rexp <- lookup (qualLeaf v) named1 =-          onExps mempty (unReplaced rexp) e-      | otherwise =-          unless (any (`elem` bound) $ freeVarsInExp e) $-            modify (M.insert (qualLeaf v) e)+    onExps bound (Var v _ _) e = do+      unless (any (`elem` bound) $ freeVarsInExp e) $+        modify (M.insert (qualLeaf v) e)+      case lookup (qualLeaf v) named1 of+        Just rexp -> onExps mempty (unReplaced rexp) e+        Nothing -> pure ()     onExps _bound e (Var v _ _)       | Just rexp <- lookup (qualLeaf v) named2 =           onExps mempty e (unReplaced rexp)@@ -997,7 +997,7 @@         substTypesAny (fmap (fmap (second (const mempty))) . (`M.lookup` substs'))       params' = map (substPat substStructType) params   params'' <- withArgs shape_names $ mapM transformPat params'-  exp_naming <- paramGetClean+  exp_naming <- get <* put mempty    let args = S.fromList $ foldMap patNames params       arg_params = map snd exp_naming@@ -1006,7 +1006,7 @@     withParams exp_naming $       withArgs (args <> shape_names) $         hardTransformRetType (applySubst (`M.lookup` substs') rettype)-  extNaming <- paramGetClean+  extNaming <- get <* put mempty   scope <- S.union shape_names <$> askScope'   let (rettype'', new_params) = arrowArg scope args arg_params rettype'       bind_t' = substTypesAny (`M.lookup` substs') bind_t@@ -1062,11 +1062,6 @@     shape_params = filter (not . isTypeParam) tparams      updateExpTypes substs = astMap (mapper substs)--    paramGetClean = do-      ret <- get-      put mempty-      pure ret      hardTransformRetType (RetType dims ty) = do       ty' <- transformType ty
src/Futhark/Optimise/Fusion.hs view
@@ -387,18 +387,19 @@     | wacc2_cons_nms <- namesFromList $ concatMap (\(_, nms, _) -> nms) w2_inps,       wacc1_indep_nms <- map getName is1,       all (`notNameIn` wacc2_cons_nms) wacc1_indep_nms = do-        -- \^ the other safety checks are done inside `tryFuseWithAccs`+        -- the other safety checks are done inside `tryFuseWithAccs`         lam1' <- fst <$> doFusionInLambda lam1         lam2' <- fst <$> doFusionInLambda lam2         let stm1 = Let pat1 aux1 (WithAcc w1_inps lam1')             stm2 = Let pat2 aux2 (WithAcc w2_inps lam2')-        mstm <- SF.tryFuseWithAccs infusible stm1 stm2+        mstm <- sequence $ SF.tryFuseWithAccs infusible stm1 stm2         case mstm of+          Nothing -> pure Nothing           Just (Let pat aux (WithAcc w_inps wlam)) -> do             (wlam', success) <- doFusionInLambda wlam             let new_stm = Let pat aux (WithAcc w_inps wlam')             if success then fusedSomething (StmNode new_stm) else pure Nothing-          _ -> error "Illegal result of tryFuseWithAccs called from vFuseNodeT."+          Just _ -> error "Illegal result of tryFuseWithAccs called from vFuseNodeT." -- vFuseNodeT _ _ _ _ = pure Nothing 
src/Futhark/Optimise/Fusion/RulesWithAccs.hs view
@@ -400,7 +400,7 @@   [VName] ->   Stm SOACS ->   Stm SOACS ->-  m (Maybe (Stm SOACS))+  Maybe (m (Stm SOACS)) tryFuseWithAccs   infusible   (Let pat1 aux1 (WithAcc w_inps1 lam1))@@ -424,7 +424,7 @@       all (`notElem` infusible) bs,       -- safety 3:       cs <- namesFromList $ concatMap ((\(_, xs, _) -> xs) . accTup2) acc_tup2,-      all ((`notNameIn` cs) . patElemName . fst) other_pr1 = do+      all ((`notNameIn` cs) . patElemName . fst) other_pr1 = Just $ do         let getCertPairs (t1, t2) = (paramName (accTup3 t2), paramName (accTup3 t1))             tab_certs = M.fromList $ map getCertPairs tup_common             lam2_bdy' = substituteNames tab_certs (lambdaBody lam2)@@ -472,8 +472,7 @@                 ++ map fst (other_pr1 ++ other_pr2)             res_w_inps = map (accTup2 . fst) tup_common ++ map accTup2 (acc_tup1' ++ acc_tup2')         res_w_inps' <- mapM renameLamInWAccInp res_w_inps-        let stm_res = Let (Pat res_pat) (aux1 <> aux2) $ WithAcc res_w_inps' res_lam'-        pure $ Just stm_res+        pure $ Let (Pat res_pat) (aux1 <> aux2) $ WithAcc res_w_inps' res_lam'     where       -- local helpers: @@ -535,7 +534,7 @@       renameLamInWAccInp winp = pure winp -- tryFuseWithAccs _ _ _ =-  pure Nothing+  Nothing  ------------------------------- --- simple helper functions ---
src/Futhark/Optimise/Simplify/Rules/Index.hs view
@@ -8,6 +8,8 @@ where  import Control.Monad (guard)+import Data.Bifunctor (first)+import Data.List qualified as L import Data.List.NonEmpty (NonEmpty (..)) import Data.Maybe import Futhark.Analysis.PrimExp.Convert@@ -33,6 +35,8 @@   | SubExpResult Certs SubExp  -- Fake expressions that we can recognise.+--+-- See Note [Simplifying a Slice]. fakeIndices :: [TPrimExp Int64 VName] fakeIndices = map f [0 :: Int ..]   where@@ -61,6 +65,7 @@         worthInlining e,         all (`ST.elem` vtable) (unCerts cs) ->           Just $ SubExpResult cs <$> toSubExp "index_primexp" e+      -- For the two cases below, see Note [Simplifying a Slice].       | Just inds' <- sliceIndices (Slice inds),         Just (ST.IndexedArray cs arr inds'') <- ST.index idd inds' vtable,         all (worthInlining . untyped) inds'',@@ -76,17 +81,28 @@         all (`ST.elem` vtable) (unCerts cs),         not consuming,         not $ consumed arr,-        Just inds''' <- mapM okIdx inds'' -> do-          Just $ IndexResult cs arr . Slice <$> sequence inds'''+        Just (ordering, inds''') <- first concat . unzip <$> mapM okIdx inds'',+        Just perm <- L.sort ordering `isPermutationOf` ordering ->+          if isIdentityPerm perm+            then Just $ IndexResult cs arr . Slice <$> sequence inds'''+            else Just $ do+              arr_sliced <-+                certifying cs $+                  letExp (baseString arr <> "_sliced") . BasicOp . Index arr . Slice+                    =<< sequence inds'''+              arr_sliced_tr <-+                letSubExp (baseString arr_sliced <> "_tr") $+                  BasicOp (Rearrange perm arr_sliced)+              pure $ SubExpResult mempty arr_sliced_tr       where-        matches = zip fakeIndices $ sliceDims $ Slice inds+        matches = zip fakeIndices $ zip [0 :: Int ..] $ sliceDims $ Slice inds         okIdx i =           case lookup i matches of-            Just w ->-              Just $ pure $ DimSlice (constant (0 :: Int64)) w (constant (1 :: Int64))+            Just (j, w) ->+              Just ([j], pure $ DimSlice (constant (0 :: Int64)) w (constant (1 :: Int64)))             Nothing -> do               guard $ not $ any ((`namesIntersect` freeIn i) . freeIn . fst) matches-              Just $ DimFix <$> toSubExp "index_primexp" i+              Just ([], DimFix <$> toSubExp "index_primexp" i)     Nothing -> Nothing     Just (SubExp (Var v), cs) ->       Just $ pure $ IndexResult cs v $ Slice inds@@ -258,3 +274,48 @@           True       | otherwise =           False++-- Note [Simplifying a Slice]+--+-- The 'indexOp' simplification machinery permits simplification of+-- full indexing (i.e., where every component of the Slice is a+-- DimFix). We use this in a creative way to also simplify slices.+-- For example, for a slice+--+--   A[i:j:+n]+--+-- we synthesize a uniquely recognizable index expression "ie" (see+-- 'fakeIndices'), we use `indexOp` to simplify the full indexing+--+--   A[ie]+--+-- and if that produces a simplification result+--+--   B[ie]+--+-- then we can replace the "ie" DimFix with our original slice and+-- produce+--+--   B[i:j:+n]+--+-- While the above case is trivial, this is useful for cases that+-- intermix indexing and slicing. We must be careful, however: If we+-- have an original expression+--+--   A[i0:j0:+n0,i1:j1:+n1]+--+-- for which we then synthesize the expression+--+--   A[ie0, ie1]+--+-- then if we receive back the result+--+--   B[ie1, ie0]+--+-- we cannot just replace the indexes with the original slices, as+-- that would change the shape (and semantics) of the result:+--+--   B[i1:j1:+n1,i0:j0:+n0]+--+-- In such cases we must actually insert a Rearrange operation to move+-- the dimensions of the result appropriately.
src/Language/Futhark/Interpreter.hs view
@@ -35,6 +35,7 @@ import Data.Array import Data.Bifunctor import Data.Bitraversable+import Data.Either (fromRight) import Data.List   ( find,     foldl',@@ -1351,8 +1352,9 @@      adToPrim v = putV $ AD.primitive v -    adBinOp op x y = AD.doOp op [x, y]-    adUnOp op x = AD.doOp op [x]+    adBinOp op x y =+      either (const Nothing) Just $ AD.doOp op [x, y]+    adUnOp op x = either (const Nothing) Just $ AD.doOp op [x]      fun1 f =       TermValue Nothing $ ValueFun $ \x -> f x@@ -1421,7 +1423,7 @@         _           | Just x' <- getAD x,             Just y' <- getAD y,-            Just z <- msum $ map (`bopDefAD'` (x', y')) fs -> do+            Just z <- msum $ map (`bopDefAD` (x', y')) fs -> do               breakOnNaN [adToPrim x', adToPrim y'] $ adToPrim z               pure $ putAD z         _ ->@@ -1436,7 +1438,7 @@           x' <- valf x           y' <- valf y           retf =<< op x' y'-        bopDefAD' (_, _, _, dop) (x, y) = dop x y+        bopDefAD (_, _, _, dop) (x, y) = dop x y      unopDef fs = fun1 $ \x ->       case x of@@ -1471,7 +1473,7 @@         Just [x, y]           | Just x' <- getAD x,             Just y' <- getAD y,-            Just z <- AD.doOp op [x', y'] -> do+            Right z <- AD.doOp op [x', y'] -> do               breakOnNaN [adToPrim x', adToPrim y'] $ adToPrim z               pure $ putAD z         _ ->@@ -2029,7 +2031,7 @@         -- TODO: Perhaps this could be fully abstracted by AD?         -- Making addFor private would be nice..         add x y =-          fromMaybe (error "jvp: illtyped add") $+          fromRight (error "jvp: illtyped add") $             AD.doOp (AD.OpBin $ AD.addFor $ P.primValueType $ AD.primitive x) [x, y]     def "jvp2" = Just $       -- TODO: This could be much better. Currently, it is very inefficient
src/Language/Futhark/Interpreter/AD.hs view
@@ -15,7 +15,7 @@ where  import Control.Monad (foldM, zipWithM)-import Data.Either (isRight)+import Data.Either (fromRight, isRight) import Data.List (find, foldl') import Data.Map qualified as M import Data.Maybe (fromMaybe)@@ -70,12 +70,14 @@ mulFor Bool = LogAnd mulFor t = error $ "mulFor: " ++ show t +type Depth = Int+ -- Types and utility functions-- -- When taking the partial derivative of a function, we -- must differentiate between the values which are kept -- constant, and those which are not data ADValue-  = Variable Int ADVariable+  = Variable Depth ADVariable   | Constant PrimValue   deriving (Show) @@ -88,7 +90,7 @@   | JVP JVPValue   deriving (Show) -depth :: ADValue -> Int+depth :: ADValue -> Depth depth (Variable d _) = d depth (Constant _) = 0 @@ -97,6 +99,12 @@ primal (Variable _ (JVP (JVPValue v _))) = primal v primal (Constant v) = Constant v +primalFor :: Depth -> ADValue -> ADValue+primalFor cur v@(Variable tag _) | cur /= tag = v+primalFor _ (Variable _ (VJP (VJPValue t))) = tapePrimal t+primalFor cur (Variable _ (JVP (JVPValue v _))) = primalFor cur v+primalFor _ (Constant v) = Constant v+ primitive :: ADValue -> PrimValue primitive (Variable _ v) = varPrimal v primitive (Constant v) = v@@ -106,9 +114,11 @@ varPrimal (JVP (JVPValue v _)) = primitive $ primal v  -- Evaluates a PrimExp using doOp-evalPrimExp :: M.Map VName ADValue -> PrimExp VName -> Maybe ADValue-evalPrimExp m (LeafExp n _) = M.lookup n m-evalPrimExp _ (ValueExp pv) = Just $ Constant pv+evalPrimExp :: M.Map VName ADValue -> PrimExp VName -> Either String ADValue+evalPrimExp m (LeafExp n _) =+  maybe (Left $ "Unknown variable " <> show n) Right $ M.lookup n m+evalPrimExp _ (ValueExp pv) =+  Right $ Constant pv evalPrimExp m (BinOpExp op x y) = do   x' <- evalPrimExp m x   y' <- evalPrimExp m y@@ -141,23 +151,25 @@ -- This function performs a mathematical operation on a -- list of operands, performing automatic differentiation -- if one or more operands is a Variable (of depth > 0)-doOp :: Op -> [ADValue] -> Maybe ADValue+doOp :: Op -> [ADValue] -> Either String ADValue doOp op o   | not $ opTypeMatch op (map primValueType pv) =       -- This function may be called with arguments of invalid types,       -- because it is used as part of an overloaded operator.-      Nothing+      Left $ unwords ["invalid types for op", show op, "and operands", show o]   | otherwise = do       let dep = case op of             OpCmp _ -> 0 -- AD is not well-defined for comparason operations             -- There are no derivatives for those written in             -- PrimExp (check lookupPDs)             _ -> maximum (map depth o)-      if dep == 0 then constCase else nonconstCase dep+      if dep == 0+        then maybe (Left "failed to evaluate const") Right constCase+        else nonconstCase dep   where     pv = map primitive o -    divideDepths :: Int -> ADValue -> Either ADValue ADVariable+    divideDepths :: Depth -> ADValue -> Either ADValue ADVariable     divideDepths _ v@(Constant {}) = Left v     divideDepths d v@(Variable d' v') = if d' < d then Left v else Right v' @@ -198,7 +210,7 @@       -- have to perform the necessary steps for AD        -- First, we calculate the value for the previous depth-      let oprev = map primal o+      let oprev = map (primalFor dep) o       vprev <- doOp op oprev        -- Then we separate the values of the maximum depth from@@ -209,28 +221,31 @@         -- Finally, we perform the necessary steps for the given         -- type of AD         Just (Right (VJP {})) ->-          Just . Variable dep . VJP . VJPValue $ vjpHandleOp op (map extractVJP o') vprev+          Right . Variable dep . VJP . VJPValue $+            vjpHandleOp op (map extractVJP o') vprev         Just (Right (JVP {})) ->-          Variable dep . JVP . JVPValue vprev <$> jvpHandleFn op (map extractJVP o')+          Variable dep . JVP . JVPValue vprev+            <$> jvpHandleOp op (map extractJVP o')         _ ->           -- Since the maximum depth is non-zero, there must be at           -- least one variable of depth > 0           error "find isRight"  calculatePDs :: Op -> [ADValue] -> [ADValue]-calculatePDs op p =+calculatePDs op args =   -- Create a unique VName for each operand-  let n = map (\i -> VName (nameFromString $ "x" ++ show i) i) [1 .. length p]+  let n = map (\i -> VName (nameFromString $ "x" ++ show i) i) [1 .. length args]       -- Put the operands in the environment-      m = M.fromList $ zip n p+      m = M.fromList $ zip n args        -- Look up, and calculate the partial derivative       -- of the operation with respect to each operand       pde =         fromMaybe (error "lookupPDs failed") $           lookupPDs op $-            map (`LeafExp` opReturnType op) n-   in map (fromMaybe (error "evalPrimExp failed") . evalPrimExp m) pde+            zipWith (\v val -> LeafExp v $ primValueType $ primitive val) n args+      res = map (either (error . ("evalPrimExp failed: " <>)) id . evalPrimExp m) pde+   in res  -- VJP / Reverse mode automatic differentiation-- -- In reverse mode AD, the entire computation@@ -244,7 +259,7 @@ data Tape   = -- | This represents a variable. Each variable is given a unique ID,     -- and has an initial value-    TapeID Int ADValue+    TapeID Depth ADValue   | -- | This represents a constant.     TapeConst ADValue   | -- | This represents the application of a mathematical operation.@@ -279,7 +294,7 @@   let s'' = case op of         OpConv op' ->           -- In case of type conversion, simply convert the sensitivity-          [ fromMaybe (error "deriveTape: doOp failed") $+          [ fromRight (error "deriveTape: doOp failed") $               doOp (OpConv $ flipConvOp op') [s]           ]         _ ->@@ -291,10 +306,10 @@       foldl' (M.unionWith add) M.empty pd   where     add x y =-      fromMaybe (error "deriveTape: add failed") $+      fromRight (error "deriveTape: add failed") $         doOp (OpBin $ addFor $ opReturnType op) [x, y]     mul x y =-      fromMaybe (error "deriveTape: mul failed") $+      fromRight (error "deriveTape: mul failed") $         doOp (OpBin $ mulFor $ opReturnType op) [x, y]  -- JVP / Forward mode automatic differentiation--@@ -304,27 +319,26 @@ data JVPValue = JVPValue ADValue ADValue   deriving (Show) --- | This calculates the derivative part of the JVPValue resulting+-- | This calculates the tangent part of the JVPValue resulting -- from the application of a mathematical operation on one or more -- JVPValues.-jvpHandleFn :: Op -> [Either ADValue JVPValue] -> Maybe ADValue-jvpHandleFn op p = do+jvpHandleOp :: Op -> [Either ADValue JVPValue] -> Either String ADValue+jvpHandleOp op p = do   case op of     OpConv _ ->       -- In case of type conversion, simply convert-      -- the old derivative-      doOp op [derivative $ head p]+      -- the old tangent+      doOp op [tangent $ head p]     _ -> do-      -- Calculate the new derivative using the chain-      -- rule+      -- Calculate the new tangent using the chain rule       let pds = calculatePDs op $ map primal' p-      vs <- zipWithM mul pds $ map derivative p-      foldM add (Constant $ blankPrimValue $ opReturnType op) vs+      vs <- zipWithM mul pds $ map tangent p+      foldM add (Constant $ blankPrimValue op_t) vs   where+    op_t = opReturnType op     primal' (Left v) = v     primal' (Right (JVPValue v _)) = v-    derivative (Left v) = Constant $ blankPrimValue $ primValueType $ primitive v-    derivative (Right (JVPValue _ d)) = d-+    tangent (Left _) = Constant $ blankPrimValue $ opReturnType op+    tangent (Right (JVPValue _ d)) = d     add x y = doOp (OpBin $ addFor $ opReturnType op) [x, y]     mul x y = doOp (OpBin $ mulFor $ opReturnType op) [x, y]
src/Language/Futhark/Interpreter/Values.hs view
@@ -200,7 +200,8 @@ valueAccum :: (a -> Value m -> (a, Value m)) -> a -> Value m -> (a, Value m) valueAccum f i v@(ValuePrim {}) = f i v valueAccum f i v@(ValueAD {}) = f i v-valueAccum f i (ValueRecord m) = second ValueRecord $ M.mapAccum (valueAccum f) i m+valueAccum f i (ValueRecord m) =+  second ValueRecord $ M.mapAccum (valueAccum f) i m valueAccum f i (ValueArray s a) = do   -- TODO: This could probably be better   -- Transform into a map
src/Language/Futhark/TypeChecker/Modules.hs view
@@ -343,12 +343,12 @@ missingType :: (Pretty a) => Loc -> a -> Either TypeError b missingType loc name =   Left . TypeError loc mempty $-    "Module does not define a type named" <+> pretty name <> "."+    "Module does not define a type named" <+> dquotes (pretty name) <> "."  missingVal :: (Pretty a) => Loc -> a -> Either TypeError b missingVal loc name =   Left . TypeError loc mempty $-    "Module does not define a value named" <+> pretty name <> "."+    "Module does not define a value named" <+> dquotes (pretty name) <> "."  topLevelSize :: Loc -> VName -> Either TypeError b topLevelSize loc name =@@ -358,7 +358,7 @@ missingMod :: (Pretty a) => Loc -> a -> Either TypeError b missingMod loc name =   Left . TypeError loc mempty $-    "Module does not define a module named" <+> pretty name <> "."+    "Module does not define a module named" <+> dquotes (pretty name) <> "."  mismatchedType ::   Loc ->