packages feed

atp-0.1.0.0: test/Property/Main.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TemplateHaskell #-}

{-|
Module       : Main
Description  : QuickCheck properties of the atp library.
Copyright    : (c) Evgenii Kotelnikov, 2019-2021
License      : GPL-3
Maintainer   : evgeny.kotelnikov@gmail.com
Stability    : experimental
-}

module Main (main) where

import Control.Monad (unless)
import Data.Function (on)
#if !MIN_VERSION_base(4, 11, 0)
import Data.Semigroup (Semigroup(..))
#endif
import System.Exit (exitFailure)

import Test.QuickCheck (
    Testable, Property, property, (===), (==>), counterexample, forAll,
    forAllProperties, quickCheckWithResult, stdArgs, Args(..), withMaxSuccess
  )

import ATP hiding ((===), (==>))
import ATP.Codec.TPTP

import Property.Generators ()
import Property.Modifiers.AlphaEquivalent


-- * Helper functions

infix 4 ~==
infix 4 ~~=
infix 4 ~==~

-- | Like '(===)', but for alpha equivalence.
(~==) :: (Show e, FirstOrder e) => e -> e -> Property
a ~== b = counterexample (show a ++ " ~/= " ++ show b) (a ~= b)

-- | Like '(~==)', but for results of partial computations.
(~~=) :: (Show e, FirstOrder e) => Partial e -> Partial e -> Property
x ~~= y
  | Right a <- liftPartial x, Right b <- liftPartial y = a ~== b
  | otherwise = counterexample (show x ++ " ~/= " ++ show y) False

-- | Like '(~==~)', but modulo simplification.
(~==~) :: (Show e, FirstOrder e, Simplify e) => Partial e -> Partial e -> Property
(~==~) = (~~=) `on` fmap simplify

satisfies :: (Show b, Testable prop) => (a -> b) -> (b -> prop) -> a -> Property
satisfies f p a = counterexample (show b) (p b) where b = f a


-- * Generators

-- ** 'genAlphaEquivalent' does not introduce new free variables

freeCountAlphaEquivalent :: (Show e, FirstOrder e) => e -> Property
freeCountAlphaEquivalent a =
  forAll (genAlphaEquivalent a) $ \b ->
    length (free a) === length (free b)

prop_freeCountAlphaEquivalentFormula :: Formula -> Property
prop_freeCountAlphaEquivalentFormula =
  withMaxSuccess 100000 . freeCountAlphaEquivalent

prop_freeCountAlphaEquivalentClause :: Clause -> Property
prop_freeCountAlphaEquivalentClause = freeCountAlphaEquivalent

prop_freeCountAlphaEquivalentLiteral :: Literal -> Property
prop_freeCountAlphaEquivalentLiteral = freeCountAlphaEquivalent

prop_freeCountAlphaEquivalentTerm :: Term -> Property
prop_freeCountAlphaEquivalentTerm = freeCountAlphaEquivalent


-- ** 'genAlphaEquivalent' produces alpha equivalent expressions

actuallyAlphaEquivalent :: (Show e, FirstOrder e) => e -> Property
actuallyAlphaEquivalent a =
  forAll (genAlphaEquivalent a) $ \b ->
    a ~= b

prop_actuallyAlphaEquivalentFormula :: Formula -> Property
prop_actuallyAlphaEquivalentFormula =
  withMaxSuccess 100000 . actuallyAlphaEquivalent

prop_actuallyAlphaEquivalentClause :: Clause -> Property
prop_actuallyAlphaEquivalentClause = actuallyAlphaEquivalent

prop_actuallyAlphaEquivalentLiteral :: Literal -> Property
prop_actuallyAlphaEquivalentLiteral = actuallyAlphaEquivalent

prop_actuallyAlphaEquivalentTerm :: Term -> Property
prop_actuallyAlphaEquivalentTerm = actuallyAlphaEquivalent


-- ** 'genAlphaInequivalent' produces alpha inequivalent expressions

actuallyAlphaInequivalent :: (Show e, FirstOrder e) => e -> Property
actuallyAlphaInequivalent a =
  length (vars a) > 1 ==>
    forAll (genAlphaInequivalent a) $ \b ->
      not (a ~= b)

