-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
copilot-theorem
: Make What4 backend refuse to prove existentially quantified properties
#254
Comments
I'm with you. I was also confused. But I don't see how What do you suggest? |
I think the current stream-based interpretation is fine, as long as we also remember the associated quantifier. I don't know that I would necessarily expect a run-time interpretation of these properties, but they are certainly an important distinction when proving properties about a spec. It's important not to assume an existential property as though it is universal (which is unsound); and the proof techniques one can use for universal properties are different than for existential (induction vs model checking). In principle, one could interpret a universal property as a safety property and monitor it, but the trigger mechanism already basically does that. |
Here is a test case that demonstrates the issue: {-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import Data.Foldable
import Data.Functor
import Copilot.Theorem.What4
import Language.Copilot
trueThenFalses :: Stream Bool
trueThenFalses = [True] ++ constant False
spec :: Spec
spec = do
void $ prop "forAll trueThenFalses (should be invalid)" (forAll trueThenFalses)
void $ prop "exists trueThenFalses (should be valid)" (exists trueThenFalses)
main :: IO ()
main = do
spec' <- reify spec
results <- prove Z3 spec'
forM_ results $ \(nm, res) -> do
putStr $ nm <> ": "
case res of
Valid -> putStrLn "valid"
Invalid -> putStrLn "invalid"
Unknown -> putStrLn "unknown" As its name suggests, |
Here is the Kind2 equivalent of the program above: {-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import Data.Functor
import Copilot.Theorem.Kind2
import Copilot.Theorem.Prove
import Language.Copilot
trueThenFalses :: Stream Bool
trueThenFalses = [True] ++ constant False
spec :: Spec
spec = do
void $ theorem "t1" (forAll trueThenFalses) (check (kind2Prover def))
void $ theorem "t2" (exists trueThenFalses) (check (kind2Prover def))
main :: IO ()
main = void $ reify spec
Both the |
Description
Type
Additional context None. Requester
Method to check presence of bug The issue can be detected with the following sample programs: {-# LANGUAGE NoImplicitPrelude #-}
module Main (main) where
import Data.Foldable
import Data.Functor
import Copilot.Theorem.What4
import Language.Copilot
trueThenFalses :: Stream Bool
trueThenFalses = [True] ++ constant False
spec :: Spec
spec = do
void $ prop "forAll trueThenFalses (should be invalid)" (forAll trueThenFalses)
void $ prop "exists trueThenFalses (should be valid)" (exists trueThenFalses)
main :: IO ()
main = do
spec' <- reify spec
results <- prove Z3 spec'
forM_ results $ \(nm, res) -> do
putStr $ nm <> ": "
case res of
Valid -> putStrLn "valid"
Invalid -> putStrLn "invalid"
Unknown -> putStrLn "unknown" Expected result Running the specs above or a similar variant via What4 should yield If the prover is not able to prove a property of that kind, a suitable error message should be printed. Desired result Running the specs above or a similar variant via What4 should yield If the prover is not able to prove a property of that kind, a suitable error message should be printed. Proposed solution Modify Further notes None. |
…ge#254. Previously, `copilot-language` would remember whether a proposition (i.e., a stream of booleans) was quantified universally (i.e., using `forAll`) or existentially (i.e., using `exists`). When translating from `copilot-language` to `copilot-core`, however, the quantifier would be discarded. This meant that a `copilot-core` `Property` would not record any quantifier information at all, making it impossible for downstream libraries that use `copilot-core` to handle universal quantification differently from existential quantification. This commit changes the `copilot-core` API to preserve quantifier information when translating `copilot-language` propositions into `copilot-core` ones. Specifically, it introduces a `Prop` data type in `copilot-core` (largely inspired by a data type of the same name in `copilot-language`) to record a proposition's quantifier, and it changes the `propertyExpr :: Expr Bool` field of `copilot-core`'s `Property` data type to `propertyProp :: Prop`. These API changes require some corresponding code changes in `copilot-language`, `copilot-prettyprinter`, and `copilot-theorem`. This commit also introduces an `extractProp :: Prop -> Expr Bool` function for retrieving the underlying boolean expression. Generally, this function should not be used, as different quantifiers usually require different treatment, and misuse of the `extractProp` function can potentially lead to unsoundness. There are a handful of places where the use of `extractProp` is justified, however. In each such place, a comment has been left to justify why the use of `extractProp` is sound. The payoff for these API changes will happen in a follow-up commit, where `copilot-theorem` will use the quantifier information in a `copilot-core` `Prop`, rather than unsoundly discarding the quantifiers using `extractProp`.
…4 backend. Refs Copilot-Language#254. The functions in `Copilot.Theorem.What4` (e.g., `prove`) take a proposition and attempt to prove that it is valid at all possible time steps. Previously, they would do this regardless of whether the proposition was universally quantified or existentiall quantifier, however, which was unsound. The reason that this happened is because the functions would call `extractProp` on the proposition being proven, causing the proposition's quantifier not to be taken into account. This commit removes the unsound uses of `extractProp` and instead checks if the proposition was actually quantified universally (i.e., with a `Forall`), which is the intended use case for `Copilot.Theorem.What4`. If the proposition was instead quantified existentially (i.e., with an `Exists`), then a `ProveException` will be thrown. Warnings have been added to Haddocks of the relevant functions that can potentially throw a `ProveException`.
Change Manager: Confirmed that the issue exists. |
Technical Lead: Confirmed that the issue should be addressed. |
Technical Lead: Issue scheduled for fixing in Copilot 4.3. Fix assigned to: @RyanGlScott . |
prop
and theorem
forget their quantifier?copilot-theorem
: Make What4 backend refuse to prove existentially quantified properties
…4 backend. Refs Copilot-Language#254. The functions in `Copilot.Theorem.What4` (e.g., `prove`) take a proposition and attempt to prove that it is valid at all possible time steps. Previously, they would do this regardless of whether the proposition was universally quantified or existential quantifier, however, which was unsound. The reason that this happened is because the functions would call `extractProp` on the proposition being proven, causing the proposition's quantifier not to be taken into account. This commit removes the unsound uses of `extractProp` and instead checks if the proposition was actually quantified universally (i.e., with a `Forall`), which is the intended use case for `Copilot.Theorem.What4`. If the proposition was instead quantified existentially (i.e., with an `Exists`), then a `ProveException` will be thrown. Warnings have been added to Haddocks of the relevant functions that can potentially throw a `ProveException`.
Implementor: Solution implemented, review requested. |
…ge#254. Previously, `copilot-language` would remember whether a proposition (i.e., a stream of booleans) was quantified universally (i.e., using `forAll`) or existentially (i.e., using `exists`). When translating from `copilot-language` to `copilot-core`, however, the quantifier would be discarded. This meant that a `copilot-core` `Property` would not record any quantifier information at all, making it impossible for downstream libraries that use `copilot-core` to handle universal quantification differently from existential quantification. This commit changes the `copilot-core` API to preserve quantifier information when translating `copilot-language` propositions into `copilot-core` ones. Specifically, it introduces a `Prop` data type in `copilot-core` (largely inspired by a data type of the same name in `copilot-language`) to record a proposition's quantifier, and it changes the `propertyExpr :: Expr Bool` field of `copilot-core`'s `Property` data type to `propertyProp :: Prop`. These API changes require some corresponding code changes in `copilot-language`, `copilot-prettyprinter`, and `copilot-theorem`. This commit also introduces an `extractProp :: Prop -> Expr Bool` function for retrieving the underlying boolean expression. Generally, this function should not be used, as different quantifiers usually require different treatment, and misuse of the `extractProp` function can potentially lead to unsoundness. There are a handful of places where the use of `extractProp` is justified, however. In each such place, a comment has been left to justify why the use of `extractProp` is sound. The payoff for these API changes will happen in a follow-up commit, where `copilot-theorem` will use the quantifier information in a `copilot-core` `Prop`, rather than unsoundly discarding the quantifiers using `extractProp`.
…4 backend. Refs Copilot-Language#254. The functions in `Copilot.Theorem.What4` (e.g., `prove`) take a proposition and attempt to prove that it is valid at all possible time steps. Previously, they would do this regardless of whether the proposition was universally quantified or existential quantifier, however, which was unsound. The reason that this happened is because the functions would call `extractProp` on the proposition being proven, causing the proposition's quantifier not to be taken into account. This commit removes the unsound uses of `extractProp` and instead checks if the proposition was actually quantified universally (i.e., with a `Forall`), which is the intended use case for `Copilot.Theorem.What4`. If the proposition was instead quantified existentially (i.e., with an `Exists`), then a `ProveException` will be thrown. Warnings have been added to Haddocks of the relevant functions that can potentially throw a `ProveException`.
…ge#254. Previously, `copilot-language` would remember whether a proposition (i.e., a stream of booleans) was quantified universally (i.e., using `forAll`) or existentially (i.e., using `exists`). When translating from `copilot-language` to `copilot-core`, however, the quantifier would be discarded. This meant that a `copilot-core` `Property` would not record any quantifier information at all, making it impossible for downstream libraries that use `copilot-core` to handle universal quantification differently from existential quantification. This commit changes the `copilot-core` API to preserve quantifier information when translating `copilot-language` propositions into `copilot-core` ones. Specifically, it introduces a `Prop` data type in `copilot-core` (largely inspired by a data type of the same name in `copilot-language`) to record a proposition's quantifier, and it changes the `propertyExpr :: Expr Bool` field of `copilot-core`'s `Property` data type to `propertyProp :: Prop`. These API changes require some corresponding code changes in `copilot-language`, `copilot-prettyprinter`, and `copilot-theorem`. This commit also introduces an `extractProp :: Prop -> Expr Bool` function for retrieving the underlying boolean expression. Generally, this function should not be used, as different quantifiers usually require different treatment, and misuse of the `extractProp` function can potentially lead to unsoundness. There are a handful of places where the use of `extractProp` is justified, however. In each such place, a comment has been left to justify why the use of `extractProp` is sound. The payoff for these API changes will happen in a follow-up commit, where `copilot-theorem` will use the quantifier information in a `copilot-core` `Prop`, rather than unsoundly discarding the quantifiers using `extractProp`.
…4 backend. Refs Copilot-Language#254. The functions in `Copilot.Theorem.What4` (e.g., `prove`) take a proposition and attempt to prove that it is valid at all possible time steps. Previously, they would do this regardless of whether the proposition was universally quantified or existential quantifier, however, which was unsound. The reason that this happened is because the functions would call `extractProp` on the proposition being proven, causing the proposition's quantifier not to be taken into account. This commit removes the unsound uses of `extractProp` and instead checks if the proposition was actually quantified universally (i.e., with a `Forall`), which is the intended use case for `Copilot.Theorem.What4`. If the proposition was instead quantified existentially (i.e., with an `Exists`), then a `ProveException` will be thrown. Warnings have been added to Haddocks of the relevant functions that can potentially throw a `ProveException`.
Looking at the datatypes involved in the high-level and low-level
Spec
datatypes (e.g.SpecItem
), it seems to me that properties and theorems are both represented as a bare stream of booleans. It strikes me as odd that we don't retain information about which quantifier was used to state the property, as it makes it impossible later to determine what semantics where intended; in particular, the quantifier for the property is not passed to theaskProver
method ofCopilot.Theorem.Prove.prove
, which strikes me as very strange. Am I missing something here?The text was updated successfully, but these errors were encountered: