algebraic-graphs-0.6: test/Algebra/Graph/Test/Arbitrary.hs
{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-----------------------------------------------------------------------------
-- |
-- Module : Algebra.Graph.Test.Arbitrary
-- Copyright : (c) Andrey Mokhov 2016-2021
-- License : MIT (see the file LICENSE)
-- Maintainer : andrey.mokhov@gmail.com
-- Stability : experimental
--
-- Generators and orphan Arbitrary instances for various data types.
-----------------------------------------------------------------------------
module Algebra.Graph.Test.Arbitrary (
-- * Generators of arbitrary graph instances
arbitraryGraph, arbitraryRelation, arbitraryAdjacencyMap,
) where
import Control.Monad
import Data.List.NonEmpty (NonEmpty (..), toList)
import Data.Maybe (catMaybes)
import Data.Tree
import Test.QuickCheck
import Algebra.Graph
import Algebra.Graph.Export
import Algebra.Graph.Label
import qualified Algebra.Graph.Undirected as UG
import qualified Algebra.Graph.Acyclic.AdjacencyMap as AAM
import qualified Algebra.Graph.AdjacencyIntMap as AIM
import qualified Algebra.Graph.AdjacencyMap as AM
import qualified Algebra.Graph.Bipartite.AdjacencyMap as BAM
import qualified Algebra.Graph.Bipartite.AdjacencyMap.Algorithm as BAMA
import qualified Algebra.Graph.NonEmpty.AdjacencyMap as NAM
import qualified Algebra.Graph.Class as C
import qualified Algebra.Graph.Labelled as LG
import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM
import qualified Algebra.Graph.NonEmpty as NonEmpty
import qualified Algebra.Graph.Relation as Relation
import qualified Algebra.Graph.Relation.Preorder as Preorder
import qualified Algebra.Graph.Relation.Reflexive as Reflexive
import qualified Algebra.Graph.Relation.Symmetric as Symmetric
import qualified Algebra.Graph.Relation.Transitive as Transitive
-- | Generate an arbitrary 'C.Graph' value of a specified size.
arbitraryGraph :: (C.Graph g, Arbitrary (C.Vertex g)) => Gen g
arbitraryGraph = sized expr
where
expr 0 = return C.empty
expr 1 = C.vertex <$> arbitrary
expr n = do
left <- choose (0, n)
oneof [ C.overlay <$> expr left <*> expr (n - left)
, C.connect <$> expr left <*> expr (n - left) ]
instance Arbitrary a => Arbitrary (Graph a) where
arbitrary = arbitraryGraph
shrink Empty = []
shrink (Vertex _) = [Empty]
shrink (Overlay x y) = [Empty, x, y]
++ [Overlay x' y' | (x', y') <- shrink (x, y) ]
shrink (Connect x y) = [Empty, x, y, Overlay x y]
++ [Connect x' y' | (x', y') <- shrink (x, y) ]
-- An Arbitrary instance for Graph.Undirected
instance Arbitrary a => Arbitrary (UG.Graph a) where
arbitrary = arbitraryGraph
-- An Arbitrary instance for Acyclic.AdjacencyMap
instance (Ord a, Arbitrary a) => Arbitrary (AAM.AdjacencyMap a) where
arbitrary = AAM.shrink <$> arbitrary
shrink g = shrinkVertices ++ shrinkEdges
where
shrinkVertices =
let vertices = AAM.vertexList g
in [ AAM.removeVertex x g | x <- vertices ]
shrinkEdges =
let edges = AAM.edgeList g
in [ AAM.removeEdge x y g | (x, y) <- edges ]
-- | Generate an arbitrary 'NonEmpty.Graph' value of a specified size.
arbitraryNonEmptyGraph :: Arbitrary a => Gen (NonEmpty.Graph a)
arbitraryNonEmptyGraph = sized expr
where
expr 0 = NonEmpty.vertex <$> arbitrary -- can't generate non-empty graph of size 0
expr 1 = NonEmpty.vertex <$> arbitrary
expr n = do
left <- choose (1, n)
oneof [ NonEmpty.overlay <$> expr left <*> expr (n - left)
, NonEmpty.connect <$> expr left <*> expr (n - left) ]
instance Arbitrary a => Arbitrary (NonEmpty.Graph a) where
arbitrary = arbitraryNonEmptyGraph
shrink (NonEmpty.Vertex _) = []
shrink (NonEmpty.Overlay x y) = [x, y]
++ [NonEmpty.Overlay x' y' | (x', y') <- shrink (x, y) ]
shrink (NonEmpty.Connect x y) = [x, y, NonEmpty.Overlay x y]
++ [NonEmpty.Connect x' y' | (x', y') <- shrink (x, y) ]
-- | Generate an arbitrary 'Relation'.
arbitraryRelation :: (Arbitrary a, Ord a) => Gen (Relation.Relation a)
arbitraryRelation = Relation.stars <$> arbitrary
-- TODO: Implement a custom shrink method.
instance (Arbitrary a, Ord a) => Arbitrary (Relation.Relation a) where
arbitrary = arbitraryRelation
shrink g = shrinkVertices ++ shrinkEdges
where
shrinkVertices =
let vertices = Relation.vertexList g
in [ Relation.removeVertex v g | v <- vertices ]
shrinkEdges =
let edges = Relation.edgeList g
in [ Relation.removeEdge v w g | (v, w) <- edges ]
-- TODO: Simplify.
instance (Arbitrary a, Ord a) => Arbitrary (Reflexive.ReflexiveRelation a) where
arbitrary = Reflexive.fromRelation . Relation.reflexiveClosure
<$> arbitraryRelation
instance (Arbitrary a, Ord a) => Arbitrary (Symmetric.Relation a) where
arbitrary = Symmetric.toSymmetric <$> arbitraryRelation
instance (Arbitrary a, Ord a) => Arbitrary (Transitive.TransitiveRelation a) where
arbitrary = Transitive.fromRelation . Relation.transitiveClosure
<$> arbitraryRelation
instance (Arbitrary a, Ord a) => Arbitrary (Preorder.PreorderRelation a) where
arbitrary = Preorder.fromRelation . Relation.closure
<$> arbitraryRelation
-- | Generate an arbitrary 'AdjacencyMap'. It is guaranteed that the
-- resulting adjacency map is 'consistent'.
arbitraryAdjacencyMap :: (Arbitrary a, Ord a) => Gen (AM.AdjacencyMap a)
arbitraryAdjacencyMap = AM.stars <$> arbitrary
instance (Arbitrary a, Ord a) => Arbitrary (AM.AdjacencyMap a) where
arbitrary = arbitraryAdjacencyMap
shrink g = shrinkVertices ++ shrinkEdges
where
shrinkVertices = [ AM.removeVertex v g | v <- AM.vertexList g ]
shrinkEdges = [ AM.removeEdge v w g | (v, w) <- AM.edgeList g ]
-- | Generate an arbitrary non-empty 'NAM.AdjacencyMap'. It is guaranteed that
-- the resulting adjacency map is 'consistent'.
arbitraryNonEmptyAdjacencyMap :: (Arbitrary a, Ord a) => Gen (NAM.AdjacencyMap a)
arbitraryNonEmptyAdjacencyMap = NAM.stars1 <$> nonEmpty
where
nonEmpty = do
xs <- arbitrary
case xs of
[] -> do
x <- arbitrary
return ((x, []) :| []) -- There must be at least one vertex
(x:xs) -> return (x :| xs)
instance (Arbitrary a, Ord a) => Arbitrary (NAM.AdjacencyMap a) where
arbitrary = arbitraryNonEmptyAdjacencyMap
shrink g = shrinkVertices ++ shrinkEdges
where
shrinkVertices =
let vertices = toList $ NAM.vertexList1 g
in catMaybes [ NAM.removeVertex1 v g | v <- vertices ]
shrinkEdges =
let edges = NAM.edgeList g
in [ NAM.removeEdge v w g | (v, w) <- edges ]
instance Arbitrary AIM.AdjacencyIntMap where
arbitrary = AIM.stars <$> arbitrary
shrink g = shrinkVertices ++ shrinkEdges
where
shrinkVertices = [ AIM.removeVertex x g | x <- AIM.vertexList g ]
shrinkEdges = [ AIM.removeEdge x y g | (x, y) <- AIM.edgeList g ]
-- | Generate an arbitrary labelled 'LAM.AdjacencyMap'. It is guaranteed
-- that the resulting adjacency map is 'consistent'.
arbitraryLabelledAdjacencyMap :: (Arbitrary a, Ord a, Eq e, Arbitrary e, Monoid e) => Gen (LAM.AdjacencyMap e a)
arbitraryLabelledAdjacencyMap = LAM.fromAdjacencyMaps <$> arbitrary
instance (Arbitrary a, Ord a, Eq e, Arbitrary e, Monoid e) => Arbitrary (LAM.AdjacencyMap e a) where
arbitrary = arbitraryLabelledAdjacencyMap
shrink g = shrinkVertices ++ shrinkEdges
where
shrinkVertices =
let vertices = LAM.vertexList g
in [ LAM.removeVertex v g | v <- vertices ]
shrinkEdges =
let edges = LAM.edgeList g
in [ LAM.removeEdge v w g | (_, v, w) <- edges ]
-- | Generate an arbitrary labelled 'LAM.Graph' value of a specified size.
arbitraryLabelledGraph :: (Arbitrary a, Arbitrary e) => Gen (LG.Graph e a)
arbitraryLabelledGraph = sized expr
where
expr 0 = return LG.empty
expr 1 = LG.vertex <$> arbitrary
expr n = do
label <- arbitrary
left <- choose (0, n)
LG.connect label <$> expr left <*> expr (n - left)
instance (Arbitrary a, Arbitrary e, Monoid e) => Arbitrary (LG.Graph e a) where
arbitrary = arbitraryLabelledGraph
shrink LG.Empty = []
shrink (LG.Vertex _) = [LG.Empty]
shrink (LG.Connect e x y) = [LG.Empty, x, y, LG.Connect mempty x y]
++ [LG.Connect e x' y' | (x', y') <- shrink (x, y) ]
-- TODO: Implement a custom shrink method.
instance Arbitrary s => Arbitrary (Doc s) where
arbitrary = mconcat . map literal <$> arbitrary
instance (Arbitrary a, Num a, Ord a) => Arbitrary (Distance a) where
arbitrary = (\x -> if x < 0 then distance infinite else distance (unsafeFinite x)) <$> arbitrary
instance (Arbitrary a, Num a, Ord a) => Arbitrary (Capacity a) where
arbitrary = (\x -> if x < 0 then capacity infinite else capacity (unsafeFinite x)) <$> arbitrary
instance (Arbitrary a, Num a, Ord a) => Arbitrary (Count a) where
arbitrary = (\x -> if x < 0 then count infinite else count (unsafeFinite x)) <$> arbitrary
instance Arbitrary a => Arbitrary (Minimum a) where
arbitrary = frequency [(10, pure <$> arbitrary), (1, pure noMinimum)]
instance (Arbitrary a, Ord a) => Arbitrary (PowerSet a) where
arbitrary = PowerSet <$> arbitrary
instance (Arbitrary o, Arbitrary a) => Arbitrary (Optimum o a) where
arbitrary = Optimum <$> arbitrary <*> arbitrary
instance (Arbitrary a, Arbitrary b, Ord a, Ord b) => Arbitrary (BAM.AdjacencyMap a b) where
arbitrary = BAM.toBipartite <$> arbitrary
shrink = map BAM.toBipartite . shrink . BAM.fromBipartite
instance (Arbitrary a, Arbitrary b) => Arbitrary (BAM.List a b) where
arbitrary = sized go
where
go 0 = return BAM.Nil
go 1 = do h <- arbitrary
return $ BAM.Cons h BAM.Nil
go n = do f <- arbitrary
s <- arbitrary
(BAM.Cons f . BAM.Cons s) <$> go (n - 2)
instance (Arbitrary a, Arbitrary b, Ord a, Ord b) => Arbitrary (BAMA.Matching a b) where
arbitrary = BAMA.matching <$> arbitrary