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

msakai / toysolver / 551

10 Dec 2024 12:19AM UTC coverage: 70.818% (+0.6%) from 70.181%
551

push

github

web-flow
Merge a6fb30ddb into d7e84538d

10 of 12 new or added lines in 1 file covered. (83.33%)

44 existing lines in 8 files now uncovered.

10287 of 14526 relevant lines covered (70.82%)

0.71 hits per line

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

79.37
/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
  , evalPBSoftFormula
95
  , pbLowerBound
96
  , pbUpperBound
97
  , removeNegationFromPBSum
98

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

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

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

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

136
type VarSet = IntSet
137
type VarMap = IntMap
138

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

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

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

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

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

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

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

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

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

170
type LitSet = IntSet
171
type LitMap = IntMap
172

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

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

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

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

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

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

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

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

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

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

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

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

249
type PackedVar = PackedLit
250
type PackedLit = Int32
251

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

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

258
type PackedClause = VU.Vector PackedLit
259

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

481
evalPBSoftFormula :: IModel m => m -> PBFile.SoftFormula -> Maybe Integer
482
evalPBSoftFormula m formula = do
1✔
483
  obj <- liftM sum $ forM (PBFile.wboConstraints formula) $ \(cost, constr) -> do
1✔
484
    case cost of 
1✔
485
      Nothing -> do
1✔
486
        guard $ evalPBConstraint m constr
1✔
487
        return 0
1✔
488
      Just w
489
        | evalPBConstraint m constr -> return 0
1✔
NEW
490
        | otherwise -> return w
×
491
  case PBFile.wboTopCost formula of
1✔
NEW
492
    Nothing -> return ()
×
493
    Just c -> guard (obj < c)
1✔
494
  return obj
1✔
495

496
pbLowerBound :: PBSum -> Integer
497
pbLowerBound xs = sum [c | (c,ls) <- xs, c < 0 || null ls]
1✔
498

499
pbUpperBound :: PBSum -> Integer
500
pbUpperBound xs = sum [c | (c,ls) <- xs, c > 0 || null ls]
1✔
501

502
removeNegationFromPBSum :: PBSum -> PBSum
503
removeNegationFromPBSum ts =
1✔
504
  [(c, IntSet.toList m) | (m, c) <- Map.toList $ Map.unionsWith (+) $ map f ts, c /= 0]
1✔
505
  where
506
    f :: PBTerm -> Map VarSet Integer
507
    f (c, ls) = IntSet.foldl' g (Map.singleton IntSet.empty c) (IntSet.fromList ls)
1✔
508

509
    g :: Map VarSet Integer -> Lit -> Map VarSet Integer
510
    g m l
1✔
511
      | l > 0     = Map.mapKeysWith (+) (IntSet.insert v) m
1✔
512
      | otherwise = Map.unionWith (+) m $ Map.fromListWith (+) [(IntSet.insert v xs, negate c) | (xs,c) <- Map.toList m]
×
513
      where
514
        v = litVar l
1✔
515

516
-- | XOR clause
517
--
518
-- '([l1,l2..ln], b)' means l1 ⊕ l2 ⊕ ⋯ ⊕ ln = b.
519
--
520
-- Note that:
521
--
522
-- * True can be represented as ([], False)
523
--
524
-- * False can be represented as ([], True)
525
--
526
type XORClause = ([Lit], Bool)
527

528
-- | Normalize XOR clause
529
normalizeXORClause :: XORClause -> XORClause
530
normalizeXORClause (lits, b) =
1✔
531
  case IntMap.keys m of
1✔
532
    0:xs -> (xs, not b)
1✔
533
    xs -> (xs, b)
1✔
534
  where
535
    m = IntMap.filter id $ IntMap.unionsWith xor [f lit | lit <- lits]
1✔
536
    xor = (/=)
1✔
537

538
    f 0 = IntMap.singleton 0 True
×
539
    f lit =
540
      if litPolarity lit
1✔
541
      then IntMap.singleton lit True
1✔
542
      else IntMap.fromList [(litVar lit, True), (0, True)]  -- ¬x = x ⊕ 1
1✔
543

