Skip to content

Commit ed24a0e

Browse files
committed
copilot: Add example of how to use proveWithCounterExample. Refs #589.
Currently, the `Copilot.Theorem.What4.prove` function returns a list of results, where each result contains a `SatResult` that describes whether a property is `Valid`, `Invalid`, or `Unknown`. The `Invalid` result has the limitation that it does not give any information about a specific counterexample that could drive Copilot into falsifying the property, however. This makes it challenging to interpret what the results of prove mean. To demonstrate how to effectively use the newly added `proveWithCounterExample` function, this commit adds a new `examples/what4/ArithmeticCounterExamples.hs` function that behaves like `examples/what4/Arithmetic.hs`, but using `proveWithCounterExamples` instead of `prove`.
1 parent 0727256 commit ed24a0e

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

copilot/copilot.cabal

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ executable what4-arithmetic
8686
else
8787
buildable: False
8888

89+
executable what4-arithmetic-counterexamples
90+
main-is: ArithmeticCounterExamples.hs
91+
hs-source-dirs: examples/what4
92+
build-depends: base
93+
, containers
94+
, copilot
95+
, copilot-theorem
96+
default-language: Haskell2010
97+
if flag(examples)
98+
buildable: True
99+
else
100+
buildable: False
101+
89102
executable what4-structs
90103
main-is: Structs.hs
91104
hs-source-dirs: examples/what4
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
-- | An example showing the usage of the What4 backend in copilot-theorem for
2+
-- simple arithmetic. This example uses the 'proveWithCounterExamples' function
3+
-- to demonstrate counterexamples in the event of invalid properties.
4+
5+
module Main where
6+
7+
import qualified Prelude as P
8+
import Control.Monad (void, forM_)
9+
import qualified Data.Map as Map
10+
11+
import Language.Copilot
12+
import Copilot.Theorem.What4
13+
14+
spec :: Spec
15+
spec = do
16+
-- Define some external streams. Their values are not important, so external
17+
-- streams suffice.
18+
let eint8 :: Stream Int8
19+
eint8 = extern "eint8" Nothing
20+
eword8 :: Stream Word8
21+
eword8 = extern "eword8" Nothing
22+
efloat :: Stream Float
23+
efloat = extern "efloat" Nothing
24+
25+
-- The simplest example involving numbers: equality on constant values.
26+
void $ prop "Example 1" (forAll ((constant (1 :: Int8)) == (constant 1)))
27+
28+
-- Testing "a < a + 1". This should fail, because it isn't true.
29+
void $ prop "Example 2" (forAll (eint8 < (eint8 + 1)))
30+
31+
-- Adding another condition to the above property to make it true.
32+
void $ prop "Example 3" (forAll ((eint8 < (eint8 + 1)) || (eint8 == 127)))
33+
34+
-- Just like the previous example, but with words.
35+
void $ prop "Example 4" (forAll ((eword8 < (eword8 + 1)) || (eword8 == 255)))
36+
37+
-- An example with floats.
38+
void $ prop "Example 5" (forAll ((2 * efloat) == (efloat + efloat)))
39+
40+
-- Another example with floats. This fails, because it isn't true.
41+
void $ prop "Example 6" (forAll ((efloat + 1) /= efloat))
42+
43+
main :: IO ()
44+
main = do
45+
spec' <- reify spec
46+
47+
-- Use Z3 to prove the properties.
48+
results <- proveWithCounterExample Z3 spec'
49+
50+
-- Print the results.
51+
forM_ results $ \(nm, res) -> do
52+
putStr $ nm <> ": "
53+
case res of
54+
ValidCex -> putStrLn "valid"
55+
InvalidCex cex -> do
56+
putStrLn "invalid"
57+
putStrLn $ ppCounterExample cex
58+
UnknownCex -> putStrLn "unknown"
59+
60+
-- | Pretty-print a counterexample for user display.
61+
ppCounterExample :: CounterExample -> String
62+
ppCounterExample cex
63+
| any P.not (baseCases cex)
64+
= if Map.null baseCaseVals
65+
then
66+
" All possible extern values during the base case(s) " P.++
67+
"constitute a counterexample."
68+
else
69+
unlines $
70+
" The base cases failed with the following extern values:" :
71+
map
72+
(\((name, _), val) -> " " P.++ name P.++ ": " P.++ show val)
73+
(Map.toList baseCaseVals)
74+
75+
| P.not (inductionStep cex)
76+
= if Map.null inductionStepVals
77+
then
78+
" All possible extern values during the induction step " P.++
79+
"constitute a counterexample."
80+
else
81+
unlines $
82+
" The induction step failed with the following extern values:" :
83+
map
84+
(\((name, _), val) -> " " P.++ name P.++ ": " P.++ show val)
85+
(Map.toList inductionStepVals)
86+
87+
| otherwise
88+
= error $
89+
"ppCounterExample: " P.++
90+
"Counterexample without failing base cases or induction step"
91+
where
92+
allExternVals = concreteExternValues cex
93+
94+
baseCaseVals =
95+
Map.filterWithKey
96+
(\(_, offset) _ ->
97+
case offset of
98+
AbsoluteOffset {} -> True
99+
RelativeOffset {} -> False
100+
)
101+
allExternVals
102+
103+
inductionStepVals =
104+
Map.filterWithKey
105+
(\(_, offset) _ ->
106+
case offset of
107+
AbsoluteOffset {} -> False
108+
RelativeOffset {} -> True
109+
)
110+
allExternVals

0 commit comments

Comments
 (0)