Skip to content

Commit

Permalink
KD: Rearrange list proofs
Browse files Browse the repository at this point in the history
  • Loading branch information
LeventErkok committed Sep 5, 2024
1 parent 3f03db2 commit bc381a0
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 160 deletions.
142 changes: 142 additions & 0 deletions Documentation/SBV/Examples/KnuckleDragger/AppendRev.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
-----------------------------------------------------------------------------

Check failure on line 1 in Documentation/SBV/Examples/KnuckleDragger/AppendRev.hs

View workflow job for this annotation

GitHub Actions / hlint

Error: Parse error: Unsupported extension: TypeAbstractions ▫︎ Found: "-----------------------------------------------------------------------------\n-- |\n-- Module : Documentation.SBV.Examples.KnuckleDragger.AppendRev\n-- Copyright : (c) Levent Erkok\n-- License : BSD3\n-- Maintainer: [email protected]\n-- Stability : experimental\n--\n-- Example use of the KnuckleDragger, on list append and reverses.\n-----------------------------------------------------------------------------\n\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE ScopedTypeVariables #-}\n{-# LANGUAGE TypeAbstractions #-}\n{-# LANGUAGE TypeApplications #-}\n\n{-# OPTIONS_GHC -Wall -Werror #-}\n\nmodule Documentation.SBV.Examples.KnuckleDragger.AppendRev where\n\nimport Prelude hiding (sum, length, reverse, (++))\n\nimport Data.SBV\nimport Data.SBV.Tools.KnuckleDragger\n\nimport Data.SBV.List ((.:), (++), reverse)\nimport qualified Data.SBV.List as SL\n\n-- | @xs ++ [] == xs@\n--\n-- We have:\n--\n-- >>> appendNull\n-- Lemma: appendNull Q.E.D.\nappendNull :: IO Proven\nappendNull = lemma \"appendNull\"\n (\\(Forall @\"xs\" (xs :: SList Integer)) -> xs ++ SL.nil .== xs)\n []\n\n-- | @(x : xs) ++ ys == x : (xs ++ ys)@\n--\n-- We have:\n-- >>> consApp\n-- Lemma: consApp Q.E.D.\nconsApp :: IO Proven\nconsApp = lemma \"consApp\"\n (\\(Forall @\"x\" (x :: SInteger)) (Forall @\"xs\" xs) (Forall @\"ys\" ys) -> (x .: xs) ++ ys .== x .: (xs ++ ys))\n []\n\n-- | Prove that list append is associative.\n--\n-- We have:\n-- >>> appendAssoc\n-- Axiom: List(a).induction Admitted.\n-- Lemma: consApp Q.E.D.\n-- Lemma: appendAssoc Q.E.D.\nappendAssoc :: IO Proven\nappendAssoc = do\n\n -- The classic proof by induction on xs\n let p :: SymVal a => SList a -> SList a -> SList a -> SBool\n p xs ys zs = xs ++ (ys ++ zs) .== (xs ++ ys) ++ zs\n\n -- Do the proof over integers. We use 'inductionPrinciple3', since our predicate has 3 arguments.\n induct <- inductionPrinciple3 (p @integer)\n\n -- Get a hold on to the consApp lemma that we'll need\n lconsApp <- consApp\n\n lemma \"appendAssoc\"\n (\\(Forall @\"xs\" (xs :: SList Integer)) (Forall @\"ys\" ys) (Forall @\"zs\" zs) -> p xs ys zs)\n [lconsApp , induct]\n\n-- | Prove that reversing a list twice leaves the list unchanged.\n--\n-- We have:\n--\n-- >>> reverseReverse\n-- Lemma: consApp Q.E.D.\n-- Axiom: List(a).induction Admitted.\n-- Lemma: consApp Q.E.D.\n-- Lemma: appendAssoc Q.E.D.\n-- Lemma: appendNull Q.E.D.\n-- Chain: revApp_induction_pre\n-- Lemma: revApp_induction_pre.1 Q.E.D.\n-- Lemma: revApp_induction_pre.2 Q.E.D.\n-- Lemma: revApp_induction_pre Q.E.D.\n-- Chain: revApp_induction_post\n-- Lemma: revApp_induction_post.1 Q.E.D.\n-- Lemma: revApp_induction_post.2 Q.E.D.\n-- Lemma: revApp_induction_post Q.E.D.\n-- Axiom: List(a).induction Admitted.\n-- Sorry: revApp Blindly believed.\n-- Axiom: List(a).induction Admitted.\n-- Lemma: reverseReverse Q.E.D. [Modulo: revApp]\nreverseReverse :: IO Proven\nreverseReverse = do\n -- We'll need the consApp and associativity-of-append proof, so get a hold on to them\n lconsApp <- consApp\n lAppendAssoc <- appendAssoc\n lAppendNull <- appendNull\n\n -- The usual inductive proof relies on @reverse (xs ++ ys) == reverse ys ++ reverse xs@, so first prove that:\n revApp <- do\n revApp_induction_pre <- chainLemma \"revApp_induction_pre\"\n (\\(Forall @\"x\" (x :: SInteger)) (Forall @\"xs\" xs) (Forall @\"ys\" ys)\n -> reverse ((x .: xs) ++ ys) .== (reverse (xs ++ ys)) ++ SL.singleton x)\n (\\(x :: SInteger) xs ys ->\n [ reverse ((x .: xs) ++ ys)\n , reverse (x .: (xs ++ ys))\n , reverse (xs ++ ys) ++ SL.singleton x\n ]\n ) [lconsApp]\n\n revApp_induction_post <- chainLemma \"revApp_induction_post\"\n (\\(Forall @\"x\" (x :: SInteger)) (Forall @\"xs\" xs) (Forall @\"ys\" ys)\n -> (reverse ys ++ reverse xs) ++ SL.singleton x .== reverse ys ++ reverse (x .: xs))\n (\\(x :: SInteger) xs ys ->\n [ (reverse ys ++ reverse xs) ++ SL.singleton x\n , reverse ys ++ (reverse xs ++ SL.singleton x)\n , reverse ys ++ reverse (x .: xs)\n ]\n ) [lAppendAssoc]\n\n let q :: SymVal a => SList a -> SList a -> SBo
-- |
-- Module : Documentation.SBV.Examples.KnuckleDragger.AppendRev
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: [email protected]
-- Stability : experimental
--
-- Example use of the KnuckleDragger, on list append and reverses.
-----------------------------------------------------------------------------

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeApplications #-}

