-- Print redundant candidates -- -- Copyright (C) 2023-2024 Rudy Matela -- Distributed under the 3-Clause BSD licence (see the file LICENSE). import Conjure -- This script needs some internal utilities of Conjure: import Conjure.Engine import Conjure.Defn import Conjure.Defn.Redundancy import Conjure.Defn.Test import Conjure.Utils import Test.LeanCheck.Tiers (discardT) -- | This function prints redundant candidates. -- -- The arguments are, in their respective order: -- -- * maximum candidate size (5 = few seconds, 7 = few minutes); -- * function name (for pretty-printing purposes); -- * proxy value to indicate the type of functions to generate; -- * list of primitives, in Conjure-compatible form. printRedundantCandidates :: Conjurable f => Int -> String -> f -> [Prim] -> IO () printRedundantCandidates n nm f ps = do putStrLn $ "Redundant candidates for: " ++ nm ++ " :: " ++ show (typeOf f) putStrLn $ " pruning with " ++ show nRules ++ "/" ++ show nREs ++ " rules" putStrLn $ " " ++ show (map length css) ++ " candidates" putStrLn $ " " ++ show numUnique ++ "/" ++ show numCandidates ++ " unique candidates" putStrLn $ " " ++ show numRedundant ++ "/" ++ show numCandidates ++ " redundant candidates" putStrLn "" printThy thy putStrLn $ unlines . map showClass $ filter (\xs -> length xs > 1) $ classes where numCandidates = length candidates numUnique = length classes numRedundant = numCandidates - numUnique classes = classifyBy (equalModuloTesting maxTests maxEvalRecursions nm f) candidates candidates = concat css css = take n . discardT isRedundantByIntroduction -- additional pruning rule $ css' (css', thy) = candidateDefnsC args nm f ps -- Conjure uses this for listing candidates nRules = length (rules thy) nREs = length (equations thy) + nRules maxTests = 60 -- a hardcoded value probably will not hurt in this simple benchmark maxEvalRecursions = 30 -- shows a class of candidates showClass :: [Defn] -> String showClass cs = unlines $ heading : map (indent . showDefn) cs where heading = "class of " ++ show (length cs) ++ " equivalent candidates:\n" main :: IO () main = do -- This N value limits the maximum size of candidates, -- increase it to print redundant candidates of bigger size. let n = 5 -- With n = 5, this should run in a few seconds. -- With n = 6, this should run in a few more seconds. -- With n = 7 and -O2 optimization, -- this should take a few minutes to run and generate a ~300K text file. -- We can also customize the n per-function below: printRedundantCandidates (n+1) "foo" (undefined :: Int -> Int) [ pr (0 :: Int) , pr (1 :: Int) , prim "+" ((+) :: Int -> Int -> Int) , prim "*" ((*) :: Int -> Int -> Int) , prim "-" ((-) :: Int -> Int -> Int) ] printRedundantCandidates n "?" (undefined :: Int -> Int -> Int) [ pr (0 :: Int) , prim "+" ((+) :: Int -> Int -> Int) , prim "*" ((*) :: Int -> Int -> Int) , prim "dec" (subtract 1 :: Int -> Int) ] printRedundantCandidates (n+1) "goo" (undefined :: [Int] -> [Int]) [ pr ([] :: [Int]) , prim ":" ((:) :: Int -> [Int] -> [Int]) , prim "++" ((++) :: [Int] -> [Int] -> [Int]) ] printRedundantCandidates n "??" (undefined :: [Int] -> [Int] -> [Int]) [ pr ([] :: [Int]) , prim ":" ((:) :: Int -> [Int] -> [Int]) , prim "++" ((++) :: [Int] -> [Int] -> [Int]) ] printRedundantCandidates (n+1) "ton" (undefined :: Bool -> Bool) [ pr False , pr True , prim "&&" (&&) , prim "||" (||) , prim "not" not ] printRedundantCandidates n "&|" (undefined :: Bool -> Bool -> Bool) [ pr False , pr True , prim "&&" (&&) , prim "||" (||) , prim "not" not ] -- Degenerate case: -- lots of redundancy, since 0 `mod` 0 = undefined -- Speculate is not able to discover that x `mod` x = 0 -- nevertheless useful for observing candidate filtering -- through other means {- printRedundantCandidates n "gcd" (undefined :: Int -> Int -> Int) [ pr (0::Int) , prim "`mod`" (mod :: Int -> Int -> Int) ] -}