module ToySolver.Text.PBFile
(
Formula (..)
, Constraint
, Op (..)
, SoftFormula (..)
, SoftConstraint
, Sum
, WeightedTerm
, Term
, Lit (..)
, Var
, parseOPBString
, parseOPBFile
, parseWBOString
, parseWBOFile
, showOPB
, showWBO
) where
import Prelude hiding (sum)
import Control.Monad
import Data.Maybe
import Data.List hiding (sum)
import Text.ParserCombinators.Parsec
import Data.Word
import Control.Exception (assert)
import Text.Printf
import ToySolver.Internal.TextUtil
data Formula
= Formula
{ pbObjectiveFunction :: Maybe Sum
, pbConstraints :: [Constraint]
, pbNumVars :: !Int
, pbNumConstraints :: !Int
}
deriving (Eq, Ord, Show)
type Constraint = (Sum, Op, Integer)
data Op
= Ge
| Eq
deriving (Eq, Ord, Show, Enum, Bounded)
data SoftFormula
= SoftFormula
{ wboTopCost :: Maybe Integer
, wboConstraints :: [SoftConstraint]
, wboNumVars :: !Int
, wboNumConstraints :: !Int
}
deriving (Eq, Ord, Show)
type SoftConstraint = (Maybe Integer, Constraint)
type Sum = [WeightedTerm]
type WeightedTerm = (Integer, Term)
type Term = [Lit]
type Lit = Int
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
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)
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)
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
term :: Parser Term
term = oneOrMoreLiterals
oneOrMoreLiterals :: Parser [Lit]
oneOrMoreLiterals = do
l <- literal
mplus (try $ oneOrMoreSpace >> liftM (l:) (oneOrMoreLiterals)) (return [l])
literal :: Parser Lit
literal = variablename <|> (char '~' >> liftM negate variablename)
parseOPBString :: SourceName -> String -> Either ParseError Formula
parseOPBString = parse formula
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)
}
softheader :: Parser (Maybe Integer)
softheader = do
_ <- string "soft:"
zeroOrMoreSpace
top <- optionMaybe unsigned_integer
zeroOrMoreSpace
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
c <- constraint
return (Just cost, c)
parseWBOString :: SourceName -> String -> Either ParseError SoftFormula
parseWBOString = parse softformula
parseWBOFile :: FilePath -> IO (Either ParseError SoftFormula)
parseWBOFile = parseFromFile softformula
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