Skip to content

Commit 885ab1b

Browse files
committed
[WIP] Upload work on a Presburger arithmetic decision procedure
1 parent 352fe4d commit 885ab1b

9 files changed

Lines changed: 424 additions & 0 deletions

File tree

bench/cardano-recon-framework/cardano-recon-framework.cabal

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ library
7979
, Cardano.ReCon.LTL.Satisfy
8080
, Cardano.ReCon.LTL.Subst
8181

82+
, Cardano.ReCon.Presburger.AffineDNF
83+
, Cardano.ReCon.Presburger.CompNF
84+
, Cardano.ReCon.Presburger.Complete
85+
, Cardano.ReCon.Presburger.DNF
86+
, Cardano.ReCon.Presburger.IntTerm.Types
87+
, Cardano.ReCon.Presburger.NegNF
88+
, Cardano.ReCon.Presburger.NormAffineDNF
89+
, Cardano.ReCon.Presburger.QuantifierFree
90+
8291
, Cardano.ReCon.Trace.Feed
8392
, Cardano.ReCon.Trace.Ingest
8493

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
module Cardano.ReCon.Presburger.AffineDNF(IntLtForm(..), IntDivForm(..), AffineDNFConjunct(..), AffineDNFDisjunct, AffineDNF, fromDNF) where
2+
import Cardano.ReCon.Presburger.DNF (DNF, DNFConjunct)
3+
import qualified Cardano.ReCon.Presburger.DNF as DNF
4+
import Cardano.ReCon.Presburger.IntTerm.Types
5+
6+
import qualified Data.Map.Strict as Map
7+
import Data.Maybe (fromMaybe)
8+
import Data.Word (Word64)
9+
10+
data IntLtForm =
11+
-- | h · x < i, where h ∈ ℕ⁺, i ∈ ℤ[x̄\{x}]
12+
LtL Word64 IntTerm
13+
-- | i < h · x, where h ∈ ℕ⁺, i ∈ ℤ⟦x̄\{x}⟧
14+
| LtR IntTerm Word64
15+
-- i < j, where i, j ∈ ℤ⟦x̄\{x}⟧
16+
| LtC IntTerm IntTerm deriving (Show, Eq, Ord)
17+
18+
data IntDivForm =
19+
-- | (h | k · x + i), where h, k ∈ ℕ⁺, i ∈ ℤ[x̄\{x}]
20+
DivL Word64 Word64 IntTerm
21+
-- | (h | i), where h ∈ ℕ⁺, i ∈ ℤ[x̄\{x}]
22+
| DivC Word64 IntTerm deriving (Show, Eq, Ord)
23+
24+
data AffineDNFConjunct =
25+
-- | h · x < i | i < h · x | i < j
26+
IntLt IntLtForm
27+
-- | (h | k · x + i) | (h | i)
28+
| IntDiv IntDivForm
29+
-- | ¬ (h | k · x + i) | ¬ (h | i)
30+
| IntNegDiv IntDivForm
31+
deriving (Show, Eq, Ord)
32+
33+
type AffineDNFDisjunct = [AffineDNFConjunct]
34+
35+
type AffineDNF = [AffineDNFDisjunct]
36+
37+
classifyIntLt :: IntIdentifier -> IntTerm -> IntTerm -> IntLtForm
38+
classifyIntLt x a b =
39+
let nfa = nf a in
40+
let nfb = nf b in
41+
let ka = fromMaybe 0 $ Map.lookup x nfa.coeff in
42+
let kb = fromMaybe 0 $ Map.lookup x nfb.coeff in
43+
case compare (ka - kb) 0 of
44+
-- (k(a) - k(b)) · x < ...
45+
GT -> LtL (fromIntegral $ ka - kb)
46+
(quote (nullify x nfb `minus` nullify x nfa))
47+
-- ... < ...
48+
EQ -> LtC (quote $ nullify x nfa) (quote $ nullify x nfb)
49+
-- ... < (k(b) - k(a)) · x
50+
LT -> LtR (quote (nullify x nfa `minus` nullify x nfb))
51+
(fromIntegral $ kb - ka)
52+
53+
classifyIntDiv :: IntIdentifier -> Word64 -> IntTerm -> IntDivForm
54+
classifyIntDiv x k t =
55+
let coef = fromMaybe 0 $ Map.lookup x (nf t).coeff in
56+
if coef == 0
57+
then DivC k (normalise t)
58+
else DivL k (fromIntegral $ abs coef) (quote $ nullify x (nf t))
59+
60+
fromDNFConjunct :: IntIdentifier -> DNFConjunct -> AffineDNFConjunct
61+
fromDNFConjunct x (DNF.IntLt i j) = IntLt $ classifyIntLt x i j
62+
fromDNFConjunct x (DNF.IntDiv k i) = IntDiv $ classifyIntDiv x k i
63+
fromDNFConjunct x (DNF.IntNegDiv k i) = IntNegDiv $ classifyIntDiv x k i
64+
65+
fromDNF :: IntIdentifier -> DNF -> AffineDNF
66+
fromDNF x = map (map (fromDNFConjunct x))
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
module Cardano.ReCon.Presburger.CompNF(CompNF(..), fromNegNF) where
2+
import qualified Cardano.ReCon.Presburger.Complete as BinRel (IntBinRel (..))
3+
import Cardano.ReCon.Presburger.IntTerm.Types
4+
import Cardano.ReCon.Presburger.NegNF (NegNF)
5+
import qualified Cardano.ReCon.Presburger.NegNF as N
6+
7+
import Data.Word (Word64)
8+
9+
data CompNF =
10+
-- | φ ∨ ψ
11+
Or CompNF CompNF
12+
-- | φ ∧ ψ
13+
| And CompNF CompNF
14+
-- | T
15+
| Top
16+
-- |
17+
| Bottom
18+
-- | i < i
19+
| IntLt IntTerm IntTerm
20+
-- | ℕ⁺ \| i (divisibility relation, first argument is a non-zero ℕ)
21+
| IntDiv Word64 IntTerm
22+
-- | ¬ (ℕ⁺ \| i)
23+
| IntNegDiv Word64 IntTerm
24+
deriving (Show, Eq, Ord)
25+
26+
fromNegNF :: NegNF -> CompNF
27+
fromNegNF (N.Or a b) = Or (fromNegNF a) (fromNegNF b)
28+
fromNegNF (N.And a b) = And (fromNegNF a) (fromNegNF b)
29+
fromNegNF N.Bottom = Bottom
30+
fromNegNF N.Top = Top
31+
-- s = t <=> s < t + 1 ∧ t < s + 1
32+
fromNegNF (N.IntBinRel BinRel.IntEq s t) = And (IntLt s (IntSum t (IntConst 1))) (IntLt t (IntSum s (IntConst 1)))
33+
fromNegNF (N.IntBinRel BinRel.IntLt s t) = IntLt s t
34+
-- s ≤ t <=> s < t + 1
35+
fromNegNF (N.IntBinRel BinRel.IntLte s t) = IntLt s (IntSum t (IntConst 1))
36+
-- s > t <=> t < s
37+
fromNegNF (N.IntBinRel BinRel.IntGt s t) = IntLt t s
38+
-- s ≥ t <=> t < s + 1
39+
fromNegNF (N.IntBinRel BinRel.IntGte s t) = IntLt t (IntSum s (IntConst 1))
40+
-- ¬ (s = t) <=> s < t ∨ t < s
41+
fromNegNF (N.IntNegBinRel BinRel.IntEq s t) = Or (IntLt s t) (IntLt t s)
42+
-- ¬ (s < t) <=> t < s + 1
43+
fromNegNF (N.IntNegBinRel BinRel.IntLt s t) = IntLt t (IntSum s (IntConst 1))
44+
-- ¬ (s ≤ t) <=> s > t <=> t < s
45+
fromNegNF (N.IntNegBinRel BinRel.IntLte s t) = IntLt t s
46+
-- ¬ (s > t) <=> s ≤ t <=> s < t + 1
47+
fromNegNF (N.IntNegBinRel BinRel.IntGt s t) = IntLt s (IntSum t (IntConst 1))
48+
-- ¬ (s ≥ t) <=> s < t
49+
fromNegNF (N.IntNegBinRel BinRel.IntGte s t) = IntLt s t
50+
fromNegNF (N.IntDiv k t) = IntDiv k t
51+
fromNegNF (N.IntNegDiv k t) = IntNegDiv k t
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
2+
{-# HLINT ignore "Eta reduce" #-}
3+
module Cardano.ReCon.Presburger.Complete(IntBinRel(..), Complete(..), unfoldImplies) where
4+
5+
import Cardano.ReCon.Presburger.IntTerm.Types
6+
7+
import Data.Word (Word64)
8+
9+
data IntBinRel = IntEq | IntLt | IntLte | IntGt | IntGte deriving (Show, Eq, Ord)
10+
11+
data Complete =
12+
-- | φ ∨ ψ
13+
Or Complete Complete
14+
-- | φ ∧ ψ
15+
| And Complete Complete
16+
-- | ¬ φ
17+
| Not Complete
18+
-- | φ ⇒ ψ
19+
| Implies Complete Complete
20+
-- | T
21+
| Top
22+
-- |
23+
| Bottom
24+
-- | ∀x ∈ ℤ. φ
25+
| IntForall IntIdentifier Complete
26+
-- | ∃x ∈ ℤ. φ
27+
| IntExists IntIdentifier Complete
28+
-- | i = i | i < i | i ≤ i | i > i | i ≥ i
29+
| IntBinRel IntBinRel IntTerm IntTerm
30+
-- | ℕ⁺ \| i (divisibility relation, first argument is a non-zero ℕ)
31+
| IntDiv Word64 IntTerm deriving (Show, Eq, Ord)
32+
33+
unfoldImplies :: Complete -> Complete -> Complete
34+
unfoldImplies a b = Or (Not a) b
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Cardano.ReCon.Presburger.DNF(DNFConjunct(..), DNFDisjunct, DNF, fromCompNF) where
2+
3+
4+
import Cardano.ReCon.Presburger.CompNF (CompNF)
5+
import qualified Cardano.ReCon.Presburger.CompNF as C
6+
import Cardano.ReCon.Presburger.IntTerm.Types
7+
8+
import Data.Word (Word64)
9+
10+
data DNFConjunct =
11+
-- | i < i
12+
IntLt IntTerm IntTerm
13+
-- | ℕ⁺ \| i (divisibility relation, first argument is a non-zero ℕ)
14+
| IntDiv Word64 IntTerm
15+
-- | ¬ (ℕ⁺ \| i)
16+
| IntNegDiv Word64 IntTerm deriving (Show, Eq, Ord)
17+
18+
type DNFDisjunct = [DNFConjunct]
19+
type DNF = [DNFDisjunct]
20+
21+
fromCompNF :: CompNF -> DNF
22+
fromCompNF (C.Or a b) = fromCompNF a ++ fromCompNF b
23+
fromCompNF (C.And a b) = [p ++ q | p <- fromCompNF a, q <- fromCompNF b]
24+
fromCompNF C.Top = [[]]
25+
fromCompNF C.Bottom = []
26+
fromCompNF (C.IntLt i j) = [[IntLt i j]]
27+
fromCompNF (C.IntDiv n i) = [[IntDiv n i]]
28+
fromCompNF (C.IntNegDiv n i) = [[IntNegDiv n i]]
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
module Cardano.ReCon.Presburger.IntTerm.Types(
2+
IntIdentifier
3+
, IntValue
4+
, NatValue
5+
, IntTerm(..)
6+
, IntTermNF(..)
7+
, mulTerm
8+
, zero
9+
, plus
10+
, minus
11+
, mul
12+
, nullify
13+
, nf
14+
, quote
15+
, normalise) where
16+
import Data.Int (Int64)
17+
import qualified Data.List as List
18+
import Data.Map.Strict (Map)
19+
import qualified Data.Map.Strict as Map
20+
import Data.Text (Text)
21+
import Data.Word (Word64)
22+
23+
type IntIdentifier = Text
24+
type IntValue = Int64
25+
type NatValue = Word64
26+
27+
-- i, j ::= i + i | <int> · x | <int>, where <int> is an arbitrary integer
28+
data IntTerm = IntVar IntValue IntIdentifier | IntConst Int64 | IntSum IntTerm IntTerm deriving (Show, Eq, Ord)
29+
30+
-- k · i
31+
-- k · (k' · x) = (k * k') · x
32+
-- k · c = k * c
33+
-- k · (i + j) = k · i + k · j
34+
mulTerm :: IntValue -> IntTerm -> IntTerm
35+
mulTerm k (IntVar k' x) = IntVar (k * k') x
36+
mulTerm k (IntConst k') = IntConst (k * k')
37+
mulTerm k (IntSum a b) = IntSum (mulTerm k a) (mulTerm k b)
38+
39+
-- | c + k₀ · x₀ + k₁ · x₁ + ... + kₙ · xₙ
40+
data IntTermNF = IntTermNF {
41+
-- | Invariant: the coefficients must be non-zero.
42+
coeff :: Map IntIdentifier IntValue,
43+
constant :: IntValue
44+
} deriving (Show, Eq, Ord)
45+
46+
-- 0 + 0 · x₀ + ... + 0 · xₙ
47+
zero :: IntTermNF
48+
zero = IntTermNF{coeff = Map.empty, constant = 0}
49+
50+
-- | (c + k₀ · x₀ + k₁ · x₁ + ... + kₙ · xₙ) + (c' + k₀' · x₀ + k₁' · x₁ + ... + kₙ' · xₙ)
51+
-- =
52+
-- ((c + c') + (k₀ + k₀') · x₀ + (k₁ + k₁') · x₁ + ... + (kₙ + kₙ') · xₙ)
53+
plus :: IntTermNF -> IntTermNF -> IntTermNF
54+
plus IntTermNF{coeff, constant} IntTermNF{coeff = coeff', constant = constant'} =
55+
IntTermNF{coeff = Map.filter (/= 0) $ Map.unionWith (+) coeff coeff', constant = constant + constant'}
56+
57+
minus :: IntTermNF -> IntTermNF -> IntTermNF
58+
minus IntTermNF{coeff, constant} IntTermNF{coeff = coeff', constant = constant'} =
59+
IntTermNF{coeff = Map.filter (/= 0) $ Map.unionWith (-) coeff coeff', constant = constant + constant'}
60+
61+
-- | k * (c + k₀ · x₀ + k₁ · x₁ + ... + kₙ · xₙ)
62+
-- =
63+
-- ((k * c) + (k * k₀) · x₀ + (k * k₁) · x₁ + ... + (k * kₙ) · xₙ)
64+
mul :: IntValue -> IntTermNF -> IntTermNF
65+
mul k IntTermNF{..} = IntTermNF{constant = k * constant, coeff = Map.filter (/= 0) $ Map.map (* k) coeff}
66+
67+
setCoeff :: IntValue -> IntIdentifier -> IntTermNF -> IntTermNF
68+
setCoeff k x IntTermNF{..} = IntTermNF{coeff = Map.filter (/= 0) $ Map.insert x k coeff, constant}
69+
70+
nullify :: IntIdentifier -> IntTermNF -> IntTermNF
71+
nullify = setCoeff 0
72+
73+
nf :: IntTerm -> IntTermNF
74+
nf (IntConst k) = IntTermNF {coeff = Map.empty, constant = k}
75+
nf (IntVar k x) = IntTermNF {coeff = Map.filter (/= 0) (Map.singleton x k), constant = 0}
76+
nf (IntSum a b) = nf a `plus` nf b
77+
78+
quote :: IntTermNF -> IntTerm
79+
quote IntTermNF{..} = IntSum (IntConst constant) (List.foldl' go (IntConst 0) (Map.toList coeff)) where
80+
go :: IntTerm -> (IntIdentifier, IntValue) -> IntTerm
81+
go lhs (x, k) = IntSum lhs (IntVar k x)
82+
83+
normalise :: IntTerm -> IntTerm
84+
normalise = quote . nf
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module Cardano.ReCon.Presburger.NegNF(NegNF(..), fromQuantifierFree) where
2+
3+
import Cardano.ReCon.Presburger.Complete (IntBinRel)
4+
import Cardano.ReCon.Presburger.IntTerm.Types
5+
import Cardano.ReCon.Presburger.QuantifierFree (QuantifierFree, unfoldImplies)
6+
import qualified Cardano.ReCon.Presburger.QuantifierFree as Q
7+
8+
import Data.Word (Word64)
9+
10+
data NegNF =
11+
-- | φ ∨ ψ
12+
Or NegNF NegNF
13+
-- | φ ∧ ψ
14+
| And NegNF NegNF
15+
-- | T
16+
| Top
17+
-- |
18+
| Bottom
19+
-- | i = i | i < i | i ≤ i | i > i | i ≥ i
20+
| IntBinRel IntBinRel IntTerm IntTerm
21+
-- | ¬ (i = i) | ¬ (i < i) | ¬ (i ≤ i) | ¬ (i > i) | ¬ (i ≥ i)
22+
| IntNegBinRel IntBinRel IntTerm IntTerm
23+
-- | ℕ⁺ \| i (divisibility relation, first argument is a non-zero ℕ)
24+
| IntDiv Word64 IntTerm
25+
-- | ¬ (ℕ⁺ \| i)
26+
| IntNegDiv Word64 IntTerm
27+
deriving (Show, Eq, Ord)
28+
29+
fromQuantifierFreeNeg :: QuantifierFree -> NegNF
30+
fromQuantifierFreeNeg (Q.Or a b) = And (fromQuantifierFreeNeg a) (fromQuantifierFreeNeg b)
31+
fromQuantifierFreeNeg (Q.And a b) = Or (fromQuantifierFreeNeg a) (fromQuantifierFreeNeg b)
32+
fromQuantifierFreeNeg Q.Top = Bottom
33+
fromQuantifierFreeNeg Q.Bottom = Top
34+
fromQuantifierFreeNeg (Q.IntBinRel r i i') = IntNegBinRel r i i'
35+
fromQuantifierFreeNeg (Q.IntDiv r i) = IntNegDiv r i
36+
fromQuantifierFreeNeg (Q.Not t) = fromQuantifierFree t
37+
fromQuantifierFreeNeg (Q.Implies a b) = fromQuantifierFreeNeg (unfoldImplies a b)
38+
39+
fromQuantifierFree :: QuantifierFree -> NegNF
40+
fromQuantifierFree (Q.Or a b) = Or (fromQuantifierFree a) (fromQuantifierFree b)
41+
fromQuantifierFree (Q.And a b) = And (fromQuantifierFree a) (fromQuantifierFree b)
42+
fromQuantifierFree Q.Top = Top
43+
fromQuantifierFree Q.Bottom = Bottom
44+
fromQuantifierFree (Q.IntBinRel r i i') = IntBinRel r i i'
45+
fromQuantifierFree (Q.IntDiv r i) = IntDiv r i
46+
fromQuantifierFree (Q.Not t) = fromQuantifierFreeNeg t
47+
fromQuantifierFree (Q.Implies a b) = fromQuantifierFree (unfoldImplies a b)

0 commit comments

Comments
 (0)