{-# OPTIONS_GHC -Wall -Werror #-}

module Documentation.SBV.Examples.KnuckleDragger.AppendRev where

import Prelude hiding (sum, length, reverse, (++))

import Data.SBV
import Data.SBV.Tools.KnuckleDragger

import Data.SBV.List ((.:), (++), reverse)
import qualified Data.SBV.List as SL

-- | @xs ++ [] == xs@
--
-- We have:
--
-- >>> appendNull
-- Lemma: appendNull Q.E.D.
appendNull :: IO Proven
appendNull = lemma "appendNull"
(\(Forall @"xs" (xs :: SList Integer)) -> xs ++ SL.nil .== xs)
[]

-- | @(x : xs) ++ ys == x : (xs ++ ys)@
--
-- We have:
-- >>> consApp
-- Lemma: consApp Q.E.D.
consApp :: IO Proven
consApp = lemma "consApp"
(\(Forall @"x" (x :: SInteger)) (Forall @"xs" xs) (Forall @"ys" ys) -> (x .: xs) ++ ys .== x .: (xs ++ ys))
[]

-- | Prove that list append is associative.
--
-- We have:
-- >>> appendAssoc
-- Axiom: List(a).induction Admitted.
-- Lemma: consApp Q.E.D.
-- Lemma: appendAssoc Q.E.D.
appendAssoc :: IO Proven
appendAssoc = do

-- The classic proof by induction on xs
let p :: SymVal a => SList a -> SList a -> SList a -> SBool
p xs ys zs = xs ++ (ys ++ zs) .== (xs ++ ys) ++ zs

-- Do the proof over integers. We use 'inductionPrinciple3', since our predicate has 3 arguments.
induct <- inductionPrinciple3 (p @Integer)

-- Get a hold on to the consApp lemma that we'll need
lconsApp <- consApp

lemma "appendAssoc"
(\(Forall @"xs" (xs :: SList Integer)) (Forall @"ys" ys) (Forall @"zs" zs) -> p xs ys zs)
[lconsApp , induct]

-- | Prove that reversing a list twice leaves the list unchanged.
--
-- We have:
--
-- >>> reverseReverse
-- Lemma: consApp Q.E.D.
-- Axiom: List(a).induction Admitted.
-- Lemma: consApp Q.E.D.
-- Lemma: appendAssoc Q.E.D.
-- Lemma: appendNull Q.E.D.
-- Chain: revApp_induction_pre
-- Lemma: revApp_induction_pre.1 Q.E.D.
-- Lemma: revApp_induction_pre.2 Q.E.D.
-- Lemma: revApp_induction_pre Q.E.D.
-- Chain: revApp_induction_post
-- Lemma: revApp_induction_post.1 Q.E.D.
-- Lemma: revApp_induction_post.2 Q.E.D.
-- Lemma: revApp_induction_post Q.E.D.
-- Axiom: List(a).induction Admitted.
-- Sorry: revApp Blindly believed.
-- Axiom: List(a).induction Admitted.
-- Lemma: reverseReverse Q.E.D. [Modulo: revApp]
reverseReverse :: IO Proven
reverseReverse = do
-- We'll need the consApp and associativity-of-append proof, so get a hold on to them
lconsApp <- consApp
lAppendAssoc <- appendAssoc
lAppendNull <- appendNull

-- The usual inductive proof relies on @reverse (xs ++ ys) == reverse ys ++ reverse xs@, so first prove that:
revApp <- do
revApp_induction_pre <- chainLemma "revApp_induction_pre"
(\(Forall @"x" (x :: SInteger)) (Forall @"xs" xs) (Forall @"ys" ys)
-> reverse ((x .: xs) ++ ys) .== (reverse (xs ++ ys)) ++ SL.singleton x)
(\(x :: SInteger) xs ys ->
[ reverse ((x .: xs) ++ ys)
, reverse (x .: (xs ++ ys))
, reverse (xs ++ ys) ++ SL.singleton x
]
) [lconsApp]

revApp_induction_post <- chainLemma "revApp_induction_post"
(\(Forall @"x" (x :: SInteger)) (Forall @"xs" xs) (Forall @"ys" ys)
-> (reverse ys ++ reverse xs) ++ SL.singleton x .== reverse ys ++ reverse (x .: xs))
(\(x :: SInteger) xs ys ->
[ (reverse ys ++ reverse xs) ++ SL.singleton x
, reverse ys ++ (reverse xs ++ SL.singleton x)
, reverse ys ++ reverse (x .: xs)
]
) [lAppendAssoc]

let q :: SymVal a => SList a -> SList a -> SBool
q xs ys = reverse (xs ++ ys) .== (reverse ys ++ reverse xs)

inductQ <- inductionPrinciple2 (q @Integer)

sorry "revApp" (\(Forall @"xs" (xs :: SList Integer)) (Forall @"ys" ys) -> reverse (xs ++ ys) .== reverse ys ++ reverse xs)
[revApp_induction_pre, lAppendNull, inductQ, revApp_induction_post]

-- Result now follows from revApp and induction, and associativity of append.

let p :: SymVal a => SList a -> SBool
p xs = reverse (reverse xs) .== xs

induct <- inductionPrinciple (p @Integer)

lemma "reverseReverse"
(\(Forall @"xs" (xs :: SList Integer)) -> p xs)
[induct, revApp, lAppendAssoc]
84 changes: 84 additions & 0 deletions Documentation/SBV/Examples/KnuckleDragger/ListLen.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
-----------------------------------------------------------------------------

Check failure on line 1 in Documentation/SBV/Examples/KnuckleDragger/ListLen.hs

View workflow job for this annotation

GitHub Actions / hlint

Error: Parse error: Unsupported extension: TypeAbstractions ▫︎ Found: "-----------------------------------------------------------------------------\n-- |\n-- Module : Documentation.SBV.Examples.KnuckleDragger.ListLen\n-- Copyright : (c) Levent Erkok\n-- License : BSD3\n-- Maintainer: [email protected]\n-- Stability : experimental\n--\n-- Example use of the KnuckleDragger, about lenghts of lists\n-----------------------------------------------------------------------------\n\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE ScopedTypeVariables #-}\n{-# LANGUAGE TypeAbstractions #-}\n{-# LANGUAGE TypeApplications #-}\n\n{-# OPTIONS_GHC -Wall -Werror -Wno-unused-do-bind #-}\n\nmodule Documentation.SBV.Examples.KnuckleDragger.ListLen where\n\nimport Prelude hiding (sum, length, reverse, (++))\n\nimport Data.SBV\nimport Data.SBV.Tools.KnuckleDragger\n\nimport qualified Data.SBV.List as SL\n\n-- $setup\n-- >>> -- For doctest purposes only:\n-- >>> :set -XScopedTypeVariables\n-- >>> import Control.Exception\n\n-- | Prove that the length of a list is one more than the length of its tail.\n--\n-- We have:\n--\n-- >>> listLengthProof\n-- Axiom: List(a).induction Admitted.\n-- Lemma: length_correct Q.E.D.\nlistLengthProof :: IO Proven\nlistLengthProof = do\n let length :: SList Integer -> SInteger\n length = smtFunction \"length\" $ \\xs -> ite (SL.null xs) 0 (1 + length (SL.tail xs))\n\n spec :: SList Integer -> SInteger\n spec = SL.length\n\n p :: SList Integer -> SBool\n p xs = observe \"imp\" (length xs) .== observe \"spec\" (spec xs)\n\n induct <- inductionPrinciple p\n\n lemma \"length_correct\" (\\(Forall @\"xs\" xs) -> p xs) [induct]\n\n-- | It is instructive to see what kind of counter-example we get if a lemma fails to prove.\n-- Below, we do a variant of the 'listLengthProof', but with a bad implementation, and see the\n-- counter-example. Our implementation returns an incorrect answer if the given list is longer\n-- than 5 elements and have 42 in it. We have:\n--\n-- >>> badProof `catch` (\\(_ :: SomeException) -> pure ())\n-- Axiom: List(a).induction Admitted.\n-- Lemma: bad\n-- *** Failed to prove bad.\n-- Falsifiable. Counter-example:\n-- xs = [42,99,100,101,59,102] :: [Integer]\n-- imp = 42 :: Integer\n-- spec = 6 :: Integer\nbadProof :: IO ()\nbadProof = do\n let length :: SList Integer -> SInteger\n length = smtFunction \"length\" $ \\xs -> ite (SL.length xs .> 5 .&& 42 `SL.elem` xs) 42\n $ ite (SL.null xs) 0 (1 + length (SL.tail xs))\n\n spec :: SList Integer -> SInteger\n spec = SL.length\n\n p :: SList Integer -> SBool\n p xs = observe \"imp\" (length xs) .== observe \"spec\" (spec xs)\n\n induct <- inductionPrinciple p\n\n lemma \"bad\" (\\(Forall @\"xs\" xs) -> p xs) [induct]\n\n pure ()\n"
-- |
-- Module : Documentation.SBV.Examples.KnuckleDragger.ListLen
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: [email protected]
-- Stability : experimental
--
-- Example use of the KnuckleDragger, about lenghts of lists
-----------------------------------------------------------------------------

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeApplications #-}

{-# OPTIONS_GHC -Wall -Werror -Wno-unused-do-bind #-}

module Documentation.SBV.Examples.KnuckleDragger.ListLen where

import Prelude hiding (sum, length, reverse, (++))

import Data.SBV
import Data.SBV.Tools.KnuckleDragger

import qualified Data.SBV.List as SL

-- $setup
-- >>> -- For doctest purposes only:
-- >>> :set -XScopedTypeVariables
-- >>> import Control.Exception

-- | Prove that the length of a list is one more than the length of its tail.
--
-- We have:
--
-- >>> listLengthProof
-- Axiom: List(a).induction Admitted.
-- Lemma: length_correct Q.E.D.
listLengthProof :: IO Proven
listLengthProof = do
let length :: SList Integer -> SInteger
length = smtFunction "length" $ \xs -> ite (SL.null xs) 0 (1 + length (SL.tail xs))

spec :: SList Integer -> SInteger
spec = SL.length

p :: SList Integer -> SBool
p xs = observe "imp" (length xs) .== observe "spec" (spec xs)

induct <- inductionPrinciple p

lemma "length_correct" (\(Forall @"xs" xs) -> p xs) [induct]

-- | It is instructive to see what kind of counter-example we get if a lemma fails to prove.
-- Below, we do a variant of the 'listLengthProof', but with a bad implementation, and see the
-- counter-example. Our implementation returns an incorrect answer if the given list is longer
-- than 5 elements and have 42 in it. We have:
--
-- >>> badProof `catch` (\(_ :: SomeException) -> pure ())
-- Axiom: List(a).induction Admitted.
-- Lemma: bad
-- *** Failed to prove bad.
-- Falsifiable. Counter-example:
-- xs = [42,99,100,101,59,102] :: [Integer]
-- imp = 42 :: Integer
-- spec = 6 :: Integer
badProof :: IO ()
badProof = do
let length :: SList Integer -> SInteger
length = smtFunction "length" $ \xs -> ite (SL.length xs .> 5 .&& 42 `SL.elem` xs) 42
$ ite (SL.null xs) 0 (1 + length (SL.tail xs))

spec :: SList Integer -> SInteger
spec = SL.length

p :: SList Integer -> SBool
p xs = observe "imp" (length xs) .== observe "spec" (spec xs)

induct <- inductionPrinciple p

lemma "bad" (\(Forall @"xs" xs) -> p xs) [induct]

pure ()
159 changes: 0 additions & 159 deletions Documentation/SBV/Examples/KnuckleDragger/Lists.hs

This file was deleted.

3 changes: 2 additions & 1 deletion sbv.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,10 @@ Library
, Documentation.SBV.Examples.Lists.Fibonacci
, Documentation.SBV.Examples.Lists.BoundedMutex
, Documentation.SBV.Examples.Lists.CountOutAndTransfer
, Documentation.SBV.Examples.KnuckleDragger.AppendRev
, Documentation.SBV.Examples.KnuckleDragger.Kleene
, Documentation.SBV.Examples.KnuckleDragger.Induction
, Documentation.SBV.Examples.KnuckleDragger.Lists
, Documentation.SBV.Examples.KnuckleDragger.ListLen
, Documentation.SBV.Examples.KnuckleDragger.Sqrt2IsIrrational
, Documentation.SBV.Examples.Misc.Definitions
, Documentation.SBV.Examples.Misc.Enumerate
Expand Down

0 comments on commit bc381a0

Please sign in to comment.