{-# LANGUAGE BangPatterns #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Text.PBFile -- Copyright : (c) Masahiro Sakai 2011 -- License : BSD-style -- -- Maintainer : masahiro.sakai@gmail.com -- Portability : non-portable (BangPatterns) -- -- A parser library for .opb file and .wbo files used by PB Competition. -- -- References: -- -- * Input/Output Format and Solver Requirements for the Competitions of -- Pseudo-Boolean Solvers -- -- ----------------------------------------------------------------------------- module ToySolver.Text.PBFile ( -- * Abstract Syntax Formula (..) , Constraint , Op (..) , SoftFormula (..) , SoftConstraint , Sum , WeightedTerm , Term , Lit (..) , Var -- * Parsing .opb files , parseOPBString , parseOPBFile -- * Parsing .wbo files , parseWBOString , parseWBOFile -- * Show .opb files , renderOPB -- * Show .wbo files , renderWBO ) where import Prelude hiding (sum) import Control.Exception (assert) import Control.Monad import Data.List hiding (sum) import Data.Maybe import Data.Word import Text.Parsec import Text.Parsec.String import Text.Printf import ToySolver.Internal.TextUtil -- | Pair of /objective function/ and a list of constraints. data Formula = Formula { pbObjectiveFunction :: Maybe Sum , pbConstraints :: [Constraint] , pbNumVars :: !Int , pbNumConstraints :: !Int } deriving (Eq, Ord, Show) -- | Lhs, relational operator and rhs. type Constraint = (Sum, Op, Integer) -- | Relational operators data Op = Ge -- ^ /greater than or equal/ | Eq -- ^ /equal/ deriving (Eq, Ord, Show, Enum, Bounded) -- | A pair of /top cost/ and a list of soft constraints. data SoftFormula = SoftFormula { wboTopCost :: Maybe Integer , wboConstraints :: [SoftConstraint] , wboNumVars :: !Int , wboNumConstraints :: !Int } deriving (Eq, Ord, Show) -- | A pair of weight and constraint. type SoftConstraint = (Maybe Integer, Constraint) -- | Sum of 'WeightedTerm' type Sum = [WeightedTerm] -- | Coefficient and 'Term' type WeightedTerm = (Integer, Term) -- | List of variables interpreted as products type Term = [Lit] -- | Positive (resp. negative) literal is represented as a positive (resp. negative) integer. type Lit = Int -- | Variable are repserented positive integer. type Var = Int -- ::= [] formula :: Parser Formula formula = do h <- optionMaybe hint sequence_of_comments obj <- optionMaybe objective cs <- sequence_of_comments_or_constraints return $ Formula { pbObjectiveFunction = obj , pbConstraints = cs , pbNumVars = fromMaybe (pbComputeNumVars obj cs) (fmap fst h) , pbNumConstraints = fromMaybe (length cs) (fmap snd h) } hint :: Parser (Int,Int) hint = try $ do _ <- char '*' zeroOrMoreSpace string "#variable=" zeroOrMoreSpace nv <- unsigned_integer oneOrMoreSpace string "#constraint=" zeroOrMoreSpace nc <- unsigned_integer _ <- manyTill anyChar eol return (fromIntegral nv, fromIntegral nc) -- ::= [] sequence_of_comments :: Parser () sequence_of_comments = skipMany comment -- XXX: we allow empty sequence -- ::= "*" comment :: Parser () comment = do _ <- char '*' _ <- manyTill anyChar eol return () -- ::= [] sequence_of_comments_or_constraints :: Parser [Constraint] sequence_of_comments_or_constraints = do xs <- many1 comment_or_constraint return $ catMaybes xs -- ::= | comment_or_constraint :: Parser (Maybe Constraint) comment_or_constraint = (comment >> return Nothing) <|> (liftM Just constraint) -- ::= "min:" ";" objective :: Parser Sum objective = do _ <- string "min:" zeroOrMoreSpace obj <- sum _ <- char ';' eol return obj -- ::= ";" constraint :: Parser Constraint constraint = do lhs <- sum op <- relational_operator zeroOrMoreSpace rhs <- integer zeroOrMoreSpace semi return (lhs, op, rhs) -- ::= | sum :: Parser Sum sum = many1 weightedterm -- ::= weightedterm :: Parser WeightedTerm weightedterm = do w <- integer oneOrMoreSpace t <- term oneOrMoreSpace return (w,t) -- ::= | "+" | "-" integer :: Parser Integer integer = msum [ unsigned_integer , char '+' >> unsigned_integer , char '-' >> liftM negate unsigned_integer ] -- ::= | unsigned_integer :: Parser Integer unsigned_integer = do ds <- many1 digit return $! readUnsignedInteger ds -- ::= ">=" | "=" relational_operator :: Parser Op relational_operator = (string ">=" >> return Ge) <|> (string "=" >> return Eq) -- ::= "x" variablename :: Parser Var variablename = do _ <- char 'x' i <- unsigned_integer return $! fromIntegral i -- ::= " " [] oneOrMoreSpace :: Parser () oneOrMoreSpace = skipMany1 (char ' ') -- ::= [" " ] zeroOrMoreSpace :: Parser () zeroOrMoreSpace = skipMany (char ' ') eol :: Parser () eol = char '\n' >> return () semi :: Parser () semi = char ';' >> eol {- For linear pseudo-Boolean instances, is defined as ::= For non-linear instances, is defined as ::= -} term :: Parser Term term = oneOrMoreLiterals -- ::= | oneOrMoreLiterals :: Parser [Lit] oneOrMoreLiterals = do l <- literal mplus (try $ oneOrMoreSpace >> liftM (l:) (oneOrMoreLiterals)) (return [l]) -- Note that we cannot use sepBy1. -- In "p `sepBy1` q", p should success whenever q success. -- But it's not the case here. -- ::= | "~" literal :: Parser Lit literal = variablename <|> (char '~' >> liftM negate variablename) -- | Parse a .opb file containing pseudo boolean problem. parseOPBString :: SourceName -> String -> Either ParseError Formula parseOPBString = parse formula -- | Parse a .opb format string containing pseudo boolean problem. parseOPBFile :: FilePath -> IO (Either ParseError Formula) parseOPBFile = parseFromFile formula -- ::= softformula :: Parser SoftFormula softformula = do h <- optionMaybe hint sequence_of_comments top <- softheader cs <- wbo_sequence_of_comments_or_constraints return $ SoftFormula { wboTopCost = top , wboConstraints = cs , wboNumVars = fromMaybe (wboComputeNumVars cs) (fmap fst h) , wboNumConstraints = fromMaybe (length cs) (fmap snd h) } -- ::= "soft:" [] ";" softheader :: Parser (Maybe Integer) softheader = do _ <- string "soft:" zeroOrMoreSpace -- XXX top <- optionMaybe unsigned_integer zeroOrMoreSpace -- XXX semi return top -- ::= [] wbo_sequence_of_comments_or_constraints :: Parser [SoftConstraint] wbo_sequence_of_comments_or_constraints = do xs <- many1 wbo_comment_or_constraint return $ catMaybes xs -- ::= || wbo_comment_or_constraint :: Parser (Maybe SoftConstraint) wbo_comment_or_constraint = (comment >> return Nothing) <|> m where m = liftM Just $ (constraint >>= \c -> return (Nothing, c)) <|> softconstraint -- ::= "[" "]" softconstraint :: Parser SoftConstraint softconstraint = do _ <- char '[' zeroOrMoreSpace cost <- unsigned_integer zeroOrMoreSpace _ <- char ']' zeroOrMoreSpace -- XXX c <- constraint return (Just cost, c) -- | Parse a .wbo file containing weighted boolean optimization problem. parseWBOString :: SourceName -> String -> Either ParseError SoftFormula parseWBOString = parse softformula -- | Parse a .wbo format string containing weighted boolean optimization problem. parseWBOFile :: FilePath -> IO (Either ParseError SoftFormula) parseWBOFile = parseFromFile softformula renderOPB :: Formula -> String renderOPB opb = showOPB opb "" renderWBO :: SoftFormula -> String renderWBO wbo = showWBO wbo "" showOPB :: Formula -> ShowS showOPB opb = (size . part1 . part2) where nv = pbNumVars opb nc = pbNumConstraints opb size = showString (printf "* #variable= %d #constraint= %d\n" nv nc) part1 = case pbObjectiveFunction opb of Nothing -> id Just o -> showString "min: " . showSum o . showString ";\n" part2 = foldr (.) id (map showConstraint (pbConstraints opb)) showWBO :: SoftFormula -> ShowS showWBO wbo = size . part1 . part2 where nv = wboNumVars wbo nc = wboNumConstraints wbo size = showString (printf "* #variable= %d #constraint= %d\n" nv nc) part1 = case wboTopCost wbo of Nothing -> showString "soft: ;\n" Just t -> showString "soft: " . showsPrec 0 t . showString ";\n" part2 = foldr (.) id (map showSoftConstraint (wboConstraints wbo)) showSum :: Sum -> ShowS showSum = foldr (.) id . map showWeightedTerm showWeightedTerm :: WeightedTerm -> ShowS showWeightedTerm (c, lits) = foldr (\f g -> f . showChar ' ' . g) id (x:xs) where x = if c >= 0 then showChar '+' . showsPrec 0 c else showsPrec 0 c xs = map showLit lits showLit :: Lit -> ShowS showLit lit = if lit > 0 then v else showChar '~' . v where v = showChar 'x' . showsPrec 0 (abs lit) showConstraint :: Constraint -> ShowS showConstraint (lhs, op, rhs) = showSum lhs . f op . showChar ' ' . showsPrec 0 rhs . showString ";\n" where f Eq = showString "=" f Ge = showString ">=" showSoftConstraint :: SoftConstraint -> ShowS showSoftConstraint (cost, constr) = case cost of Nothing -> showConstraint constr Just c -> showChar '[' . showsPrec 0 c . showChar ']' . showChar ' ' . showConstraint constr pbComputeNumVars :: Maybe Sum -> [Constraint] -> Int pbComputeNumVars obj cs = maximum (0 : vs) where vs = do s <- maybeToList obj ++ [s | (s,_,_) <- cs] (_, tm) <- s lit <- tm return $ abs lit wboComputeNumVars :: [SoftConstraint] -> Int wboComputeNumVars cs = maximum (0 : vs) where vs = do s <- [s | (_, (s,_,_)) <- cs] (_, tm) <- s lit <- tm return $ abs lit