toysolver-0.3.0: src/ToySolver/SAT/PBNLC.hs
{-# OPTIONS_GHC -Wall #-}
-----------------------------------------------------------------------------
-- |
-- Module : ToySolver.SAT.PBNLC
-- Copyright : (c) Masahiro Sakai 2015
-- License : BSD-style
--
-- Maintainer : masahiro.sakai@gmail.com
-- Stability : provisional
-- Portability : portable
--
-----------------------------------------------------------------------------
module ToySolver.SAT.PBNLC
(
PBTerm
, PBSum
-- * Adding constraints
, addPBAtLeast
, addPBAtMost
, addPBExactly
, addPBAtLeastSoft
, addPBAtMostSoft
, addPBExactlySoft
-- * Linearlization
, linearizePBSum
, linearizePBSumWithPolarity
-- * Evaluation
, evalPBSum
) where
import qualified ToySolver.SAT as SAT
import ToySolver.SAT.Types
import ToySolver.SAT.TseitinEncoder
import ToySolver.Internal.Util (revForM)
type PBTerm = (Integer, [Lit])
type PBSum = [PBTerm]
-- | Add a non-linear pseudo boolean constraints /c1*ls1 + c2*ls2 + … ≥ n/.
addPBAtLeast
:: Encoder
-> PBSum -- ^ @[(c1,ls1),(c2,ls2),…]@
-> Integer -- ^ /n/
-> IO ()
addPBAtLeast enc lhs rhs = do
let c = sum [c | (c,[]) <- lhs]
lhs' <- linearizePBSumWithPolarity enc polarityPos [(c,ls) | (c,ls) <- lhs, not (null ls)]
SAT.addPBAtLeast (encSolver enc) lhs' (rhs - c)
-- | Add a non-linear pseudo boolean constraints /c1*ls1 + c2*ls2 + … ≥ n/.
addPBAtMost
:: Encoder
-> PBSum -- ^ @[(c1,ls1),(c2,ls2),…]@
-> Integer -- ^ /n/
-> IO ()
addPBAtMost enc lhs rhs =
addPBAtLeast enc [(-c,ls) | (c,ls) <- lhs] (negate rhs)
-- | Add a non-linear pseudo boolean constraints /c1*ls1 + c2*ls2 + … = n/.
addPBExactly
:: Encoder
-> PBSum -- ^ @[(c1,ls1),(c2,ls2),…]@
-> Integer -- ^ /n/
-> IO ()
addPBExactly enc lhs rhs = do
let c = sum [c | (c,[]) <- lhs]
lhs' <- linearizePBSum enc [(c,ls) | (c,ls) <- lhs, not (null ls)]
SAT.addPBExactly (encSolver enc) lhs' (rhs - c)
-- | Add a soft non-linear pseudo boolean constraints /sel ⇒ c1*ls1 + c2*ls2 + … ≥ n/.
addPBAtLeastSoft
:: Encoder
-> Lit -- ^ Selector literal @sel@
-> PBSum -- ^ @[(c1,ls1),(c2,ls2),…]@
-> Integer -- ^ /n/
-> IO ()
addPBAtLeastSoft enc sel lhs rhs = do
let c = sum [c | (c,[]) <- lhs]
lhs' <- linearizePBSumWithPolarity enc polarityPos [(c,ls) | (c,ls) <- lhs, not (null ls)]
SAT.addPBAtLeastSoft (encSolver enc) sel lhs' (rhs - c)
-- | Add a soft non-linear pseudo boolean constraints /sel ⇒ c1*ls1 + c2*ls2 + … ≤ n/.
addPBAtMostSoft
:: Encoder
-> Lit -- ^ Selector literal @sel@
-> PBSum -- ^ @[(c1,ls1),(c2,ls2),…]@
-> Integer -- ^ /n/
-> IO ()
addPBAtMostSoft enc sel lhs rhs =
addPBAtLeastSoft enc sel [(negate c, lit) | (c,lit) <- lhs] (negate rhs)
-- | Add a soft non-linear pseudo boolean constraints /lit ⇒ c1*ls1 + c2*ls2 + … = n/.
addPBExactlySoft
:: Encoder
-> Lit -- ^ indicator @lit@
-> PBSum -- ^ @[(c1,ls1),(c2,ls2),…]@
-> Integer -- ^ /n/
-> IO ()
addPBExactlySoft enc sel lhs rhs = do
let c = sum [c | (c,[]) <- lhs]
lhs' <- linearizePBSum enc [(c,ls) | (c,ls) <- lhs, not (null ls)]
SAT.addPBExactlySoft (encSolver enc) sel lhs' (rhs - c)
-- | Encode a non-linear 'PBSum' into a lienar 'PBLinSum'.
--
-- @linearizePBSum enc s@ is equivalent to @linearizePBSumWithPolarity enc polarityBoth@.
linearizePBSum
:: Encoder
-> PBSum
-> IO PBLinSum
linearizePBSum enc = linearizePBSumWithPolarity enc polarityBoth
-- | Linearize a non-linear 'PBSum' into a lienar 'PBLinSum'.
--
-- The input 'PBSum' is assumed to occur only in specified polarity.
--
-- * If @'polarityPosOccurs' p@, the value of resulting 'PBLinSum' is /greater than/ or /equal to/ the value of original 'PBSum'.
--
-- * If @'polarityNegOccurs' p@, the value of resulting 'PBLinSum' is /lesser than/ or /equal to/ the value of original 'PBSum'.
--
linearizePBSumWithPolarity
:: Encoder
-> Polarity -- polarity /p/
-> PBSum
-> IO PBLinSum
linearizePBSumWithPolarity enc p xs =
revForM xs $ \(c,ls) -> do
l <- if c > 0 then
encodeConjWithPolarity enc p ls
else
encodeConjWithPolarity enc (negatePolarity p) ls
return (c,l)
evalPBSum :: IModel m => m -> PBSum -> Integer
evalPBSum m xs = sum [c | (c,lits) <- xs, all (evalLit m) lits]