@@ -14,12 +14,17 @@ import Control.Monad ( guard )
1414import Control.Monad.ST ( runST )
1515
1616import Data.Constraint
17+#if __GLASGOW_HASKELL__ < 800
18+import Data.Proxy
19+#endif
1720import qualified Data.Vector.Storable as VS
1821import qualified Data.Vector.Storable.Mutable as VS ( write )
1922import Data.Singletons
2023import Data.Singletons.Prelude.List
2124import Data.Singletons.TypeLits
2225
26+-- import Data.Type.Equality
27+
2328import Hedgehog
2429import qualified Hedgehog.Gen as Gen
2530import Hedgehog.Internal.Source
@@ -27,15 +32,7 @@ import Hedgehog.Internal.Property ( failWith )
2732
2833import Grenade
2934
30−#if MIN_VERSION_base(4,11,0)
31−import GHC.TypeLits hiding (natVal)
32−#else
3335import GHC.TypeLits
34−#endif
35−#if MIN_VERSION_base(4,9,0)
36−import Data.Kind (Type)
37−#endif
38−
3936import GHC.TypeLits.Witnesses
4037import Test.Hedgehog.Compat
4138import Test.Hedgehog.TypeLits
@@ -46,7 +43,7 @@ import Numeric.LinearAlgebra ( flatten )
4643import Numeric.LinearAlgebra.Static ( extract, norm_Inf )
4744import Unsafe.Coerce
4845
49−data SomeNetwork :: Type where
46+data SomeNetwork :: * where
5047 SomeNetwork :: ( SingI shapes, SingI (Head shapes), SingI (Last shapes), Show (Network layers shapes) ) => Network layers shapes -> SomeNetwork
5148
5249instance Show SomeNetwork where
@@ -56,7 +53,7 @@ instance Show SomeNetwork where
5653--
5754-- This is slightly insane for a few reasons. Everything must be wrapped up
5855-- in a SomeNetwork.
59−genNetwork :: Gen SomeNetwork
56+genNetwork :: Monad m => Gen.Gen m SomeNetwork
6057genNetwork =
6158 Gen.recursive Gen.choice [
6259 do SomeSing ( r :: Sing final ) <- genShape
@@ -65,11 +62,11 @@ genNetwork =
6562 ] [
6663 do SomeNetwork ( rest :: Network layers shapes ) <- genNetwork
6764 case ( sing :: Sing shapes ) of
68− SNil -> Gen.discard -- Can't occur
65+ SNil -> Gen.discard
6966 SCons ( h :: Sing h ) ( _ :: Sing hs ) ->
7067 withSingI h $
7168 case h of
72− D1Sing l@SNat ->
69+ D1Sing l -> withKnownNat l $
7370 Gen.choice [
7471 pure (SomeNetwork (Tanh :~> rest :: Network ( Tanh ': layers ) ( h ': h ': hs )))
7572 , pure (SomeNetwork (Logit :~> rest :: Network ( Logit ': layers ) ( h ': h ': hs )))
@@ -77,9 +74,8 @@ genNetwork =
7774 , pure (SomeNetwork (Elu :~> rest :: Network ( Elu ': layers ) ( h ': h ': hs )))
7875 , pure (SomeNetwork (Softmax :~> rest :: Network ( Softmax ': layers ) ( h ': h ': hs )))
7976 , do -- Reshape to two dimensions
80− let divisors :: Integer -> [Integer]
81− divisors n = 1 : [x | x <- [2..(n-1)], n `rem` x == 0]
82− let len = fromIntegral $ natVal l
77+ let divisors n = 1 : [x | x <- [2..(n-1)], n `rem` x == 0]
78+ let len = natVal l
8379 rs <- Gen.element $ divisors len
8480 let cs = len `quot` rs
8581 case ( someNatVal rs, someNatVal cs, someNatVal len ) of
@@ -91,7 +87,7 @@ genNetwork =
9187 pure (SomeNetwork (Reshape :~> rest :: Network ( Reshape ': layers ) ( ('D2 inRows inCols) ': h ': hs )))
9288 _ -> Gen.discard -- Doesn't occur
9389 ]
94− D2Sing r@SNat c@SNat ->
90+ D2Sing r c -> withKnownNat r $ withKnownNat c $
9591 Gen.choice [
9692 pure (SomeNetwork (Tanh :~> rest :: Network ( Tanh ': layers ) ( h ': h ': hs )))
9793 , pure (SomeNetwork (Logit :~> rest :: Network ( Logit ': layers ) ( h ': h ': hs )))
@@ -100,8 +96,8 @@ genNetwork =
10096 , do -- Build a convolution layer with one filter output
10197 -- Figure out some kernel sizes which work for this layer
10298 -- There must be a better way than this...
103− let output_r = fromIntegral $ natVal r
104− let output_c = fromIntegral $ natVal c
99+ let output_r = natVal r
100+ let output_c = natVal c
105101
106102 let ok extent kernel = [stride | stride <- [ 1 .. extent ], (extent - kernel) `mod` stride == 0]
107103
@@ -140,8 +136,8 @@ genNetwork =
140136 , do -- Build a convolution layer with one filter output
141137 -- Figure out some kernel sizes which work for this layer
142138 -- There must be a better way than this...
143− let output_r = fromIntegral $ natVal r
144− let output_c = fromIntegral $ natVal c
139+ let output_r = natVal r
140+ let output_c = natVal c
145141
146142 let ok extent kernel = [stride | stride <- [ 1 .. extent ], (extent - kernel) `mod` stride == 0]
147143
@@ -182,8 +178,8 @@ genNetwork =
182178 pure (SomeNetwork (conv :~> rest :: Network ( Convolution channels 1 kernelRows kernelCols strideRows strideCols ': layers ) ( ('D3 inRows inCols channels) ': h ': hs )))
183179 _ -> Gen.discard -- Can't occur
184180 , do -- Build a Pooling layer
185− let output_r = fromIntegral $ natVal r
186− let output_c = fromIntegral $ natVal c
181+ let output_r = natVal r
182+ let output_c = natVal c
187183
188184 let ok extent kernel = [stride | stride <- [ 1 .. extent ], (extent - kernel) `mod` stride == 0]
189185
@@ -218,219 +214,25 @@ genNetwork =
218214 (Dict, Dict, Dict, Dict) ->
219215 pure (SomeNetwork (Pooling :~> rest :: Network ( Pooling kernelRows kernelCols strideRows strideCols ': layers ) ( ('D2 inRows inCols) ': h ': hs )))
220216 _ -> Gen.discard -- Can't occur
221− , do -- Build a Pad layer
222− let output_r = fromIntegral $ natVal r
223− let output_c = fromIntegral $ natVal c
224−
225− pad_left <- choose 0 (output_r - 1)
226− pad_right <- choose 0 (output_r - 1 - pad_left)
227− pad_top <- choose 0 (output_c - 1)
228− pad_bottom <- choose 0 (output_c - 1 - pad_top)
229−
230− let input_r = output_r - pad_top - pad_bottom
231− let input_c = output_c - pad_left - pad_right
232−
233− guard (input_r > 1)
234− guard (input_c > 1)
235−
236− -- Remake types for input
237− case ( someNatVal input_r, someNatVal input_c, someNatVal output_r, someNatVal output_c, someNatVal pad_left, someNatVal pad_right, someNatVal pad_top, someNatVal pad_bottom ) of
238− ( Just (SomeNat (_ :: Proxy inputRows)), Just (SomeNat (_ :: Proxy inputColumns)),
239− Just (SomeNat (_ :: Proxy outputRows)), Just (SomeNat (_ :: Proxy outputColumns)),
240− Just (SomeNat (_ :: Proxy padLeft)), Just (SomeNat (_ :: Proxy padRight)),
241− Just (SomeNat (_ :: Proxy padTop)), Just (SomeNat (_ :: Proxy padBottom))) ->
242− case ( (unsafeCoerce (Dict :: Dict ()) :: Dict ( ( 'D2 outputRows outputColumns) ~ h ))
243− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( (inputRows + padTop + padBottom) ~ outputRows) )
244− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( (inputColumns + padLeft + padRight) ~ outputColumns ))) of
245− ( Dict, Dict, Dict ) ->
246− pure (SomeNetwork (Pad :~> rest :: Network ( Pad padLeft padTop padRight padBottom ': layers ) ( ('D2 inputRows inputColumns) ': h ': hs )))
247− _ -> Gen.discard -- Can't occur
248− , do -- Build a Crop layer
249− let output_r = fromIntegral $ natVal r
250− let output_c = fromIntegral $ natVal c
251−
252− crop_left <- choose 0 10
253− crop_right <- choose 0 10
254− crop_top <- choose 0 10
255− crop_bottom <- choose 0 10
256−
257− let input_r = output_r + crop_top + crop_bottom
258− let input_c = output_c + crop_left + crop_right
259−
260− -- Remake types for input
261− case ( someNatVal input_r, someNatVal input_c, someNatVal output_r, someNatVal output_c, someNatVal crop_left, someNatVal crop_right, someNatVal crop_top, someNatVal crop_bottom ) of
262− ( Just (SomeNat (_ :: Proxy inputRows)), Just (SomeNat (_ :: Proxy inputColumns)),
263− Just (SomeNat (_ :: Proxy outputRows)), Just (SomeNat (_ :: Proxy outputColumns)),
264− Just (SomeNat (_ :: Proxy cropLeft)), Just (SomeNat (_ :: Proxy cropRight)),
265− Just (SomeNat (_ :: Proxy cropTop)), Just (SomeNat (_ :: Proxy cropBottom))) ->
266− case ( (unsafeCoerce (Dict :: Dict ()) :: Dict ( ( 'D2 outputRows outputColumns) ~ h ))
267− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( (outputRows + cropTop + cropBottom) ~ inputRows ) )
268− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( (outputColumns + cropLeft + cropRight) ~ inputColumns ))) of
269− (Dict, Dict, Dict) ->
270− pure (SomeNetwork (Crop :~> rest :: Network ( Crop cropLeft cropTop cropRight cropBottom ': layers ) ( ('D2 inputRows inputColumns) ': h ': hs )))
271− _ -> Gen.discard -- Can't occur
272217 ]
273− D3Sing r@SNat c@SNat f@SNat ->
218+ D3Sing r c d -> withKnownNat d $ withKnownNat r $ withKnownNat c $
274219 Gen.choice [
275220 pure (SomeNetwork (Tanh :~> rest :: Network ( Tanh ': layers ) ( h ': h ': hs )))
276221 , pure (SomeNetwork (Logit :~> rest :: Network ( Logit ': layers ) ( h ': h ': hs )))
277222 , pure (SomeNetwork (Relu :~> rest :: Network ( Relu ': layers ) ( h ': h ': hs )))
278223 , pure (SomeNetwork (Elu :~> rest :: Network ( Elu ': layers ) ( h ': h ': hs )))
279− , do -- Build a convolution layer with one filter output
280− -- Figure out some kernel sizes which work for this layer
281− -- There must be a better way than this...
282− let output_r = fromIntegral $ natVal r
283− let output_c = fromIntegral $ natVal c
284− let output_f = fromIntegral $ natVal f
285−
286− let ok extent kernel = [stride | stride <- [ 1 .. extent ], (extent - kernel) `mod` stride == 0]
287−
288− -- Get some kernels which will fit
289− kernel_r <- choose 1 output_r
290− kernel_c <- choose 1 output_c
291− channels <- choose 1 10
292−
293− -- Build up some strides which also fit
294− stride_r <- Gen.element $ ok output_r kernel_r
295− stride_c <- Gen.element $ ok output_c kernel_c
296−
297− -- Determine the input size
298− let input_r = (output_r - 1) * stride_r + kernel_r
299− let input_c = (output_c - 1) * stride_c + kernel_c
300−
301− guard (input_r < 100)
302− guard (input_c < 100)
303−
304− -- Remake types for input
305− case ( someNatVal channels, someNatVal output_f, someNatVal input_r, someNatVal input_c, someNatVal output_r, someNatVal output_c, someNatVal kernel_r, someNatVal kernel_c, someNatVal stride_r, someNatVal stride_c ) of
306− ( Just (SomeNat (chan :: Proxy channels)), Just (SomeNat (_ :: Proxy filters)),
307− Just (SomeNat (pinr :: Proxy inRows)), Just (SomeNat (_ :: Proxy inCols)),
308− Just (SomeNat (_ :: Proxy outRows)), Just (SomeNat (_ :: Proxy outCols)),
309− Just (SomeNat (pkr :: Proxy kernelRows)), Just (SomeNat (pkc :: Proxy kernelCols)),
310− Just (SomeNat (_ :: Proxy strideRows)), Just (SomeNat (_ :: Proxy strideCols))) ->
311− let p1 = natDict pkr
312− p2 = natDict pkc
313− p3 = natDict chan
314− in case ( p1 %* p2 %* p3
315− , natDict pinr %* natDict chan
316− -- Fake it till you make it.
317− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( ( 'D3 outRows outCols filters) ~ h ))
318− , (unsafeCoerce (Dict :: Dict ()) :: Dict (((outRows - 1) * strideRows) ~ (inRows - kernelRows)))
319− , (unsafeCoerce (Dict :: Dict ()) :: Dict (((outCols - 1) * strideCols) ~ (inCols - kernelCols)))) of
320− (Dict, Dict, Dict, Dict, Dict) -> do
321− conv <- genConvolution
322− pure (SomeNetwork (conv :~> rest :: Network ( Convolution channels filters kernelRows kernelCols strideRows strideCols ': layers ) ( ('D3 inRows inCols channels) ': h ': hs )))
323− _ -> Gen.discard -- Can't occur
324− , do -- Build a Pooling layer
325− let output_r = fromIntegral $ natVal r
326− let output_c = fromIntegral $ natVal c
327− let output_f = fromIntegral $ natVal f
328−
329− let ok extent kernel = [stride | stride <- [ 1 .. extent ], (extent - kernel) `mod` stride == 0]
330−
331− -- Get some kernels which will fit
332− kernel_r <- choose 1 output_r
333− kernel_c <- choose 1 output_c
334−
335− -- Build up some strides which also fit
336− stride_r <- Gen.element $ ok output_r kernel_r
337− stride_c <- Gen.element $ ok output_c kernel_c
338−
339− -- Determine the input size
340− let input_r = (output_r - 1) * stride_r + kernel_r
341− let input_c = (output_c - 1) * stride_c + kernel_c
342−
343− guard (input_r < 100)
344− guard (input_c < 100)
345−
346− -- Remake types for input
347− case ( someNatVal output_f, someNatVal input_r, someNatVal input_c, someNatVal output_r, someNatVal output_c, someNatVal kernel_r, someNatVal kernel_c, someNatVal stride_r, someNatVal stride_c ) of
348− ( Just (SomeNat (chan :: Proxy filters)),
349− Just (SomeNat (pinr :: Proxy inRows)), Just (SomeNat (_ :: Proxy inCols)),
350− Just (SomeNat (_ :: Proxy outRows)), Just (SomeNat (_ :: Proxy outCols)),
351− Just (SomeNat (pkr :: Proxy kernelRows)), Just (SomeNat (pkc :: Proxy kernelCols)),
352− Just (SomeNat (_ :: Proxy strideRows)), Just (SomeNat (_ :: Proxy strideCols))) ->
353− let p1 = natDict pkr
354− p2 = natDict pkc
355− p3 = natDict chan
356− in case ( p1 %* p2 %* p3
357− , natDict pinr %* natDict chan
358− -- Fake it till you make it.
359− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( ( 'D3 outRows outCols filters) ~ h ))
360− , (unsafeCoerce (Dict :: Dict ()) :: Dict (((outRows - 1) * strideRows) ~ (inRows - kernelRows)))
361− , (unsafeCoerce (Dict :: Dict ()) :: Dict (((outCols - 1) * strideCols) ~ (inCols - kernelCols)))) of
362− (Dict, Dict, Dict, Dict, Dict) ->
363− pure (SomeNetwork (Pooling :~> rest :: Network ( Pooling kernelRows kernelCols strideRows strideCols ': layers ) ( ('D3 inRows inCols filters) ': h ': hs )))
364− _ -> Gen.discard -- Can't occur
365− , do -- Build a Pad layer
366− let output_r = fromIntegral $ natVal r
367− let output_c = fromIntegral $ natVal c
368− let output_f = fromIntegral $ natVal f
369−
370− pad_left <- choose 0 (output_r - 1)
371− pad_right <- choose 0 (output_r - 1 - pad_left)
372− pad_top <- choose 0 (output_c - 1)
373− pad_bottom <- choose 0 (output_c - 1 - pad_top)
374−
375− let input_r = output_r - pad_top - pad_bottom
376− let input_c = output_c - pad_left - pad_right
377−
378− guard (input_r > 1)
379− guard (input_c > 1)
380−
381− -- Remake types for input
382− case ( someNatVal output_f, someNatVal input_r, someNatVal input_c, someNatVal output_r, someNatVal output_c, someNatVal pad_left, someNatVal pad_right, someNatVal pad_top, someNatVal pad_bottom ) of
383− ( Just (SomeNat (chan :: Proxy filters)),
384− Just (SomeNat (pinr :: Proxy inputRows)), Just (SomeNat (_ :: Proxy inputColumns)),
385− Just (SomeNat (_ :: Proxy outputRows)), Just (SomeNat (_ :: Proxy outputColumns)),
386− Just (SomeNat (_ :: Proxy padLeft)), Just (SomeNat (_ :: Proxy padRight)),
387− Just (SomeNat (_ :: Proxy padTop)), Just (SomeNat (_ :: Proxy padBottom))) ->
388− case ( natDict pinr %* natDict chan
389− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( ( 'D3 outputRows outputColumns filters) ~ h ))
390− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( (inputRows + padTop + padBottom) ~ outputRows) )
391− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( (inputColumns + padLeft + padRight) ~ outputColumns ))) of
392− (Dict, Dict, Dict, Dict) ->
393− pure (SomeNetwork (Pad :~> rest :: Network ( Pad padLeft padTop padRight padBottom ': layers ) ( ('D3 inputRows inputColumns filters) ': h ': hs )))
394− _ -> Gen.discard -- Can't occur
395− , do -- Build a Crop layer
396− let output_r = fromIntegral $ natVal r
397− let output_c = fromIntegral $ natVal c
398− let output_f = fromIntegral $ natVal f
399−
400− crop_left <- choose 0 10
401− crop_right <- choose 0 10
402− crop_top <- choose 0 10
403− crop_bottom <- choose 0 10
404−
405− let input_r = output_r + crop_top + crop_bottom
406− let input_c = output_c + crop_left + crop_right
407−
408− -- Remake types for input
409− case ( someNatVal output_f, someNatVal input_r, someNatVal input_c, someNatVal output_r, someNatVal output_c, someNatVal crop_left, someNatVal crop_right, someNatVal crop_top, someNatVal crop_bottom ) of
410− ( Just (SomeNat (chan :: Proxy filters)),
411− Just (SomeNat (pinr :: Proxy inputRows)), Just (SomeNat (_ :: Proxy inputColumns)),
412− Just (SomeNat (_ :: Proxy outputRows)), Just (SomeNat (_ :: Proxy outputColumns)),
413− Just (SomeNat (_ :: Proxy cropLeft)), Just (SomeNat (_ :: Proxy cropRight)),
414− Just (SomeNat (_ :: Proxy cropTop)), Just (SomeNat (_ :: Proxy cropBottom))) ->
415− case ( natDict pinr %* natDict chan
416− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( ( 'D3 outputRows outputColumns filters) ~ h ))
417− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( (outputRows + cropTop + cropBottom) ~ inputRows ) )
418− , (unsafeCoerce (Dict :: Dict ()) :: Dict ( (outputColumns + cropLeft + cropRight) ~ inputColumns ))) of
419− (Dict, Dict, Dict, Dict) ->
420− pure (SomeNetwork (Crop :~> rest :: Network ( Crop cropLeft cropTop cropRight cropBottom ': layers ) ( ('D3 inputRows inputColumns filters) ': h ': hs )))
421− _ -> Gen.discard -- Can't occur
422− ]
224+ ]
423225 ]
424226
425227-- | Test a partial derivative numerically for a random network and input
426228--
427229-- This is the most important test.
428230prop_auto_diff :: Property
429−prop_auto_diff = withDiscards 1000 . withTests 10000 . property $ do
231+prop_auto_diff = withTests 10000 . property $ do
430232 SomeNetwork (network :: Network layers shapes) <- forAll genNetwork
431− (input :: S (Head shapes)) <- forAllWith nice genOfShape
432− (target :: S (Last shapes)) <- forAllWith nice oneUp
433− (tested :: S (Head shapes)) <- forAllWith nice oneUp
233+ (input :: S (Head shapes)) <- forAllRender nice genOfShape
234+ (target :: S (Last shapes)) <- forAllRender nice oneUp
235+ (tested :: S (Head shapes)) <- forAllRender nice oneUp
434236
435237 let (!tapes, !output) = runNetwork network input
436238 let (_, !backgrad) = runGradient network tapes target
@@ -441,14 +243,16 @@ prop_auto_diff = withDiscards 1000 . withTests 10000 . property $ do
441243
442244 result ~~~ expected
443245
246+ where
247+
444248-- Make a shape where all are 0 except for 1 value, which is 1.
445−oneUp :: forall shape. ( SingI shape ) => Gen (S shape)
249+oneUp :: forall shape m. ( Monad m, SingI shape ) => Gen.Gen m (S shape)
446250oneUp =
447251 case ( sing :: Sing shape ) of
448− D1Sing SNat ->
252+ D1Sing l -> withKnownNat l $
449253 let x = 0 :: S ( shape )
450254 in case x of
451− ( S1D x' ) -> do
255+ ( S1D x' ) -> do
452256 let ex = extract x'
453257 let len = VS.length ex
454258 ix <- choose 0 (len - 1)
@@ -457,10 +261,10 @@ oneUp =
457261 VS.freeze ex'
458262 maybe Gen.discard pure . fromStorable $ nx
459263
460− D2Sing SNat SNat ->
264+ D2Sing r c -> withKnownNat r $ withKnownNat c $
461265 let x = 0 :: S ( shape )
462266 in case x of
463− ( S2D x' ) -> do
267+ ( S2D x' ) -> do
464268 let ex = flatten ( extract x' )
465269 let len = VS.length ex
466270 ix <- choose 0 (len - 1)
@@ -469,10 +273,10 @@ oneUp =
469273 VS.freeze ex'
470274 maybe Gen.discard pure . fromStorable $ nx
471275
472− D3Sing SNat SNat SNat ->
276+ D3Sing r c d -> withKnownNat r $ withKnownNat c $ withKnownNat d $
473277 let x = 0 :: S ( shape )
474278 in case x of
475− ( S3D x' ) -> do
279+ ( S3D x' ) -> do
476280 let ex = flatten ( extract x' )
477281 let len = VS.length ex
478282 ix <- choose 0 (len - 1)
@@ -486,9 +290,9 @@ maxVal ( S1D x ) = norm_Inf x
486290maxVal ( S2D x ) = norm_Inf x
487291maxVal ( S3D x ) = norm_Inf x
488292
489−(~~~) :: (Monad m, HasCallStack) => Double -> Double -> PropertyT m ()
293+(~~~) :: (Monad m, HasCallStack) => Double -> Double -> Test m ()
490294(~~~) x y =
491− if abs (x - y) < 2e-5 then
295+ if abs (x - y) < 1e-5 then
492296 success
493297 else
494298 withFrozenCallStack $
@@ -501,4 +305,4 @@ infix 4 ~~~
501305
502306
503307tests :: IO Bool
504−tests = checkParallel $$(discover)
308+tests = $$(checkConcurrent)