prop_actuallyAlphaInequivalentFormula :: Formula -> Property
prop_actuallyAlphaInequivalentFormula =
  withMaxSuccess 50000 . actuallyAlphaInequivalent

prop_actuallyAlphaInequivalentClause :: Clause -> Property
prop_actuallyAlphaInequivalentClause = actuallyAlphaInequivalent

prop_actuallyAlphaInequivalentLiteral :: Literal -> Property
prop_actuallyAlphaInequivalentLiteral = actuallyAlphaInequivalent

prop_actuallyAlphaInequivalentTerm :: Term -> Property
prop_actuallyAlphaInequivalentTerm = actuallyAlphaInequivalent


-- * Free and bound variables

freeBoundVars :: FirstOrder e => e -> Property
freeBoundVars e = free e <> bound e === vars e

prop_freeBoundVarsFormula :: Formula -> Property
prop_freeBoundVarsFormula = freeBoundVars

prop_freeBoundVarsClause :: Clause -> Property
prop_freeBoundVarsClause = freeBoundVars

prop_freeBoundVarsLiteral :: Literal -> Property
prop_freeBoundVarsLiteral = freeBoundVars

prop_freeBoundVarsTerm :: Term -> Property
prop_freeBoundVarsTerm = freeBoundVars


-- * Alpha equivalence

-- ** Alpha equivalence is reflexive

alphaEquivalenceReflexivity :: FirstOrder e => e -> Property
alphaEquivalenceReflexivity e = property (e ~= e)

prop_alphaEquivalenceReflexivityFormula :: Formula -> Property
prop_alphaEquivalenceReflexivityFormula =
  withMaxSuccess 100000 . alphaEquivalenceReflexivity

prop_alphaEquivalenceReflexivityClause :: Clause -> Property
prop_alphaEquivalenceReflexivityClause = alphaEquivalenceReflexivity

prop_alphaEquivalenceReflexivityLiteral :: Literal -> Property
prop_alphaEquivalenceReflexivityLiteral = alphaEquivalenceReflexivity

prop_alphaEquivalenceReflexivityTerm :: Term -> Property
prop_alphaEquivalenceReflexivityTerm = alphaEquivalenceReflexivity


-- ** Alpha equivalence is symmetric

alphaEquivalenceSymmetry :: (Show e, FirstOrder e) => e -> Property
alphaEquivalenceSymmetry a =
  forAll (genAlphaEquivalent a) $ \b ->
    b ~= a

prop_alphaEquivalenceSymmetryFormula :: Formula -> Property
prop_alphaEquivalenceSymmetryFormula =
  withMaxSuccess 100000 . alphaEquivalenceSymmetry

prop_alphaEquivalenceSymmetryClause :: Clause -> Property
prop_alphaEquivalenceSymmetryClause = alphaEquivalenceSymmetry

prop_alphaEquivalenceSymmetryLiteral :: Literal -> Property
prop_alphaEquivalenceSymmetryLiteral = alphaEquivalenceSymmetry

prop_alphaEquivalenceSymmetryTerm :: Term -> Property
prop_alphaEquivalenceSymmetryTerm = alphaEquivalenceSymmetry


-- ** Alpha equivalence is transitive

alphaEquivalenceTransitivity :: (Show e, FirstOrder e) => e -> Property
alphaEquivalenceTransitivity a =
  forAll (genAlphaEquivalent a) $ \b ->
    forAll (genAlphaEquivalent b) $ \c ->
      a ~= c

prop_alphaEquivalenceTransitivityFormula :: Formula -> Property
prop_alphaEquivalenceTransitivityFormula =
  withMaxSuccess 100000 . alphaEquivalenceTransitivity

prop_alphaEquivalenceTransitivityClause :: Clause -> Property
prop_alphaEquivalenceTransitivityClause = alphaEquivalenceTransitivity

prop_alphaEquivalenceTransitivityLiteral :: Literal -> Property
prop_alphaEquivalenceTransitivityLiteral = alphaEquivalenceTransitivity

prop_alphaEquivalenceTransitivityTerm :: Term -> Property
prop_alphaEquivalenceTransitivityTerm = alphaEquivalenceTransitivity


