packages feed

toysolver-0.7.0: samples/programs/probsat/probsat.hs

module Main where

import Control.Monad
import Data.Char
import Data.Default.Class
import Data.Monoid
import qualified Data.Vector.Unboxed as VU
import Options.Applicative
import System.Clock
import System.IO
import qualified System.Random.MWC as Rand
import Text.Printf

import qualified ToySolver.FileFormat as FF
import qualified ToySolver.FileFormat.CNF as CNF
import qualified ToySolver.SAT.Solver.SLS.ProbSAT as ProbSAT
import ToySolver.SAT.Printer (maxsatPrintModel)

data Options = Options
  { optAlgorithm :: String
  , optFileName :: FilePath
  , optOptions :: ProbSAT.Options
  , optRandomSeed :: Maybe Rand.Seed
  , optProbSATFunc :: String
  , optProbSATCB :: Double
  , optProbSATCM :: Double
  , optWalkSATP :: Double
  } deriving (Eq, Show)

optionsParser :: Parser Options
optionsParser = Options
  <$> algOption
  <*> fileInput
  <*> solverOptions
  <*> randomSeedOption
  <*> func
  <*> cb
  <*> cm
  <*> p
  where
    fileInput :: Parser FilePath
    fileInput = argument str (metavar "FILE")

    algOption :: Parser String
    algOption = strOption
      $  long "alg"
      <> metavar "ALGORITHM"
      <> help "Algorithm: walksat, probsat"
      <> value "probsat"
      <> showDefaultWith id

    solverOptions :: Parser ProbSAT.Options
    solverOptions = ProbSAT.Options
      <$> targetOption
      <*> maxTriesOption
      <*> maxFlipsOption
      <*> pickClauseWeightedOption

    randomSeedOption :: Parser (Maybe Rand.Seed)
    randomSeedOption = optional $
      (fmap (Rand.toSeed . VU.fromList . map read . words)) $
      strOption $
        mconcat
        [ long "random-seed"
        , metavar "\"INT ..\""
        , help "random seed"
        ]

    targetOption :: Parser Integer
    targetOption = option auto
      $  long "target"
      <> help "target objective value"
      <> showDefault
      <> metavar "INT"
      <> value (ProbSAT.optTarget def)

    maxTriesOption :: Parser Int
    maxTriesOption = option auto
      $  long "max-tries"
      <> help "maximum number of tries"
      <> showDefault
      <> metavar "INT"
      <> value (ProbSAT.optMaxTries def)

    maxFlipsOption :: Parser Int
    maxFlipsOption = option auto
      $  long "max-flips"
      <> help "maximum number of flips per try"
      <> showDefault
      <> metavar "INT"
      <> value (ProbSAT.optMaxFlips def)

    pickClauseWeightedOption :: Parser Bool
    pickClauseWeightedOption = switch
      $  short 'w'
      <> long "weighted"
      <> help "enable weighted clause selection"
      <> showDefault

    func :: Parser String
    func = strOption
      $  long "probsat-func"
      <> help "function type: exp, poly"
      <> showDefaultWith id
      <> metavar "FUNC"
      <> value "exp"

    cb :: Parser Double
    cb = option auto
      $  long "probsat-cb"
      <> help "c_b parameter"
      <> showDefault
      <> metavar "REAL"
      <> value 3.6

    cm :: Parser Double
    cm = option auto
      $  long "probsat-cm"
      <> help "c_m parameter"
      <> showDefault
      <> metavar "REAL"
      <> value 0.5

    p :: Parser Double
    p = option auto
      $  long "walksat-p"
      <> help "p parameter"
      <> showDefault
      <> metavar "REAL"
      <> value 0.1

parserInfo :: ParserInfo Options
parserInfo = info (helper <*> optionsParser)
  $  fullDesc
  <> header "probsat - an example program of ToySolver.SAT.SLS.ProbSAT"

main :: IO ()
main = do
  opt <- execParser parserInfo
  wcnf <- FF.readFile (optFileName opt)
  solver <- ProbSAT.newSolverWeighted wcnf
  gen <-
    case optRandomSeed opt of
      Nothing -> Rand.createSystemRandom
      Just seed -> Rand.restore seed
  seed <- Rand.save gen
  putStrLn $ "c use --random-seed=" ++ show (unwords . map show . VU.toList . Rand.fromSeed $ seed) ++ " option to reproduce the execution"
  ProbSAT.setRandomGen solver gen
  let callbacks =
        def
        { ProbSAT.cbOnUpdateBestSolution = \_solver obj _sol -> printf "o %d\n" obj
        }
  case map toLower (optAlgorithm opt) of
    "walksat" -> do
      let p = optWalkSATP opt
      ProbSAT.walksat solver (optOptions opt) callbacks p
    "probsat" -> do
      let cb = optProbSATCB opt
          cm = optProbSATCM opt
      seq cb $ seq cm $ return ()
      case map toLower (optProbSATFunc opt) of
        "exp" -> do
          let f make break = cm**make / cb**break
          ProbSAT.probsat solver (optOptions opt) callbacks f
        "poly" -> do
          let eps = 1
              f make break = make**cm / (eps + break**cb)
          ProbSAT.probsat solver (optOptions opt) callbacks f
        _ -> error ("unknown function type: " ++ optProbSATFunc opt)
    _ -> error ("unknown algorithm name: " ++ optAlgorithm opt)
  (obj,sol) <- ProbSAT.getBestSolution solver
  stat <- ProbSAT.getStatistics solver
  printf "c TotalCPUTime = %fs\n" (fromIntegral (toNanoSecs (ProbSAT.statTotalCPUTime stat)) / 10^(9::Int) :: Double)
  printf "c FlipsPerSecond = %f\n" (ProbSAT.statFlipsPerSecond stat)
  when (obj == 0) $ do
    putStrLn "s OPTIMUM FOUND"
  maxsatPrintModel stdout sol (CNF.wcnfNumVars wcnf)