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

msakai / toysolver / 513

24 Nov 2024 08:49AM UTC coverage: 68.675% (-1.0%) from 69.681%
513

push

github

web-flow
Merge 496ed4263 into 46e1509c4

5 of 121 new or added lines in 7 files covered. (4.13%)

105 existing lines in 14 files now uncovered.

9745 of 14190 relevant lines covered (68.68%)

0.69 hits per line

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

63.17
/src/ToySolver/Converter/PB.hs
1
{-# OPTIONS_GHC -Wall #-}
2
{-# OPTIONS_HADDOCK show-extensions #-}
3
{-# LANGUAGE BangPatterns #-}
4
{-# LANGUAGE OverloadedStrings #-}
5
{-# LANGUAGE TypeFamilies #-}
6
-----------------------------------------------------------------------------
7
-- |
8
-- Module      :  ToySolver.Converter.PB
9
-- Copyright   :  (c) Masahiro Sakai 2013,2016-2018
10
-- License     :  BSD-style
11
--
12
-- Maintainer  :  masahiro.sakai@gmail.com
13
-- Stability   :  experimental
14
-- Portability :  non-portable
15
--
16
-----------------------------------------------------------------------------
17
module ToySolver.Converter.PB
18
  ( module ToySolver.Converter.Base
19
  , module ToySolver.Converter.Tseitin
20

21
  -- * Normalization of PB/WBO problems
22
  , normalizePB
23
  , normalizeWBO
24

25
  -- * Linealization of PB/WBO problems
26
  , linearizePB
27
  , linearizeWBO
28
  , PBLinearizeInfo
29

30
  -- * Quadratization of PB problems
31
  , quadratizePB
32
  , quadratizePB'
33
  , PBQuadratizeInfo
34

35
  -- * Converting inequality constraints into equality constraints
36
  , inequalitiesToEqualitiesPB
37
  , PBInequalitiesToEqualitiesInfo
38

39
  -- * Converting constraints into penalty terms in objective function
40
  , unconstrainPB
41
  , PBUnconstrainInfo
42

43
  -- * PB↔WBO conversion
44
  , pb2wbo
45
  , PB2WBOInfo
46
  , wbo2pb
47
  , WBO2PBInfo (..)
48
  , addWBO
49

50
  -- * SAT↔PB conversion
51
  , sat2pb
52
  , SAT2PBInfo
53
  , pb2sat
54
  , pb2satWith
55
  , PB2SATInfo
56

57
  -- * MaxSAT↔WBO conversion
58
  , maxsat2wbo
59
  , MaxSAT2WBOInfo
60
  , wbo2maxsat
61
  , wbo2maxsatWith
62
  , WBO2MaxSATInfo
63

64
  -- * PB→QUBO conversion
65
  , pb2qubo'
66
  , PB2QUBOInfo'
67
  ) where
68

69
import Control.Monad
70
import Control.Monad.Primitive
71
import Control.Monad.ST
72
import qualified Data.Aeson as J
73
import qualified Data.Aeson.KeyMap as KeyMap
74
import Data.Aeson ((.=))
75
import Data.Array.IArray
76
import Data.Bits hiding (And (..))
77
import Data.Default.Class
78
import qualified Data.Foldable as F
79
import Data.IntMap.Strict (IntMap)
80
import qualified Data.IntMap.Strict as IntMap
81
import Data.IntSet (IntSet)
82
import qualified Data.IntSet as IntSet
83
import Data.List
84
import Data.Map.Strict (Map)
85
import qualified Data.Map.Strict as Map
86
import Data.Maybe
87
import Data.Primitive.MutVar
88
import qualified Data.Sequence as Seq
89
import Data.Set (Set)
90
import qualified Data.Set as Set
91
import Data.String
92
import qualified Data.PseudoBoolean as PBFile
93

94
import ToySolver.Converter.Base
95
import qualified ToySolver.Converter.PB.Internal.Product as Product
96
import ToySolver.Converter.Tseitin
97
import qualified ToySolver.FileFormat.CNF as CNF
98
import qualified ToySolver.SAT.Types as SAT
99
import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin
100
import ToySolver.SAT.Encoder.Tseitin (Formula (..))
101
import qualified ToySolver.SAT.Encoder.PB as PB
102
import qualified ToySolver.SAT.Encoder.PBNLC as PBNLC
103
import ToySolver.SAT.Internal.JSON
104
import ToySolver.SAT.Store.CNF
105
import ToySolver.SAT.Store.PB
106

107
-- -----------------------------------------------------------------------------
108

109
-- XXX: we do not normalize objective function, because normalization might
110
-- introduce constant terms, but OPB file format does not allow constant terms.
111
--
112
-- Options:
113
-- (1) not normalize objective function (current implementation),
114
-- (2) normalize and simply delete constant terms (in pseudo-boolean package?),
115
-- (3) normalize and introduce dummy variable to make constant terms
116
--     into non-constant terms (in pseudo-boolean package?).
117
normalizePB :: PBFile.Formula -> PBFile.Formula
118
normalizePB formula =
×
119
  formula
×
120
  { PBFile.pbConstraints =
121
      map normalizePBConstraint (PBFile.pbConstraints formula)
×
122
  }
123

124
normalizeWBO :: PBFile.SoftFormula -> PBFile.SoftFormula
125
normalizeWBO formula =
×
126
  formula
×
127
  { PBFile.wboConstraints =
128
      map (\(w,constr) -> (w, normalizePBConstraint constr)) (PBFile.wboConstraints formula)
×
129
  }
130

131
normalizePBConstraint :: PBFile.Constraint -> PBFile.Constraint
132
normalizePBConstraint (lhs,op,rhs) =
×
133
  case mapAccumL h 0 lhs of
×
134
    (offset, lhs') -> (lhs', op, rhs - offset)
×
135
  where
136
    h s (w,[x]) | x < 0 = (s+w, (-w,[-x]))
×
137
    h s t = (s,t)
×
138

139
-- -----------------------------------------------------------------------------
140

141
type PBLinearizeInfo = TseitinInfo
142

143
linearizePB :: PBFile.Formula -> Bool -> (PBFile.Formula, PBLinearizeInfo)
144
linearizePB formula usePB = runST $ do
×
145
  db <- newPBStore
×
146
  SAT.newVars_ db (PBFile.pbNumVars formula)
×
147
  tseitin <-  Tseitin.newEncoderWithPBLin db
×
148
  Tseitin.setUsePB tseitin usePB
×
149
  pbnlc <- PBNLC.newEncoder db tseitin
×
150
  cs' <- forM (PBFile.pbConstraints formula) $ \(lhs,op,rhs) -> do
×
151
    let p = case op of
×
152
              PBFile.Ge -> Tseitin.polarityPos
×
153
              PBFile.Eq -> Tseitin.polarityBoth
×
154
    lhs' <- PBNLC.linearizePBSumWithPolarity pbnlc p lhs
×
155
    return ([(c,[l]) | (c,l) <- lhs'],op,rhs)
×
156
  obj' <-
157
    case PBFile.pbObjectiveFunction formula of
×
158
      Nothing -> return Nothing
×
159
      Just obj -> do
×
160
        obj' <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityNeg obj
×
161
        return $ Just [(c, [l]) | (c,l) <- obj']
×
162
  formula' <- getPBFormula db
×
163
  defs <- Tseitin.getDefinitions tseitin
×
164
  return $
×
165
    ( formula'
×
166
      { PBFile.pbObjectiveFunction = obj'
×
167
      , PBFile.pbConstraints = cs' ++ PBFile.pbConstraints formula'
×
168
      , PBFile.pbNumConstraints = PBFile.pbNumConstraints formula + PBFile.pbNumConstraints formula'
×
169
      }
170
    , TseitinInfo (PBFile.pbNumVars formula) (PBFile.pbNumVars formula') defs
×
171
    )
172

173
-- -----------------------------------------------------------------------------
174

175
linearizeWBO :: PBFile.SoftFormula -> Bool -> (PBFile.SoftFormula, PBLinearizeInfo)
176
linearizeWBO formula usePB = runST $ do
×
177
  db <- newPBStore
×
178
  SAT.newVars_ db (PBFile.wboNumVars formula)
×
179
  tseitin <-  Tseitin.newEncoderWithPBLin db
×
180
  Tseitin.setUsePB tseitin usePB
×
181
  pbnlc <- PBNLC.newEncoder db tseitin
×
182
  cs' <- forM (PBFile.wboConstraints formula) $ \(cost,(lhs,op,rhs)) -> do
×
183
    let p = case op of
×
184
              PBFile.Ge -> Tseitin.polarityPos
×
185
              PBFile.Eq -> Tseitin.polarityBoth
×
186
    lhs' <- PBNLC.linearizePBSumWithPolarity pbnlc p lhs
×
187
    return (cost,([(c,[l]) | (c,l) <- lhs'],op,rhs))
×
188
  formula' <- getPBFormula db
×
189
  defs <- Tseitin.getDefinitions tseitin
×
190
  return $
×
191
    ( PBFile.SoftFormula
×
192
      { PBFile.wboTopCost = PBFile.wboTopCost formula
×
193
      , PBFile.wboConstraints = cs' ++ [(Nothing, constr) | constr <- PBFile.pbConstraints formula']
×
194
      , PBFile.wboNumVars = PBFile.pbNumVars formula'
×
195
      , PBFile.wboNumConstraints = PBFile.wboNumConstraints formula + PBFile.pbNumConstraints formula'
×
196
      }
197
    , TseitinInfo (PBFile.wboNumVars formula) (PBFile.pbNumVars formula') defs
×
198
    )
199

200
-- -----------------------------------------------------------------------------
201

202
-- | Quandratize PBO/PBS problem without introducing additional constraints.
203
quadratizePB :: PBFile.Formula -> ((PBFile.Formula, Integer), PBQuadratizeInfo)
204
quadratizePB formula = quadratizePB' (formula, SAT.pbUpperBound obj)
1✔
205
  where
206
    obj = fromMaybe [] $ PBFile.pbObjectiveFunction formula
1✔
207

208
-- | Quandratize PBO/PBS problem without introducing additional constraints.
209
quadratizePB' :: (PBFile.Formula, Integer) -> ((PBFile.Formula, Integer), PBQuadratizeInfo)
210
quadratizePB' (formula, maxObj) =
1✔
211
  ( ( PBFile.Formula
1✔
212
      { PBFile.pbObjectiveFunction = Just $ conv obj ++ penalty
1✔
213
      , PBFile.pbConstraints = [(conv lhs, op, rhs) | (lhs,op,rhs) <- PBFile.pbConstraints formula]
1✔
214
      , PBFile.pbNumVars = nv2
1✔
215
      , PBFile.pbNumConstraints = PBFile.pbNumConstraints formula
1✔
216
      }
217
    , maxObj
1✔
218
    )
219
  , PBQuadratizeInfo $ TseitinInfo nv1 nv2 [(v, And [Atom l1, Atom l2]) | (v, (l1,l2)) <- prodDefs]
1✔
220
  )
221
  where
222
    nv1 = PBFile.pbNumVars formula
1✔
223
    nv2 = PBFile.pbNumVars formula + Set.size termsToReplace
1✔
224

225
    degGe3Terms :: Set IntSet
226
    degGe3Terms = collectDegGe3Terms formula
1✔
227

228
    m :: Map IntSet (IntSet,IntSet)
229
    m = Product.decomposeToBinaryProducts degGe3Terms
1✔
230

231
    termsToReplace :: Set IntSet
232
    termsToReplace = go ts0 Set.empty
1✔
233
      where
234
        ts0 = concat [[t1,t2] | t <- Set.toList degGe3Terms, let (t1,t2) = m Map.! t]
1✔
235
        go [] !ret = ret
1✔
236
        go (t : ts) !ret
237
          | IntSet.size t < 2  = go ts ret
1✔
238
          | t `Set.member` ret = go ts ret
1✔
239
          | otherwise =
×
240
              case Map.lookup t m of
1✔
241
                Nothing -> error "quadratizePB.termsToReplace: should not happen"
×
242
                Just (t1,t2) -> go (t1 : t2 : ts) (Set.insert t ret)
1✔
243

244
    fromV :: IntMap IntSet
245
    toV :: Map IntSet Int
246
    (fromV, toV) = (IntMap.fromList l, Map.fromList [(s,v) | (v,s) <- l])
1✔
247
      where
248
        l = zip [PBFile.pbNumVars formula + 1 ..] (Set.toList termsToReplace)
1✔
249

250
    prodDefs :: [(SAT.Var, (SAT.Var, SAT.Var))]
251
    prodDefs = [(v, (f t1, f t2)) | (v,t) <- IntMap.toList fromV, let (t1,t2) = m Map.! t]
1✔
252
      where
253
        f t
1✔
254
          | IntSet.size t == 1 = head (IntSet.toList t)
1✔
255
          | otherwise =
×
256
               case Map.lookup t toV of
1✔
257
                 Nothing -> error "quadratizePB.prodDefs: should not happen"
×
258
                 Just v -> v
1✔
259

260
    obj :: PBFile.Sum
261
    obj = fromMaybe [] $ PBFile.pbObjectiveFunction formula
1✔
262

263
    minObj :: Integer
264
    minObj = SAT.pbLowerBound obj
1✔
265

266
    penalty :: PBFile.Sum
267
    penalty = [(w * w2, ts) | (w,ts) <- concat [p x y z | (z,(x,y)) <- prodDefs]]
1✔
268
      where
269
        -- The penalty function P(x,y,z) = xy − 2xz − 2yz + 3z is such that
270
        -- P(x,y,z)=0 when z⇔xy and P(x,y,z)>0 when z⇎xy.
271
        p x y z = [(1,[x,y]), (-2,[x,z]), (-2,[y,z]), (3,[z])]
1✔
272
        w2 = max (maxObj - minObj) 0 + 1
1✔
273

274
    conv :: PBFile.Sum -> PBFile.Sum
275
    conv s = [(w, f t) | (w,t) <- s]
1✔
276
      where
277
        f t =
1✔
278
          case Map.lookup t' toV of
1✔
279
            Just v  -> [v]
1✔
280
            Nothing
281
              | IntSet.size t' >= 3 -> map g [t1, t2]
1✔
282
              | otherwise -> t
×
283
          where
284
            t' = IntSet.fromList t
1✔
285
            (t1, t2) = m Map.! t'
1✔
286
        g t
1✔
287
          | IntSet.size t == 1 = head $ IntSet.toList t
1✔
288
          | otherwise = toV Map.! t
×
289

290

291
collectDegGe3Terms :: PBFile.Formula -> Set IntSet
292
collectDegGe3Terms formula = Set.fromList [t' | t <- terms, let t' = IntSet.fromList t, IntSet.size t' >= 3]
1✔
293
  where
294
    sums = maybeToList (PBFile.pbObjectiveFunction formula) ++
1✔
295
           [lhs | (lhs,_,_) <- PBFile.pbConstraints formula]
1✔
296
    terms = [t | s <- sums, (_,t) <- s]
1✔
297

298
newtype PBQuadratizeInfo = PBQuadratizeInfo TseitinInfo
299
  deriving (Eq, Show)
×
300

301
instance Transformer PBQuadratizeInfo where
302
  type Source PBQuadratizeInfo = SAT.Model
303
  type Target PBQuadratizeInfo = SAT.Model
304

305
instance ForwardTransformer PBQuadratizeInfo where
306
  transformForward (PBQuadratizeInfo info) = transformForward info
1✔
307

308
instance BackwardTransformer PBQuadratizeInfo where
309
  transformBackward (PBQuadratizeInfo info) = transformBackward info
1✔
310

311
instance ObjValueTransformer PBQuadratizeInfo where
312
  type SourceObjValue PBQuadratizeInfo = Integer
313
  type TargetObjValue PBQuadratizeInfo = Integer
314

315
instance ObjValueForwardTransformer PBQuadratizeInfo where
316
  transformObjValueForward _ = id
1✔
317

318
instance ObjValueBackwardTransformer PBQuadratizeInfo where
319
  transformObjValueBackward _ = id
1✔
320

NEW
321
instance J.ToJSON PBQuadratizeInfo where
×
NEW
322
  toJSON (PBQuadratizeInfo info) =
×
NEW
323
    case J.toJSON info of
×
NEW
324
      J.Object obj -> J.Object $ KeyMap.insert "type" (J.String "PBQuadratizeInfo") obj
×
NEW
325
      _ -> undefined
×
326

327
-- -----------------------------------------------------------------------------
328

329
-- | Convert inequality constraints into equality constraints by introducing surpass variables.
330
inequalitiesToEqualitiesPB :: PBFile.Formula -> (PBFile.Formula, PBInequalitiesToEqualitiesInfo)
331
inequalitiesToEqualitiesPB formula = runST $ do
1✔
332
  db <- newPBStore
1✔
333
  SAT.newVars_ db (PBFile.pbNumVars formula)
1✔
334

335
  defs <- liftM catMaybes $ forM (PBFile.pbConstraints formula) $ \constr -> do
1✔
336
    case constr of
1✔
337
      (lhs, PBFile.Eq, rhs) -> do
1✔
338
        SAT.addPBNLExactly db lhs rhs
1✔
339
        return Nothing
1✔
340
      (lhs, PBFile.Ge, rhs) -> do
1✔
341
        case asClause (lhs,rhs) of
1✔
342
          Just clause -> do
1✔
343
            SAT.addPBNLExactly db [(1, [- l | l <- clause])] 0
1✔
344
            return Nothing
1✔
345
          Nothing -> do
1✔
346
            let maxSurpass = max (SAT.pbUpperBound lhs - rhs) 0
1✔
347
                maxSurpassNBits = head [i | i <- [0..], maxSurpass < bit i]
1✔
348
            vs <- SAT.newVars db maxSurpassNBits
1✔
349
            SAT.addPBNLExactly db (lhs ++ [(-c,[x]) | (c,x) <- zip (iterate (*2) 1) vs]) rhs
1✔
350
            if maxSurpassNBits > 0 then do
1✔
351
              return $ Just (lhs, rhs, vs)
1✔
352
            else
353
              return Nothing
1✔
354

355
  formula' <- getPBFormula db
1✔
356
  return
1✔
357
    ( formula'{ PBFile.pbObjectiveFunction = PBFile.pbObjectiveFunction formula }
1✔
358
    , PBInequalitiesToEqualitiesInfo (PBFile.pbNumVars formula) (PBFile.pbNumVars formula') defs
1✔
359
    )
360
  where
361
    asLinSum :: SAT.PBSum -> Maybe (SAT.PBLinSum, Integer)
362
    asLinSum s = do
1✔
363
      ret <- forM s $ \(c, ls) -> do
1✔
364
        case ls of
1✔
365
          [] -> return (Nothing, c)
1✔
366
          [l] -> return (Just (c,l), 0)
1✔
367
          _ -> mzero
1✔
368
      return (catMaybes (map fst ret), sum (map snd ret))
1✔
369

370
    asClause :: (SAT.PBSum, Integer) -> Maybe SAT.Clause
371
    asClause (lhs, rhs) = do
1✔
372
      (lhs', off) <- asLinSum lhs
1✔
373
      let rhs' = rhs - off
1✔
374
      case SAT.normalizePBLinAtLeast (lhs', rhs') of
1✔
375
        (lhs'', 1) | all (\(c,_) -> c == 1) lhs'' -> return (map snd lhs'')
×
376
        _ -> mzero
1✔
377

378
data PBInequalitiesToEqualitiesInfo
379
  = PBInequalitiesToEqualitiesInfo !Int !Int [(PBFile.Sum, Integer, [SAT.Var])]
380
  deriving (Eq, Show)
×
381

382
instance Transformer PBInequalitiesToEqualitiesInfo where
383
  type Source PBInequalitiesToEqualitiesInfo = SAT.Model
384
  type Target PBInequalitiesToEqualitiesInfo = SAT.Model
385

386
instance ForwardTransformer PBInequalitiesToEqualitiesInfo where
387
  transformForward (PBInequalitiesToEqualitiesInfo _nv1 nv2 defs) m =
1✔
388
    array (1, nv2) $ assocs m ++ [(v, testBit n i) | (lhs, rhs, vs) <- defs, let n = SAT.evalPBSum m lhs - rhs, (i,v) <- zip [0..] vs]
1✔
389

390
instance BackwardTransformer PBInequalitiesToEqualitiesInfo where
391
  transformBackward (PBInequalitiesToEqualitiesInfo nv1 _nv2 _defs) = SAT.restrictModel nv1
1✔
392

393
instance ObjValueTransformer PBInequalitiesToEqualitiesInfo where
394
  type SourceObjValue PBInequalitiesToEqualitiesInfo = Integer
395
  type TargetObjValue PBInequalitiesToEqualitiesInfo = Integer
396

397
instance ObjValueForwardTransformer PBInequalitiesToEqualitiesInfo where
398
  transformObjValueForward _ = id
1✔
399

400
instance ObjValueBackwardTransformer PBInequalitiesToEqualitiesInfo where
401
  transformObjValueBackward _ = id
1✔
402

NEW
403
instance J.ToJSON PBInequalitiesToEqualitiesInfo where
×
NEW
404
  toJSON (PBInequalitiesToEqualitiesInfo nv1 nv2 defs) =
×
NEW
405
    J.object
×
NEW
406
    [ "type" .= J.String "PBInequalitiesToEqualitiesInfo"
×
NEW
407
    , "num_original_variables" .= nv1
×
NEW
408
    , "num_transformed_variables" .= nv2
×
NEW
409
    , "slack" .= J.toJSONList
×
NEW
410
        [ J.object
×
NEW
411
          [ "lhs" .= jPBSum lhs
×
NEW
412
          , "rhs" .= rhs
×
NEW
413
          , "slack" .= J.toJSONList [fromString ("x" ++ show v) :: J.Value | v <- vs]
×
414
          ]
NEW
415
        | (lhs, rhs, vs) <- defs
×
416
        ]
417
    ]
418

419
-- -----------------------------------------------------------------------------
420

421
unconstrainPB :: PBFile.Formula -> ((PBFile.Formula, Integer), PBUnconstrainInfo)
422
unconstrainPB formula = (unconstrainPB' formula', PBUnconstrainInfo info)
1✔
423
  where
424
    (formula', info) = inequalitiesToEqualitiesPB formula
1✔
425

426
newtype PBUnconstrainInfo = PBUnconstrainInfo PBInequalitiesToEqualitiesInfo
427
  deriving (Eq, Show)
×
428

429
instance Transformer PBUnconstrainInfo where
430
  -- type Source PBUnconstrainInfo = Source PBInequalitiesToEqualitiesInfo
431
  type Source PBUnconstrainInfo = SAT.Model
432
  -- type Target PBUnconstrainInfo = Target PBInequalitiesToEqualitiesInfo
433
  type Target PBUnconstrainInfo = SAT.Model
434

435
instance ForwardTransformer PBUnconstrainInfo where
436
  transformForward (PBUnconstrainInfo info) = transformForward info
1✔
437

438
instance BackwardTransformer PBUnconstrainInfo where
439
  transformBackward (PBUnconstrainInfo info) = transformBackward info
1✔
440

441
instance ObjValueTransformer PBUnconstrainInfo where
442
  -- type SourceObjValue PBUnconstrainInfo = SourceObjValue PBInequalitiesToEqualitiesInfo
443
  type SourceObjValue PBUnconstrainInfo = Integer
444
  -- type TargetObjValue PBUnconstrainInfo = TargetObjValue PBInequalitiesToEqualitiesInfo
445
  type TargetObjValue PBUnconstrainInfo = Integer
446

447
instance ObjValueForwardTransformer PBUnconstrainInfo where
448
  transformObjValueForward (PBUnconstrainInfo info) = transformObjValueForward info
×
449

450
instance ObjValueBackwardTransformer PBUnconstrainInfo where
451
  transformObjValueBackward (PBUnconstrainInfo info) = transformObjValueBackward info
×
452

NEW
453
instance J.ToJSON PBUnconstrainInfo where
×
NEW
454
  toJSON (PBUnconstrainInfo info) =
×
NEW
455
    case J.toJSON info of
×
NEW
456
      J.Object obj -> J.Object $ KeyMap.insert "type" (J.String "PBUnconstrainInfo") obj
×
NEW
457
      _ -> undefined
×
458

459
unconstrainPB' :: PBFile.Formula -> (PBFile.Formula, Integer)
460
unconstrainPB' formula =
1✔
461
  ( formula
1✔
462
    { PBFile.pbObjectiveFunction = Just $ obj1 ++ obj2
1✔
463
    , PBFile.pbConstraints = []
1✔
464
    , PBFile.pbNumConstraints = 0
1✔
465
    }
466
  , obj1ub
1✔
467
  )
468
  where
469
    obj1 = fromMaybe [] (PBFile.pbObjectiveFunction formula)
1✔
470
    obj1ub = SAT.pbUpperBound obj1
1✔
471
    obj1lb = SAT.pbLowerBound obj1
1✔
472
    p = obj1ub - obj1lb + 1
1✔
473
    obj2 = [(p*c, IntSet.toList ls) | (ls, c) <- Map.toList obj2', c /= 0]
1✔
474
    obj2' = Map.unionsWith (+) [sq ((-rhs, []) : lhs) | (lhs, PBFile.Eq, rhs) <- PBFile.pbConstraints formula]
1✔
475
    sq ts = Map.fromListWith (+) $ do
1✔
476
              (c1,ls1) <- ts
1✔
477
              (c2,ls2) <- ts
1✔
478
              let ls3 = IntSet.fromList ls1 `IntSet.union` IntSet.fromList ls2
1✔
479
              guard $ not $ isFalse ls3
1✔
480
              return (ls3, c1*c2)
1✔
481
    isFalse ls = not $ IntSet.null $ ls `IntSet.intersection` IntSet.map negate ls
1✔
482

483
-- -----------------------------------------------------------------------------
484

485
pb2qubo' :: PBFile.Formula -> ((PBFile.Formula, Integer), PB2QUBOInfo')
486
pb2qubo' formula = ((formula2, th2), ComposedTransformer info1 info2)
1✔
487
  where
488
    ((formula1, th1), info1) = unconstrainPB formula
1✔
489
    ((formula2, th2), info2) = quadratizePB' (formula1, th1)
1✔
490

491
type PB2QUBOInfo' = ComposedTransformer PBUnconstrainInfo PBQuadratizeInfo
492

493
-- -----------------------------------------------------------------------------
494

495
type PB2WBOInfo = IdentityTransformer SAT.Model
496

497
pb2wbo :: PBFile.Formula -> (PBFile.SoftFormula, PB2WBOInfo)
498
pb2wbo formula
×
499
  = ( PBFile.SoftFormula
×
500
      { PBFile.wboTopCost = Nothing
×
501
      , PBFile.wboConstraints = cs1 ++ cs2
×
502
      , PBFile.wboNumVars = PBFile.pbNumVars formula
×
503
      , PBFile.wboNumConstraints = PBFile.pbNumConstraints formula + length cs2
×
504
      }
505
    , IdentityTransformer
×
506
    )
507
  where
508
    cs1 = [(Nothing, c) | c <- PBFile.pbConstraints formula]
×
509
    cs2 = case PBFile.pbObjectiveFunction formula of
×
510
            Nothing -> []
×
511
            Just e  ->
512
              [ if w >= 0
×
513
                then (Just w,       ([(-1,ls)], PBFile.Ge, 0))
×
514
                else (Just (abs w), ([(1,ls)],  PBFile.Ge, 1))
×
515
              | (w,ls) <- e
×
516
              ]
517

518
wbo2pb :: PBFile.SoftFormula -> (PBFile.Formula, WBO2PBInfo)
519
wbo2pb wbo = runST $ do
1✔
520
  let nv = PBFile.wboNumVars wbo
1✔
521
  db <- newPBStore
1✔
522
  (obj, defs) <- addWBO db wbo
1✔
523
  formula <- getPBFormula db
1✔
524
  return
1✔
525
    ( formula{ PBFile.pbObjectiveFunction = Just obj }
1✔
526
    , WBO2PBInfo nv (PBFile.pbNumVars formula) defs
1✔
527
    )
528

529
data WBO2PBInfo = WBO2PBInfo !Int !Int [(SAT.Var, PBFile.Constraint)]
530
  deriving (Eq, Show)
×
531

532
instance Transformer WBO2PBInfo where
533
  type Source WBO2PBInfo = SAT.Model
534
  type Target WBO2PBInfo = SAT.Model
535

536
instance ForwardTransformer WBO2PBInfo where
537
  transformForward (WBO2PBInfo _nv1 nv2 defs) m =
1✔
538
    array (1, nv2) $ assocs m ++ [(v, SAT.evalPBConstraint m constr) | (v, constr) <- defs]
1✔
539

540
instance BackwardTransformer WBO2PBInfo where
541
  transformBackward (WBO2PBInfo nv1 _nv2 _defs) = SAT.restrictModel nv1
1✔
542

NEW
543
instance J.ToJSON WBO2PBInfo where
×
NEW
544
  toJSON (WBO2PBInfo nv1 nv2 defs) =
×
NEW
545
    J.object
×
NEW
546
    [ "type" .= J.String "WBO2PBInfo"
×
NEW
547
    , "num_original_variables" .= nv1
×
NEW
548
    , "num_transformed_variables" .= nv2
×
NEW
549
    , "definitions" .= J.object
×
NEW
550
        [ fromString ("x" ++ show v) .= jPBConstraint constr
×
NEW
551
        | (v, constr) <- defs
×
552
        ]
553
    ]
554

555
addWBO :: (PrimMonad m, SAT.AddPBNL m enc) => enc -> PBFile.SoftFormula -> m (SAT.PBSum, [(SAT.Var, PBFile.Constraint)])
556
addWBO db wbo = do
1✔
557
  SAT.newVars_ db $ PBFile.wboNumVars wbo
1✔
558

559
  objRef <- newMutVar []
1✔
560
  objOffsetRef <- newMutVar 0
1✔
561
  defsRef <- newMutVar []
1✔
562
  trueLitRef <- newMutVar SAT.litUndef
1✔
563

564
  forM_ (PBFile.wboConstraints wbo) $ \(cost, constr@(lhs,op,rhs)) -> do
1✔
565
    case cost of
1✔
566
      Nothing -> do
1✔
567
        case op of
1✔
568
          PBFile.Ge -> SAT.addPBNLAtLeast db lhs rhs
1✔
569
          PBFile.Eq -> SAT.addPBNLExactly db lhs rhs
1✔
570
        trueLit <- readMutVar trueLitRef
1✔
571
        when (trueLit == SAT.litUndef) $ do
1✔
572
          case detectTrueLit constr of
1✔
573
            Nothing -> return ()
×
574
            Just l -> writeMutVar trueLitRef l
1✔
575
      Just w -> do
1✔
576
        case op of
1✔
577
          PBFile.Ge -> do
1✔
578
            case lhs of
1✔
579
              [(c,ls)] | c > 0 && (rhs + c - 1) `div` c == 1 -> do
1✔
580
                -- c ∧L ≥ rhs ⇔ ∧L ≥ ⌈rhs / c⌉
581
                -- ∧L ≥ 1 ⇔ ∧L
582
                -- obj += w * (1 - ∧L)
583
                unless (null ls) $ do
1✔
584
                  modifyMutVar objRef (\obj -> (-w,ls) : obj)
1✔
585
                  modifyMutVar objOffsetRef (+ w)
1✔
586
              [(c,ls)] | c < 0 && (rhs + abs c - 1) `div` abs c + 1 == 1 -> do
1✔
587
                -- c*∧L ≥ rhs ⇔ -1*∧L ≥ ⌈rhs / abs c⌉ ⇔ (1 - ∧L) ≥ ⌈rhs / abs c⌉ + 1 ⇔ ¬∧L ≥ ⌈rhs / abs c⌉ + 1
588
                -- ¬∧L ≥ 1 ⇔ ¬∧L
589
                -- obj += w * ∧L
590
                if null ls then do
×
591
                  modifyMutVar objOffsetRef (+ w)
×
592
                else do
1✔
593
                  modifyMutVar objRef ((w,ls) :)
1✔
594
              _ | rhs > 0 && and [c >= rhs && length ls == 1 | (c,ls) <- lhs] -> do
1✔
595
                -- ∑L ≥ 1 ⇔ ∨L ⇔ ¬∧¬L
596
                -- obj += w * ∧¬L
597
                if null lhs then do
1✔
598
                  modifyMutVar objOffsetRef (+ w)
1✔
599
                else do
1✔
600
                  modifyMutVar objRef ((w, [-l | (_,[l]) <- lhs]) :)
1✔
601
              _ -> do
1✔
602
                sel <- SAT.newVar db
1✔
603
                SAT.addPBNLAtLeastSoft db sel lhs rhs
1✔
604
                modifyMutVar objRef ((w,[-sel]) :)
1✔
605
                modifyMutVar defsRef ((sel,constr) :)
1✔
606
          PBFile.Eq -> do
1✔
607
            sel <- SAT.newVar db
1✔
608
            SAT.addPBNLExactlySoft db sel lhs rhs
1✔
609
            modifyMutVar objRef ((w,[-sel]) :)
1✔
610
            modifyMutVar defsRef ((sel,constr) :)
1✔
611

612
  offset <- readMutVar objOffsetRef
1✔
613
  when (offset /= 0) $ do
1✔
614
    l <- readMutVar trueLitRef
1✔
615
    trueLit <-
616
      if l /= SAT.litUndef then
1✔
617
        return l
1✔
618
      else do
1✔
619
        v <- SAT.newVar db
1✔
620
        SAT.addClause db [v]
1✔
UNCOV
621
        modifyMutVar defsRef ((v, ([], PBFile.Ge, 0)) :)
×
622
        return v
1✔
623
    modifyMutVar objRef ((offset,[trueLit]) :)
1✔
624

625
  obj <- liftM reverse $ readMutVar objRef
1✔
626
  defs <- liftM reverse $ readMutVar defsRef
1✔
627

628
  case PBFile.wboTopCost wbo of
1✔
629
    Nothing -> return ()
×
630
    Just t -> SAT.addPBNLAtMost db obj (t - 1)
1✔
631

632
  return (obj, defs)
1✔
633

634

635
detectTrueLit :: PBFile.Constraint -> Maybe SAT.Lit
636
detectTrueLit (lhs, op, rhs) =
1✔
637
  case op of
1✔
638
    PBFile.Ge -> f lhs rhs
1✔
639
    PBFile.Eq -> f lhs rhs `mplus` f [(- c, ls) | (c,ls) <- lhs] (- rhs)
1✔
640
  where
641
    f [(c, [l])] rhs
1✔
642
      | c > 0 && (rhs + c - 1) `div` c == 1 =
1✔
643
          -- c l ≥ rhs ↔ l ≥ ⌈rhs / c⌉
644
          return l
1✔
645
      | c < 0 && rhs `div` c == 0 =
1✔
646
          -- c l ≥ rhs ↔ l ≤ ⌊rhs / c⌋
647
          return (- l)
1✔
648
    f _ _ = Nothing
1✔
649

650
-- -----------------------------------------------------------------------------
651

652
type SAT2PBInfo = IdentityTransformer SAT.Model
653

654
sat2pb :: CNF.CNF -> (PBFile.Formula, SAT2PBInfo)
655
sat2pb cnf
×
656
  = ( PBFile.Formula
×
657
      { PBFile.pbObjectiveFunction = Nothing
×
658
      , PBFile.pbConstraints = map f (CNF.cnfClauses cnf)
×
659
      , PBFile.pbNumVars = CNF.cnfNumVars cnf
×
660
      , PBFile.pbNumConstraints = CNF.cnfNumClauses cnf
×
661
      }
662
    , IdentityTransformer
×
663
    )
664
  where
665
    f clause = ([(1,[l]) | l <- SAT.unpackClause clause], PBFile.Ge, 1)
×
666

667
type PB2SATInfo = TseitinInfo
668

669
-- | Convert a pseudo boolean formula φ to a equisatisfiable CNF formula ψ
670
-- together with two functions f and g such that:
671
--
672
-- * if M ⊨ φ then f(M) ⊨ ψ
673
--
674
-- * if M ⊨ ψ then g(M) ⊨ φ
675
--
676
pb2sat :: PBFile.Formula -> (CNF.CNF, PB2SATInfo)
677
pb2sat = pb2satWith def
×
678

679
pb2satWith :: PB.Strategy -> PBFile.Formula -> (CNF.CNF, PB2SATInfo)
680
pb2satWith strategy formula = runST $ do
1✔
681
  db <- newCNFStore
1✔
682
  let nv1 = PBFile.pbNumVars formula
1✔
683
  SAT.newVars_ db nv1
1✔
684
  tseitin <-  Tseitin.newEncoder db
1✔
685
  pb <- PB.newEncoderWithStrategy tseitin strategy
1✔
686
  pbnlc <- PBNLC.newEncoder pb tseitin
×
687
  forM_ (PBFile.pbConstraints formula) $ \(lhs,op,rhs) -> do
1✔
688
    case op of
1✔
689
      PBFile.Ge -> SAT.addPBNLAtLeast pbnlc lhs rhs
1✔
690
      PBFile.Eq -> SAT.addPBNLExactly pbnlc lhs rhs
1✔
691
  cnf <- getCNFFormula db
1✔
692
  defs <- Tseitin.getDefinitions tseitin
1✔
693
  return (cnf, TseitinInfo nv1 (CNF.cnfNumVars cnf) defs)
1✔
694

695
-- -----------------------------------------------------------------------------
696

697
type MaxSAT2WBOInfo = IdentityTransformer SAT.Model
698

699
maxsat2wbo :: CNF.WCNF -> (PBFile.SoftFormula, MaxSAT2WBOInfo)
700
maxsat2wbo
×
701
  CNF.WCNF
702
  { CNF.wcnfTopCost = top
703
  , CNF.wcnfClauses = cs
704
  , CNF.wcnfNumVars = nv
705
  , CNF.wcnfNumClauses = nc
706
  } =
707
  ( PBFile.SoftFormula
×
708
    { PBFile.wboTopCost = Nothing
×
709
    , PBFile.wboConstraints = map f cs
×
710
    , PBFile.wboNumVars = nv
×
711
    , PBFile.wboNumConstraints = nc
×
712
    }
713
  , IdentityTransformer
×
714
  )
715
  where
716
    f (w,c)
×
717
     | w>=top    = (Nothing, p) -- hard constraint
×
718
     | otherwise = (Just w, p)  -- soft constraint
×
719
     where
720
       p = ([(1,[l]) | l <- SAT.unpackClause c], PBFile.Ge, 1)
×
721

722
type WBO2MaxSATInfo = TseitinInfo
723

724
wbo2maxsat :: PBFile.SoftFormula -> (CNF.WCNF, WBO2MaxSATInfo)
725
wbo2maxsat = wbo2maxsatWith def
1✔
726

727
wbo2maxsatWith :: PB.Strategy -> PBFile.SoftFormula -> (CNF.WCNF, WBO2MaxSATInfo)
728
wbo2maxsatWith strategy formula = runST $ do
1✔
729
  db <- newCNFStore
1✔
730
  SAT.newVars_ db (PBFile.wboNumVars formula)
1✔
731
  tseitin <-  Tseitin.newEncoder db
1✔
732
  pb <- PB.newEncoderWithStrategy tseitin strategy
1✔
733
  pbnlc <- PBNLC.newEncoder pb tseitin
1✔
734

735
  softClauses <- liftM mconcat $ forM (PBFile.wboConstraints formula) $ \(cost, (lhs,op,rhs)) -> do
1✔
736
    case cost of
1✔
737
      Nothing ->
738
        case op of
1✔
739
          PBFile.Ge -> SAT.addPBNLAtLeast pbnlc lhs rhs >> return mempty
1✔
740
          PBFile.Eq -> SAT.addPBNLExactly pbnlc lhs rhs >> return mempty
1✔
741
      Just c -> do
1✔
742
        case op of
1✔
743
          PBFile.Ge -> do
1✔
744
            lhs2 <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityPos lhs
×
745
            let (lhs3,rhs3) = SAT.normalizePBLinAtLeast (lhs2,rhs)
1✔
746
            if rhs3==1 && and [c==1 | (c,_) <- lhs3] then
1✔
747
              return $ Seq.singleton (c, SAT.packClause [l | (_,l) <- lhs3])
1✔
748
            else do
1✔
749
              lit <- PB.encodePBLinAtLeast pb (lhs3,rhs3)
1✔
750
              return $ Seq.singleton (c, SAT.packClause [lit])
1✔
751
          PBFile.Eq -> do
1✔
752
            lhs2 <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityBoth lhs
×
753
            lit1 <- PB.encodePBLinAtLeast pb (lhs2, rhs)
1✔
754
            lit2 <- PB.encodePBLinAtLeast pb ([(-c, l) | (c,l) <- lhs2], negate rhs)
1✔
755
            lit <- Tseitin.encodeConjWithPolarity tseitin Tseitin.polarityPos [lit1,lit2]
1✔
756
            return $ Seq.singleton (c, SAT.packClause [lit])
1✔
757

758
  case PBFile.wboTopCost formula of
1✔
759
    Nothing -> return ()
×
760
    Just top -> SAT.addPBNLAtMost pbnlc [(c, [-l | l <- SAT.unpackClause clause]) | (c,clause) <- F.toList softClauses] (top - 1)
1✔
761

762
  let top = F.sum (fst <$> softClauses) + 1
1✔
763
  cnf <- getCNFFormula db
1✔
764
  let cs = softClauses <> Seq.fromList [(top, clause) | clause <- CNF.cnfClauses cnf]
1✔
765
  let wcnf = CNF.WCNF
1✔
766
             { CNF.wcnfNumVars = CNF.cnfNumVars cnf
1✔
767
             , CNF.wcnfNumClauses = Seq.length cs
1✔
768
             , CNF.wcnfTopCost = top
1✔
769
             , CNF.wcnfClauses = F.toList cs
1✔
770
             }
771
  defs <- Tseitin.getDefinitions tseitin
1✔
772
  return (wcnf, TseitinInfo (PBFile.wboNumVars formula) (CNF.cnfNumVars cnf) defs)
1✔
773

774
-- -----------------------------------------------------------------------------
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

© 2025 Coveralls, Inc