packages feed

toysolver-0.10.0: app/toysolver_check.hs

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where

import Control.Monad
import qualified Data.ByteString.Lazy.Char8 as BL
import Data.Default.Class
import Data.Scientific
import qualified Data.Version as V
import qualified Numeric.Optimization.MIP as MIP
import qualified Numeric.Optimization.MIP.Solution.Gurobi as GurobiSol
import Options.Applicative hiding (Const)
import System.Exit
import System.IO

import qualified ToySolver.FileFormat as FF
import ToySolver.Internal.SolutionChecker
import ToySolver.SAT.LogParser (parseSATLog, parseMaxSATLog, parsePBLog)
import ToySolver.Internal.Util (setEncodingChar8)
import ToySolver.Version


data Mode = ModeSAT | ModePB | ModeWBO | ModeMaxSAT | ModeMIP
  deriving (Eq, Ord, Show)

data Options = Options
  { optInputFile :: FilePath
  , optSolutionFile :: FilePath
  , optMode :: Maybe Mode
  , optFileEncoding :: Maybe String
  , optPBFastParser :: Bool
  , optMIPTol :: MIP.Tol Scientific
  }

optionsParser :: Parser Options
optionsParser = Options
  <$> fileInput
  <*> solutionFileInput
  <*> modeOption
  <*> fileEncodingOption
  <*> pbFastParserOption
  <*> mipTolOptions
  where
    fileInput :: Parser FilePath
    fileInput = argument str (metavar "FILE")

    solutionFileInput :: Parser FilePath
    solutionFileInput = argument str (metavar "SOLUTION_FILE")

    modeOption :: Parser (Maybe Mode)
    modeOption = optional $
          flag' ModeSAT    (long "sat"    <> help "load boolean satisfiability problem in .cnf file")
      <|> flag' ModePB     (long "pb"     <> help "load pseudo boolean problem in .opb file")
      <|> flag' ModeWBO    (long "wbo"    <> help "load weighted boolean optimization problem in .wbo file")
      <|> flag' ModeMaxSAT (long "maxsat" <> help "load MaxSAT problem in .cnf or .wcnf file")
      <|> flag' ModeMIP    (long "mip"    <> help "load LP/MIP problem in .lp or .mps file")
      <|> flag' ModeMIP    (long "lp"     <> help "alias for --mip (deprecated)")

    fileEncodingOption :: Parser (Maybe String)
    fileEncodingOption = optional $ strOption
      $  long "encoding"
      <> metavar "ENCODING"
      <> help "file encoding for LP/MPS files"

    pbFastParserOption :: Parser Bool
    pbFastParserOption = switch
      $  long "pb-fast-parser"
      <> help "use attoparsec-based parser instead of megaparsec-based one for speed"

    mipTolOptions :: Parser (MIP.Tol Scientific)
    mipTolOptions = MIP.Tol <$> intTol <*> feasTol <*> optTol
      where
        intTol = option auto
          $  long "tol-integrality"
          <> metavar "REAL"
          <> help "If a value of integer variable is within this amount from its nearest integer, it is considered feasible."
          <> value (MIP.integralityTol def)
          <> showDefault
        feasTol = option auto
          $  long "tol-feasibility"
          <> metavar "REAL"
          <> help "If the amount of violation of constraints is within this amount, it is considered feasible."
          <> value (MIP.feasibilityTol def)
          <> showDefault
        optTol = option auto
          $  long "tol-optimality"
          <> metavar "REAL"
          <> help "Feasibility tolerance of dual constraints."
          <> value (MIP.optimalityTol def)
          <> showDefault

parserInfo :: ParserInfo Options
parserInfo = info (helper <*> versionOption <*> optionsParser)
  $  fullDesc
  <> header "toysolver-check - a solution checker"
  where
    versionOption :: Parser (a -> a)
    versionOption = infoOption (V.showVersion version)
      $  hidden
      <> long "version"
      <> help "Show version"

main :: IO ()
main = do
#ifdef FORCE_CHAR8
  setEncodingChar8
#endif

  opt <- execParser parserInfo
  let mode =
        case optMode opt of
          Just m  -> m
          Nothing ->
            case FF.getBaseExtension (optInputFile opt) of
              ".cnf"  -> ModeSAT
              ".opb"  -> ModePB
              ".wbo"  -> ModeWBO
              ".wcnf" -> ModeMaxSAT
              ".lp"   -> ModeMIP
              ".mps"  -> ModeMIP
              _ -> ModeSAT

  (ok, ls) <- case mode of
    ModeSAT -> do
      cnf  <- FF.readFile (optInputFile opt)
      (status, m) <- liftM parseSATLog (BL.readFile (optSolutionFile opt))
      pure $ checkSATResult cnf (status, m)

    ModePB -> do
      opb <-
        if optPBFastParser opt then
          liftM FF.unWithFastParser $ FF.readFile (optInputFile opt)
        else
          FF.readFile (optInputFile opt)
      (status, o, m) <- liftM parsePBLog (BL.readFile (optSolutionFile opt))
      pure $ checkPBResult opb (status, o, m)

    ModeWBO -> do
      wbo <-
        if optPBFastParser opt then
          liftM FF.unWithFastParser $ FF.readFile (optInputFile opt)
        else
          FF.readFile (optInputFile opt)
      (status, o, m) <- liftM parsePBLog (BL.readFile (optSolutionFile opt))
      pure $ checkWBOResult wbo (status, o, m)

    ModeMaxSAT -> do
      wcnf  <- FF.readFile (optInputFile opt)
      (status, o, m) <- liftM parseMaxSATLog (BL.readFile (optSolutionFile opt))
      pure $ checkMaxSATResult wcnf (status, o, m)

    ModeMIP -> do
      enc <- mapM mkTextEncoding $ optFileEncoding opt
      mip <- MIP.readFile def{ MIP.optFileEncoding = enc } (optInputFile opt)
      sol <- GurobiSol.readFile (optSolutionFile opt)
      let tol = optMIPTol opt
      pure $ checkMIPResult tol mip sol

  mapM_ putStrLn ls

  unless ok $ exitFailure