module ToySolver.Text.MaxSAT
(
WCNF (..)
, WeightedClause
, Weight
, parseFile
, parseString
, parseByteString
) where
import qualified Data.ByteString.Lazy.Char8 as BS
import Data.Char
import qualified ToySolver.SAT.Types as SAT
import ToySolver.Internal.TextUtil
data WCNF
= WCNF
{ numVars :: !Int
, numClauses :: !Int
, topCost :: !Weight
, clauses :: [WeightedClause]
}
type WeightedClause = (Weight, SAT.Clause)
type Weight = Integer
parseFile :: FilePath -> IO (Either String WCNF)
parseFile filename = do
s <- BS.readFile filename
return $ parseByteString s
parseString :: String -> Either String WCNF
parseString s =
case words l of
(["p","wcnf", nvar, nclause, top]) ->
Right $
WCNF
{ numVars = read nvar
, numClauses = read nclause
, topCost = read top
, clauses = map parseWCNFLine ls
}
(["p","wcnf", nvar, nclause]) ->
Right $
WCNF
{ numVars = read nvar
, numClauses = read nclause
, topCost = fromInteger $ 2^(63::Int) 1
, clauses = map parseWCNFLine ls
}
(["p","cnf", nvar, nclause]) ->
Right $
WCNF
{ numVars = read nvar
, numClauses = read nclause
, topCost = fromInteger $ 2^(63::Int) 1
, clauses = map parseCNFLine ls
}
_ ->
Left "cannot find wcnf/cnf header"
where
(l:ls) = filter (not . isComment) (lines s)
isComment :: String -> Bool
isComment ('c':_) = True
isComment _ = False
parseWCNFLine :: String -> WeightedClause
parseWCNFLine s =
case words s of
(w:xs)
| last xs == "0" -> seq w' $ seqList ys $ (w', ys)
| otherwise -> error "ToySolver.Text.MaxSAT: parse error"
where
w' = readUnsignedInteger w
ys = map readInt $ init xs
_ -> error "ToySolver.Text.MaxSAT: parse error"
parseCNFLine :: String -> WeightedClause
parseCNFLine s
| null xs || last xs /= 0 = error "ToySolver.Text.MaxSAT: parse error"
| otherwise = seqList ys $ (1, ys)
where
xs = map readInt (words s)
ys = init xs
parseByteString :: BS.ByteString -> Either String WCNF
parseByteString s =
case BS.words l of
(["p","wcnf", nvar, nclause, top]) ->
Right $
WCNF
{ numVars = read $ BS.unpack nvar
, numClauses = read $ BS.unpack nclause
, topCost = read $ BS.unpack top
, clauses = map parseWCNFLineBS ls
}
(["p","wcnf", nvar, nclause]) ->
Right $
WCNF
{ numVars = read $ BS.unpack nvar
, numClauses = read $ BS.unpack nclause
, topCost = fromInteger $ 2^(63::Int) 1
, clauses = map parseWCNFLineBS ls
}
(["p","cnf", nvar, nclause]) ->
Right $
WCNF
{ numVars = read $ BS.unpack nvar
, numClauses = read $ BS.unpack nclause
, topCost = fromInteger $ 2^(63::Int) 1
, clauses = map parseCNFLineBS ls
}
_ ->
Left "cannot find wcnf/cnf header"
where
l :: BS.ByteString
ls :: [BS.ByteString]
(l:ls) = filter (not . isCommentBS) (BS.lines s)
parseWCNFLineBS :: BS.ByteString -> WeightedClause
parseWCNFLineBS s =
case BS.readInteger (BS.dropWhile isSpace s) of
Nothing -> error "ToySolver.Text.MaxSAT: no weight"
Just (w, s') -> seq w $ seq xs $ (w, xs)
where
xs = parseClauseBS s'
parseCNFLineBS :: BS.ByteString -> WeightedClause
parseCNFLineBS s = seq xs $ (1, xs)
where
xs = parseClauseBS s
parseClauseBS :: BS.ByteString -> SAT.Clause
parseClauseBS s = seqList xs $ xs
where
xs = go s
go s =
case BS.readInt (BS.dropWhile isSpace s) of
Nothing -> error "ToySolver.Text.MaxSAT: parse error"
Just (0,_) -> []
Just (i,s') -> i : go s'
isCommentBS :: BS.ByteString -> Bool
isCommentBS s =
case BS.uncons s of
Just ('c',_) -> True
_ -> False
seqList :: [a] -> b -> b
seqList [] b = b
seqList (x:xs) b = seq x $ seqList xs b