• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

msakai / toysolver / 486

10 Nov 2024 12:29AM UTC coverage: 71.177% (-1.0%) from 72.221%
486

push

github

web-flow
Merge pull request #115 from msakai/github-actions-hash-stack-yaml

GitHub Actions: include stack.yaml contents into the key of the cache

9962 of 13996 relevant lines covered (71.18%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.57
/src/ToySolver/SAT/Types.hs
1
{-# OPTIONS_HADDOCK show-extensions #-}
2
{-# LANGUAGE BangPatterns #-}
3
{-# LANGUAGE FlexibleInstances #-}
4
{-# LANGUAGE FunctionalDependencies #-}
5
{-# LANGUAGE MultiParamTypeClasses #-}
6
{-# LANGUAGE ScopedTypeVariables #-}
7
-----------------------------------------------------------------------------
8
-- |
9
-- Module      :  ToySolver.SAT.Types
10
-- Copyright   :  (c) Masahiro Sakai 2012
11
-- License     :  BSD-style
12
--
13
-- Maintainer  :  masahiro.sakai@gmail.com
14
-- Stability   :  provisional
15
-- Portability :  non-portable
16
--
17
-----------------------------------------------------------------------------
18
module ToySolver.SAT.Types
19
  (
20
  -- * Variable
21
    Var
22
  , VarSet
23
  , VarMap
24
  , validVar
25

26
  -- * Model
27
  , IModel (..)
28
  , Model
29
  , restrictModel
30

31
  -- * Literal
32
  , Lit
33
  , LitSet
34
  , LitMap
35
  , litUndef
36
  , validLit
37
  , literal
38
  , litNot
39
  , litVar
40
  , litPolarity
41
  , evalLit
42

43
  -- * Clause
44
  , Clause
45
  , normalizeClause
46
  , instantiateClause
47
  , clauseSubsume
48
  , evalClause
49
  , clauseToPBLinAtLeast
50

51
  -- * Packed Clause
52
  , PackedVar
53
  , PackedLit
54
  , packLit
55
  , unpackLit
56
  , PackedClause
57
  , packClause
58
  , unpackClause
59

60
  -- * Cardinality Constraint
61
  , AtLeast
62
  , Exactly
63
  , normalizeAtLeast
64
  , instantiateAtLeast
65
  , evalAtLeast
66
  , evalExactly
67

68
  -- * (Linear) Pseudo Boolean Constraint
69
  , PBLinTerm
70
  , PBLinSum
71
  , PBLinAtLeast
72
  , PBLinExactly
73
  , normalizePBLinSum
74
  , normalizePBLinAtLeast
75
  , normalizePBLinExactly
76
  , instantiatePBLinAtLeast
77
  , instantiatePBLinExactly
78
  , cutResolve
79
  , cardinalityReduction
80
  , negatePBLinAtLeast
81
  , evalPBLinSum
82
  , evalPBLinAtLeast
83
  , evalPBLinExactly
84
  , pbLinLowerBound
85
  , pbLinUpperBound
86
  , pbLinSubsume
87

88
  -- * Non-linear Pseudo Boolean constraint
89
  , PBTerm
90
  , PBSum
91
  , evalPBSum
92
  , evalPBConstraint
93
  , evalPBFormula
94
  , pbLowerBound
95
  , pbUpperBound
96
  , removeNegationFromPBSum
97

98
  -- * XOR Clause
99
  , XORClause
100
  , normalizeXORClause
101
  , instantiateXORClause
102
  , evalXORClause
103

104
  -- * Type classes for solvers
105
  , NewVar (..)
106
  , AddClause (..)
107
  , AddCardinality (..)
108
  , AddPBLin (..)
109
  , AddPBNL (..)
110
  , AddXORClause (..)
111
  ) where
112

113
import Control.Monad
114
import Control.Exception
115
import Data.Array.Unboxed
116
import Data.Ord
117
import Data.List
118
import Data.Int
119
import Data.IntMap.Strict (IntMap)
120
import qualified Data.IntMap.Strict as IntMap
121
import Data.IntSet (IntSet)
122
import qualified Data.IntSet as IntSet
123
import Data.Map.Strict (Map)
124
import qualified Data.Map.Strict as Map
125
import Data.Maybe
126
import qualified Data.Vector as V
127
import qualified Data.Vector.Unboxed as VU
128
import qualified Data.PseudoBoolean as PBFile
129
import ToySolver.Data.LBool
130
import qualified ToySolver.Combinatorial.SubsetSum as SubsetSum
131

132
-- | Variable is represented as positive integers (DIMACS format).
133
type Var = Int
134

135
type VarSet = IntSet
136
type VarMap = IntMap
137

138
{-# INLINE validVar #-}
139
validVar :: Var -> Bool
140
validVar v = v > 0
×
141

142
class IModel a where
143
  evalVar :: a -> Var -> Bool
144

145
-- | A model is represented as a mapping from variables to its values.
146
type Model = UArray Var Bool
147

148
-- | Restrict model to first @nv@ variables.
149
restrictModel :: Int -> Model -> Model
150
restrictModel nv m = array (1,nv) [(v, m ! v) | v <- [1..nv]]
1✔
151

152
instance IModel (UArray Var Bool) where
153
  evalVar m v = m ! v
1✔
154

155
instance IModel (Array Var Bool) where
156
  evalVar m v = m ! v
1✔
157

158
instance IModel (Var -> Bool) where
159
  evalVar m v = m v
×
160

161
-- | Positive (resp. negative) literals are represented as positive (resp.
162
-- negative) integers. (DIMACS format).
163
type Lit = Int
164

165
{-# INLINE litUndef #-}
166
litUndef :: Lit
167
litUndef = 0
1✔
168

169
type LitSet = IntSet
170
type LitMap = IntMap
171

172
{-# INLINE validLit #-}
173
validLit :: Lit -> Bool
174
validLit l = l /= 0
×
175

176
{-# INLINE literal #-}
177
-- | Construct a literal from a variable and its polarity.
178
-- 'True' (resp 'False') means positive (resp. negative) literal.
179
literal :: Var  -- ^ variable
180
        -> Bool -- ^ polarity
181
        -> Lit
182
literal v polarity =
1✔
183
  assert (validVar v) $ if polarity then v else litNot v
×
184

185
{-# INLINE litNot #-}
186
-- | Negation of the 'Lit'.
187
litNot :: Lit -> Lit
188
litNot l = assert (validLit l) $ negate l
×
189

190
{-# INLINE litVar #-}
191
-- | Underlying variable of the 'Lit'
192
litVar :: Lit -> Var
193
litVar l = assert (validLit l) $ abs l
×
194

195
{-# INLINE litPolarity #-}
196
-- | Polarity of the 'Lit'.
197
-- 'True' means positive literal and 'False' means negative literal.
198
litPolarity :: Lit -> Bool
199
litPolarity l = assert (validLit l) $ l > 0
×
200

201
{-# INLINEABLE evalLit #-}
202
{-# SPECIALIZE evalLit :: Model -> Lit -> Bool #-}
203
evalLit :: IModel m => m -> Lit -> Bool
204
evalLit m l = if l > 0 then evalVar m l else not (evalVar m (abs l))
1✔
205

206
-- | Disjunction of 'Lit'.
207
type Clause = [Lit]
208

209
-- | Normalizing clause
210
--
211
-- 'Nothing' if the clause is trivially true.
212
normalizeClause :: Clause -> Maybe Clause
213
normalizeClause lits = assert (IntSet.size ys `mod` 2 == 0) $
×
214
  if IntSet.null ys
1✔
215
    then Just (IntSet.toList xs)
1✔
216
    else Nothing
1✔
217
  where
218
    xs = IntSet.fromList lits
1✔
219
    ys = xs `IntSet.intersection` (IntSet.map litNot xs)
1✔
220

221
{-# SPECIALIZE instantiateClause :: (Lit -> IO LBool) -> Clause -> IO (Maybe Clause) #-}
222
instantiateClause :: forall m. Monad m => (Lit -> m LBool) -> Clause -> m (Maybe Clause)
223
instantiateClause evalLitM = loop []
1✔
224
  where
225
    loop :: [Lit] -> [Lit] -> m (Maybe Clause)
226
    loop ret [] = return $ Just ret
1✔
227
    loop ret (l:ls) = do
1✔
228
      val <- evalLitM l
1✔
229
      if val==lTrue then
1✔
230
        return Nothing
1✔
231
      else if val==lFalse then
1✔
232
        loop ret ls
1✔
233
      else
234
        loop (l : ret) ls
1✔
235

236
clauseSubsume :: Clause -> Clause -> Bool
237
clauseSubsume cl1 cl2 = cl1' `IntSet.isSubsetOf` cl2'
×
238
  where
239
    cl1' = IntSet.fromList cl1
×
240
    cl2' = IntSet.fromList cl2
×
241

242
evalClause :: IModel m => m -> Clause -> Bool
243
evalClause m cl = any (evalLit m) cl
1✔
244

245
clauseToPBLinAtLeast :: Clause -> PBLinAtLeast
246
clauseToPBLinAtLeast xs = ([(1,l) | l <- xs], 1)
1✔
247

248
type PackedVar = PackedLit
249
type PackedLit = Int32
250

251
packLit :: Lit -> PackedLit
252
packLit = fromIntegral
1✔
253

254
unpackLit :: PackedLit -> Lit
255
unpackLit = fromIntegral
1✔
256

257
type PackedClause = VU.Vector PackedLit
258

259
packClause :: Clause -> PackedClause
260
packClause = VU.fromList . map packLit
1✔
261

262
unpackClause :: PackedClause -> Clause
263
unpackClause = map unpackLit . VU.toList
1✔
264

265
type AtLeast = ([Lit], Int)
266
type Exactly = ([Lit], Int)
267

268
normalizeAtLeast :: AtLeast -> AtLeast
269
normalizeAtLeast (lits,n) = assert (IntSet.size ys `mod` 2 == 0) $
×
270
   (IntSet.toList lits', n')
1✔
271
   where
272
     xs = IntSet.fromList lits
1✔
273
     ys = xs `IntSet.intersection` (IntSet.map litNot xs)
1✔
274
     lits' = xs `IntSet.difference` ys
1✔
275
     n' = n - (IntSet.size ys `div` 2)
1✔
276

277
{-# SPECIALIZE instantiateAtLeast :: (Lit -> IO LBool) -> AtLeast -> IO AtLeast #-}
278
instantiateAtLeast :: forall m. Monad m => (Lit -> m LBool) -> AtLeast -> m AtLeast
279
instantiateAtLeast evalLitM (xs,n) = loop ([],n) xs
1✔
280
  where
281
    loop :: AtLeast -> [Lit] -> m AtLeast
282
    loop ret [] = return ret
1✔
283
    loop (ys,m) (l:ls) = do
1✔
284
      val <- evalLitM l
1✔
285
      if val == lTrue then
1✔
286
        loop (ys, m-1) ls
1✔
287
      else if val == lFalse then
1✔
288
        loop (ys, m) ls
1✔
289
      else
290
        loop (l:ys, m) ls
1✔
291

292
evalAtLeast :: IModel m => m -> AtLeast -> Bool
293
evalAtLeast m (lits,n) = sum [1 | lit <- lits, evalLit m lit] >= n
1✔
294

295
evalExactly :: IModel m => m -> Exactly -> Bool
296
evalExactly m (lits,n) = sum [1 | lit <- lits, evalLit m lit] == n
×
297

298
type PBLinTerm = (Integer, Lit)
299
type PBLinSum = [PBLinTerm]
300
type PBLinAtLeast = (PBLinSum, Integer)
301
type PBLinExactly = (PBLinSum, Integer)
302

303
-- | normalizing PB term of the form /c1 x1 + c2 x2 ... cn xn + c/ into
304
-- /d1 x1 + d2 x2 ... dm xm + d/ where d1,...,dm ≥ 1.
305
normalizePBLinSum :: (PBLinSum, Integer) -> (PBLinSum, Integer)
306
normalizePBLinSum = step2 . step1
1✔
307
  where
308
    -- 同じ変数が複数回現れないように、一度全部 @v@ に統一。
309
    step1 :: (PBLinSum, Integer) -> (PBLinSum, Integer)
310
    step1 (xs,n) =
1✔
311
      case loop (IntMap.empty,n) xs of
1✔
312
        (ys,n') -> ([(c,v) | (v,c) <- IntMap.toList ys], n')
1✔
313
      where
314
        loop :: (VarMap Integer, Integer) -> PBLinSum -> (VarMap Integer, Integer)
315
        loop (ys,m) [] = (ys,m)
1✔
316
        loop (ys,m) ((c,l):zs) =
317
          if litPolarity l
1✔
318
            then loop (IntMap.insertWith (+) l c ys, m) zs
1✔
319
            else loop (IntMap.insertWith (+) (litNot l) (negate c) ys, m+c) zs
1✔
320

321
    -- 係数が0のものも取り除き、係数が負のリテラルを反転することで、
322
    -- 係数が正になるようにする。
323
    step2 :: (PBLinSum, Integer) -> (PBLinSum, Integer)
324
    step2 (xs,n) = loop ([],n) xs
1✔
325
      where
326
        loop (ys,m) [] = (ys,m)
1✔
327
        loop (ys,m) (t@(c,l):zs)
328
          | c == 0 = loop (ys,m) zs
1✔
329
          | c < 0  = loop ((negate c,litNot l):ys, m+c) zs
1✔
330
          | otherwise = loop (t:ys,m) zs
1✔
331

332
-- | normalizing PB constraint of the form /c1 x1 + c2 cn ... cn xn >= b/.
333
normalizePBLinAtLeast :: PBLinAtLeast -> PBLinAtLeast
334
normalizePBLinAtLeast a =
1✔
335
  case step1 a of
1✔
336
    (xs,n)
337
      | n > 0     -> step4 $ step3 (xs,n)
1✔
338
      | otherwise -> ([], 0) -- trivially true
1✔
339
  where
340
    step1 :: PBLinAtLeast -> PBLinAtLeast
341
    step1 (xs,n) =
1✔
342
      case normalizePBLinSum (xs,-n) of
1✔
343
        (ys,m) -> (ys, -m)
1✔
344

345
    -- saturation + gcd reduction
346
    step3 :: PBLinAtLeast -> PBLinAtLeast
347
    step3 (xs,n) =
1✔
348
      case [c | (c,_) <- xs, assert (c>0) (c < n)] of
×
349
        [] -> ([(1,l) | (c,l) <- xs], 1)
1✔
350
        cs ->
351
          let d = foldl1' gcd cs
1✔
352
              m = (n+d-1) `div` d
1✔
353
          in ([(if c >= n then m else c `div` d, l) | (c,l) <- xs], m)
1✔
354

355
    -- subset sum
356
    step4 :: PBLinAtLeast -> PBLinAtLeast
357
    step4 (xs,n) =
1✔
358
      case SubsetSum.minSubsetSum (V.fromList [c | (c,_) <- xs]) n of
1✔
359
        Just (m, _) -> (xs, m)
1✔
360
        Nothing -> ([], 1) -- false
1✔
361

362
-- | normalizing PB constraint of the form /c1 x1 + c2 cn ... cn xn = b/.
363
normalizePBLinExactly :: PBLinExactly -> PBLinExactly
364
normalizePBLinExactly a =
1✔
365
 case step1 $ a of
1✔
366
    (xs,n)
367
      | n >= 0    -> step3 $ step2 (xs, n)
1✔
368
      | otherwise -> ([], 1) -- false
1✔
369
  where
370
    step1 :: PBLinExactly -> PBLinExactly
371
    step1 (xs,n) =
1✔
372
      case normalizePBLinSum (xs,-n) of
1✔
373
        (ys,m) -> (ys, -m)
1✔
374

375
    -- omega test と同様の係数の gcd による単純化
376
    step2 :: PBLinExactly -> PBLinExactly
377
    step2 ([],n) = ([],n)
1✔
378
    step2 (xs,n)
379
      | n `mod` d == 0 = ([(c `div` d, l) | (c,l) <- xs], n `div` d)
1✔
380
      | otherwise      = ([], 1) -- false
1✔
381
      where
382
        d = foldl1' gcd [c | (c,_) <- xs]
1✔
383

384
    -- subset sum
385
    step3 :: PBLinExactly -> PBLinExactly
386
    step3 constr@(xs,n) =
1✔
387
      case SubsetSum.subsetSum (V.fromList [c | (c,_) <- xs]) n of
1✔
388
        Just _ -> constr
1✔
389
        Nothing -> ([], 1) -- false
1✔
390

391
{-# SPECIALIZE instantiatePBLinAtLeast :: (Lit -> IO LBool) -> PBLinAtLeast -> IO PBLinAtLeast #-}
392
instantiatePBLinAtLeast :: forall m. Monad m => (Lit -> m LBool) -> PBLinAtLeast -> m PBLinAtLeast
393
instantiatePBLinAtLeast evalLitM (xs,n) = loop ([],n) xs
1✔
394
  where
395
    loop :: PBLinAtLeast -> PBLinSum -> m PBLinAtLeast
396
    loop ret [] = return ret
1✔
397
    loop (ys,m) ((c,l):ts) = do
1✔
398
      val <- evalLitM l
1✔
399
      if val == lTrue then
1✔
400
        loop (ys, m-c) ts
1✔
401
      else if val == lFalse then
1✔
402
        loop (ys, m) ts
1✔
403
      else
404
        loop ((c,l):ys, m) ts
1✔
405

406
{-# SPECIALIZE instantiatePBLinExactly :: (Lit -> IO LBool) -> PBLinExactly -> IO PBLinExactly #-}
407
instantiatePBLinExactly :: Monad m => (Lit -> m LBool) -> PBLinExactly -> m PBLinExactly
408
instantiatePBLinExactly = instantiatePBLinAtLeast
1✔
409

410
cutResolve :: PBLinAtLeast -> PBLinAtLeast -> Var -> PBLinAtLeast
411
cutResolve (lhs1,rhs1) (lhs2,rhs2) v = assert (l1 == litNot l2) $ normalizePBLinAtLeast pb
×
412
  where
413
    (c1,l1) = head [(c,l) | (c,l) <- lhs1, litVar l == v]
×
414
    (c2,l2) = head [(c,l) | (c,l) <- lhs2, litVar l == v]
×
415
    g = gcd c1 c2
1✔
416
    s1 = c2 `div` g
1✔
417
    s2 = c1 `div` g
1✔
418
    pb = ([(s1*c,l) | (c,l) <- lhs1] ++ [(s2*c,l) | (c,l) <- lhs2], s1*rhs1 + s2 * rhs2)
1✔
419

420
cardinalityReduction :: PBLinAtLeast -> AtLeast
421
cardinalityReduction (lhs,rhs) = (ls, rhs')
1✔
422
  where
423
    rhs' = go1 0 0 (sortBy (flip (comparing fst)) lhs)
1✔
424

425
    go1 !s !k ((a,_):ts)
1✔
426
      | s < rhs   = go1 (s+a) (k+1) ts
1✔
427
      | otherwise = k
1✔
428
    go1 _ _ [] = error "ToySolver.SAT.Types.cardinalityReduction: should not happen"
×
429

430
    ls = go2 (minimum (rhs : map (subtract 1 . fst) lhs)) (sortBy (comparing fst) lhs)
1✔
431

432
    go2 !guard' ((a,_) : ts)
1✔
433
      | a - 1 < guard' = go2 (guard' - a) ts
×
434
      | otherwise      = map snd ts
1✔
435
    go2 _ [] = error "ToySolver.SAT.Types.cardinalityReduction: should not happen"
×
436

437
negatePBLinAtLeast :: PBLinAtLeast -> PBLinAtLeast
438
negatePBLinAtLeast (xs, rhs) = ([(-c,lit) | (c,lit)<-xs] , -rhs + 1)
×
439

440
evalPBLinSum :: IModel m => m -> PBLinSum -> Integer
441
evalPBLinSum m xs = sum [c | (c,lit) <- xs, evalLit m lit]
1✔
442

443
evalPBLinAtLeast :: IModel m => m -> PBLinAtLeast -> Bool
444
evalPBLinAtLeast m (lhs,rhs) = evalPBLinSum m lhs >= rhs
1✔
445

446
evalPBLinExactly :: IModel m => m -> PBLinAtLeast -> Bool
447
evalPBLinExactly m (lhs,rhs) = evalPBLinSum m lhs == rhs
1✔
448

449
pbLinLowerBound :: PBLinSum -> Integer
450
pbLinLowerBound xs = sum [if c < 0 then c else 0 | (c,_) <- xs]
1✔
451

452
pbLinUpperBound :: PBLinSum -> Integer
453
pbLinUpperBound xs = sum [if c > 0 then c else 0 | (c,_) <- xs]
1✔
454

455
-- (Σi ci li ≥ rhs1) subsumes (Σi di li ≥ rhs2) iff rhs1≥rhs2 and di≥ci for all i.
456
pbLinSubsume :: PBLinAtLeast -> PBLinAtLeast -> Bool
457
pbLinSubsume (lhs1,rhs1) (lhs2,rhs2) =
1✔
458
  rhs1 >= rhs2 && and [di >= ci | (ci,li) <- lhs1, let di = IntMap.findWithDefault 0 li lhs2']
×
459
  where
460
    lhs2' = IntMap.fromList [(l,c) | (c,l) <- lhs2]
1✔
461

462
type PBTerm = (Integer, [Lit])
463
type PBSum = [PBTerm]
464

465
evalPBSum :: IModel m => m -> PBSum -> Integer
466
evalPBSum m xs = sum [c | (c,lits) <- xs, all (evalLit m) lits]
1✔
467

468
evalPBConstraint :: IModel m => m -> PBFile.Constraint -> Bool
469
evalPBConstraint m (lhs,op,rhs) = op' (evalPBSum m lhs) rhs
1✔
470
  where
471
    op' = case op of
1✔
472
            PBFile.Ge -> (>=)
1✔
473
            PBFile.Eq -> (==)
1✔
474

475
evalPBFormula :: IModel m => m -> PBFile.Formula -> Maybe Integer
476
evalPBFormula m formula = do
1✔
477
  guard $ all (evalPBConstraint m) $ PBFile.pbConstraints formula
1✔
478
  return $ evalPBSum m $ fromMaybe [] $ PBFile.pbObjectiveFunction formula
1✔
479

480
pbLowerBound :: PBSum -> Integer
481
pbLowerBound xs = sum [c | (c,ls) <- xs, c < 0 || null ls]
1✔
482

483
pbUpperBound :: PBSum -> Integer
484
pbUpperBound xs = sum [c | (c,ls) <- xs, c > 0 || null ls]
1✔
485

486
removeNegationFromPBSum :: PBSum -> PBSum
487
removeNegationFromPBSum ts =
1✔
488
  [(c, IntSet.toList m) | (m, c) <- Map.toList $ Map.unionsWith (+) $ map f ts, c /= 0]
1✔
489
  where
490
    f :: PBTerm -> Map VarSet Integer
491
    f (c, ls) = IntSet.foldl' g (Map.singleton IntSet.empty c) (IntSet.fromList ls)
1✔
492

493
    g :: Map VarSet Integer -> Lit -> Map VarSet Integer
494
    g m l
1✔
495
      | l > 0     = Map.mapKeysWith (+) (IntSet.insert v) m
1✔
496
      | otherwise = Map.unionWith (+) m $ Map.fromListWith (+) [(IntSet.insert v xs, negate c) | (xs,c) <- Map.toList m]
×
497
      where
498
        v = litVar l
1✔
499

500
-- | XOR clause
501
--
502
-- '([l1,l2..ln], b)' means l1 ⊕ l2 ⊕ ⋯ ⊕ ln = b.
503
--
504
-- Note that:
505
--
506
-- * True can be represented as ([], False)
507
--
508
-- * False can be represented as ([], True)
509
--
510
type XORClause = ([Lit], Bool)
511

512
-- | Normalize XOR clause
513
normalizeXORClause :: XORClause -> XORClause
514
normalizeXORClause (lits, b) =
1✔
515
  case IntMap.keys m of
1✔
516
    0:xs -> (xs, not b)
1✔
517
    xs -> (xs, b)
1✔
518
  where
519
    m = IntMap.filter id $ IntMap.unionsWith xor [f lit | lit <- lits]
1✔
520
    xor = (/=)
1✔
521

522
    f 0 = IntMap.singleton 0 True
×
523
    f lit =
524
      if litPolarity lit
1✔
525
      then IntMap.singleton lit True
1✔
526
      else IntMap.fromList [(litVar lit, True), (0, True)]  -- ¬x = x ⊕ 1
1✔
527

528
{-# SPECIALIZE instantiateXORClause :: (Lit -> IO LBool) -> XORClause -> IO XORClause #-}
529
instantiateXORClause :: forall m. Monad m => (Lit -> m LBool) -> XORClause -> m XORClause
530
instantiateXORClause evalLitM (ls,b) = loop [] b ls
1✔
531
  where
532
    loop :: [Lit] -> Bool -> [Lit] -> m XORClause
533
    loop lhs !rhs [] = return (lhs, rhs)
1✔
534
    loop lhs !rhs (l:ls) = do
1✔
535
      val <- evalLitM l
1✔
536
      if val==lTrue then
1✔
537
        loop lhs (not rhs) ls
1✔
538
      else if val==lFalse then
1✔
539
        loop lhs rhs ls
1✔
540
      else
541
        loop (l : lhs) rhs ls
1✔
542

543
evalXORClause :: IModel m => m -> XORClause -> Bool
544
evalXORClause m (lits, rhs) = foldl' xor False (map f lits) == rhs
1✔
545
  where
546
    xor = (/=)
1✔
547
    f 0 = True
×
548
    f lit = evalLit m lit
1✔
549

550

551
class Monad m => NewVar m a | a -> m where
552
  {-# MINIMAL newVar #-}
553

554
  -- | Add a new variable
555
  newVar :: a -> m Var
556

557
  -- | Add variables. @newVars a n = replicateM n (newVar a)@, but maybe faster.
558
  newVars :: a -> Int -> m [Var]
559
  newVars a n = replicateM n (newVar a)
1✔
560

561
  -- | Add variables. @newVars_ a n = newVars a n >> return ()@, but maybe faster.
562
  newVars_ :: a -> Int -> m ()
563
  newVars_ a n = replicateM_ n (newVar a)
1✔
564

565
class NewVar m a => AddClause m a | a -> m where
566
  addClause :: a -> Clause -> m ()
567

568
class AddClause m a => AddCardinality m a | a -> m where
569
  {-# MINIMAL addAtLeast #-}
570

571
  -- | Add a cardinality constraints /atleast({l1,l2,..},n)/.
572
  addAtLeast
573
    :: a
574
    -> [Lit] -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
575
    -> Int   -- ^ /n/
576
    -> m ()
577

578
  -- | Add a cardinality constraints /atmost({l1,l2,..},n)/.
579
  addAtMost
580
    :: a
581
    -> [Lit] -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
582
    -> Int   -- ^ /n/
583
    -> m ()
584
  addAtMost a lits n = do
1✔
585
    addAtLeast a (map litNot lits) (length lits - n)
1✔
586

587
  -- | Add a cardinality constraints /exactly({l1,l2,..},n)/.
588
  addExactly
589
    :: a
590
    -> [Lit] -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
591
    -> Int   -- ^ /n/
592
    -> m ()
593
  addExactly a lits n = do
×
594
    addAtLeast a lits n
×
595
    addAtMost a lits n
×
596

597
class AddCardinality m a => AddPBLin m a | a -> m where
598
  {-# MINIMAL addPBAtLeast #-}
599

600
  -- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … ≥ n/.
601
  addPBAtLeast
602
    :: a
603
    -> PBLinSum -- ^ list of terms @[(c1,l1),(c2,l2),…]@
604
    -> Integer  -- ^ /n/
605
    -> m ()
606

607
  -- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … ≤ n/.
608
  addPBAtMost
609
    :: a
610
    -> PBLinSum -- ^ list of @[(c1,l1),(c2,l2),…]@
611
    -> Integer  -- ^ /n/
612
    -> m ()
613
  addPBAtMost a ts n = addPBAtLeast a [(-c,l) | (c,l) <- ts] (negate n)
1✔
614

615
  -- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … = n/.
616
  addPBExactly
617
    :: a
618
    -> PBLinSum -- ^ list of terms @[(c1,l1),(c2,l2),…]@
619
    -> Integer  -- ^ /n/
620
    -> m ()
621
  addPBExactly a ts n = do
1✔
622
    addPBAtLeast a ts n
1✔
623
    addPBAtMost a ts n
1✔
624

625
  -- | Add a soft pseudo boolean constraints /sel ⇒ c1*l1 + c2*l2 + … ≥ n/.
626
  addPBAtLeastSoft
627
    :: a
628
    -> Lit             -- ^ Selector literal @sel@
629
    -> PBLinSum        -- ^ list of terms @[(c1,l1),(c2,l2),…]@
630
    -> Integer         -- ^ /n/
631
    -> m ()
632
  addPBAtLeastSoft a sel lhs rhs = do
×
633
    let (lhs2,rhs2) = normalizePBLinAtLeast (lhs,rhs)
×
634
    addPBAtLeast a ((rhs2, litNot sel) : lhs2) rhs2
×
635

636
  -- | Add a soft pseudo boolean constraints /sel ⇒ c1*l1 + c2*l2 + … ≤ n/.
637
  addPBAtMostSoft
638
    :: a
639
    -> Lit             -- ^ Selector literal @sel@
640
    -> PBLinSum        -- ^ list of terms @[(c1,l1),(c2,l2),…]@
641
    -> Integer         -- ^ /n/
642
    -> m ()
643
  addPBAtMostSoft a sel lhs rhs =
1✔
644
    addPBAtLeastSoft a sel [(negate c, lit) | (c,lit) <- lhs] (negate rhs)
1✔
645

646
  -- | Add a soft pseudo boolean constraints /sel ⇒ c1*l1 + c2*l2 + … = n/.
647
  addPBExactlySoft
648
    :: a
649
    -> Lit             -- ^ Selector literal @sel@
650
    -> PBLinSum        -- ^ list of terms @[(c1,l1),(c2,l2),…]@
651
    -> Integer         -- ^ /n/
652
    -> m ()
653
  addPBExactlySoft a sel lhs rhs = do
×
654
    addPBAtLeastSoft a sel lhs rhs
×
655
    addPBAtMostSoft a sel lhs rhs
×
656

657
class AddPBLin m a => AddPBNL m a | a -> m where
658
  {-# MINIMAL addPBNLAtLeast #-}
659

660
  -- | Add a non-linear pseudo boolean constraints /c1*ls1 + c2*ls2 + … ≥ n/.
661
  addPBNLAtLeast
662
    :: a
663
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
664
    -> Integer -- ^ /n/
665
    -> m ()
666

667
  -- | Add a non-linear pseudo boolean constraints /c1*ls1 + c2*ls2 + … ≥ n/.
668
  addPBNLAtMost
669
    :: a
670
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
671
    -> Integer -- ^ /n/
672
    -> m ()
673
  addPBNLAtMost a ts n = addPBNLAtLeast a [(-c,ls) | (c,ls) <- ts] (negate n)
1✔
674

675
  -- | Add a non-linear pseudo boolean constraints /c1*ls1 + c2*ls2 + … = n/.
676
  addPBNLExactly
677
    :: a
678
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
679
    -> Integer -- ^ /n/
680
    -> m ()
681
  addPBNLExactly a ts n = do
×
682
    addPBNLAtLeast a ts n
×
683
    addPBNLAtMost a ts n
×
684

685
  -- | Add a soft non-linear pseudo boolean constraints /sel ⇒ c1*ls1 + c2*ls2 + … ≥ n/.
686
  addPBNLAtLeastSoft
687
    :: a
688
    -> Lit     -- ^ Selector literal @sel@
689
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
690
    -> Integer -- ^ /n/
691
    -> m ()
692
  addPBNLAtLeastSoft a sel lhs rhs = do
1✔
693
    let n = rhs - sum [min c 0 | (c,_) <- lhs]
1✔
694
    addPBNLAtLeast a ((n, [litNot sel]) : lhs) rhs
1✔
695

696
  -- | Add a soft non-linear pseudo boolean constraints /sel ⇒ c1*ls1 + c2*ls2 + … ≤ n/.
697
  addPBNLAtMostSoft
698
    :: a
699
    -> Lit     -- ^ Selector literal @sel@
700
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
701
    -> Integer -- ^ /n/
702
    -> m ()
703
  addPBNLAtMostSoft a sel lhs rhs =
1✔
704
    addPBNLAtLeastSoft a sel [(negate c, ls) | (c,ls) <- lhs] (negate rhs)
1✔
705

706
  -- | Add a soft non-linear pseudo boolean constraints /lit ⇒ c1*ls1 + c2*ls2 + … = n/.
707
  addPBNLExactlySoft
708
    :: a
709
    -> Lit     -- ^ Selector literal @sel@
710
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
711
    -> Integer -- ^ /n/
712
    -> m ()
713
  addPBNLExactlySoft a sel lhs rhs = do
1✔
714
    addPBNLAtLeastSoft a sel lhs rhs
1✔
715
    addPBNLAtMostSoft a sel lhs rhs
1✔
716

717
class AddClause m a => AddXORClause m a | a -> m where
718
  {-# MINIMAL addXORClause #-}
719

720
  -- | Add a parity constraint /l1 ⊕ l2 ⊕ … ⊕ ln = rhs/
721
  addXORClause
722
    :: a
723
    -> [Lit]  -- ^ literals @[l1, l2, …, ln]@
724
    -> Bool   -- ^ /rhs/
725
    -> m ()
726

727
  -- | Add a soft parity constraint /sel ⇒ l1 ⊕ l2 ⊕ … ⊕ ln = rhs/
728
  addXORClauseSoft
729
    :: a
730
    -> Lit    -- ^ Selector literal @sel@
731
    -> [Lit]  -- ^ literals @[l1, l2, …, ln]@
732
    -> Bool   -- ^ /rhs/
733
    -> m ()
734
  addXORClauseSoft a sel lits rhs = do
×
735
    reified <- newVar a
×
736
    addXORClause a (litNot reified : lits) rhs
×
737
    addClause a [litNot sel, reified] -- sel ⇒ reified
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc