-----------------------------------------------------------------------------
-- |
-- Module : Documentation.SBV.Examples.TP.Kleene
-- Copyright : (c) Levent Erkok
-- License : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Example use of the TP layer, proving some Kleene algebra theorems.
--
-- Based on
-----------------------------------------------------------------------------
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeAbstractions #-}
{-# OPTIONS_GHC -Wall -Werror -Wno-unused-matches #-}
module Documentation.SBV.Examples.TP.Kleene where
import Prelude hiding((<=))
import Data.SBV
import Data.SBV.TP
-- | An uninterpreted sort, corresponding to the type of Kleene algebra strings.
data Kleene
mkSymbolic [''Kleene]
-- | Star operator over kleene algebras. We're leaving this uninterpreted.
star :: SKleene -> SKleene
star = uninterpret "STAR"
-- | The 'Num' instance for Kleene makes it easy to write regular expressions
-- in the more familiar form.
instance Num SKleene where
(+) = uninterpret "PAR"
(*) = uninterpret "SEQ"
abs = error "SKleene: not defined: abs"
signum = error "SKleene: not defined: signum"
negate = error "SKleene: not defined: signum"
fromInteger 0 = uninterpret "zero"
fromInteger 1 = uninterpret "one"
fromInteger n = error $ "SKleene: not defined: fromInteger " ++ show n
-- | The set of strings matched by one regular expression is a subset of the second,
-- if adding it to the second doesn't change the second set.
(<=) :: SKleene -> SKleene -> SBool
x <= y = x + y .== y
-- | A sequence of Kleene algebra proofs. See
--
-- We have:
--
-- >>> kleeneProofs
-- Axiom: par_assoc
-- Axiom: par_comm
-- Axiom: par_idem
-- Axiom: par_zero
-- Axiom: seq_assoc
-- Axiom: seq_zero
-- Axiom: seq_one
-- Axiom: rdistrib
-- Axiom: ldistrib
-- Axiom: unfold
-- Axiom: least_fix
-- Lemma: par_lzero Q.E.D.
-- Lemma: par_monotone Q.E.D.
-- Lemma: seq_monotone Q.E.D.
-- Lemma: star_star_1
-- Step: 1 (unfold) Q.E.D.
-- Step: 2 (factor out x * star x) Q.E.D.
-- Step: 3 (par_idem) Q.E.D.
-- Step: 4 (unfold) Q.E.D.
-- Result: Q.E.D.
-- Lemma: subset_eq Q.E.D.
-- Lemma: star_star_2_2 Q.E.D.
-- Lemma: star_star_2_3 Q.E.D.
-- Lemma: star_star_2_1 Q.E.D.
-- Lemma: star_star_2 Q.E.D.
kleeneProofs :: IO ()
kleeneProofs = runTP $ do
-- Kozen axioms
par_assoc <- axiom "par_assoc" $ \(Forall @"x" (x :: SKleene)) (Forall @"y" y) (Forall @"z" z) -> x + (y + z) .== (x + y) + z
par_comm <- axiom "par_comm" $ \(Forall @"x" (x :: SKleene)) (Forall @"y" y) -> x + y .== y + x
par_idem <- axiom "par_idem" $ \(Forall @"x" (x :: SKleene)) -> x + x .== x
par_zero <- axiom "par_zero" $ \(Forall @"x" (x :: SKleene)) -> x + 0 .== x
seq_assoc <- axiom "seq_assoc" $ \(Forall @"x" (x :: SKleene)) (Forall @"y" y) (Forall @"z" z) -> x * (y * z) .== (x * y) * z
seq_zero <- axiom "seq_zero" $ \(Forall @"x" (x :: SKleene)) -> x * 0 .== 0
seq_one <- axiom "seq_one" $ \(Forall @"x" (x :: SKleene)) -> x * 1 .== x
rdistrib <- axiom "rdistrib" $ \(Forall @"x" (x :: SKleene)) (Forall @"y" y) (Forall @"z" z) -> x * (y + z) .== x * y + x * z
ldistrib <- axiom "ldistrib" $ \(Forall @"x" (x :: SKleene)) (Forall @"y" y) (Forall @"z" z) -> (y + z) * x .== y * x + z * x
unfold <- axiom "unfold" $ \(Forall @"e" e) -> star e .== 1 + e * star e
least_fix <- axiom "least_fix" $ \(Forall @"x" x) (Forall @"e" e) (Forall @"f" f) -> ((f + e * x) <= x) .=> ((star e * f) <= x)
-- Collect the basic axioms in a list for easy reference
let kleene = [ proofOf par_assoc, proofOf par_comm, proofOf par_idem, proofOf par_zero
, proofOf seq_assoc, proofOf seq_zero, proofOf seq_one
, proofOf ldistrib, proofOf rdistrib
, proofOf unfold
, proofOf least_fix
]
-- Various proofs:
par_lzero <- lemma "par_lzero" (\(Forall @"x" x) -> (0 :: SKleene) + x .== x) kleene
par_monotone <- lemma "par_monotone" (\(Forall @"x" x) (Forall @"y" y) (Forall @"z" z) -> x <= y .=> ((x + z) <= (y + z))) kleene
seq_monotone <- lemma "seq_monotone" (\(Forall @"x" x) (Forall @"y" y) (Forall @"z" z) -> x <= y .=> ((x * z) <= (y * z))) kleene
-- This one requires a chain of reasoning: x* x* == x*
star_star_1 <- calc "star_star_1"
(\(Forall @"x" x) -> star x * star x .== star x) $
\x -> [] |- star x * star x ?? unfold
=: (1 + x * star x) * (1 + x * star x)
?? "factor out x * star x"
?? kleene
=: (1 + 1) + (x * star x + x * star x) ?? par_idem
=: 1 + x * star x ?? unfold
=: star x
=: qed
subset_eq <- lemma "subset_eq" (\(Forall @"x" x) (Forall @"y" y) -> (x .== y) .== (x <= y .&& y <= x)) kleene
-- Prove: x** = x*
star_star_2 <- do _1 <- lemma "star_star_2_2" (\(Forall @"x" x) -> ((star x * star x + 1) <= star x) .=> star (star x) <= star x) kleene
_2 <- lemma "star_star_2_3" (\(Forall @"x" x) -> star (star x) <= star x) (kleene ++ [proofOf _1])
_3 <- lemma "star_star_2_1" (\(Forall @"x" x) -> star x <= star (star x)) kleene
lemma "star_star_2" (\(Forall @"x" x) -> star (star x) .== star x) [proofOf subset_eq, proofOf _2, proofOf _3]
pure ()