packages feed

toysolver-0.0.3: src/SAT/PBO.hs

{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  SAT.PBO
-- Copyright   :  (c) Masahiro Sakai 2012
-- License     :  BSD-style
-- 
-- Maintainer  :  masahiro.sakai@gmail.com
-- Stability   :  provisional
-- Portability :  portable
--
-- Pseudo-Boolean Optimization (PBO) Solver
--
-----------------------------------------------------------------------------
module SAT.PBO where

import Control.Monad
import Data.List
import Data.Ord
import Text.Printf
import SAT
import SAT.Types

data SearchStrategy
  = LinearSearch
  | BinarySearch
-- 'nothaddock' is inserted not to confuse haddock
  -- nothaddock | AdaptiveSearch

data Options
  = Options
  { optLogger               :: String -> IO ()
  , optUpdater              :: Model -> Integer -> IO ()
  , optObjFunVarsHeuristics :: Bool
  , optSearchStrategy       :: SearchStrategy
  }

defaultOptions :: Options
defaultOptions
  = Options
  { optLogger               = \_ -> return ()
  , optUpdater              = \_ _ -> return ()
  , optObjFunVarsHeuristics = True
  , optSearchStrategy       = LinearSearch
  }

minimize :: Solver -> [(Integer, Lit)] -> Options -> IO (Maybe Model)
minimize solver obj opt = do
  when (optObjFunVarsHeuristics opt) $ do
    forM_ obj $ \(c,l) -> do
      let p = if c > 0 then not (litPolarity l) else litPolarity l
      setVarPolarity solver (litVar l) p
    forM_ (zip [1..] (map snd (sortBy (comparing fst) [(abs c, l) | (c,l) <- obj]))) $ \(n,l) -> do
      replicateM n $ varBumpActivity solver (litVar l)

  result <- solve solver
  if not result then
     return Nothing
   else
     case optSearchStrategy opt of
       LinearSearch -> liftM Just linSearch
       BinarySearch -> liftM Just binSearch

  where
   logIO :: String -> IO ()
   logIO = optLogger opt

   update :: Model -> Integer -> IO ()
   update = optUpdater opt

   linSearch :: IO Model
   linSearch = do
     m <- model solver
     let v = pbEval m obj
     update m v
     addPBAtMost solver obj (v - 1)
     result <- solve solver
     if result
       then linSearch
       else return m

   binSearch :: IO Model
   binSearch = do
{-
     logIO $ printf "Binary Search: minimizing %s \n" $ 
       intercalate " "
         [c' ++ " " ++ l'
         | (c,l) <- obj
         , let c' = if c < 0 then show c else "+" ++ show c
         , let l' = (if l < 0 then "~" else "") ++ "x" ++ show (litVar l)
         ]
-}
     m0 <- model solver
     let v0 = pbEval m0 obj
     update m0 v0
     let ub0 = v0 - 1
         lb0 = pbLowerBound obj
     addPBAtMost solver obj ub0

     let loop lb ub m | ub < lb = return m
         loop lb ub m = do
           let mid = (lb + ub) `div` 2
           logIO $ printf "Binary Search: %d <= obj <= %d; guessing obj <= %d" lb ub mid
           sel <- newVar solver
           addPBAtMostSoft solver sel obj mid
           ret <- solveWith solver [sel]
           if ret
           then do
             m2 <- model solver
             let v = pbEval m2 obj
             update m2 v
             -- deactivating temporary constraint
             -- FIXME: 本来は制約の削除をしたい
             addClause solver [-sel]
             let ub' = v - 1
             logIO $ printf "Binary Search: updating upper bound: %d -> %d" ub ub'
             addPBAtMost solver obj ub'
             loop lb ub' m2
           else do
             -- deactivating temporary constraint
             -- FIXME: 本来は制約の削除をしたい
             addClause solver [-sel]
             let lb' = mid + 1
             logIO $ printf "Binary Search: updating lower bound: %d -> %d" lb lb'
             addPBAtLeast solver obj lb'
             loop lb' ub m

     loop lb0 ub0 m0