@@ -14,17 +14,12 @@ import Control.Monad ( guard )
1414import Control.Monad.ST ( runST )
1515
1616import Data.Constraint
17−#if __GLASGOW_HASKELL__ < 800
18−import Data.Proxy
19−#endif
2017import qualified Data.Vector.Storable as VS
2118import qualified Data.Vector.Storable.Mutable as VS ( write )
2219import Data.Singletons
2320import Data.Singletons.Prelude.List
2421import Data.Singletons.TypeLits
2522
26−-- import Data.Type.Equality
27−
2823import Hedgehog
2924import qualified Hedgehog.Gen as Gen
3025import Hedgehog.Internal.Source
@@ -32,7 +27,15 @@ import Hedgehog.Internal.Property ( failWith )
3227
3328import Grenade
3429
30+#if MIN_VERSION_base(4,11,0)
31+import GHC.TypeLits hiding (natVal)
32+#else
3533import GHC.TypeLits
34+#endif
35+#if MIN_VERSION_base(4,9,0)
36+import Data.Kind (Type)
37+#endif
38+
3639import GHC.TypeLits.Witnesses
3740import Test.Hedgehog.Compat
3841import Test.Hedgehog.TypeLits
@@ -43,7 +46,7 @@ import Numeric.LinearAlgebra ( flatten )
4346import Numeric.LinearAlgebra.Static ( extract, norm_Inf )
4447import Unsafe.Coerce
4548
46−data SomeNetwork :: * where
49+data SomeNetwork :: Type where
4750 SomeNetwork :: ( SingI shapes, SingI (Head shapes), SingI (Last shapes), Show (Network layers shapes) ) => Network layers shapes -> SomeNetwork
4851
4952instance Show SomeNetwork where
@@ -53,7 +56,7 @@ instance Show SomeNetwork where
5356--
5457-- This is slightly insane for a few reasons. Everything must be wrapped up
5558-- in a SomeNetwork.
56−genNetwork :: Monad m => Gen.Gen m SomeNetwork
59+genNetwork :: Gen SomeNetwork
5760genNetwork =
5861 Gen.recursive Gen.choice [
5962 do SomeSing ( r :: Sing final ) <- genShape
@@ -62,11 +65,11 @@ genNetwork =
6265 ] [
6366 do SomeNetwork ( rest :: Network layers shapes ) <- genNetwork
6467 case ( sing :: Sing shapes ) of
65− SNil -> Gen.discard
68+ SNil -> Gen.discard -- Can't occur
6669 SCons ( h :: Sing h ) ( _ :: Sing hs ) ->
6770 withSingI h $
6871 case h of
69− D1Sing l -> withKnownNat l $
72+ D1Sing l@SNat ->
7073 Gen.choice [
7174 pure (SomeNetwork (Tanh :~> rest :: Network ( Tanh ': layers ) ( h ': h ': hs )))
7275 , pure (SomeNetwork (Logit :~> rest :: Network ( Logit ': layers ) ( h ': h ': hs )))
@@ -74,8 +77,9 @@ genNetwork =
7477 , pure (SomeNetwork (Elu :~> rest :: Network ( Elu ': layers ) ( h ': h ': hs )))
7578 , pure (SomeNetwork (Softmax :~> rest :: Network ( Softmax ': layers ) ( h ': h ': hs )))
7679 , do -- Reshape to two dimensions
77− let divisors n = 1 : [x | x <- [2..(n-1)], n `rem` x == 0]
78− let len = natVal l
80+ let divisors :: Integer -> [Integer]
81+ divisors n = 1 : [x | x <- [2..(n-1)], n `rem` x == 0]
82+ let len = fromIntegral $ natVal l
7983 rs <- Gen.element $ divisors len
8084 let cs = len `quot` rs
8185 case ( someNatVal rs, someNatVal cs, someNatVal len ) of
@@ -87,7 +91,7 @@ genNetwork =
8791 pure (SomeNetwork (Reshape :~> rest :: Network ( Reshape ': layers ) ( ('D2 inRows inCols) ': h ': hs )))
8892 _ -> Gen.discard -- Doesn't occur
8993 ]
90− D2Sing r c -> withKnownNat r $ withKnownNat c $
94+ D2Sing r@SNat c@SNat ->
9195 Gen.choice [
9296 pure (SomeNetwork (Tanh :~> rest :: Network ( Tanh ': layers ) ( h ': h ': hs )))
9397 , pure (SomeNetwork (Logit :~> rest :: Network ( Logit ': layers ) ( h ': h ': hs )))
@@ -96,8 +100,8 @@ genNetwork =
96100 , do -- Build a convolution layer with one filter output
97101 -- Figure out some kernel sizes which work for this layer
98102 -- There must be a better way than this...
99− let output_r = natVal r
100− let output_c = natVal c
103+ let output_r = fromIntegral $ natVal r
104+ let output_c = fromIntegral $ natVal c
101105
102106 let ok extent kernel = [stride | stride <- [ 1 .. extent ], (extent - kernel) `mod` stride == 0]
103107
@@ -136,8 +140,8 @@ genNetwork =
136140 , do -- Build a convolution layer with one filter output
137141 -- Figure out some kernel sizes which work for this layer
138142 -- There must be a better way than this...
139− let output_r = natVal r
140− let output_c = natVal c
143+ let output_r = fromIntegral $ natVal r
144+ let output_c = fromIntegral $ natVal c
141145
142146 let ok extent kernel = [stride | stride <- [ 1 .. extent ], (extent - kernel) `mod` stride == 0]
143147
@@ -178,8 +182,8 @@ genNetwork =
178182 pure (SomeNetwork (conv :~> rest :: Network ( Convolution channels 1 kernelRows kernelCols strideRows strideCols ': layers ) ( ('D3 inRows inCols channels) ': h ': hs )))
179183 _ -> Gen.discard -- Can't occur
180184 , do -- Build a Pooling layer
181− let output_r = natVal r
182− let output_c = natVal c
185+ let output_r = fromIntegral $ natVal r
186+ let output_c = fromIntegral $ natVal c
183187
184188 let ok extent kernel = [stride | stride <- [ 1 .. extent ], (extent - kernel) `mod` stride == 0]
185189
@@ -214,25 +218,219 @@ genNetwork =
214218 (Dict, Dict, Dict, Dict) ->
215219 pure (SomeNetwork (Pooling :~> rest :: Network ( Pooling kernelRows kernelCols strideRows strideCols ': layers ) ( ('D2 inRows inCols) ': h ': hs )))
216220 _ -> 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
217272 ]
218− D3Sing r c d -> withKnownNat d $ withKnownNat r $ withKnownNat c $
273+ D3Sing r@SNat c@SNat f@SNat ->
219274 Gen.choice [
220275 pure (SomeNetwork (Tanh :~> rest :: Network ( Tanh ': layers ) ( h ': h ': hs )))
221276 , pure (SomeNetwork (Logit :~> rest :: Network ( Logit ': layers ) ( h ': h ': hs )))
222277 , pure (SomeNetwork (Relu :~> rest :: Network ( Relu ': layers ) ( h ': h ': hs )))
223278 , pure (SomeNetwork (Elu :~> rest :: Network ( Elu ': layers ) ( h ': h ': hs )))
224− ]
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+ ]
225423 ]
226424
227425-- | Test a partial derivative numerically for a random network and input
228426--
229427-- This is the most important test.
230428prop_auto_diff :: Property
231−prop_auto_diff = withTests 10000 . property $ do
429+prop_auto_diff = withDiscards 1000 . withTests 10000 . property $ do
232430 SomeNetwork (network :: Network layers shapes) <- forAll genNetwork
233− (input :: S (Head shapes)) <- forAllRender nice genOfShape
234− (target :: S (Last shapes)) <- forAllRender nice oneUp
235− (tested :: S (Head shapes)) <- forAllRender nice oneUp
431+ (input :: S (Head shapes)) <- forAllWith nice genOfShape
432+ (target :: S (Last shapes)) <- forAllWith nice oneUp
433+ (tested :: S (Head shapes)) <- forAllWith nice oneUp
236434
237435 let (!tapes, !output) = runNetwork network input
238436 let (_, !backgrad) = runGradient network tapes target
@@ -243,16 +441,14 @@ prop_auto_diff = withTests 10000 . property $ do
243441
244442 result ~~~ expected
245443
246− where
247−
248444-- Make a shape where all are 0 except for 1 value, which is 1.
249−oneUp :: forall shape m. ( Monad m, SingI shape ) => Gen.Gen m (S shape)
445+oneUp :: forall shape. ( SingI shape ) => Gen (S shape)
250446oneUp =
251447 case ( sing :: Sing shape ) of
252− D1Sing l -> withKnownNat l $
448+ D1Sing SNat ->
253449 let x = 0 :: S ( shape )
254450 in case x of
255− ( S1D x' ) -> do
451+ ( S1D x' ) -> do
256452 let ex = extract x'
257453 let len = VS.length ex
258454 ix <- choose 0 (len - 1)
@@ -261,10 +457,10 @@ oneUp =
261457 VS.freeze ex'
262458 maybe Gen.discard pure . fromStorable $ nx
263459
264− D2Sing r c -> withKnownNat r $ withKnownNat c $
460+ D2Sing SNat SNat ->
265461 let x = 0 :: S ( shape )
266462 in case x of
267− ( S2D x' ) -> do
463+ ( S2D x' ) -> do
268464 let ex = flatten ( extract x' )
269465 let len = VS.length ex
270466 ix <- choose 0 (len - 1)
@@ -273,10 +469,10 @@ oneUp =
273469 VS.freeze ex'
274470 maybe Gen.discard pure . fromStorable $ nx
275471
276− D3Sing r c d -> withKnownNat r $ withKnownNat c $ withKnownNat d $
472+ D3Sing SNat SNat SNat ->
277473 let x = 0 :: S ( shape )
278474 in case x of
279− ( S3D x' ) -> do
475+ ( S3D x' ) -> do
280476 let ex = flatten ( extract x' )
281477 let len = VS.length ex
282478 ix <- choose 0 (len - 1)
@@ -290,9 +486,9 @@ maxVal ( S1D x ) = norm_Inf x
290486maxVal ( S2D x ) = norm_Inf x
291487maxVal ( S3D x ) = norm_Inf x
292488
293−(~~~) :: (Monad m, HasCallStack) => Double -> Double -> Test m ()
489+(~~~) :: (Monad m, HasCallStack) => Double -> Double -> PropertyT m ()
294490(~~~) x y =
295− if abs (x - y) < 1e-5 then
491+ if abs (x - y) < 2e-5 then
296492 success
297493 else
298494 withFrozenCallStack $
@@ -305,4 +501,4 @@ infix 4 ~~~
305501
306502
307503tests :: IO Bool
308−tests = $$(checkConcurrent)
504+tests = checkParallel $$(discover)