Skip to content

Commit de55ece

Browse files
committed
caching utility
1 parent 758384b commit de55ece

File tree

5 files changed

+121
-1
lines changed

5 files changed

+121
-1
lines changed

neural.cabal

+4-1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ library
5858
, Data.Utils
5959
, Data.Utils.Analytic
6060
, Data.Utils.Arrow
61+
, Data.Utils.Cache
6162
, Data.Utils.List
6263
, Data.Utils.Matrix
6364
, Data.Utils.Pipes
@@ -72,6 +73,7 @@ library
7273
, ad
7374
, array
7475
, bytestring
76+
, containers
7577
, deepseq
7678
, directory
7779
, filepath
@@ -104,7 +106,8 @@ test-suite neural-test
104106
, hspec
105107
, MonadRandom
106108
, neural
107-
other-modules: Utils.MatrixSpec
109+
other-modules: Utils.CacheSpec
110+
, Utils.MatrixSpec
108111
, Utils.VectorSpec
109112
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -fexcess-precision -optc-O3 -optc-ffast-math
110113
default-language: Haskell2010

src/Data/Utils.hs

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This module reexports various utility modules for convenience.
1515
module Data.Utils
1616
( module Data.Utils.Analytic
1717
, module Data.Utils.Arrow
18+
, module Data.Utils.Cache
1819
, module Data.Utils.Matrix
1920
, module Data.Utils.Pipes
2021
, module Data.Utils.Random
@@ -26,6 +27,7 @@ module Data.Utils
2627

2728
import Data.Utils.Analytic
2829
import Data.Utils.Arrow
30+
import Data.Utils.Cache
2931
import Data.Utils.Matrix
3032
import Data.Utils.Pipes
3133
import Data.Utils.Random

src/Data/Utils/Cache.hs

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
{-# OPTIONS_HADDOCK show-extensions #-}
2+
3+
{-# LANGUAGE DeriveFunctor #-}
4+
{-# LANGUAGE ScopedTypeVariables #-}
5+
6+
{-|
7+
Module : Data.Utils.Cache
8+
Description : caching
9+
Copyright : (c) Lars Brünjes, 2016
10+
License : MIT
11+
Maintainer : brunjlar@gmail.com
12+
Stability : experimental
13+
Portability : portable
14+
15+
This module defines simple size-limitted caches and utilities for working with them.
16+
-}
17+
18+
module Data.Utils.Cache
19+
( Cache
20+
, newCache
21+
, retrieveC
22+
) where
23+
24+
import qualified Data.Map as M
25+
import Data.MyPrelude
26+
import qualified Data.Set as S
27+
28+
-- | A @'Cache' m k v@ is a cache with keys of type @k@ and values of type @v@.
29+
-- If an uncached key is requested, its value is retrieved using effects in monad @m@.
30+
--
31+
data Cache m k v = Cache
32+
{ cMap :: M.Map k v
33+
, cQueue :: [k]
34+
, cGet :: [k] -> m [v]
35+
, cCapacity :: Int
36+
}
37+
38+
-- | Creates a new cache with the given retrieval function and maximal cache capacity.
39+
--
40+
newCache :: ([k] -> m [v]) -> Int -> Cache m k v
41+
newCache g c = Cache
42+
{ cMap = M.empty
43+
, cQueue = []
44+
, cGet = g
45+
, cCapacity = max c 0
46+
}
47+
48+
instance (Show k, Show v) => Show (Cache m k v) where
49+
50+
show c = show (cMap c, cQueue c)
51+
52+
-- | Retrieves the values for the specified keys from the cache.
53+
-- Uncached values will be retrieved and cached while respecting the maximal cache capacity
54+
-- (old values will be dropped first).
55+
--
56+
retrieveC :: forall m k v. (Monad m, Ord k) => Cache m k v -> [k] -> m ([v], Cache m k v)
57+
retrieveC c xs = do
58+
let (m, xs') = f
59+
(ys, zs) = splitAt (length xs') $ cQueue c
60+
n = foldl' (flip M.delete) (cMap c) ys
61+
xs'' = toList xs'
62+
vs <- cGet c xs''
63+
let xvs = zip xs'' vs
64+
l = max (cCapacity c) $ length ys
65+
m' = foldl' (\m'' (x, v) -> M.insert x v m'') m xvs
66+
n' = foldl' (\n'' (x, v) -> M.insert x v n'') n $ take l xvs
67+
zs' = zs ++ take l xs''
68+
c' = c { cMap = n', cQueue = zs' }
69+
vs' = (m' M.!) <$> xs
70+
return (vs', c')
71+
72+
where
73+
74+
f :: (M.Map k v, S.Set k)
75+
f = foldl' f' (M.empty, S.empty) xs where
76+
f' (m, xs') x = case M.lookup x (cMap c) of
77+
Just y -> (M.insert x y m, xs')
78+
Nothing -> ( m, S.insert x xs')

src/Numeric/Neural/Normalization.hs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# OPTIONS_HADDOCK show-extensions #-}
2+
23
{-# LANGUAGE ScopedTypeVariables #-}
34
{-# LANGUAGE RankNTypes #-}
45
{-# LANGUAGE TypeOperators #-}

test/Utils/CacheSpec.hs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
module Utils.CacheSpec (spec) where
2+
3+
import Test.Hspec
4+
import Data.MyPrelude
5+
import Data.Utils
6+
7+
spec :: Spec
8+
spec = cacheSpec
9+
10+
cacheSpec :: Spec
11+
cacheSpec = describe "retrieveC" $ do
12+
13+
it "should retrieve uncached elements" $
14+
let w = do
15+
(xs, _) <- retrieveC mkCache [1, 2, 3]
16+
return xs
17+
in runWriter w `shouldBe` ([1, 2, 3], [1, 2, 3])
18+
19+
it "should use the cache if possible" $
20+
let w = do
21+
(_, c) <- retrieveC mkCache [1, 2, 3]
22+
(xs, _) <- retrieveC c [3, 3, 1, 4]
23+
return xs
24+
in runWriter w `shouldBe` ([3, 3, 1, 4], [1, 2, 3, 4])
25+
26+
it "should respect the cache capacity" $
27+
let w = do
28+
(_, c ) <- retrieveC mkCache [1, 2, 3]
29+
(_, c') <- retrieveC c [3, 3, 1, 4]
30+
(xs, _ ) <- retrieveC c' [4, 1]
31+
return xs
32+
in runWriter w `shouldBe` ([4, 1], [1, 2, 3, 4, 1])
33+
34+
mkCache :: Cache (Writer [Int]) Int Int
35+
mkCache = newCache f 3 where
36+
f xs = tell xs >> return xs

0 commit comments

Comments
 (0)