feat(theory): add difference logic solver
This commit is contained in:
1 parent
ec9edca5fa
commit
95cd91cedc
1 file changed
+132
@@ -0,0 +1,132 @@
|
|||||||
|
module Sift.Theory.DiffLogic
|
||||||
|
( DiffAtom(..)
|
||||||
|
, DlProblem(..)
|
||||||
|
, DlResult(..)
|
||||||
|
, solveDl
|
||||||
|
, feasible
|
||||||
|
, evalAtom
|
||||||
|
, dlModelSatisfies
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Sift.Types
|
||||||
|
import Sift.Verify (satisfies)
|
||||||
|
import Sift.Cdcl (CdclResult(..), solveCdcl)
|
||||||
|
import Data.List (foldl')
|
||||||
|
import Data.Map.Strict (Map)
|
||||||
|
import qualified Data.Map.Strict as Map
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
|
data DiffAtom = DiffAtom
|
||||||
|
{ dlLhs :: Int
|
||||||
|
, dlRhs :: Int
|
||||||
|
, dlBound :: Int
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
data DlProblem = DlProblem
|
||||||
|
{ dlAtoms :: [DiffAtom]
|
||||||
|
, dlCnf :: CNF
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
data DlResult
|
||||||
|
= DlSat (Map Int Int)
|
||||||
|
| DlUnsat
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
data Asserted = Asserted
|
||||||
|
{ acBlock :: Lit
|
||||||
|
, acFrom :: Int
|
||||||
|
, acTo :: Int
|
||||||
|
, acW :: Int
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
solveDl :: DlProblem -> DlResult
|
||||||
|
solveDl prob = loop iterLimit (dlCnf prob)
|
||||||
|
where
|
||||||
|
atoms = dlAtoms prob
|
||||||
|
numAtoms = length atoms
|
||||||
|
iterLimit :: Int
|
||||||
|
iterLimit = if numAtoms >= 20 then 1000000 else 2 ^ numAtoms + 1
|
||||||
|
loop budget cnf
|
||||||
|
| budget <= 0 = DlUnsat
|
||||||
|
| otherwise =
|
||||||
|
case solveCdcl cnf of
|
||||||
|
CdclUnsat -> DlUnsat
|
||||||
|
CdclSat model ->
|
||||||
|
let asserted =
|
||||||
|
[ assertionOf i atom model | (i, atom) <- zip [1 ..] atoms ]
|
||||||
|
in case feasible asserted of
|
||||||
|
Right potentials -> DlSat potentials
|
||||||
|
Left core ->
|
||||||
|
let blocking = map acBlock core
|
||||||
|
in loop (budget - 1) (CNF (blocking : clauses cnf))
|
||||||
|
|
||||||
|
assertionOf :: Int -> DiffAtom -> Assignment -> Asserted
|
||||||
|
assertionOf i (DiffAtom lhs rhs bound) model =
|
||||||
|
if Map.findWithDefault True (Var i) model
|
||||||
|
then Asserted (negLit (Var i)) rhs lhs bound
|
||||||
|
else Asserted (posLit (Var i)) lhs rhs (negate bound - 1)
|
||||||
|
|
||||||
|
feasible :: [Asserted] -> Either [Asserted] (Map Int Int)
|
||||||
|
feasible asserted =
|
||||||
|
case changed of
|
||||||
|
[] -> Right distV
|
||||||
|
(x : _) ->
|
||||||
|
let onCycle = walkBack (length nodes) x
|
||||||
|
in Left (traceCycle onCycle)
|
||||||
|
where
|
||||||
|
nodes = Set.toList (Set.fromList (concatMap (\a -> [acFrom a, acTo a]) asserted))
|
||||||
|
dist0 = Map.fromList [ (nd, 0) | nd <- nodes ]
|
||||||
|
(distV, predsV) = iterateRelax (length nodes) asserted dist0 Map.empty
|
||||||
|
(distV1, predsV1) = relaxRound asserted distV predsV
|
||||||
|
changed = [ nd | nd <- nodes, Map.findWithDefault 0 nd distV1 < Map.findWithDefault 0 nd distV ]
|
||||||
|
|
||||||
|
walkBack :: Int -> Int -> Int
|
||||||
|
walkBack 0 node = node
|
||||||
|
walkBack k node =
|
||||||
|
case Map.lookup node predsV1 of
|
||||||
|
Just e -> walkBack (k - 1) (acFrom e)
|
||||||
|
Nothing -> node
|
||||||
|
|
||||||
|
traceCycle :: Int -> [Asserted]
|
||||||
|
traceCycle startNode =
|
||||||
|
case go (length nodes + 1) startNode [] of
|
||||||
|
Just cyc -> cyc
|
||||||
|
Nothing -> asserted
|
||||||
|
where
|
||||||
|
go steps cur acc
|
||||||
|
| steps < 0 = Nothing
|
||||||
|
| otherwise =
|
||||||
|
case Map.lookup cur predsV1 of
|
||||||
|
Nothing -> Nothing
|
||||||
|
Just edge ->
|
||||||
|
let prev = acFrom edge
|
||||||
|
in if prev == startNode
|
||||||
|
then Just (edge : acc)
|
||||||
|
else go (steps - 1) prev (edge : acc)
|
||||||
|
|
||||||
|
iterateRelax :: Int -> [Asserted] -> Map Int Int -> Map Int Asserted -> (Map Int Int, Map Int Asserted)
|
||||||
|
iterateRelax 0 _ dist preds = (dist, preds)
|
||||||
|
iterateRelax k edges dist preds =
|
||||||
|
let (dist', preds') = relaxRound edges dist preds
|
||||||
|
in iterateRelax (k - 1) edges dist' preds'
|
||||||
|
|
||||||
|
relaxRound :: [Asserted] -> Map Int Int -> Map Int Asserted -> (Map Int Int, Map Int Asserted)
|
||||||
|
relaxRound edges dist preds = foldl' relax (dist, preds) edges
|
||||||
|
where
|
||||||
|
relax (d, p) a =
|
||||||
|
let du = Map.findWithDefault 0 (acFrom a) d
|
||||||
|
dv = Map.findWithDefault 0 (acTo a) d
|
||||||
|
in if du + acW a < dv
|
||||||
|
then (Map.insert (acTo a) (du + acW a) d, Map.insert (acTo a) a p)
|
||||||
|
else (d, p)
|
||||||
|
|
||||||
|
evalAtom :: Map Int Int -> DiffAtom -> Bool
|
||||||
|
evalAtom assignment (DiffAtom lhs rhs bound) =
|
||||||
|
Map.findWithDefault 0 lhs assignment - Map.findWithDefault 0 rhs assignment <= bound
|
||||||
|
|
||||||
|
dlModelSatisfies :: DlProblem -> Map Int Int -> Bool
|
||||||
|
dlModelSatisfies prob assignment =
|
||||||
|
satisfies atomTruth (dlCnf prob)
|
||||||
|
where
|
||||||
|
atomTruth = Map.fromList
|
||||||
|
[ (Var i, evalAtom assignment atom) | (i, atom) <- zip [1 ..] (dlAtoms prob) ]
|
||||||
Reference in new issue
Block a user