-- * Simplification

-- ** Clauses

prop_simplifyClause :: Clause -> Property
prop_simplifyClause = simplify `satisfies` isSimplifiedClause

isSimplifiedClause :: Clause -> Bool
isSimplifiedClause (Literals ls) =
  not (any isNegatedConstant ls) &&
  FalsityLiteral `notElem` ls &&
  (ls == [TautologyLiteral] || TautologyLiteral `notElem` ls)

isNegatedConstant :: Signed Literal -> Bool
isNegatedConstant = \case
  Signed Negative Propositional{} -> True
  _ -> False

prop_simplifyClauses :: Clauses -> Property
prop_simplifyClauses = simplify `satisfies` areSimplifiedClauses

areSimplifiedClauses :: Clauses -> Bool
areSimplifiedClauses (Clauses []) = True
areSimplifiedClauses (Clauses cs) =
  all isSimplifiedClause cs &&
  (cs == [EmptyClause] || EmptyClause `notElem` cs)


-- ** Formulas

prop_simplifyFormula :: Formula -> Property
prop_simplifyFormula = simplify `satisfies` isSimplifiedFormula

isSimplifiedFormula :: Formula -> Bool
isSimplifiedFormula f =
  not (containsDoubleNegation f) &&
  not (containsLeftAssocitivity f)

containsDoubleNegation :: Formula -> Bool
containsDoubleNegation = \case
  Atomic{} -> False
  Negate Negate{} -> True
  Negate f -> containsDoubleNegation f
  Connected  _ f g -> containsDoubleNegation f || containsDoubleNegation g
  Quantified _ _ f -> containsDoubleNegation f

containsLeftAssocitivity :: Formula -> Bool
containsLeftAssocitivity = \case
  Atomic{} -> False
  Negate f -> containsLeftAssocitivity f
  Connected  c (Connected c' _ _) _ | c' == c && isAssociative c -> True
  Connected  _ f g -> containsLeftAssocitivity f || containsLeftAssocitivity g
  Quantified _ _ f -> containsLeftAssocitivity f


-- ** Idempotence

simplifyIdempotent :: (Eq a, Show a, Simplify a) => a -> Property
simplifyIdempotent a = simplify a ==~ a
  where (==~) = (===) `on` simplify

prop_simplifyIdempotentClause :: Clause -> Property
prop_simplifyIdempotentClause = simplifyIdempotent

prop_simplifyIdempotentFormula :: Formula -> Property
prop_simplifyIdempotentFormula = simplifyIdempotent

prop_simplifyIdempotentLogicalExpression :: LogicalExpression -> Property
prop_simplifyIdempotentLogicalExpression = simplifyIdempotent

prop_simplifyIdempotentClauses :: Clauses -> Property
prop_simplifyIdempotentClauses = simplifyIdempotent

prop_simplifyIdempotentTheorem :: Theorem -> Property
prop_simplifyIdempotentTheorem = simplifyIdempotent


-- * Conversions

prop_liftUnliftSignedLiteral :: Signed Literal -> Property
prop_liftUnliftSignedLiteral s =
  unliftSignedLiteral (liftSignedLiteral s) === Just s

prop_liftUnliftClause :: Clause -> Property
prop_liftUnliftClause c = unliftClause (liftClause c) ==~ Just c
  where (==~) = (===) `on` fmap simplify

prop_liftUnliftContradiction :: (Show f, Eq f) => Contradiction f -> Property
prop_liftUnliftContradiction c =
  unliftContradiction (liftContradiction c) === Just c


-- * Codec

prop_codecClause :: Clause -> Property
prop_codecClause c = return c ~==~ decodeClause (encodeClause c)

prop_codecFormula :: Formula -> Property
prop_codecFormula f = return f ~==~ decodeFormula (encodeFormula f)

prop_codec :: LogicalExpression -> Property
prop_codec e = return e ~==~ decode (encode e)


-- * Runner

return []

main :: IO ()
main = do
  let args = stdArgs{maxSuccess=1000, maxDiscardRatio=50}
  success <- $forAllProperties (quickCheckWithResult args)
  unless success exitFailure