mirror of
https://github.com/Jaxan/ons-hs.git
synced 2025-04-27 06:37:44 +02:00
Updates testing and benchmarking
This commit is contained in:
parent
d0540fe073
commit
9575a62e56
7 changed files with 106 additions and 73 deletions
|
@ -114,6 +114,9 @@ values, that can be much faster.
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
version 0.2.1.0 (2024-11-04):
|
||||||
|
* Updates the testing and benchmarking framework.
|
||||||
|
|
||||||
version 0.2.0.0 (2024-11-01):
|
version 0.2.0.0 (2024-11-01):
|
||||||
* Resolves compiler warnings.
|
* Resolves compiler warnings.
|
||||||
* Moved from own `Generic` to `GHC.Generically` (needs base 4.17+). If you want
|
* Moved from own `Generic` to `GHC.Generically` (needs base 4.17+). If you want
|
||||||
|
|
6
cabal.project.local
Normal file
6
cabal.project.local
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package *
|
||||||
|
optimization: True
|
||||||
|
|
||||||
|
package ons-hs
|
||||||
|
-- profiling: True
|
||||||
|
ghc-options: -Wall -fexpose-all-unfoldings -fspecialise-aggressively
|
12
ons-hs.cabal
12
ons-hs.cabal
|
@ -1,6 +1,6 @@
|
||||||
cabal-version: 2.2
|
cabal-version: 2.2
|
||||||
name: ons-hs
|
name: ons-hs
|
||||||
version: 0.2.0.0
|
version: 0.2.1.0
|
||||||
synopsis: Implementation of the ONS (Ordered Nominal Sets) library in Haskell
|
synopsis: Implementation of the ONS (Ordered Nominal Sets) library in Haskell
|
||||||
description: Nominal sets are structured infinite sets. They have symmetries which make them finitely representable. This library provides basic manipulation of them for the total order symmetry. It includes: products, sums, maps and sets. Can work with custom data types.
|
description: Nominal sets are structured infinite sets. They have symmetries which make them finitely representable. This library provides basic manipulation of them for the total order symmetry. It includes: products, sums, maps and sets. Can work with custom data types.
|
||||||
homepage: https://github.com/Jaxan/ons-hs
|
homepage: https://github.com/Jaxan/ons-hs
|
||||||
|
@ -75,16 +75,20 @@ benchmark ons-hs-bench
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
main-is: Bench.hs
|
main-is: Bench.hs
|
||||||
build-depends:
|
build-depends:
|
||||||
criterion,
|
|
||||||
deepseq,
|
deepseq,
|
||||||
ons-hs
|
ons-hs,
|
||||||
|
tasty-bench
|
||||||
|
|
||||||
test-suite ons-hs-test
|
test-suite ons-hs-test
|
||||||
import: stuff
|
import: stuff
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
main-is: Spec.hs
|
main-is: Spec.hs
|
||||||
build-depends: ons-hs
|
build-depends:
|
||||||
|
ons-hs,
|
||||||
|
tasty,
|
||||||
|
tasty-hunit,
|
||||||
|
tasty-quickcheck
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
|
|
|
@ -32,8 +32,3 @@ separatedProduct pa pb oa ob = OrbPair (OrbRec oa) (OrbRec ob) <$> sepProdString
|
||||||
-- "Left product": A |x B = { (a,b) | C supports a => C supports b }
|
-- "Left product": A |x B = { (a,b) | C supports a => C supports b }
|
||||||
leftProduct :: (Nominal a, Nominal b) => Proxy a -> Proxy b -> Orbit a -> Orbit b -> [Orbit (a,b)]
|
leftProduct :: (Nominal a, Nominal b) => Proxy a -> Proxy b -> Orbit a -> Orbit b -> [Orbit (a,b)]
|
||||||
leftProduct pa pb oa ob = OrbPair (OrbRec oa) (OrbRec ob) <$> rincProdStrings (index pa oa) (index pb ob)
|
leftProduct pa pb oa ob = OrbPair (OrbRec oa) (OrbRec ob) <$> rincProdStrings (index pa oa) (index pb ob)
|
||||||
|
|
||||||
{-# INLINABLE product #-}
|
|
||||||
{-# INLINABLE separatedProduct #-}
|
|
||||||
{-# INLINABLE leftProduct #-}
|
|
||||||
|
|
||||||
|
|
|
@ -27,15 +27,13 @@ rincProdStrings = memo2 gen where
|
||||||
gen n 0 = pure $ replicate n LT
|
gen n 0 = pure $ replicate n LT
|
||||||
gen 0 _ = empty
|
gen 0 _ = empty
|
||||||
gen 1 1 = pure [EQ]
|
gen 1 1 = pure [EQ]
|
||||||
gen n m
|
gen n m
|
||||||
| n < m = empty
|
| n < m = empty
|
||||||
| otherwise = (LT :) <$> rincProdStrings (n-1) m
|
| otherwise = (LT :) <$> rincProdStrings (n-1) m
|
||||||
<|> (EQ :) <$> rincProdStrings (n-1) (m-1)
|
<|> (EQ :) <$> rincProdStrings (n-1) (m-1)
|
||||||
|
|
||||||
{-# INLINABLE prodStrings #-}
|
{- NOTE on performance:
|
||||||
{-# INLINABLE sepProdStrings #-}
|
Previously, I had INLINABLE and SPECIALIZE pragmas for all above definitions.
|
||||||
{-# INLINABLE rincProdStrings #-}
|
But with benchmarking, I concluded that they do not make any difference. So
|
||||||
|
I have removed them. The memoisation does seem to help. So that stays.
|
||||||
{-# SPECIALIZE prodStrings :: Int -> Int -> [[Ordering]] #-}
|
-}
|
||||||
{-# SPECIALIZE sepProdStrings :: Int -> Int -> [[Ordering]] #-}
|
|
||||||
{-# SPECIALIZE rincProdStrings :: Int -> Int -> [[Ordering]] #-}
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
{-# LANGUAGE PartialTypeSignatures #-}
|
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
|
{-# LANGUAGE PartialTypeSignatures #-}
|
||||||
|
{-# OPTIONS_GHC -Wno-partial-type-signatures -Wno-orphans #-}
|
||||||
|
|
||||||
import Control.DeepSeq
|
import Control.DeepSeq
|
||||||
import Criterion.Main
|
import Test.Tasty.Bench
|
||||||
|
|
||||||
import Nominal
|
|
||||||
import Support
|
|
||||||
import EquivariantSet
|
|
||||||
import EquivariantMap
|
import EquivariantMap
|
||||||
|
import EquivariantSet
|
||||||
|
import Nominal
|
||||||
|
import OrbitList (repeatRationals, size)
|
||||||
|
import Support
|
||||||
|
|
||||||
instance NFData Rat
|
instance NFData Rat
|
||||||
|
|
||||||
|
@ -16,35 +17,41 @@ instance NFData Rat
|
||||||
(\/) = EquivariantSet.union
|
(\/) = EquivariantSet.union
|
||||||
|
|
||||||
bigset :: (Rat, Rat, Rat, _) -> Bool
|
bigset :: (Rat, Rat, Rat, _) -> Bool
|
||||||
bigset (p, q, r, t) = EquivariantSet.member t s where
|
bigset (p, q, r, t) = EquivariantSet.member t s
|
||||||
|
where
|
||||||
s1 = singleOrbit ((p, p), p) \/ singleOrbit ((p, p), q) \/ singleOrbit ((p, q), r)
|
s1 = singleOrbit ((p, p), p) \/ singleOrbit ((p, p), q) \/ singleOrbit ((p, q), r)
|
||||||
s2 = singleOrbit (p, q) \/ singleOrbit (q, r) \/ singleOrbit (r, p)
|
s2 = singleOrbit (p, q) \/ singleOrbit (q, r) \/ singleOrbit (r, p)
|
||||||
s = EquivariantSet.product s1 s2
|
s = EquivariantSet.product s1 s2
|
||||||
|
|
||||||
bigmap :: (Rat, Rat, _) -> Maybe (Rat, (Rat, Rat))
|
bigmap :: (Rat, Rat, _) -> Maybe (Rat, (Rat, Rat))
|
||||||
bigmap (p, q, t) = EquivariantMap.lookup t m3 where
|
bigmap (p, q, t) = EquivariantMap.lookup t m3
|
||||||
|
where
|
||||||
s = EquivariantSet.product (EquivariantSet.singleOrbit (p, q)) (EquivariantSet.singleOrbit (q, p))
|
s = EquivariantSet.product (EquivariantSet.singleOrbit (p, q)) (EquivariantSet.singleOrbit (q, p))
|
||||||
s2 = EquivariantSet.product s s
|
s2 = EquivariantSet.product s s
|
||||||
s3 = EquivariantSet.map (\((a, b), (c, d)) -> ((b, a), (d, c))) s2
|
s3 = EquivariantSet.map (\((a, b), (c, d)) -> ((b, a), (d, c))) s2
|
||||||
m1 = EquivariantMap.fromSet (\(((a, b), (c, d)), ((e, f), (g, h))) -> (b,(d,h))) s2
|
m1 = EquivariantMap.fromSet (\(((_, b), (_, d)), (_, (_, h))) -> (b, (d, h))) s2
|
||||||
m2 = EquivariantMap.fromSet (\(((a, b), (c, d)), ((e, f), (g, h))) -> (b,(d,h))) s3
|
m2 = EquivariantMap.fromSet (\(((_, b), (_, d)), (_, (_, h))) -> (b, (d, h))) s3
|
||||||
m3 = EquivariantMap.unionWith const m1 m2
|
m3 = EquivariantMap.unionWith const m1 m2
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = defaultMain
|
main =
|
||||||
-- ~ 300 ms
|
defaultMain
|
||||||
[ bgroup "bigmap"
|
[ bgroup
|
||||||
[ bench "1 y" $ nf bigmap (Rat 1, Rat 2, (((Rat 1, Rat 23), (Rat 5, Rat 4)), ((Rat 2, Rat 3), (Rat 54, Rat 43)))) -- found
|
"bigmap"
|
||||||
, bench "2 n" $ nf bigmap (Rat 1, Rat 2, (((Rat 1, Rat 23), (Rat 5, Rat 4)), ((Rat 2, Rat 3), (Rat 54, Rat 65)))) -- not found
|
[ bench "1 y" $ nf bigmap (Rat 1, Rat 2, (((Rat 1, Rat 23), (Rat 5, Rat 4)), ((Rat 2, Rat 3), (Rat 54, Rat 43)))) -- found
|
||||||
, bench "3 y" $ nf bigmap (Rat 1, Rat 2, (((Rat 1, Rat 100), (Rat 90, Rat 20)), ((Rat 30, Rat 80), (Rat 70, Rat 65)))) -- found
|
, bench "2 n" $ nf bigmap (Rat 1, Rat 2, (((Rat 1, Rat 23), (Rat 5, Rat 4)), ((Rat 2, Rat 3), (Rat 54, Rat 65)))) -- not found
|
||||||
, bench "4 y" $ nf bigmap (Rat 1, Rat 2, (((Rat 1, Rat 100), (Rat 100, Rat 1)), ((Rat 1, Rat 100), (Rat 100, Rat 1)))) -- found
|
, bench "3 y" $ nf bigmap (Rat 1, Rat 2, (((Rat 1, Rat 100), (Rat 90, Rat 20)), ((Rat 30, Rat 80), (Rat 70, Rat 65)))) -- found
|
||||||
, bench "5 y" $ nf bigmap (Rat 1, Rat 2, (((Rat 100, Rat 1), (Rat 1, Rat 100)), ((Rat 200, Rat 2), (Rat 2, Rat 200)))) -- found
|
, bench "4 y" $ nf bigmap (Rat 1, Rat 2, (((Rat 1, Rat 100), (Rat 100, Rat 1)), ((Rat 1, Rat 100), (Rat 100, Rat 1)))) -- found
|
||||||
]
|
, bench "5 y" $ nf bigmap (Rat 1, Rat 2, (((Rat 100, Rat 1), (Rat 1, Rat 100)), ((Rat 200, Rat 2), (Rat 2, Rat 200)))) -- found
|
||||||
-- ~ 13 us
|
]
|
||||||
, bgroup "bigset"
|
, bgroup
|
||||||
[ bench "1 y" $ nf bigset (Rat 1, Rat 2, Rat 3, ( ((Rat 1, Rat 1), Rat 1), (Rat 1, Rat 2) )) -- found
|
"bigset"
|
||||||
, bench "2 y" $ nf bigset (Rat 1, Rat 2, Rat 3, ( ((Rat 37, Rat 37), Rat 42), (Rat 1, Rat 2) )) -- found
|
[ bench "1 y" $ nf bigset (Rat 1, Rat 2, Rat 3, (((Rat 1, Rat 1), Rat 1), (Rat 1, Rat 2))) -- found
|
||||||
, bench "3 n" $ nf bigset (Rat 1, Rat 2, Rat 3, ( ((Rat 37, Rat 31), Rat 42), (Rat 1, Rat 2) )) -- not found
|
, bench "2 y" $ nf bigset (Rat 1, Rat 2, Rat 3, (((Rat 37, Rat 37), Rat 42), (Rat 1, Rat 2))) -- found
|
||||||
, bench "4 y" $ nf bigset (Rat 1, Rat 2, Rat 3, ( ((Rat 1, Rat 2), Rat 3), (Rat 5, Rat 4) )) -- found
|
, bench "3 n" $ nf bigset (Rat 1, Rat 2, Rat 3, (((Rat 37, Rat 31), Rat 42), (Rat 1, Rat 2))) -- not found
|
||||||
]
|
, bench "4 y" $ nf bigset (Rat 1, Rat 2, Rat 3, (((Rat 1, Rat 2), Rat 3), (Rat 5, Rat 4))) -- found
|
||||||
]
|
]
|
||||||
|
, bgroup
|
||||||
|
"counting orbits"
|
||||||
|
[bench (show i) $ nf (sum . OrbitList.size . repeatRationals) i | i <- [1 .. 7]]
|
||||||
|
]
|
||||||
|
|
76
test/Spec.hs
76
test/Spec.hs
|
@ -1,25 +1,27 @@
|
||||||
{-# LANGUAGE PartialTypeSignatures #-}
|
{-# LANGUAGE PartialTypeSignatures #-}
|
||||||
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
|
{-# OPTIONS_GHC -Wno-partial-type-signatures -Wno-orphans #-}
|
||||||
|
|
||||||
-- TODO: QuickCheck instead of these unit tests
|
|
||||||
|
|
||||||
import GHC.Stack
|
|
||||||
|
|
||||||
import Data.Maybe (isJust, isNothing)
|
import Data.Maybe (isJust, isNothing)
|
||||||
import Prelude (id, const, not, ($), error, return, Bool(..), IO(..), print, (>>=))
|
import Test.Tasty
|
||||||
import qualified Prelude as P -- hide stuff
|
import Test.Tasty.HUnit hiding (assert)
|
||||||
|
import Test.Tasty.QuickCheck as QC
|
||||||
|
import Prelude (Bool (..), Eq (..), IO, Int, const, id, length, not, show, (!!), ($), (<$>))
|
||||||
|
|
||||||
import Support (Rat(..))
|
import EquivariantMap (fromSet, lookup, unionWith)
|
||||||
import EquivariantSet (product, member, singleOrbit, union, map, isSubsetOf)
|
import EquivariantSet (isSubsetOf, map, member, product, singleOrbit, union)
|
||||||
import EquivariantMap (unionWith, lookup, fromSet)
|
import Nominal (Nominal (..))
|
||||||
|
import OrbitList (repeatRationals, size)
|
||||||
|
import Support (Rat (..))
|
||||||
|
|
||||||
assert :: (a -> Bool) -> a -> IO ()
|
assert :: HasCallStack => (a -> Bool) -> a -> IO ()
|
||||||
assert f x = case f x of
|
assert f x = assertBool "" (f x)
|
||||||
True -> return ()
|
|
||||||
False -> whoCreated x >>= print
|
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = defaultMain (testGroup "main" [unitTests, countingTests, qcTests])
|
||||||
|
|
||||||
|
|
||||||
|
unitTests :: _
|
||||||
|
unitTests = testCase "Examples" $ do
|
||||||
let p = Rat 1
|
let p = Rat 1
|
||||||
let q = Rat 2
|
let q = Rat 2
|
||||||
let s = product (singleOrbit (p, q)) (singleOrbit (q, p))
|
let s = product (singleOrbit (p, q)) (singleOrbit (q, p))
|
||||||
|
@ -39,12 +41,12 @@ main = do
|
||||||
assert id $ member (((Rat 5, Rat 4), (Rat 1, Rat 2)), ((Rat 5, Rat 4), (Rat 1, Rat 2))) s3
|
assert id $ member (((Rat 5, Rat 4), (Rat 1, Rat 2)), ((Rat 5, Rat 4), (Rat 1, Rat 2))) s3
|
||||||
assert id $ member (((Rat 2, Rat 1), (Rat 4, Rat 5)), ((Rat 2, Rat 1), (Rat 4, Rat 5))) s3
|
assert id $ member (((Rat 2, Rat 1), (Rat 4, Rat 5)), ((Rat 2, Rat 1), (Rat 4, Rat 5))) s3
|
||||||
|
|
||||||
let m1 = fromSet (\(((a, b), (c, d)), ((e, f), (g, h))) -> (b,(d,h))) s2
|
let m1 = fromSet (\(((_, b), (_, d)), (_, (_, h))) -> (b, (d, h))) s2
|
||||||
assert isJust $ lookup (((Rat 1, Rat 2), (Rat 2, Rat 1)), ((Rat 1, Rat 2), (Rat 3, Rat 2))) m1
|
assert isJust $ lookup (((Rat 1, Rat 2), (Rat 2, Rat 1)), ((Rat 1, Rat 2), (Rat 3, Rat 2))) m1
|
||||||
assert isNothing $ lookup (((Rat 1, Rat 2), (Rat 2, Rat 1)), ((Rat 1, Rat 2), (Rat 1, Rat 2))) m1
|
assert isNothing $ lookup (((Rat 1, Rat 2), (Rat 2, Rat 1)), ((Rat 1, Rat 2), (Rat 1, Rat 2))) m1
|
||||||
|
|
||||||
let m2 = fromSet (\(((a, b), (c, d)), ((e, f), (g, h))) -> (b,(d,h))) s3
|
let m2 = fromSet (\(((_, b), (_, d)), (_, (_, h))) -> (b, (d, h))) s3
|
||||||
assert isJust $ lookup (((Rat 6, Rat 1), (Rat 1, Rat 5)), ((Rat 4, Rat 1), (Rat 1, Rat 3))) m2
|
assert isJust $ lookup (((Rat 6, Rat 1), (Rat 1, Rat 5)), ((Rat 4, Rat 1), (Rat 1, Rat 3))) m2
|
||||||
assert isNothing $ lookup (((Rat 1, Rat 2), (Rat 2, Rat 1)), ((Rat 1, Rat 2), (Rat 4, Rat 2))) m2
|
assert isNothing $ lookup (((Rat 1, Rat 2), (Rat 2, Rat 1)), ((Rat 1, Rat 2), (Rat 4, Rat 2))) m2
|
||||||
|
|
||||||
let m3 = unionWith const m1 m2
|
let m3 = unionWith const m1 m2
|
||||||
|
@ -55,14 +57,32 @@ main = do
|
||||||
assert isJust $ lookup (((Rat 100, Rat 1), (Rat 1, Rat 100)), ((Rat 200, Rat 2), (Rat 2, Rat 200))) m3
|
assert isJust $ lookup (((Rat 100, Rat 1), (Rat 1, Rat 100)), ((Rat 200, Rat 2), (Rat 2, Rat 200))) m3
|
||||||
|
|
||||||
let r = Rat 3
|
let r = Rat 3
|
||||||
let s1 = singleOrbit ((p, p), p) `union` singleOrbit ((p, p), q) `union` singleOrbit ((p, q), r)
|
let s4 = singleOrbit ((p, p), p) `union` singleOrbit ((p, p), q) `union` singleOrbit ((p, q), r)
|
||||||
let s2 = singleOrbit (p, q) `union` singleOrbit (q, r) `union` singleOrbit (r, p)
|
let s5 = singleOrbit (p, q) `union` singleOrbit (q, r) `union` singleOrbit (r, p)
|
||||||
assert id $ s2 `isSubsetOf` product (singleOrbit p) (singleOrbit p)
|
assert id $ s5 `isSubsetOf` product (singleOrbit p) (singleOrbit p)
|
||||||
assert not $ product (singleOrbit p) (singleOrbit p) `isSubsetOf` s2
|
assert not $ product (singleOrbit p) (singleOrbit p) `isSubsetOf` s5
|
||||||
|
|
||||||
let s = product s1 s2
|
let s6 = product s4 s5
|
||||||
assert id $ member ( ((Rat 1, Rat 1), Rat 1), (Rat 1, Rat 2) ) s
|
assert id $ member (((Rat 1, Rat 1), Rat 1), (Rat 1, Rat 2)) s6
|
||||||
assert id $ member ( ((Rat 37, Rat 37), Rat 42), (Rat 1, Rat 2) ) s
|
assert id $ member (((Rat 37, Rat 37), Rat 42), (Rat 1, Rat 2)) s6
|
||||||
assert not $ member ( ((Rat 37, Rat 31), Rat 42), (Rat 1, Rat 2) ) s
|
assert not $ member (((Rat 37, Rat 31), Rat 42), (Rat 1, Rat 2)) s6
|
||||||
assert id $ member ( ((Rat 1, Rat 2), Rat 3), (Rat 5, Rat 4) ) s
|
assert id $ member (((Rat 1, Rat 2), Rat 3), (Rat 5, Rat 4)) s6
|
||||||
|
|
||||||
|
|
||||||
|
-- Verifying that the number of orbits is correct. Up to length 7, because
|
||||||
|
-- length 8 and longer take at least one second.
|
||||||
|
countingTests :: _
|
||||||
|
countingTests = testGroup "Counting" [testCase (show n) $ length (OrbitList.size (repeatRationals n)) @?= (a000670 !! n) | n <- [0..7]]
|
||||||
|
|
||||||
|
-- A000670: Ordered Bell numbers or Fubini numbers
|
||||||
|
a000670 :: [Int]
|
||||||
|
a000670 = [1, 1, 3, 13, 75, 541, 4683, 47293, 545835, 7087261, 102247563, 1622632573, 28091567595]
|
||||||
|
|
||||||
|
|
||||||
|
-- TODO: Add more quickcheck tests
|
||||||
|
qcTests :: _
|
||||||
|
qcTests = testGroup "QuickCheck" [QC.testProperty "all atoms in same orbit" $ \p q -> toOrbit (p :: Rat) == toOrbit (q :: Rat)]
|
||||||
|
|
||||||
|
instance Arbitrary Rat where
|
||||||
|
arbitrary = Rat <$> arbitrary
|
||||||
|
shrink (Rat p) = Rat <$> shrink p
|
||||||
|
|
Loading…
Add table
Reference in a new issue