toysolver-0.7.0: src/ToySolver/SAT/Solver/SLS/UBCSAT.hs
{-# LANGUAGE CPP #-}
{-# 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
#if MIN_VERSION_megaparsec(6,0,0)
import Data.Void
#endif
import System.Directory
import System.IO
import System.IO.Temp
import System.Process
import Text.Megaparsec hiding (try)
#if MIN_VERSION_megaparsec(6,0,0)
import Text.Megaparsec.Char
#else
import Text.Megaparsec.String
#endif
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
#if MIN_VERSION_megaparsec(6,0,0)
solution :: MonadParsec Void String m => Int -> m (Integer, SAT.Model)
#else
solution :: Int -> Parser (Integer, SAT.Model)
#endif
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