@@ -1,8 +1,11 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE ForeignFunctionInterface #-}
1module Grenade.Layers.Internal.Convolution (
3module Grenade.Layers.Internal.Convolution (
2 col2vidUnsafe
4 col2vidUnsafe
3 , col2imUnsafe
5 , col2imUnsafe
4 , vid2colUnsafe
6 , vid2colUnsafe
5 , im2colUnsafe
7 , im2colUnsafe
6 , fittingStarts
9 , fittingStarts
7 , unsafeModifyMatrix
10 , unsafeModifyMatrix
@@ -13,11 +16,15 @@ import Data.STRef ( newSTRef, modifySTRef', writeSTRef, readSTRef )
13import Data.Foldable ( forM_ )
16import Data.Foldable ( forM_ )
14import Data.Traversable ( forM )
17import Data.Traversable ( forM )
19+import Foreign ( mallocForeignPtrArray0, withForeignPtr )
20+import Foreign.Ptr ( Ptr )
16import Foreign.Storable( Storable )
21import Foreign.Storable( Storable )
18import Numeric.LinearAlgebra hiding ( uniformSample, konst )
23import Numeric.LinearAlgebra hiding ( uniformSample, konst )
19import qualified Numeric.LinearAlgebra.Devel as U
24import qualified Numeric.LinearAlgebra.Devel as U
26+import System.IO.Unsafe ( unsafePerformIO )
21-- This module provides provides im2col function and friends, ala caffe.
28-- This module provides provides im2col function and friends, ala caffe.
23-- /* From Caffe */
30-- /* From Caffe */
@@ -105,13 +112,13 @@ col2vidUnsafe kernelRows kernelColumns strideRows strideColumns destinationRows
105 offsetR <- newSTRef 0
112 offsetR <- newSTRef 0
106 offsetC <- newSTRef 0
113 offsetC <- newSTRef 0
107 forM_ [0 .. columnMatrixRows - 1] $ \ir -> do
114 forM_ [0 .. columnMatrixRows - 1] $ \ir -> do
108− inputColumn <- newSTRef 0
115+ inputColumn <- newSTRef offsetM
109− offsetR' <- readSTRef offsetR
116+ offsetR' <- readSTRef offsetR
110− offsetC' <- readSTRef offsetC
117+ offsetC' <- readSTRef offsetC
111 forM_ [offsetR' .. offsetR' + kernelRows -1] $ \kr ->
118 forM_ [offsetR' .. offsetR' + kernelRows -1] $ \kr ->
112 forM_ [offsetC' .. offsetC' + kernelColumns -1] $ \kc -> do
119 forM_ [offsetC' .. offsetC' + kernelColumns -1] $ \kc -> do
113 ic <- readSTRef inputColumn
120 ic <- readSTRef inputColumn
114− unsafeModifyMatrix dataIm kr kc (+ U.atM' columnMatrix ir (ic + offsetM))
121+ unsafeModifyMatrix dataIm kr kc (+ U.atM' columnMatrix ir ic)
115 modifySTRef' inputColumn (+1)
122 modifySTRef' inputColumn (+1)
117 if offsetC' + kernelColumns < destinationCols
124 if offsetC' + kernelColumns < destinationCols
@@ -120,6 +127,62 @@ col2vidUnsafe kernelRows kernelColumns strideRows strideColumns destinationRows
121 U.unsafeFreezeMatrix dataIm
128 U.unsafeFreezeMatrix dataIm
131+im2col_c :: Int -> Int -> Int -> Int -> Matrix Double -> Matrix Double
132+im2col_c kernelRows kernelColumns strideRows strideColumns dataIm =
133+ let height = rows dataIm
135+ vec = flatten dataIm
136+ rowOut = (height - kernelRows) `div` strideRows + 1
137+ colOut = (width - kernelColumns) `div` strideColumns + 1
138+ kernelSize = kernelRows * kernelColumns
139+ numberOfPatches = rowOut * colOut
140+ in unsafePerformIO $ do
141+ outPtr <- mallocForeignPtrArray0 (numberOfPatches * kernelSize)
142+ let (inPtr, inOffset, _) = U.unsafeToForeignPtr vec
144+ withForeignPtr inPtr $ \inPtr' ->
145+ withForeignPtr outPtr $ \outPtr' ->
146+ im2col_cpu inPtr' inOffset 1 height width kernelRows kernelColumns strideRows strideColumns outPtr'
148+ let matVec = U.unsafeFromForeignPtr outPtr 0 (numberOfPatches * kernelSize)
149+ return $ U.matrixFromVector U.RowMajor numberOfPatches kernelSize matVec
151+foreign import ccall safe
153+ :: Ptr Double -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Ptr Double -> IO ()
156+unsafeModifyMatrix :: (Storable t) => U.STMatrix s t -> Int -> Int -> (t -> t) -> ST s ()
157+unsafeModifyMatrix x r c f = U.unsafeReadMatrix x r c >>= U.unsafeWriteMatrix x r c . f
158+{-# INLINE unsafeModifyMatrix #-}
161+-- | Returns the starting sub matrix locations which fit inside the larger matrix for the
162+-- convolution. Takes into account the stride and kernel size.
163+fittingStarts :: Int -> Int -> Int -> Int -> Int -> Int -> [(Int,Int)]
164+fittingStarts nrows kernelrows steprows ncols kernelcols stepcolsh =
165+ let rs = fittingStart nrows kernelrows steprows
166+ cs = fittingStart ncols kernelcols stepcolsh
167+ in concatMap ( \r -> fmap (\c -> (r , c)) cs ) rs
169+-- | Returns the starting sub vector which fit inside the larger vector for the
170+-- convolution. Takes into account the stride and kernel size.
171+fittingStart :: Int -> Int -> Int -> [Int]
172+fittingStart width kernel steps =
173+ let go left | left + kernel < width
174+ = left : go (left + steps)
175+ | left + kernel == width
184+-- | Old functions (useful for sanity checking and benchmarking)
123vid2colUnsafe :: Int -> Int -> Int -> Int -> Int -> Int -> [Matrix Double] -> Matrix Double
186vid2colUnsafe :: Int -> Int -> Int -> Int -> Int -> Int -> [Matrix Double] -> Matrix Double
124vid2colUnsafe kernelRows kernelColumns striderows stridecols vidrows vidcols dataVid = U.runSTMatrix $ do
187vid2colUnsafe kernelRows kernelColumns striderows stridecols vidrows vidcols dataVid = U.runSTMatrix $ do
125 let starts = fittingStarts vidrows kernelRows striderows vidcols kernelColumns stridecols
188 let starts = fittingStarts vidrows kernelRows striderows vidcols kernelColumns stridecols
@@ -168,28 +231,3 @@ im2colUnsafe kernelRows kernelColumns striderows stridecols dataIm = U.runSTMatr
168 modifySTRef' inputRowRef (+1)
231 modifySTRef' inputRowRef (+1)
170 return dataCol
233 return dataCol
172−unsafeModifyMatrix :: (Storable t) => U.STMatrix s t -> Int -> Int -> (t -> t) -> ST s ()
173−unsafeModifyMatrix x r c f = U.unsafeReadMatrix x r c >>= U.unsafeWriteMatrix x r c . f
174−{-# INLINE unsafeModifyMatrix #-}
177−-- | Returns the starting sub matrix locations which fit inside the larger matrix for the
178−-- convolution. Takes into account the stride and kernel size.
179−fittingStarts :: Int -> Int -> Int -> Int -> Int -> Int -> [(Int,Int)]
180−fittingStarts nrows kernelrows steprows ncols kernelcols stepcolsh =
181− let rs = fittingStart nrows kernelrows steprows
182− cs = fittingStart ncols kernelcols stepcolsh
183− in concatMap ( \r -> fmap (\c -> (r , c)) cs ) rs
185−-- | Returns the starting sub vector which fit inside the larger vector for the
186−-- convolution. Takes into account the stride and kernel size.
187−fittingStart :: Int -> Int -> Int -> [Int]
188−fittingStart width kernel steps =
189− let go left | left + kernel < width
190− = left : go (left + steps)
191− | left + kernel == width