packages feed

toysolver-0.1.0: toysat/UBCSAT.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall #-}
module UBCSAT (ubcsat) where

import Control.Exception
import Control.Monad
import Data.Array.IArray
import Data.Char
import Data.Either
import Data.Function
import Data.List
import System.Process
import Text.ParserCombinators.Parsec hiding (try)

import qualified ToySolver.SAT.Types as SAT
import qualified ToySolver.Text.MaxSAT as MaxSAT

ubcsat :: FilePath -> FilePath -> MaxSAT.WCNF -> IO (Maybe SAT.Model)
ubcsat cmd fname wcnf = do
  let args =
        [ "-w" | ".wcnf" `isSuffixOf` map toLower fname] ++
        [ "-alg", "irots"
        , "-seed", "0"
        , "-runs", "10"
        , "-cutoff", show (MaxSAT.numVars wcnf * 50)
        , "-solve"
        , "-r", "bestsol"
        , "-inst", fname
        ]
      stdinStr = ""
    
  ret <- try $ readProcess cmd args stdinStr
  case ret of
    Left (err :: IOError) -> do
      forM_ (lines (show err)) $ \l -> do
        putStr "c " >> putStrLn l
      return Nothing
    Right s -> do
      forM_ (lines s) $ \l -> putStr "c " >> putStrLn l
      case scanSolutions s of
        [] -> return Nothing
        sols -> do
          let (obj,m) = minimumBy (compare `on` fst) sols
          if obj < MaxSAT.topCost wcnf then
            return $ Just $ array (1, MaxSAT.numVars wcnf) (zip [1..] m)
          else
            return Nothing

scanSolutions :: String -> [(Integer, [Bool])]
scanSolutions s = rights $ map (parse solution "") $ lines s

solution :: Parser (Integer, [Bool])
solution = do
  skipMany1 digit
  spaces
  _ <- char '0' <|> char '1'
  spaces
  obj <- liftM read $ many1 digit
  spaces
  values <- many ((char '0' >> return False) <|> (char '1' >> return True))
  return (obj, values)