544
{-# SPECIALIZE instantiateXORClause :: (Lit -> IO LBool) -> XORClause -> IO XORClause #-}
545
instantiateXORClause :: forall m. Monad m => (Lit -> m LBool) -> XORClause -> m XORClause
546
instantiateXORClause evalLitM (ls,b) = loop [] b ls
1✔
547
  where
548
    loop :: [Lit] -> Bool -> [Lit] -> m XORClause
549
    loop lhs !rhs [] = return (lhs, rhs)
1✔
550
    loop lhs !rhs (l:ls) = do
1✔
551
      val <- evalLitM l
1✔
552
      if val==lTrue then
1✔
553
        loop lhs (not rhs) ls
1✔
554
      else if val==lFalse then
1✔
555
        loop lhs rhs ls
1✔
556
      else
557
        loop (l : lhs) rhs ls
1✔
558

559
evalXORClause :: IModel m => m -> XORClause -> Bool
560
evalXORClause m (lits, rhs) = foldl' xor False (map f lits) == rhs
1✔
561
  where
562
    xor = (/=)
1✔
563
    f 0 = True
×
564
    f lit = evalLit m lit
1✔
565

566

567
class Monad m => NewVar m a | a -> m where
568
  {-# MINIMAL newVar #-}
569

570
  -- | Add a new variable
571
  newVar :: a -> m Var
572

573
  -- | Add variables. @newVars a n = replicateM n (newVar a)@, but maybe faster.
574
  newVars :: a -> Int -> m [Var]
575
  newVars a n = replicateM n (newVar a)
1✔
576

577
  -- | Add variables. @newVars_ a n = newVars a n >> return ()@, but maybe faster.
578
  newVars_ :: a -> Int -> m ()
579
  newVars_ a n = replicateM_ n (newVar a)
1✔
580

581
class NewVar m a => AddClause m a | a -> m where
582
  addClause :: a -> Clause -> m ()
583

584
class AddClause m a => AddCardinality m a | a -> m where
585
  {-# MINIMAL addAtLeast #-}
586

587
  -- | Add a cardinality constraints /atleast({l1,l2,..},n)/.
588
  addAtLeast
589
    :: a
590
    -> [Lit] -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
591
    -> Int   -- ^ /n/
592
    -> m ()
593

594
  -- | Add a cardinality constraints /atmost({l1,l2,..},n)/.
595
  addAtMost
596
    :: a
597
    -> [Lit] -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
598
    -> Int   -- ^ /n/
599
    -> m ()
600
  addAtMost a lits n = do
1✔
601
    addAtLeast a (map litNot lits) (length lits - n)
1✔
602

603
  -- | Add a cardinality constraints /exactly({l1,l2,..},n)/.
604
  addExactly
605
    :: a
606
    -> [Lit] -- ^ set of literals /{l1,l2,..}/ (duplicated elements are ignored)
607
    -> Int   -- ^ /n/
608
    -> m ()
609
  addExactly a lits n = do
1✔
610
    addAtLeast a lits n
1✔
611
    addAtMost a lits n
1✔
612

613
class AddCardinality m a => AddPBLin m a | a -> m where
614
  {-# MINIMAL addPBAtLeast #-}
615

616
  -- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … ≥ n/.
617
  addPBAtLeast
618
    :: a
619
    -> PBLinSum -- ^ list of terms @[(c1,l1),(c2,l2),…]@
620
    -> Integer  -- ^ /n/
621
    -> m ()
622

623
  -- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … ≤ n/.
624
  addPBAtMost
625
    :: a
626
    -> PBLinSum -- ^ list of @[(c1,l1),(c2,l2),…]@
627
    -> Integer  -- ^ /n/
628
    -> m ()
629
  addPBAtMost a ts n = addPBAtLeast a [(-c,l) | (c,l) <- ts] (negate n)
1✔
630

631
  -- | Add a pseudo boolean constraints /c1*l1 + c2*l2 + … = n/.
632
  addPBExactly
633
    :: a
634
    -> PBLinSum -- ^ list of terms @[(c1,l1),(c2,l2),…]@
635
    -> Integer  -- ^ /n/
636
    -> m ()
637
  addPBExactly a ts n = do
1✔
638
    addPBAtLeast a ts n
1✔
639
    addPBAtMost a ts n
1✔
640

641
  -- | Add a soft pseudo boolean constraints /sel ⇒ c1*l1 + c2*l2 + … ≥ n/.
642
  addPBAtLeastSoft
643
    :: a
644
    -> Lit             -- ^ Selector literal @sel@
645
    -> PBLinSum        -- ^ list of terms @[(c1,l1),(c2,l2),…]@
646
    -> Integer         -- ^ /n/
647
    -> m ()
648
  addPBAtLeastSoft a sel lhs rhs = do
×
649
    let (lhs2,rhs2) = normalizePBLinAtLeast (lhs,rhs)
×
650
    addPBAtLeast a ((rhs2, litNot sel) : lhs2) rhs2
×
651

652
  -- | Add a soft pseudo boolean constraints /sel ⇒ c1*l1 + c2*l2 + … ≤ n/.
653
  addPBAtMostSoft
654
    :: a
655
    -> Lit             -- ^ Selector literal @sel@
656
    -> PBLinSum        -- ^ list of terms @[(c1,l1),(c2,l2),…]@
657
    -> Integer         -- ^ /n/
658
    -> m ()
659
  addPBAtMostSoft a sel lhs rhs =
1✔
660
    addPBAtLeastSoft a sel [(negate c, lit) | (c,lit) <- lhs] (negate rhs)
1✔
661

662
  -- | Add a soft pseudo boolean constraints /sel ⇒ c1*l1 + c2*l2 + … = n/.
663
  addPBExactlySoft
664
    :: a
665
    -> Lit             -- ^ Selector literal @sel@
666
    -> PBLinSum        -- ^ list of terms @[(c1,l1),(c2,l2),…]@
667
    -> Integer         -- ^ /n/
668
    -> m ()
669
  addPBExactlySoft a sel lhs rhs = do
×
670
    addPBAtLeastSoft a sel lhs rhs
×
671
    addPBAtMostSoft a sel lhs rhs
×
672

673
class AddPBLin m a => AddPBNL m a | a -> m where
674
  {-# MINIMAL addPBNLAtLeast #-}
675

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

683
  -- | Add a non-linear pseudo boolean constraints /c1*ls1 + c2*ls2 + … ≥ n/.
684
  addPBNLAtMost
685
    :: a
686
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
687
    -> Integer -- ^ /n/
688
    -> m ()
689
  addPBNLAtMost a ts n = addPBNLAtLeast a [(-c,ls) | (c,ls) <- ts] (negate n)
1✔
690

691
  -- | Add a non-linear pseudo boolean constraints /c1*ls1 + c2*ls2 + … = n/.
692
  addPBNLExactly
693
    :: a
694
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
695
    -> Integer -- ^ /n/
696
    -> m ()
697
  addPBNLExactly a ts n = do
×
698
    addPBNLAtLeast a ts n
×
699
    addPBNLAtMost a ts n
×
700

701
  -- | Add a soft non-linear pseudo boolean constraints /sel ⇒ c1*ls1 + c2*ls2 + … ≥ n/.
702
  addPBNLAtLeastSoft
703
    :: a
704
    -> Lit     -- ^ Selector literal @sel@
705
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
706
    -> Integer -- ^ /n/
707
    -> m ()
708
  addPBNLAtLeastSoft a sel lhs rhs = do
1✔
709
    let n = rhs - sum [min c 0 | (c,_) <- lhs]
1✔
710
    addPBNLAtLeast a ((n, [litNot sel]) : lhs) rhs
1✔
711

712
  -- | Add a soft non-linear pseudo boolean constraints /sel ⇒ c1*ls1 + c2*ls2 + … ≤ n/.
713
  addPBNLAtMostSoft
714
    :: a
715
    -> Lit     -- ^ Selector literal @sel@
716
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
717
    -> Integer -- ^ /n/
718
    -> m ()
719
  addPBNLAtMostSoft a sel lhs rhs =
1✔
720
    addPBNLAtLeastSoft a sel [(negate c, ls) | (c,ls) <- lhs] (negate rhs)
1✔
721

722
  -- | Add a soft non-linear pseudo boolean constraints /lit ⇒ c1*ls1 + c2*ls2 + … = n/.
723
  addPBNLExactlySoft
724
    :: a
725
    -> Lit     -- ^ Selector literal @sel@
726
    -> PBSum   -- ^ List of terms @[(c1,ls1),(c2,ls2),…]@
727
    -> Integer -- ^ /n/
728
    -> m ()
729
  addPBNLExactlySoft a sel lhs rhs = do
1✔
730
    addPBNLAtLeastSoft a sel lhs rhs
1✔
731
    addPBNLAtMostSoft a sel lhs rhs
1✔
732

733
class AddClause m a => AddXORClause m a | a -> m where
734
  {-# MINIMAL addXORClause #-}
735

736
  -- | Add a parity constraint /l1 ⊕ l2 ⊕ … ⊕ ln = rhs/
737
  addXORClause
738
    :: a
739
    -> [Lit]  -- ^ literals @[l1, l2, …, ln]@
740
    -> Bool   -- ^ /rhs/
741
    -> m ()
742

743
  -- | Add a soft parity constraint /sel ⇒ l1 ⊕ l2 ⊕ … ⊕ ln = rhs/
744
  addXORClauseSoft
745
    :: a
746
    -> Lit    -- ^ Selector literal @sel@
747
    -> [Lit]  -- ^ literals @[l1, l2, …, ln]@
748
    -> Bool   -- ^ /rhs/
749
    -> m ()
750
  addXORClauseSoft a sel lits rhs = do
×
751
    reified <- newVar a
×
752
    addXORClause a (litNot reified : lits) rhs
×
753
    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