packages feed

toysolver-0.6.0: app/toyqbf.hs

{-# LANGUAGE ScopedTypeVariables, CPP #-}
{-# OPTIONS_GHC -Wall #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  toyqbf
-- Copyright   :  (c) Masahiro Sakai 2016
-- License     :  BSD-style
-- 
-- Maintainer  :  masahiro.sakai@gmail.com
-- Stability   :  experimental
-- Portability :  non-portable (ScopedTypeVariables, CPP)
--
-----------------------------------------------------------------------------

module Main where

import Control.Monad
import Data.Char
import qualified Data.IntSet as IntSet
import Data.List
import Data.Monoid
import Data.Ord
import Data.Version
import Options.Applicative
import System.Exit
import System.IO

import ToySolver.Data.Boolean
import qualified ToySolver.Data.BoolExpr as BoolExpr
import qualified ToySolver.FileFormat.CNF as CNF
import qualified ToySolver.QBF as QBF
import ToySolver.Internal.Util (setEncodingChar8)
import ToySolver.Version

data Options
  = Options
  { optAlgorithm :: String
  , optInput :: FilePath
  }

optionsParser :: Parser Options
optionsParser = Options
  <$> algorithmOption
  <*> fileInput
  where
    fileInput :: Parser FilePath
    fileInput = argument str (metavar "FILE")

    algorithmOption :: Parser String
    algorithmOption = strOption
      $  long "algorithm"
      <> metavar "STR"
      <> help "Algorithm: naive, cegar, cegar-incremental, qe"
      <> value "cegar-incremental"
      <> showDefaultWith id

parserInfo :: ParserInfo Options
parserInfo = info (helper <*> versionOption <*> optionsParser)
  $  fullDesc
  <> header "toyqbf - an QBF solver"
  where
    versionOption :: Parser (a -> a)
    versionOption = infoOption (showVersion version)
      $  hidden
      <> long "version"
      <> help "Show version"

main :: IO ()
main = do
#ifdef FORCE_CHAR8
  setEncodingChar8
#endif
  opt <- execParser parserInfo

  ret <- CNF.parseFile (optInput opt)
  case ret of
    Left err -> hPutStrLn stderr err >> exitFailure
    Right qdimacs -> do
      let nv = CNF.qdimacsNumVars qdimacs
          nc = CNF.qdimacsNumClauses qdimacs
          prefix' = QBF.quantifyFreeVariables nv [(q, IntSet.fromList xs) | (q,xs) <- CNF.qdimacsPrefix qdimacs]
          matrix' = andB [orB [if lit > 0 then BoolExpr.Atom lit else notB (BoolExpr.Atom (abs lit)) | lit <- CNF.unpackClause clause] | clause <- CNF.qdimacsMatrix qdimacs]
      (ans, certificate) <-
        case map toLower (optAlgorithm opt) of
          "naive" -> QBF.solveNaive nv prefix' matrix'
          "cegar" -> QBF.solveCEGAR nv prefix' matrix'
          "cegar-incremental" -> QBF.solveCEGARIncremental nv prefix' matrix'
          "qe" -> QBF.solveQE nv prefix' matrix'
          _ -> do
            putStrLn $ "c unknown --algorithm option: " ++ show (optAlgorithm opt)
            putStrLn $ "s cnf 0 " ++ show nv ++ " " ++ show nc
            exitFailure
      putStrLn $ "s cnf " ++ (if ans then "1" else "0") ++ " " ++ show nv ++ " " ++ show nc
      case certificate of
        Nothing -> return ()
        Just lits -> do
          forM_ (sortBy (comparing abs) (IntSet.toList lits)) $ \lit -> do
            putStrLn ("V " ++ show lit)
      if ans then
        exitWith (ExitFailure 10)
      else
        exitWith (ExitFailure 20)