toysolver-0.8.0: src/ToySolver/SAT/Solver/SLS/UBCSAT.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_HADDOCK show-extensions #-}
----------------------------------------------------------------------
-- |
-- Module : ToySolver.SAT.Solver.SLS.UBCSAT
-- Copyright : (c) Masahiro Sakai 2017
-- License : BSD-style
--
-- Maintainer : masahiro.sakai@gmail.com
-- Stability : provisional
-- Portability : non-portable
--
-- References:
--
----------------------------------------------------------------------
module ToySolver.SAT.Solver.SLS.UBCSAT
( ubcsatBest
, ubcsatBestFeasible
, ubcsatMany
, Options (..)
) where
import Control.Exception
import Control.Monad
import Data.Array.IArray
import Data.Char
import Data.Default
import Data.Either
import Data.Function
import Data.List
import Data.Void
import System.Directory
import System.IO
import System.IO.Temp
import System.Process
import Text.Megaparsec hiding (try)
import Text.Megaparsec.Char
import qualified ToySolver.FileFormat.CNF as CNF
import qualified ToySolver.SAT.Types as SAT
data Options
= Options
{ optCommand :: FilePath
, optTempDir :: Maybe FilePath
, optProblem :: CNF.WCNF
, optProblemFile :: Maybe FilePath
, optVarInit :: [SAT.Lit]
}
instance Default Options where
def = Options
{ optCommand = "ubcsat"
, optTempDir = Nothing
, optProblem =
CNF.WCNF
{ CNF.wcnfNumVars = 0
, CNF.wcnfNumClauses = 0
, CNF.wcnfTopCost = 1
, CNF.wcnfClauses = []
}
, optProblemFile = Nothing
, optVarInit = []
}
ubcsatBestFeasible :: Options -> IO (Maybe (Integer, SAT.Model))
ubcsatBestFeasible opt = do
ret <- ubcsatBest opt
case ret of
Nothing -> return Nothing
Just (obj,_) ->
if obj < CNF.wcnfTopCost (optProblem opt) then
return ret
else
return Nothing
ubcsatBest :: Options -> IO (Maybe (Integer, SAT.Model))
ubcsatBest opt = do
sols <- ubcsatMany opt
case sols of
[] -> return Nothing
_ -> return $ Just $ minimumBy (compare `on` fst) sols
ubcsatMany :: Options -> IO [(Integer, SAT.Model)]
ubcsatMany opt = do
dir <- case optTempDir opt of
Just dir -> return dir
Nothing -> getTemporaryDirectory
let f fname
| null (optVarInit opt) = ubcsat' opt fname Nothing
| otherwise = do
withTempFile dir ".txt" $ \varInitFile h -> do
hSetBinaryMode h True
hSetBuffering h (BlockBuffering Nothing)
forM_ (split 10 (optVarInit opt)) $ \xs -> do
hPutStrLn h $ unwords (map show xs)
hClose h
ubcsat' opt fname (Just varInitFile)
case optProblemFile opt of
Just fname -> f fname
Nothing -> do
withTempFile dir ".wcnf" $ \fname h -> do
hClose h
CNF.writeFile fname (optProblem opt)
f fname
ubcsat' :: Options -> FilePath -> Maybe FilePath -> IO [(Integer, SAT.Model)]
ubcsat' opt fname varInitFile = do
let wcnf = optProblem opt
let args =
[ "-w" | ".wcnf" `isSuffixOf` map toLower fname] ++
[ "-alg", "irots"
, "-seed", "0"
, "-runs", "10"
, "-cutoff", show (CNF.wcnfNumVars wcnf * 50)
, "-timeout", show (10 :: Int)
, "-gtimeout", show (30 :: Int)
, "-solve"
, "-r", "bestsol"
, "-inst", fname
] ++
(case varInitFile of
Nothing -> []
Just fname2 -> ["-varinitfile", fname2])
stdinStr = ""
putStrLn $ "c Running " ++ show (optCommand opt) ++ " with " ++ show args
ret <- try $ readProcess (optCommand opt) args stdinStr
case ret of
Left (err :: IOError) -> do
forM_ (lines (show err)) $ \l -> do
putStr "c " >> putStrLn l
return []
Right s -> do
forM_ (lines s) $ \l -> putStr "c " >> putStrLn l
return $ scanSolutions (CNF.wcnfNumVars wcnf) s
scanSolutions :: Int -> String -> [(Integer, SAT.Model)]
scanSolutions nv s = rights $ map (parse (solution nv) "") $ lines s
solution :: MonadParsec Void String m => Int -> m (Integer, SAT.Model)
solution nv = do
skipSome digitChar
space
_ <- char '0' <|> char '1'
space
obj <- liftM read $ some digitChar
space
values <- many ((char '0' >> return False) <|> (char '1' >> return True))
let m = array (1, nv) (zip [1..] values)
return (obj, m)
split :: Int -> [a] -> [[a]]
split n = go
where
go [] = []
go xs =
case splitAt n xs of
(ys, zs) -> ys : go zs