toysolver 0.5.0 → 0.6.0
raw patch · 146 files changed
+9402/−4639 lines, 146 filesdep +ansi-wl-pprintdep +bytestring-encodingdep +case-insensitivedep −prettyclassdep ~basedep ~bytestringdep ~clocknew-component:exe:probsat
Dependencies added: ansi-wl-pprint, bytestring-encoding, case-insensitive, optparse-applicative, pretty, zlib
Dependencies removed: prettyclass
Dependency ranges changed: base, bytestring, clock, criterion, hashable, haskeline, lattices, logic-TPTP, megaparsec, mwc-random, tasty-hunit, tasty-quickcheck
Files
- CHANGELOG.markdown +27/−0
- README.md +6/−8
- app/maxsatverify.hs +6/−6
- app/pbverify.hs +2/−2
- app/pigeonhole.hs +3/−1
- app/toyconvert.hs +237/−167
- app/toyfmf.hs +28/−9
- app/toyqbf.hs +59/−79
- app/toysat/UBCSAT.hs +11/−13
- app/toysat/toysat.hs +433/−344
- app/toysmt/ToySolver/SMT/SMTLIB2Solver.hs +0/−1
- app/toysmt/toysmt.hs +39/−52
- app/toysolver.hs +172/−129
- benchmarks/BenchmarkSATLIB.hs +6/−5
- misc/build_bdist_smtcomp.sh +26/−0
- misc/maxsat/toysat/README.md +1/−1
- misc/maxsat/toysat_ls/README.md +2/−2
- misc/pb/README.md +1/−1
- misc/qbf/README.md +1/−1
- misc/smtcomp/bin/starexec_run_default +2/−0
- misc/smtcomp/starexec_description.txt +1/−0
- samples/programs/assign/assign.hs +0/−1
- samples/programs/nonogram/nonogram.hs +1/−3
- samples/programs/numberlink/numberlink.hs +2/−2
- samples/programs/probsat/probsat.hs +177/−0
- samples/programs/survey-propagation/survey-propagation.hs +7/−6
- src/ToySolver/Arith/ContiTraverso.hs +1/−1
- src/ToySolver/Arith/Cooper/Base.hs +11/−3
- src/ToySolver/Arith/DifferenceLogic.hs +4/−2
- src/ToySolver/Arith/MIP.hs +1/−1
- src/ToySolver/Arith/OmegaTest/Base.hs +1/−1
- src/ToySolver/Arith/Simplex.hs +153/−59
- src/ToySolver/Arith/Simplex/Textbook.hs +2/−2
- src/ToySolver/BitVector/Base.hs +14/−2
- src/ToySolver/BitVector/Solver.hs +7/−6
- src/ToySolver/Combinatorial/HittingSet/FredmanKhachiyan1996.hs +2/−2
- src/ToySolver/Combinatorial/HittingSet/GurvichKhachiyan1999.hs +3/−3
- src/ToySolver/Converter.hs +43/−0
- src/ToySolver/Converter/Base.hs +116/−0
- src/ToySolver/Converter/GCNF2MaxSAT.hs +34/−18
- src/ToySolver/Converter/MIP2PB.hs +39/−20
- src/ToySolver/Converter/MIP2SMT.hs +5/−5
- src/ToySolver/Converter/MaxSAT2IP.hs +0/−25
- src/ToySolver/Converter/MaxSAT2WBO.hs +0/−40
- src/ToySolver/Converter/NAESAT.hs +214/−0
- src/ToySolver/Converter/ObjType.hs +1/−1
- src/ToySolver/Converter/PB.hs +674/−0
- src/ToySolver/Converter/PB/Internal/LargestIntersectionFinder.hs +99/−0
- src/ToySolver/Converter/PB/Internal/Product.hs +75/−0
- src/ToySolver/Converter/PB2IP.hs +72/−15
- src/ToySolver/Converter/PB2LSP.hs +6/−6
- src/ToySolver/Converter/PB2SAT.hs +0/−60
- src/ToySolver/Converter/PB2SMP.hs +3/−3
- src/ToySolver/Converter/PB2WBO.hs +0/−39
- src/ToySolver/Converter/PBLinearization.hs +0/−77
- src/ToySolver/Converter/QBF2IPC.hs +67/−0
- src/ToySolver/Converter/QUBO.hs +297/−0
- src/ToySolver/Converter/SAT2IP.hs +0/−26
- src/ToySolver/Converter/SAT2KSAT.hs +43/−28
- src/ToySolver/Converter/SAT2MaxCut.hs +128/−0
- src/ToySolver/Converter/SAT2MaxSAT.hs +281/−0
- src/ToySolver/Converter/SAT2PB.hs +0/−29
- src/ToySolver/Converter/Tseitin.hs +42/−0
- src/ToySolver/Converter/WBO2MaxSAT.hs +0/−89
- src/ToySolver/Converter/WBO2PB.hs +0/−93
- src/ToySolver/Data/AlgebraicNumber/Real.hs +2/−2
- src/ToySolver/Data/AlgebraicNumber/Root.hs +1/−1
- src/ToySolver/Data/BoolExpr.hs +1/−3
- src/ToySolver/Data/Boolean.hs +2/−1
- src/ToySolver/Data/Delta.hs +1/−1
- src/ToySolver/Data/FOL/Arith.hs +1/−1
- src/ToySolver/Data/LA.hs +2/−2
- src/ToySolver/Data/MIP.hs +81/−33
- src/ToySolver/Data/MIP/Base.hs +34/−21
- src/ToySolver/Data/MIP/FileUtils.hs +31/−0
- src/ToySolver/Data/MIP/LPFile.hs +15/−17
- src/ToySolver/Data/MIP/MPSFile.hs +15/−16
- src/ToySolver/Data/MIP/Solver/Glpsol.hs +0/−1
- src/ToySolver/Data/Polyhedron.hs +1/−1
- src/ToySolver/Data/Polynomial/Base.hs +7/−7
- src/ToySolver/Data/Polynomial/Factorization/Hensel/Internal.hs +1/−1
- src/ToySolver/EUF/CongruenceClosure.hs +0/−1
- src/ToySolver/FileFormat.hs +30/−0
- src/ToySolver/FileFormat/Base.hs +88/−0
- src/ToySolver/FileFormat/CNF.hs +389/−0
- src/ToySolver/Graph/ShortestPath.hs +3/−1
- src/ToySolver/Internal/Data/Vec.hs +1/−1
- src/ToySolver/Internal/TextUtil.hs +0/−5
- src/ToySolver/MaxCut.hs +76/−0
- src/ToySolver/QBF.hs +93/−9
- src/ToySolver/QUBO.hs +143/−0
- src/ToySolver/SAT.hs +132/−332
- src/ToySolver/SAT/Config.hs +10/−69
- src/ToySolver/SAT/Encoder/Cardinality.hs +68/−0
- src/ToySolver/SAT/Encoder/Cardinality/Internal/Naive.hs +43/−0
- src/ToySolver/SAT/Encoder/Cardinality/Internal/ParallelCounter.hs +91/−0
- src/ToySolver/SAT/Encoder/Integer.hs +1/−0
- src/ToySolver/SAT/Encoder/PB/Internal/Adder.hs +7/−6
- src/ToySolver/SAT/Encoder/PB/Internal/BDD.hs +1/−0
- src/ToySolver/SAT/ExistentialQuantification.hs +80/−26
- src/ToySolver/SAT/MessagePassing/SurveyPropagation.hs +4/−7
- src/ToySolver/SAT/MessagePassing/SurveyPropagation/OpenCL.hs +4/−5
- src/ToySolver/SAT/PBO.hs +1/−1
- src/ToySolver/SAT/PBO/BCD.hs +1/−1
- src/ToySolver/SAT/PBO/BCD2.hs +5/−5
- src/ToySolver/SAT/PBO/Context.hs +3/−3
- src/ToySolver/SAT/PBO/MSU4.hs +1/−1
- src/ToySolver/SAT/PBO/UnsatBased.hs +1/−1
- src/ToySolver/SAT/SLS/ProbSAT.hs +548/−0
- src/ToySolver/SAT/Store/CNF.hs +8/−6
- src/ToySolver/SAT/Types.hs +56/−10
- src/ToySolver/SDP.hs +196/−0
- src/ToySolver/SMT.hs +2/−2
- src/ToySolver/Text/CNF.hs +18/−71
- src/ToySolver/Text/GCNF.hs +13/−109
- src/ToySolver/Text/MaxSAT.hs +0/−149
- src/ToySolver/Text/QDimacs.hs +25/−105
- src/ToySolver/Text/SDPFile.hs +74/−23
- src/ToySolver/Text/WCNF.hs +48/−0
- src/ToySolver/Version.hs +17/−2
- src/ToySolver/Version/TH.hs +2/−2
- test/Test/Arith.hs +18/−1
- test/Test/BitVector.hs +4/−2
- test/Test/BoolExpr.hs +0/−1
- test/Test/CNF.hs +72/−0
- test/Test/Converter.hs +349/−0
- test/Test/FiniteModelFinder.hs +1/−1
- test/Test/LPFile.hs +1/−1
- test/Test/MIP.hs +9/−3
- test/Test/ProbSAT.hs +100/−0
- test/Test/QBF.hs +9/−1
- test/Test/QUBO.hs +135/−0
- test/Test/SAT.hs +591/−2036
- test/Test/SAT/Encoder.hs +193/−0
- test/Test/SAT/ExistentialQuantification.hs +204/−0
- test/Test/SAT/MUS.hs +254/−0
- test/Test/SAT/TheorySolver.hs +358/−0
- test/Test/SAT/Types.hs +254/−0
- test/Test/SAT/Utils.hs +548/−0
- test/Test/SDPFile.hs +4/−4
- test/Test/SMT.hs +2/−2
- test/Test/SimplexTextbook.hs +1/−1
- test/Test/Smtlib.hs +2/−4
- test/TestPolynomial.hs +3/−3
- test/TestSuite.hs +18/−0
- toysolver.cabal +106/−29
CHANGELOG.markdown view
@@ -1,3 +1,30 @@+0.6.0+-----+* new solvers:+ * `ToySolver.SAT.SLS.ProbSAT` and sample `probsat` program+* new converters:+ * `ToySolver.Converter.NAESAT`+ * `ToySolver.Converter.SAT2MaxCut`+ * `ToySolver.Converter.SAT2MaxSAT`: SAT and 3-SAT to Max-2-SAT converter+ * `ToySolver.Converter.QBF2IPC`+ * `ToySolver.Converter.QUBO`: QUBO↔IsingModel converter+* new file format API:+ * merge `ToySolver.Text.MaxSAT`, `ToySolver.Text.GCNF`, `ToySolver.Text.QDimacs`, and `ToySolver.Text.CNF`+ info `ToySolver.FileFormat` and `ToySolver.FileFormat.CNF`+ * allow reading/writing `gzip`ped CNF/WCNF/GCNF/QDimacs/LP/MPS files+* rename modules:+ * `ToySolver.Arith.Simplex2` to `ToySolver.Arith.Simplex`+ * `ToySolver.Arith.MIPSolver2` to `ToySolver.Arith.MIP`+ * `ToySolver.Data.Var` to `ToySolver.Data.IntVar`+* `ToySolver.SAT`:+ * add `cancel` function for interruption+ * introduce `PackedClause` type+* `ToySolver.Arith.Simplex`+ * introduce `Config` data type+ * implement bound tightening+* switch from `System.Console.GetOpt` to `optparse-applicative`+* stop supporting GHC-7.8+ 0.5.0 ----- * new solvers:
README.md view
@@ -1,9 +1,13 @@ toysolver ========= -[](https://gitter.im/msakai/toysolver?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)+[](https://gitter.im/msakai/toysolver) -[](http://travis-ci.org/msakai/toysolver) [](https://ci.appveyor.com/project/msakai/toysolver/branch/master) [](https://coveralls.io/r/msakai/toysolver) [](https://hackage.haskell.org/package/toysolver)+[](http://travis-ci.org/msakai/toysolver)+[](https://ci.appveyor.com/project/msakai/toysolver/branch/master)+[](https://coveralls.io/r/msakai/toysolver)+[](https://hackage.haskell.org/package/toysolver)+[](https://opensource.org/licenses/BSD-3-Clause) It provides solver implementations of various problems including SAT, SMT, Max-SAT, PBS (Pseudo Boolean Satisfaction), PBO (Pseudo Boolean Optimization), MILP (Mixed Integer Linear Programming) and non-linear real arithmetic. @@ -122,9 +126,3 @@ * [ersatz-toysat](http://hackage.haskell.org/package/ersatz-toysat) - toysat backend driver for [ersatz](http://hackage.haskell.org/package/ersatz) * [satchmo-toysat](http://hackage.haskell.org/package/satchmo-toysat) - toysat backend driver for [satchmo](http://hackage.haskell.org/package/satchmo)--TODO-------* Local search-
app/maxsatverify.hs view
@@ -6,7 +6,7 @@ import Data.IORef import System.Environment import Text.Printf-import qualified ToySolver.Text.MaxSAT as MaxSAT+import qualified ToySolver.FileFormat.CNF as CNF import ToySolver.SAT.Types import ToySolver.Internal.Util (setEncodingChar8) @@ -17,20 +17,20 @@ #endif [problemFile, modelFile] <- getArgs- Right wcnf <- MaxSAT.parseFile problemFile+ wcnf <- CNF.readFile problemFile model <- liftM readModel (readFile modelFile) costRef <- newIORef 0- forM_ (MaxSAT.clauses wcnf) $ \(w,c) ->+ forM_ (CNF.wcnfClauses wcnf) $ \(w,c) -> unless (eval model c) $- if w == MaxSAT.topCost wcnf+ if w == CNF.wcnfTopCost wcnf then printf "violated hard constraint: %s\n" (show c) else do tc <- readIORef costRef writeIORef costRef $! tc + w printf "total cost = %d\n" =<< readIORef costRef -eval :: Model -> Clause -> Bool-eval m lits = or [evalLit m lit | lit <- lits]+eval :: Model -> PackedClause -> Bool+eval m lits = or [evalLit m lit | lit <- unpackClause lits] readModel :: String -> Model readModel s = array (1, maximum (0 : map fst ls2)) ls2
app/pbverify.hs view
@@ -6,7 +6,7 @@ import System.Environment import Text.Printf import qualified Data.PseudoBoolean as PBFile-import qualified Data.PseudoBoolean.Attoparsec as PBFileAttoparsec+import qualified ToySolver.FileFormat as FF import ToySolver.SAT.Types import ToySolver.Internal.Util (setEncodingChar8) @@ -17,7 +17,7 @@ #endif [problemFile, modelFile] <- getArgs- Right formula <- PBFileAttoparsec.parseOPBFile problemFile+ formula <- FF.readFile problemFile model <- liftM readModel (readFile modelFile) forM_ (PBFile.pbConstraints formula) $ \c -> unless (eval model c) $
app/pigeonhole.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE CPP #-} module Main where +import qualified Data.ByteString.Builder as ByteStringBuilder import Data.List import qualified Data.Map as Map import Data.Map (Map)@@ -8,6 +9,7 @@ import System.Exit import System.IO import Data.PseudoBoolean as PBFile+import qualified ToySolver.FileFormat as FF import ToySolver.Internal.Util (setEncodingChar8) pigeonHole :: Integer -> Integer -> Formula@@ -41,7 +43,7 @@ case xs of [p,h] -> do let opb = pigeonHole (read p) (read h)- hPutOPB stdout opb+ ByteStringBuilder.hPutBuilder stdout $ FF.render opb _ -> do hPutStrLn stderr "Usage: pigeonhole number_of_pigeons number_of_holes" exitFailure
app/toyconvert.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wall #-} ----------------------------------------------------------------------------- -- |@@ -8,111 +9,194 @@ -- -- Maintainer : masahiro.sakai@gmail.com -- Stability : experimental--- Portability : non-portable (CPP)+-- Portability : non-portable -- ----------------------------------------------------------------------------- module Main where import Control.Applicative+import Control.Monad import qualified Data.ByteString.Builder as ByteStringBuilder import Data.Char import Data.Default.Class-import Data.Maybe import qualified Data.Foldable as F+import Data.Maybe+import Data.Monoid import Data.Scientific (Scientific) import qualified Data.Text.Lazy.Builder as TextBuilder import qualified Data.Text.Lazy.IO as TLIO import qualified Data.Traversable as T import qualified Data.Version as V-import System.Environment+import Options.Applicative import System.IO import System.Exit import System.FilePath-import System.Console.GetOpt+import Text.PrettyPrint.ANSI.Leijen ((<+>))+import qualified Text.PrettyPrint.ANSI.Leijen as PP import qualified Data.PseudoBoolean as PBFile-import qualified Data.PseudoBoolean.Attoparsec as PBFileAttoparsec import qualified ToySolver.Data.MIP as MIP-import qualified ToySolver.Text.GCNF as GCNF-import qualified ToySolver.Text.MaxSAT as MaxSAT-import qualified ToySolver.Text.CNF as CNF+import ToySolver.Converter import ToySolver.Converter.ObjType-import qualified ToySolver.Converter.SAT2PB as SAT2PB-import qualified ToySolver.Converter.GCNF2MaxSAT as GCNF2MaxSAT-import qualified ToySolver.Converter.MIP2PB as MIP2PB import qualified ToySolver.Converter.MIP2SMT as MIP2SMT-import qualified ToySolver.Converter.MaxSAT2WBO as MaxSAT2WBO-import qualified ToySolver.Converter.PB2IP as PB2IP-import qualified ToySolver.Converter.PBLinearization as PBLinearization-import qualified ToySolver.Converter.PB2LSP as PB2LSP-import qualified ToySolver.Converter.PB2WBO as PB2WBO import qualified ToySolver.Converter.PBSetObj as PBSetObj-import qualified ToySolver.Converter.PB2SMP as PB2SMP-import qualified ToySolver.Converter.PB2SAT as PB2SAT-import qualified ToySolver.Converter.SAT2KSAT as SAT2KSAT-import qualified ToySolver.Converter.WBO2PB as WBO2PB-import qualified ToySolver.Converter.WBO2MaxSAT as WBO2MaxSAT+import qualified ToySolver.FileFormat as FF+import qualified ToySolver.QUBO as QUBO import ToySolver.Version import ToySolver.Internal.Util (setEncodingChar8) -data Flag- = Help- | Version- | Output String- | AsMaxSAT- | ObjType ObjType- | IndicatorConstraint- | SMTSetLogic String- | SMTOptimize- | SMTNoCheck- | SMTNoProduceModel- | Yices2- | Linearization- | LinearizationUsingPB- | KSat !Int- | FileEncoding String- | RemoveUserCuts- deriving Eq+data Options = Options+ { optInput :: FilePath+ , optOutput :: Maybe FilePath+ , optAsMaxSAT :: Bool+ , optObjType :: ObjType+ , optIndicatorConstraint :: Bool+ , optSMTSetLogic :: Maybe String+ , optSMTOptimize :: Bool+ , optSMTNoCheck :: Bool+ , optSMTNoProduceModel :: Bool+ , optYices2 :: Bool+ , optLinearization :: Bool+ , optLinearizationUsingPB :: Bool+ , optKSat :: Maybe Int+ , optFileEncoding :: Maybe String+ , optRemoveUserCuts :: Bool+ } deriving (Eq, Show) -options :: [OptDescr Flag]-options =- [ Option ['h'] ["help"] (NoArg Help) "show help"- , Option ['v'] ["version"] (NoArg Version) "show version number"- , Option ['o'] [] (ReqArg Output "FILE") "output filename"- , Option [] ["maxsat"] (NoArg AsMaxSAT) "treat *.cnf file as MAX-SAT problem"- , Option [] ["obj"] (ReqArg (ObjType . parseObjType) "STRING") "objective function for SAT/PBS: none (default), max-one, max-zero"- , Option [] ["indicator"] (NoArg IndicatorConstraint) "use indicator constraints in output LP file"- , Option [] ["smt-set-logic"] (ReqArg SMTSetLogic "STRING") "output \"(set-logic STRING)\""- , Option [] ["smt-optimize"] (NoArg SMTOptimize) "output optimiality condition which uses quantifiers"- , Option [] ["smt-no-check"] (NoArg SMTNoCheck) "do not output \"(check)\""- , Option [] ["smt-no-produce-model"] (NoArg SMTNoProduceModel) "do not output \"(set-option :produce-models true)\"" - , Option [] ["yices2"] (NoArg Yices2) "output for yices2 rather than yices1"- , Option [] ["linearize"] (NoArg Linearization) "linearize nonlinear pseudo-boolean constraints"- , Option [] ["linearizer-pb"] (NoArg LinearizationUsingPB) "Use PB constraint in linearization"- , Option [] ["ksat"] (ReqArg (KSat . read) "NUMBER") "generate k-SAT formula when outputing .cnf file"- , Option [] ["encoding"] (ReqArg FileEncoding "<ENCODING>") "file encoding for LP/MPS files"- , Option [] ["remove-usercuts"] (NoArg RemoveUserCuts) "remove user-defined cuts from LP/MPS files"- ]+optionsParser :: Parser Options+optionsParser = Options+ <$> fileInput+ <*> outputOption+ <*> maxsatOption+ <*> objOption+ <*> indicatorConstraintOption+ <*> smtSetLogicOption+ <*> smtOptimizeOption+ <*> smtNoCheckOption+ <*> smtNoProduceModelOption+ <*> yices2Option+ <*> linearizationOption+ <*> linearizationPBOption+ <*> kSATOption+ <*> encodingOption+ <*> removeUserCutsOption where- parseObjType s =- case map toLower s of- "none" -> ObjNone- "max-one" -> ObjMaxOne- "max-zero" -> ObjMaxZero- _ -> error ("unknown obj: " ++ s)+ fileInput :: Parser FilePath+ fileInput = argument str (metavar "FILE") -header :: String-header = unlines- [ "Usage:"- , " toyconvert -o <outputfile> <inputfile>"- , ""- , "Supported formats:"- , " input: .cnf .wcnf .opb .wbo .gcnf .lp .mps"- , " output: .cnf .wcnf .opb .wbo .lsp .lp .mps .smp .smt2 .ys"- , ""- , "Options:"+ outputOption :: Parser (Maybe FilePath)+ outputOption = optional $ strOption+ $ long "output"+ <> short 'o'+ <> metavar "FILE"+ <> help "output filename"++ maxsatOption :: Parser Bool+ maxsatOption = switch+ $ long "maxsat"+ <> help "treat *.cnf file as MAX-SAT problem"++ objOption :: Parser ObjType+ objOption = option parseObjType+ $ long "obj"+ <> metavar "STR"+ <> help "objective function for SAT/PBS: none (default), max-one, max-zero"+ <> value ObjNone+ <> showDefaultWith showObjType+ where+ showObjType :: ObjType -> String+ showObjType ObjNone = "none"+ showObjType ObjMaxOne = "max-one"+ showObjType ObjMaxZero = "max-zero"++ parseObjType :: ReadM ObjType+ parseObjType = eitherReader $ \s ->+ case map toLower s of+ "none" -> return ObjNone+ "max-one" -> return ObjMaxOne+ "max-zero" -> return ObjMaxZero+ _ -> Left ("unknown obj: " ++ s)++ indicatorConstraintOption :: Parser Bool+ indicatorConstraintOption = switch+ $ long "indicator"+ <> help "use indicator constraints in output LP file"++ smtSetLogicOption :: Parser (Maybe String)+ smtSetLogicOption = optional $ strOption+ $ long "smt-set-logic"+ <> metavar "STR"+ <> help "output \"(set-logic STR)\""++ smtOptimizeOption :: Parser Bool+ smtOptimizeOption = switch+ $ long "smt-optimize"+ <> help "output optimiality condition which uses quantifiers"++ smtNoCheckOption :: Parser Bool+ smtNoCheckOption = switch+ $ long "smt-no-check"+ <> help "do not output \"(check)\""++ smtNoProduceModelOption :: Parser Bool+ smtNoProduceModelOption = switch+ $ long "smt-no-produce-model"+ <> help "do not output \"(set-option :produce-models true)\""++ yices2Option :: Parser Bool+ yices2Option = switch+ $ long "yices2"+ <> help "output for yices2 rather than yices1"++ linearizationOption :: Parser Bool+ linearizationOption = switch+ $ long "linearize"+ <> help "linearize nonlinear pseudo-boolean constraints"++ linearizationPBOption :: Parser Bool+ linearizationPBOption = switch+ $ long "linearizer-pb"+ <> help "Use PB constraint in linearization"++ kSATOption :: Parser (Maybe Int)+ kSATOption = optional $ option auto+ $ long "ksat"+ <> metavar "INT"+ <> help "generate k-SAT formula when outputing .cnf file"++ encodingOption :: Parser (Maybe String)+ encodingOption = optional $ strOption+ $ long "encoding"+ <> metavar "ENCODING"+ <> help "file encoding for LP/MPS files"++ removeUserCutsOption :: Parser Bool+ removeUserCutsOption = switch+ $ long "remove-usercuts"+ <> help "remove user-defined cuts from LP/MPS files"++parserInfo :: ParserInfo Options+parserInfo = info (helper <*> versionOption <*> optionsParser)+ $ fullDesc+ <> header "toyconvert - converter between various kind of problem files"+ <> footerDoc (Just supportedFormatsDoc)+ where+ versionOption :: Parser (a -> a)+ versionOption = infoOption (V.showVersion version)+ $ hidden+ <> long "version"+ <> help "Show version"++supportedFormatsDoc :: PP.Doc+supportedFormatsDoc =+ PP.vsep+ [ PP.text "Supported formats:"+ , PP.indent 2 $ PP.vsep+ [ PP.text "input:" <+> (PP.align $ PP.fillSep $ map PP.text $ words ".cnf .wcnf .opb .wbo .gcnf .lp .mps .qubo")+ , PP.text "output:" <+> (PP.align $ PP.fillSep $ map PP.text $ words ".cnf .wcnf .opb .wbo .lsp .lp .mps .smp .smt2 .ys .qubo")+ ] ] data Problem@@ -120,89 +204,80 @@ | ProbWBO PBFile.SoftFormula | ProbMIP (MIP.Problem Scientific) -readProblem :: [Flag] -> String -> IO Problem+readProblem :: Options -> String -> IO Problem readProblem o fname = do- enc <- T.mapM mkTextEncoding $ last $ Nothing : [Just s | FileEncoding s <- o]- case map toLower (takeExtension fname) of+ enc <- T.mapM mkTextEncoding (optFileEncoding o)+ case getExt fname of ".cnf"- | AsMaxSAT `elem` o -> readWCNF+ | optAsMaxSAT o ->+ liftM (ProbWBO . fst . maxsat2wbo) $ FF.readFile fname | otherwise -> do- ret <- CNF.parseFile fname- case ret of- Left err -> hPrint stderr err >> exitFailure- Right cnf -> return $ ProbOPB $ SAT2PB.convert cnf- ".wcnf" -> readWCNF- ".opb" -> do- ret <- PBFileAttoparsec.parseOPBFile fname- case ret of- Left err -> hPutStrLn stderr err >> exitFailure- Right opb -> return $ ProbOPB opb- ".wbo" -> do- ret <- PBFileAttoparsec.parseWBOFile fname- case ret of- Left err -> hPutStrLn stderr err >> exitFailure- Right wbo -> return $ ProbWBO wbo- ".gcnf" -> do- ret <- GCNF.parseFile fname- case ret of- Left err -> hPutStrLn stderr err >> exitFailure- Right gcnf -> return $ ProbWBO $ MaxSAT2WBO.convert $ GCNF2MaxSAT.convert gcnf+ liftM (ProbOPB . fst . sat2pb) $ FF.readFile fname+ ".wcnf" ->+ liftM (ProbWBO . fst . maxsat2wbo) $ FF.readFile fname+ ".opb" -> liftM ProbOPB $ FF.readFile fname+ ".wbo" -> liftM ProbWBO $ FF.readFile fname+ ".gcnf" ->+ liftM (ProbWBO . fst . maxsat2wbo . fst . gcnf2maxsat) $ FF.readFile fname ".lp" -> ProbMIP <$> MIP.readLPFile def{ MIP.optFileEncoding = enc } fname ".mps" -> ProbMIP <$> MIP.readMPSFile def{ MIP.optFileEncoding = enc } fname+ ".qubo" -> do+ (qubo :: QUBO.Problem Scientific) <- FF.readFile fname+ return $ ProbOPB $ fst $ qubo2pb qubo ext -> error $ "unknown file extension: " ++ show ext- where - readWCNF = do- ret <- MaxSAT.parseFile fname- case ret of- Left err -> hPutStrLn stderr err >> exitFailure- Right wcnf -> return $ ProbWBO $ MaxSAT2WBO.convert $ wcnf -transformProblem :: [Flag] -> Problem -> Problem+getExt :: String -> String+getExt name | (base, ext) <- splitExtension name =+ case map toLower ext of+#ifdef WITH_ZLIB+ ".gz" -> getExt base+#endif+ s -> s++transformProblem :: Options -> Problem -> Problem transformProblem o = transformObj o . transformPBLinearization o . transformMIPRemoveUserCuts o -transformObj :: [Flag] -> Problem -> Problem+transformObj :: Options -> Problem -> Problem transformObj o problem = case problem of- ProbOPB opb | isNothing (PBFile.pbObjectiveFunction opb) -> ProbOPB $ PBSetObj.setObj objType opb+ ProbOPB opb | isNothing (PBFile.pbObjectiveFunction opb) -> ProbOPB $ PBSetObj.setObj (optObjType o) opb _ -> problem- where- objType = last (ObjNone : [t | ObjType t <- o]) -transformPBLinearization :: [Flag] -> Problem -> Problem+transformPBLinearization :: Options -> Problem -> Problem transformPBLinearization o problem- | Linearization `elem` o =+ | optLinearization o = case problem of- ProbOPB opb -> ProbOPB $ PBLinearization.linearize opb (LinearizationUsingPB `elem` o)- ProbWBO wbo -> ProbWBO $ PBLinearization.linearizeWBO wbo (LinearizationUsingPB `elem` o)+ ProbOPB opb -> ProbOPB $ fst $ linearizePB opb (optLinearizationUsingPB o)+ ProbWBO wbo -> ProbWBO $ fst $ linearizeWBO wbo (optLinearizationUsingPB o) ProbMIP mip -> ProbMIP mip | otherwise = problem -transformMIPRemoveUserCuts :: [Flag] -> Problem -> Problem+transformMIPRemoveUserCuts :: Options -> Problem -> Problem transformMIPRemoveUserCuts o problem- | RemoveUserCuts `elem` o =+ | optRemoveUserCuts o = case problem of ProbMIP mip -> ProbMIP $ mip{ MIP.userCuts = [] } _ -> problem | otherwise = problem -writeProblem :: [Flag] -> Problem -> IO ()+writeProblem :: Options -> Problem -> IO () writeProblem o problem = do- enc <- T.mapM mkTextEncoding $ last $ Nothing : [Just s | FileEncoding s <- o]+ enc <- T.mapM mkTextEncoding (optFileEncoding o) let mip2smtOpt = def- { MIP2SMT.optSetLogic = listToMaybe [logic | SMTSetLogic logic <- o]- , MIP2SMT.optCheckSAT = not (SMTNoCheck `elem` o)- , MIP2SMT.optProduceModel = not (SMTNoProduceModel `elem` o)- , MIP2SMT.optOptimize = SMTOptimize `elem` o+ { MIP2SMT.optSetLogic = optSMTSetLogic o+ , MIP2SMT.optCheckSAT = not (optSMTNoCheck o)+ , MIP2SMT.optProduceModel = not (optSMTNoProduceModel o)+ , MIP2SMT.optOptimize = optSMTOptimize o }- case head ([Just fname | Output fname <- o] ++ [Nothing]) of+ case optOutput o of Nothing -> do hSetBinaryMode stdout True hSetBuffering stdout (BlockBuffering Nothing) case problem of- ProbOPB opb -> PBFile.hPutOPB stdout opb- ProbWBO wbo -> PBFile.hPutWBO stdout wbo+ ProbOPB opb -> ByteStringBuilder.hPutBuilder stdout $ FF.render opb+ ProbWBO wbo -> ByteStringBuilder.hPutBuilder stdout $ FF.render wbo ProbMIP mip -> do case MIP.toLPString def mip of Left err -> hPutStrLn stderr ("conversion failure: " ++ err) >> exitFailure@@ -213,46 +288,46 @@ let opb = case problem of ProbOPB opb -> opb ProbWBO wbo ->- case WBO2PB.convert wbo of- (opb, _, _)- | Linearization `elem` o ->+ case wbo2pb wbo of+ (opb, _)+ | optLinearization o -> -- WBO->OPB conversion may have introduced non-linearity- PBLinearization.linearize opb (LinearizationUsingPB `elem` o)+ fst $ linearizePB opb (optLinearizationUsingPB o) | otherwise -> opb ProbMIP mip ->- case MIP2PB.convert (fmap toRational mip) of+ case mip2pb (fmap toRational mip) of Left err -> error err- Right (opb, _, _) -> opb+ Right (opb, _) -> opb wbo = case problem of- ProbOPB opb -> PB2WBO.convert opb+ ProbOPB opb -> fst $ pb2wbo opb ProbWBO wbo -> wbo- ProbMIP _ -> PB2WBO.convert opb+ ProbMIP _ -> fst $ pb2wbo opb lp = case problem of ProbOPB opb ->- case PB2IP.convert opb of- (ip, _, _) -> fmap fromInteger ip+ case pb2ip opb of+ (ip, _) -> fmap fromInteger ip ProbWBO wbo ->- case PB2IP.convertWBO (IndicatorConstraint `elem` o) wbo of- (ip, _, _) -> fmap fromInteger ip+ case wbo2ip (optIndicatorConstraint o) wbo of+ (ip, _) -> fmap fromInteger ip ProbMIP mip -> mip lsp = case problem of- ProbOPB opb -> PB2LSP.convert opb- ProbWBO wbo -> PB2LSP.convertWBO wbo- ProbMIP _ -> PB2LSP.convert opb- case map toLower (takeExtension fname) of- ".opb" -> PBFile.writeOPBFile fname opb- ".wbo" -> PBFile.writeWBOFile fname wbo+ ProbOPB opb -> pb2lsp opb+ ProbWBO wbo -> wbo2lsp wbo+ ProbMIP _ -> pb2lsp opb+ case getExt fname of+ ".opb" -> FF.writeFile fname $ normalizePB opb+ ".wbo" -> FF.writeFile fname $ normalizeWBO wbo ".cnf" ->- case PB2SAT.convert opb of- (cnf, _, _) ->- case head ([Just k | KSat k <- o] ++ [Nothing]) of- Nothing -> CNF.writeFile fname cnf+ case pb2sat opb of+ (cnf, _) ->+ case optKSat o of+ Nothing -> FF.writeFile fname cnf Just k ->- let (cnf2, _, _) = SAT2KSAT.convert k cnf- in CNF.writeFile fname cnf2+ let (cnf2, _) = sat2ksat k cnf+ in FF.writeFile fname cnf2 ".wcnf" ->- case WBO2MaxSAT.convert wbo of- (wcnf, _, _) -> MaxSAT.writeFile fname wcnf+ case wbo2maxsat wbo of+ (wcnf, _) -> FF.writeFile fname wcnf ".lsp" -> withBinaryFile fname WriteMode $ \h -> ByteStringBuilder.hPutBuilder h lsp@@ -260,36 +335,31 @@ ".mps" -> MIP.writeMPSFile def{ MIP.optFileEncoding = enc } fname lp ".smp" -> do withBinaryFile fname WriteMode $ \h ->- ByteStringBuilder.hPutBuilder h (PB2SMP.convert False opb)+ ByteStringBuilder.hPutBuilder h (pb2smp False opb) ".smt2" -> do withFile fname WriteMode $ \h -> do F.mapM_ (hSetEncoding h) enc TLIO.hPutStr h $ TextBuilder.toLazyText $- MIP2SMT.convert mip2smtOpt (fmap toRational lp)+ MIP2SMT.mip2smt mip2smtOpt (fmap toRational lp) ".ys" -> do- let lang = MIP2SMT.YICES (if Yices2 `elem` o then MIP2SMT.Yices2 else MIP2SMT.Yices1)+ let lang = MIP2SMT.YICES (if optYices2 o then MIP2SMT.Yices2 else MIP2SMT.Yices1) withFile fname WriteMode $ \h -> do F.mapM_ (hSetEncoding h) enc TLIO.hPutStr h $ TextBuilder.toLazyText $- MIP2SMT.convert mip2smtOpt{ MIP2SMT.optLanguage = lang } (fmap toRational lp)+ MIP2SMT.mip2smt mip2smtOpt{ MIP2SMT.optLanguage = lang } (fmap toRational lp)+ ".qubo" ->+ case pb2qubo opb of+ ((qubo, _th), _) -> FF.writeFile fname (fmap (fromInteger :: Integer -> Scientific) qubo) ext -> do error $ "unknown file extension: " ++ show ext- + main :: IO () main = do #ifdef FORCE_CHAR8 setEncodingChar8 #endif - args <- getArgs- case getOpt Permute options args of- (o,_,[])- | Help `elem` o -> putStrLn (usageInfo header options)- | Version `elem` o -> putStrLn (V.showVersion version)- (o,[fname],[]) -> do- prob <- readProblem o fname- let prob2 = transformProblem o prob- writeProblem o prob2- (_,_,errs) -> do- hPutStrLn stderr $ concat errs ++ usageInfo header options- exitFailure+ opt <- execParser parserInfo+ prob <- readProblem opt (optInput opt)+ let prob2 = transformProblem opt prob+ writeProblem opt prob2
app/toyfmf.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS_GHC -Wall #-} {-# LANGUAGE CPP, TypeFamilies, OverloadedStrings #-} ----------------------------------------------------------------------------- -- |@@ -22,23 +23,41 @@ import Data.Ratio import Data.String import qualified Data.Text as Text-import System.Environment-import System.IO+import Options.Applicative import qualified Codec.TPTP as TPTP import ToySolver.Data.Boolean import qualified ToySolver.EUF.FiniteModelFinder as MF import ToySolver.Internal.Util (setEncodingChar8) +data Options+ = Options+ { optInput :: FilePath+ , optSize :: Int+ }++optionsParser :: Parser Options+optionsParser = Options+ <$> fileInput+ <*> sizeInput+ where+ fileInput :: Parser FilePath+ fileInput = argument str $ metavar "FILE.tptp"++ sizeInput :: Parser Int+ sizeInput = argument auto $ metavar "SIZE"++parserInfo :: ParserInfo Options+parserInfo = info (helper <*> optionsParser)+ $ fullDesc+ <> header "toyfmf - a finite model finder"+ main :: IO () main = do #ifdef FORCE_CHAR8 setEncodingChar8 #endif-- args <- getArgs- case args of- [fpath, size] -> solve fpath (read size)- _ -> hPutStrLn stderr "Usage: toyfmf <file.tptp> <size>"+ opt <- execParser parserInfo+ solve (optInput opt) (optSize opt) solve :: FilePath -> Int -> IO () solve _ size | size <= 0 = error "<size> should be >=1"@@ -46,7 +65,7 @@ inputs <- TPTP.parseFile fpath let fs = translateProblem inputs - ref <- newIORef 0+ ref <- newIORef (0::Int) let skolem name _ = do n <- readIORef ref let fsym = intern $ unintern name <> "#" <> fromString (show n)@@ -75,7 +94,7 @@ case i of TPTP.Comment _ -> [] TPTP.Include _ _ -> error "\"include\" is not supported yet "- TPTP.AFormula{ TPTP.name = _, TPTP.role = role, TPTP.formula = formula, TPTP.annotations = _ } ->+ TPTP.AFormula{ TPTP.name = _, TPTP.role = _, TPTP.formula = formula, TPTP.annotations = _ } -> return $ translateFormula formula translateFormula :: TPTP.Formula -> MF.Formula
app/toyqbf.hs view
@@ -16,107 +16,87 @@ import Control.Monad import Data.Char-import Data.Default.Class import qualified Data.IntSet as IntSet import Data.List+import Data.Monoid import Data.Ord import Data.Version-import System.Console.GetOpt-import System.Environment+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 qualified ToySolver.Text.QDimacs as QDimacs import ToySolver.Internal.Util (setEncodingChar8) import ToySolver.Version -data Mode- = ModeHelp- | ModeVersion- deriving (Eq, Ord, Bounded, Enum)- data Options = Options- { optMode :: Maybe Mode- , optAlgorithm :: String+ { optAlgorithm :: String+ , optInput :: FilePath } -instance Default Options where- def =- Options- { optMode = Nothing- , optAlgorithm = "cegar-incremental"- }+optionsParser :: Parser Options+optionsParser = Options+ <$> algorithmOption+ <*> fileInput+ where+ fileInput :: Parser FilePath+ fileInput = argument str (metavar "FILE") -options :: [OptDescr (Options -> Options)]-options =- [ Option ['h'] ["help"] (NoArg (\opt -> opt{ optMode = Just ModeHelp })) "show help"- , Option [] ["version"] (NoArg (\opt -> opt{ optMode = Just ModeVersion})) "show version"- , Option [] ["algorithm"]- (ReqArg (\val opt -> opt{ optAlgorithm = val }) "<str>")- "Algorithm: naive, cegar, cegar-incremental (default)"- ]+ 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-- args <- getArgs- case getOpt Permute options args of- (_,_,errs@(_:_)) -> do- mapM_ putStrLn errs- exitFailure-- (o,args2,[]) -> do- let opt = foldl (flip id) def o- case optMode opt of- Just ModeHelp -> showHelp stdout- Just ModeVersion -> hPutStrLn stdout (showVersion version)- Nothing -> do- case args2 of- [fname] -> do- ret <- QDimacs.parseFile fname- case ret of- Left err -> hPutStrLn stderr err >> exitFailure- Right qdimacs -> do- let nv = QDimacs.numVars qdimacs- nc = QDimacs.numClauses qdimacs- prefix' = QBF.quantifyFreeVariables nv [(q, IntSet.fromList xs) | (q,xs) <- QDimacs.prefix qdimacs]- matrix' = andB [orB [if lit > 0 then BoolExpr.Atom lit else notB (BoolExpr.Atom (abs lit)) | lit <- clause] | clause <- QDimacs.matrix 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'- _ -> 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 "-1") ++ " " ++ 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)- _ -> showHelp stderr >> exitFailure--showHelp :: Handle -> IO ()-showHelp h = hPutStrLn h (usageInfo header options)+ opt <- execParser parserInfo -header :: String-header = unlines- [ "Usage:"- , " toyqbf [OPTION]... [file.qdimacs]"- , " toyqbf [OPTION]... [file.cnf]"- , ""- , "Options:"- ]+ 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)
app/toysat/UBCSAT.hs view
@@ -31,14 +31,14 @@ import Text.Megaparsec.String #endif +import qualified ToySolver.FileFormat.CNF as CNF import qualified ToySolver.SAT.Types as SAT-import qualified ToySolver.Text.MaxSAT as MaxSAT data Options = Options { optCommand :: FilePath , optTempDir :: Maybe FilePath- , optProblem :: MaxSAT.WCNF+ , optProblem :: CNF.WCNF , optProblemFile :: Maybe FilePath , optVarInit :: [SAT.Lit] }@@ -48,11 +48,11 @@ { optCommand = "ubcsat" , optTempDir = Nothing , optProblem =- MaxSAT.WCNF- { MaxSAT.numVars = 0- , MaxSAT.numClauses = 0- , MaxSAT.topCost = 1- , MaxSAT.clauses = []+ CNF.WCNF+ { CNF.wcnfNumVars = 0+ , CNF.wcnfNumClauses = 0+ , CNF.wcnfTopCost = 1+ , CNF.wcnfClauses = [] } , optProblemFile = Nothing , optVarInit = []@@ -64,7 +64,7 @@ case ret of Nothing -> return Nothing Just (obj,_) ->- if obj < MaxSAT.topCost (optProblem opt) then+ if obj < CNF.wcnfTopCost (optProblem opt) then return ret else return Nothing@@ -97,10 +97,8 @@ Just fname -> f fname Nothing -> do withTempFile dir ".wcnf" $ \fname h -> do- hSetBinaryMode h True- hSetBuffering h (BlockBuffering Nothing)- MaxSAT.hPutWCNF h (optProblem opt) hClose h+ CNF.writeFile fname (optProblem opt) f fname ubcsat' :: Options -> FilePath -> Maybe FilePath -> IO [(Integer, SAT.Model)]@@ -111,7 +109,7 @@ [ "-alg", "irots" , "-seed", "0" , "-runs", "10"- , "-cutoff", show (MaxSAT.numVars wcnf * 50)+ , "-cutoff", show (CNF.wcnfNumVars wcnf * 50) , "-timeout", show (10 :: Int) , "-gtimeout", show (30 :: Int) , "-solve"@@ -132,7 +130,7 @@ return [] Right s -> do forM_ (lines s) $ \l -> putStr "c " >> putStrLn l- return $ scanSolutions (MaxSAT.numVars wcnf) s+ return $ scanSolutions (CNF.wcnfNumVars wcnf) s scanSolutions :: Int -> String -> [(Integer, SAT.Model)] scanSolutions nv s = rights $ map (parse (solution nv) "") $ lines s
app/toysat/toysat.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE ScopedTypeVariables, CPP #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-} ----------------------------------------------------------------------------- -- |@@ -16,7 +18,6 @@ module Main where -import Control.Applicative ((<$>)) import Control.Concurrent (getNumCapabilities) import Control.Concurrent.Timeout import Control.Monad@@ -36,42 +37,35 @@ import Data.IORef import Data.List import Data.Maybe+import Data.Monoid import Data.Ord-import Data.Word import qualified Data.Vector.Unboxed as V import Data.Version import Data.Scientific as Scientific import Data.Time+import Options.Applicative hiding (info)+import qualified Options.Applicative import System.IO-import System.Environment import System.Exit #if !MIN_VERSION_time(1,5,0) import System.Locale (defaultTimeLocale) #endif import System.Clock-import System.Console.GetOpt import System.FilePath import qualified System.Info as SysInfo import qualified System.Random.MWC as Rand import Text.Printf #ifdef __GLASGOW_HASKELL__ import GHC.Environment (getFullArgs)-#endif-#if defined(__GLASGOW_HASKELL__) import qualified GHC.Stats as Stats #endif import qualified Data.PseudoBoolean as PBFile-import qualified Data.PseudoBoolean.Attoparsec as PBFileAttoparsec import qualified ToySolver.Data.MIP as MIP import qualified ToySolver.Data.MIP.Solution.Gurobi as GurobiSol-import qualified ToySolver.Converter.GCNF2MaxSAT as GCNF2MaxSAT-import qualified ToySolver.Converter.MaxSAT2WBO as MaxSAT2WBO-import qualified ToySolver.Converter.MIP2PB as MIP2PB-import qualified ToySolver.Converter.PB2SAT as PB2SAT-import qualified ToySolver.Converter.PB2WBO as PB2WBO-import qualified ToySolver.Converter.WBO2MaxSAT as WBO2MaxSAT-import qualified ToySolver.Converter.WBO2PB as WBO2PB+import ToySolver.Converter+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.FileFormat as FF import qualified ToySolver.SAT as SAT import qualified ToySolver.SAT.Types as SAT import qualified ToySolver.SAT.PBO as PBO@@ -82,9 +76,6 @@ import qualified ToySolver.SAT.MUS as MUS import qualified ToySolver.SAT.MUS.Enum as MUSEnum import ToySolver.SAT.Printer-import qualified ToySolver.Text.CNF as CNF-import qualified ToySolver.Text.MaxSAT as MaxSAT-import qualified ToySolver.Text.GCNF as GCNF import ToySolver.Version import ToySolver.Internal.Util (showRational, setEncodingChar8) @@ -92,11 +83,13 @@ -- ------------------------------------------------------------------------ -data Mode = ModeHelp | ModeVersion | ModeSAT | ModeMUS | ModePB | ModeWBO | ModeMaxSAT | ModeMIP+data Mode = ModeSAT | ModeMUS | ModeAllMUS | ModePB | ModeWBO | ModeMaxSAT | ModeMIP+ deriving (Eq, Ord, Enum, Bounded) data Options = Options- { optMode :: Maybe Mode+ { optInput :: String+ , optMode :: Maybe Mode , optSATConfig :: SAT.Config , optRandomSeed :: Maybe Rand.Seed , optLinearizerPB :: Bool@@ -104,7 +97,6 @@ , optObjFunVarsHeuristics :: Bool , optLocalSearchInitial :: Bool , optMUSMethod :: MUS.Method- , optAllMUSes :: Bool , optAllMUSMethod :: MUSEnum.Method , optPrintRational :: Bool , optTimeout :: Integer@@ -118,7 +110,8 @@ instance Default Options where def = Options- { optMode = Nothing+ { optInput = "" -- XXX+ , optMode = Nothing , optSATConfig = def , optRandomSeed = Nothing , optLinearizerPB = False@@ -126,7 +119,6 @@ , optObjFunVarsHeuristics = PBO.defaultEnableObjFunVarsHeuristics , optLocalSearchInitial = False , optMUSMethod = MUS.optMethod def- , optAllMUSes = False , optAllMUSMethod = MUSEnum.optMethod def , optPrintRational = False , optTimeout = 0@@ -137,176 +129,296 @@ , optFileEncoding = Nothing } -options :: [OptDescr (Options -> Options)]-options =- [ Option ['h'] ["help"] (NoArg (\opt -> opt{ optMode = Just ModeHelp })) "show help"- , Option [] ["version"] (NoArg (\opt -> opt{ optMode = Just ModeVersion})) "show version"+optionsParser :: Parser Options+optionsParser = Options+ <$> fileInput+ <*> modeOption+ <*> satConfigParser+ <*> randomSeedOption+ <*> linearizerPBOption+ <*> optMethodOption+ <*> objFunVarsHeuristicsOption+ <*> localSearchInitialOption+ <*> musMethodOption+ <*> allMUSMethodOption+ <*> printRationalOption+ <*> timeoutOption+ <*> writeFileOption+ <*> ubcsatOption+ <*> initSPOption+ <*> tempDirOption+ <*> fileEncodingOption+ where+ fileInput :: Parser String+ fileInput = strArgument $ metavar "(FILE|-)" - , Option [] ["sat"] (NoArg (\opt -> opt{ optMode = Just ModeSAT })) "solve boolean satisfiability problem in .cnf file (default)"- , Option [] ["mus"] (NoArg (\opt -> opt{ optMode = Just ModeMUS })) "solve minimally unsatisfiable subset problem in .gcnf or .cnf file"- , Option [] ["pb"] (NoArg (\opt -> opt{ optMode = Just ModePB })) "solve pseudo boolean problem in .opb file"- , Option [] ["wbo"] (NoArg (\opt -> opt{ optMode = Just ModeWBO })) "solve weighted boolean optimization problem in .wbo file"- , Option [] ["maxsat"] (NoArg (\opt -> opt{ optMode = Just ModeMaxSAT })) "solve MaxSAT problem in .cnf or .wcnf file"- , Option [] ["lp"] (NoArg (\opt -> opt{ optMode = Just ModeMIP })) "solve bounded integer programming problem in .lp or .mps file"+ modeOption :: Parser (Maybe Mode)+ -- modeOption = liftA msum $ T.sequenceA $ map optional $+ modeOption = optional $ foldr (<|>) empty+ [ flag' ModeSAT $ long "sat" <> help "solve boolean satisfiability problem in .cnf file"+ , flag' ModeMUS $ long "mus" <> help "solve minimally unsatisfiable subset problem in .gcnf or .cnf file"+ , flag' ModeAllMUS $ long "all-mus" <> help "enumerate minimally unsatisfiable subset of .gcnf or .cnf file"+ , flag' ModePB $ long "pb" <> help "solve pseudo boolean problem in .opb file"+ , flag' ModeWBO $ long "wbo" <> help "solve weighted boolean optimization problem in .wbo file"+ , flag' ModeMaxSAT $ long "maxsat" <> help "solve MaxSAT problem in .cnf or .wcnf file"+ , flag' ModeMIP $ long "lp" <> help "solve LP/MIP problem in .lp or .mps file"+ ] - , Option [] ["restart"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configRestartStrategy = parseRestartStrategy val } }) "<str>")- ("Restart startegy: " ++ intercalate ", "- [ SAT.showRestartStrategy s ++ (if SAT.configRestartStrategy (optSATConfig def) == s then " (default)" else "")- | s <- [minBound .. maxBound] ])- , Option [] ["restart-first"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configRestartFirst = read val } }) "<int>")- (printf "The initial restart limit. (default %d)" (SAT.configRestartFirst def))- , Option [] ["restart-inc"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configRestartInc = read val } }) "<real>")- (printf "The factor with which the restart limit is multiplied in each restart. (default %f)" (SAT.configRestartInc def))- , Option [] ["learning"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configLearningStrategy = parseLearningStrategy val } }) "<str>")- ("Leaning scheme: " ++ intercalate ", "- [ SAT.showLearningStrategy s ++ (if SAT.configLearningStrategy (optSATConfig def) == s then " (default)" else "")- | s <- [minBound .. maxBound] ])- , Option [] ["learnt-size-first"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configLearntSizeFirst = read val } }) "<int>")- "The initial limit for learnt clauses."- , Option [] ["learnt-size-inc"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configLearntSizeInc = read val } }) "<real>")- (printf "The limit for learnt clauses is multiplied with this factor periodically. (default %f)" (SAT.configLearntSizeInc def))- , Option [] ["branch"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configBranchingStrategy = parseBranchingStrategy val } }) "<str>")- ("Branching startegy: " ++ intercalate ", "- [ SAT.showBranchingStrategy s ++ (if SAT.configBranchingStrategy (optSATConfig def) == s then " (default)" else "")- | s <- [minBound .. maxBound] ])- , Option [] ["erwa-alpha-first"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configERWAStepSizeFirst = read val } }) "<real>")- (printf "step-size alpha in ERWA and LRB branching heuristic is initialized with this value. (default %f)" (SAT.configERWAStepSizeFirst def))- , Option [] ["erwa-alpha-dec"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configERWAStepSizeDec = read val } }) "<real>")- (printf "step-size alpha in ERWA and LRB branching heuristic is decreased by this value after each conflict. (default %f)" (SAT.configERWAStepSizeDec def))- , Option [] ["erwa-alpha-min"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configERWAStepSizeMin = read val } }) "<real>")- (printf "step-size alpha in ERWA and LRB branching heuristic is decreased until it reach the value. (default %f)" (SAT.configERWAStepSizeMin def))- , Option [] ["ema-decay"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEMADecay = read val } }) "<real>")- (printf "inverse of the variable EMA decay factor used by LRB branching heuristic. (default %f)" (SAT.configEMADecay def))- , Option [] ["ccmin"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configCCMin = read val } }) "<int>")- (printf "Conflict clause minimization (0=none, 1=local, 2=recursive; default %d)" (SAT.configCCMin def))- , Option [] ["enable-phase-saving"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEnablePhaseSaving = True } }))- ("Enable phase saving" ++ (if SAT.configEnablePhaseSaving def then " (default)" else ""))- , Option [] ["disable-phase-saving"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEnablePhaseSaving = False } }))- ("Disable phase saving" ++ (if SAT.configEnablePhaseSaving def then "" else " (default)"))- , Option [] ["enable-forward-subsumption-removal"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEnableForwardSubsumptionRemoval = True } }))- ("Enable forward subumption removal (clauses only)" ++ (if SAT.configEnableForwardSubsumptionRemoval def then " (default)" else ""))- , Option [] ["disable-forward-subsumption-removal"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEnableForwardSubsumptionRemoval = False } }))- ("Disable forward subsumption removal (clauses only)" ++ (if SAT.configEnableForwardSubsumptionRemoval def then "" else " (default)"))- , Option [] ["enable-backward-subsumption-removal"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEnableBackwardSubsumptionRemoval = True } }))- ("Enable backward subsumption removal." ++ (if SAT.configEnableBackwardSubsumptionRemoval def then " (default)" else ""))- , Option [] ["disable-backward-subsumption-removal"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEnableBackwardSubsumptionRemoval = False } }))- ("Disable backward subsumption removal." ++ (if SAT.configEnableBackwardSubsumptionRemoval def then "" else " (default)"))+ randomSeedOption = optional $ fmap (Rand.toSeed . V.fromList . map read . words) $ strOption+ $ long "random-seed"+ <> long "random-gen"+ <> metavar "\"INT ..\""+ <> help "random seed used by the random variable selection" - , Option [] ["random-freq"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configRandomFreq = read val } }) "<0..1>")- (printf "The frequency with which the decision heuristic tries to choose a random variable (default %f)" (SAT.configRandomFreq def))- , Option [] ["random-seed"]- (ReqArg (\val opt -> opt{ optRandomSeed = Just (Rand.toSeed (V.singleton (read val) :: V.Vector Word32)) }) "<int>")- "random seed used by the random variable selection"- , Option [] ["random-gen"]- (ReqArg (\val opt -> opt{ optRandomSeed = Just (Rand.toSeed (V.fromList (map read $ words $ val) :: V.Vector Word32)) }) "<str>")- "another way of specifying random seed used by the random variable selection"+ linearizerPBOption = switch+ $ long "linearizer-pb"+ <> help "Use PB constraint in linearization." - , Option [] ["init-sp"]- (NoArg (\opt -> opt{ optInitSP = True }))- "Use survey propation to compute initial polarity (when possible)"+ optMethodOption = option (maybeReader PBO.parseMethod)+ $ long "opt-method"+ <> metavar "STR"+ <> help ("Optimization method: " ++ intercalate ", " [PBO.showMethod m | m <- [minBound..maxBound]])+ <> value (optOptMethod def)+ <> showDefaultWith PBO.showMethod - , Option [] ["linearizer-pb"]- (NoArg (\opt -> opt{ optLinearizerPB = True }))- "Use PB constraint in linearization."+ objFunVarsHeuristicsOption =+ flag' True+ ( long "objfun-heuristics"+ <> help ("Enable heuristics for polarity/activity of variables in objective function" +++ (if PBO.defaultEnableObjFunVarsHeuristics then " (default)" else "")))+ <|> flag' False+ ( long "no-objfun-heuristics"+ <> help ("Disable heuristics for polarity/activity of variables in objective function" +++ (if PBO.defaultEnableObjFunVarsHeuristics then "" else " (default)")))+ <|> pure PBO.defaultEnableObjFunVarsHeuristics - , Option [] ["pb-handler"]- (ReqArg (\val opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configPBHandlerType = parsePBHandler val } }) "<str>")- ("PB constraint handler: " ++ intercalate ", "- [ SAT.showPBHandlerType h ++ (if SAT.configPBHandlerType (optSATConfig def) == h then " (default)" else "")- | h <- [minBound .. maxBound] ])- , Option [] ["pb-split-clause-part"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEnablePBSplitClausePart = True } }))- ("Split clause part of PB constraints." ++ (if SAT.configEnablePBSplitClausePart def then " (default)" else ""))- , Option [] ["no-pb-split-clause-part"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configEnablePBSplitClausePart = False } }))- ("Do not split clause part of PB constraints." ++ (if SAT.configEnablePBSplitClausePart def then "" else " (default)"))+ localSearchInitialOption = switch+ $ long "ls-initial"+ <> help "Use local search (currently UBCSAT) for finding initial solution" - , Option [] ["opt-method"]- (ReqArg (\val opt -> opt{ optOptMethod = parseOptMethod val }) "<str>")- ("Optimization method: " ++ intercalate ", "- [PBO.showMethod m ++ (if optOptMethod def == m then " (default)" else "") | m <- [minBound .. maxBound]])- , Option [] ["objfun-heuristics"]- (NoArg (\opt -> opt{ optObjFunVarsHeuristics = True }))- "Enable heuristics for polarity/activity of variables in objective function (default)"- , Option [] ["no-objfun-heuristics"]- (NoArg (\opt -> opt{ optObjFunVarsHeuristics = False }))- "Disable heuristics for polarity/activity of variables in objective function"- , Option [] ["ls-initial"]- (NoArg (\opt -> opt{ optLocalSearchInitial = True }))- "Use local search (currently UBCSAT) for finding initial solution"+ musMethodOption = option (maybeReader MUS.parseMethod)+ $ long "mus-method"+ <> metavar "STR"+ <> help ("MUS computation method: " ++ intercalate ", " [MUS.showMethod m | m <- [minBound..maxBound]])+ <> value (optMUSMethod def)+ <> showDefaultWith MUS.showMethod - , Option [] ["all-mus"]- (NoArg (\opt -> opt{ optMode = Just ModeMUS, optAllMUSes = True }))- "enumerate all MUSes"- , Option [] ["mus-method"]- (ReqArg (\val opt -> opt{ optMUSMethod = parseMUSMethod val }) "<str>")- ("MUS computation method: " ++ intercalate ", "- [MUS.showMethod m ++ (if optMUSMethod def == m then " (default)" else "") | m <- [minBound .. maxBound]])- , Option [] ["all-mus-method"]- (ReqArg (\val opt -> opt{ optAllMUSMethod = parseAllMUSMethod val }) "<str>")- ("MUS enumeration method: " ++ intercalate ", "- [MUSEnum.showMethod m ++ (if optAllMUSMethod def == m then " (default)" else "") | m <- [minBound .. maxBound]])+ allMUSMethodOption = option (maybeReader MUSEnum.parseMethod)+ $ long "all-mus-method"+ <> metavar "STR"+ <> help (("MUS enumeration method: " ++ intercalate ", " [MUSEnum.showMethod m | m <- [minBound..maxBound]]))+ <> value (optAllMUSMethod def)+ <> showDefaultWith MUSEnum.showMethod - , Option [] ["print-rational"]- (NoArg (\opt -> opt{ optPrintRational = True }))- "print rational numbers instead of decimals"- , Option ['w'] []- (ReqArg (\val opt -> opt{ optWriteFile = Just val }) "<filename>")- "write model to filename in Gurobi .sol format"+ printRationalOption = switch+ $ long "print-rational"+ <> help "print rational numbers instead of decimals" - , Option [] ["check-model"]- (NoArg (\opt -> opt{ optSATConfig = (optSATConfig opt){ SAT.configCheckModel = True } }))- "check model for debug"+ timeoutOption = option auto+ $ long "timeout"+ <> metavar "INT"+ <> help "Kill toysat after given number of seconds (0 means no limit)"+ <> value (optTimeout def)+ <> showDefault - , Option [] ["timeout"]- (ReqArg (\val opt -> opt{ optTimeout = read val }) "<int>")- "Kill toysat after given number of seconds (default 0 (no limit))"+ writeFileOption = optional $ strOption+ $ short 'w'+ <> metavar "FILE"+ <> help "write model to filename in Gurobi .sol format" - , Option [] ["with-ubcsat"]- (ReqArg (\val opt -> opt{ optUBCSAT = val }) "<PATH>")- "give the path to the UBCSAT command"- , Option [] ["temp-dir"]- (ReqArg (\val opt -> opt{ optTempDir = Just val }) "<PATH>")- "temporary directory"+ ubcsatOption = strOption+ $ long "with-ubcsat"+ <> metavar "PATH"+ <> help "give the path to the UBCSAT command"+ <> value (optUBCSAT def) - , Option [] ["encoding"]- (ReqArg (\val opt -> opt{ optFileEncoding = Just val }) "<ENCODING>")- "file encoding for LP/MPS files"- ]+ initSPOption = switch+ $ long "init-sp"+ <> help "Use survey propation to compute initial polarity (when possible)"++ tempDirOption = optional $ strOption+ $ long "temp-dir"+ <> metavar "PATH"+ <> help "temporary directory"++ fileEncodingOption = optional $ strOption+ $ long "encoding"+ <> metavar "ENCODING"+ <> help "file encoding for LP/MPS files"+++satConfigParser :: Parser SAT.Config+satConfigParser = SAT.Config+ <$> restartOption+ <*> restartFirstOption+ <*> restartIncOption+ <*> learningOption+ <*> learntSizeFirstOption+ <*> learntSizeIncOption+ <*> ccMinOption+ <*> branchOption+ <*> eRWAStepSizeFirstOption+ <*> eRWAStepSizeDecOption+ <*> eRWAStepSizeMinOption+ <*> eMADecayOption+ <*> enablePhaseSavingOption+ <*> enableForwardSubsumptionRemovalOption+ <*> enableBackwardSubsumptionRemovalOption+ <*> randomFreqOption+ <*> pbHandlerTypeOption+ <*> enablePBSplitClausePartOption+ <*> checkModelOption+ <*> pure (SAT.configVarDecay def)+ <*> pure (SAT.configConstrDecay def) where- parseOptMethod s = fromMaybe (error (printf "unknown optimization method \"%s\"" s)) (PBO.parseMethod s)+ restartOption = option (maybeReader SAT.parseRestartStrategy)+ $ long "restart"+ <> metavar "STR"+ <> help ("Restart startegy: " ++ intercalate ", " [SAT.showRestartStrategy s | s <- [minBound..maxBound]])+ <> value (SAT.configRestartStrategy def)+ <> showDefaultWith SAT.showRestartStrategy+ restartFirstOption = option auto+ $ long "restart-first"+ <> metavar "INT"+ <> help "The initial restart limit."+ <> value (SAT.configRestartFirst def)+ <> showDefault+ restartIncOption = option auto+ $ long "restart-inc"+ <> metavar "REAL"+ <> help "The factor with which the restart limit is multiplied in each restart."+ <> value (SAT.configRestartInc def)+ <> showDefault - parseMUSMethod s = fromMaybe (error (printf "unknown MUS finding method \"%s\"" s)) (MUS.parseMethod s)+ learningOption = option (maybeReader SAT.parseLearningStrategy)+ $ long "learning"+ <> metavar "STR"+ <> help ("Leaning scheme: " ++ intercalate ", " [SAT.showLearningStrategy s | s <- [minBound..maxBound]])+ <> value (SAT.configLearningStrategy def)+ <> showDefaultWith SAT.showLearningStrategy+ learntSizeFirstOption = option auto+ $ long "learnt-size-first"+ <> metavar "INT"+ <> help "The initial limit for learnt clauses."+ <> value (SAT.configLearntSizeFirst def)+ <> showDefault+ learntSizeIncOption = option auto+ $ long "learnt-size-inc"+ <> metavar "REAL"+ <> help "The limit for learnt clauses is multiplied with this factor periodically."+ <> value (SAT.configLearntSizeInc def)+ <> showDefault - parseAllMUSMethod s = fromMaybe (error (printf "unknown MUS enumeration method \"%s\"" s)) (MUSEnum.parseMethod s)+ ccMinOption = option auto+ $ long "ccmin"+ <> metavar "INT"+ <> help "Conflict clause minimization: 0=none, 1=local, 2=recursive"+ <> value (SAT.configCCMin def)+ <> showDefault+ branchOption = option (maybeReader SAT.parseBranchingStrategy)+ $ long "branch"+ <> metavar "STR"+ <> help ("Branching startegy: " ++ intercalate ", " [SAT.showBranchingStrategy s | s <- [minBound..maxBound]])+ <> value (SAT.configBranchingStrategy def)+ <> showDefaultWith SAT.showBranchingStrategy - parseRestartStrategy s = fromMaybe (error (printf "unknown restart strategy \"%s\"" s)) (SAT.parseRestartStrategy s)+ eRWAStepSizeFirstOption = option auto+ $ long "erwa-alpha-first"+ <> metavar "REAL"+ <> help "step-size alpha in ERWA and LRB branching heuristic is initialized with this value."+ <> value (SAT.configERWAStepSizeFirst def)+ <> showDefault+ eRWAStepSizeDecOption = option auto+ $ long "erwa-alpha-dec"+ <> metavar "REAL"+ <> help "step-size alpha in ERWA and LRB branching heuristic is decreased by this value after each conflict."+ <> value (SAT.configERWAStepSizeDec def)+ <> showDefault+ eRWAStepSizeMinOption = option auto+ $ long "erwa-alpha-min"+ <> metavar "REAL"+ <> help "step-size alpha in ERWA and LRB branching heuristic is decreased until it reach the value."+ <> value (SAT.configERWAStepSizeMin def)+ <> showDefault - parseLearningStrategy s = fromMaybe (error (printf "unknown learning strategy \"%s\"" s)) (SAT.parseLearningStrategy s)+ eMADecayOption = option auto+ $ long "ema-decay"+ <> metavar "REAL"+ <> help "inverse of the variable EMA decay factor used by LRB branching heuristic."+ <> value (SAT.configEMADecay def)+ <> showDefault - parseBranchingStrategy s = fromMaybe (error (printf "unknown branching strategy \"%s\"" s)) (SAT.parseBranchingStrategy s)+ enablePhaseSavingOption =+ flag' True (long "enable-phase-saving" <> help ("Enable phase saving" ++ (if SAT.configEnablePhaseSaving def then " (default)" else "")))+ <|> flag' False (long "disable-phase-saving" <> help ("Disable phase saving" ++ (if SAT.configEnablePhaseSaving def then "" else " (default)")))+ <|> pure (SAT.configEnablePhaseSaving def) - parsePBHandler s = fromMaybe (error (printf "unknown PB constraint handler \"%s\"" s)) (SAT.parsePBHandlerType s)+ enableForwardSubsumptionRemovalOption =+ flag' True+ ( long "enable-forward-subsumption-removal"+ <> help ("Enable forward subumption removal (clauses only)" ++ (if SAT.configEnableForwardSubsumptionRemoval def then " (default)" else "")))+ <|> flag' False+ ( long "disable-forward-subsumption-removal"+ <> help ("Disable forward subumption removal (clauses only)" ++ (if SAT.configEnableForwardSubsumptionRemoval def then "" else " (default)")))+ <|> pure (SAT.configEnableForwardSubsumptionRemoval def)+ enableBackwardSubsumptionRemovalOption =+ flag' True+ ( long "enable-backward-subsumption-removal"+ <> help ("Enable backward subumption removal (clauses only)" ++ (if SAT.configEnableBackwardSubsumptionRemoval def then " (default)" else "")))+ <|> flag' False+ ( long "disable-backward-subsumption-removal"+ <> help ("Disable backward subumption removal (clauses only)" ++ (if SAT.configEnableBackwardSubsumptionRemoval def then "" else " (default)")))+ <|> pure (SAT.configEnableBackwardSubsumptionRemoval def) + randomFreqOption = option auto+ $ long "random-freq"+ <> metavar "0..1"+ <> help "The frequency with which the decision heuristic tries to choose a random variable"+ <> value (SAT.configRandomFreq def)+ <> showDefault++ pbHandlerTypeOption = option (maybeReader SAT.parsePBHandlerType)+ $ long "pb-handler"+ <> metavar "STR"+ <> help ("PB constraint handler: " ++ intercalate ", " [SAT.showPBHandlerType h | h <- [minBound..maxBound]])+ <> value (SAT.configPBHandlerType def)+ <> showDefaultWith SAT.showPBHandlerType++ enablePBSplitClausePartOption =+ flag' True+ ( long "pb-split-clause-part"+ <> help ("Split clause part of PB constraints." ++ (if SAT.configEnablePBSplitClausePart def then " (default)" else "")))+ <|> flag' False+ ( long "no-pb-split-clause-part"+ <> help ("Do not split clause part of PB constraints." ++ (if SAT.configEnablePBSplitClausePart def then "" else " (default)")))+ <|> pure (SAT.configEnablePBSplitClausePart def)++ checkModelOption = switch+ $ long "check-model"+ <> help "check model for debugging"++parserInfo :: ParserInfo Options+parserInfo = Options.Applicative.info (helper <*> versionOption <*> optionsParser)+ $ fullDesc+ <> header "toysat - a solver for SAT-related problems"+ where+ versionOption :: Parser (a -> a)+ versionOption = infoOption (showVersion version)+ $ hidden+ <> long "version"+ <> help "Show version"++#if !MIN_VERSION_optparse_applicative(0,13,0)++-- | Convert a function producing a 'Maybe' into a reader.+maybeReader :: (String -> Maybe a) -> ReadM a+maybeReader f = eitherReader $ \arg ->+ case f arg of+ Nothing -> Left $ "cannot parse value `" ++ arg ++ "'"+ Just a -> Right a++#endif+ main :: IO () main = do #ifdef FORCE_CHAR8@@ -315,65 +427,59 @@ startCPU <- getTime ProcessCPUTime startWC <- getTime Monotonic- args <- getArgs- case getOpt Permute options args of- (_,_,errs@(_:_)) -> do- mapM_ putStrLn errs- exitFailure - (o,args2,[]) -> do- let opt = foldl (flip id) def o - mode =- case optMode opt of- Just m -> m- Nothing ->- case args2 of- [] -> ModeHelp- fname : _ ->- case map toLower (takeExtension fname) of- ".cnf" -> ModeSAT- ".gcnf" -> ModeMUS- ".opb" -> ModePB- ".wbo" -> ModeWBO- ".wcnf" -> ModeMaxSAT- ".lp" -> ModeMIP- ".mps" -> ModeMIP- _ -> ModeSAT+ opt <- execParser parserInfo+ let mode =+ case optMode opt of+ Just m -> m+ Nothing ->+ case getExt (optInput opt) of+ ".cnf" -> ModeSAT+ ".gcnf" -> ModeMUS+ ".opb" -> ModePB+ ".wbo" -> ModeWBO+ ".wcnf" -> ModeMaxSAT+ ".lp" -> ModeMIP+ ".mps" -> ModeMIP+ _ -> ModeSAT - case mode of- ModeHelp -> showHelp stdout- ModeVersion -> hPutStrLn stdout (showVersion version)- _ -> do- printSysInfo+ printSysInfo #ifdef __GLASGOW_HASKELL__- fullArgs <- getFullArgs+ fullArgs <- getFullArgs #else- let fullArgs = args+ let fullArgs = args #endif- putCommentLine $ printf "command line = %s" (show fullArgs)+ putCommentLine $ printf "command line = %s" (show fullArgs) - let timelim = optTimeout opt * 10^(6::Int)- - ret <- timeout (if timelim > 0 then timelim else (-1)) $ do- solver <- newSolver opt- case mode of- ModeHelp -> showHelp stdout- ModeVersion -> hPutStrLn stdout (showVersion version)- ModeSAT -> mainSAT opt solver args2- ModeMUS -> mainMUS opt solver args2- ModePB -> mainPB opt solver args2- ModeWBO -> mainWBO opt solver args2- ModeMaxSAT -> mainMaxSAT opt solver args2- ModeMIP -> mainMIP opt solver args2- - when (isNothing ret) $ do- putCommentLine "TIMEOUT"- endCPU <- getTime ProcessCPUTime- endWC <- getTime Monotonic- putCommentLine $ printf "total CPU time = %.3fs" (durationSecs startCPU endCPU)- putCommentLine $ printf "total wall clock time = %.3fs" (durationSecs startWC endWC)- printGCStat+ let timelim = optTimeout opt * 10^(6::Int)+ + ret <- timeout (if timelim > 0 then timelim else (-1)) $ do+ solver <- newSolver opt+ case mode of+ ModeSAT -> mainSAT opt solver+ ModeMUS -> mainMUS opt solver+ ModeAllMUS -> mainMUS opt solver+ ModePB -> mainPB opt solver+ ModeWBO -> mainWBO opt solver+ ModeMaxSAT -> mainMaxSAT opt solver+ ModeMIP -> mainMIP opt solver + when (isNothing ret) $ do+ putCommentLine "TIMEOUT"+ endCPU <- getTime ProcessCPUTime+ endWC <- getTime Monotonic+ putCommentLine $ printf "total CPU time = %.3fs" (durationSecs startCPU endCPU)+ putCommentLine $ printf "total wall clock time = %.3fs" (durationSecs startWC endWC)+ printGCStat++getExt :: String -> String+getExt name | (base, ext) <- splitExtension name =+ case map toLower ext of+#ifdef WITH_ZLIB+ ".gz" -> getExt base+#endif+ s -> s+ printGCStat :: IO () #if defined(__GLASGOW_HASKELL__) #if __GLASGOW_HASKELL__ >= 802@@ -444,22 +550,6 @@ printGCStat = return () #endif -showHelp :: Handle -> IO ()-showHelp h = hPutStrLn h (usageInfo header options)--header :: String-header = unlines- [ "Usage:"- , " toysat [OPTION]... [file.cnf|-]"- , " toysat [OPTION]... --mus [file.gcnf|-]"- , " toysat [OPTION]... --pb [file.opb|-]"- , " toysat [OPTION]... --wbo [file.wbo|-]"- , " toysat [OPTION]... --maxsat [file.cnf|file.wcnf|-]"- , " toysat [OPTION]... --lp [file.lp|file.mps|-]"- , ""- , "Options:"- ]- printSysInfo :: IO () printSysInfo = do tm <- getZonedTime@@ -506,47 +596,46 @@ -- ------------------------------------------------------------------------ -mainSAT :: Options -> SAT.Solver -> [String] -> IO ()-mainSAT opt solver args = do- ret <- case args of- ["-"] -> liftM CNF.parseByteString $ BS.hGetContents stdin- [fname] -> CNF.parseFile fname- _ -> showHelp stderr >> exitFailure+mainSAT :: Options -> SAT.Solver -> IO ()+mainSAT opt solver = do+ ret <- case optInput opt of+ "-" -> liftM FF.parse $ BS.hGetContents stdin+ fname -> FF.parseFile fname case ret of Left err -> hPrint stderr err >> exitFailure Right cnf -> do- let fname = case args of- [fname] | or [".cnf" `isSuffixOf` map toLower fname] -> Just fname- _ -> Nothing+ let fname = if ".cnf" `isSuffixOf` map toLower (optInput opt)+ then Just (optInput opt)+ else Nothing solveSAT opt solver cnf fname solveSAT :: Options -> SAT.Solver -> CNF.CNF -> Maybe FilePath -> IO () solveSAT opt solver cnf cnfFileName = do- putCommentLine $ printf "#vars %d" (CNF.numVars cnf)- putCommentLine $ printf "#constraints %d" (CNF.numClauses cnf)- SAT.newVars_ solver (CNF.numVars cnf)- forM_ (CNF.clauses cnf) $ \clause ->- SAT.addClause solver clause+ putCommentLine $ printf "#vars %d" (CNF.cnfNumVars cnf)+ putCommentLine $ printf "#constraints %d" (CNF.cnfNumClauses cnf)+ SAT.newVars_ solver (CNF.cnfNumVars cnf)+ forM_ (CNF.cnfClauses cnf) $ \clause ->+ SAT.addClause solver (SAT.unpackClause clause) spHighlyBiased <- if optInitSP opt then do- initPolarityUsingSP solver (CNF.numVars cnf)- (CNF.numVars cnf) [(1, clause) | clause <- CNF.clauses cnf]+ initPolarityUsingSP solver (CNF.cnfNumVars cnf)+ (CNF.cnfNumVars cnf) [(1, clause) | clause <- CNF.cnfClauses cnf] else return IntMap.empty when (optLocalSearchInitial opt) $ do fixed <- SAT.getFixedLiterals solver- let var_init1 = IntMap.fromList [(abs lit, lit > 0) | lit <- fixed, abs lit <= CNF.numVars cnf]+ let var_init1 = IntMap.fromList [(abs lit, lit > 0) | lit <- fixed, abs lit <= CNF.cnfNumVars cnf] var_init2 = IntMap.map (>0) spHighlyBiased -- note that IntMap.union is left-biased. var_init = [if b then v else -v | (v, b) <- IntMap.toList (var_init1 `IntMap.union` var_init2)] let wcnf =- MaxSAT.WCNF- { MaxSAT.numVars = CNF.numVars cnf- , MaxSAT.numClauses = CNF.numClauses cnf- , MaxSAT.topCost = 1- , MaxSAT.clauses = [(1, clause) | clause <- CNF.clauses cnf]+ CNF.WCNF+ { CNF.wcnfNumVars = CNF.cnfNumVars cnf+ , CNF.wcnfNumClauses = CNF.cnfNumClauses cnf+ , CNF.wcnfTopCost = 1+ , CNF.wcnfClauses = [(1, clause) | clause <- CNF.cnfClauses cnf] } let opt2 = def@@ -567,15 +656,15 @@ putSLine $ if result then "SATISFIABLE" else "UNSATISFIABLE" when result $ do m <- SAT.getModel solver- satPrintModel stdout m (CNF.numVars cnf)- writeSOLFile opt m Nothing (CNF.numVars cnf)+ satPrintModel stdout m (CNF.cnfNumVars cnf)+ writeSOLFile opt m Nothing (CNF.cnfNumVars cnf) -initPolarityUsingSP :: SAT.Solver -> Int -> Int -> [(Double, SAT.Clause)] -> IO (IntMap Double)+initPolarityUsingSP :: SAT.Solver -> Int -> Int -> [(Double, SAT.PackedClause)] -> IO (IntMap Double) initPolarityUsingSP solver nvOrig nv clauses = do n <- getNumCapabilities putCommentLine $ "Running survey propgation using " ++ show n ++" threads ..." startWC <- getTime Monotonic- sp <- SP.newSolver nv clauses + sp <- SP.newSolver nv clauses SP.initializeRandom sp =<< SAT.getRandomGen solver SP.setNThreads sp n lits <- SAT.getFixedLiterals solver@@ -602,72 +691,71 @@ -- ------------------------------------------------------------------------ -mainMUS :: Options -> SAT.Solver -> [String] -> IO ()-mainMUS opt solver args = do- gcnf <- case args of- ["-"] -> do+mainMUS :: Options -> SAT.Solver -> IO ()+mainMUS opt solver = do+ gcnf <- case optInput opt of+ "-" -> do s <- BS.hGetContents stdin- case GCNF.parseByteString s of+ case FF.parse s of Left err -> hPutStrLn stderr err >> exitFailure Right gcnf -> return gcnf- [fname] -> do- ret <- GCNF.parseFile fname+ fname -> do+ ret <- FF.parseFile fname case ret of Left err -> hPutStrLn stderr err >> exitFailure Right gcnf -> return gcnf- _ -> showHelp stderr >> exitFailure solveMUS opt solver gcnf -solveMUS :: Options -> SAT.Solver -> GCNF.GCNF -> IO ()+solveMUS :: Options -> SAT.Solver -> CNF.GCNF -> IO () solveMUS opt solver gcnf = do- putCommentLine $ printf "#vars %d" (GCNF.numVars gcnf)- putCommentLine $ printf "#constraints %d" (GCNF.numClauses gcnf)- putCommentLine $ printf "#groups %d" (GCNF.lastGroupIndex gcnf)+ putCommentLine $ printf "#vars %d" (CNF.gcnfNumVars gcnf)+ putCommentLine $ printf "#constraints %d" (CNF.gcnfNumClauses gcnf)+ putCommentLine $ printf "#groups %d" (CNF.gcnfLastGroupIndex gcnf) - SAT.resizeVarCapacity solver (GCNF.numVars gcnf + GCNF.lastGroupIndex gcnf)- SAT.newVars_ solver (GCNF.numVars gcnf)+ SAT.resizeVarCapacity solver (CNF.gcnfNumVars gcnf + CNF.gcnfLastGroupIndex gcnf)+ SAT.newVars_ solver (CNF.gcnfNumVars gcnf) - tbl <- forM [1 .. GCNF.lastGroupIndex gcnf] $ \i -> do+ tbl <- forM [1 .. CNF.gcnfLastGroupIndex gcnf] $ \i -> do sel <- SAT.newVar solver return (i, sel) let idx2sel :: Array Int SAT.Var- idx2sel = array (1, GCNF.lastGroupIndex gcnf) tbl+ idx2sel = array (1, CNF.gcnfLastGroupIndex gcnf) tbl selrng = if null tbl then (0,-1) else (snd $ head tbl, snd $ last tbl) sel2idx :: Array SAT.Lit Int sel2idx = array selrng [(sel, idx) | (idx, sel) <- tbl] - (idx2clausesM :: IOArray Int [SAT.Clause]) <- newArray (1, GCNF.lastGroupIndex gcnf) []- forM_ (GCNF.clauses gcnf) $ \(idx, clause) ->+ (idx2clausesM :: IOArray Int [SAT.PackedClause]) <- newArray (1, CNF.gcnfLastGroupIndex gcnf) []+ forM_ (CNF.gcnfClauses gcnf) $ \(idx, clause) -> if idx==0- then SAT.addClause solver clause+ then SAT.addClause solver (SAT.unpackClause clause) else do- SAT.addClause solver (- (idx2sel ! idx) : clause)+ SAT.addClause solver (- (idx2sel ! idx) : SAT.unpackClause clause) cs <- readArray idx2clausesM idx writeArray idx2clausesM idx (clause : cs)- (idx2clauses :: Array Int [SAT.Clause]) <- freeze idx2clausesM+ (idx2clauses :: Array Int [SAT.PackedClause]) <- freeze idx2clausesM when (optInitSP opt) $ do- let wcnf = GCNF2MaxSAT.convert gcnf- initPolarityUsingSP solver (GCNF.numVars gcnf)- (MaxSAT.numVars wcnf) [(fromIntegral w, clause) | (w, clause) <- MaxSAT.clauses wcnf]+ let (wcnf, _) = gcnf2maxsat gcnf+ initPolarityUsingSP solver (CNF.gcnfNumVars gcnf)+ (CNF.wcnfNumVars wcnf) [(fromIntegral w, clause) | (w, clause) <- CNF.wcnfClauses wcnf] return () - result <- SAT.solveWith solver (map (idx2sel !) [1..GCNF.lastGroupIndex gcnf])+ result <- SAT.solveWith solver (map (idx2sel !) [1..CNF.gcnfLastGroupIndex gcnf]) putSLine $ if result then "SATISFIABLE" else "UNSATISFIABLE" if result then do m <- SAT.getModel solver- satPrintModel stdout m (GCNF.numVars gcnf)- writeSOLFile opt m Nothing (GCNF.numVars gcnf)+ satPrintModel stdout m (CNF.gcnfNumVars gcnf)+ writeSOLFile opt m Nothing (CNF.gcnfNumVars gcnf) else do- if not (optAllMUSes opt)+ if optMode opt /= Just ModeAllMUS then do let opt2 = def { MUS.optMethod = optMUSMethod opt , MUS.optLogger = putCommentLine , MUS.optShowLit = \lit -> show (sel2idx ! lit) , MUS.optEvalConstr = \m sel ->- and [SAT.evalClause m c | c <- idx2clauses ! (sel2idx ! sel)]+ and [SAT.evalClause m (SAT.unpackClause c) | c <- idx2clauses ! (sel2idx ! sel)] } mus <- MUS.findMUSAssumptions solver opt2 let mus2 = sort $ map (sel2idx !) $ IntSet.toList mus@@ -680,7 +768,7 @@ , MUSEnum.optLogger = putCommentLine , MUSEnum.optShowLit = \lit -> show (sel2idx ! lit) , MUSEnum.optEvalConstr = \m sel ->- and [SAT.evalClause m c | c <- idx2clauses ! (sel2idx ! sel)]+ and [SAT.evalClause m (SAT.unpackClause c) | c <- idx2clauses ! (sel2idx ! sel)] , MUSEnum.optOnMCSFound = \mcs -> do i <- readIORef mcsCounter modifyIORef' mcsCounter (+1)@@ -698,12 +786,11 @@ -- ------------------------------------------------------------------------ -mainPB :: Options -> SAT.Solver -> [String] -> IO ()-mainPB opt solver args = do- ret <- case args of- ["-"] -> liftM PBFileAttoparsec.parseOPBByteString $ BS.hGetContents stdin- [fname] -> PBFileAttoparsec.parseOPBFile fname- _ -> showHelp stderr >> exitFailure+mainPB :: Options -> SAT.Solver -> IO ()+mainPB opt solver = do+ ret <- case optInput opt of+ "-" -> liftM FF.parse $ BS.hGetContents stdin+ fname -> FF.parseFile fname case ret of Left err -> hPutStrLn stderr err >> exitFailure Right formula -> solvePB opt solver formula@@ -727,14 +814,15 @@ spHighlyBiased <- if optInitSP opt then do- let (cnf, _, _) = PB2SAT.convert formula- initPolarityUsingSP solver nv (CNF.numVars cnf) [(1.0, clause) | clause <- CNF.clauses cnf]+ let (cnf, _) = pb2sat formula+ initPolarityUsingSP solver nv (CNF.cnfNumVars cnf) [(1.0, clause) | clause <- CNF.cnfClauses cnf] else return IntMap.empty initialModel <- if optLocalSearchInitial opt then do- let (wcnf, _, mtrans) = WBO2MaxSAT.convert $ PB2WBO.convert formula+ let (wbo, info1) = pb2wbo formula+ (wcnf, info2) = wbo2maxsat wbo fixed <- filter (\lit -> abs lit <= nv) <$> SAT.getFixedLiterals solver let var_init1 = IntMap.fromList [(abs lit, lit > 0) | lit <- fixed, abs lit <= nv] var_init2 = IntMap.map (>0) spHighlyBiased@@ -751,10 +839,10 @@ case ret of Nothing -> return Nothing Just (obj,m) -> do- let m2 = mtrans m+ let m2 = transformBackward info1 $ transformBackward info2 m forM_ (assocs m2) $ \(v, val) -> do SAT.setVarPolarity solver v val- if obj < MaxSAT.topCost wcnf then+ if obj < CNF.wcnfTopCost wcnf then return $ Just m2 else return Nothing@@ -816,22 +904,23 @@ -- ------------------------------------------------------------------------ -mainWBO :: Options -> SAT.Solver -> [String] -> IO ()-mainWBO opt solver args = do- ret <- case args of- ["-"] -> liftM PBFileAttoparsec.parseWBOByteString $ BS.hGetContents stdin- [fname] -> PBFileAttoparsec.parseWBOFile fname- _ -> showHelp stderr >> exitFailure+mainWBO :: Options -> SAT.Solver -> IO ()+mainWBO opt solver = do+ ret <- case optInput opt of+ "-" -> liftM FF.parse $ BS.hGetContents stdin+ fname -> FF.parseFile fname case ret of Left err -> hPutStrLn stderr err >> exitFailure Right formula -> solveWBO opt solver False formula solveWBO :: Options -> SAT.Solver -> Bool -> PBFile.SoftFormula -> IO () solveWBO opt solver isMaxSat formula =- solveWBO' opt solver isMaxSat formula (WBO2MaxSAT.convert formula) Nothing+ solveWBO' opt solver isMaxSat formula (wbo2maxsat formula) Nothing -solveWBO' :: Options -> SAT.Solver -> Bool -> PBFile.SoftFormula -> (MaxSAT.WCNF, SAT.Model -> SAT.Model, SAT.Model -> SAT.Model) -> Maybe FilePath -> IO ()-solveWBO' opt solver isMaxSat formula (wcnf, _, mtrans) wcnfFileName = do+solveWBO'+ :: (BackwardTransformer wbo2maxsat_info, Source wbo2maxsat_info ~ SAT.Model, Target wbo2maxsat_info ~ SAT.Model)+ => Options -> SAT.Solver -> Bool -> PBFile.SoftFormula -> (CNF.WCNF, wbo2maxsat_info) -> Maybe FilePath -> IO ()+solveWBO' opt solver isMaxSat formula (wcnf, wbo2maxsat_info) wcnfFileName = do let nv = PBFile.wboNumVars formula nc = PBFile.wboNumConstraints formula putCommentLine $ printf "#vars %d" nv@@ -841,16 +930,16 @@ enc <- Tseitin.newEncoderWithPBLin solver Tseitin.setUsePB enc (optLinearizerPB opt) pbnlc <- PBNLC.newEncoder solver enc- (obj, defsPB) <- WBO2PB.addWBO pbnlc formula+ (obj, defsPB) <- addWBO pbnlc formula objLin <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityNeg obj spHighlyBiased <- if optInitSP opt then do- initPolarityUsingSP solver nv (MaxSAT.numVars wcnf) [(fromIntegral w, c) | (w, c) <- MaxSAT.clauses wcnf]+ initPolarityUsingSP solver nv (CNF.wcnfNumVars wcnf) [(fromIntegral w, c) | (w, c) <- CNF.wcnfClauses wcnf] else return IntMap.empty - initialModel <- liftM (fmap (mtrans . snd)) $+ initialModel <- liftM (fmap (transformBackward wbo2maxsat_info . snd)) $ if optLocalSearchInitial opt then do fixed <- SAT.getFixedLiterals solver let var_init1 = IntMap.fromList [(abs lit, lit > 0) | lit <- fixed, abs lit <= nv]@@ -921,31 +1010,32 @@ -- ------------------------------------------------------------------------ -mainMaxSAT :: Options -> SAT.Solver -> [String] -> IO ()-mainMaxSAT opt solver args = do- ret <- case args of- ["-"] -> liftM MaxSAT.parseByteString BS.getContents- [fname] -> MaxSAT.parseFile fname- _ -> showHelp stderr >> exitFailure+mainMaxSAT :: Options -> SAT.Solver -> IO ()+mainMaxSAT opt solver = do+ ret <- case optInput opt of+ "-" -> liftM FF.parse BS.getContents+ fname -> FF.parseFile fname case ret of Left err -> hPutStrLn stderr err >> exitFailure Right wcnf -> do- let fname = case args of- [fname] | or [s `isSuffixOf` map toLower fname | s <- [".cnf", ".wcnf"]] -> Just fname- _ -> Nothing+ let fname = if or [s `isSuffixOf` map toLower (optInput opt) | s <- [".cnf", ".wcnf"]]+ then Just (optInput opt)+ else Nothing solveMaxSAT opt solver wcnf fname -solveMaxSAT :: Options -> SAT.Solver -> MaxSAT.WCNF -> Maybe FilePath -> IO ()+solveMaxSAT :: Options -> SAT.Solver -> CNF.WCNF -> Maybe FilePath -> IO () solveMaxSAT opt solver wcnf wcnfFileName =- solveWBO' opt solver True (MaxSAT2WBO.convert wcnf) (wcnf, id, id) wcnfFileName+ solveWBO' opt solver True wbo (wcnf, ReversedTransformer info) wcnfFileName+ where+ (wbo, info) = maxsat2wbo wcnf -- ------------------------------------------------------------------------ -mainMIP :: Options -> SAT.Solver -> [String] -> IO ()-mainMIP opt solver args = do+mainMIP :: Options -> SAT.Solver -> IO ()+mainMIP opt solver = do mip <-- case args of- [fname@"-"] -> do+ case optInput opt of+ fname@"-" -> do F.mapM_ (\s -> hSetEncoding stdin =<< mkTextEncoding s) (optFileEncoding opt) s <- hGetContents stdin case MIP.parseLPString def fname s of@@ -957,10 +1047,9 @@ hPrint stderr err hPrint stderr err2 exitFailure- [fname] -> do+ fname -> do enc <- T.mapM mkTextEncoding (optFileEncoding opt) MIP.readFile def{ MIP.optFileEncoding = enc } fname- _ -> showHelp stderr >> exitFailure solveMIP opt solver (fmap toRational mip) solveMIP :: Options -> SAT.Solver -> MIP.Problem Rational -> IO ()@@ -968,18 +1057,18 @@ enc <- Tseitin.newEncoderWithPBLin solver Tseitin.setUsePB enc (optLinearizerPB opt) pbnlc <- PBNLC.newEncoder solver enc- ret <- MIP2PB.addMIP pbnlc mip+ ret <- addMIP pbnlc mip case ret of Left msg -> do putCommentLine msg putSLine "UNKNOWN" exitFailure- Right (obj, otrans, mtrans) -> do+ Right (obj, info) -> do (linObj, linObjOffset) <- Integer.linearize pbnlc obj - let transformObjVal :: Integer -> Rational- transformObjVal val = otrans (val + linObjOffset)- + let transformObjValBackward :: Integer -> Rational+ transformObjValBackward val = transformObjValueBackward info (val + linObjOffset)+ printModel :: Map MIP.Var Integer -> IO () printModel m = do forM_ (Map.toList m) $ \(v, val) -> do@@ -1001,7 +1090,7 @@ pbo <- PBO.newOptimizer solver linObj setupOptimizer pbo opt PBO.setOnUpdateBestSolution pbo $ \_ val -> do- putOLine $ showRational (optPrintRational opt) (transformObjVal val)+ putOLine $ showRational (optPrintRational opt) (transformObjValBackward val) finally (PBO.optimize pbo) $ do ret <- PBO.getBestSolution pbo@@ -1016,8 +1105,8 @@ if b then putSLine "OPTIMUM FOUND" else putSLine "SATISFIABLE"- let m2 = mtrans m- val2 = transformObjVal val+ let m2 = transformBackward info m+ val2 = transformObjValBackward val printModel m2 writeSol m2 val2
app/toysmt/ToySolver/SMT/SMTLIB2Solver.hs view
@@ -74,7 +74,6 @@ , echo ) where -import Control.Applicative import qualified Control.Exception as E import Control.Monad import Data.Interned (unintern)
app/toysmt/toysmt.hs view
@@ -13,19 +13,19 @@ ----------------------------------------------------------------------------- module Main where -import Control.Applicative ((<*))+import Control.Applicative import Control.Monad import Control.Monad.Trans-import Data.Default.Class+import Data.Monoid import Data.Version-import System.Console.GetOpt+import Options.Applicative hiding (Parser)+import qualified Options.Applicative as Opt #ifdef USE_HASKELINE_PACKAGE import qualified System.Console.Haskeline as Haskeline #endif-import System.Environment import System.Exit import System.IO-import Text.Parsec+import Text.Parsec hiding (many) import Text.Parsec.String import Smtlib.Parsers.CommandsParsers@@ -42,51 +42,49 @@ data Options = Options- { optMode :: Maybe Mode- , optInteractive :: Bool+ { optInteractive :: Bool+ , optFiles :: [FilePath] } -instance Default Options where- def =- Options- { optMode = Nothing- , optInteractive = False - }+optionsParser :: Opt.Parser Options+optionsParser = Options+ <$> interactiveOption+ <*> fileArgs+ where+ interactiveOption :: Opt.Parser Bool+ interactiveOption = switch+ $ long "interactive"+ <> help "force interactive mode" -options :: [OptDescr (Options -> Options)]-options =- [ Option ['h'] ["help"] (NoArg (\opt -> opt{ optMode = Just ModeHelp })) "show help"- , Option [] ["version"] (NoArg (\opt -> opt{ optMode = Just ModeVersion})) "show version"- , Option [] ["interactive"] (NoArg (\opt -> opt{ optMode = Just ModeInteractive })) "force interactive mode"- ]+ fileArgs :: Opt.Parser [FilePath]+ fileArgs = many $ strArgument $ metavar "FILE" +parserInfo :: ParserInfo Options+parserInfo = info (helper <*> versionOption <*> optionsParser)+ $ fullDesc+ <> header "toysmt - a SMT solver"+ where+ versionOption :: Opt.Parser (a -> a)+ versionOption = infoOption (showVersion version)+ $ hidden+ <> long "version"+ <> help "Show version"+ main :: IO () main = do #ifdef FORCE_CHAR8 setEncodingChar8 #endif-- args <- getArgs- case getOpt Permute options args of- (_,_,errs@(_:_)) -> do- mapM_ putStrLn errs- exitFailure-- (o,args2,[]) -> do- let opt = foldl (flip id) def o- case optMode opt of- Just ModeHelp -> showHelp stdout- Just ModeVersion -> hPutStrLn stdout (showVersion version)- Just ModeInteractive -> do- solver <- newSolver- mapM_ (loadFile solver) args2- repl solver - Nothing -> do- solver <- newSolver- if null args2 then- repl solver- else- mapM_ (loadFile solver) args2+ opt <- execParser parserInfo+ solver <- newSolver+ if optInteractive opt then do+ mapM_ (loadFile solver) (optFiles opt)+ repl solver + else do+ if null (optFiles opt) then+ repl solver+ else+ mapM_ (loadFile solver) (optFiles opt) loadFile :: Solver -> FilePath -> IO () loadFile solver fname = do@@ -135,14 +133,3 @@ lift $ execCommand solver cmd #endif--showHelp :: Handle -> IO ()-showHelp h = hPutStrLn h (usageInfo header options)--header :: String-header = unlines- [ "Usage:"- , " toysmt [OPTION]... [file.smt2]"- , ""- , "Options:"- ]
app/toysolver.hs view
@@ -21,6 +21,7 @@ import Data.Default.Class import Data.List import Data.Maybe+import Data.Monoid import Data.Ratio import Data.Scientific (Scientific) import qualified Data.Scientific as Scientific@@ -32,6 +33,7 @@ import qualified Data.IntMap as IntMap import qualified Data.IntSet as IntSet import qualified Data.Traversable as T+import Options.Applicative hiding (Const) import System.Exit import System.Environment import System.FilePath@@ -41,8 +43,6 @@ import GHC.Conc (getNumProcessors, setNumCapabilities) import Data.OptDir-import qualified Data.PseudoBoolean as PBFile-import qualified Data.PseudoBoolean.Attoparsec as PBFileAttoparsec import ToySolver.Data.OrdRel import ToySolver.Data.FOL.Arith as FOL@@ -59,11 +59,8 @@ import qualified ToySolver.Arith.MIP as MIPSolver import qualified ToySolver.Arith.CAD as CAD import qualified ToySolver.Arith.ContiTraverso as ContiTraverso-import qualified ToySolver.Text.CNF as CNF-import qualified ToySolver.Text.MaxSAT as MaxSAT-import qualified ToySolver.Converter.SAT2IP as SAT2IP-import qualified ToySolver.Converter.PB2IP as PB2IP-import qualified ToySolver.Converter.MaxSAT2IP as MaxSAT2IP+import qualified ToySolver.FileFormat as FF+import ToySolver.Converter import ToySolver.SAT.Printer import qualified ToySolver.SAT.Types as SAT import ToySolver.Version@@ -72,54 +69,133 @@ -- --------------------------------------------------------------------------- data Mode = ModeSAT | ModePB | ModeWBO | ModeMaxSAT | ModeMIP- deriving (Eq, Ord)+ deriving (Eq, Ord, Show) -data Flag- = Help- | Version- | Solver String- | PrintRational- | WriteFile !FilePath- | NoMIP- | PivotStrategy String- | NThread !Int- | OmegaReal String- | Mode !Mode- | FileEncoding String- deriving Eq+data Options = Options+ { optInput :: FilePath+ , optMode :: Maybe Mode+ , optSolver :: String+ , optPrintRational :: Bool+ , optWriteFile :: Maybe FilePath+ , optNoMIP :: Bool+ , optPivotStrategy :: Simplex.PivotStrategy -- String+ , optBoundTightening :: Bool+ , optNThread :: Int+ , optOmegaReal :: String+ , optFileEncoding :: Maybe String+ } deriving (Eq, Show) -options :: [OptDescr Flag]-options =- [ Option ['h'] ["help"] (NoArg Help) "show help"- , Option ['v'] ["version"] (NoArg Version) "show version number"- , Option [] ["solver"] (ReqArg Solver "SOLVER") "mip (default), omega-test, cooper, cad, old-mip, ct"- , Option [] ["print-rational"] (NoArg PrintRational) "print rational numbers instead of decimals"- , Option ['w'] [] (ReqArg WriteFile "<filename>") "write solution to filename in Gurobi .sol format"+optionsParser :: Parser Options+optionsParser = Options+ <$> fileInput+ <*> modeOption+ <*> solverOption+ <*> printRationalOption+ <*> writeFileOption+ <*> noMIPOption+ <*> pivotStrategyOption+ <*> boundTighteningOption+ <*> nThreadOption+ <*> omegaRealOption+ <*> fileEncodingOption+ where+ fileInput :: Parser FilePath+ fileInput = argument str (metavar "FILE") - , Option [] ["pivot-strategy"] (ReqArg PivotStrategy "[bland-rule|largest-coefficient]") "pivot strategy for simplex (default: bland-rule)"- , Option [] ["threads"] (ReqArg (NThread . read) "INTEGER") "number of threads to use"+ modeOption :: Parser (Maybe Mode)+ modeOption = optional $+ flag' ModeSAT (long "sat" <> help "solve boolean satisfiability problem in .cnf file")+ <|> flag' ModePB (long "pb" <> help "solve pseudo boolean problem in .opb file")+ <|> flag' ModeWBO (long "wbo" <> help "solve weighted boolean optimization problem in .wbo file")+ <|> flag' ModeMaxSAT (long "maxsat" <> help "solve MaxSAT problem in .cnf or .wcnf file")+ <|> flag' ModeMIP (long "lp" <> help "solve LP/MIP problem in .lp or .mps file") - , Option [] ["omega-real"] (ReqArg OmegaReal "SOLVER") "fourier-motzkin (default), virtual-substitution (or vs), cad, simplex, none"+ solverOption :: Parser String+ solverOption = strOption+ $ long "solver"+ <> metavar "SOLVER"+ <> help "Solver algorithm: mip, omega-test, cooper, cad, old-mip, ct"+ <> value "mip"+ <> showDefaultWith id - , Option [] ["sat"] (NoArg (Mode ModeSAT)) "solve boolean satisfiability problem in .cnf file"- , Option [] ["pb"] (NoArg (Mode ModePB)) "solve pseudo boolean problem in .opb file"- , Option [] ["wbo"] (NoArg (Mode ModeWBO)) "solve weighted boolean optimization problem in .wbo file"- , Option [] ["maxsat"] (NoArg (Mode ModeMaxSAT)) "solve MaxSAT problem in .cnf or .wcnf file"- , Option [] ["lp"] (NoArg (Mode ModeMIP)) "solve LP/MIP problem in .lp or .mps file (default)"+ printRationalOption :: Parser Bool+ printRationalOption = switch+ $ long "print-rational"+ <> help "print rational numbers instead of decimals" - , Option [] ["nomip"] (NoArg NoMIP) "consider all integer variables as continuous"+ writeFileOption :: Parser (Maybe FilePath)+ writeFileOption = optional $ strOption+ $ short 'w'+ <> metavar "FILE"+ <> help "write solution to filename in Gurobi .sol format" - , Option [] ["encoding"] (ReqArg FileEncoding "<ENCODING>") "file encoding for LP/MPS files"- ]+ noMIPOption :: Parser Bool+ noMIPOption = switch+ $ long "nomip"+ <> help "consider all integer variables as continuous" -header :: String-header = "Usage: toysolver [OPTION]... file"+ pivotStrategyOption :: Parser Simplex.PivotStrategy+ pivotStrategyOption = option (maybeReader Simplex.parsePivotStrategy)+ $ long "pivot-strategy"+ <> metavar "NAME"+ <> help ("pivot strategy for simplex: " ++ intercalate ", " [Simplex.showPivotStrategy ps | ps <- [minBound..maxBound]])+ <> value (Simplex.configPivotStrategy def)+ <> showDefaultWith Simplex.showPivotStrategy + boundTighteningOption :: Parser Bool+ boundTighteningOption = switch+ $ long "bound-tightening"+ <> help "enable bound tightening in simplex algorithm"++ nThreadOption :: Parser Int+ nThreadOption = option auto+ $ long "threads"+ <> metavar "INT"+ <> help "number of threads to use (0: auto)"+ <> value 0+ <> showDefault++ omegaRealOption :: Parser String+ omegaRealOption = strOption+ $ long "omega-real"+ <> metavar "SOLVER"+ <> help "fourier-motzkin, virtual-substitution (or vs), cad, simplex, none"+ <> value "fourier-motzkin"+ <> showDefaultWith id++ fileEncodingOption :: Parser (Maybe String)+ fileEncodingOption = optional $ strOption+ $ long "encoding"+ <> metavar "ENCODING"+ <> help "file encoding for LP/MPS files"++parserInfo :: ParserInfo Options+parserInfo = info (helper <*> versionOption <*> optionsParser)+ $ fullDesc+ <> header "toysolver - a solver for arithmetic problems"+ where+ versionOption :: Parser (a -> a)+ versionOption = infoOption (V.showVersion version)+ $ hidden+ <> long "version"+ <> help "Show version"++#if !MIN_VERSION_optparse_applicative(0,13,0)++-- | Convert a function producing a 'Maybe' into a reader.+maybeReader :: (String -> Maybe a) -> ReadM a+maybeReader f = eitherReader $ \arg ->+ case f arg of+ Nothing -> Left $ "cannot parse value `" ++ arg ++ "'"+ Just a -> Right a++#endif+ -- --------------------------------------------------------------------------- run :: String- -> [Flag]+ -> Options -> MIP.Problem Rational -> (Map MIP.Var Rational -> IO ()) -> IO ()@@ -177,8 +253,8 @@ Just _ -> error "indicator constraint is not supported yet" ivs- | NoMIP `elem` opt = Set.empty- | otherwise = MIP.integerVariables mip+ | optNoMIP opt = Set.empty+ | otherwise = MIP.integerVariables mip vs2 = IntMap.keysSet varToName ivs2 = IntSet.fromList . map (nameToVar Map.!) . Set.toList $ ivs@@ -211,7 +287,7 @@ } where realSolver =- case last ("fourier-motzkin" : [s | OmegaReal s <- opt]) of+ case optOmegaReal opt of "fourier-motzkin" -> OmegaTest.checkRealByFM "virtual-substitution" -> OmegaTest.checkRealByVS "vs" -> OmegaTest.checkRealByVS@@ -246,14 +322,14 @@ solveByMIP2 = do solver <- Simplex.newSolver - let ps = last ("bland-rule" : [s | PivotStrategy s <- opt])- case ps of- "bland-rule" -> Simplex.setPivotStrategy solver Simplex.PivotStrategyBlandRule- "largest-coefficient" -> Simplex.setPivotStrategy solver Simplex.PivotStrategyLargestCoefficient- _ -> error ("unknown pivot strategy \"" ++ ps ++ "\"")-- let nthreads = last (0 : [n | NThread n <- opt])+ let config =+ def+ { Simplex.configPivotStrategy = optPivotStrategy opt+ , Simplex.configEnableBoundTightening = optBoundTightening opt+ }+ nthreads = optNThread opt + Simplex.setConfig solver config Simplex.setLogger solver putCommentLine Simplex.enableTimeRecording solver replicateM (length vsAssoc) (Simplex.newVar solver) -- XXX@@ -355,7 +431,7 @@ printModel m3 printRat :: Bool- printRat = PrintRational `elem` opt+ printRat = optPrintRational opt showValue :: Rational -> String showValue = showRational printRat@@ -386,87 +462,52 @@ -- --------------------------------------------------------------------------- -getSolver :: [Flag] -> String-getSolver xs = last $ "mip" : [s | Solver s <- xs]- main :: IO () main = do #ifdef FORCE_CHAR8 setEncodingChar8 #endif - args <- getArgs- case getOpt Permute options args of- (o,_,[])- | Help `elem` o -> putStrLn (usageInfo header options)- | Version `elem` o -> putStrLn (V.showVersion version)- (o,[fname],[]) -> do-- let mode =- case reverse [m | Mode m <- o] of- m:_ -> m- [] ->- case map toLower (takeExtension fname) of- ".cnf" -> ModeSAT- ".opb" -> ModePB- ".wbo" -> ModeWBO- ".wcnf" -> ModeMaxSAT- ".lp" -> ModeMIP- ".mps" -> ModeMIP- _ -> ModeMIP+ o <- execParser parserInfo - case mode of- ModeSAT -> do- ret <- CNF.parseFile fname- case ret of- Left err -> hPrint stderr err >> exitFailure- Right cnf -> do- let (mip,_,mtrans) = SAT2IP.convert cnf- run (getSolver o) o (fmap fromInteger mip) $ \m -> do- let m2 = mtrans m- satPrintModel stdout m2 0- writeSOLFileSAT o m2- ModePB -> do- ret <- PBFileAttoparsec.parseOPBFile fname- case ret of- Left err -> hPutStrLn stderr err >> exitFailure- Right pb -> do- let (mip,_,mtrans) = PB2IP.convert pb- run (getSolver o) o (fmap fromInteger mip) $ \m -> do- let m2 = mtrans m- pbPrintModel stdout m2 0- writeSOLFileSAT o m2- ModeWBO -> do- ret <- PBFileAttoparsec.parseWBOFile fname- case ret of- Left err -> hPutStrLn stderr err >> exitFailure- Right wbo -> do- let (mip,_,mtrans) = PB2IP.convertWBO False wbo- run (getSolver o) o (fmap fromInteger mip) $ \m -> do- let m2 = mtrans m- pbPrintModel stdout m2 0- writeSOLFileSAT o m2- ModeMaxSAT -> do- ret <- MaxSAT.parseFile fname- case ret of- Left err -> hPutStrLn stderr err >> exitFailure- Right wcnf -> do- let (mip,_,mtrans) = MaxSAT2IP.convert False wcnf- run (getSolver o) o (fmap fromInteger mip) $ \m -> do- let m2 = mtrans m- maxsatPrintModel stdout m2 0- writeSOLFileSAT o m2- ModeMIP -> do- enc <- T.mapM mkTextEncoding $ last $ Nothing : [Just s | FileEncoding s <- o]- mip <- MIP.readFile def{ MIP.optFileEncoding = enc } fname- run (getSolver o) o (fmap toRational mip) $ \m -> do- mipPrintModel stdout (PrintRational `elem` o) m- writeSOLFileMIP o m- (_,_,errs) ->- hPutStrLn stderr $ concat errs ++ usageInfo header options+ case fromMaybe ModeMIP (optMode o) of+ ModeSAT -> do+ cnf <- FF.readFile (optInput o)+ let (mip,info) = sat2ip cnf+ run (optSolver o) o (fmap fromInteger mip) $ \m -> do+ let m2 = transformBackward info m+ satPrintModel stdout m2 0+ writeSOLFileSAT o m2+ ModePB -> do+ pb <- FF.readFile (optInput o)+ let (mip,info) = pb2ip pb+ run (optSolver o) o (fmap fromInteger mip) $ \m -> do+ let m2 = transformBackward info m+ pbPrintModel stdout m2 0+ writeSOLFileSAT o m2+ ModeWBO -> do+ wbo <- FF.readFile (optInput o)+ let (mip,info) = wbo2ip False wbo+ run (optSolver o) o (fmap fromInteger mip) $ \m -> do+ let m2 = transformBackward info m+ pbPrintModel stdout m2 0+ writeSOLFileSAT o m2+ ModeMaxSAT -> do+ wcnf <- FF.readFile (optInput o)+ let (mip,info) = maxsat2ip False wcnf+ run (optSolver o) o (fmap fromInteger mip) $ \m -> do+ let m2 = transformBackward info m+ maxsatPrintModel stdout m2 0+ writeSOLFileSAT o m2+ ModeMIP -> do+ enc <- T.mapM mkTextEncoding $ optFileEncoding o+ mip <- MIP.readFile def{ MIP.optFileEncoding = enc } (optInput o)+ run (optSolver o) o (fmap toRational mip) $ \m -> do+ mipPrintModel stdout (optPrintRational o) m+ writeSOLFileMIP o m -- FIXME: 目的関数値を表示するように-writeSOLFileMIP :: [Flag] -> Map MIP.Var Rational -> IO ()+writeSOLFileMIP :: Options -> Map MIP.Var Rational -> IO () writeSOLFileMIP opt m = do let sol = MIP.Solution { MIP.solStatus = MIP.StatusUnknown@@ -476,7 +517,7 @@ writeSOLFileRaw opt sol -- FIXME: 目的関数値を表示するように-writeSOLFileSAT :: [Flag] -> SAT.Model -> IO ()+writeSOLFileSAT :: Options -> SAT.Model -> IO () writeSOLFileSAT opt m = do let sol = MIP.Solution { MIP.solStatus = MIP.StatusUnknown@@ -485,7 +526,9 @@ } writeSOLFileRaw opt sol -writeSOLFileRaw :: [Flag] -> MIP.Solution Scientific -> IO ()+writeSOLFileRaw :: Options -> MIP.Solution Scientific -> IO () writeSOLFileRaw opt sol = do- forM_ [fname | WriteFile fname <- opt ] $ \fname -> do- GurobiSol.writeFile fname sol+ case optWriteFile opt of+ Just fname -> GurobiSol.writeFile fname sol+ Nothing -> return ()+
benchmarks/BenchmarkSATLIB.hs view
@@ -6,18 +6,19 @@ import Text.Printf import Criterion.Main import qualified ToySolver.SAT as SAT-import qualified ToySolver.Text.CNF as CNF+import qualified ToySolver.FileFormat as FF+import qualified ToySolver.FileFormat.CNF as CNF solve :: FilePath -> IO () solve fname = do- ret <- CNF.parseFile fname+ ret <- FF.parseFile fname case ret of Left err -> error $ show err Right cnf -> do solver <- SAT.newSolverWithConfig def{ SAT.configRandomFreq = 0 }- _ <- replicateM (CNF.numVars cnf) (SAT.newVar solver)- forM_ (CNF.clauses cnf) $ \clause ->- SAT.addClause solver clause+ _ <- replicateM (CNF.cnfNumVars cnf) (SAT.newVar solver)+ forM_ (CNF.cnfClauses cnf) $ \clause ->+ SAT.addClause solver (SAT.unpackClause clause) SAT.solve solver return ()
+ misc/build_bdist_smtcomp.sh view
@@ -0,0 +1,26 @@+#!/bin/bash+export CABALVER=1.22+export GHCVER=7.10.3++sudo add-apt-repository -y ppa:hvr/ghc+sudo apt-get update++sudo apt-get install cabal-install-$CABALVER ghc-$GHCVER+export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:~/.cabal/bin:$PATH++sudo apt-get install happy-1.19.4 alex-3.1.3+export PATH=/opt/alex/3.1.3/bin:/opt/happy/1.19.4/bin:$PATH++cabal sandbox init+cabal update+cabal install --only-dependencies+#cabal configure --disable-shared --ghc-options="-static -optl-static -optl-pthread"+cabal configure -fLinuxStatic -fForceChar8+cabal build++PKG=toysmt-smtcomp`date +%Y`-`date +%Y%m%d`-`git rev-parse --short HEAD`+rm -r $PKG+cp -a misc/smtcomp $PKG+cp dist/build/toysmt/toysmt $PKG/bin+cd $PKG+tar zcf ../$PKG.tar.gz . --owner=sakai --group=sakai
misc/maxsat/toysat/README.md view
@@ -23,4 +23,4 @@ Improvements to Core-Guided binary search for MaxSAT, in Theory and Applications of Satisfiability Testing (SAT 2012), pp. 284-297.- <http://dx.doi.org/10.1007/978-3-642-31612-8_22>+ <https://doi.org/10.1007/978-3-642-31612-8_22>
misc/maxsat/toysat_ls/README.md view
@@ -26,9 +26,9 @@ Improvements to Core-Guided binary search for MaxSAT, in Theory and Applications of Satisfiability Testing (SAT 2012), pp. 284-297.- <http://dx.doi.org/10.1007/978-3-642-31612-8_22>+ <https://doi.org/10.1007/978-3-642-31612-8_22> * [2] D. Tompkins and H. Hoos, UBCSAT: An implementation and experimentation environment for SLS algorithms for SAT and MAX-SAT, in Theory and Applications of Satisfiability Testing (2004), Springer, 2005, pp. 306-320.- <http://dx.doi.org/10.1007/11527695_24>+ <https://doi.org/10.1007/11527695_24>
misc/pb/README.md view
@@ -52,7 +52,7 @@ Improvements to Core-Guided binary search for MaxSAT, in Theory and Applications of Satisfiability Testing (SAT 2012), pp. 284-297.- <http://dx.doi.org/10.1007/978-3-642-31612-8_22>+ <https://doi.org/10.1007/978-3-642-31612-8_22> * [2] Masahiro Sakai. <https://github.com/msakai/toysolver>
misc/qbf/README.md view
@@ -30,5 +30,5 @@ * [1] Mikoláš Janota, William Klieber, Joao Marques-Silva, Edmund Clarke. Solving QBF with Counterexample Guided Refinement. In Theory and Applications of Satisfiability Testing (SAT 2012), pp. 114-128.- <http://dx.doi.org/10.1007/978-3-642-31612-8_10>+ <https://doi.org/10.1007/978-3-642-31612-8_10> <https://www.cs.cmu.edu/~wklieber/papers/qbf-cegar-sat-2012.pdf>
+ misc/smtcomp/bin/starexec_run_default view
@@ -0,0 +1,2 @@+#!/bin/sh+./toysmt "$1"
+ misc/smtcomp/starexec_description.txt view
@@ -0,0 +1,1 @@+A toylevel SMT solver for QFUFLRA and its sublogics
samples/programs/assign/assign.hs view
@@ -22,7 +22,6 @@ -} module Main where -import Control.Applicative import Control.Monad import Data.Attoparsec.ByteString.Char8 hiding (isSpace) import qualified Data.Attoparsec.ByteString.Lazy as AL
samples/programs/nonogram/nonogram.hs view
@@ -2,13 +2,11 @@ {-# OPTIONS_GHC -Wall #-} module Main where -import Control.Applicative import Control.Monad import Data.Array.IArray import Data.Array.Unboxed import Data.IORef import Data.List (group)-import Data.Map (Map) import qualified Data.Map as Map import System.Console.GetOpt import System.Environment@@ -141,7 +139,7 @@ m <- SAT.getModel solver SAT.addClause solver [if val then -var else var | (var,val) <- assocs m] let sol = amap (SAT.evalLit m) bs- return (Just sol) + return (Just sol) data Options = Options
samples/programs/numberlink/numberlink.hs view
@@ -2,7 +2,6 @@ {-# OPTIONS_GHC -Wall #-} module Main where -import Control.Applicative hiding (many, optional) import Control.Monad import Data.Array.IArray import qualified Data.ByteString.Lazy.Char8 as BL@@ -21,6 +20,7 @@ import System.IO import Text.Parsec hiding (try) import qualified Text.Parsec.ByteString.Lazy as ParsecBL+import qualified ToySolver.FileFormat as FF import qualified ToySolver.SAT as SAT import qualified ToySolver.SAT.PBO as PBO import qualified ToySolver.SAT.Store.PB as PBStore@@ -466,7 +466,7 @@ obj <- encodeObj store opt prob encoded return $ Just [(c,[v]) | (c,v) <- obj] opb <- PBStore.getPBFormula store- PB.writeOPBFile fname2 $ opb{ PB.pbObjectiveFunction = obj }+ FF.writeFile fname2 $ opb{ PB.pbObjectiveFunction = obj } _ -> do showHelp stderr
+ samples/programs/probsat/probsat.hs view
@@ -0,0 +1,177 @@+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.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)
samples/programs/survey-propagation/survey-propagation.hs view
@@ -8,7 +8,8 @@ import System.Environment import System.Exit import System.IO-import qualified ToySolver.Text.MaxSAT as MaxSAT+import qualified ToySolver.FileFormat as FF+import qualified ToySolver.FileFormat.CNF as CNF import qualified ToySolver.SAT.MessagePassing.SurveyPropagation as SP #ifdef ENABLE_OPENCL import Control.Parallel.OpenCL@@ -96,7 +97,7 @@ (o,[fname],_) -> do let opt = foldl (flip id) def o handle (\(e::SomeException) -> hPrint stderr e) $ do- Right wcnf <- MaxSAT.parseFile fname+ wcnf <- FF.readFile fname #ifdef ENABLE_OPENCL if optOpenCL opt then do@@ -110,10 +111,10 @@ error ("platform " ++ name ++ " has only " ++ show (length devs) ++ " devices") context <- clCreateContext [] [dev] print solver <- SPCL.newSolver putStrLn context dev- (MaxSAT.numVars wcnf) [(fromIntegral w, clause) | (w,clause) <- MaxSAT.clauses wcnf]+ (CNF.wcnfNumVars wcnf) [(fromIntegral w, clause) | (w,clause) <- CNF.wcnfClauses wcnf] -- Rand.withSystemRandom $ SPCL.initializeRandom solver print =<< SPCL.propagate solver- forM_ [1 .. MaxSAT.numVars wcnf] $ \v -> do+ forM_ [1 .. CNF.wcnfNumVars wcnf] $ \v -> do prob <- SPCL.getVarProb solver v print (v,prob) SPCL.deleteSolver solver@@ -123,11 +124,11 @@ #endif else do solver <- SP.newSolver- (MaxSAT.numVars wcnf) [(fromIntegral w, clause) | (w,clause) <- MaxSAT.clauses wcnf]+ (CNF.wcnfNumVars wcnf) [(fromIntegral w, clause) | (w,clause) <- CNF.wcnfClauses wcnf] SP.setNThreads solver (optNThreads opt) -- Rand.withSystemRandom $ SP.initializeRandom solver print =<< SP.propagate solver- forM_ [1 .. MaxSAT.numVars wcnf] $ \v -> do+ forM_ [1 .. CNF.wcnfNumVars wcnf] $ \v -> do prob <- SP.getVarProb solver v print (v,prob) SP.deleteSolver solver
src/ToySolver/Arith/ContiTraverso.hs view
@@ -13,7 +13,7 @@ -- * P. Conti and C. Traverso, "Buchberger algorithm and integer programming," -- Applied Algebra, Algebraic Algorithms and Error-Correcting Codes, -- Lecture Notes in Computer Science Volume 539, 1991, pp 130-139--- <http://dx.doi.org/10.1007/3-540-54522-0_102>+-- <https://doi.org/10.1007/3-540-54522-0_102> -- <http://posso.dm.unipi.it/users/traverso/conti-traverso-ip.ps> -- -- * IKEGAMI Daisuke, "数列と多項式の愛しい関係," 2011,
src/ToySolver/Arith/Cooper/Base.hs view
@@ -1,5 +1,7 @@ {-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Arith.Cooper.Base@@ -54,8 +56,8 @@ import Data.Maybe import qualified Data.IntMap as IM import qualified Data.IntSet as IS-import Data.Monoid import Data.Ratio+import qualified Data.Semigroup as Semigroup import Data.Set (Set) import qualified Data.Set as Set import Data.VectorSpace hiding (project)@@ -371,9 +373,15 @@ newtype LCM a = LCM{ getLCM :: a } +instance Integral a => Semigroup.Semigroup (LCM a) where+ LCM a <> LCM b = LCM $ lcm a b+ stimes = Semigroup.stimesIdempotent+ instance Integral a => Monoid (LCM a) where mempty = LCM 1- LCM a `mappend` LCM b = LCM $ lcm a b+#if !(MIN_VERSION_base(4,11,0))+ mappend = (Semigroup.<>)+#endif checkedDiv :: Integer -> Integer -> Integer checkedDiv a b =
src/ToySolver/Arith/DifferenceLogic.hs view
@@ -61,13 +61,15 @@ d = bellmanFord lastInEdge g vs -- M = {a−b ≤ 2, b−c ≤ 3, c−a ≤ −3}-test_sat = solve xs+_test_sat :: Either (HashSet Int) (HashMap Char Int)+_test_sat = solve xs where xs :: [(Int, SimpleAtom Char Int)] xs = [(1, ('a' :- 'b' :<= 2)), (2, ('b' :- 'c' :<= 3)), (3, ('c' :- 'a' :<= -3))] -- M = {a−b ≤ 2, b−c ≤ 3, c−a ≤ −7}-test_unsat = solve xs+_test_unsat :: Either (HashSet Int) (HashMap Char Int)+_test_unsat = solve xs where xs :: [(Int, SimpleAtom Char Int)] xs = [(1, ('a' :- 'b' :<= 2)), (2, ('b' :- 'c' :<= 3)), (3, ('c' :- 'a' :<= -7))]
src/ToySolver/Arith/MIP.hs view
@@ -323,7 +323,7 @@ ret <- try $ restore loop case ret of Left e -> atomically (putTMVar ex e)- Right _ -> return () + Right _ -> return () let propagateException :: SomeException -> IO () propagateException e = do
src/ToySolver/Arith/OmegaTest/Base.hs view
@@ -288,7 +288,7 @@ pickupZ (Nothing,Nothing) = return 0 pickupZ (Just x, Nothing) = return x pickupZ (Nothing, Just x) = return x-pickupZ (Just x, Just y) = if x <= y then return x else mzero +pickupZ (Just x, Just y) = if x <= y then return x else mzero -- ---------------------------------------------------------------------------
src/ToySolver/Arith/Simplex.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE ScopedTypeVariables, Rank2Types, TypeOperators, TypeSynonymInstances, FlexibleInstances, TypeFamilies, CPP #-}+{-# LANGUAGE BangPatterns #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Arith.Simplex@@ -97,7 +98,13 @@ , clearLogger , enableTimeRecording , disableTimeRecording+ , Config (..)+ , setConfig+ , getConfig+ , modifyConfig , PivotStrategy (..)+ , showPivotStrategy+ , parsePivotStrategy , setPivotStrategy -- * Debug@@ -108,9 +115,11 @@ ) where import Prelude hiding (log)+import Control.Arrow ((***)) import Control.Exception import Control.Monad import Control.Monad.Primitive+import Data.Char import Data.Default.Class import Data.Ord import Data.List@@ -155,16 +164,15 @@ , svLB :: !(MutVar (PrimState m) (IntMap (v, ConstrIDSet))) , svUB :: !(MutVar (PrimState m) (IntMap (v, ConstrIDSet))) , svModel :: !(MutVar (PrimState m) (IntMap v))- , svExplanation :: !(MutVar (PrimState m) ConstrIDSet)+ , svExplanation :: !(MutVar (PrimState m) (Maybe ConstrIDSet)) , svVCnt :: !(MutVar (PrimState m) Int)- , svOk :: !(MutVar (PrimState m) Bool) , svOptDir :: !(MutVar (PrimState m) OptDir) , svDefDB :: !(MutVar (PrimState m) (Map (LA.Expr Rational) Var)) , svLogger :: !(MutVar (PrimState m) (Maybe (String -> m ()))) , svRecTime :: !(MutVar (PrimState m) (Maybe (GenericSolverM m v -> (m :~> m))))- , svPivotStrategy :: !(MutVar (PrimState m) PivotStrategy)+ , svConfig :: !(MutVar (PrimState m) Config) , svNPivot :: !(MutVar (PrimState m) Int) , svBacktrackPoints :: !(MutVar (PrimState m) [BacktrackPoint m v])@@ -186,12 +194,11 @@ m <- newMutVar (IntMap.singleton objVar zeroV) e <- newMutVar mempty v <- newMutVar 0- ok <- newMutVar True dir <- newMutVar OptMin defs <- newMutVar Map.empty logger <- newMutVar Nothing rectm <- newMutVar Nothing- pivot <- newMutVar PivotStrategyBlandRule+ config <- newMutVar def npivot <- newMutVar 0 bps <- newMutVar [] return $@@ -202,13 +209,12 @@ , svModel = m , svExplanation = e , svVCnt = v- , svOk = ok , svOptDir = dir , svDefDB = defs , svLogger = logger , svRecTime = rectm , svNPivot = npivot- , svPivotStrategy = pivot+ , svConfig = config , svBacktrackPoints = bps } @@ -220,12 +226,11 @@ m <- newMutVar =<< readMutVar (svModel solver) e <- newMutVar =<< readMutVar (svExplanation solver) v <- newMutVar =<< readMutVar (svVCnt solver)- ok <- newMutVar =<< readMutVar (svOk solver) dir <- newMutVar =<< readMutVar (svOptDir solver) defs <- newMutVar =<< readMutVar (svDefDB solver) logger <- newMutVar =<< readMutVar (svLogger solver) rectm <- newMutVar =<< readMutVar (svRecTime solver)- pivot <- newMutVar =<< readMutVar (svPivotStrategy solver)+ config <- newMutVar =<< readMutVar (svConfig solver) npivot <- newMutVar =<< readMutVar (svNPivot solver) bps <- newMutVar =<< mapM cloneBacktrackPoint =<< readMutVar (svBacktrackPoints solver) return $@@ -236,15 +241,14 @@ , svModel = m , svExplanation = e , svVCnt = v- , svOk = ok , svOptDir = dir , svDefDB = defs , svLogger = logger , svRecTime = rectm , svNPivot = npivot- , svPivotStrategy = pivot+ , svConfig = config , svBacktrackPoints = bps- } + } class (VectorSpace v, Scalar v ~ Rational, Ord v) => SolverValue v where toValue :: Rational -> v@@ -285,6 +289,39 @@ boundExplanation :: SolverValue v => Bound v -> ConstrIDSet boundExplanation = maybe mempty snd +addBound :: SolverValue v => Bound v -> Bound v -> Bound v+addBound b1 b2 = do+ (a1,cs1) <- b1+ (a2,cs2) <- b2+ let a3 = a1 ^+^ a2+ cs3 = IntSet.union cs1 cs2+ seq a3 $ seq cs3 $ return (a3,cs3)++scaleBound :: SolverValue v => Scalar v -> Bound v -> Bound v+scaleBound c = fmap ((c *^) *** id)++data Config+ = Config+ { configPivotStrategy :: !PivotStrategy+ , configEnableBoundTightening :: !Bool+ } deriving (Show, Eq, Ord)++instance Default Config where+ def =+ Config+ { configPivotStrategy = PivotStrategyBlandRule+ , configEnableBoundTightening = False+ }++setConfig :: PrimMonad m => GenericSolverM m v -> Config -> m ()+setConfig solver config = writeMutVar (svConfig solver) config++getConfig :: PrimMonad m => GenericSolverM m v -> m Config+getConfig solver = readMutVar (svConfig solver)++modifyConfig :: PrimMonad m => GenericSolverM m v -> (Config -> Config) -> m ()+modifyConfig solver = modifyMutVar' (svConfig solver)+ {- Largest coefficient rule: original rule suggested by G. Dantzig. Largest increase rule: computationally more expensive in comparison with Largest coefficient, but locally maximizes the progress.@@ -297,10 +334,23 @@ = PivotStrategyBlandRule | PivotStrategyLargestCoefficient -- | PivotStrategySteepestEdge- deriving (Eq, Ord, Enum, Show, Read)+ deriving (Eq, Ord, Enum, Bounded, Show, Read) +showPivotStrategy :: PivotStrategy -> String+showPivotStrategy PivotStrategyBlandRule = "bland-rule"+showPivotStrategy PivotStrategyLargestCoefficient = "largest-coefficient"++parsePivotStrategy :: String -> Maybe PivotStrategy+parsePivotStrategy s =+ case map toLower s of+ "bland-rule" -> Just PivotStrategyBlandRule+ "largest-coefficient" -> Just PivotStrategyLargestCoefficient+ _ -> Nothing++{-# DEPRECATED nVars "Use setConfig instead" #-} setPivotStrategy :: PrimMonad m => GenericSolverM m v -> PivotStrategy -> m ()-setPivotStrategy solver ps = writeMutVar (svPivotStrategy solver) ps+setPivotStrategy solver ps = modifyConfig solver $ \config ->+ config{ configPivotStrategy = ps } {-------------------------------------------------------------------- problem description@@ -341,7 +391,7 @@ writeMutVar (svUB solver) $ IntMap.mergeWithKey (\_ _curr saved -> saved) id (const IntMap.empty) ubs savedUBs writeMutVar (svBacktrackPoints solver) bps'- writeMutVar (svOk solver) True+ writeMutVar (svExplanation solver) Nothing withBacktrackpoint :: PrimMonad m => GenericSolverM m v -> (BacktrackPoint m v -> m ()) -> m () withBacktrackpoint solver f = do@@ -438,7 +488,10 @@ Just 0 -> do modifyMutVar (svLB solver) (IntMap.insert v (toValue 0, mempty)) modifyMutVar (svUB solver) (IntMap.insert v (toValue 0, mempty))- _ -> return ()+ _ -> do+ config <- getConfig solver+ when (configEnableBoundTightening config) $ do+ tightenBounds solver v return (v,op'',rhs'') where scale :: LA.Expr Rational -> (Rational, LA.Expr Rational)@@ -449,18 +502,20 @@ c2 = signum $ head ([c | (c,x) <- LA.terms e] ++ [1]) assertLower :: (PrimMonad m, SolverValue v) => GenericSolverM m v -> Var -> v -> m ()-assertLower solver x l = assertLower' solver x l Nothing+assertLower solver x l = assertLB solver x (Just (l, IntSet.empty)) assertLower' :: (PrimMonad m, SolverValue v) => GenericSolverM m v -> Var -> v -> Maybe ConstrID -> m ()-assertLower' solver x l cid = do- let cidSet = IntSet.fromList $ maybeToList cid+assertLower' solver x l cid = assertLB solver x (Just (l, IntSet.fromList (maybeToList cid)))++assertLB :: (PrimMonad m, SolverValue v) => GenericSolverM m v -> Var -> Bound v -> m ()+assertLB solver x Nothing = return ()+assertLB solver x (Just (l, cidSet)) = do l0 <- getLB solver x u0 <- getUB solver x case (l0,u0) of (Just (l0', _), _) | l <= l0' -> return () (_, Just (u0', cidSet2)) | u0' < l -> do- writeMutVar (svExplanation solver) $ cidSet `IntSet.union` cidSet2- markBad solver+ setExplanation solver $ cidSet `IntSet.union` cidSet2 _ -> do bpSaveLB solver x modifyMutVar (svLB solver) (IntMap.insert x (l, cidSet))@@ -470,18 +525,20 @@ checkNBFeasibility solver assertUpper :: (PrimMonad m, SolverValue v) => GenericSolverM m v -> Var -> v -> m ()-assertUpper solver x u = assertUpper' solver x u Nothing +assertUpper solver x u = assertUB solver x (Just (u, IntSet.empty)) assertUpper' :: (PrimMonad m, SolverValue v) => GenericSolverM m v -> Var -> v -> Maybe ConstrID -> m ()-assertUpper' solver x u cid = do- let cidSet = IntSet.fromList $ maybeToList cid+assertUpper' solver x u cid = assertUB solver x (Just (u, IntSet.fromList (maybeToList cid)))++assertUB :: (PrimMonad m, SolverValue v) => GenericSolverM m v -> Var -> Bound v -> m ()+assertUB solver x Nothing = return ()+assertUB solver x (Just (u, cidSet)) = do l0 <- getLB solver x u0 <- getUB solver x case (l0,u0) of (_, Just (u0', _)) | u0' <= u -> return () (Just (l0', cidSet2), _) | u < l0' -> do- writeMutVar (svExplanation solver) $ cidSet `IntSet.union` cidSet2- markBad solver+ setExplanation solver $ cidSet `IntSet.union` cidSet2 _ -> do bpSaveUB solver x modifyMutVar (svUB solver) (IntMap.insert x (u, cidSet))@@ -502,7 +559,7 @@ modifyMutVar (svTableau solver) $ \t -> IntMap.insert v (LA.applySubst t e) t modifyMutVar (svModel solver) $ \m -> - IntMap.insert v (LA.evalLinear m (toValue 1) e) m + IntMap.insert v (LA.evalLinear m (toValue 1) e) m setOptDir :: PrimMonad m => GenericSolverM m v -> OptDir -> m () setOptDir solver dir = writeMutVar (svOptDir solver) dir@@ -567,7 +624,7 @@ -- (aij < 0 and β(xj) < uj) or (aij > 0 and β(xj) > lj) canDecrease solver xi_def <- getRow solver xi- r <- liftM (fmap snd) $ findM q (LA.terms xi_def) + r <- liftM (fmap snd) $ findM q (LA.terms xi_def) case r of Nothing -> do let c = if isLBViolated then li else ui@@ -582,21 +639,24 @@ getLB solver xj else do getUB solver xj- writeMutVar (svExplanation solver) core- markBad solver+ setExplanation solver core return False Just xj -> do pivotAndUpdate solver xi xj (fromJust $ boundValue $ if isLBViolated then li else ui)- loop+ m <- readMutVar (svExplanation solver)+ if isJust m then+ return False+ else+ loop - ok <- readMutVar (svOk solver)- if not ok- then return False- else do- log solver "check"- result <- recordTime solver loop- when result $ checkFeasibility solver- return result+ m <- readMutVar (svExplanation solver)+ case m of+ Just _ -> return False+ Nothing -> do+ log solver "check"+ result <- recordTime solver loop+ when result $ checkFeasibility solver+ return result selectViolatingBasicVariable :: forall m v. (PrimMonad m, SolverValue v) => GenericSolverM m v -> m (Maybe Var) selectViolatingBasicVariable solver = do@@ -610,8 +670,8 @@ return $ not (testLB li vi) || not (testUB ui vi) vs <- basicVariables solver - ps <- readMutVar (svPivotStrategy solver)- case ps of+ config <- getConfig solver+ case configPivotStrategy config of PivotStrategyBlandRule -> findM p vs PivotStrategyLargestCoefficient -> do@@ -628,6 +688,25 @@ else return (xi, vi ^-^ fromJust (boundValue ui)) return $ Just $ fst $ maximumBy (comparing snd) xs2 +tightenBounds :: (PrimMonad m, SolverValue v) => GenericSolverM m v -> Var -> m ()+tightenBounds solver x = do+ -- x must be basic variable+ defs <- readMutVar (svTableau solver)+ let x_def = defs IntMap.! x+ f (!lb,!ub) (c,xk) = do+ if LA.unitVar == xk then do+ return (addBound lb (Just (toValue c, IntSet.empty)), addBound ub (Just (toValue c, IntSet.empty)))+ else do+ lb_k <- getLB solver xk+ ub_k <- getUB solver xk+ if c > 0 then do+ return (addBound lb (scaleBound c lb_k), addBound ub (scaleBound c ub_k))+ else do+ return (addBound lb (scaleBound c ub_k), addBound ub (scaleBound c lb_k))+ (lb,ub) <- foldM f (Just (zeroV, IntSet.empty), Just (zeroV, IntSet.empty)) (LA.terms x_def)+ assertLB solver x lb+ assertUB solver x ub+ {-------------------------------------------------------------------- Optimization --------------------------------------------------------------------}@@ -685,9 +764,9 @@ selectEnteringVariable :: forall m v. (PrimMonad m, SolverValue v) => GenericSolverM m v -> m (Maybe (Rational, Var)) selectEnteringVariable solver = do- ps <- readMutVar (svPivotStrategy solver)+ config <- getConfig solver obj_def <- getRow solver objVar- case ps of+ case configPivotStrategy config of PivotStrategyBlandRule -> findM canEnter (LA.terms obj_def) PivotStrategyLargestCoefficient -> do@@ -817,21 +896,24 @@ getLB solver xj else do getUB solver xj- writeMutVar (svExplanation solver) core- markBad solver+ setExplanation solver core return Unsat Just xj -> do pivotAndUpdate solver xi xj (fromJust $ boundValue $ if isLBViolated then li else ui)- loop+ m <- readMutVar (svExplanation solver)+ if isJust m then+ return Unsat+ else+ loop - ok <- readMutVar (svOk solver)- if not ok- then return Unsat- else do- log solver "dual simplex"- result <- recordTime solver loop- when (result == Optimum) $ checkFeasibility solver- return result+ m <- readMutVar (svExplanation solver)+ case m of+ Just _ -> return Unsat+ Nothing -> do+ log solver "dual simplex"+ result <- recordTime solver loop+ when (result == Optimum) $ checkFeasibility solver+ return result dualRTest :: PrimMonad m => GenericSolverM m Rational -> LA.Expr Rational -> Bool -> m (Maybe Var) dualRTest solver row isLBViolated = do@@ -889,13 +971,17 @@ return (x,val) getObjValue :: PrimMonad m => GenericSolverM m v -> m v-getObjValue solver = getValue solver objVar +getObjValue solver = getValue solver objVar type Model = IntMap Rational explain :: PrimMonad m => GenericSolverM m v -> m ConstrIDSet-explain solver = readMutVar (svExplanation solver)- +explain solver = do+ m <- readMutVar (svExplanation solver)+ case m of+ Nothing -> error "no explanation is available"+ Just cs -> return cs+ {-------------------------------------------------------------------- major function --------------------------------------------------------------------}@@ -947,6 +1033,10 @@ pivot solver xi xj + config <- getConfig solver+ when (configEnableBoundTightening config) $ do+ tightenBounds solver xj+ -- log solver $ printf "after pivotAndUpdate x%d x%d (%s)" xi xj (show v) -- dump solver @@ -987,8 +1077,12 @@ xi_def <- getRow solver xi return $! LA.coeff xj xi_def -markBad :: PrimMonad m => GenericSolverM m v -> m ()-markBad solver = writeMutVar (svOk solver) False+setExplanation :: PrimMonad m => GenericSolverM m v -> ConstrIDSet -> m ()+setExplanation solver !cs = do+ m <- readMutVar (svExplanation solver)+ case m of+ Just _ -> return ()+ Nothing -> writeMutVar (svExplanation solver) (Just cs) {-------------------------------------------------------------------- utility
src/ToySolver/Arith/Simplex/Textbook.hs view
@@ -163,7 +163,7 @@ and [IS.null (IM.keysSet m `IS.intersection` vs) | (m,_) <- IM.elems tbl'] where tbl' = IM.delete objRowIndex tbl- vs = IM.keysSet tbl' + vs = IM.keysSet tbl' isFeasible :: Real r => Tableau r -> Bool isFeasible tbl = @@ -179,7 +179,7 @@ isImproving :: Real r => OptDir -> Tableau r -> Tableau r -> Bool isImproving OptMin from to = currentObjValue to <= currentObjValue from -isImproving OptMax from to = currentObjValue to >= currentObjValue from +isImproving OptMax from to = currentObjValue to >= currentObjValue from -- --------------------------------------------------------------------------- -- primal simplex
src/ToySolver/BitVector/Base.hs view
@@ -1,4 +1,5 @@ {-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}@@ -46,6 +47,7 @@ import qualified Data.Map as Map import Data.Monoid import Data.Ord+import qualified Data.Semigroup as Semigroup import qualified Data.Vector as V import qualified Data.Vector.Generic as VG import qualified Data.Vector.Unboxed as VU@@ -124,9 +126,14 @@ compare (BV bs1) (BV bs2) = (comparing VG.length <> comparing VG.reverse) bs1 bs2 +instance Semigroup.Semigroup BV where+ BV hi <> BV lo = BV (lo <> hi)+ instance Monoid BV where mempty = BV VG.empty- mappend (BV hi) (BV lo) = BV (lo <> hi) +#if !(MIN_VERSION_base(4,11,0))+ mappend = (Semigroup.<>)+#endif instance Show BV where show bv = "0b" ++ [if b then '1' else '0' | b <- toDescBits bv]@@ -398,9 +405,14 @@ bvashr = EOp2 OpAShr bvcomp = EOp2 OpComp +instance Semigroup.Semigroup Expr where+ (<>) = EOp2 OpConcat+ instance Monoid Expr where mempty = EConst mempty- mappend = EOp2 OpConcat+#if !(MIN_VERSION_base(4,11,0))+ mappend = (Semigroup.<>)+#endif instance Bits Expr where (.&.) = bvand
src/ToySolver/BitVector/Solver.hs view
@@ -28,7 +28,6 @@ ) where import Prelude hiding (repeat)-import Control.Applicative hiding (Const (..)) import Control.Monad import qualified Data.Foldable as F import Data.IntMap (IntMap)@@ -39,7 +38,9 @@ import Data.Map (Map) import qualified Data.Map as Map import Data.Maybe+#if !MIN_VERSION_base(4,11,0) import Data.Monoid+#endif import qualified Data.Vector.Generic as VG import qualified Data.Vector.Unboxed as VU import Data.Sequence (Seq)@@ -99,7 +100,7 @@ return $ Var{ varWidth = w, varId = v } data NormalizedRel = NRSLt | NRULt | NREql- deriving (Eq, Ord, Enum, Bounded, Show) + deriving (Eq, Ord, Enum, Bounded, Show) data NormalizedAtom = NormalizedAtom NormalizedRel Expr Expr deriving (Eq, Ord, Show)@@ -503,8 +504,8 @@ -- ------------------------------------------------------------------------ -test1 :: IO ()-test1 = do+_test1 :: IO ()+_test1 = do solver <- newSolver v1 <- newVar solver 8 v2 <- newVar solver 8@@ -513,8 +514,8 @@ m <- getModel solver print m -test2 :: IO ()-test2 = do+_test2 :: IO ()+_test2 = do solver <- newSolver v1 <- newVar solver 8 v2 <- newVar solver 8
src/ToySolver/Combinatorial/HittingSet/FredmanKhachiyan1996.hs view
@@ -89,8 +89,8 @@ lhs = or [is `IntSet.isSubsetOf` xs | is <- Set.toList f] rhs = or [xs `disjoint` js | js <- Set.toList g] -volume :: Set IntSet -> Set IntSet -> Int-volume f g = Set.size f * Set.size g+_volume :: Set IntSet -> Set IntSet -> Int+_volume f g = Set.size f * Set.size g condition_1_1 :: Set IntSet -> Set IntSet -> Bool condition_1_1 f g = all (\is -> all (\js -> is `intersect` js) g) f
src/ToySolver/Combinatorial/HittingSet/GurvichKhachiyan1999.hs view
@@ -141,9 +141,9 @@ evalDNF :: Set IntSet -> IntSet -> Bool evalDNF dnf xs = or [is `IntSet.isSubsetOf` xs | is <- Set.toList dnf] -evalCNF :: Set IntSet -> IntSet -> Bool-evalCNF cnf xs = and [not $ IntSet.null $ is `IntSet.intersection` xs | is <- Set.toList cnf]- +_evalCNF :: Set IntSet -> IntSet -> Bool+_evalCNF cnf xs = and [not $ IntSet.null $ is `IntSet.intersection` xs | is <- Set.toList cnf]+ f, g :: Set IntSet f = Set.fromList $ map IntSet.fromList [[2,4,7], [7,8], [9]]
+ src/ToySolver/Converter.hs view
@@ -0,0 +1,43 @@+{-# OPTIONS_GHC -Wall #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+-- +-- Maintainer : masahiro.sakai@gmail.com+-- Stability : experimental+-- Portability : portable+--+-----------------------------------------------------------------------------+module ToySolver.Converter+ ( module ToySolver.Converter.Base+ , module ToySolver.Converter.GCNF2MaxSAT+ , module ToySolver.Converter.MIP2PB+ , module ToySolver.Converter.NAESAT+ , module ToySolver.Converter.PB+ , module ToySolver.Converter.PB2IP+ , module ToySolver.Converter.PB2LSP+ , module ToySolver.Converter.PB2SMP+ , module ToySolver.Converter.QBF2IPC+ , module ToySolver.Converter.QUBO+ , module ToySolver.Converter.SAT2KSAT+ , module ToySolver.Converter.SAT2MaxCut+ , module ToySolver.Converter.SAT2MaxSAT+ , module ToySolver.Converter.Tseitin+ ) where++import ToySolver.Converter.Base+import ToySolver.Converter.GCNF2MaxSAT+import ToySolver.Converter.MIP2PB+import ToySolver.Converter.NAESAT+import ToySolver.Converter.PB+import ToySolver.Converter.PB2IP+import ToySolver.Converter.PB2LSP+import ToySolver.Converter.PB2SMP+import ToySolver.Converter.QBF2IPC+import ToySolver.Converter.QUBO+import ToySolver.Converter.SAT2KSAT+import ToySolver.Converter.SAT2MaxCut+import ToySolver.Converter.SAT2MaxSAT+import ToySolver.Converter.Tseitin
+ src/ToySolver/Converter/Base.hs view
@@ -0,0 +1,116 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter.Base+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+-- +-- Maintainer : masahiro.sakai@gmail.com+-- Stability : experimental+-- Portability : non-portable+--+-----------------------------------------------------------------------------+module ToySolver.Converter.Base+ ( Transformer (..)+ , ForwardTransformer (..)+ , BackwardTransformer (..)+ , ObjValueTransformer (..)+ , ObjValueForwardTransformer (..)+ , ObjValueBackwardTransformer (..)+ , ComposedTransformer (..)+ , IdentityTransformer (..)+ , ReversedTransformer (..)+ ) where+++class (Eq a, Show a) => Transformer a where+ type Source a+ type Target a++class Transformer a => ForwardTransformer a where+ transformForward :: a -> Source a -> Target a++class Transformer a => BackwardTransformer a where+ transformBackward :: a -> Target a -> Source a+++class ObjValueTransformer a where+ type SourceObjValue a+ type TargetObjValue a++class ObjValueTransformer a => ObjValueForwardTransformer a where+ transformObjValueForward :: a -> SourceObjValue a -> TargetObjValue a++class ObjValueTransformer a => ObjValueBackwardTransformer a where+ transformObjValueBackward :: a -> TargetObjValue a -> SourceObjValue a+++data ComposedTransformer a b = ComposedTransformer a b+ deriving (Eq, Show, Read)++instance (Transformer a, Transformer b, Target a ~ Source b) => Transformer (ComposedTransformer a b) where+ type Source (ComposedTransformer a b) = Source a+ type Target (ComposedTransformer a b) = Target b++instance (ForwardTransformer a, ForwardTransformer b, Target a ~ Source b)+ => ForwardTransformer (ComposedTransformer a b) where+ transformForward (ComposedTransformer a b) = transformForward b . transformForward a++instance (BackwardTransformer a, BackwardTransformer b, Target a ~ Source b)+ => BackwardTransformer (ComposedTransformer a b) where+ transformBackward (ComposedTransformer a b) = transformBackward a . transformBackward b+++instance (ObjValueTransformer a, ObjValueTransformer b, TargetObjValue a ~ SourceObjValue b)+ => ObjValueTransformer (ComposedTransformer a b) where+ type SourceObjValue (ComposedTransformer a b) = SourceObjValue a+ type TargetObjValue (ComposedTransformer a b) = TargetObjValue b++instance (ObjValueForwardTransformer a, ObjValueForwardTransformer b, TargetObjValue a ~ SourceObjValue b)+ => ObjValueForwardTransformer (ComposedTransformer a b) where+ transformObjValueForward (ComposedTransformer a b) = transformObjValueForward b . transformObjValueForward a++instance (ObjValueBackwardTransformer a, ObjValueBackwardTransformer b, TargetObjValue a ~ SourceObjValue b)+ => ObjValueBackwardTransformer (ComposedTransformer a b) where+ transformObjValueBackward (ComposedTransformer a b) = transformObjValueBackward a . transformObjValueBackward b++++data IdentityTransformer a = IdentityTransformer+ deriving (Eq, Show, Read)++instance Transformer (IdentityTransformer a) where+ type Source (IdentityTransformer a) = a+ type Target (IdentityTransformer a) = a++instance ForwardTransformer (IdentityTransformer a) where+ transformForward IdentityTransformer = id++instance BackwardTransformer (IdentityTransformer a) where+ transformBackward IdentityTransformer = id+++data ReversedTransformer t = ReversedTransformer t+ deriving (Eq, Show, Read)++instance Transformer t => Transformer (ReversedTransformer t) where+ type Source (ReversedTransformer t) = Target t+ type Target (ReversedTransformer t) = Source t++instance BackwardTransformer t => ForwardTransformer (ReversedTransformer t) where+ transformForward (ReversedTransformer t) = transformBackward t++instance ForwardTransformer t => BackwardTransformer (ReversedTransformer t) where+ transformBackward (ReversedTransformer t) = transformForward t++instance ObjValueTransformer t => ObjValueTransformer (ReversedTransformer t) where+ type SourceObjValue (ReversedTransformer t) = TargetObjValue t+ type TargetObjValue (ReversedTransformer t) = SourceObjValue t++instance ObjValueBackwardTransformer t => ObjValueForwardTransformer (ReversedTransformer t) where+ transformObjValueForward (ReversedTransformer t) = transformObjValueBackward t++instance ObjValueForwardTransformer t => ObjValueBackwardTransformer (ReversedTransformer t) where+ transformObjValueBackward (ReversedTransformer t) = transformObjValueForward t
src/ToySolver/Converter/GCNF2MaxSAT.hs view
@@ -1,4 +1,5 @@ {-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Converter.GCNF2MaxSAT@@ -7,31 +8,46 @@ -- -- Maintainer : masahiro.sakai@gmail.com -- Stability : experimental--- Portability : portable+-- Portability : non-portable -- ----------------------------------------------------------------------------- module ToySolver.Converter.GCNF2MaxSAT- ( convert+ ( gcnf2maxsat+ , GCNF2MaxSATInfo (..) ) where -import qualified ToySolver.Text.GCNF as GCNF-import qualified ToySolver.Text.MaxSAT as MaxSAT+import qualified Data.Vector.Generic as VG+import ToySolver.Converter.Base+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.SAT.Types as SAT -convert :: GCNF.GCNF -> MaxSAT.WCNF-convert- GCNF.GCNF- { GCNF.numVars = nv- , GCNF.numClauses = nc- , GCNF.lastGroupIndex = lastg- , GCNF.clauses = cs+data GCNF2MaxSATInfo = GCNF2MaxSATInfo !Int+ deriving (Eq, Show, Read)++instance Transformer GCNF2MaxSATInfo where+ type Source GCNF2MaxSATInfo = SAT.Model+ type Target GCNF2MaxSATInfo = SAT.Model++instance BackwardTransformer GCNF2MaxSATInfo where+ transformBackward (GCNF2MaxSATInfo nv1) = SAT.restrictModel nv1++gcnf2maxsat :: CNF.GCNF -> (CNF.WCNF, GCNF2MaxSATInfo)+gcnf2maxsat+ CNF.GCNF+ { CNF.gcnfNumVars = nv+ , CNF.gcnfNumClauses = nc+ , CNF.gcnfLastGroupIndex = lastg+ , CNF.gcnfClauses = cs } =- MaxSAT.WCNF- { MaxSAT.topCost = top- , MaxSAT.clauses = [(top, if g==0 then c else -(nv+g) : c) | (g,c) <- cs] ++ [(1,[v]) | v <- [nv+1..nv+lastg]]- , MaxSAT.numVars = nv + lastg- , MaxSAT.numClauses = nc + lastg- }+ ( CNF.WCNF+ { CNF.wcnfTopCost = top+ , CNF.wcnfClauses = [(top, if g==0 then c else VG.cons (-(nv+g)) c) | (g,c) <- cs] ++ [(1, SAT.packClause [v]) | v <- [nv+1..nv+lastg]]+ , CNF.wcnfNumVars = nv + lastg+ , CNF.wcnfNumClauses = nc + lastg+ }+ , GCNF2MaxSATInfo nv+ ) where- top :: MaxSAT.Weight+ top :: CNF.Weight top = fromIntegral (lastg + 1)
src/ToySolver/Converter/MIP2PB.hs view
@@ -1,5 +1,6 @@ {-# OPTIONS_GHC -Wall #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Converter.MIP2PB@@ -12,7 +13,8 @@ -- ----------------------------------------------------------------------------- module ToySolver.Converter.MIP2PB- ( convert+ ( mip2pb+ , MIP2PBInfo (..) , addMIP ) where @@ -29,6 +31,7 @@ import Data.VectorSpace import qualified Data.PseudoBoolean as PBFile+import ToySolver.Converter.Base import qualified ToySolver.Data.MIP as MIP import ToySolver.Data.OrdRel import qualified ToySolver.SAT.Types as SAT@@ -38,22 +41,42 @@ -- ----------------------------------------------------------------------------- -convert :: MIP.Problem Rational -> Either String (PBFile.Formula, Integer -> Rational, SAT.Model -> Map MIP.Var Integer)-convert mip = runST $ runExceptT $ m+mip2pb :: MIP.Problem Rational -> Either String (PBFile.Formula, MIP2PBInfo)+mip2pb mip = runST $ runExceptT $ m where- m :: ExceptT String (ST s) (PBFile.Formula, Integer -> Rational, SAT.Model -> Map MIP.Var Integer)+ m :: ExceptT String (ST s) (PBFile.Formula, MIP2PBInfo) m = do db <- lift $ newPBStore- (Integer.Expr obj, otrans, mtrans) <- addMIP' db mip+ (Integer.Expr obj, info) <- addMIP' db mip formula <- lift $ getPBFormula db- return $ (formula{ PBFile.pbObjectiveFunction = Just obj }, otrans, mtrans)+ return $ (formula{ PBFile.pbObjectiveFunction = Just obj }, info) +data MIP2PBInfo = MIP2PBInfo (Map MIP.Var Integer.Expr) !Integer+ deriving (Eq, Show)++instance Transformer MIP2PBInfo where+ type Source MIP2PBInfo = Map MIP.Var Integer+ type Target MIP2PBInfo = SAT.Model++instance BackwardTransformer MIP2PBInfo where+ transformBackward (MIP2PBInfo vmap _d) m = fmap (Integer.eval m) vmap++instance ObjValueTransformer MIP2PBInfo where+ type SourceObjValue MIP2PBInfo = Rational+ type TargetObjValue MIP2PBInfo = Integer++instance ObjValueForwardTransformer MIP2PBInfo where+ transformObjValueForward (MIP2PBInfo _vmap d) val = asInteger (val * fromIntegral d)++instance ObjValueBackwardTransformer MIP2PBInfo where+ transformObjValueBackward (MIP2PBInfo _vmap d) val = fromIntegral val / fromIntegral d+ -- ----------------------------------------------------------------------------- -addMIP :: SAT.AddPBNL m enc => enc -> MIP.Problem Rational -> m (Either String (Integer.Expr, Integer -> Rational, SAT.Model -> Map MIP.Var Integer))+addMIP :: SAT.AddPBNL m enc => enc -> MIP.Problem Rational -> m (Either String (Integer.Expr, MIP2PBInfo)) addMIP enc mip = runExceptT $ addMIP' enc mip -addMIP' :: SAT.AddPBNL m enc => enc -> MIP.Problem Rational -> ExceptT String m (Integer.Expr, Integer -> Rational, SAT.Model -> Map MIP.Var Integer)+addMIP' :: SAT.AddPBNL m enc => enc -> MIP.Problem Rational -> ExceptT String m (Integer.Expr, MIP2PBInfo) addMIP' enc mip = do if not (Set.null nivs) then do throwE $ "cannot handle non-integer variables: " ++ intercalate ", " (map MIP.fromVar (Set.toList nivs))@@ -108,23 +131,12 @@ (if MIP.objDir obj == MIP.OptMin then 1 else -1) obj2 = sumV [asInteger (r * fromIntegral d) *^ product [vmap Map.! v | v <- vs] | MIP.Term r vs <- MIP.terms (MIP.objExpr obj)] - let transformObjVal :: Integer -> Rational- transformObjVal val = fromIntegral val / fromIntegral d-- transformModel :: SAT.Model -> Map MIP.Var Integer- transformModel m = Map.fromList- [ (v, Integer.eval m (vmap Map.! v)) | v <- Set.toList ivs ]+ return (obj2, MIP2PBInfo vmap d) - return (obj2, transformObjVal, transformModel) where ivs = MIP.integerVariables mip nivs = MIP.variables mip `Set.difference` ivs - asInteger :: Rational -> Integer- asInteger r- | denominator r /= 1 = error (show r ++ " is not integer")- | otherwise = numerator r- nonAdjacentPairs :: [a] -> [(a,a)] nonAdjacentPairs (x1:x2:xs) = [(x1,x3) | x3 <- xs] ++ nonAdjacentPairs (x2:xs) nonAdjacentPairs _ = []@@ -132,5 +144,12 @@ asBin :: Integer.Expr -> SAT.Lit asBin (Integer.Expr [(1,[lit])]) = lit asBin _ = error "asBin: failure"++-- -----------------------------------------------------------------------------++asInteger :: Rational -> Integer+asInteger r+ | denominator r /= 1 = error (show r ++ " is not integer")+ | otherwise = numerator r -- -----------------------------------------------------------------------------
src/ToySolver/Converter/MIP2SMT.hs view
@@ -12,7 +12,7 @@ -- ----------------------------------------------------------------------------- module ToySolver.Converter.MIP2SMT- ( convert+ ( mip2smt , Options (..) , Language (..) , YicesVersion (..)@@ -312,8 +312,8 @@ nonAdjacentPairs (x1:x2:xs) = [(x1,x3) | x3 <- xs] ++ nonAdjacentPairs (x2:xs) nonAdjacentPairs _ = [] -convert :: Options -> MIP.Problem Rational -> Builder-convert opt mip =+mip2smt :: Options -> MIP.Problem Rational -> Builder+mip2smt opt mip = mconcat $ map (<> B.singleton '\n') $ options ++ set_logic ++ defs ++ map (assert opt) (conditions opt False env mip) ++ [ assert opt (optimality, Nothing) | optOptimize opt ]@@ -401,10 +401,10 @@ testFile :: FilePath -> IO () testFile fname = do mip <- MIP.readLPFile def fname- TLIO.putStrLn $ B.toLazyText $ convert def (fmap toRational mip)+ TLIO.putStrLn $ B.toLazyText $ mip2smt def (fmap toRational mip) test :: IO ()-test = TLIO.putStrLn $ B.toLazyText $ convert def testdata+test = TLIO.putStrLn $ B.toLazyText $ mip2smt def testdata testdata :: MIP.Problem Rational Right testdata = fmap (fmap toRational) $ MIP.parseLPString def "test" $ unlines
− src/ToySolver/Converter/MaxSAT2IP.hs
@@ -1,25 +0,0 @@-{-# OPTIONS_GHC -Wall #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.MaxSAT2IP--- Copyright : (c) Masahiro Sakai 2011-2014--- License : BSD-style--- --- Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : portable----------------------------------------------------------------------------------module ToySolver.Converter.MaxSAT2IP- ( convert- ) where--import Data.Map (Map)-import qualified ToySolver.Data.MIP as MIP-import qualified ToySolver.Text.MaxSAT as MaxSAT-import qualified ToySolver.SAT.Types as SAT-import qualified ToySolver.Converter.MaxSAT2WBO as MaxSAT2WBO-import qualified ToySolver.Converter.PB2IP as PB2IP--convert :: Bool -> MaxSAT.WCNF -> (MIP.Problem Integer, SAT.Model -> Map MIP.Var Rational, Map MIP.Var Rational -> SAT.Model)-convert useIndicator wcnf = PB2IP.convertWBO useIndicator (MaxSAT2WBO.convert wcnf)
− src/ToySolver/Converter/MaxSAT2WBO.hs
@@ -1,40 +0,0 @@-{-# OPTIONS_GHC -Wall #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.MaxSAT2WBO--- Copyright : (c) Masahiro Sakai 2013--- License : BSD-style--- --- Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : portable----------------------------------------------------------------------------------module ToySolver.Converter.MaxSAT2WBO- ( convert- ) where--import qualified Data.PseudoBoolean as PBFile-import qualified ToySolver.Text.MaxSAT as MaxSAT--convert :: MaxSAT.WCNF -> PBFile.SoftFormula-convert- MaxSAT.WCNF- { MaxSAT.topCost = top- , MaxSAT.clauses = cs- , MaxSAT.numVars = nv- , MaxSAT.numClauses = nc- } =- PBFile.SoftFormula- { PBFile.wboTopCost = Nothing- , PBFile.wboConstraints = map f cs- , PBFile.wboNumVars = nv- , PBFile.wboNumConstraints = nc- }- where- f (w,ls)- | w>=top = (Nothing, p) -- hard constraint- | otherwise = (Just w, p) -- soft constraint- where- p = ([(1,[l]) | l <- ls], PBFile.Ge, 1)-
+ src/ToySolver/Converter/NAESAT.hs view
@@ -0,0 +1,214 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter.NAESAT+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-- Not-All-Equal SAT problems.+--+-----------------------------------------------------------------------------+module ToySolver.Converter.NAESAT+ (+ -- * Definition of NAE (Not-All-Equall) SAT problems.+ NAESAT+ , evalNAESAT+ , NAEClause+ , evalNAEClause++ -- * Conversion with SAT problem+ , SAT2NAESATInfo (..)+ , sat2naesat+ , NAESAT2SATInfo+ , naesat2sat++ -- * Conversion from general NAE-SAT to NAE-k-SAT+ , NAESAT2NAEKSATInfo (..)+ , naesat2naeksat++ -- ** NAE-SAT to MAX-2-SAT+ , NAESAT2Max2SATInfo+ , naesat2max2sat+ , NAE3SAT2Max2SATInfo+ , nae3sat2max2sat+ ) where++import Control.Monad.State.Strict+import Data.Array.Unboxed+import qualified Data.IntMap as IntMap+import qualified Data.Vector.Generic as VG+import qualified Data.Vector.Unboxed as VU+import ToySolver.Converter.Base+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.SAT.Types as SAT++type NAESAT = (Int, [NAEClause])++evalNAESAT :: SAT.IModel m => m -> NAESAT -> Bool+evalNAESAT m (_,cs) = all (evalNAEClause m) cs++type NAEClause = VU.Vector SAT.Lit++evalNAEClause :: SAT.IModel m => m -> NAEClause -> Bool+evalNAEClause m c =+ VG.any (SAT.evalLit m) c && VG.any (not . SAT.evalLit m) c++-- ------------------------------------------------------------------------++-- | Information of 'sat2naesat' conversion+newtype SAT2NAESATInfo = SAT2NAESATInfo SAT.Var+ deriving (Eq, Show, Read)++-- | Convert a CNF formula φ to an equisatifiable NAE-SAT formula ψ,+-- together with a 'SAT2NAESATInfo'+sat2naesat :: CNF.CNF -> (NAESAT, SAT2NAESATInfo)+sat2naesat cnf = (ret, SAT2NAESATInfo z)+ where+ z = CNF.cnfNumVars cnf + 1+ ret =+ ( CNF.cnfNumVars cnf + 1+ , [VG.snoc clause z | clause <- CNF.cnfClauses cnf]+ )++instance Transformer SAT2NAESATInfo where+ type Source SAT2NAESATInfo = SAT.Model+ type Target SAT2NAESATInfo = SAT.Model++instance ForwardTransformer SAT2NAESATInfo where+ transformForward (SAT2NAESATInfo z) m = array (1,z) $ (z,False) : assocs m++instance BackwardTransformer SAT2NAESATInfo where+ transformBackward (SAT2NAESATInfo z) m = + SAT.restrictModel (z - 1) $+ if SAT.evalVar m z then amap not m else m++-- | Information of 'naesat2sat' conversion+type NAESAT2SATInfo = IdentityTransformer SAT.Model++-- | Convert a NAE-SAT formula φ to an equisatifiable CNF formula ψ,+-- together with a 'NAESAT2SATInfo'+naesat2sat :: NAESAT -> (CNF.CNF, NAESAT2SATInfo)+naesat2sat (n,cs) =+ ( CNF.CNF+ { CNF.cnfNumVars = n+ , CNF.cnfNumClauses = length cs * 2+ , CNF.cnfClauses = concat [[c, VG.map negate c] | c <- cs]+ }+ , IdentityTransformer+ )++-- ------------------------------------------------------------------------++-- Information of 'naesat2naeksta' conversion+data NAESAT2NAEKSATInfo = NAESAT2NAEKSATInfo !Int !Int [(SAT.Var, NAEClause, NAEClause)]+ deriving (Eq, Show, Read)++naesat2naeksat :: Int -> NAESAT -> (NAESAT, NAESAT2NAEKSATInfo)+naesat2naeksat k _ | k < 3 = error "naesat2naeksat: k must be >=3"+naesat2naeksat k (n,cs) = ((n', cs'), NAESAT2NAEKSATInfo n n' (reverse table))+ where+ (cs',(n',table)) = flip runState (n,[]) $ do+ liftM concat $ forM cs $ \c -> do+ let go c' r =+ if VG.length c' <= k then do+ return $ reverse (c' : r)+ else do+ let (cs1, cs2) = VG.splitAt (k - 1) c'+ (i, tbl) <- get+ let w = i+1+ seq w $ put (w, (w,cs1,cs2) : tbl)+ go (VG.cons (-w) cs2) (VG.snoc cs1 w : r)+ go c []++instance Transformer NAESAT2NAEKSATInfo where+ type Source NAESAT2NAEKSATInfo = SAT.Model+ type Target NAESAT2NAEKSATInfo = SAT.Model++instance ForwardTransformer NAESAT2NAEKSATInfo where+ transformForward (NAESAT2NAEKSATInfo _n1 n2 table) m =+ array (1,n2) (go (IntMap.fromList (assocs m)) table)+ where+ go im [] = IntMap.toList im+ go im ((w,cs1,cs2) : tbl) = go (IntMap.insert w val im) tbl+ where+ ev x+ | x > 0 = im IntMap.! x+ | otherwise = not $ im IntMap.! (- x)+ needTrue = VG.all ev cs2 || VG.all (not . ev) cs1+ needFalse = VG.all ev cs1 || VG.all (not . ev) cs2+ val+ | needTrue && needFalse = True -- error "naesat2naeksat_forward: invalid model"+ | needTrue = True+ | needFalse = False+ | otherwise = False++instance BackwardTransformer NAESAT2NAEKSATInfo where+ transformBackward (NAESAT2NAEKSATInfo n1 _n2 _table) = SAT.restrictModel n1++-- ------------------------------------------------------------------------++type NAESAT2Max2SATInfo = ComposedTransformer NAESAT2NAEKSATInfo NAE3SAT2Max2SATInfo++naesat2max2sat :: NAESAT -> ((CNF.WCNF, Integer), NAESAT2Max2SATInfo)+naesat2max2sat x = (x2, (ComposedTransformer info1 info2))+ where+ (x1, info1) = naesat2naeksat 3 x+ (x2, info2) = nae3sat2max2sat x1++-- ------------------------------------------------------------------------++type NAE3SAT2Max2SATInfo = IdentityTransformer SAT.Model++-- Original nae-sat problem is satisfiable iff MAX-2-SAT problem has solution with cost <=threshold.+nae3sat2max2sat :: NAESAT -> ((CNF.WCNF, Integer), NAE3SAT2Max2SATInfo)+nae3sat2max2sat (n,cs)+ | any (\c -> VG.length c < 2) cs =+ ( ( CNF.WCNF+ { CNF.wcnfTopCost = 2+ , CNF.wcnfNumVars = n+ , CNF.wcnfClauses = [(1, SAT.packClause [])]+ , CNF.wcnfNumClauses = 1+ }+ , 0+ )+ , IdentityTransformer+ )+ | otherwise =+ ( ( CNF.WCNF+ { CNF.wcnfTopCost = fromIntegral nc' + 1+ , CNF.wcnfNumVars = n+ , CNF.wcnfClauses = cs'+ , CNF.wcnfNumClauses = nc'+ }+ , t+ )+ , IdentityTransformer+ )+ where+ nc' = length cs'+ (cs', t) = foldl f ([],0) cs+ where+ f :: ([CNF.WeightedClause], Integer) -> VU.Vector SAT.Lit -> ([CNF.WeightedClause], Integer)+ f (cs, !t) c =+ case SAT.unpackClause c of+ [] -> error "nae3sat2max2sat: should not happen"+ [_] -> error "nae3sat2max2sat: should not happen"+ [_,_] ->+ ( [(1, c), (1, VG.map negate c)] ++ cs+ , t+ )+ [l0,l1,l2] ->+ ( concat [[(1, SAT.packClause [a,b]), (1, SAT.packClause [-a,-b])] | (a,b) <- [(l0,l1),(l1,l2),(l2,l0)]] ++ cs+ , t + 1+ )+ _ -> error "nae3sat2max2sat: cannot handle nae-clause of size >3"++-- ------------------------------------------------------------------------+
src/ToySolver/Converter/ObjType.hs view
@@ -15,4 +15,4 @@ ) where data ObjType = ObjNone | ObjMaxOne | ObjMaxZero- deriving Eq+ deriving (Eq, Ord, Enum, Bounded, Show)
+ src/ToySolver/Converter/PB.hs view
@@ -0,0 +1,674 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter.PB+-- Copyright : (c) Masahiro Sakai 2013,2016-2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : experimental+-- Portability : non-portable+--+-----------------------------------------------------------------------------+module ToySolver.Converter.PB+ ( module ToySolver.Converter.Base+ , module ToySolver.Converter.Tseitin++ -- * Normalization of PB/WBO problems+ , normalizePB+ , normalizeWBO++ -- * Linealization of PB/WBO problems+ , linearizePB+ , linearizeWBO+ , PBLinearizeInfo++ -- * Quadratization of PB problems+ , quadratizePB+ , quadratizePB'+ , PBQuadratizeInfo++ -- * Converting inequality constraints into equality constraints+ , inequalitiesToEqualitiesPB+ , PBInequalitiesToEqualitiesInfo++ -- * Converting constraints into penalty terms in objective function+ , unconstrainPB+ , PBUnconstrainInfo++ -- * PB↔WBO conversion+ , pb2wbo+ , PB2WBOInfo+ , wbo2pb+ , WBO2PBInfo (..)+ , addWBO++ -- * SAT↔PB conversion+ , sat2pb+ , SAT2PBInfo+ , pb2sat+ , PB2SATInfo++ -- * MaxSAT↔WBO conversion+ , maxsat2wbo+ , MaxSAT2WBOInfo+ , wbo2maxsat+ , WBO2MaxSATInfo++ -- * PB→QUBO conversion+ , pb2qubo'+ , PB2QUBOInfo'+ ) where++import Control.Monad+import Control.Monad.Primitive+import Control.Monad.ST+import Data.Array.IArray+import Data.Bits+import qualified Data.Foldable as F+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IntMap+import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet+import Data.List+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe+#if !MIN_VERSION_base(4,11,0)+import Data.Monoid+#endif+import Data.Primitive.MutVar+import qualified Data.Sequence as Seq+import Data.Set (Set)+import qualified Data.Set as Set+import qualified Data.PseudoBoolean as PBFile++import ToySolver.Converter.Base+import qualified ToySolver.Converter.PB.Internal.Product as Product+import ToySolver.Converter.Tseitin+import ToySolver.Data.BoolExpr+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.SAT.Types as SAT+import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin+import qualified ToySolver.SAT.Encoder.PB as PB+import qualified ToySolver.SAT.Encoder.PBNLC as PBNLC+import ToySolver.SAT.Store.CNF+import ToySolver.SAT.Store.PB++-- -----------------------------------------------------------------------------++-- XXX: we do not normalize objective function, because normalization might+-- introduce constant terms, but OPB file format does not allow constant terms.+--+-- Options:+-- (1) not normalize objective function (current implementation),+-- (2) normalize and simply delete constant terms (in pseudo-boolean package?),+-- (3) normalize and introduce dummy variable to make constant terms+-- into non-constant terms (in pseudo-boolean package?).+normalizePB :: PBFile.Formula -> PBFile.Formula+normalizePB formula =+ formula+ { PBFile.pbConstraints =+ map normalizePBConstraint (PBFile.pbConstraints formula)+ }++normalizeWBO :: PBFile.SoftFormula -> PBFile.SoftFormula+normalizeWBO formula =+ formula+ { PBFile.wboConstraints =+ map (\(w,constr) -> (w, normalizePBConstraint constr)) (PBFile.wboConstraints formula)+ }++normalizePBConstraint :: PBFile.Constraint -> PBFile.Constraint+normalizePBConstraint (lhs,op,rhs) =+ case mapAccumL h 0 lhs of+ (offset, lhs') -> (lhs', op, rhs - offset)+ where+ h s (w,[x]) | x < 0 = (s+w, (-w,[-x]))+ h s t = (s,t)++-- -----------------------------------------------------------------------------++type PBLinearizeInfo = TseitinInfo++linearizePB :: PBFile.Formula -> Bool -> (PBFile.Formula, PBLinearizeInfo)+linearizePB formula usePB = runST $ do+ db <- newPBStore+ SAT.newVars_ db (PBFile.pbNumVars formula)+ tseitin <- Tseitin.newEncoderWithPBLin db+ Tseitin.setUsePB tseitin usePB+ pbnlc <- PBNLC.newEncoder db tseitin+ cs' <- forM (PBFile.pbConstraints formula) $ \(lhs,op,rhs) -> do+ let p = case op of+ PBFile.Ge -> Tseitin.polarityPos+ PBFile.Eq -> Tseitin.polarityBoth+ lhs' <- PBNLC.linearizePBSumWithPolarity pbnlc p lhs+ return ([(c,[l]) | (c,l) <- lhs'],op,rhs)+ obj' <-+ case PBFile.pbObjectiveFunction formula of+ Nothing -> return Nothing+ Just obj -> do+ obj' <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityNeg obj+ return $ Just [(c, [l]) | (c,l) <- obj']+ formula' <- getPBFormula db+ defs <- Tseitin.getDefinitions tseitin+ return $+ ( formula'+ { PBFile.pbObjectiveFunction = obj'+ , PBFile.pbConstraints = cs' ++ PBFile.pbConstraints formula'+ , PBFile.pbNumConstraints = PBFile.pbNumConstraints formula + PBFile.pbNumConstraints formula'+ }+ , TseitinInfo (PBFile.pbNumVars formula) (PBFile.pbNumVars formula') defs+ )++-- -----------------------------------------------------------------------------++linearizeWBO :: PBFile.SoftFormula -> Bool -> (PBFile.SoftFormula, PBLinearizeInfo)+linearizeWBO formula usePB = runST $ do+ db <- newPBStore+ SAT.newVars_ db (PBFile.wboNumVars formula)+ tseitin <- Tseitin.newEncoderWithPBLin db+ Tseitin.setUsePB tseitin usePB+ pbnlc <- PBNLC.newEncoder db tseitin+ cs' <- forM (PBFile.wboConstraints formula) $ \(cost,(lhs,op,rhs)) -> do+ let p = case op of+ PBFile.Ge -> Tseitin.polarityPos+ PBFile.Eq -> Tseitin.polarityBoth+ lhs' <- PBNLC.linearizePBSumWithPolarity pbnlc p lhs+ return (cost,([(c,[l]) | (c,l) <- lhs'],op,rhs))+ formula' <- getPBFormula db+ defs <- Tseitin.getDefinitions tseitin+ return $+ ( PBFile.SoftFormula+ { PBFile.wboTopCost = PBFile.wboTopCost formula+ , PBFile.wboConstraints = cs' ++ [(Nothing, constr) | constr <- PBFile.pbConstraints formula']+ , PBFile.wboNumVars = PBFile.pbNumVars formula'+ , PBFile.wboNumConstraints = PBFile.wboNumConstraints formula + PBFile.pbNumConstraints formula'+ }+ , TseitinInfo (PBFile.wboNumVars formula) (PBFile.pbNumVars formula') defs+ )++-- -----------------------------------------------------------------------------++-- | Quandratize PBO/PBS problem without introducing additional constraints.+quadratizePB :: PBFile.Formula -> ((PBFile.Formula, Integer), PBQuadratizeInfo)+quadratizePB formula = quadratizePB' (formula, SAT.pbUpperBound obj)+ where+ obj = fromMaybe [] $ PBFile.pbObjectiveFunction formula++-- | Quandratize PBO/PBS problem without introducing additional constraints.+quadratizePB' :: (PBFile.Formula, Integer) -> ((PBFile.Formula, Integer), PBQuadratizeInfo)+quadratizePB' (formula, maxObj) =+ ( ( PBFile.Formula+ { PBFile.pbObjectiveFunction = Just $ conv obj ++ penalty+ , PBFile.pbConstraints = [(conv lhs, op, rhs) | (lhs,op,rhs) <- PBFile.pbConstraints formula]+ , PBFile.pbNumVars = nv2+ , PBFile.pbNumConstraints = PBFile.pbNumConstraints formula+ }+ , maxObj+ )+ , PBQuadratizeInfo $ TseitinInfo nv1 nv2 [(v, And [Atom l1, Atom l2]) | (v, (l1,l2)) <- prodDefs]+ )+ where+ nv1 = PBFile.pbNumVars formula+ nv2 = PBFile.pbNumVars formula + Set.size termsToReplace++ degGe3Terms :: Set IntSet+ degGe3Terms = collectDegGe3Terms formula+ + m :: Map IntSet (IntSet,IntSet)+ m = Product.decomposeToBinaryProducts degGe3Terms++ termsToReplace :: Set IntSet+ termsToReplace = go ts0 Set.empty+ where+ ts0 = concat [[t1,t2] | t <- Set.toList degGe3Terms, let (t1,t2) = m Map.! t]+ go [] !ret = ret+ go (t : ts) !ret+ | IntSet.size t < 2 = go ts ret+ | t `Set.member` ret = go ts ret+ | otherwise =+ case Map.lookup t m of+ Nothing -> error "quadratizePB.termsToReplace: should not happen"+ Just (t1,t2) -> go (t1 : t2 : ts) (Set.insert t ret)++ fromV :: IntMap IntSet+ toV :: Map IntSet Int+ (fromV, toV) = (IntMap.fromList l, Map.fromList [(s,v) | (v,s) <- l])+ where+ l = zip [PBFile.pbNumVars formula + 1 ..] (Set.toList termsToReplace)++ prodDefs :: [(SAT.Var, (SAT.Var, SAT.Var))]+ prodDefs = [(v, (f t1, f t2)) | (v,t) <- IntMap.toList fromV, let (t1,t2) = m Map.! t]+ where+ f t+ | IntSet.size t == 1 = head (IntSet.toList t)+ | otherwise = + case Map.lookup t toV of+ Nothing -> error "quadratizePB.prodDefs: should not happen"+ Just v -> v++ obj :: PBFile.Sum+ obj = fromMaybe [] $ PBFile.pbObjectiveFunction formula++ minObj :: Integer+ minObj = SAT.pbLowerBound obj++ penalty :: PBFile.Sum+ penalty = [(w * w2, ts) | (w,ts) <- concat [p x y z | (z,(x,y)) <- prodDefs]]+ where+ -- The penalty function P(x,y,z) = xy − 2xz − 2yz + 3z is such that+ -- P(x,y,z)=0 when z⇔xy and P(x,y,z)>0 when z⇎xy.+ p x y z = [(1,[x,y]), (-2,[x,z]), (-2,[y,z]), (3,[z])]+ w2 = max (maxObj - minObj) 0 + 1++ conv :: PBFile.Sum -> PBFile.Sum+ conv s = [(w, f t) | (w,t) <- s]+ where+ f t =+ case Map.lookup t' toV of+ Just v -> [v]+ Nothing+ | IntSet.size t' >= 3 -> map g [t1, t2]+ | otherwise -> t+ where+ t' = IntSet.fromList t+ (t1, t2) = m Map.! t'+ g t+ | IntSet.size t == 1 = head $ IntSet.toList t+ | otherwise = toV Map.! t+++collectDegGe3Terms :: PBFile.Formula -> Set IntSet+collectDegGe3Terms formula = Set.fromList [t' | t <- terms, let t' = IntSet.fromList t, IntSet.size t' >= 3]+ where+ sums = maybeToList (PBFile.pbObjectiveFunction formula) +++ [lhs | (lhs,_,_) <- PBFile.pbConstraints formula]+ terms = [t | s <- sums, (_,t) <- s]++newtype PBQuadratizeInfo = PBQuadratizeInfo TseitinInfo+ deriving (Eq, Show)++instance Transformer PBQuadratizeInfo where+ type Source PBQuadratizeInfo = SAT.Model+ type Target PBQuadratizeInfo = SAT.Model++instance ForwardTransformer PBQuadratizeInfo where+ transformForward (PBQuadratizeInfo info) = transformForward info++instance BackwardTransformer PBQuadratizeInfo where+ transformBackward (PBQuadratizeInfo info) = transformBackward info++instance ObjValueTransformer PBQuadratizeInfo where+ type SourceObjValue PBQuadratizeInfo = Integer+ type TargetObjValue PBQuadratizeInfo = Integer++instance ObjValueForwardTransformer PBQuadratizeInfo where+ transformObjValueForward _ = id++instance ObjValueBackwardTransformer PBQuadratizeInfo where+ transformObjValueBackward _ = id++-- -----------------------------------------------------------------------------++-- | Convert inequality constraints into equality constraints by introducing surpass variables.+inequalitiesToEqualitiesPB :: PBFile.Formula -> (PBFile.Formula, PBInequalitiesToEqualitiesInfo)+inequalitiesToEqualitiesPB formula = runST $ do+ db <- newPBStore+ SAT.newVars_ db (PBFile.pbNumVars formula)++ defs <- liftM catMaybes $ forM (PBFile.pbConstraints formula) $ \constr -> do+ case constr of+ (lhs, PBFile.Eq, rhs) -> do+ SAT.addPBNLExactly db lhs rhs+ return Nothing+ (lhs, PBFile.Ge, rhs) -> do+ case asClause (lhs,rhs) of+ Just clause -> do+ SAT.addPBNLExactly db [(1, [- l | l <- clause])] 0+ return Nothing+ Nothing -> do+ let maxSurpass = max (SAT.pbUpperBound lhs - rhs) 0+ maxSurpassNBits = head [i | i <- [0..], maxSurpass < bit i]+ vs <- SAT.newVars db maxSurpassNBits+ SAT.addPBNLExactly db (lhs ++ [(-c,[x]) | (c,x) <- zip (iterate (*2) 1) vs]) rhs+ if maxSurpassNBits > 0 then do+ return $ Just (lhs, rhs, vs)+ else+ return Nothing++ formula' <- getPBFormula db+ return+ ( formula'{ PBFile.pbObjectiveFunction = PBFile.pbObjectiveFunction formula }+ , PBInequalitiesToEqualitiesInfo (PBFile.pbNumVars formula) (PBFile.pbNumVars formula') defs+ )+ where+ asLinSum :: SAT.PBSum -> Maybe (SAT.PBLinSum, Integer)+ asLinSum s = do+ ret <- forM s $ \(c, ls) -> do+ case ls of+ [] -> return (Nothing, c)+ [l] -> return (Just (c,l), 0)+ _ -> mzero+ return (catMaybes (map fst ret), sum (map snd ret))++ asClause :: (SAT.PBSum, Integer) -> Maybe SAT.Clause+ asClause (lhs, rhs) = do+ (lhs', off) <- asLinSum lhs+ let rhs' = rhs - off+ case SAT.normalizePBLinAtLeast (lhs', rhs') of+ (lhs'', 1) | all (\(c,_) -> c == 1) lhs'' -> return (map snd lhs'')+ _ -> mzero++data PBInequalitiesToEqualitiesInfo+ = PBInequalitiesToEqualitiesInfo !Int !Int [(PBFile.Sum, Integer, [SAT.Var])]+ deriving (Eq, Show)++instance Transformer PBInequalitiesToEqualitiesInfo where+ type Source PBInequalitiesToEqualitiesInfo = SAT.Model+ type Target PBInequalitiesToEqualitiesInfo = SAT.Model++instance ForwardTransformer PBInequalitiesToEqualitiesInfo where+ transformForward (PBInequalitiesToEqualitiesInfo _nv1 nv2 defs) m =+ array (1, nv2) $ assocs m ++ [(v, testBit n i) | (lhs, rhs, vs) <- defs, let n = SAT.evalPBSum m lhs - rhs, (i,v) <- zip [0..] vs]++instance BackwardTransformer PBInequalitiesToEqualitiesInfo where+ transformBackward (PBInequalitiesToEqualitiesInfo nv1 _nv2 _defs) = SAT.restrictModel nv1++instance ObjValueTransformer PBInequalitiesToEqualitiesInfo where+ type SourceObjValue PBInequalitiesToEqualitiesInfo = Integer+ type TargetObjValue PBInequalitiesToEqualitiesInfo = Integer++instance ObjValueForwardTransformer PBInequalitiesToEqualitiesInfo where+ transformObjValueForward _ = id++instance ObjValueBackwardTransformer PBInequalitiesToEqualitiesInfo where+ transformObjValueBackward _ = id++-- -----------------------------------------------------------------------------++unconstrainPB :: PBFile.Formula -> ((PBFile.Formula, Integer), PBUnconstrainInfo)+unconstrainPB formula = (unconstrainPB' formula', PBUnconstrainInfo info)+ where+ (formula', info) = inequalitiesToEqualitiesPB formula++newtype PBUnconstrainInfo = PBUnconstrainInfo PBInequalitiesToEqualitiesInfo+ deriving (Eq, Show)++instance Transformer PBUnconstrainInfo where+ -- type Source PBUnconstrainInfo = Source PBInequalitiesToEqualitiesInfo+ type Source PBUnconstrainInfo = SAT.Model+ -- type Target PBUnconstrainInfo = Target PBInequalitiesToEqualitiesInfo+ type Target PBUnconstrainInfo = SAT.Model++instance ForwardTransformer PBUnconstrainInfo where+ transformForward (PBUnconstrainInfo info) = transformForward info++instance BackwardTransformer PBUnconstrainInfo where+ transformBackward (PBUnconstrainInfo info) = transformBackward info++instance ObjValueTransformer PBUnconstrainInfo where+ -- type SourceObjValue PBUnconstrainInfo = SourceObjValue PBInequalitiesToEqualitiesInfo+ type SourceObjValue PBUnconstrainInfo = Integer+ -- type TargetObjValue PBUnconstrainInfo = TargetObjValue PBInequalitiesToEqualitiesInfo+ type TargetObjValue PBUnconstrainInfo = Integer++instance ObjValueForwardTransformer PBUnconstrainInfo where+ transformObjValueForward (PBUnconstrainInfo info) = transformObjValueForward info++instance ObjValueBackwardTransformer PBUnconstrainInfo where+ transformObjValueBackward (PBUnconstrainInfo info) = transformObjValueBackward info++unconstrainPB' :: PBFile.Formula -> (PBFile.Formula, Integer)+unconstrainPB' formula =+ ( formula+ { PBFile.pbObjectiveFunction = Just $ obj1 ++ obj2+ , PBFile.pbConstraints = []+ , PBFile.pbNumConstraints = 0+ }+ , obj1ub+ )+ where+ obj1 = fromMaybe [] (PBFile.pbObjectiveFunction formula)+ obj1ub = SAT.pbUpperBound obj1+ obj1lb = SAT.pbLowerBound obj1+ p = obj1ub - obj1lb + 1+ obj2 = [(p*c, IntSet.toList ls) | (ls, c) <- Map.toList obj2', c /= 0]+ obj2' = Map.unionsWith (+) [sq ((-rhs, []) : lhs) | (lhs, PBFile.Eq, rhs) <- PBFile.pbConstraints formula]+ sq ts = Map.fromListWith (+) $ do+ (c1,ls1) <- ts+ (c2,ls2) <- ts+ let ls3 = IntSet.fromList ls1 `IntSet.union` IntSet.fromList ls2+ guard $ not $ isFalse ls3+ return (ls3, c1*c2)+ isFalse ls = not $ IntSet.null $ ls `IntSet.intersection` IntSet.map negate ls++-- -----------------------------------------------------------------------------++pb2qubo' :: PBFile.Formula -> ((PBFile.Formula, Integer), PB2QUBOInfo')+pb2qubo' formula = ((formula2, th2), ComposedTransformer info1 info2)+ where+ ((formula1, th1), info1) = unconstrainPB formula+ ((formula2, th2), info2) = quadratizePB' (formula1, th1)++type PB2QUBOInfo' = ComposedTransformer PBUnconstrainInfo PBQuadratizeInfo++-- -----------------------------------------------------------------------------++type PB2WBOInfo = IdentityTransformer SAT.Model++pb2wbo :: PBFile.Formula -> (PBFile.SoftFormula, PB2WBOInfo)+pb2wbo formula+ = ( PBFile.SoftFormula+ { PBFile.wboTopCost = Nothing+ , PBFile.wboConstraints = cs1 ++ cs2+ , PBFile.wboNumVars = PBFile.pbNumVars formula+ , PBFile.wboNumConstraints = PBFile.pbNumConstraints formula + length cs2+ }+ , IdentityTransformer+ )+ where+ cs1 = [(Nothing, c) | c <- PBFile.pbConstraints formula]+ cs2 = case PBFile.pbObjectiveFunction formula of+ Nothing -> []+ Just e ->+ [ if w >= 0+ then (Just w, ([(-1,ls)], PBFile.Ge, 0))+ else (Just (abs w), ([(1,ls)], PBFile.Ge, 1))+ | (w,ls) <- e+ ]++wbo2pb :: PBFile.SoftFormula -> (PBFile.Formula, WBO2PBInfo)+wbo2pb wbo = runST $ do+ let nv = PBFile.wboNumVars wbo+ db <- newPBStore+ (obj, defs) <- addWBO db wbo + formula <- getPBFormula db+ return+ ( formula{ PBFile.pbObjectiveFunction = Just obj }+ , WBO2PBInfo nv (PBFile.pbNumVars formula) defs+ )++data WBO2PBInfo = WBO2PBInfo !Int !Int [(SAT.Var, PBFile.Constraint)]+ deriving (Eq, Show)++instance Transformer WBO2PBInfo where+ type Source WBO2PBInfo = SAT.Model+ type Target WBO2PBInfo = SAT.Model++instance ForwardTransformer WBO2PBInfo where+ transformForward (WBO2PBInfo _nv1 nv2 defs) m =+ array (1, nv2) $ assocs m ++ [(v, SAT.evalPBConstraint m constr) | (v, constr) <- defs]++instance BackwardTransformer WBO2PBInfo where+ transformBackward (WBO2PBInfo nv1 _nv2 _defs) = SAT.restrictModel nv1++addWBO :: (PrimMonad m, SAT.AddPBNL m enc) => enc -> PBFile.SoftFormula -> m (SAT.PBSum, [(SAT.Var, PBFile.Constraint)])+addWBO db wbo = do+ SAT.newVars_ db $ PBFile.wboNumVars wbo++ objRef <- newMutVar []+ defsRef <- newMutVar []+ forM_ (PBFile.wboConstraints wbo) $ \(cost, constr@(lhs,op,rhs)) -> do+ case cost of+ Nothing -> do+ case op of+ PBFile.Ge -> SAT.addPBNLAtLeast db lhs rhs+ PBFile.Eq -> SAT.addPBNLExactly db lhs rhs+ Just w -> do+ case op of+ PBFile.Ge -> do+ case lhs of+ [(1,ls)] | rhs == 1 -> do+ -- ∧L ≥ 1 ⇔ ∧L+ -- obj += w * (1 - ∧L)+ modifyMutVar objRef (\obj -> (w,[]) : (-w,ls) : obj)+ [(-1,ls)] | rhs == 0 -> do+ -- -1*∧L ≥ 0 ⇔ (1 - ∧L) ≥ 1 ⇔ ¬∧L+ -- obj += w * ∧L+ modifyMutVar objRef ((w,ls) :)+ _ | and [c==1 && length ls == 1 | (c,ls) <- lhs] && rhs == 1 -> do+ -- ∑L ≥ 1 ⇔ ∨L ⇔ ¬∧¬L+ -- obj += w * ∧¬L+ modifyMutVar objRef ((w, [-l | (_,[l]) <- lhs]) :)+ _ -> do+ sel <- SAT.newVar db+ SAT.addPBNLAtLeastSoft db sel lhs rhs+ modifyMutVar objRef ((w,[-sel]) :)+ modifyMutVar defsRef ((sel,constr) :)+ PBFile.Eq -> do+ sel <- SAT.newVar db+ SAT.addPBNLExactlySoft db sel lhs rhs+ modifyMutVar objRef ((w,[-sel]) :)+ modifyMutVar defsRef ((sel,constr) :)+ obj <- liftM reverse $ readMutVar objRef+ defs <- liftM reverse $ readMutVar defsRef++ case PBFile.wboTopCost wbo of+ Nothing -> return ()+ Just t -> SAT.addPBNLAtMost db obj (t - 1)++ return (obj, defs)++-- -----------------------------------------------------------------------------++type SAT2PBInfo = IdentityTransformer SAT.Model++sat2pb :: CNF.CNF -> (PBFile.Formula, SAT2PBInfo)+sat2pb cnf+ = ( PBFile.Formula+ { PBFile.pbObjectiveFunction = Nothing+ , PBFile.pbConstraints = map f (CNF.cnfClauses cnf)+ , PBFile.pbNumVars = CNF.cnfNumVars cnf+ , PBFile.pbNumConstraints = CNF.cnfNumClauses cnf+ }+ , IdentityTransformer+ )+ where+ f clause = ([(1,[l]) | l <- SAT.unpackClause clause], PBFile.Ge, 1)++type PB2SATInfo = TseitinInfo++-- | Convert a pseudo boolean formula φ to a equisatisfiable CNF formula ψ+-- together with two functions f and g such that:+--+-- * if M ⊨ φ then f(M) ⊨ ψ+--+-- * if M ⊨ ψ then g(M) ⊨ φ+-- +pb2sat :: PBFile.Formula -> (CNF.CNF, PB2SATInfo)+pb2sat formula = runST $ do+ db <- newCNFStore+ let nv1 = PBFile.pbNumVars formula+ SAT.newVars_ db nv1+ tseitin <- Tseitin.newEncoder db+ pb <- PB.newEncoder tseitin+ pbnlc <- PBNLC.newEncoder pb tseitin+ forM_ (PBFile.pbConstraints formula) $ \(lhs,op,rhs) -> do+ case op of+ PBFile.Ge -> SAT.addPBNLAtLeast pbnlc lhs rhs+ PBFile.Eq -> SAT.addPBNLExactly pbnlc lhs rhs+ cnf <- getCNFFormula db+ defs <- Tseitin.getDefinitions tseitin+ return (cnf, TseitinInfo nv1 (CNF.cnfNumVars cnf) defs)++-- -----------------------------------------------------------------------------++type MaxSAT2WBOInfo = IdentityTransformer SAT.Model++maxsat2wbo :: CNF.WCNF -> (PBFile.SoftFormula, MaxSAT2WBOInfo)+maxsat2wbo+ CNF.WCNF+ { CNF.wcnfTopCost = top+ , CNF.wcnfClauses = cs+ , CNF.wcnfNumVars = nv+ , CNF.wcnfNumClauses = nc+ } =+ ( PBFile.SoftFormula+ { PBFile.wboTopCost = Nothing+ , PBFile.wboConstraints = map f cs+ , PBFile.wboNumVars = nv+ , PBFile.wboNumConstraints = nc+ }+ , IdentityTransformer+ )+ where+ f (w,c)+ | w>=top = (Nothing, p) -- hard constraint+ | otherwise = (Just w, p) -- soft constraint+ where+ p = ([(1,[l]) | l <- SAT.unpackClause c], PBFile.Ge, 1)++type WBO2MaxSATInfo = TseitinInfo++wbo2maxsat :: PBFile.SoftFormula -> (CNF.WCNF, WBO2MaxSATInfo)+wbo2maxsat formula = runST $ do+ db <- newCNFStore+ SAT.newVars_ db (PBFile.wboNumVars formula)+ tseitin <- Tseitin.newEncoder db+ pb <- PB.newEncoder tseitin+ pbnlc <- PBNLC.newEncoder pb tseitin++ softClauses <- liftM mconcat $ forM (PBFile.wboConstraints formula) $ \(cost, (lhs,op,rhs)) -> do+ case cost of+ Nothing ->+ case op of+ PBFile.Ge -> SAT.addPBNLAtLeast pbnlc lhs rhs >> return mempty+ PBFile.Eq -> SAT.addPBNLExactly pbnlc lhs rhs >> return mempty+ Just c -> do+ case op of+ PBFile.Ge -> do+ lhs2 <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityPos lhs+ let (lhs3,rhs3) = SAT.normalizePBLinAtLeast (lhs2,rhs)+ if rhs3==1 && and [c==1 | (c,_) <- lhs3] then+ return $ Seq.singleton (c, SAT.packClause [l | (_,l) <- lhs3])+ else do+ lit <- PB.encodePBLinAtLeast pb (lhs3,rhs3)+ return $ Seq.singleton (c, SAT.packClause [lit])+ PBFile.Eq -> do+ lhs2 <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityBoth lhs+ lit1 <- PB.encodePBLinAtLeast pb (lhs2, rhs)+ lit2 <- PB.encodePBLinAtLeast pb ([(-c, l) | (c,l) <- lhs2], negate rhs)+ lit <- Tseitin.encodeConjWithPolarity tseitin Tseitin.polarityPos [lit1,lit2]+ return $ Seq.singleton (c, SAT.packClause [lit])++ case PBFile.wboTopCost formula of+ Nothing -> return ()+ Just top -> SAT.addPBNLAtMost pbnlc [(c, [-l | l <- SAT.unpackClause clause]) | (c,clause) <- F.toList softClauses] (top - 1)++ let top = F.sum (fst <$> softClauses) + 1+ cnf <- getCNFFormula db+ let cs = softClauses <> Seq.fromList [(top, clause) | clause <- CNF.cnfClauses cnf]+ let wcnf = CNF.WCNF+ { CNF.wcnfNumVars = CNF.cnfNumVars cnf+ , CNF.wcnfNumClauses = Seq.length cs+ , CNF.wcnfTopCost = top+ , CNF.wcnfClauses = F.toList cs+ }+ defs <- Tseitin.getDefinitions tseitin+ return (wcnf, TseitinInfo (PBFile.wboNumVars formula) (CNF.cnfNumVars cnf) defs)++-- -----------------------------------------------------------------------------
+ src/ToySolver/Converter/PB/Internal/LargestIntersectionFinder.hs view
@@ -0,0 +1,99 @@+{-# OPTIONS -Wall #-}+module ToySolver.Converter.PB.Internal.LargestIntersectionFinder+ ( Table+ , empty+ , fromSet+ , fromList+ , toSet+ , toList+ , insert+ , findLargestIntersectionSet+ ) where++import Data.IntMap (IntMap)+import qualified Data.IntMap.Strict as IntMap+import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet+import Data.List hiding (insert)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Monoid+import Data.Ord+import Data.Set (Set)+import qualified Data.Set as Set++data Table+ = Table+ { numSets :: !Int+ , toSetId :: Map IntSet SetId+ , fromSetId :: IntMap IntSet+ , invMember :: IntMap (IntMap Count) -- e ↦ {s ↦ 1 | e∈s}+ }+ deriving (Show)++type SetId = Int+type Count = Int++empty :: Table+empty =+ Table+ { numSets = 0+ , toSetId = Map.empty+ , fromSetId = IntMap.empty+ , invMember = IntMap.empty+ }++fromList :: [IntSet] -> Table+fromList = fromSet . Set.fromList++fromSet :: Set IntSet -> Table+fromSet ss =+ Table+ { numSets = Set.size ss+ , toSetId = Map.fromList [(s,i) | (i,s) <- l]+ , fromSetId = IntMap.fromList l+ , invMember =+ IntMap.unionsWith IntMap.union+ [ IntMap.fromAscList [(e, IntMap.singleton i 1) | e <- IntSet.toAscList s]+ | (i,s) <- l+ ]+ }+ where+ l = zip [0..] (Set.toList ss)++toSet :: Table -> Set IntSet+toSet = Map.keysSet . toSetId++toList :: Table -> [IntSet]+toList = Set.toList . toSet++insert :: IntSet -> Table -> Table+insert s t+ | s `Map.member` toSetId t = t+ | otherwise =+ t+ { numSets = n + 1+ , toSetId = Map.insert s n (toSetId t)+ , fromSetId = IntMap.insert n s (fromSetId t)+ , invMember =+ IntMap.unionWith IntMap.union+ (IntMap.fromAscList [(e, IntMap.singleton n 1) | e <- IntSet.toAscList s])+ (invMember t)+ }+ where+ n = numSets t++-- | Given a set S and a family of sets U, find a T∈S such that S∩T has maximum cardinality.+-- In case of tie, smaller T is preferred.+findLargestIntersectionSet :: IntSet -> Table -> Maybe IntSet+findLargestIntersectionSet s t+ | IntMap.null m =+ if IntSet.empty `Map.member` toSetId t+ then Just IntSet.empty+ else Nothing+ | otherwise = Just $! fromSetId t IntMap.! n+ where+ m :: IntMap Count+ m = IntMap.unionsWith (+) [IntMap.findWithDefault IntMap.empty e (invMember t) | e <- IntSet.toList s]+ (n,_,_) = maximumBy (comparing (\(_,c,_) -> c) <> flip (comparing (\(_,_,size) -> size))) $+ [(i, c, IntSet.size (fromSetId t IntMap.! i)) | (i,c) <- IntMap.toList m]
+ src/ToySolver/Converter/PB/Internal/Product.hs view
@@ -0,0 +1,75 @@+{-# OPTIONS_GHC -Wall #-}+module ToySolver.Converter.PB.Internal.Product+ ( decomposeToBinaryProducts+ ) where++import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet+import Data.List hiding (insert)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Ord+import Data.Set (Set)+import qualified Data.Set as Set++import qualified ToySolver.Converter.PB.Internal.LargestIntersectionFinder as LargestIntersectionFinder++decomposeToBinaryProducts :: Set IntSet -> Map IntSet (IntSet,IntSet)+decomposeToBinaryProducts = decompose2 . decompose1++decompose1 :: Set IntSet -> Map IntSet (Maybe (IntSet,IntSet))+decompose1 ss = snd $ foldl' (flip f) (LargestIntersectionFinder.empty, Map.empty) ss'+ where+ ss' = map fst $ sortBy (comparing snd) [(s, IntSet.size s) | s <- Set.toList ss]++ f :: IntSet+ -> (LargestIntersectionFinder.Table, Map IntSet (Maybe (IntSet,IntSet)))+ -> (LargestIntersectionFinder.Table, Map IntSet (Maybe (IntSet,IntSet)))+ f s (t,r) | IntSet.size s < 2 || s `Map.member` r = (t,r)+ f s (t,r) =+ case LargestIntersectionFinder.findLargestIntersectionSet s t of+ Nothing ->+ ( LargestIntersectionFinder.insert s t+ , Map.insert s Nothing r+ )+ Just s0 ->+ let s1 = s `IntSet.intersection` s0+ s2 = s IntSet.\\ s1+ in if IntSet.size s1 < 2 && IntSet.size s2 < 2 then+ ( LargestIntersectionFinder.insert s t+ , Map.insert s Nothing r+ )+ else if IntSet.null s2 then -- i.e. s⊆s0+ case Map.lookup s0 r of+ Nothing -> error "should not happen"+ Just Nothing -> + let s3 = s0 IntSet.\\ s+ in ( LargestIntersectionFinder.insert s3 $ LargestIntersectionFinder.insert s t+ , -- union is left-biased+ Map.insert s0 (Just (s, s3)) $+ Map.union r (Map.fromList $ filter (\(s',_) -> IntSet.size s' >= 2) [(s, Nothing), (s3, Nothing)])+ )+ Just _ ->+ ( LargestIntersectionFinder.insert s t+ , Map.union r (Map.singleton s Nothing)+ )+ else+ case f s2 (f s1 (t,r)) of+ (t',r') ->+ ( LargestIntersectionFinder.insert s t'+ , Map.insert s (Just (s1,s2)) r'+ )++decompose2 :: Map IntSet (Maybe (IntSet,IntSet)) -> Map IntSet (IntSet,IntSet)+decompose2 m = Map.fromList $ do+ (s,d) <- Map.toList m+ case d of+ Just (s1,s2) -> return (s, (s1,s2))+ Nothing -> f (IntSet.toList s) (IntSet.size s)+ where+ f s n+ | n < 2 = []+ | n == 2 = return (IntSet.fromList s, (IntSet.singleton (s !! 0), IntSet.singleton (s !! 1)))+ | otherwise =+ case splitAt (n `div` 2) s of+ (s1, s2) -> (IntSet.fromList s, (IntSet.fromList s1, IntSet.fromList s2)) : f s1 (n `div` 2) ++ f s2 (n - (n `div` 2))
src/ToySolver/Converter/PB2IP.hs view
@@ -1,4 +1,5 @@ {-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Converter.PB2IP@@ -7,12 +8,19 @@ -- -- Maintainer : masahiro.sakai@gmail.com -- Stability : experimental--- Portability : portable+-- Portability : non-portable -- ----------------------------------------------------------------------------- module ToySolver.Converter.PB2IP- ( convert- , convertWBO+ ( pb2ip+ , PB2IPInfo+ , wbo2ip+ , WBO2IPInfo++ , sat2ip+ , SAT2IPInfo+ , maxsat2ip+ , MaxSAT2IPInfo ) where import Data.Array.IArray@@ -22,12 +30,31 @@ import qualified Data.Map as Map import qualified Data.PseudoBoolean as PBFile+import ToySolver.Converter.Base+import ToySolver.Converter.PB import qualified ToySolver.Data.MIP as MIP import ToySolver.Data.MIP ((.==.), (.<=.), (.>=.))+import qualified ToySolver.FileFormat.CNF as CNF import qualified ToySolver.SAT.Types as SAT -convert :: PBFile.Formula -> (MIP.Problem Integer, SAT.Model -> Map MIP.Var Rational, Map MIP.Var Rational -> SAT.Model)-convert formula = (mip, mforth, mtrans (PBFile.pbNumVars formula))+-- -----------------------------------------------------------------------------++newtype PB2IPInfo = PB2IPInfo Int+ deriving (Eq, Show, Read)++instance Transformer PB2IPInfo where+ type Source PB2IPInfo = SAT.Model+ type Target PB2IPInfo = Map MIP.Var Rational++instance ForwardTransformer PB2IPInfo where+ transformForward _ m =+ Map.fromList [(convVar v, if val then 1 else 0) | (v,val) <- assocs m]++instance BackwardTransformer PB2IPInfo where+ transformBackward (PB2IPInfo nv) = mtrans nv++pb2ip :: PBFile.Formula -> (MIP.Problem Integer, PB2IPInfo)+pb2ip formula = (mip, PB2IPInfo (PBFile.pbNumVars formula)) where mip = def { MIP.objectiveFunction = obj2@@ -51,8 +78,6 @@ PBFile.Ge -> def{ MIP.constrExpr = lhs2, MIP.constrLB = MIP.Finite rhs2 } PBFile.Eq -> def{ MIP.constrExpr = lhs2, MIP.constrLB = MIP.Finite rhs2, MIP.constrUB = MIP.Finite rhs2 } - mforth :: SAT.Model -> Map MIP.Var Rational- mforth m = Map.fromList [(convVar v, if val then 1 else 0) | (v,val) <- assocs m] convExpr :: PBFile.Sum -> MIP.Expr Integer convExpr s = sum [product (fromIntegral w : map f tm) | (w,tm) <- s]@@ -65,8 +90,26 @@ convVar :: PBFile.Var -> MIP.Var convVar x = MIP.toVar ("x" ++ show x) -convertWBO :: Bool -> PBFile.SoftFormula -> (MIP.Problem Integer, SAT.Model -> Map MIP.Var Rational, Map MIP.Var Rational -> SAT.Model)-convertWBO useIndicator formula = (mip, mforth, mtrans (PBFile.wboNumVars formula))+-- -----------------------------------------------------------------------------++data WBO2IPInfo = WBO2IPInfo !Int [(MIP.Var, PBFile.SoftConstraint)]+ deriving (Eq, Show)++instance Transformer WBO2IPInfo where+ type Source WBO2IPInfo = SAT.Model+ type Target WBO2IPInfo = Map MIP.Var Rational++instance ForwardTransformer WBO2IPInfo where+ transformForward (WBO2IPInfo _nv relaxVariables) m = Map.union m1 m2+ where+ m1 = Map.fromList $ [(convVar v, if val then 1 else 0) | (v,val) <- assocs m]+ m2 = Map.fromList $ [(v, if SAT.evalPBConstraint m c then 0 else 1) | (v, (Just _, c)) <- relaxVariables]++instance BackwardTransformer WBO2IPInfo where+ transformBackward (WBO2IPInfo nv _relaxVariables) = mtrans nv++wbo2ip :: Bool -> PBFile.SoftFormula -> (MIP.Problem Integer, WBO2IPInfo)+wbo2ip useIndicator formula = (mip, WBO2IPInfo (PBFile.wboNumVars formula) relaxVariables) where mip = def { MIP.objectiveFunction = obj2@@ -118,12 +161,6 @@ c2 = lhsLE .<=. MIP.constExpr rhsLE [ (ts, c1), ([], c2) ] - mforth :: SAT.Model -> Map MIP.Var Rational- mforth m = Map.union m1 m2- where- m1 = Map.fromList $ [(convVar v, if val then 1 else 0) | (v,val) <- assocs m]- m2 = Map.fromList $ [(v, if SAT.evalPBConstraint m c then 0 else 1) | (v, (Just _, c)) <- relaxVariables]- splitConst :: MIP.Expr Integer -> (MIP.Expr Integer, Integer) splitConst e = (e2, c) where@@ -151,3 +188,23 @@ 1 -> True v0 -> error (show v0 ++ " is neither 0 nor 1") ]++-- -----------------------------------------------------------------------------++type SAT2IPInfo = ComposedTransformer SAT2PBInfo PB2IPInfo++sat2ip :: CNF.CNF -> (MIP.Problem Integer, SAT2IPInfo)+sat2ip cnf = (ip, ComposedTransformer info1 info2)+ where+ (pb,info1) = sat2pb cnf+ (ip,info2) = pb2ip pb++type MaxSAT2IPInfo = ComposedTransformer MaxSAT2WBOInfo WBO2IPInfo++maxsat2ip :: Bool -> CNF.WCNF -> (MIP.Problem Integer, MaxSAT2IPInfo)+maxsat2ip useIndicator wcnf = (ip, ComposedTransformer info1 info2)+ where+ (wbo, info1) = maxsat2wbo wcnf+ (ip, info2) = wbo2ip useIndicator wbo++-- -----------------------------------------------------------------------------
src/ToySolver/Converter/PB2LSP.hs view
@@ -12,8 +12,8 @@ -- ----------------------------------------------------------------------------- module ToySolver.Converter.PB2LSP- ( convert- , convertWBO+ ( pb2lsp+ , wbo2lsp ) where import Data.ByteString.Builder@@ -21,8 +21,8 @@ import Data.Monoid import qualified Data.PseudoBoolean as PBFile -convert :: PBFile.Formula -> Builder-convert formula =+pb2lsp :: PBFile.Formula -> Builder+pb2lsp formula = byteString "function model() {\n" <> decls <> constrs <>@@ -45,8 +45,8 @@ Just obj' -> byteString " minimize " <> showSum obj' <> ";\n" Nothing -> mempty -convertWBO :: PBFile.SoftFormula -> Builder-convertWBO softFormula =+wbo2lsp :: PBFile.SoftFormula -> Builder+wbo2lsp softFormula = byteString "function model() {\n" <> decls <> constrs <>
− src/ToySolver/Converter/PB2SAT.hs
@@ -1,60 +0,0 @@-{-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.PB2SAT--- Copyright : (c) Masahiro Sakai 2016--- License : BSD-style------ Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : non-portable (FlexibleContexts, MultiParamTypeClasses)----------------------------------------------------------------------------------module ToySolver.Converter.PB2SAT (convert) where--import Control.Monad-import Control.Monad.ST-import Data.Array.IArray-import qualified Data.PseudoBoolean as PBFile--import qualified ToySolver.SAT.Types as SAT-import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin-import qualified ToySolver.SAT.Encoder.PB as PB-import qualified ToySolver.SAT.Encoder.PBNLC as PBNLC-import ToySolver.SAT.Store.CNF-import qualified ToySolver.Text.CNF as CNF---- | Convert a pseudo boolean formula φ to a equisatisfiable CNF formula ψ--- together with two functions f and g such that:------ * if M ⊨ φ then f(M) ⊨ ψ------ * if M ⊨ ψ then g(M) ⊨ φ--- -convert :: PBFile.Formula -> (CNF.CNF, SAT.Model -> SAT.Model, SAT.Model -> SAT.Model)-convert formula = runST $ do- db <- newCNFStore- let nv1 = PBFile.pbNumVars formula- SAT.newVars_ db nv1- tseitin <- Tseitin.newEncoder db- pb <- PB.newEncoder tseitin- pbnlc <- PBNLC.newEncoder pb tseitin- forM_ (PBFile.pbConstraints formula) $ \(lhs,op,rhs) -> do- case op of- PBFile.Ge -> SAT.addPBNLAtLeast pbnlc lhs rhs- PBFile.Eq -> SAT.addPBNLExactly pbnlc lhs rhs- cnf <- getCNFFormula db-- defs <- Tseitin.getDefinitions tseitin- let extendModel :: SAT.Model -> SAT.Model- extendModel m = array (1, CNF.numVars cnf) (assocs a)- where- -- Use BOXED array to tie the knot- a :: Array SAT.Var Bool- a = array (1, CNF.numVars cnf) $- assocs m ++ [(v, Tseitin.evalFormula a phi) | (v, phi) <- defs]-- return (cnf, extendModel, SAT.restrictModel nv1)---- -----------------------------------------------------------------------------
src/ToySolver/Converter/PB2SMP.hs view
@@ -12,7 +12,7 @@ -- ----------------------------------------------------------------------------- module ToySolver.Converter.PB2SMP- ( convert+ ( pb2smp ) where import Data.ByteString.Builder@@ -20,8 +20,8 @@ import Data.Monoid import qualified Data.PseudoBoolean as PBFile -convert :: Bool -> PBFile.Formula -> Builder-convert isUnix formula =+pb2smp :: Bool -> PBFile.Formula -> Builder+pb2smp isUnix formula = header <> decls <> char7 '\n' <>
− src/ToySolver/Converter/PB2WBO.hs
@@ -1,39 +0,0 @@-{-# OPTIONS_GHC -Wall #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.PB2WBO--- Copyright : (c) Masahiro Sakai 2013--- License : BSD-style--- --- Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : portable------ References:------ * Improving Unsatisfiability-based Algorithms for Boolean Optimization--- <http://sat.inesc-id.pt/~ruben/talks/sat10-talk.pdf>----------------------------------------------------------------------------------module ToySolver.Converter.PB2WBO (convert) where--import qualified Data.PseudoBoolean as PBFile--convert :: PBFile.Formula -> PBFile.SoftFormula-convert formula- = PBFile.SoftFormula- { PBFile.wboTopCost = Nothing- , PBFile.wboConstraints = cs1 ++ cs2- , PBFile.wboNumVars = PBFile.pbNumVars formula- , PBFile.wboNumConstraints = PBFile.pbNumConstraints formula + length cs2- }- where- cs1 = [(Nothing, c) | c <- PBFile.pbConstraints formula]- cs2 = case PBFile.pbObjectiveFunction formula of- Nothing -> []- Just e ->- [ if w >= 0- then (Just w, ([(-1,ls)], PBFile.Ge, 0))- else (Just (abs w), ([(1,ls)], PBFile.Ge, 1))- | (w,ls) <- e- ]
− src/ToySolver/Converter/PBLinearization.hs
@@ -1,77 +0,0 @@-{-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE MultiParamTypeClasses #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.PBLinearization--- Copyright : (c) Masahiro Sakai 2016--- License : BSD-style------ Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : portable----------------------------------------------------------------------------------module ToySolver.Converter.PBLinearization- ( linearize- , linearizeWBO- ) where--import Control.Monad-import Control.Monad.ST-import qualified Data.PseudoBoolean as PBFile--import qualified ToySolver.SAT.Types as SAT-import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin-import qualified ToySolver.SAT.Encoder.PBNLC as PBNLC-import ToySolver.SAT.Store.PB--linearize :: PBFile.Formula -> Bool -> PBFile.Formula-linearize formula usePB = runST $ do- db <- newPBStore- SAT.newVars_ db (PBFile.pbNumVars formula)- tseitin <- Tseitin.newEncoderWithPBLin db- Tseitin.setUsePB tseitin usePB- pbnlc <- PBNLC.newEncoder db tseitin- cs' <- forM (PBFile.pbConstraints formula) $ \(lhs,op,rhs) -> do- let p = case op of- PBFile.Ge -> Tseitin.polarityPos- PBFile.Eq -> Tseitin.polarityBoth- lhs' <- PBNLC.linearizePBSumWithPolarity pbnlc p lhs- return ([(c,[l]) | (c,l) <- lhs'],op,rhs)- obj' <-- case PBFile.pbObjectiveFunction formula of- Nothing -> return Nothing- Just obj -> do- obj' <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityNeg obj- return $ Just [(c, [l]) | (c,l) <- obj']- formula' <- getPBFormula db- return $- formula'- { PBFile.pbObjectiveFunction = obj'- , PBFile.pbConstraints = cs' ++ PBFile.pbConstraints formula'- , PBFile.pbNumConstraints = PBFile.pbNumConstraints formula + PBFile.pbNumConstraints formula'- }--linearizeWBO :: PBFile.SoftFormula -> Bool -> PBFile.SoftFormula-linearizeWBO formula usePB = runST $ do- db <- newPBStore- SAT.newVars_ db (PBFile.wboNumVars formula)- tseitin <- Tseitin.newEncoderWithPBLin db- Tseitin.setUsePB tseitin usePB- pbnlc <- PBNLC.newEncoder db tseitin- cs' <- forM (PBFile.wboConstraints formula) $ \(cost,(lhs,op,rhs)) -> do- let p = case op of- PBFile.Ge -> Tseitin.polarityPos- PBFile.Eq -> Tseitin.polarityBoth- lhs' <- PBNLC.linearizePBSumWithPolarity pbnlc p lhs- return (cost,([(c,[l]) | (c,l) <- lhs'],op,rhs))- formula' <- getPBFormula db- return $- PBFile.SoftFormula- { PBFile.wboTopCost = PBFile.wboTopCost formula- , PBFile.wboConstraints = cs' ++ [(Nothing, constr) | constr <- PBFile.pbConstraints formula']- , PBFile.wboNumVars = PBFile.pbNumVars formula'- , PBFile.wboNumConstraints = PBFile.wboNumConstraints formula + PBFile.pbNumConstraints formula'- }---- -----------------------------------------------------------------------------
+ src/ToySolver/Converter/QBF2IPC.hs view
@@ -0,0 +1,67 @@+{-# OPTIONS_GHC -Wall #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter.QBF2IPC+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : experimental+-- Portability : non-portable+--+-- References:+--+-- * Morten Heine B. Sørensen, and Pawel Urzyczyn. Lectures on the Curry-Howard+-- Isomorphism. http://disi.unitn.it/~bernardi/RSISE11/Papers/curry-howard.pdf+--+-----------------------------------------------------------------------------+module ToySolver.Converter.QBF2IPC+ ( qbf2ipc+ ) where++import qualified Data.IntSet as IntSet++import ToySolver.Data.Boolean+import ToySolver.Data.BoolExpr (BoolExpr)+import qualified ToySolver.Data.BoolExpr as BoolExpr+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.QBF as QBF+import qualified ToySolver.SAT.Types as SAT+++qbf2ipc :: CNF.QDimacs -> (Int, [BoolExpr SAT.Var], BoolExpr SAT.Var)+qbf2ipc qdimacs = (nv2, lhs, rhs)+ where+ nv = CNF.qdimacsNumVars qdimacs+ nc = CNF.qdimacsNumClauses qdimacs++ prefix = [(q,a) | (q,as) <- qs, a <- IntSet.toList as]+ where+ qs = QBF.quantifyFreeVariables nv [(q, IntSet.fromList as) | (q,as) <- CNF.qdimacsPrefix qdimacs]++ nv2 = nv -- positive literal+ + nv -- negative literal+ + nc -- clause+ + 1 -- conjunction+ + nv -- quantified formula+ alpha_xp x = x+ alpha_xn x = nv + x+ alpha_l l = BoolExpr.Atom $ if l > 0 then alpha_xp l else alpha_xn (- l)+ alpha_c i = BoolExpr.Atom $ nv + nv + (1 + i)+ alpha_mat = BoolExpr.Atom $ nv + nv + nc + 1+ alpha_qf i = BoolExpr.Atom $ nv + nv + nc + 1 + (1 + i)++ lhs =+ snd (f (zip [0..] prefix)) +++ [foldr (.=>.) alpha_mat [alpha_c i | (i,_) <- zip [0..] (CNF.qdimacsMatrix qdimacs)]] +++ concat [[alpha_l l .=>. alpha_c i | l <- SAT.unpackClause c] | (i, c) <- zip [0..] (CNF.qdimacsMatrix qdimacs)]+ where+ f [] = (alpha_mat, [])+ f ((i,(QBF.E,x)) : qs) =+ case f qs of+ (alpha_body, ret) -> (alpha_qf i, [(alpha_l x .=>. alpha_body) .=>. alpha_qf i, (alpha_l (- x) .=>. alpha_body) .=>. alpha_qf i] ++ ret)+ f ((i,(QBF.A,x)) : qs) =+ case f qs of+ (alpha_body, ret) -> (alpha_qf i, [(alpha_l x .=>. alpha_body) .=>. (alpha_l (- x) .=>. alpha_body) .=>. alpha_qf i] ++ ret)++ rhs = alpha_qf 0
+ src/ToySolver/Converter/QUBO.hs view
@@ -0,0 +1,297 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter.QUBO+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+-- +-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+-- +-----------------------------------------------------------------------------+module ToySolver.Converter.QUBO+ ( qubo2pb+ , QUBO2PBInfo (..)++ , pb2qubo+ , PB2QUBOInfo++ , pbAsQUBO+ , PBAsQUBOInfo (..)++ , qubo2ising+ , QUBO2IsingInfo (..)++ , ising2qubo+ , Ising2QUBOInfo (..)+ ) where++import Control.Monad+import Control.Monad.State+import Data.Array.Unboxed+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IntMap+import Data.List+import Data.Maybe+import qualified Data.PseudoBoolean as PBFile+import Data.Ratio+import ToySolver.Converter.Base+import ToySolver.Converter.PB (pb2qubo', PB2QUBOInfo')+import qualified ToySolver.QUBO as QUBO+import qualified ToySolver.SAT.Types as SAT++-- -----------------------------------------------------------------------------++qubo2pb :: Real a => QUBO.Problem a -> (PBFile.Formula, QUBO2PBInfo a)+qubo2pb prob =+ ( PBFile.Formula+ { PBFile.pbObjectiveFunction = Just $+ [ (c, if x1==x2 then [x1+1] else [x1+1, x2+1])+ | (x1, row) <- IntMap.toList m2+ , (x2, c) <- IntMap.toList row+ ]+ , PBFile.pbConstraints = []+ , PBFile.pbNumVars = QUBO.quboNumVars prob+ , PBFile.pbNumConstraints = 0+ }+ , QUBO2PBInfo d+ )+ where+ m1 = fmap (fmap toRational) $ QUBO.quboMatrix prob+ d = foldl' lcm 1 [denominator c | row <- IntMap.elems m1, c <- IntMap.elems row, c /= 0]+ m2 = fmap (fmap (\c -> numerator c * (d ` div` denominator c))) m1++newtype QUBO2PBInfo a = QUBO2PBInfo Integer+ deriving (Eq, Show, Read)++instance (Eq a, Show a, Read a) => Transformer (QUBO2PBInfo a) where+ type Source (QUBO2PBInfo a) = QUBO.Solution+ type Target (QUBO2PBInfo a) = SAT.Model++instance (Eq a, Show a, Read a) => ForwardTransformer (QUBO2PBInfo a) where+ transformForward (QUBO2PBInfo _) sol = ixmap (lb+1,ub+1) (subtract 1) sol+ where+ (lb,ub) = bounds sol++instance (Eq a, Show a, Read a) => BackwardTransformer (QUBO2PBInfo a) where+ transformBackward (QUBO2PBInfo _) m = ixmap (lb-1,ub-1) (+1) m+ where+ (lb,ub) = bounds m++instance (Eq a, Show a, Read a) => ObjValueTransformer (QUBO2PBInfo a) where+ type SourceObjValue (QUBO2PBInfo a) = a+ type TargetObjValue (QUBO2PBInfo a) = Integer++instance (Eq a, Show a, Read a, Real a) => ObjValueForwardTransformer (QUBO2PBInfo a) where+ transformObjValueForward (QUBO2PBInfo d) obj = round (toRational obj) * d++instance (Eq a, Show a, Read a, Num a) => ObjValueBackwardTransformer (QUBO2PBInfo a) where+ transformObjValueBackward (QUBO2PBInfo d) obj = fromInteger $ (obj + d - 1) `div` d++-- -----------------------------------------------------------------------------++pbAsQUBO :: forall a. Real a => PBFile.Formula -> Maybe (QUBO.Problem a, PBAsQUBOInfo a)+pbAsQUBO formula = do+ (prob, offset) <- runStateT body 0+ return $ (prob, PBAsQUBOInfo offset)+ where+ body :: StateT Integer Maybe (QUBO.Problem a)+ body = do+ guard $ null (PBFile.pbConstraints formula)+ let f :: PBFile.WeightedTerm -> StateT Integer Maybe [(Integer, Int, Int)]+ f (c,[]) = modify (+c) >> return []+ f (c,[x]) = return [(c,x,x)]+ f (c,[x1,x2]) = return [(c,x1,x2)]+ f _ = mzero+ xs <- mapM f $ SAT.removeNegationFromPBSum $ fromMaybe [] $ PBFile.pbObjectiveFunction formula+ return $+ QUBO.Problem+ { QUBO.quboNumVars = PBFile.pbNumVars formula+ , QUBO.quboMatrix = mkMat $+ [ (x1', x2', fromInteger c)+ | (c,x1,x2) <- concat xs, let x1' = min x1 x2 - 1, let x2' = max x1 x2 - 1+ ]+ }++data PBAsQUBOInfo a = PBAsQUBOInfo !Integer+ deriving (Eq, Show, Read)++instance Transformer (PBAsQUBOInfo a) where+ type Source (PBAsQUBOInfo a) = SAT.Model+ type Target (PBAsQUBOInfo a) = QUBO.Solution++instance ForwardTransformer (PBAsQUBOInfo a) where+ transformForward (PBAsQUBOInfo _offset) m = ixmap (lb-1,ub-1) (+1) m+ where+ (lb,ub) = bounds m++instance BackwardTransformer (PBAsQUBOInfo a) where+ transformBackward (PBAsQUBOInfo _offset) sol = ixmap (lb+1,ub+1) (subtract 1) sol+ where+ (lb,ub) = bounds sol++instance ObjValueTransformer (PBAsQUBOInfo a) where+ type SourceObjValue (PBAsQUBOInfo a) = Integer+ type TargetObjValue (PBAsQUBOInfo a) = a++instance Num a => ObjValueForwardTransformer (PBAsQUBOInfo a) where+ transformObjValueForward (PBAsQUBOInfo offset) obj = fromInteger (obj - offset)++instance Real a => ObjValueBackwardTransformer (PBAsQUBOInfo a) where+ transformObjValueBackward (PBAsQUBOInfo offset) obj = round (toRational obj) + offset++-- -----------------------------------------------------------------------------++pb2qubo :: Real a => PBFile.Formula -> ((QUBO.Problem a, a), PB2QUBOInfo a)+pb2qubo formula = ((qubo, fromInteger (th - offset)), ComposedTransformer info1 info2)+ where+ ((qubo', th), info1) = pb2qubo' formula+ Just (qubo, info2@(PBAsQUBOInfo offset)) = pbAsQUBO qubo'++type PB2QUBOInfo a = ComposedTransformer PB2QUBOInfo' (PBAsQUBOInfo a)++-- -----------------------------------------------------------------------------++qubo2ising :: (Eq a, Show a, Fractional a) => QUBO.Problem a -> (QUBO.IsingModel a, QUBO2IsingInfo a)+qubo2ising QUBO.Problem{ QUBO.quboNumVars = n, QUBO.quboMatrix = qq } =+ ( QUBO.IsingModel+ { QUBO.isingNumVars = n+ , QUBO.isingInteraction = normalizeMat $ jj'+ , QUBO.isingExternalMagneticField = normalizeVec h'+ }+ , QUBO2IsingInfo c'+ )+ where+ {-+ Let xi = (si + 1)/2.++ Then,+ Qij xi xj+ = Qij (si + 1)/2 (sj + 1)/2+ = 1/4 Qij (si sj + si + sj + 1).++ Also,+ Qii xi xi+ = Qii (si + 1)/2 (si + 1)/2+ = 1/4 Qii (si si + 2 si + 1)+ = 1/4 Qii (2 si + 2).+ -}+ (jj', h', c') = foldl' f (IntMap.empty, IntMap.empty, 0) $ do+ (i, row) <- IntMap.toList qq+ (j, q_ij) <- IntMap.toList row+ if i /= j then+ return+ ( IntMap.singleton (min i j) $ IntMap.singleton (max i j) (q_ij / 4)+ , IntMap.fromList [(i, q_ij / 4), (j, q_ij / 4)]+ , q_ij / 4+ )+ else+ return+ ( IntMap.empty+ , IntMap.singleton i (q_ij / 2)+ , q_ij / 2+ )++ f (jj1, h1, c1) (jj2, h2, c2) =+ ( IntMap.unionWith (IntMap.unionWith (+)) jj1 jj2+ , IntMap.unionWith (+) h1 h2+ , c1+c2+ )++data QUBO2IsingInfo a = QUBO2IsingInfo a+ deriving (Eq, Show, Read)++instance (Eq a, Show a) => Transformer (QUBO2IsingInfo a) where+ type Source (QUBO2IsingInfo a) = QUBO.Solution+ type Target (QUBO2IsingInfo a) = QUBO.Solution++instance (Eq a, Show a) => ForwardTransformer (QUBO2IsingInfo a) where+ transformForward _ sol = sol++instance (Eq a, Show a) => BackwardTransformer (QUBO2IsingInfo a) where+ transformBackward _ sol = sol++instance ObjValueTransformer (QUBO2IsingInfo a) where+ type SourceObjValue (QUBO2IsingInfo a) = a+ type TargetObjValue (QUBO2IsingInfo a) = a++instance (Eq a, Show a, Num a) => ObjValueForwardTransformer (QUBO2IsingInfo a) where+ transformObjValueForward (QUBO2IsingInfo offset) obj = obj - offset++instance (Eq a, Show a, Num a) => ObjValueBackwardTransformer (QUBO2IsingInfo a) where+ transformObjValueBackward (QUBO2IsingInfo offset) obj = obj + offset++-- -----------------------------------------------------------------------------++ising2qubo :: (Eq a, Num a) => QUBO.IsingModel a -> (QUBO.Problem a, Ising2QUBOInfo a)+ising2qubo QUBO.IsingModel{ QUBO.isingNumVars = n, QUBO.isingInteraction = jj, QUBO.isingExternalMagneticField = h } =+ ( QUBO.Problem+ { QUBO.quboNumVars = n+ , QUBO.quboMatrix = mkMat m+ }+ , Ising2QUBOInfo offset+ )+ where+ {-+ Let si = 2 xi - 1++ Then,+ Jij si sj+ = Jij (2 xi - 1) (2 xj - 1)+ = Jij (4 xi xj - 2 xi - 2 xj + 1)+ = 4 Jij xi xj - 2 Jij xi - 2 Jij xj + Jij+ = 4 Jij xi xj - 2 Jij xi xi - 2 Jij xj xj + Jij++ hi si+ = hi (2 xi - 1)+ = 2 hi xi - hi+ = 2 hi xi xi - hi+ -}+ m =+ concat+ [ [(i, j, 4 * jj_ij), (i, i, - 2 * jj_ij), (j, j, - 2 * jj_ij)]+ | (i, row) <- IntMap.toList jj, (j, jj_ij) <- IntMap.toList row+ ] +++ [ (i, i, 2 * hi) | (i, hi) <- IntMap.toList h ]+ offset =+ sum [jj_ij | row <- IntMap.elems jj, jj_ij <- IntMap.elems row]+ - sum (IntMap.elems h)++data Ising2QUBOInfo a = Ising2QUBOInfo a+ deriving (Eq, Show, Read)++instance (Eq a, Show a) => Transformer (Ising2QUBOInfo a) where+ type Source (Ising2QUBOInfo a) = QUBO.Solution+ type Target (Ising2QUBOInfo a) = QUBO.Solution++instance (Eq a, Show a) => ForwardTransformer (Ising2QUBOInfo a) where+ transformForward _ sol = sol++instance (Eq a, Show a) => BackwardTransformer (Ising2QUBOInfo a) where+ transformBackward _ sol = sol++instance (Eq a, Show a) => ObjValueTransformer (Ising2QUBOInfo a) where+ type SourceObjValue (Ising2QUBOInfo a) = a+ type TargetObjValue (Ising2QUBOInfo a) = a++instance (Eq a, Show a, Num a) => ObjValueForwardTransformer (Ising2QUBOInfo a) where+ transformObjValueForward (Ising2QUBOInfo offset) obj = obj - offset++instance (Eq a, Show a, Num a) => ObjValueBackwardTransformer (Ising2QUBOInfo a) where+ transformObjValueBackward (Ising2QUBOInfo offset) obj = obj + offset++-- -----------------------------------------------------------------------------++mkMat :: (Eq a, Num a) => [(Int,Int,a)] -> IntMap (IntMap a)+mkMat m = normalizeMat $+ IntMap.unionsWith (IntMap.unionWith (+)) $+ [IntMap.singleton i (IntMap.singleton j qij) | (i,j,qij) <- m]++normalizeMat :: (Eq a, Num a) => IntMap (IntMap a) -> IntMap (IntMap a)+normalizeMat = IntMap.mapMaybe ((\m -> if IntMap.null m then Nothing else Just m) . normalizeVec)++normalizeVec :: (Eq a, Num a) => IntMap a -> IntMap a+normalizeVec = IntMap.filter (/=0)
− src/ToySolver/Converter/SAT2IP.hs
@@ -1,26 +0,0 @@-{-# OPTIONS_GHC -Wall #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.SAT2IP--- Copyright : (c) Masahiro Sakai 2011-2014--- License : BSD-style--- --- Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : portable----------------------------------------------------------------------------------module ToySolver.Converter.SAT2IP- ( convert- ) where--import Data.Map (Map)--import qualified ToySolver.Data.MIP as MIP-import qualified ToySolver.SAT.Types as SAT-import qualified ToySolver.Converter.PB2IP as PB2IP-import qualified ToySolver.Converter.SAT2PB as SAT2PB-import qualified ToySolver.Text.CNF as CNF--convert :: CNF.CNF -> (MIP.Problem Integer, SAT.Model -> Map MIP.Var Rational, Map MIP.Var Rational -> SAT.Model)-convert cnf = PB2IP.convert (SAT2PB.convert cnf)
src/ToySolver/Converter/SAT2KSAT.hs view
@@ -1,5 +1,7 @@ {-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Converter.SAT2KSAT@@ -8,10 +10,13 @@ -- -- Maintainer : masahiro.sakai@gmail.com -- Stability : experimental--- Portability : non-portable (FlexibleContexts, MultiParamTypeClasses)+-- Portability : non-portable -- ------------------------------------------------------------------------------module ToySolver.Converter.SAT2KSAT (convert) where+module ToySolver.Converter.SAT2KSAT+ ( sat2ksat+ , SAT2KSATInfo (..)+ ) where import Control.Monad import Control.Monad.ST@@ -22,18 +27,20 @@ import qualified Data.Sequence as Seq import Data.STRef +import ToySolver.Converter.Base+import qualified ToySolver.FileFormat.CNF as CNF import qualified ToySolver.SAT.Types as SAT import ToySolver.SAT.Store.CNF-import qualified ToySolver.Text.CNF as CNF -convert :: Int -> CNF.CNF -> (CNF.CNF, SAT.Model -> SAT.Model, SAT.Model -> SAT.Model)-convert k _ | k < 3 = error "ToySolver.Converter.SAT2KSAT.convert: k must be >=3"-convert k cnf = runST $ do- let nv1 = CNF.numVars cnf++sat2ksat :: Int -> CNF.CNF -> (CNF.CNF, SAT2KSATInfo)+sat2ksat k _ | k < 3 = error "ToySolver.Converter.SAT2KSAT.sat2ksat: k must be >=3"+sat2ksat k cnf = runST $ do+ let nv1 = CNF.cnfNumVars cnf db <- newCNFStore defsRef <- newSTRef Seq.empty- SAT.newVars_ db (CNF.numVars cnf)- forM_ (CNF.clauses cnf) $ \clause -> do+ SAT.newVars_ db nv1+ forM_ (CNF.cnfClauses cnf) $ \clause -> do let loop lits = do if Seq.length lits <= k then SAT.addClause db (toList lits)@@ -44,27 +51,35 @@ SAT.addClause db (toList (lits1 |> (-v))) modifySTRef' defsRef (|> (v, toList lits1)) loop (v <| lits2)- loop $ Seq.fromList clause- + loop $ Seq.fromList $ SAT.unpackClause clause cnf2 <- getCNFFormula db- defs <- readSTRef defsRef+ return (cnf2, SAT2KSATInfo nv1 (CNF.cnfNumVars cnf2) defs) - let extendModel :: SAT.Model -> SAT.Model- extendModel m = runSTUArray $ do- m2 <- newArray_ (1,CNF.numVars cnf2)- forM_ [1..nv1] $ \v -> do- writeArray m2 v (SAT.evalVar m v)- forM_ (toList defs) $ \(v, clause) -> do- let f lit =- if lit > 0 then- readArray m2 lit- else- liftM not (readArray m2 (- lit))- val <- liftM or (mapM f clause)- writeArray m2 v val- return m2 - return (cnf2, extendModel, SAT.restrictModel nv1)+data SAT2KSATInfo = SAT2KSATInfo !Int !Int (Seq.Seq (SAT.Var, [SAT.Lit]))+ deriving (Eq, Show, Read)++instance Transformer SAT2KSATInfo where+ type Source SAT2KSATInfo = SAT.Model+ type Target SAT2KSATInfo = SAT.Model++instance ForwardTransformer SAT2KSATInfo where+ transformForward (SAT2KSATInfo nv1 nv2 defs) m = runSTUArray $ do+ m2 <- newArray_ (1,nv2)+ forM_ [1..nv1] $ \v -> do+ writeArray m2 v (SAT.evalVar m v)+ forM_ (toList defs) $ \(v, clause) -> do+ let f lit =+ if lit > 0 then+ readArray m2 lit+ else+ liftM not (readArray m2 (- lit))+ val <- liftM or (mapM f clause)+ writeArray m2 v val+ return m2++instance BackwardTransformer SAT2KSATInfo where+ transformBackward (SAT2KSATInfo nv1 _nv2 _defs) = SAT.restrictModel nv1 -- -----------------------------------------------------------------------------
+ src/ToySolver/Converter/SAT2MaxCut.hs view
@@ -0,0 +1,128 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter.SAT2MaxCut+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-- http://www.cs.cornell.edu/courses/cs4820/2014sp/notes/reduction-maxcut.pdf+--+-----------------------------------------------------------------------------+module ToySolver.Converter.SAT2MaxCut+ (+ -- * SAT to MaxCut conversion+ SAT2MaxCutInfo+ , sat2maxcut++ -- * Low-level conversion++ -- ** NAE-SAT to MaxCut+ , NAESAT2MaxCutInfo+ , naesat2maxcut++ -- ** NAE-3-SAT to MaxCut+ , NAE3SAT2MaxCutInfo (..)+ , nae3sat2maxcut+ ) where++import Data.Array.Unboxed+import qualified Data.IntSet as IntSet+import qualified Data.Vector.Generic as VG+import qualified Data.Vector.Unboxed as VU++import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.MaxCut as MaxCut+import qualified ToySolver.SAT.Types as SAT+import ToySolver.Converter.Base+import ToySolver.Converter.NAESAT (NAESAT)+import qualified ToySolver.Converter.NAESAT as NAESAT++-- ------------------------------------------------------------------------++type SAT2MaxCutInfo = ComposedTransformer NAESAT.SAT2NAESATInfo NAESAT2MaxCutInfo++sat2maxcut :: CNF.CNF -> ((MaxCut.Problem Integer, Integer), SAT2MaxCutInfo)+sat2maxcut x = (x2, (ComposedTransformer info1 info2))+ where+ (x1, info1) = NAESAT.sat2naesat x+ (x2, info2) = naesat2maxcut x1++-- ------------------------------------------------------------------------++type NAESAT2MaxCutInfo = ComposedTransformer NAESAT.NAESAT2NAEKSATInfo NAE3SAT2MaxCutInfo++naesat2maxcut :: NAESAT -> ((MaxCut.Problem Integer, Integer), NAESAT2MaxCutInfo)+naesat2maxcut x = (x2, (ComposedTransformer info1 info2))+ where+ (x1, info1) = NAESAT.naesat2naeksat 3 x+ (x2, info2) = nae3sat2maxcut x1++-- ------------------------------------------------------------------------++data NAE3SAT2MaxCutInfo = NAE3SAT2MaxCutInfo+ deriving (Eq, Show, Read)++-- Original nae-sat problem is satisfiable iff Max-Cut problem has solution with >=threshold.+nae3sat2maxcut :: NAESAT -> ((MaxCut.Problem Integer, Integer), NAE3SAT2MaxCutInfo)+nae3sat2maxcut (n,cs)+ | any (\c -> VG.length c < 2) cs' =+ ( (MaxCut.fromEdges (n*2) [], 1)+ , NAE3SAT2MaxCutInfo+ )+ | otherwise =+ ( ( MaxCut.fromEdges (n*2) (variableEdges ++ clauseEdges)+ , bigM * fromIntegral n + clauseEdgesObjMax+ )+ , NAE3SAT2MaxCutInfo+ )+ where+ cs' = map (VG.fromList . IntSet.toList . IntSet.fromList . VG.toList) cs++ bigM = clauseEdgesObjMax + 1++ (clauseEdges, clauseEdgesObjMax) = foldl f ([],0) cs'+ where+ f :: ([(Int,Int,Integer)], Integer) -> VU.Vector SAT.Lit -> ([(Int,Int,Integer)], Integer)+ f (clauseEdges', !clauseEdgesObjMax') c =+ case map encodeLit (VG.toList c) of+ [] -> error "nae3sat2maxcut: should not happen"+ [_] -> error "nae3sat2maxcut: should not happen"+ [v0,v1] -> ([(v0, v1, 1)] ++ clauseEdges', clauseEdgesObjMax' + 1)+ [v0,v1,v2] -> ([(v0, v1, 1), (v1, v2, 1), (v2, v0, 1)] ++ clauseEdges', clauseEdgesObjMax' + 2)+ _ -> error "nae3sat2maxcut: cannot handle nae-clause of size >3"++ variableEdges = [(encodeLit v, encodeLit (-v), bigM) | v <- [1..n]]++instance Transformer NAE3SAT2MaxCutInfo where+ type Source NAE3SAT2MaxCutInfo = SAT.Model+ type Target NAE3SAT2MaxCutInfo = MaxCut.Solution++instance ForwardTransformer NAE3SAT2MaxCutInfo where+ transformForward _ m = array (0,2*n-1) $ do+ v <- [1..n]+ let val = SAT.evalVar m v+ [(encodeLit v, val), (encodeLit (-v), not val)]+ where+ (_,n) = bounds m++instance BackwardTransformer NAE3SAT2MaxCutInfo where+ transformBackward _ sol = array (1,n) [(v, sol ! encodeLit v) | v <- [1..n]]+ where+ (_,n') = bounds sol+ n = (n'+1) `div` 2++-- ------------------------------------------------------------------------++encodeLit :: SAT.Lit -> Int+encodeLit l =+ if l > 0+ then (l-1)*2+ else (-l-1)*2+1++-- ------------------------------------------------------------------------
+ src/ToySolver/Converter/SAT2MaxSAT.hs view
@@ -0,0 +1,281 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter.SAT2MaxSAT+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-- References:+--+-- * M. R. Garey, D. S. Johnson, and L. Stockmeyer. Some simplified NP-complete+-- problems. In STOC ’74: Proceedings of the sixth annual ACM symposium on Theory+-- of computing, pages 47–63, New York, NY, USA, 1974.+-- https://dl.acm.org/citation.cfm?doid=800119.803884+-- https://www.sciencedirect.com/science/article/pii/0304397576900591+--+-----------------------------------------------------------------------------+module ToySolver.Converter.SAT2MaxSAT+ (+ -- * SAT to Max-2-SAT conversion+ SATToMaxSAT2Info+ , satToMaxSAT2++ -- * Max-2-SAT to simple Max-Cut conversion+ , MaxSAT2ToSimpleMaxCutInfo+ , maxSAT2ToSimpleMaxCut++ -- * SAT to simple Max-Cut conversion+ , SATToSimpleMaxCutInfo+ , satToSimpleMaxCut++ -- * Low-level conversion++ -- ** 3-SAT to Max-2-SAT conversion+ , SAT3ToMaxSAT2Info (..)+ , sat3ToMaxSAT2++ -- ** Max-2-SAT to SimpleMaxSAT2 conversion+ , SimpleMaxSAT2+ , SimplifyMaxSAT2Info (..)+ , simplifyMaxSAT2++ -- ** SimpleMaxSAT2 to simple Max-Cut conversion+ , SimpleMaxSAT2ToSimpleMaxCutInfo (..)+ , simpleMaxSAT2ToSimpleMaxCut+ ) where++import Control.Monad+import Data.Array.MArray+import Data.Array.ST+import Data.Array.Unboxed+import Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap+import qualified Data.IntSet as IntSet+import Data.List+import Data.Monoid+import Data.Set (Set)+import qualified Data.Set as Set+import qualified ToySolver.FileFormat.CNF as CNF+import ToySolver.Converter.Base+import ToySolver.Converter.SAT2KSAT+import qualified ToySolver.MaxCut as MaxCut+import qualified ToySolver.SAT.Types as SAT++-- ------------------------------------------------------------------------++type SATToMaxSAT2Info = ComposedTransformer SAT2KSATInfo SAT3ToMaxSAT2Info++satToMaxSAT2 :: CNF.CNF -> ((CNF.WCNF, Integer), SATToMaxSAT2Info)+satToMaxSAT2 x = (x2, (ComposedTransformer info1 info2))+ where+ (x1, info1) = sat2ksat 3 x+ (x2, info2) = sat3ToMaxSAT2 x1+++sat3ToMaxSAT2 :: CNF.CNF -> ((CNF.WCNF, Integer), SAT3ToMaxSAT2Info)+sat3ToMaxSAT2 cnf =+ case foldl' f (CNF.cnfNumVars cnf, 0, [], [], 0) (CNF.cnfClauses cnf) of+ (!nv, !nc, !cs, ds, !t) ->+ ( ( CNF.WCNF+ { CNF.wcnfNumVars = nv+ , CNF.wcnfNumClauses = nc+ , CNF.wcnfTopCost = fromIntegral $ nc + 1+ , CNF.wcnfClauses = reverse cs+ }+ , t+ )+ , SAT3ToMaxSAT2Info (CNF.cnfNumVars cnf) nv (IntMap.fromList ds)+ )+ where+ f :: (Int, Int, [CNF.WeightedClause], [(SAT.Var,(SAT.Lit,SAT.Lit,SAT.Lit))], Integer)+ -> SAT.PackedClause+ -> (Int, Int, [CNF.WeightedClause], [(SAT.Var,(SAT.Lit,SAT.Lit,SAT.Lit))], Integer)+ f (!nv, !nc, cs, ds, t) clause =+ case SAT.unpackClause clause of+ [] -> (nv, nc+1, (1,clause) : cs, ds, t)+ [_a] -> (nv, nc+1, (1,clause) : cs, ds, t)+ [_a, _b] -> (nv, nc+1, (1,clause) : cs, ds, t)+ [a, b, c] ->+ let d = nv+1+ cs2 = [[a], [b], [c], [d], [-a,-b], [-a,-c], [-b,-c], [a,-d], [b,-d], [c,-d]]+ in (nv+1, nc + length cs2, map (\clause' -> (1, SAT.packClause clause')) cs2 ++ cs, (d, (a,b,c)) : ds, t + 3)+ _ -> error "not a 3-SAT instance"++data SAT3ToMaxSAT2Info = SAT3ToMaxSAT2Info !Int !Int (IntMap (SAT.Lit,SAT.Lit,SAT.Lit))+ deriving (Eq, Show, Read)++instance Transformer SAT3ToMaxSAT2Info where+ type Source SAT3ToMaxSAT2Info = SAT.Model+ type Target SAT3ToMaxSAT2Info = SAT.Model++instance ForwardTransformer SAT3ToMaxSAT2Info where+ transformForward (SAT3ToMaxSAT2Info nv1 nv2 ds) m = runSTUArray $ do+ m2 <- newArray_ (1,nv2)+ forM_ [1..nv1] $ \v -> do+ writeArray m2 v (SAT.evalVar m v)+ forM_ (IntMap.toList ds) $ \(d, (a,b,c)) -> do+ let n :: Int+ n = sum [1 | l <- [a,b,c], SAT.evalLit m l]+ writeArray m2 d $+ case n of+ 1 -> False+ 2 -> False -- True is also OK+ 3 -> True+ _ -> False -- precondition is violated+ return m2++instance BackwardTransformer SAT3ToMaxSAT2Info where+ transformBackward (SAT3ToMaxSAT2Info nv1 _nv2 _ds) = SAT.restrictModel nv1++-- ------------------------------------------------------------------------++type MaxSAT2ToSimpleMaxCutInfo = ComposedTransformer SimplifyMaxSAT2Info SimpleMaxSAT2ToSimpleMaxCutInfo++maxSAT2ToSimpleMaxCut :: (CNF.WCNF, Integer) -> ((MaxCut.Problem Integer, Integer), MaxSAT2ToSimpleMaxCutInfo)+maxSAT2ToSimpleMaxCut x = (x2, (ComposedTransformer info1 info2))+ where+ (x1, info1) = simplifyMaxSAT2 x+ (x2, info2) = simpleMaxSAT2ToSimpleMaxCut x1++-- ------------------------------------------------------------------------++type SimpleMaxSAT2 = (Int, Set (Int, Int), Integer)++simplifyMaxSAT2 :: (CNF.WCNF, Integer) -> (SimpleMaxSAT2, SimplifyMaxSAT2Info)+simplifyMaxSAT2 (wcnf, threshold) =+ case foldl' f (nv1, Set.empty, IntMap.empty, threshold) (CNF.wcnfClauses wcnf) of+ (nv2, cs, defs, threshold2) -> ((nv2, cs, threshold2), SimplifyMaxSAT2Info nv1 nv2 defs)+ where+ nv1 = CNF.wcnfNumVars wcnf+ f r@(nv, cs, defs, t) (w, clause) =+ case SAT.unpackClause clause of+ [] -> (nv, cs, defs, t-w)+ [a] -> applyN w (insert (a,a)) r+ [a,b] -> applyN w (insert (min a b, max a b)) r+ _ -> error "should not happen"+ insert c@(a,b) (nv,cs,defs,t)+ | c `Set.member` cs = (v, Set.insert (a,v) $ Set.insert (b,-v) cs, IntMap.insert v (a,b) defs, t)+ | otherwise = (nv, Set.insert c cs, defs, t)+ where+ v = nv + 1++applyN :: Integral n => n -> (a -> a) -> (a -> a)+applyN n f = appEndo $ mconcat $ genericReplicate n (Endo f)++data SimplifyMaxSAT2Info+ = SimplifyMaxSAT2Info !Int !Int (IntMap (SAT.Lit, SAT.Lit))+ deriving (Eq, Show, Read)++instance Transformer SimplifyMaxSAT2Info where+ type Source SimplifyMaxSAT2Info = SAT.Model+ type Target SimplifyMaxSAT2Info = SAT.Model++instance ForwardTransformer SimplifyMaxSAT2Info where+ transformForward (SimplifyMaxSAT2Info _nv1 nv2 defs) m =+ array (1,nv2) $ assocs m ++ [(v, if SAT.evalLit m a then False else True) | (v,(a,_b)) <- IntMap.toList defs]++instance BackwardTransformer SimplifyMaxSAT2Info where+ transformBackward (SimplifyMaxSAT2Info nv1 _nv2 _defs) m = SAT.restrictModel nv1 m++-- ------------------------------------------------------------------------++simpleMaxSAT2ToSimpleMaxCut+ :: SimpleMaxSAT2+ -> ( (MaxCut.Problem Integer, Integer)+ , SimpleMaxSAT2ToSimpleMaxCutInfo+ )+simpleMaxSAT2ToSimpleMaxCut (n, cs, threshold) =+ ( ( MaxCut.fromEdges numNodes [(a,b,1) | (a,b) <- (basicFramework ++ additionalEdges)]+ , w+ )+ , SimpleMaxSAT2ToSimpleMaxCutInfo n p+ )+ where+ p = Set.size cs+ (numNodes, tt, ff, t, f ,xp, xn, l) = simpleMaxSAT2ToSimpleMaxCutNodes n p++ basicFramework =+ [(tt i, ff j) | i <- [0..3*p], j <- [0..3*p]] +++ [(t i j, f i j) | i <- [1..n], j <- [0..3*p]] +++ [(xp i, f i j) | i <- [1..n], j <- [0..3*p]] +++ [(xn i, t i j) | i <- [1..n], j <- [0..3*p]]+ sizeOfBasicFramework = (3*p+1)^(2::Int) + 3 * n*(3*p+1)++ additionalEdges =+ [ (l a, l b) | (a,b) <- Set.toList cs, a /= b ] +++ [ (l a, ff (2*i-1)) | (i, (a,_b)) <- zip [1..] (Set.toList cs) ] +++ [ (l b, ff (2*i )) | (i, (_a,b)) <- zip [1..] (Set.toList cs) ]++ k = fromIntegral (Set.size cs) - threshold+ w = fromIntegral sizeOfBasicFramework + 2*k+++simpleMaxSAT2ToSimpleMaxCutNodes+ :: Int -> Int+ -> ( Int+ , Int -> Int+ , Int -> Int+ , SAT.Var -> Int -> Int+ , SAT.Var -> Int -> Int+ , SAT.Var -> Int+ , SAT.Var -> Int+ , SAT.Lit -> Int+ )+simpleMaxSAT2ToSimpleMaxCutNodes n p = (numNodes, tt, ff, t, f ,xp, xn, l)+ where+ numNodes = (3*p+1) + (3*p+1) + n*(3*p+1) + n*(3*p+1) + n + n+ tt i = i+ ff i = (3*p+1) + i+ t i j = (3*p+1) + (3*p+1) + (i-1)*(3*p+1) + j+ f i j = (3*p+1) + (3*p+1) + n*(3*p+1) + (i-1)*(3*p+1) + j+ xp i = (3*p+1) + (3*p+1) + n*(3*p+1) + n*(3*p+1) + (i-1)+ xn i = (3*p+1) + (3*p+1) + n*(3*p+1) + n*(3*p+1) + n + (i-1)+ l x = if x > 0 then xp x else xn (- x)+++data SimpleMaxSAT2ToSimpleMaxCutInfo+ = SimpleMaxSAT2ToSimpleMaxCutInfo !Int !Int+ deriving (Eq, Show, Read)++instance Transformer SimpleMaxSAT2ToSimpleMaxCutInfo where+ type Source SimpleMaxSAT2ToSimpleMaxCutInfo = SAT.Model+ type Target SimpleMaxSAT2ToSimpleMaxCutInfo = MaxCut.Solution++instance ForwardTransformer SimpleMaxSAT2ToSimpleMaxCutInfo where+ transformForward (SimpleMaxSAT2ToSimpleMaxCutInfo n p) m =+ array (0,numNodes-1) [(v, not (v `IntSet.member` s1)) | v <- [0..numNodes-1]]+ where+ (numNodes, _tt, ff, t, f ,xp, xn, _l) = simpleMaxSAT2ToSimpleMaxCutNodes n p+ s1 = IntSet.fromList $+ [ff i | i <- [0..3*p]] +++ [xp i | i <- [1..n], not (SAT.evalVar m i)] +++ [t i j | i <- [1..n], not (SAT.evalVar m i), j <- [0..3*p]] +++ [xn i | i <- [1..n], SAT.evalVar m i] +++ [f i j | i <- [1..n], SAT.evalVar m i, j <- [0..3*p]]++instance BackwardTransformer SimpleMaxSAT2ToSimpleMaxCutInfo where+ transformBackward (SimpleMaxSAT2ToSimpleMaxCutInfo n p) sol+ | p == 0 = array (1,n) [(i, False) | i <- [1..n]]+ | otherwise = array (1,n) [(i, (sol ! xp i) == b) | i <- [1..n]]+ where+ (_numNodes, _tt, ff, _t, _f ,xp, _xn, _l) = simpleMaxSAT2ToSimpleMaxCutNodes n p+ b = not (sol ! ff 0)++-- ------------------------------------------------------------------------++type SATToSimpleMaxCutInfo = ComposedTransformer SATToMaxSAT2Info MaxSAT2ToSimpleMaxCutInfo++satToSimpleMaxCut :: CNF.CNF -> ((MaxCut.Problem Integer, Integer), SATToSimpleMaxCutInfo)+satToSimpleMaxCut x = (x2, (ComposedTransformer info1 info2))+ where+ (x1, info1) = satToMaxSAT2 x+ (x2, info2) = maxSAT2ToSimpleMaxCut x1++-- ------------------------------------------------------------------------+
− src/ToySolver/Converter/SAT2PB.hs
@@ -1,29 +0,0 @@-{-# OPTIONS_GHC -Wall #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.SAT2PB--- Copyright : (c) Masahiro Sakai 2013--- License : BSD-style--- --- Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : portable----------------------------------------------------------------------------------module ToySolver.Converter.SAT2PB- ( convert- ) where--import qualified Data.PseudoBoolean as PBFile-import qualified ToySolver.Text.CNF as CNF--convert :: CNF.CNF -> PBFile.Formula-convert cnf- = PBFile.Formula- { PBFile.pbObjectiveFunction = Nothing- , PBFile.pbConstraints = map f (CNF.clauses cnf)- , PBFile.pbNumVars = CNF.numVars cnf- , PBFile.pbNumConstraints = CNF.numClauses cnf- }- where- f clause = ([(1,[l]) | l <- clause], PBFile.Ge, 1)
+ src/ToySolver/Converter/Tseitin.hs view
@@ -0,0 +1,42 @@++{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Converter.Tseitin+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : experimental+-- Portability : non-portable+--+-----------------------------------------------------------------------------+module ToySolver.Converter.Tseitin+ ( TseitinInfo (..)+ ) where++import Data.Array.IArray+import ToySolver.Converter.Base+import qualified ToySolver.SAT.Types as SAT+import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin++data TseitinInfo = TseitinInfo !Int !Int [(SAT.Var, Tseitin.Formula)]+ deriving (Eq, Show, Read)++instance Transformer TseitinInfo where+ type Source TseitinInfo = SAT.Model+ type Target TseitinInfo = SAT.Model++instance ForwardTransformer TseitinInfo where+ transformForward (TseitinInfo _nv1 nv2 defs) m = array (1, nv2) (assocs a)+ where+ -- Use BOXED array to tie the knot+ a :: Array SAT.Var Bool+ a = array (1, nv2) $+ assocs m ++ [(v, Tseitin.evalFormula a phi) | (v, phi) <- defs]++instance BackwardTransformer TseitinInfo where+ transformBackward (TseitinInfo nv1 _nv2 _defs) = SAT.restrictModel nv1++-- -----------------------------------------------------------------------------
− src/ToySolver/Converter/WBO2MaxSAT.hs
@@ -1,89 +0,0 @@-{-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE FlexibleContexts, MultiParamTypeClasses #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.WBO2MaxSAT--- Copyright : (c) Masahiro Sakai 2016--- License : BSD-style------ Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : non-portable (FlexibleContexts, MultiParamTypeClasses)----------------------------------------------------------------------------------module ToySolver.Converter.WBO2MaxSAT (convert) where--import Control.Applicative-import Control.Monad-import Control.Monad.ST-import Data.Array.IArray-import qualified Data.Foldable as F-import Data.Monoid-import qualified Data.Sequence as Seq-import qualified Data.PseudoBoolean as PBFile--import qualified ToySolver.SAT.Types as SAT-import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin-import qualified ToySolver.SAT.Encoder.PB as PB-import qualified ToySolver.SAT.Encoder.PBNLC as PBNLC-import ToySolver.SAT.Store.CNF-import qualified ToySolver.Text.MaxSAT as MaxSAT-import qualified ToySolver.Text.CNF as CNF--convert :: PBFile.SoftFormula -> (MaxSAT.WCNF, SAT.Model -> SAT.Model, SAT.Model -> SAT.Model)-convert formula = runST $ do- db <- newCNFStore- SAT.newVars_ db (PBFile.wboNumVars formula)- tseitin <- Tseitin.newEncoder db- pb <- PB.newEncoder tseitin- pbnlc <- PBNLC.newEncoder pb tseitin-- softClauses <- liftM mconcat $ forM (PBFile.wboConstraints formula) $ \(cost, (lhs,op,rhs)) -> do- case cost of- Nothing ->- case op of- PBFile.Ge -> SAT.addPBNLAtLeast pbnlc lhs rhs >> return mempty- PBFile.Eq -> SAT.addPBNLExactly pbnlc lhs rhs >> return mempty- Just c -> do- case op of- PBFile.Ge -> do- lhs2 <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityPos lhs- let (lhs3,rhs3) = SAT.normalizePBLinAtLeast (lhs2,rhs)- if rhs3==1 && and [c==1 | (c,_) <- lhs3] then- return $ Seq.singleton (c, [l | (_,l) <- lhs3])- else do- lit <- PB.encodePBLinAtLeast pb (lhs3,rhs3)- return $ Seq.singleton (c, [lit])- PBFile.Eq -> do- lhs2 <- PBNLC.linearizePBSumWithPolarity pbnlc Tseitin.polarityBoth lhs- lit1 <- PB.encodePBLinAtLeast pb (lhs2, rhs)- lit2 <- PB.encodePBLinAtLeast pb ([(-c, l) | (c,l) <- lhs2], negate rhs)- lit <- Tseitin.encodeConjWithPolarity tseitin Tseitin.polarityPos [lit1,lit2]- return $ Seq.singleton (c, [lit])-- case PBFile.wboTopCost formula of- Nothing -> return ()- Just top -> SAT.addPBNLAtMost pbnlc [(c, [-l | l <- clause]) | (c,clause) <- F.toList softClauses] (top - 1)-- let top = F.sum (fst <$> softClauses) + 1- cnf <- getCNFFormula db- let cs = softClauses <> Seq.fromList [(top, clause) | clause <- CNF.clauses cnf]- let wcnf = MaxSAT.WCNF- { MaxSAT.numVars = CNF.numVars cnf- , MaxSAT.numClauses = Seq.length cs- , MaxSAT.topCost = top- , MaxSAT.clauses = F.toList cs- }-- defs <- Tseitin.getDefinitions tseitin- let extendModel :: SAT.Model -> SAT.Model- extendModel m = array (1, CNF.numVars cnf) (assocs a)- where- -- Use BOXED array to tie the knot- a :: Array SAT.Var Bool- a = array (1, CNF.numVars cnf) $- assocs m ++ [(v, Tseitin.evalFormula a phi) | (v, phi) <- defs]-- return (wcnf, extendModel, SAT.restrictModel (PBFile.wboNumVars formula))---- -----------------------------------------------------------------------------
− src/ToySolver/Converter/WBO2PB.hs
@@ -1,93 +0,0 @@-{-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE MultiParamTypeClasses #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Converter.WBO2PB--- Copyright : (c) Masahiro Sakai 2013,2016--- License : BSD-style--- --- Maintainer : masahiro.sakai@gmail.com--- Stability : experimental--- Portability : non-portable (MultiParamTypeClasses)----------------------------------------------------------------------------------module ToySolver.Converter.WBO2PB- ( convert- , addWBO- ) where--import Control.Monad-import Control.Monad.Primitive-import Control.Monad.ST-import Data.Array.IArray-import Data.Primitive.MutVar-import qualified ToySolver.SAT.Types as SAT-import ToySolver.SAT.Store.PB-import qualified Data.PseudoBoolean as PBFile--convert :: PBFile.SoftFormula -> (PBFile.Formula, SAT.Model -> SAT.Model, SAT.Model -> SAT.Model)-convert wbo = runST $ do- let nv = PBFile.wboNumVars wbo- db <- newPBStore- (obj, defs) <- addWBO db wbo - formula <- getPBFormula db-- let mforth :: SAT.Model -> SAT.Model- mforth m = array (1, PBFile.pbNumVars formula) $ assocs m ++ [(v, SAT.evalPBConstraint m constr) | (v, constr) <- defs]-- mback :: SAT.Model -> SAT.Model- mback = SAT.restrictModel nv-- return- ( formula{ PBFile.pbObjectiveFunction = Just obj }- , mforth- , mback- )---addWBO :: (PrimMonad m, SAT.AddPBNL m enc) => enc -> PBFile.SoftFormula -> m (SAT.PBSum, [(SAT.Var, PBFile.Constraint)])-addWBO db wbo = do- SAT.newVars_ db $ PBFile.wboNumVars wbo-- objRef <- newMutVar []- defsRef <- newMutVar []- forM_ (PBFile.wboConstraints wbo) $ \(cost, constr@(lhs,op,rhs)) -> do- case cost of- Nothing -> do- case op of- PBFile.Ge -> SAT.addPBNLAtLeast db lhs rhs- PBFile.Eq -> SAT.addPBNLExactly db lhs rhs- Just w -> do- case op of- PBFile.Ge -> do- case lhs of- [(1,ls)] | rhs == 1 -> do- -- ∧L ≥ 1 ⇔ ∧L- -- obj += w * (1 - ∧L)- modifyMutVar objRef (\obj -> (w,[]) : (-w,ls) : obj)- [(-1,ls)] | rhs == 0 -> do- -- -1*∧L ≥ 0 ⇔ (1 - ∧L) ≥ 1 ⇔ ¬∧L- -- obj += w * ∧L- modifyMutVar objRef ((w,ls) :)- _ | and [c==1 && length ls == 1 | (c,ls) <- lhs] && rhs == 1 -> do- -- ∑L ≥ 1 ⇔ ∨L ⇔ ¬∧¬L- -- obj += w * ∧¬L- modifyMutVar objRef ((w, [-l | (_,[l]) <- lhs]) :)- _ -> do- sel <- SAT.newVar db- SAT.addPBNLAtLeastSoft db sel lhs rhs- modifyMutVar objRef ((w,[-sel]) :)- modifyMutVar defsRef ((sel,constr) :)- PBFile.Eq -> do- sel <- SAT.newVar db- SAT.addPBNLExactlySoft db sel lhs rhs- modifyMutVar objRef ((w,[-sel]) :)- modifyMutVar defsRef ((sel,constr) :)- obj <- liftM reverse $ readMutVar objRef- defs <- liftM reverse $ readMutVar defsRef-- case PBFile.wboTopCost wbo of- Nothing -> return ()- Just t -> SAT.addPBNLAtMost db obj (t - 1)-- return (obj, defs)
src/ToySolver/Data/AlgebraicNumber/Real.hs view
@@ -53,7 +53,7 @@ import Data.Ratio import qualified Data.Set as Set import qualified Text.PrettyPrint.HughesPJClass as PP-import Text.PrettyPrint.HughesPJClass (Doc, PrettyLevel, Pretty (..), prettyParen)+import Text.PrettyPrint.HughesPJClass (Doc, PrettyLevel, Pretty (..), maybeParens) import Data.Interval (Interval, Extended (..), (<=..<), (<..<=), (<..<), (<!), (>!)) import qualified Data.Interval as Interval@@ -418,7 +418,7 @@ instance Pretty AReal where pPrintPrec lv prec r =- prettyParen (prec > appPrec) $+ maybeParens (prec > appPrec) $ PP.hsep [PP.text "RealRoot", pPrintPrec lv (appPrec+1) p, PP.int (rootIndex r)] where p = minimalPolynomial r
src/ToySolver/Data/AlgebraicNumber/Root.hs view
@@ -88,7 +88,7 @@ f_a_b = f (P.var a) (P.var b) gbase :: [Polynomial k Var]- gbase = [ P.subst p1 (\X -> P.var a), P.subst p2 (\X -> P.var b) ] + gbase = [ P.subst p1 (\X -> P.var a), P.subst p2 (\X -> P.var b) ] -- | Given a polynomial P and polynomials {P1,…,Pn} over K, -- findPoly P [P1..Pn] computes a non-zero polynomial Q such that Q[P] = 0 modulo {P1,…,Pn}.
src/ToySolver/Data/BoolExpr.hs view
@@ -23,11 +23,9 @@ , simplify ) where -import Control.Applicative import Control.DeepSeq import Control.Monad import Data.Data-import Data.Foldable hiding (fold, concat, any) import Data.Hashable import Data.Traversable import ToySolver.Data.Boolean@@ -150,7 +148,7 @@ ite (Simplify c) (Simplify t) (Simplify e) | isTrue c = Simplify t | isFalse c = Simplify e- | otherwise = Simplify (ITE c t e) + | otherwise = Simplify (ITE c t e) instance Boolean (Simplify a) where Simplify x .=>. Simplify y
src/ToySolver/Data/Boolean.hs view
@@ -27,7 +27,8 @@ infixr 3 .&&. infixr 2 .||.-infix 1 .=>., .<=>.+infixr 1 .=>.+infix 1 .<=>. class MonotoneBoolean a where true, false :: a
src/ToySolver/Data/Delta.hs view
@@ -17,7 +17,7 @@ -- \"/A Fast Linear-Arithmetic Solver for DPLL(T)/\", -- Computer Aided Verification In Computer Aided Verification, Vol. 4144 -- (2006), pp. 81-94.--- <http://dx.doi.org/10.1007/11817963_11>+-- <https://doi.org/10.1007/11817963_11> -- <http://yices.csl.sri.com/cav06.pdf> -- -----------------------------------------------------------------------------
src/ToySolver/Data/FOL/Arith.hs view
@@ -25,7 +25,7 @@ , evalAtom -- * Arithmetic formula- , module ToySolver.Data.FOL.Formula + , module ToySolver.Data.FOL.Formula -- * Misc , SatResult (..)
src/ToySolver/Data/LA.hs view
@@ -32,7 +32,7 @@ , coeff , lookupCoeff , extract- , extractMaybe + , extractMaybe -- ** Operations , mapCoeff@@ -215,7 +215,7 @@ -- lookupCoeff v e == fmap fst (extractMaybe v e) -- @ lookupCoeff :: Num r => Var -> Expr r -> Maybe r-lookupCoeff v (Expr m) = IntMap.lookup v m +lookupCoeff v (Expr m) = IntMap.lookup v m -- | @extract v e@ returns @(c, e')@ such that @e == c *^ v ^+^ e'@ extract :: Num r => Var -> Expr r -> (r, Expr r)
src/ToySolver/Data/MIP.hs view
@@ -1,5 +1,6 @@ {-# OPTIONS_GHC -Wall #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Data.MIP@@ -25,7 +26,7 @@ , writeMPSFile , toLPString , toMPSString- , ParseError (..)+ , ParseError ) where import Prelude hiding (readFile, writeFile)@@ -34,81 +35,128 @@ import Data.Scientific (Scientific) import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.IO as TLIO-#if MIN_VERSION_megaparsec(6,0,0)-import Data.Void-#endif-import System.FilePath (takeExtension)+import System.FilePath (takeExtension, splitExtension) import System.IO hiding (readFile, writeFile)-import Text.Megaparsec import ToySolver.Data.MIP.Base+import ToySolver.Data.MIP.FileUtils (ParseError) import qualified ToySolver.Data.MIP.LPFile as LPFile import qualified ToySolver.Data.MIP.MPSFile as MPSFile +#ifdef WITH_ZLIB+import qualified Codec.Compression.GZip as GZip+import qualified Data.ByteString.Lazy as BL+import Data.ByteString.Lazy.Encoding (encode, decode)+import qualified Data.CaseInsensitive as CI+import GHC.IO.Encoding (getLocaleEncoding)+#endif+ -- | Parse .lp or .mps file based on file extension readFile :: FileOptions -> FilePath -> IO (Problem Scientific) readFile opt fname =- case map toLower (takeExtension fname) of+ case getExt fname of ".lp" -> readLPFile opt fname ".mps" -> readMPSFile opt fname ext -> ioError $ userError $ "unknown extension: " ++ ext -- | Parse a file containing LP file data. readLPFile :: FileOptions -> FilePath -> IO (Problem Scientific)+#ifndef WITH_ZLIB readLPFile = LPFile.parseFile+#else+readLPFile opt fname = do+ s <- readTextFile opt fname+ let ret = LPFile.parseString opt fname s+ case ret of+ Left e -> throw e+ Right a -> return a+#endif -- | Parse a file containing MPS file data. readMPSFile :: FileOptions -> FilePath -> IO (Problem Scientific)+#ifndef WITH_ZLIB readMPSFile = MPSFile.parseFile+#else+readMPSFile opt fname = do+ s <- readTextFile opt fname+ let ret = MPSFile.parseString opt fname s+ case ret of+ Left e -> throw e+ Right a -> return a+#endif --- | Parse a string containing LP file data.-#if MIN_VERSION_megaparsec(6,0,0)-parseLPString :: FileOptions -> String -> String -> Either (ParseError Char Void) (Problem Scientific)-#elif MIN_VERSION_megaparsec(5,0,0)-parseLPString :: FileOptions -> String -> String -> Either (ParseError Char Dec) (Problem Scientific)+readTextFile :: FileOptions -> FilePath -> IO TL.Text+#ifndef WITH_ZLIB+readTextFile opt fname = do+ h <- openFile fname ReadMode+ case MIP.optFileEncoding opt of+ Nothing -> return ()+ Just enc -> hSetEncoding h enc+ TLIO.hGetContents h #else-parseLPString :: FileOptions -> String -> String -> Either ParseError (Problem Scientific)+readTextFile opt fname = do+ enc <- case optFileEncoding opt of+ Nothing -> getLocaleEncoding+ Just enc -> return enc+ let f = if CI.mk (takeExtension fname) == ".gz" then GZip.decompress else id+ s <- BL.readFile fname+ return $ decode enc $ f s #endif++-- | Parse a string containing LP file data.+parseLPString :: FileOptions -> String -> String -> Either (ParseError String) (Problem Scientific) parseLPString = LPFile.parseString -- | Parse a string containing MPS file data.-#if MIN_VERSION_megaparsec(6,0,0)-parseMPSString :: FileOptions -> String -> String -> Either (ParseError Char Void) (Problem Scientific)-#elif MIN_VERSION_megaparsec(5,0,0)-parseMPSString :: FileOptions -> String -> String -> Either (ParseError Char Dec) (Problem Scientific)-#else-parseMPSString :: FileOptions -> String -> String -> Either ParseError (Problem Scientific)-#endif+parseMPSString :: FileOptions -> String -> String -> Either (ParseError String) (Problem Scientific) parseMPSString = MPSFile.parseString writeFile :: FileOptions -> FilePath -> Problem Scientific -> IO () writeFile opt fname prob =- case map toLower (takeExtension fname) of+ case getExt fname of ".lp" -> writeLPFile opt fname prob ".mps" -> writeMPSFile opt fname prob ext -> ioError $ userError $ "unknown extension: " ++ ext +getExt :: String -> String+getExt name | (base, ext) <- splitExtension name =+ case map toLower ext of+#ifdef WITH_ZLIB+ ".gz" -> getExt base+#endif+ s -> s+ writeLPFile :: FileOptions -> FilePath -> Problem Scientific -> IO () writeLPFile opt fname prob = case LPFile.render opt prob of Left err -> ioError $ userError err- Right s ->- withFile fname WriteMode $ \h -> do- case optFileEncoding opt of- Nothing -> return ()- Just enc -> hSetEncoding h enc- TLIO.hPutStr h s+ Right s -> writeTextFile opt fname s writeMPSFile :: FileOptions -> FilePath -> Problem Scientific -> IO () writeMPSFile opt fname prob = case MPSFile.render opt prob of Left err -> ioError $ userError err- Right s ->- withFile fname WriteMode $ \h -> do- case optFileEncoding opt of- Nothing -> return ()- Just enc -> hSetEncoding h enc- TLIO.hPutStr h s+ Right s -> writeTextFile opt fname s++writeTextFile :: FileOptions -> FilePath -> TL.Text -> IO ()+writeTextFile opt fname s = do+ let writeSimple = do+ withFile fname WriteMode $ \h -> do+ case optFileEncoding opt of+ Nothing -> return ()+ Just enc -> hSetEncoding h enc+ TLIO.hPutStr h s+#ifdef WITH_ZLIB+ if CI.mk (takeExtension fname) /= ".gz" then do+ writeSimple+ else do+ enc <- case optFileEncoding opt of+ Nothing -> getLocaleEncoding+ Just enc -> return enc+ BL.writeFile fname $ GZip.compress $ encode enc s+#else+ writeSimple+#endif toLPString :: FileOptions -> Problem Scientific -> Either String TL.Text toLPString = LPFile.render
src/ToySolver/Data/MIP/Base.hs view
@@ -1,8 +1,9 @@ {-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Data.MIP.Base--- Copyright : (c) Masahiro Sakai 2011-2014+-- Copyright : (c) Masahiro Sakai 2011-2019 -- License : BSD-style -- -- Maintainer : masahiro.sakai@gmail.com@@ -69,6 +70,7 @@ -- * Solutions , Solution (..) , Status (..)+ , meetStatus -- * File I/O options , FileOptions (..)@@ -78,7 +80,9 @@ , intersectBounds ) where +#if !MIN_VERSION_lattices(2,0,0) import Algebra.Lattice+#endif import Algebra.PartialOrd import Control.Arrow ((***)) import Data.Default.Class@@ -351,27 +355,36 @@ , (StatusInfeasibleOrUnbounded, StatusInfeasible) ] ++meetStatus :: Status -> Status -> Status+StatusUnknown `meetStatus` b = StatusUnknown+StatusFeasible `meetStatus` b+ | StatusFeasible `leq` b = StatusFeasible+ | otherwise = StatusUnknown+StatusOptimal `meetStatus` StatusOptimal = StatusOptimal+StatusOptimal `meetStatus` b+ | StatusFeasible `leq` b = StatusFeasible+ | otherwise = StatusUnknown+StatusInfeasibleOrUnbounded `meetStatus` b+ | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded+ | otherwise = StatusUnknown+StatusInfeasible `meetStatus` StatusInfeasible = StatusInfeasible+StatusInfeasible `meetStatus` b+ | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded+ | otherwise = StatusUnknown+StatusUnbounded `meetStatus` StatusUnbounded = StatusUnbounded+StatusUnbounded `meetStatus` b+ | StatusFeasible `leq` b = StatusFeasible+ | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded+ | otherwise = StatusUnknown++#if !MIN_VERSION_lattices(2,0,0)+ instance MeetSemiLattice Status where- StatusUnknown `meet` b = StatusUnknown- StatusFeasible `meet` b- | StatusFeasible `leq` b = StatusFeasible- | otherwise = StatusUnknown- StatusOptimal `meet` StatusOptimal = StatusOptimal- StatusOptimal `meet` b- | StatusFeasible `leq` b = StatusFeasible- | otherwise = StatusUnknown- StatusInfeasibleOrUnbounded `meet` b- | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded- | otherwise = StatusUnknown- StatusInfeasible `meet` StatusInfeasible = StatusInfeasible- StatusInfeasible `meet` b- | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded- | otherwise = StatusUnknown- StatusUnbounded `meet` StatusUnbounded = StatusUnbounded- StatusUnbounded `meet` b- | StatusFeasible `leq` b = StatusFeasible- | StatusInfeasibleOrUnbounded `leq` b = StatusInfeasibleOrUnbounded- | otherwise = StatusUnknown+ meet = meetStatus++#endif+ data Solution r = Solution
+ src/ToySolver/Data/MIP/FileUtils.hs view
@@ -0,0 +1,31 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Data.MIP.FileUtils+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : portable+--+-----------------------------------------------------------------------------+module ToySolver.Data.MIP.FileUtils+ ( ParseError+ ) where++#if MIN_VERSION_megaparsec(6,0,0)+import Data.Void+#endif+import qualified Text.Megaparsec as MP++#if MIN_VERSION_megaparsec(7,0,0)+type ParseError s = MP.ParseErrorBundle s Void+#elif MIN_VERSION_megaparsec(6,0,0)+type ParseError s = MP.ParseError (MP.Token s) Void+#elif MIN_VERSION_megaparsec(5,0,0)+type ParseError s = MP.ParseError (MP.Token s) MP.Dec+#else+type ParseError s = MP.ParseError+#endif
src/ToySolver/Data/MIP/LPFile.hs view
@@ -30,12 +30,13 @@ module ToySolver.Data.MIP.LPFile ( parseString , parseFile+ , ParseError , parser , render ) where -import Control.Applicative-import Control.Exception (throw)+import Control.Applicative hiding (many)+import Control.Exception (throwIO) import Control.Monad import Control.Monad.Writer import Control.Monad.ST@@ -62,21 +63,19 @@ import qualified Data.Text.Lazy.Builder.Scientific as B import qualified Data.Text.Lazy.IO as TLIO import Data.OptDir-#if MIN_VERSION_megaparsec(6,0,0)-import Data.Void-#endif import System.IO #if MIN_VERSION_megaparsec(6,0,0)-import Text.Megaparsec hiding (label, skipManyTill)+import Text.Megaparsec hiding (label, skipManyTill, ParseError) import Text.Megaparsec.Char hiding (string', char') import qualified Text.Megaparsec.Char.Lexer as P #else-import Text.Megaparsec hiding (label, string', char')+import Text.Megaparsec hiding (label, string', char', ParseError) import qualified Text.Megaparsec.Lexer as P import Text.Megaparsec.Prim (MonadParsec ()) #endif import qualified ToySolver.Data.MIP.Base as MIP+import ToySolver.Data.MIP.FileUtils (ParseError) import ToySolver.Internal.Util (combineMaybe) -- ---------------------------------------------------------------------------@@ -92,11 +91,11 @@ -- | Parse a string containing LP file data. -- The source name is only | used in error messages and may be the empty string. #if MIN_VERSION_megaparsec(6,0,0)-parseString :: (Stream s, Token s ~ Char, IsString (Tokens s)) => MIP.FileOptions -> String -> s -> Either (ParseError Char Void) (MIP.Problem Scientific)+parseString :: (Stream s, Token s ~ Char, IsString (Tokens s)) => MIP.FileOptions -> String -> s -> Either (ParseError s) (MIP.Problem Scientific) #elif MIN_VERSION_megaparsec(5,0,0)-parseString :: (Stream s, Token s ~ Char) => MIP.FileOptions -> String -> s -> Either (ParseError Char Dec) (MIP.Problem Scientific)+parseString :: (Stream s, Token s ~ Char) => MIP.FileOptions -> String -> s -> Either (ParseError s) (MIP.Problem Scientific) #else-parseString :: Stream s Char => MIP.FileOptions -> String -> s -> Either ParseError (MIP.Problem Scientific)+parseString :: Stream s Char => MIP.FileOptions -> String -> s -> Either (ParseError s) (MIP.Problem Scientific) #endif parseString _ = parse (parser <* eof) @@ -109,16 +108,15 @@ Just enc -> hSetEncoding h enc ret <- parse (parser <* eof) fname <$> TLIO.hGetContents h case ret of-#if MIN_VERSION_megaparsec(6,0,0)- Left e -> throw (e :: ParseError Char Void)-#elif MIN_VERSION_megaparsec(5,0,0)- Left e -> throw (e :: ParseError Char Dec)-#else- Left e -> throw (e :: ParseError)-#endif+ Left e -> throwIO (e :: ParseError TL.Text) Right a -> return a -- ---------------------------------------------------------------------------++#if MIN_VERSION_megaparsec(7,0,0)+anyChar :: C e s m => m Char+anyChar = anySingle+#endif char' :: C e s m => Char -> m Char char' c = (char c <|> char (toUpper c)) <?> show c
src/ToySolver/Data/MIP/MPSFile.hs view
@@ -31,12 +31,13 @@ module ToySolver.Data.MIP.MPSFile ( parseString , parseFile+ , ParseError , parser , render ) where import Control.Applicative ((<$>), (<*))-import Control.Exception (throw)+import Control.Exception (throwIO) import Control.Monad import Control.Monad.Writer import Data.Default.Class@@ -55,24 +56,22 @@ import Data.Text.Lazy.Builder (Builder) import qualified Data.Text.Lazy.Builder as B import qualified Data.Text.Lazy.IO as TLIO-#if MIN_VERSION_megaparsec(6,0,0)-import Data.Void-#endif import System.IO #if MIN_VERSION_megaparsec(6,0,0)-import Text.Megaparsec+import Text.Megaparsec hiding (ParseError) import Text.Megaparsec.Char hiding (string', newline) import qualified Text.Megaparsec.Char as P import qualified Text.Megaparsec.Char.Lexer as Lexer #else import qualified Text.Megaparsec as P-import Text.Megaparsec hiding (string', newline)+import Text.Megaparsec hiding (string', newline, ParseError) import qualified Text.Megaparsec.Lexer as Lexer import Text.Megaparsec.Prim (MonadParsec ()) #endif import Data.OptDir import qualified ToySolver.Data.MIP.Base as MIP+import ToySolver.Data.MIP.FileUtils (ParseError) type Column = MIP.Var type Row = InternedText@@ -104,11 +103,11 @@ -- | Parse a string containing MPS file data. -- The source name is only | used in error messages and may be the empty string. #if MIN_VERSION_megaparsec(6,0,0)-parseString :: (Stream s, Token s ~ Char, IsString (Tokens s)) => MIP.FileOptions -> String -> s -> Either (ParseError Char Void) (MIP.Problem Scientific)+parseString :: (Stream s, Token s ~ Char, IsString (Tokens s)) => MIP.FileOptions -> String -> s -> Either (ParseError s) (MIP.Problem Scientific) #elif MIN_VERSION_megaparsec(5,0,0)-parseString :: (Stream s, Token s ~ Char) => MIP.FileOptions -> String -> s -> Either (ParseError Char Dec) (MIP.Problem Scientific)+parseString :: (Stream s, Token s ~ Char) => MIP.FileOptions -> String -> s -> Either (ParseError s) (MIP.Problem Scientific) #else-parseString :: Stream s Char => MIP.FileOptions -> String -> s -> Either ParseError (MIP.Problem Scientific)+parseString :: Stream s Char => MIP.FileOptions -> String -> s -> Either (ParseError s) (MIP.Problem Scientific) #endif parseString _ = parse (parser <* eof) @@ -121,16 +120,16 @@ Just enc -> hSetEncoding h enc ret <- parse (parser <* eof) fname <$> TLIO.hGetContents h case ret of-#if MIN_VERSION_megaparsec(6,0,0)- Left e -> throw (e :: ParseError Char Void)-#elif MIN_VERSION_megaparsec(5,0,0)- Left e -> throw (e :: ParseError Char Dec)-#else- Left e -> throw (e :: ParseError)-#endif+ Left e -> throwIO (e :: ParseError TL.Text) Right a -> return a -- ---------------------------------------------------------------------------+++#if MIN_VERSION_megaparsec(7,0,0)+anyChar :: C e s m => m Char+anyChar = anySingle+#endif space' :: C e s m => m Char space' = oneOf [' ', '\t']
src/ToySolver/Data/MIP/Solver/Glpsol.hs view
@@ -6,7 +6,6 @@ ) where import Algebra.PartialOrd-import Control.Monad import Data.Default.Class import Data.IORef import qualified Data.Text.Lazy.IO as TLIO
src/ToySolver/Data/Polyhedron.hs view
@@ -64,7 +64,7 @@ instance Lattice Polyhedron instance BoundedJoinSemiLattice Polyhedron where- bottom = empty + bottom = empty instance BoundedMeetSemiLattice Polyhedron where top = univ
src/ToySolver/Data/Polynomial/Base.hs view
@@ -144,7 +144,7 @@ import qualified Data.IntMap.Strict as IntMap import Data.VectorSpace import qualified Text.PrettyPrint.HughesPJClass as PP-import Text.PrettyPrint.HughesPJClass (Doc, PrettyLevel, Pretty (..), prettyParen)+import Text.PrettyPrint.HughesPJClass (Doc, PrettyLevel, Pretty (..), maybeParens) infixl 7 `div`, `mod` @@ -333,7 +333,7 @@ cont p = foldl1' Prelude.gcd ns % foldl' Prelude.lcm 1 ds where ns = [abs (numerator c) | (c,_) <- terms p]- ds = [denominator c | (c,_) <- terms p] + ds = [denominator c | (c,_) <- terms p] pp p = mapCoeff (numerator . (/ c)) p where@@ -522,12 +522,12 @@ [] -> PP.int 0 [t] -> pLeadingTerm prec t t:ts ->- prettyParen (prec > addPrec) $+ maybeParens (prec > addPrec) $ PP.hcat (pLeadingTerm addPrec t : map pTrailingTerm ts) where pLeadingTerm prec' (c,xs) = if pOptIsNegativeCoeff opt c- then prettyParen (prec' > addPrec) $+ then maybeParens (prec' > addPrec) $ PP.char '-' <> prettyPrintTerm opt lv (addPrec+1) (-c,xs) else prettyPrintTerm opt lv prec' (c,xs) @@ -545,7 +545,7 @@ -- intentionally specify (appPrec+1) to parenthesize any composite expression | len == 1 && c == 1 = pPow prec $ head (mindices xs) | otherwise =- prettyParen (prec > mulPrec) $+ maybeParens (prec > mulPrec) $ PP.hcat $ intersperse (PP.char '*') fs where len = Map.size $ mindicesMap xs@@ -554,7 +554,7 @@ pPow prec' (x,1) = pOptPrintVar opt lv prec' x pPow prec' (x,n) =- prettyParen (prec' > expPrec) $+ maybeParens (prec' > expPrec) $ pOptPrintVar opt lv (expPrec+1) x <> PP.char '^' <> PP.integer n class PrettyCoeff a where@@ -570,7 +570,7 @@ pPrintCoeff lv p r | denominator r == 1 = pPrintCoeff lv p (numerator r) | otherwise = - prettyParen (p > ratPrec) $+ maybeParens (p > ratPrec) $ pPrintCoeff lv (ratPrec+1) (numerator r) <> PP.char '/' <> pPrintCoeff lv (ratPrec+1) (denominator r)
src/ToySolver/Data/Polynomial/Factorization/Hensel/Internal.hs view
@@ -115,7 +115,7 @@ es = map (g*) $ cabook_proposition_5_10 fs c = sum [ei `P.div` fi | (ei,fi) <- zip es fs] es2 = case zipWith P.mod es fs of- e2' : es2' -> e2' + c * head fs : es2' + e2' : es2' -> e2' + c * head fs : es2' check :: [UPolynomial k] -> Bool check es' =
src/ToySolver/EUF/CongruenceClosure.hs view
@@ -119,7 +119,6 @@ instance Semigroup Class where xs <> ys = ClassUnion (classSize xs + classSize ys) xs ys- stimes = stimesIdempotent classSize :: Class -> Int classSize (ClassSingleton _) = 1
+ src/ToySolver/FileFormat.hs view
@@ -0,0 +1,30 @@+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.FileFormat+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+-- +-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-----------------------------------------------------------------------------+module ToySolver.FileFormat+ ( module ToySolver.FileFormat.Base+ ) where++import qualified Data.PseudoBoolean as PBFile+import qualified Data.PseudoBoolean.Attoparsec as PBFileAttoparsec+import qualified Data.PseudoBoolean.ByteStringBuilder as PBFileBB+import ToySolver.FileFormat.Base+import ToySolver.FileFormat.CNF () -- importing instances+import ToySolver.QUBO () -- importing instances++instance FileFormat PBFile.Formula where+ parse = PBFileAttoparsec.parseOPBByteString+ render = PBFileBB.opbBuilder++instance FileFormat PBFile.SoftFormula where+ parse = PBFileAttoparsec.parseWBOByteString+ render = PBFileBB.wboBuilder
+ src/ToySolver/FileFormat/Base.hs view
@@ -0,0 +1,88 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.FileFormat.Base+-- Copyright : (c) Masahiro Sakai 2016-2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-----------------------------------------------------------------------------+module ToySolver.FileFormat.Base+ (+ -- * FileFormat class+ FileFormat (..)+ , ParseError (..)+ , parseFile+ , readFile+ , writeFile+ ) where++import Prelude hiding (readFile, writeFile)+import Control.Exception+import Control.Monad.IO.Class+import qualified Data.ByteString.Lazy.Char8 as BS+import Data.ByteString.Builder+import Data.Typeable+import System.IO hiding (readFile, writeFile)++#ifdef WITH_ZLIB+import qualified Codec.Compression.GZip as GZip+import qualified Data.CaseInsensitive as CI+import System.FilePath+#endif++-- | A type class that abstracts file formats+class FileFormat a where+ -- | Parse a lazy byte string, and either returns error message or a parsed value+ parse :: BS.ByteString -> Either String a++ -- | Encode a value into 'Builder'+ render :: a -> Builder++-- | 'ParseError' represents a parse error and it wraps a error message.+data ParseError = ParseError String+ deriving (Show, Typeable)++instance Exception ParseError++-- | Parse a file but returns an error message when parsing fails.+parseFile :: (FileFormat a, MonadIO m) => FilePath -> m (Either String a)+parseFile filename = liftIO $ do+ s <- BS.readFile filename+#ifdef WITH_ZLIB+ let s2 = if CI.mk (takeExtension filename) == ".gz" then+ GZip.decompress s+ else+ s+#else+ let s2 = s+#endif+ return $ parse s2++-- | Parse a file. Similar to 'parseFile' but this function throws 'ParseError' when parsing fails.+readFile :: (FileFormat a, MonadIO m) => FilePath -> m a+readFile filename = liftIO $ do+ ret <- parseFile filename+ case ret of+ Left msg -> throwIO $ ParseError msg+ Right a -> return a++-- | Write a value into a file.+writeFile :: (FileFormat a, MonadIO m) => FilePath -> a -> m ()+writeFile filepath a = liftIO $ do+ withBinaryFile filepath WriteMode $ \h -> do+ hSetBuffering h (BlockBuffering Nothing)+#ifdef WITH_ZLIB+ if CI.mk (takeExtension filepath) == ".gz" then do+ BS.hPut h $ GZip.compress $ toLazyByteString $ render a+ else do+ hPutBuilder h (render a)+#else+ hPutBuilder h (render a)+#endif
+ src/ToySolver/FileFormat/CNF.hs view
@@ -0,0 +1,389 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.FileFormat.CNF+-- Copyright : (c) Masahiro Sakai 2016-2018+-- License : BSD-style+-- +-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-- Reader and Writer for DIMACS CNF and family of similar formats.+--+-----------------------------------------------------------------------------+module ToySolver.FileFormat.CNF+ (+ -- * FileFormat class+ module ToySolver.FileFormat.Base++ -- * CNF format+ , CNF (..)++ -- * WCNF format+ , WCNF (..)+ , WeightedClause+ , Weight++ -- * GCNF format+ , GCNF (..)+ , GroupIndex+ , GClause++ -- * QDIMACS format+ , QDimacs (..)+ , Quantifier (..)+ , QuantSet+ , Atom++ -- * Re-exports+ , Lit+ , Clause+ , PackedClause+ , packClause+ , unpackClause+ ) where++import Prelude hiding (readFile, writeFile)+import Control.DeepSeq+import qualified Data.ByteString.Lazy.Char8 as BS+import Data.ByteString.Builder+import Data.Char+#if !MIN_VERSION_base(4,11,0)+import Data.Monoid+#endif++import ToySolver.FileFormat.Base+import qualified ToySolver.SAT.Types as SAT+import ToySolver.SAT.Types (Lit, Clause, PackedClause, packClause, unpackClause)++-- -------------------------------------------------------------------++-- | DIMACS CNF format+data CNF+ = CNF+ { cnfNumVars :: !Int+ -- ^ Number of variables+ , cnfNumClauses :: !Int+ -- ^ Number of clauses+ , cnfClauses :: [SAT.PackedClause]+ -- ^ Clauses+ }+ deriving (Eq, Ord, Show, Read)++instance FileFormat CNF where+ parse s =+ case BS.words l of+ (["p","cnf", nvar, nclause]) ->+ Right $+ CNF+ { cnfNumVars = read $ BS.unpack nvar+ , cnfNumClauses = read $ BS.unpack nclause+ , cnfClauses = map parseClauseBS ls+ }+ _ ->+ Left "cannot find cnf header"+ where+ l :: BS.ByteString+ ls :: [BS.ByteString]+ (l:ls) = filter (not . isCommentBS) (BS.lines s)++ render cnf = header <> mconcat (map f (cnfClauses cnf))+ where+ header = mconcat+ [ byteString "p cnf "+ , intDec (cnfNumVars cnf), char7 ' '+ , intDec (cnfNumClauses cnf), char7 '\n'+ ]+ f c = mconcat [intDec lit <> char7 ' '| lit <- SAT.unpackClause c] <> byteString "0\n"++readInts :: BS.ByteString -> [Int]+readInts s =+ case BS.readInt (BS.dropWhile isSpace s) of+ Just (0,_) -> []+ Just (z,s') -> z : readInts s'+ Nothing -> error "ToySolver.FileFormat.CNF.readInts: 0 is missing"++parseClauseBS :: BS.ByteString -> SAT.PackedClause+parseClauseBS = SAT.packClause . readInts++isCommentBS :: BS.ByteString -> Bool+isCommentBS s =+ case BS.uncons s of+ Just ('c',_) -> True+ _ -> False++-- -------------------------------------------------------------------++-- | WCNF format for representing partial weighted Max-SAT problems.+--+-- This format is used for for MAX-SAT evaluations.+--+-- References:+--+-- * <http://maxsat.ia.udl.cat/requirements/>+data WCNF+ = WCNF+ { wcnfNumVars :: !Int+ -- ^ Number of variables+ , wcnfNumClauses :: !Int+ -- ^ Number of (weighted) clauses+ , wcnfTopCost :: !Weight+ -- ^ Hard clauses have weight equal or greater than "top". + -- We assure that "top" is a weight always greater than the sum of the weights of violated soft clauses in the solution.+ , wcnfClauses :: [WeightedClause]+ -- ^ Weighted clauses+ }+ deriving (Eq, Ord, Show, Read)++-- | Weighted clauses+type WeightedClause = (Weight, SAT.PackedClause)++-- | Weigths must be greater than or equal to 1, and smaller than 2^63.+type Weight = Integer++instance FileFormat WCNF where+ parse s =+ case BS.words l of+ (["p","wcnf", nvar, nclause, top]) ->+ Right $+ WCNF+ { wcnfNumVars = read $ BS.unpack nvar+ , wcnfNumClauses = read $ BS.unpack nclause+ , wcnfTopCost = read $ BS.unpack top+ , wcnfClauses = map parseWCNFLineBS ls+ }+ (["p","wcnf", nvar, nclause]) ->+ Right $+ WCNF+ { wcnfNumVars = read $ BS.unpack nvar+ , wcnfNumClauses = read $ BS.unpack nclause+ -- top must be greater than the sum of the weights of violated soft clauses.+ , wcnfTopCost = fromInteger $ 2^(63::Int) - 1+ , wcnfClauses = map parseWCNFLineBS ls+ }+ (["p","cnf", nvar, nclause]) ->+ Right $+ WCNF+ { wcnfNumVars = read $ BS.unpack nvar+ , wcnfNumClauses = read $ BS.unpack nclause+ -- top must be greater than the sum of the weights of violated soft clauses.+ , wcnfTopCost = fromInteger $ 2^(63::Int) - 1+ , wcnfClauses = map ((\c -> seq c (1,c)) . parseClauseBS) ls+ }+ _ ->+ Left "cannot find wcnf/cnf header"+ where+ l :: BS.ByteString+ ls :: [BS.ByteString]+ (l:ls) = filter (not . isCommentBS) (BS.lines s)++ render wcnf = header <> mconcat (map f (wcnfClauses wcnf))+ where+ header = mconcat+ [ byteString "p wcnf "+ , intDec (wcnfNumVars wcnf), char7 ' '+ , intDec (wcnfNumClauses wcnf), char7 ' '+ , integerDec (wcnfTopCost wcnf), char7 '\n'+ ]+ f (w,c) = integerDec w <> mconcat [char7 ' ' <> intDec lit | lit <- SAT.unpackClause c] <> byteString " 0\n"++parseWCNFLineBS :: BS.ByteString -> WeightedClause+parseWCNFLineBS s =+ case BS.readInteger (BS.dropWhile isSpace s) of+ Nothing -> error "ToySolver.FileFormat.CNF: no weight"+ Just (w, s') -> seq w $ seq xs $ (w, xs)+ where+ xs = parseClauseBS s'++-- -------------------------------------------------------------------++-- | Group oriented CNF Input Format+--+-- This format is used in Group oriented MUS track of the SAT Competition 2011.+--+-- References:+--+-- * <http://www.satcompetition.org/2011/rules.pdf>+data GCNF+ = GCNF+ { gcnfNumVars :: !Int+ -- ^ Nubmer of variables+ , gcnfNumClauses :: !Int+ -- ^ Number of clauses+ , gcnfLastGroupIndex :: !GroupIndex+ -- ^ The last index of a group in the file number of components contained in the file.+ , gcnfClauses :: [GClause]+ }+ deriving (Eq, Ord, Show, Read)++-- | Component number between 0 and `gcnfLastGroupIndex`+type GroupIndex = Int++-- | Clause together with component number+type GClause = (GroupIndex, SAT.PackedClause)++instance FileFormat GCNF where+ parse s =+ case BS.words l of+ (["p","gcnf", nbvar', nbclauses', lastGroupIndex']) ->+ Right $+ GCNF+ { gcnfNumVars = read $ BS.unpack nbvar'+ , gcnfNumClauses = read $ BS.unpack nbclauses'+ , gcnfLastGroupIndex = read $ BS.unpack lastGroupIndex'+ , gcnfClauses = map parseGCNFLineBS ls+ }+ (["p","cnf", nbvar', nbclause']) ->+ Right $+ GCNF+ { gcnfNumVars = read $ BS.unpack nbvar'+ , gcnfNumClauses = read $ BS.unpack nbclause'+ , gcnfLastGroupIndex = read $ BS.unpack nbclause'+ , gcnfClauses = zip [1..] $ map parseClauseBS ls+ }+ _ ->+ Left "cannot find gcnf header"+ where+ l :: BS.ByteString+ ls :: [BS.ByteString]+ (l:ls) = filter (not . isCommentBS) (BS.lines s)++ render gcnf = header <> mconcat (map f (gcnfClauses gcnf))+ where+ header = mconcat+ [ byteString "p gcnf "+ , intDec (gcnfNumVars gcnf), char7 ' '+ , intDec (gcnfNumClauses gcnf), char7 ' '+ , intDec (gcnfLastGroupIndex gcnf), char7 '\n'+ ]+ f (idx,c) = char7 '{' <> intDec idx <> char7 '}' <>+ mconcat [char7 ' ' <> intDec lit | lit <- SAT.unpackClause c] <>+ byteString " 0\n"++parseGCNFLineBS :: BS.ByteString -> GClause+parseGCNFLineBS s+ | Just ('{', s1) <- BS.uncons (BS.dropWhile isSpace s)+ , Just (!idx,s2) <- BS.readInt s1+ , Just ('}', s3) <- BS.uncons s2 =+ let ys = parseClauseBS s3+ in seq ys $ (idx, ys)+ | otherwise = error "ToySolver.FileFormat.CNF: parse error"++-- -------------------------------------------------------------------++{-+http://www.qbflib.org/qdimacs.html++<input> ::= <preamble> <prefix> <matrix> EOF++<preamble> ::= [<comment_lines>] <problem_line>+<comment_lines> ::= <comment_line> <comment_lines> | <comment_line>+<comment_line> ::= c <text> EOL+<problem_line> ::= p cnf <pnum> <pnum> EOL++<prefix> ::= [<quant_sets>]+<quant_sets> ::= <quant_set> <quant_sets> | <quant_set>+<quant_set> ::= <quantifier> <atom_set> 0 EOL+<quantifier> ::= e | a+<atom_set> ::= <pnum> <atom_set> | <pnum>++<matrix> ::= <clause_list>+<clause_list> ::= <clause> <clause_list> | <clause>+<clause> ::= <literal> <clause> | <literal> 0 EOL+<literal> ::= <num>++<text> ::= {A sequence of non-special ASCII characters}+<num> ::= {A 32-bit signed integer different from 0}+<pnum> ::= {A 32-bit signed integer greater than 0}+-}++-- | QDIMACS format+--+-- Quantified boolean expression in prenex normal form,+-- consisting of a sequence of quantifiers ('qdimacsPrefix') and+-- a quantifier-free CNF part ('qdimacsMatrix').+--+-- References:+--+-- * QDIMACS standard Ver. 1.1+-- <http://www.qbflib.org/qdimacs.html>+data QDimacs+ = QDimacs+ { qdimacsNumVars :: !Int+ -- ^ Number of variables+ , qdimacsNumClauses :: !Int+ -- ^ Number of clauses+ , qdimacsPrefix :: [QuantSet]+ -- ^ Sequence of quantifiers+ , qdimacsMatrix :: [SAT.PackedClause]+ -- ^ Clauses+ }+ deriving (Eq, Ord, Show, Read)++-- | Quantifier+data Quantifier+ = E -- ^ existential quantifier (∃)+ | A -- ^ universal quantifier (∀)+ deriving (Eq, Ord, Show, Read, Enum, Bounded)++-- | Quantified set of variables+type QuantSet = (Quantifier, [Atom])++-- | Synonym of 'SAT.Var'+type Atom = SAT.Var++instance FileFormat QDimacs where+ parse = f . BS.lines+ where+ f [] = Left "ToySolver.FileFormat.CNF.parse: premature end of file"+ f (l : ls) =+ case BS.uncons l of+ Nothing -> Left "ToySolver.FileFormat.CNF.parse: no problem line"+ Just ('c', _) -> f ls+ Just ('p', s) ->+ case BS.words s of+ ["cnf", numVars', numClauses'] ->+ case parsePrefix ls of+ (prefix', ls') -> Right $+ QDimacs+ { qdimacsNumVars = read $ BS.unpack numVars'+ , qdimacsNumClauses = read $ BS.unpack numClauses'+ , qdimacsPrefix = prefix'+ , qdimacsMatrix = map parseClauseBS ls'+ }+ _ -> Left "ToySolver.FileFormat.CNF.parse: invalid problem line"+ Just (c, _) -> Left ("ToySolver.FileFormat.CNF.parse: invalid prefix " ++ show c)++ render qdimacs = problem_line <> prefix' <> mconcat (map f (qdimacsMatrix qdimacs))+ where+ problem_line = mconcat+ [ byteString "p cnf "+ , intDec (qdimacsNumVars qdimacs), char7 ' '+ , intDec (qdimacsNumClauses qdimacs), char7 '\n'+ ]+ prefix' = mconcat+ [ char7 (if q == E then 'e' else 'a') <> mconcat [char7 ' ' <> intDec atom | atom <- atoms] <> byteString " 0\n"+ | (q, atoms) <- qdimacsPrefix qdimacs+ ]+ f c = mconcat [intDec lit <> char7 ' '| lit <- SAT.unpackClause c] <> byteString "0\n"++parsePrefix :: [BS.ByteString] -> ([QuantSet], [BS.ByteString])+parsePrefix = go []+ where+ go result [] = (reverse result, [])+ go result lls@(l : ls) =+ case BS.uncons l of+ Just (c,s)+ | c=='a' || c=='e' ->+ let atoms = readInts s+ q = if c=='a' then A else E+ in seq q $ deepseq atoms $ go ((q, atoms) : result) ls+ | otherwise ->+ (reverse result, lls)+ _ -> error "ToySolver.FileFormat.CNF.parseProblem: invalid line"++-- -------------------------------------------------------------------
src/ToySolver/Graph/ShortestPath.hs view
@@ -36,6 +36,7 @@ , path , firstOutEdge , lastInEdge+ , cost -- * Path data types , Path (..)@@ -51,6 +52,7 @@ , pathVertexesBackward , pathVertexesSeq , pathFold+ , pathMin -- * Shortest-path algorithms , bellmanFord@@ -61,7 +63,6 @@ , bellmanFordDetectNegativeCycle ) where -import Control.Applicative import Control.Monad import Control.Monad.ST import Control.Monad.Trans@@ -298,6 +299,7 @@ lift $ do writeSTRef updatedRef HashSet.empty forM_ (HashSet.toList us) $ \u -> do+ -- modifySTRef' updatedRef (HashSet.delete u) -- possible optimization Just (Pair du a) <- H.lookup d u forM_ (HashMap.lookupDefault [] u g) $ \(v, c, l) -> do m <- H.lookup d v
src/ToySolver/Internal/Data/Vec.hs view
@@ -177,7 +177,7 @@ growTo :: A.MArray a e IO => GenericVec a e -> Int -> IO () growTo v !n = do m <- getSize v- when (m < n) $ resize v n + when (m < n) $ resize v n {-# SPECIALIZE push :: Vec e -> e -> IO () #-} {-# SPECIALIZE push :: UVec Int -> Int -> IO () #-}
src/ToySolver/Internal/TextUtil.hs view
@@ -56,12 +56,7 @@ result = go 0 str lim :: Word-#if !MIN_VERSION_base(4,6,1) && WORD_SIZE_IN_BITS == 32- {- To avoid a bug of maxBound <https://ghc.haskell.org/trac/ghc/ticket/8072> -}- lim = 0xFFFFFFFF `div` 10-#else lim = maxBound `div` 10-#endif go :: Integer -> [Char] -> Integer go !r [] = r
+ src/ToySolver/MaxCut.hs view
@@ -0,0 +1,76 @@+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.MaxCut+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : portable+--+-----------------------------------------------------------------------------+module ToySolver.MaxCut+ ( Problem (..)+ , fromEdges+ , edges+ , buildDSDPMaxCutGraph+ , buildDSDPMaxCutGraph'+ , Solution+ , eval+ , evalEdge+ ) where++import Data.Array.Unboxed+import Data.ByteString.Builder+import Data.ByteString.Builder.Scientific+import qualified Data.ByteString.Lazy.Char8 as BL+import qualified Data.Foldable as F+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IntMap+import Data.Monoid+import Data.Scientific (Scientific)++data Problem a+ = Problem+ { numNodes :: !Int+ -- ^ Number of nodes N. Nodes are numbered from 0 to N-1.+ , numEdges :: !Int+ -- ^ Number of edges.+ , matrix :: IntMap (IntMap a)+ -- ^ Non-zero entries of symmetric weight matrix+ } deriving (Eq, Ord, Show)++instance Functor Problem where+ fmap f Problem{ numNodes = n, numEdges = m, matrix = mat } =+ Problem{ numNodes = n, numEdges = m, matrix = fmap (fmap f) mat }++fromEdges :: Num a => Int -> [(Int,Int,a)] -> Problem a+fromEdges n es = Problem n (length es) $ IntMap.unionsWith (IntMap.unionWith (+)) $+ [IntMap.fromList [(v1, IntMap.singleton v2 w), (v2, IntMap.singleton v1 w)] | (v1,v2,w) <- es]++edges :: Problem a -> [(Int,Int,a)]+edges prob = do+ (a,m) <- IntMap.toList $ matrix prob+ (b,w) <- IntMap.toList $ snd $ IntMap.split a m+ return (a,b,w)++buildDSDPMaxCutGraph :: Problem Scientific -> Builder+buildDSDPMaxCutGraph = buildDSDPMaxCutGraph' scientificBuilder++buildDSDPMaxCutGraph' :: (a -> Builder) -> Problem a -> Builder+buildDSDPMaxCutGraph' weightBuilder prob = header <> body+ where+ header = intDec (numNodes prob) <> char7 ' ' <> intDec (numEdges prob) <> char7 '\n'+ body = mconcat $ do+ (a,b,w) <- edges prob+ return $ intDec (a+1) <> char7 ' ' <> intDec (b+1) <> char7 ' ' <> weightBuilder w <> char7 '\n'++type Solution = UArray Int Bool++eval :: Num a => Solution -> Problem a -> a+eval sol prob = sum [w | (a,b,w) <- edges prob, sol ! a /= sol ! b]++evalEdge :: Num a => Solution -> (Int,Int,a) -> a+evalEdge sol (a,b,w) + | sol ! a /= sol ! b = w+ | otherwise = 0
src/ToySolver/QBF.hs view
@@ -15,7 +15,7 @@ -- * Mikoláš Janota, William Klieber, Joao Marques-Silva, Edmund Clarke. -- Solving QBF with Counterexample Guided Refinement. -- In Theory and Applications of Satisfiability Testing (SAT 2012), pp. 114-128.--- <http://dx.doi.org/10.1007/978-3-642-31612-8_10>+-- <https://doi.org/10.1007/978-3-642-31612-8_10> -- <https://www.cs.cmu.edu/~wklieber/papers/qbf-cegar-sat-2012.pdf> -- -----------------------------------------------------------------------------@@ -29,6 +29,8 @@ , solveNaive , solveCEGAR , solveCEGARIncremental+ , solveQE+ , solveQE_CNF ) where import Control.Monad@@ -43,11 +45,14 @@ import ToySolver.Data.Boolean import ToySolver.Data.BoolExpr (BoolExpr) import qualified ToySolver.Data.BoolExpr as BoolExpr+import ToySolver.FileFormat.CNF (Quantifier (..))+import qualified ToySolver.FileFormat.CNF as CNF import qualified ToySolver.SAT as SAT import ToySolver.SAT.Types (LitSet, VarSet, VarMap) import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin+import ToySolver.SAT.Store.CNF -import ToySolver.Text.QDimacs (Quantifier (..))+import qualified ToySolver.SAT.ExistentialQuantification as QE -- ---------------------------------------------------------------------------- @@ -346,7 +351,7 @@ end -} f :: Int -> LitSet -> Prefix -> Matrix -> IO (Maybe LitSet)- f nv assumptions prefix matrix = do+ f nv _assumptions prefix matrix = do solver <- SAT.newSolver SAT.newVars_ solver nv enc <- Tseitin.newEncoder solver@@ -360,14 +365,14 @@ return xs let g :: Int -> LitSet -> Prefix -> Matrix -> IO (Maybe LitSet) g _nv _assumptions [] _matrix = error "should not happen"- g nv assumptions [(q,xs)] matrix = do+ g nv assumptions [(_q,xs)] matrix = do ret <- SAT.solveWith solver (IntSet.toList assumptions) if ret then do m <- SAT.getModel solver return $ Just $ IntSet.fromList [if SAT.evalLit m x then x else -x | x <- IntSet.toList xs] else return Nothing - g nv assumptions prefix@((q,xs) : prefix'@((_q2,_) : prefix'')) matrix = do+ g nv assumptions ((q,xs) : prefix'@((_q2,_) : prefix'')) matrix = do let loop counterMoves = do let ys = [(nv, prefix'', reduct matrix nu) | nu <- counterMoves] (nv2, prefix2, matrix2) =@@ -388,17 +393,96 @@ -- ---------------------------------------------------------------------------- +data CNFOrDNF+ = CNF [LitSet]+ | DNF [LitSet]+ deriving (Show)++negateCNFOrDNF :: CNFOrDNF -> CNFOrDNF+negateCNFOrDNF (CNF xss) = DNF (map (IntSet.map negate) xss)+negateCNFOrDNF (DNF xss) = CNF (map (IntSet.map negate) xss)++toCNF :: Int -> CNFOrDNF -> CNF.CNF+toCNF nv (CNF clauses) = CNF.CNF nv (length clauses) (map (SAT.packClause . IntSet.toList) clauses)+toCNF nv (DNF []) = CNF.CNF nv 1 [SAT.packClause []]+toCNF nv (DNF cubes) = CNF.CNF (nv + length cubes) (length cs) (map SAT.packClause cs)+ where+ zs = zip [nv+1..] cubes+ cs = map fst zs : [[-sel, lit] | (sel, cube) <- zs, lit <- IntSet.toList cube]++solveQE :: Int -> Prefix -> Matrix -> IO (Bool, Maybe LitSet)+solveQE nv prefix matrix = do+ store <- newCNFStore+ SAT.newVars_ store nv+ encoder <- Tseitin.newEncoder store+ Tseitin.addFormula encoder matrix+ cnf <- getCNFFormula store+ let prefix' =+ if CNF.cnfNumVars cnf > nv then+ prefix ++ [(E, IntSet.fromList [nv+1 .. CNF.cnfNumVars cnf])]+ else+ prefix+ (b, m) <- solveQE_CNF (CNF.cnfNumVars cnf) prefix' (map SAT.unpackClause (CNF.cnfClauses cnf))+ return (b, fmap (IntSet.filter (\lit -> abs lit <= nv)) m)++solveQE_CNF :: Int -> Prefix -> [SAT.Clause] -> IO (Bool, Maybe LitSet)+solveQE_CNF nv prefix matrix = g (normalizePrefix prefix) matrix+ where+ g :: Prefix -> [SAT.Clause] -> IO (Bool, Maybe LitSet)+ g ((E,xs) : prefix') matrix = do+ cnf <- liftM (toCNF nv) $ f prefix' matrix+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars cnf)+ forM_ (CNF.cnfClauses cnf) $ \clause -> do+ SAT.addClause solver (SAT.unpackClause clause)+ ret <- SAT.solve solver+ if ret then do+ m <- SAT.getModel solver+ return (True, Just $ IntSet.fromList [if SAT.evalLit m x then x else -x | x <- IntSet.toList xs])+ else do+ return (False, Nothing)+ g ((A,xs) : prefix') matrix = do+ cnf <- liftM (toCNF nv . negateCNFOrDNF) $ f prefix' matrix+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars cnf)+ forM_ (CNF.cnfClauses cnf) $ \clause -> do+ SAT.addClause solver (SAT.unpackClause clause)+ ret <- SAT.solve solver+ if ret then do+ m <- SAT.getModel solver+ return (False, Just $ IntSet.fromList [if SAT.evalLit m x then x else -x | x <- IntSet.toList xs])+ else do+ return (True, Nothing)+ g prefix matrix = do+ ret <- f prefix matrix+ case ret of+ CNF xs -> return (not (any IntSet.null xs), Nothing)+ DNF xs -> return (any IntSet.null xs, Nothing)++ f :: Prefix -> [SAT.Clause] -> IO CNFOrDNF+ f [] matrix = return $ CNF [IntSet.fromList clause | clause <- matrix]+ f ((E,xs) : prefix') matrix = do+ cnf <- liftM (toCNF nv) $ f prefix' matrix+ dnf <- QE.shortestImplicantsE (xs `IntSet.union` IntSet.fromList [nv+1 .. CNF.cnfNumVars cnf]) cnf+ return $ DNF dnf+ f ((A,xs) : prefix') matrix = do+ cnf <- liftM (toCNF nv . negateCNFOrDNF) $ f prefix' matrix+ dnf <- QE.shortestImplicantsE (xs `IntSet.union` IntSet.fromList [nv+1 .. CNF.cnfNumVars cnf]) cnf+ return $ negateCNFOrDNF $ DNF dnf++-- ----------------------------------------------------------------------------+ -- ∀y ∃x. x ∧ (y ∨ ¬x)-test = solveNaive 2 [(A, IntSet.singleton 2), (E, IntSet.singleton 1)] (x .&&. (y .||. notB x))+_test = solveNaive 2 [(A, IntSet.singleton 2), (E, IntSet.singleton 1)] (x .&&. (y .||. notB x)) where x = BoolExpr.Atom 1 y = BoolExpr.Atom 2 -test' = solveCEGAR 2 [(A, IntSet.singleton 2), (E, IntSet.singleton 1)] (x .&&. (y .||. notB x))+_test' = solveCEGAR 2 [(A, IntSet.singleton 2), (E, IntSet.singleton 1)] (x .&&. (y .||. notB x)) where x = BoolExpr.Atom 1 y = BoolExpr.Atom 2 -test1 = prenexAnd (1, [(A, IntSet.singleton 1)], BoolExpr.Atom 1) (1, [(A, IntSet.singleton 1)], notB (BoolExpr.Atom 1))+_test1 = prenexAnd (1, [(A, IntSet.singleton 1)], BoolExpr.Atom 1) (1, [(A, IntSet.singleton 1)], notB (BoolExpr.Atom 1)) -test2 = prenexOr (1, [(A, IntSet.singleton 1)], BoolExpr.Atom 1) (1, [(A, IntSet.singleton 1)], BoolExpr.Atom 1)+_test2 = prenexOr (1, [(A, IntSet.singleton 1)], BoolExpr.Atom 1) (1, [(A, IntSet.singleton 1)], BoolExpr.Atom 1)
+ src/ToySolver/QUBO.hs view
@@ -0,0 +1,143 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.QUBO+-- Copyright : (c) Masahiro Sakai 2018+-- License : BSD-style+-- +-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+-- +-----------------------------------------------------------------------------+module ToySolver.QUBO+ ( -- * QUBO (quadratic unconstrained boolean optimization)+ Problem (..)+ , Solution+ , eval++ -- * Ising Model+ , IsingModel (..)+ , evalIsingModel+ ) where++import Control.Monad+import Data.Array.Unboxed+import Data.ByteString.Builder+import Data.ByteString.Builder.Scientific+import qualified Data.ByteString.Lazy.Char8 as BS+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IntMap+import Data.Monoid+import Data.Scientific+import ToySolver.FileFormat.Base++-- | QUBO (quadratic unconstrained boolean optimization) problem.+--+-- Minimize \(\sum_{i\le j} Q_{i,j} x_i x_j\) where \(x_i \in \{0,1\}\) for \(i \in \{0 \ldots N-1\}\).+--+-- In the `Solution` type. 0 and 1 are represented as @False@ and @True@ respectively.+data Problem a+ = Problem+ { quboNumVars :: !Int+ -- ^ Number of variables N. Variables are numbered from 0 to N-1.+ , quboMatrix :: IntMap (IntMap a)+ -- ^ Upper triangular matrix Q+ }+ deriving (Eq, Show)++instance Functor Problem where+ fmap f prob =+ Problem+ { quboNumVars = quboNumVars prob+ , quboMatrix = fmap (fmap f) (quboMatrix prob)+ }++parseProblem :: (BS.ByteString -> a) -> BS.ByteString -> Either String (Problem a)+parseProblem f s = + case BS.words l of+ ["p", filetype, topology, maxNodes, _nNodes, _nCouplers] ->+ if filetype /= "qubo" then+ Left $ "unknown filetype: " ++ BS.unpack filetype+ else if topology /= "0" && topology /= "unconstrained" then+ Left $ "unknown topology: " ++ BS.unpack topology+ else+ Right $ Problem+ { quboNumVars = read (BS.unpack maxNodes)+ , quboMatrix =+ IntMap.unionsWith IntMap.union $ do+ l <- ls+ case BS.words l of+ [i, j, v] -> return $ IntMap.singleton (read (BS.unpack i)) $ IntMap.singleton (read (BS.unpack j)) $ f v+ }+ where+ (l:ls) = filter (not . isCommentBS) (BS.lines s)++ isCommentBS :: BS.ByteString -> Bool+ isCommentBS s =+ case BS.uncons s of+ Just ('c',_) -> True+ _ -> False++renderProblem :: (a -> Builder) -> Problem a -> Builder+renderProblem f prob = header+ <> mconcat [ intDec i <> char7 ' ' <> intDec i <> char7 ' ' <> f val <> char7 '\n'+ | (i,val) <- IntMap.toList nodes+ ]+ <> mconcat [intDec i <> char7 ' ' <> intDec j <> char7 ' ' <> f val <> char7 '\n'+ | (i,row) <- IntMap.toList couplers, (j,val) <- IntMap.toList row+ ]+ where+ nodes = IntMap.mapMaybeWithKey IntMap.lookup (quboMatrix prob)+ nNodes = IntMap.size nodes+ couplers = IntMap.mapWithKey IntMap.delete (quboMatrix prob)+ nCouplers = sum [IntMap.size row | row <- IntMap.elems couplers]+ header = mconcat+ ["p qubo 0 "+ , intDec (quboNumVars prob), char7 ' '+ , intDec nNodes, char7 ' '+ , intDec nCouplers, char7 '\n'+ ]++instance FileFormat (Problem Scientific) where+ parse = parseProblem (read . BS.unpack)+ render = renderProblem scientificBuilder+++type Solution = UArray Int Bool++eval :: Num a => Solution -> Problem a -> a+eval sol prob = sum $ do+ (x1, row) <- IntMap.toList $ quboMatrix prob+ guard $ sol ! x1+ (x2, c) <- IntMap.toList row+ guard $ sol ! x2+ return c+++-- | Ising model.+--+-- Minimize \(\sum_{i<j} J_{i,j} \sigma_i \sigma_j + \sum_i h_i \sigma_i\) where \(\sigma_i \in \{-1,+1\}\) for \(i \in \{0 \ldots N-1\}\).+--+-- In the `Solution` type. -1 and +1 are represented as @False@ and @True@ respectively.+data IsingModel a+ = IsingModel+ { isingNumVars :: !Int+ -- ^ Number of variables N. Variables are numbered from 0 to N-1.+ , isingInteraction :: IntMap (IntMap a)+ -- ^ Interaction \(J_{i,j}\) with \(i < j\).+ , isingExternalMagneticField :: IntMap a+ -- ^ External magnetic field \(h_j\).+ }+ deriving (Eq, Show)++evalIsingModel :: Num a => Solution -> IsingModel a -> a+evalIsingModel sol m+ = sum [ jj_ij * sigma i * sigma j+ | (i, row) <- IntMap.toList $ isingInteraction m, (j, jj_ij) <- IntMap.toList row+ ]+ + sum [ h_i * sigma i | (i, h_i) <- IntMap.toList $ isingExternalMagneticField m ]+ where+ sigma i = if sol ! i then 1 else -1
src/ToySolver/SAT.hs view
@@ -49,6 +49,9 @@ , AddClause (..) , Clause , evalClause+ , PackedClause+ , packClause+ , unpackClause -- ** Cardinality constraints , AddCardinality (..) , AtLeast@@ -97,26 +100,6 @@ , getRandomGen , setConfBudget - -- ** Deprecated- , setRestartStrategy- , setRestartFirst- , setRestartInc- , setLearntSizeFirst- , setLearntSizeInc- , setCCMin- , setLearningStrategy- , setEnablePhaseSaving- , getEnablePhaseSaving- , setEnableForwardSubsumptionRemoval- , getEnableForwardSubsumptionRemoval- , setEnableBackwardSubsumptionRemoval- , getEnableBackwardSubsumptionRemoval- , setCheckModel- , setRandomFreq- , setPBHandlerType- , setPBSplitClausePart- , getPBSplitClausePart- -- * Read state , getNVars , getNConstraints@@ -125,19 +108,12 @@ , getLitFixed , getFixedLiterals - -- * Read state (deprecated)- , nVars- , nAssigns- , nConstraints- , nLearnt - -- * Internal API , varBumpActivity , varDecayActivity ) where import Prelude hiding (log)-import Control.Applicative hiding (empty) import Control.Loop import Control.Monad import Control.Monad.IO.Class@@ -403,6 +379,8 @@ , svLearntLim :: !(IORef Int) , svLearntLimAdjCnt :: !(IORef Int) , svLearntLimSeq :: !(IORef [(Int,Int)])+ , svSeen :: !(Vec.UVec Bool)+ , svPBLearnt :: !(IORef (Maybe PBLinAtLeast)) -- | Amount to bump next variable with. , svVarInc :: !(IOURef Double)@@ -499,7 +477,7 @@ val <- readIORef (vdValue vd) when (val == lUndef) $ error "unassign: should not happen" - flag <- getEnablePhaseSaving solver+ flag <- configEnablePhaseSaving <$> getConfig solver when flag $ writeIORef (vdPolarity vd) $! fromJust (unliftBool val) writeIORef (vdValue vd) lUndef@@ -694,42 +672,22 @@ getNVars :: Solver -> IO Int getNVars solver = Vec.getSize (svVarData solver) -{-# DEPRECATED nVars "Use getNVars instead" #-}--- | number of variables of the problem.-nVars :: Solver -> IO Int-nVars = getNVars- -- | number of assigned getNAssigned :: Solver -> IO Int getNAssigned solver = Vec.getSize (svTrail solver) -{-# DEPRECATED nAssigns "nAssigns is deprecated" #-}--- | number of assigned variables.-nAssigns :: Solver -> IO Int-nAssigns = getNAssigned- -- | number of constraints. getNConstraints :: Solver -> IO Int getNConstraints solver = do xs <- readIORef (svConstrDB solver) return $ length xs -{-# DEPRECATED nConstraints "Use getNConstraints instead" #-}--- | number of constraints.-nConstraints :: Solver -> IO Int-nConstraints = getNConstraints- -- | number of learnt constrints. getNLearntConstraints :: Solver -> IO Int getNLearntConstraints solver = do (n,_) <- readIORef (svLearntDB solver) return n -{-# DEPRECATED nLearnt "Use getNLearntConstraints instead" #-}--- | number of learnt constrints.-nLearnt :: Solver -> IO Int-nLearnt = getNLearntConstraints- learntConstraints :: Solver -> IO [SomeConstraintHandler] learntConstraints solver = do (_,cs) <- readIORef (svLearntDB solver)@@ -788,6 +746,9 @@ tsolver <- newIORef Nothing tchecked <- newIOURef 0 + seen <- Vec.new+ pbLearnt <- newIORef Nothing+ alpha <- newIOURef 0.4 emaScale <- newIOURef 1.0 learntCounter <- newIOURef 0@@ -838,6 +799,8 @@ , svLearntLimSeq = learntLimSeq , svVarInc = varInc , svConstrInc = constrInc+ , svSeen = seen+ , svPBLearnt = pbLearnt , svERWAStepSize = alpha , svEMAScale = emaScale@@ -870,6 +833,7 @@ vd <- newVarData Vec.push (svVarData solver) vd PQ.enqueue (svVarQueue solver) v+ Vec.push (svSeen solver) False return v newVars :: Solver -> Int -> IO [Var]@@ -888,6 +852,7 @@ resizeVarCapacity :: Solver -> Int -> IO () resizeVarCapacity solver n = do Vec.resizeCapacity (svVarData solver) n+ Vec.resizeCapacity (svSeen solver) n PQ.resizeHeapCapacity (svVarQueue solver) n PQ.resizeTableCapacity (svVarQueue solver) (n+1) @@ -972,7 +937,7 @@ else do removeBackwardSubsumedBy solver (ts', n') (ts'',n'') <- do- b <- getPBSplitClausePart solver+ b <- configEnablePBSplitClausePart <$> getConfig solver if b then pbSplitClausePart solver (ts',n') else return (ts',n')@@ -1307,11 +1272,14 @@ learnHybrid :: IORef Int -> SomeConstraintHandler -> IO (Maybe SearchResult) learnHybrid conflictCounter constr = do- ((learntClause, clauseLevel), pb) <- analyzeConflictHybrid solver constr- let minLevel =- case pb of- Nothing -> clauseLevel- Just (_, pbLevel) -> min clauseLevel pbLevel+ (learntClause, clauseLevel) <- analyzeConflict solver constr+ (pb, minLevel) <- do+ z <- readIORef (svPBLearnt solver)+ case z of+ Nothing -> return (z, clauseLevel)+ Just pb -> do+ pbLevel <- pbBacktrackLevel solver pb+ return (z, min clauseLevel pbLevel) backtrackTo solver minLevel case learntClause of@@ -1337,7 +1305,7 @@ Nothing -> do case pb of Nothing -> return Nothing- Just ((lhs,rhs), _pbLevel) -> do+ Just (lhs,rhs) -> do h <- newPBHandlerPromoted solver lhs rhs True case h of CHClause _ -> do@@ -1413,7 +1381,7 @@ checkForwardSubsumption :: Solver -> Clause -> IO Bool checkForwardSubsumption solver lits = do- flag <- getEnableForwardSubsumptionRemoval solver+ flag <- configEnableForwardSubsumptionRemoval <$> getConfig solver if not flag then return False else do@@ -1430,13 +1398,13 @@ where withEnablePhaseSaving solver flag m = bracket- (getEnablePhaseSaving solver)- (setEnablePhaseSaving solver)- (\_ -> setEnablePhaseSaving solver flag >> m)+ (getConfig solver)+ (\saved -> modifyConfig solver (\config -> config{ configEnablePhaseSaving = configEnablePhaseSaving saved }))+ (\saved -> setConfig solver saved{ configEnablePhaseSaving = flag } >> m) removeBackwardSubsumedBy :: Solver -> PBLinAtLeast -> IO () removeBackwardSubsumedBy solver pb = do- flag <- getEnableBackwardSubsumptionRemoval solver+ flag <- configEnableBackwardSubsumptionRemoval <$> getConfig solver when flag $ do xs <- backwardSubsumedBy solver pb when debugMode $ do@@ -1458,7 +1426,7 @@ -- because only such constraints are added to occur list. -- See 'addToDB'. pb2 <- instantiatePBLinAtLeast (getLitFixed solver) =<< toPBLinAtLeast c- return $ pbSubsume pb pb2+ return $ pbLinSubsume pb pb2 liftM HashSet.fromList $ filterM p $ HashSet.toList@@ -1491,57 +1459,15 @@ setConfig :: Solver -> Config -> IO () setConfig solver conf = do- orig <- readIORef $ svConfig solver+ orig <- getConfig solver writeIORef (svConfig solver) conf when (configBranchingStrategy orig /= configBranchingStrategy conf) $ do PQ.rebuild (svVarQueue solver) modifyConfig :: Solver -> (Config -> Config) -> IO ()-modifyConfig solver = modifyIORef' (svConfig solver)--{-# DEPRECATED setRestartStrategy "Use setConfig" #-}-setRestartStrategy :: Solver -> RestartStrategy -> IO ()-setRestartStrategy solver s = modifyIORef' (svConfig solver) $ \config -> config{ configRestartStrategy = s }---- | The initial restart limit. (default 100)--- Zero and negative values are used to disable restart.-{-# DEPRECATED setRestartFirst "Use setConfig" #-}-setRestartFirst :: Solver -> Int -> IO ()-setRestartFirst solver !n = modifyIORef' (svConfig solver) $ \config -> config{ configRestartFirst = n }---- | The factor with which the restart limit is multiplied in each restart. (default 1.5)--- --- This must be @>1@.-{-# DEPRECATED setRestartInc "Use setConfig" #-}-setRestartInc :: Solver -> Double -> IO ()-setRestartInc solver !r- | r > 1 = modifyIORef' (svConfig solver) $ \config -> config{ configRestartInc = r }- | otherwise = error "setRestartInc: RestartInc must be >1"--{-# DEPRECATED setLearningStrategy "Use setConfig" #-}-setLearningStrategy :: Solver -> LearningStrategy -> IO ()-setLearningStrategy solver l = modifyIORef' (svConfig solver) $ \config -> config{ configLearningStrategy = l }---- | The initial limit for learnt clauses.--- --- Negative value means computing default value from problem instance.-{-# DEPRECATED setLearntSizeFirst "Use setConfig" #-}-setLearntSizeFirst :: Solver -> Int -> IO ()-setLearntSizeFirst solver !x = modifyIORef' (svConfig solver) $ \config -> config{ configLearntSizeFirst = x }---- | The limit for learnt clauses is multiplied with this factor each restart. (default 1.1)--- --- This must be @>1@.-{-# DEPRECATED setLearntSizeInc "Use setConfig" #-}-setLearntSizeInc :: Solver -> Double -> IO ()-setLearntSizeInc solver !r- | r > 1 = modifyIORef' (svConfig solver) $ \config -> config{ configLearntSizeInc = r }- | otherwise = error "setLearntSizeInc: LearntSizeInc must be >1"---- | Controls conflict clause minimization (0=none, 1=basic, 2=deep)-{-# DEPRECATED setCCMin "Use setConfig" #-}-setCCMin :: Solver -> Int -> IO ()-setCCMin solver !v = modifyIORef' (svConfig solver) $ \config -> config{ configCCMin = v }+modifyConfig solver f = do+ config <- getConfig solver+ setConfig solver $ f config -- | The default polarity of a variable. setVarPolarity :: Solver -> Var -> Bool -> IO ()@@ -1549,17 +1475,6 @@ vd <- varData solver v writeIORef (vdPolarity vd) val -{-# DEPRECATED setCheckModel "Use setConfig" #-}-setCheckModel :: Solver -> Bool -> IO ()-setCheckModel solver flag = do- modifyIORef' (svConfig solver) $ \config -> config{ configCheckModel = flag }---- | The frequency with which the decision heuristic tries to choose a random variable-{-# DEPRECATED setRandomFreq "Use setConfig" #-}-setRandomFreq :: Solver -> Double -> IO ()-setRandomFreq solver r =- modifyIORef' (svConfig solver) $ \config -> config{ configRandomFreq = r }- -- | Set random generator used by the random variable selection setRandomGen :: Solver -> Rand.GenIO -> IO () setRandomGen solver = writeIORef (svRandomGen solver)@@ -1572,69 +1487,6 @@ setConfBudget solver (Just b) | b >= 0 = writeIOURef (svConfBudget solver) b setConfBudget solver _ = writeIOURef (svConfBudget solver) (-1) -{-# DEPRECATED setPBHandlerType "Use setConfig" #-}-setPBHandlerType :: Solver -> PBHandlerType -> IO ()-setPBHandlerType solver ht = do- modifyIORef' (svConfig solver) $ \config -> config{ configPBHandlerType = ht }---- | Split PB-constraints into a PB part and a clause part.------ Example from minisat+ paper:------ * 4 x1 + 4 x2 + 4 x3 + 4 x4 + 2y1 + y2 + y3 ≥ 4--- --- would be split into------ * x1 + x2 + x3 + x4 + ¬z ≥ 1 (clause part)------ * 2 y1 + y2 + y3 + 4 z ≥ 4 (PB part)------ where z is a newly introduced variable, not present in any other constraint.--- --- Reference:--- --- * N. Eén and N. Sörensson. Translating Pseudo-Boolean Constraints into SAT. JSAT 2:1–26, 2006.----{-# DEPRECATED setPBSplitClausePart "Use setConfig" #-}-setPBSplitClausePart :: Solver -> Bool -> IO ()-setPBSplitClausePart solver b =- modifyIORef' (svConfig solver) $ \config -> config{ configEnablePBSplitClausePart = b }---- | See documentation of 'setPBSplitClausePart'.-getPBSplitClausePart :: Solver -> IO Bool-getPBSplitClausePart solver =- configEnablePBSplitClausePart <$> getConfig solver--{-# DEPRECATED setEnablePhaseSaving "Use setConfig" #-}-setEnablePhaseSaving :: Solver -> Bool -> IO ()-setEnablePhaseSaving solver flag = do- modifyIORef' (svConfig solver) $ \config -> config{ configEnablePhaseSaving = flag }--{-# DEPRECATED getEnablePhaseSaving "Use getConfig" #-}-getEnablePhaseSaving :: Solver -> IO Bool-getEnablePhaseSaving solver = do- configEnablePhaseSaving <$> getConfig solver--{-# DEPRECATED setEnableForwardSubsumptionRemoval "Use setConfig" #-}-setEnableForwardSubsumptionRemoval :: Solver -> Bool -> IO ()-setEnableForwardSubsumptionRemoval solver flag = do- modifyIORef' (svConfig solver) $ \config -> config{ configEnableForwardSubsumptionRemoval = flag }--{-# DEPRECATED getEnableForwardSubsumptionRemoval "Use getConfig" #-}-getEnableForwardSubsumptionRemoval :: Solver -> IO Bool-getEnableForwardSubsumptionRemoval solver = do- configEnableForwardSubsumptionRemoval <$> getConfig solver--{-# DEPRECATED setEnableBackwardSubsumptionRemoval "Use setConfig" #-}-setEnableBackwardSubsumptionRemoval :: Solver -> Bool -> IO ()-setEnableBackwardSubsumptionRemoval solver flag = do- modifyIORef' (svConfig solver) $ \config -> config{ configEnableBackwardSubsumptionRemoval = flag }--{-# DEPRECATED getEnableBackwardSubsumptionRemoval "Use getConfig" #-}-getEnableBackwardSubsumptionRemoval :: Solver -> IO Bool-getEnableBackwardSubsumptionRemoval solver = do- configEnableBackwardSubsumptionRemoval <$> getConfig solver- {-------------------------------------------------------------------- API for implementation of @solve@ --------------------------------------------------------------------}@@ -1753,55 +1605,99 @@ analyzeConflict :: ConstraintHandler c => Solver -> c -> IO (Clause, Level) analyzeConflict solver constr = do+ config <- getConfig solver+ let isHybrid = configLearningStrategy config == LearningHybrid+ d <- getDecisionLevel solver+ (out :: Vec.UVec Lit) <- Vec.new+ Vec.push out 0 -- (leave room for the asserting literal)+ (pathC :: IOURef Int) <- newIOURef 0 - let split :: [Lit] -> IO (LitSet, LitSet)- split = go (IS.empty, IS.empty)- where- go (xs,ys) [] = return (xs,ys)- go (xs,ys) (l:ls) = do- lv <- litLevel solver l- if lv == levelRoot then- go (xs,ys) ls- else if lv >= d then- go (IS.insert l xs, ys) ls- else- go (xs, IS.insert l ys) ls+ pbConstrRef <- newIORef undefined - let loop :: LitSet -> LitSet -> IO LitSet- loop lits1 lits2- | sz==1 = do- return $ lits1 `IS.union` lits2- | sz>=2 = do- l <- peekTrail solver- if litNot l `IS.notMember` lits1 then do- popTrail solver- loop lits1 lits2+ let f lits = do+ forM_ lits $ \lit -> do+ let !v = litVar lit+ lv <- litLevel solver lit+ b <- Vec.unsafeRead (svSeen solver) (v - 1)+ when (not b && lv > levelRoot) $ do+ varBumpActivity solver v+ varIncrementParticipated solver v+ if lv >= d then do+ Vec.unsafeWrite (svSeen solver) (v - 1) True+ modifyIOURef pathC (+1) else do- m <- varReason solver (litVar l)- case m of- Nothing -> error "analyzeConflict: should not happen"- Just constr2 -> do- constrBumpActivity solver constr2- xs <- reasonOf solver constr2 (Just l)- forM_ xs $ \lit -> do- varBumpActivity solver (litVar lit)- varIncrementParticipated solver (litVar lit)- popTrail solver- (ys,zs) <- split xs- loop (IS.delete (litNot l) lits1 `IS.union` ys)- (lits2 `IS.union` zs)- | otherwise = error "analyzeConflict: should not happen: reason of current level is empty"- where- sz = IS.size lits1+ Vec.push out lit + processLitHybrid pb constr lit getLits = do+ pb2 <- do+ let clausePB = do+ lits <- getLits+ return $ clauseToPBLinAtLeast (lit : lits)+ b <- isPBRepresentable constr+ if not b then do+ clausePB+ else do+ pb2 <- toPBLinAtLeast constr+ o <- pbOverSAT solver pb2+ if o then do+ clausePB+ else+ return pb2+ let pb3 = cutResolve pb pb2 (litVar lit)+ ls = IS.fromList [l | (_,l) <- fst pb3]+ seq ls $ writeIORef pbConstrRef (ls, pb3)++ popUnseen = do+ l <- peekTrail solver+ let !v = litVar l+ b <- Vec.unsafeRead (svSeen solver) (v - 1)+ if b then do+ return ()+ else do+ when isHybrid $ do+ (ls, pb) <- readIORef pbConstrRef+ when (litNot l `IS.member` ls) $ do+ Just constr <- varReason solver v+ processLitHybrid pb constr l (reasonOf solver constr (Just l))+ popTrail solver+ popUnseen++ loop = do+ popUnseen+ l <- peekTrail solver+ let !v = litVar l+ Vec.unsafeWrite (svSeen solver) (v - 1) False+ modifyIOURef pathC (subtract 1)+ c <- readIOURef pathC+ if c > 0 then do+ Just constr <- varReason solver v+ constrBumpActivity solver constr+ lits <- reasonOf solver constr (Just l)+ f lits+ when isHybrid $ do+ (ls, pb) <- readIORef pbConstrRef+ when (litNot l `IS.member` ls) $ do+ processLitHybrid pb constr l (return lits)+ popTrail solver+ loop+ else do+ Vec.unsafeWrite out 0 (litNot l)+ constrBumpActivity solver constr- conflictClause <- reasonOf solver constr Nothing- forM_ conflictClause $ \lit -> do- varBumpActivity solver (litVar lit)- varIncrementParticipated solver (litVar lit)- (ys,zs) <- split conflictClause- lits <- loop ys zs+ falsifiedLits <- reasonOf solver constr Nothing+ f falsifiedLits+ when isHybrid $ do+ pb <- do+ b <- isPBRepresentable constr+ if b then+ toPBLinAtLeast constr+ else+ return (clauseToPBLinAtLeast falsifiedLits)+ let ls = IS.fromList [l | (_,l) <- fst pb]+ seq ls $ writeIORef pbConstrRef (ls, pb)+ loop+ lits <- liftM IS.fromList $ Vec.getElems out lits2 <- minimizeConflictClause solver lits @@ -1812,6 +1708,12 @@ lv <- litLevel solver l return (l,lv) + when isHybrid $ do+ (_, pb) <- readIORef pbConstrRef+ case pbToClause pb of+ Just _ -> writeIORef (svPBLearnt solver) Nothing+ Nothing -> writeIORef (svPBLearnt solver) (Just pb)+ let level = case xs of [] -> error "analyzeConflict: should not happen" [_] -> levelRoot@@ -1844,103 +1746,6 @@ n <- Vec.getSize (svTrail solver) go (n-1) (IS.singleton (litVar p)) [p] -analyzeConflictHybrid :: ConstraintHandler c => Solver -> c -> IO ((Clause, Level), Maybe (PBLinAtLeast, Level))-analyzeConflictHybrid solver constr = do- d <- getDecisionLevel solver-- let split :: [Lit] -> IO (LitSet, LitSet)- split = go (IS.empty, IS.empty)- where- go (xs,ys) [] = return (xs,ys)- go (xs,ys) (l:ls) = do- lv <- litLevel solver l- if lv == levelRoot then- go (xs,ys) ls- else if lv >= d then- go (IS.insert l xs, ys) ls- else- go (xs, IS.insert l ys) ls-- let loop :: LitSet -> LitSet -> PBLinAtLeast -> IO (LitSet, PBLinAtLeast)- loop lits1 lits2 pb- | sz==1 = do- return $ (lits1 `IS.union` lits2, pb)- | sz>=2 = do- l <- peekTrail solver- m <- varReason solver (litVar l)- case m of- Nothing -> error "analyzeConflictHybrid: should not happen"- Just constr2 -> do- xs <- reasonOf solver constr2 (Just l)- (lits1',lits2') <-- if litNot l `IS.notMember` lits1 then- return (lits1,lits2)- else do- constrBumpActivity solver constr2- forM_ xs $ \lit -> do- varBumpActivity solver (litVar lit)- varIncrementParticipated solver (litVar lit)- (ys,zs) <- split xs- return (IS.delete (litNot l) lits1 `IS.union` ys, lits2 `IS.union` zs)-- pb' <- if any (\(_,l2) -> litNot l == l2) (fst pb)- then do- pb2 <- do- b <- isPBRepresentable constr2- if not b then do- return $ clauseToPBLinAtLeast (l:xs)- else do- pb2 <- toPBLinAtLeast constr2- o <- pbOverSAT solver pb2- if o then- return $ clauseToPBLinAtLeast (l:xs)- else- return pb2- return $ cutResolve pb pb2 (litVar l)- else return pb-- popTrail solver- loop lits1' lits2' pb'-- | otherwise = error "analyzeConflictHybrid: should not happen: reason of current level is empty"- where- sz = IS.size lits1-- constrBumpActivity solver constr- conflictClause <- reasonOf solver constr Nothing- pbConfl <- do- b <- isPBRepresentable constr- if b then- toPBLinAtLeast constr- else- return (clauseToPBLinAtLeast conflictClause)- forM_ conflictClause $ \lit -> do- varBumpActivity solver (litVar lit)- varIncrementParticipated solver (litVar lit)- (ys,zs) <- split conflictClause- (lits, pb) <- loop ys zs pbConfl-- lits2 <- minimizeConflictClause solver lits-- incrementReasoned solver (IS.toList lits2)-- xs <- liftM (sortBy (flip (comparing snd))) $- forM (IS.toList lits2) $ \l -> do- lv <- litLevel solver l- return (l,lv)-- let level = case xs of- [] -> error "analyzeConflict: should not happen"- [_] -> levelRoot- _:(_,lv):_ -> lv-- case pbToClause pb of- Nothing -> do - pblevel <- pbBacktrackLevel solver pb- return ((map fst xs, level), Just (pb, pblevel))- Just _ -> do- return ((map fst xs, level), Nothing)- pbBacktrackLevel :: Solver -> PBLinAtLeast -> IO Level pbBacktrackLevel _ ([], rhs) = assert (rhs > 0) $ return levelRoot pbBacktrackLevel solver (lhs, rhs) = do@@ -1963,7 +1768,10 @@ replay lvs slack_lv let initial_slack = sum [c | (c,_) <- lhs] - rhs- replay (IM.toList levelToLiterals) initial_slack+ if any (\(c,_) -> c > initial_slack) lhs then+ return 0+ else do+ replay (IM.toList levelToLiterals) initial_slack minimizeConflictClause :: Solver -> LitSet -> IO LitSet minimizeConflictClause solver lits = do@@ -2138,7 +1946,7 @@ modifyIORef' ref (IS.insert lit) forM_ [0..n-1] $ \i -> do lit <- Vec.read (svAssumptions solver) i- modifyIORef' ref (IS.delete lit) + modifyIORef' ref (IS.delete lit) constrDecayActivity :: Solver -> IO () constrDecayActivity solver = do@@ -2458,11 +2266,7 @@ (# w2, ret #) -> (# w2, I# ret #) where go# :: Int# -> Int# -> State# RealWorld -> (# State# RealWorld, Int# #)-#if __GLASGOW_HASKELL__ < 708- go# i end w | i ># end = (# w, -1# #)-#else go# i end w | isTrue# (i ># end) = (# w, -1# #)-#endif go# i end w = case unIO (litValue solver =<< unsafeRead a (I# i)) w of (# w2, val #) ->@@ -2496,11 +2300,7 @@ (# w2, ret #) -> (# w2, I# ret #) where go# :: Int# -> Int# -> State# RealWorld -> (# State# RealWorld, Int# #)-#if __GLASGOW_HASKELL__ < 708- go# i end w | i ># end = (# w, -1# #)-#else go# i end w | isTrue# (i ># end) = (# w, -1# #)-#endif go# i end w = case unIO (litValue solver =<< unsafeRead a (I# i)) w of (# w2, val #) ->@@ -3031,7 +2831,7 @@ {-------------------------------------------------------------------- Pseudo Boolean Constraint (Counter)---------------------------------------------------------------------} +--------------------------------------------------------------------} data PBHandlerCounter = PBHandlerCounter
src/ToySolver/SAT/Config.hs view
@@ -13,18 +13,6 @@ , PBHandlerType (..) , showPBHandlerType , parsePBHandlerType-- -- ** Deprecated- , defaultRestartFirst- , defaultRestartInc- , defaultLearntSizeFirst- , defaultLearntSizeInc- , defaultCCMin- , defaultEnablePhaseSaving- , defaultEnableForwardSubsumptionRemoval- , defaultEnableBackwardSubsumptionRemoval- , defaultRandomFreq- , defaultPBSplitClausePart ) where import Data.Char@@ -93,23 +81,23 @@ def = Config { configRestartStrategy = def- , configRestartFirst = defaultRestartFirst- , configRestartInc = defaultRestartInc+ , configRestartFirst = 100+ , configRestartInc = 1.5 , configLearningStrategy = def- , configLearntSizeFirst = defaultLearntSizeFirst- , configLearntSizeInc = defaultLearntSizeInc- , configCCMin = defaultCCMin+ , configLearntSizeFirst = -1+ , configLearntSizeInc = 1.1+ , configCCMin = 2 , configBranchingStrategy = def , configERWAStepSizeFirst = 0.4 , configERWAStepSizeDec = 10**(-6) , configERWAStepSizeMin = 0.06 , configEMADecay = 1 / 0.95- , configEnablePhaseSaving = defaultEnablePhaseSaving- , configEnableForwardSubsumptionRemoval = defaultEnableForwardSubsumptionRemoval- , configEnableBackwardSubsumptionRemoval = defaultEnableBackwardSubsumptionRemoval- , configRandomFreq = defaultRandomFreq+ , configEnablePhaseSaving = True+ , configEnableForwardSubsumptionRemoval = False+ , configEnableBackwardSubsumptionRemoval = False+ , configRandomFreq = 0.005 , configPBHandlerType = def- , configEnablePBSplitClausePart = defaultPBSplitClausePart+ , configEnablePBSplitClausePart = False , configCheckModel = False , configVarDecay = 1 / 0.95 , configConstrDecay = 1 / 0.999@@ -137,16 +125,6 @@ "luby" -> Just LubyRestarts _ -> Nothing --- | default value for @RestartFirst@.-{-# DEPRECATED defaultRestartFirst "Use configRestartFirst def" #-}-defaultRestartFirst :: Int-defaultRestartFirst = 100---- | default value for @RestartInc@.-{-# DEPRECATED defaultRestartInc "Use configRestartInc def" #-}-defaultRestartInc :: Double-defaultRestartInc = 1.5- -- | Learning strategy. -- -- The default value can be obtained by 'def'.@@ -204,26 +182,6 @@ "lrb" -> Just BranchingLRB _ -> Nothing --- | default value for @LearntSizeFirst@.-{-# DEPRECATED defaultLearntSizeFirst "Use learntSizeFirst def" #-}-defaultLearntSizeFirst :: Int-defaultLearntSizeFirst = -1----- | default value for @LearntSizeInc@.-{-# DEPRECATED defaultLearntSizeInc "Use learntSizeInc def" #-}-defaultLearntSizeInc :: Double-defaultLearntSizeInc = 1.1---- | default value for @CCMin@.-{-# DEPRECATED defaultCCMin "Use ccMin def" #-}-defaultCCMin :: Int-defaultCCMin = 2--{-# DEPRECATED defaultRandomFreq "Use configRandomFreq def" #-}-defaultRandomFreq :: Double-defaultRandomFreq = 0.005- -- | Pseudo boolean constraint handler implimentation. -- -- The default value can be obtained by 'def'.@@ -243,20 +201,3 @@ "counter" -> Just PBHandlerTypeCounter "pueblo" -> Just PBHandlerTypePueblo _ -> Nothing---- | See documentation of 'setPBSplitClausePart'.-{-# DEPRECATED defaultPBSplitClausePart "Use configEnablePBSplitClausePart def" #-}-defaultPBSplitClausePart :: Bool-defaultPBSplitClausePart = False--{-# DEPRECATED defaultEnablePhaseSaving "Use configEnablePhaseSaving def" #-}-defaultEnablePhaseSaving :: Bool-defaultEnablePhaseSaving = True--{-# DEPRECATED defaultEnableForwardSubsumptionRemoval "Use configEnableForwardSubsumptionRemoval def" #-}-defaultEnableForwardSubsumptionRemoval :: Bool-defaultEnableForwardSubsumptionRemoval = False--{-# DEPRECATED defaultEnableBackwardSubsumptionRemoval "Use configEnableBackwardSubsumptionRemoval def" #-}-defaultEnableBackwardSubsumptionRemoval :: Bool-defaultEnableBackwardSubsumptionRemoval = False
+ src/ToySolver/SAT/Encoder/Cardinality.hs view
@@ -0,0 +1,68 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.SAT.Encoder.Cardinality+-- Copyright : (c) Masahiro Sakai 2019+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-----------------------------------------------------------------------------+module ToySolver.SAT.Encoder.Cardinality+ ( Encoder+ , Strategy (..)+ , newEncoder+ , newEncoderWithStrategy+ , encodeAtLeast+ ) where++import Control.Monad.Primitive+import qualified ToySolver.SAT.Types as SAT+import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin+import ToySolver.SAT.Encoder.Cardinality.Internal.Naive+import ToySolver.SAT.Encoder.Cardinality.Internal.ParallelCounter++-- -------------------------------------------------------------------++data Encoder m = Encoder (Tseitin.Encoder m) Strategy++data Strategy+ = Naive+ | ParallelCounter+ deriving (Show, Eq, Ord, Enum, Bounded)++newEncoder :: Monad m => Tseitin.Encoder m -> m (Encoder m)+newEncoder tseitin = return $ Encoder tseitin ParallelCounter++newEncoderWithStrategy :: Monad m => Tseitin.Encoder m -> Strategy -> m (Encoder m)+newEncoderWithStrategy tseitin strategy = return (Encoder tseitin strategy)++-- getTseitinEncoder :: Encoder m -> Tseitin.Encoder m+-- getTseitinEncoder (Encoder tseitin _) = tseitin++instance Monad m => SAT.NewVar m (Encoder m) where+ newVar (Encoder tseitin _) = SAT.newVar tseitin+ newVars (Encoder tseitin _) = SAT.newVars tseitin+ newVars_ (Encoder tseitin _) = SAT.newVars_ tseitin++instance Monad m => SAT.AddClause m (Encoder m) where+ addClause (Encoder tseitin _) = SAT.addClause tseitin++instance PrimMonad m => SAT.AddCardinality m (Encoder m) where+ addAtLeast (Encoder tseitin strategy) lhs rhs+ | rhs <= 0 = return ()+ | otherwise =+ case strategy of+ Naive -> addAtLeastNaive tseitin (lhs,rhs)+ ParallelCounter -> addAtLeastParallelCounter tseitin (lhs,rhs)++encodeAtLeast :: PrimMonad m => Encoder m -> SAT.AtLeast -> m SAT.Lit+encodeAtLeast (Encoder tseitin strategy) =+ case strategy of+ Naive -> encodeAtLeastNaive tseitin+ ParallelCounter -> encodeAtLeastParallelCounter tseitin
+ src/ToySolver/SAT/Encoder/Cardinality/Internal/Naive.hs view
@@ -0,0 +1,43 @@+{-# OPTIONS_GHC -Wall #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.SAT.Encoder.Cardinality.Internal.Naive+-- Copyright : (c) Masahiro Sakai 2019+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : portable+--+-----------------------------------------------------------------------------+module ToySolver.SAT.Encoder.Cardinality.Internal.Naive+ ( addAtLeastNaive+ , encodeAtLeastNaive+ ) where++import Control.Monad.Primitive+import qualified ToySolver.SAT.Types as SAT+import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin++addAtLeastNaive :: PrimMonad m => Tseitin.Encoder m -> SAT.AtLeast -> m ()+addAtLeastNaive enc (lhs,rhs) = do+ let n = length lhs+ if n < rhs then do+ SAT.addClause enc []+ else do+ mapM_ (SAT.addClause enc) (comb (n - rhs + 1) lhs)++-- TODO: consider polarity+encodeAtLeastNaive :: PrimMonad m => Tseitin.Encoder m -> SAT.AtLeast -> m SAT.Lit+encodeAtLeastNaive enc (lhs,rhs) = do+ let n = length lhs+ if n < rhs then do+ Tseitin.encodeDisj enc []+ else do+ ls <- mapM (Tseitin.encodeDisj enc) (comb (n - rhs + 1) lhs)+ Tseitin.encodeConj enc ls++comb :: Int -> [a] -> [[a]]+comb 0 _ = [[]]+comb _ [] = []+comb n (x:xs) = map (x:) (comb (n-1) xs) ++ comb n xs
+ src/ToySolver/SAT/Encoder/Cardinality/Internal/ParallelCounter.hs view
@@ -0,0 +1,91 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ScopedTypeVariables #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.SAT.Encoder.Cardinality.Internal+-- Copyright : (c) Masahiro Sakai 2019+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-----------------------------------------------------------------------------+module ToySolver.SAT.Encoder.Cardinality.Internal.ParallelCounter+ ( addAtLeastParallelCounter+ , encodeAtLeastParallelCounter+ ) where++import Control.Monad.Primitive+import Control.Monad.State.Strict+import Data.Bits+import Data.Vector (Vector)+import qualified Data.Vector as V+import qualified ToySolver.SAT.Types as SAT+import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin++addAtLeastParallelCounter :: PrimMonad m => Tseitin.Encoder m -> SAT.AtLeast -> m ()+addAtLeastParallelCounter enc constr = do+ l <- encodeAtLeastParallelCounter enc constr+ SAT.addClause enc [l]++-- TODO: consider polarity+encodeAtLeastParallelCounter :: forall m. PrimMonad m => Tseitin.Encoder m -> SAT.AtLeast -> m SAT.Lit+encodeAtLeastParallelCounter enc (lhs,rhs) = do+ let rhs_bits = bits (fromIntegral rhs)+ (cnt, overflowBits) <- encodeSumParallelCounter enc (length rhs_bits) lhs+ isGE <- encodeGE enc cnt rhs_bits+ Tseitin.encodeDisj enc $ isGE : overflowBits+ where+ bits :: Integer -> [Bool]+ bits n = f n 0+ where+ f 0 !_ = []+ f n i = testBit n i : f (clearBit n i) (i+1)++encodeSumParallelCounter :: forall m. PrimMonad m => Tseitin.Encoder m -> Int -> [SAT.Lit] -> m ([SAT.Lit], [SAT.Lit])+encodeSumParallelCounter enc w lits = do+ let add :: [SAT.Lit] -> [SAT.Lit] -> SAT.Lit -> StateT [SAT.Lit] m [SAT.Lit]+ add = go 0 []+ where+ go :: Int -> [SAT.Lit] -> [SAT.Lit] -> [SAT.Lit] -> SAT.Lit -> StateT [SAT.Lit] m [SAT.Lit]+ go i ret _xs _ys c | i == w = do+ modify (c:)+ return $ reverse ret+ go _i ret [] [] c = return $ reverse (c : ret)+ go i ret (x : xs) (y : ys) c = do+ z <- lift $ Tseitin.encodeFASum enc x y c+ c' <- lift $ Tseitin.encodeFACarry enc x y c+ go (i+1) (z : ret) xs ys c'+ go _ _ _ _ _ = error "encodeSumParallelCounter: should not happen"++ f :: Vector SAT.Lit -> StateT [SAT.Lit] m [SAT.Lit]+ f xs+ | V.null xs = return []+ | otherwise = do+ let len2 = V.length xs `div` 2+ cnt1 <- f (V.slice 0 len2 xs)+ cnt2 <- f (V.slice len2 len2 xs)+ c <- if V.length xs `mod` 2 == 0 then+ lift $ Tseitin.encodeDisj enc []+ else+ lift $ return $ xs V.! (V.length xs - 1)+ add cnt1 cnt2 c++ runStateT (f (V.fromList lits)) []++encodeGE :: forall m. PrimMonad m => Tseitin.Encoder m -> [SAT.Lit] -> [Bool] -> m SAT.Lit+encodeGE enc lhs rhs = do+ let f :: [SAT.Lit] -> [Bool] -> SAT.Lit -> m SAT.Lit+ f [] [] r = return r+ f [] (True : _) _ = Tseitin.encodeDisj enc [] -- false+ f [] (False : bs) r = f [] bs r+ f (l : ls) (True : bs) r = do+ f ls bs =<< Tseitin.encodeConj enc [l, r]+ f (l : ls) (False : bs) r = do+ f ls bs =<< Tseitin.encodeDisj enc [l, r]+ f (l : ls) [] r = do+ f ls [] =<< Tseitin.encodeDisj enc [l, r]+ t <- Tseitin.encodeConj enc [] -- true+ f lhs rhs t
src/ToySolver/SAT/Encoder/Integer.hs view
@@ -19,6 +19,7 @@ import qualified ToySolver.SAT.Encoder.PBNLC as PBNLC newtype Expr = Expr SAT.PBSum+ deriving (Eq, Show, Read) newVar :: SAT.AddPBNL m enc => enc -> Integer -> Integer -> m Expr newVar enc lo hi
src/ToySolver/SAT/Encoder/PB/Internal/Adder.hs view
@@ -24,6 +24,7 @@ import Control.Monad import Control.Monad.Primitive import Data.Bits+import Data.Maybe import Data.Primitive.MutVar import Data.Sequence (Seq) import qualified Data.Sequence as Seq@@ -103,19 +104,19 @@ b <- Tseitin.encodeDisj enc [] -- False loop (i+1) (b : ret) 1 -> do- Just b <- SQ.dequeue q+ b <- fromJust <$> SQ.dequeue q loop (i+1) (b : ret) 2 -> do- Just b1 <- SQ.dequeue q- Just b2 <- SQ.dequeue q+ b1 <- fromJust <$> SQ.dequeue q+ b2 <- fromJust <$> SQ.dequeue q s <- encodeHASum enc b1 b2 c <- encodeHACarry enc b1 b2 insert (i+1) c loop (i+1) (s : ret) _ -> do- Just b1 <- SQ.dequeue q- Just b2 <- SQ.dequeue q- Just b3 <- SQ.dequeue q+ b1 <- fromJust <$> SQ.dequeue q+ b2 <- fromJust <$> SQ.dequeue q+ b3 <- fromJust <$> SQ.dequeue q s <- Tseitin.encodeFASum enc b1 b2 b3 c <- Tseitin.encodeFACarry enc b1 b2 b3 insert i s
src/ToySolver/SAT/Encoder/PB/Internal/BDD.hs view
@@ -49,6 +49,7 @@ Just l -> return l Nothing -> do case xs of+ [] -> error "encodePBLinAtLeastBDD: should not happen" [(_,l)] -> return l (c,l) : xs' -> do thenLit <- f xs' (rhs - c) slack
src/ToySolver/SAT/ExistentialQuantification.hs view
@@ -1,6 +1,6 @@ {-# Language BangPatterns #-} {-# OPTIONS_GHC -Wall #-}--- -------------------------------------------------------------------+---------------------------------------------------------------------- -- | -- Module : ToySolver.SAT.ExistentialQuantification -- Copyright : (c) Masahiro Sakai 2017@@ -18,21 +18,22 @@ -- pp. 191-207. -- <https://www.embedded.rwth-aachen.de/lib/exe/fetch.php?media=bib:bkk11a.pdf> ----- --------------------------------------------------------------------+---------------------------------------------------------------------- module ToySolver.SAT.ExistentialQuantification ( project , shortestImplicants+ , shortestImplicantsE+ , negateCNF ) where -import Control.Applicative import Control.Monad import qualified Data.IntMap as IntMap import qualified Data.IntSet as IntSet import Data.IORef+import qualified Data.Vector.Generic as VG+import ToySolver.FileFormat.CNF as CNF import ToySolver.SAT as SAT import ToySolver.SAT.Types as SAT-import ToySolver.Text.CNF as CNF -- ------------------------------------------------------------------- @@ -42,7 +43,17 @@ , backwardMap :: SAT.VarMap SAT.Lit } -dualRailEncoding :: SAT.VarSet -> CNF.CNF -> (CNF.CNF, Info)+-- | Given a set of variables \(X = \{x_1, \ldots, x_k\}\) and CNF formula \(\phi\), this function+--+-- * duplicates \(X\) with \(X^+ = \{x^+_1,\ldots,x^+_k\}\) and \(X^- = \{x^-_1,\ldots,x^-_k\}\),+--+-- * replaces positive literals \(x_i\) with \(x^+_i\), and negative literals \(\neg x_i\) with \(x^-_i\), and+--+-- * adds constraints \(\neg x^+_i \vee \neg x^-_i\).+dualRailEncoding+ :: SAT.VarSet -- ^ \(X\)+ -> CNF.CNF -- ^ \(\phi\)+ -> (CNF.CNF, Info) dualRailEncoding vs cnf = ( cnf' , Info@@ -53,9 +64,9 @@ where cnf' = CNF.CNF- { CNF.numVars = CNF.numVars cnf + IntSet.size vs- , CNF.numClauses = CNF.numClauses cnf + IntSet.size vs- , CNF.clauses = [ fmap f c | c <- CNF.clauses cnf ] ++ [[-xp,-xn] | (xp,xn) <- IntMap.elems forward]+ { CNF.cnfNumVars = CNF.cnfNumVars cnf + IntSet.size vs+ , CNF.cnfNumClauses = CNF.cnfNumClauses cnf + IntSet.size vs+ , CNF.cnfClauses = [ VG.map f c | c <- CNF.cnfClauses cnf ] ++ [SAT.packClause [-xp,-xn] | (xp,xn) <- IntMap.elems forward] } f x = case IntMap.lookup (abs x) forward of@@ -64,7 +75,7 @@ forward = IntMap.fromList [ (x, (x,x'))- | (x,x') <- zip (IntSet.toList vs) [CNF.numVars cnf + 1 ..]+ | (x,x') <- zip (IntSet.toList vs) [CNF.cnfNumVars cnf + 1 ..] ] backward = IntMap.fromList $ concat $ [ [(xp,x), (xn,-x)]@@ -92,12 +103,35 @@ blockingClause :: Info -> SAT.Model -> Clause blockingClause info m = [-y | y <- IntMap.keys (backwardMap info), SAT.evalLit m y] -shortestImplicants :: SAT.VarSet -> CNF.CNF -> IO [LitSet]-shortestImplicants vs formula = do- let (tau_formula, info) = dualRailEncoding vs formula+{-# DEPRECATED shortestImplicants "Use shortestImplicantsE instead" #-} +-- | Given a set of variables \(X = \{x_1, \ldots, x_k\}\) and CNF formula \(\phi\),+-- this function computes shortest implicants of \(\phi\) in terms of \(X\).+-- Variables except \(X\) are treated as if they are existentially quantified.+--+-- Resulting shortest implicants form a DNF (disjunctive normal form) formula that is+-- equivalent to the original (existentially quantified) formula.+shortestImplicants+ :: SAT.VarSet -- ^ \(X\)+ -> CNF.CNF -- ^ \(\phi\)+ -> IO [LitSet]+shortestImplicants xs formula =+ shortestImplicantsE (IntSet.fromList [1 .. CNF.cnfNumVars formula] IntSet.\\ xs) formula++-- | Given a set of variables \(X = \{x_1, \ldots, x_k\}\) and CNF formula \(\phi\),+-- this function computes shortest implicants of \(\exists X. \phi\).+--+-- Resulting shortest implicants form a DNF (disjunctive normal form) formula that is+-- equivalent to the original formula \(\exists X. \phi\).+shortestImplicantsE+ :: SAT.VarSet -- ^ \(X\)+ -> CNF.CNF -- ^ \(\phi\)+ -> IO [LitSet]+shortestImplicantsE xs formula = do+ let (tau_formula, info) = dualRailEncoding (IntSet.fromList [1 .. CNF.cnfNumVars formula] IntSet.\\ xs) formula solver <- SAT.newSolver- SAT.newVars_ solver (CNF.numVars tau_formula)- forM_ (CNF.clauses tau_formula) (addClause solver)+ SAT.newVars_ solver (CNF.cnfNumVars tau_formula)+ forM_ (CNF.cnfClauses tau_formula) $ \c -> do+ SAT.addClause solver (SAT.unpackClause c) ref <- newIORef [] @@ -122,22 +156,42 @@ loop 0 reverse <$> readIORef ref -project :: SAT.VarSet -> CNF.CNF -> IO CNF.CNF+-- | Given a CNF formula \(\phi\), this function returns another CNF formula \(\psi\)+-- that is equivalent to \(\neg\phi\).+negateCNF+ :: CNF.CNF -- ^ \(\phi\)+ -> IO CNF.CNF -- ^ \(\psi \equiv \neg\phi\)+negateCNF formula = do+ implicants <- shortestImplicantsE IntSet.empty formula+ return $+ CNF.CNF+ { CNF.cnfNumVars = CNF.cnfNumVars formula+ , CNF.cnfNumClauses = length implicants+ , CNF.cnfClauses = map (SAT.packClause . map negate . IntSet.toList) implicants+ }++-- | Given a set of variables \(X = \{x_1, \ldots, x_k\}\) and CNF formula \(\phi\),+-- this function computes a CNF formula \(\psi\) that is equivalent to \(\exists X. \phi\)+-- (i.e. \((\exists X. \phi) \leftrightarrow \psi\)).+project+ :: SAT.VarSet -- ^ \(X\)+ -> CNF.CNF -- ^ \(\phi\)+ -> IO CNF.CNF -- ^ \(\psi\) project xs cnf = do- let ys = IntSet.fromList [1 .. CNF.numVars cnf] IntSet.\\ xs+ let ys = IntSet.fromList [1 .. CNF.cnfNumVars cnf] IntSet.\\ xs nv = if IntSet.null ys then 0 else IntSet.findMax ys- implicants <- shortestImplicants ys cnf+ implicants <- shortestImplicantsE xs cnf let cnf' = CNF.CNF- { CNF.numVars = nv- , CNF.numClauses = length implicants- , CNF.clauses = map (map negate . IntSet.toList) implicants+ { CNF.cnfNumVars = nv+ , CNF.cnfNumClauses = length implicants+ , CNF.cnfClauses = map (SAT.packClause . map negate . IntSet.toList) implicants }- negated_implicates <- shortestImplicants ys cnf'- let implicates = map (map negate . IntSet.toList) negated_implicates+ negated_implicates <- shortestImplicantsE xs cnf'+ let implicates = map (SAT.packClause . map negate . IntSet.toList) negated_implicates return $ CNF.CNF- { CNF.numVars = nv- , CNF.numClauses = length implicates- , CNF.clauses = implicates+ { CNF.cnfNumVars = nv+ , CNF.cnfNumClauses = length implicates+ , CNF.cnfClauses = implicates }
src/ToySolver/SAT/MessagePassing/SurveyPropagation.hs view
@@ -53,17 +53,14 @@ , printInfo ) where -import Control.Applicative import Control.Concurrent import Control.Concurrent.STM import Control.Exception import Control.Loop import Control.Monad-import qualified Data.Array.IArray as A import qualified Data.IntMap as IntMap import qualified Data.IntSet as IntSet import Data.IORef-import Data.Maybe (fromJust) import qualified Data.Vector as V import qualified Data.Vector.Mutable as VM import qualified Data.Vector.Unboxed as VU@@ -108,10 +105,10 @@ , svNThreadsRef :: !(IORef Int) } -newSolver :: Int -> [(Double, SAT.Clause)] -> IO Solver+newSolver :: Int -> [(Double, SAT.PackedClause)] -> IO Solver newSolver nv clauses = do let num_clauses = length clauses- num_edges = sum [length c | (_,c) <- clauses]+ num_edges = sum [VG.length c | (_,c) <- clauses] varEdgesRef <- newIORef IntMap.empty clauseEdgesM <- VGM.new num_clauses@@ -121,7 +118,7 @@ ref <- newIORef 0 forM_ (zip [0..] clauses) $ \(i,(_,c)) -> do- es <- forM c $ \lit -> do+ es <- forM (SAT.unpackClause c) $ \lit -> do e <- readIORef ref modifyIORef' ref (+1) modifyIORef' varEdgesRef (IntMap.insertWith IntSet.union (abs lit) (IntSet.singleton e))@@ -181,7 +178,7 @@ return solver deleteSolver :: Solver -> IO ()-deleteSolver solver = return ()+deleteSolver _solver = return () initializeRandom :: Solver -> Rand.GenIO -> IO () initializeRandom solver gen = do
src/ToySolver/SAT/MessagePassing/SurveyPropagation/OpenCL.hs view
@@ -49,7 +49,6 @@ , unfixLit ) where -import Control.Applicative ((<$>)) import Control.Exception import Control.Loop import Control.Monad@@ -102,13 +101,13 @@ , svIterLimRef :: !(IORef (Maybe Int)) } -newSolver :: (String -> IO ()) -> CLContext -> CLDeviceID -> Int -> [(Double, SAT.Clause)] -> IO Solver+newSolver :: (String -> IO ()) -> CLContext -> CLDeviceID -> Int -> [(Double, SAT.PackedClause)] -> IO Solver newSolver outputMessage context dev nv clauses = do _ <- clRetainContext context queue <- clCreateCommandQueue context dev [] let num_clauses = length clauses- num_edges = sum [length c | (_,c) <- clauses]+ num_edges = sum [VG.length c | (_,c) <- clauses] (varEdgesTmp :: VM.IOVector [(Int,Bool,Double)]) <- VGM.replicate nv [] clauseOffset <- VGM.new num_clauses@@ -117,8 +116,8 @@ ref <- newIORef 0 forM_ (zip [0..] clauses) $ \(i,(w,c)) -> do VGM.write clauseOffset i =<< liftM fromIntegral (readIORef ref)- VGM.write clauseLength i (fromIntegral (length c))- forM_ c $ \lit -> do+ VGM.write clauseLength i (fromIntegral (VG.length c))+ forM_ (SAT.unpackClause c) $ \lit -> do e <- readIORef ref modifyIORef' ref (+1) #if MIN_VERSION_vector(0,11,0)
src/ToySolver/SAT/PBO.hs view
@@ -189,7 +189,7 @@ AdaptiveSearch -> do lim <- getTrialLimitConf opt adaptiveSearch cxt solver lim- _ -> error "ToySolver.SAT.PBO.minimize: should not happen" + _ -> error "ToySolver.SAT.PBO.minimize: should not happen" getMethod :: Optimizer -> IO Method getMethod opt = readIORef (optMethodRef opt)
src/ToySolver/SAT/PBO/BCD.hs view
@@ -20,7 +20,7 @@ -- Improvements to Core-Guided binary search for MaxSAT, -- in Theory and Applications of Satisfiability Testing (SAT 2012), -- pp. 284-297.--- <http://dx.doi.org/10.1007/978-3-642-31612-8_22>+-- <https://doi.org/10.1007/978-3-642-31612-8_22> -- <http://ulir.ul.ie/handle/10344/2771> -- -----------------------------------------------------------------------------
src/ToySolver/SAT/PBO/BCD2.hs view
@@ -21,7 +21,7 @@ -- Improvements to Core-Guided binary search for MaxSAT, -- in Theory and Applications of Satisfiability Testing (SAT 2012), -- pp. 284-297.--- <http://dx.doi.org/10.1007/978-3-642-31612-8_22>+-- <https://doi.org/10.1007/978-3-642-31612-8_22> -- <http://ulir.ul.ie/handle/10344/2771> -- -----------------------------------------------------------------------------@@ -89,7 +89,7 @@ SAT.addClause solver [-sel] getCoreLB :: CoreInfo -> IO Integer-getCoreLB = readIORef . coreLBRef +getCoreLB = readIORef . coreLBRef solve :: C.Context cxt => cxt -> SAT.Solver -> Options -> IO () solve cxt solver opt = solveWBO (C.normalize cxt) solver opt@@ -107,12 +107,12 @@ nsatRef <- newIORef 1 nunsatRef <- newIORef 1 - lastUBRef <- newIORef $ SAT.pbUpperBound obj+ lastUBRef <- newIORef $ SAT.pbLinUpperBound obj coresRef <- newIORef [] let getLB = do xs <- readIORef coresRef- foldM (\s core -> do{ v <- getCoreLB core; return $! s + v }) 0 xs + foldM (\s core -> do{ v <- getCoreLB core; return $! s + v }) 0 xs deductedWeightRef <- newIORef weights let deductWeight d core =@@ -169,7 +169,7 @@ lastModel <- atomically $ C.getBestModel cxt sels <- liftM IntMap.fromList $ forM cores $ \core -> do coreLB <- getCoreLB core- let coreUB = SAT.pbUpperBound (coreCostFun core)+ let coreUB = SAT.pbLinUpperBound (coreCostFun core) if coreUB < coreLB then do -- Note: we have detected unsatisfiability C.logMessage cxt $ printf "BCD2: coreLB (%d) exceeds coreUB (%d)" coreLB coreUB
src/ToySolver/SAT/PBO/Context.hs view
@@ -72,7 +72,7 @@ ret <- getBestValue ctx case ret of Just val -> return $ val - 1- Nothing -> return $ SAT.pbUpperBound $ getObjectiveFunction ctx+ Nothing -> return $ SAT.pbLinUpperBound $ getObjectiveFunction ctx setFinished :: Context a => a -> IO () setFinished cxt = do@@ -151,7 +151,7 @@ newSimpleContext2 obj obj2 = do unsatRef <- newTVarIO False bestsolRef <- newTVarIO Nothing- lbRef <- newTVarIO $! SAT.pbLowerBound obj+ lbRef <- newTVarIO $! SAT.pbLinLowerBound obj onUpdateBestSolRef <- newIORef $ \_ _ -> return () onUpdateLBRef <- newIORef $ \_ -> return ()@@ -172,7 +172,7 @@ } setOnUpdateBestSolution :: SimpleContext -> (SAT.Model -> Integer -> IO ()) -> IO ()-setOnUpdateBestSolution sc h = writeIORef (scOnUpdateBestSolutionRef sc) h +setOnUpdateBestSolution sc h = writeIORef (scOnUpdateBestSolutionRef sc) h setOnUpdateLowerBound :: SimpleContext -> (Integer -> IO ()) -> IO () setOnUpdateLowerBound sc h = writeIORef (scOnUpdateLowerBoundRef sc) h
src/ToySolver/SAT/PBO/MSU4.hs view
@@ -15,7 +15,7 @@ -- Algorithms for Maximum Satisfiability using Unsatisfiable Cores. -- In Design, Automation and Test in Europe, 2008 (DATE '08). March 2008. -- pp. 408-413, doi:10.1109/date.2008.4484715.--- <http://dx.doi.org/10.1109/date.2008.4484715>+-- <https://doi.org/10.1109/date.2008.4484715> -- <http://eprints.soton.ac.uk/265000/1/jpms-date08.pdf> -- <http://www.csi.ucd.ie/staff/jpms/talks/talksite/jpms-date08.pdf> --
src/ToySolver/SAT/PBO/UnsatBased.hs view
@@ -15,7 +15,7 @@ -- * Vasco Manquinho Ruben Martins Inês Lynce -- Improving Unsatisfiability-based Algorithms for Boolean Optimization. -- Theory and Applications of Satisfiability Testing – SAT 2010, pp 181-193.--- <http://dx.doi.org/10.1007/978-3-642-14186-7_16>+-- <https://doi.org/10.1007/978-3-642-14186-7_16> -- <http://sat.inesc-id.pt/~ruben/papers/manquinho-sat10.pdf> -- <http://sat.inesc-id.pt/~ruben/talks/sat10-talk.pdf> --
+ src/ToySolver/SAT/SLS/ProbSAT.hs view
@@ -0,0 +1,548 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module : ToySolver.SAT.SLS.ProbSAT+-- Copyright : (c) Masahiro Sakai 2017+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : non-portable+--+-- References:+--+----------------------------------------------------------------------+module ToySolver.SAT.SLS.ProbSAT+ ( Solver+ , newSolver+ , newSolverWeighted+ , getNumVars+ , getRandomGen+ , setRandomGen+ , getBestSolution+ , getStatistics++ , Options (..)+ , Callbacks (..)+ , Statistics (..)++ , generateUniformRandomSolution++ , probsat+ , walksat+ ) where++import Prelude hiding (break)++import Control.Exception+import Control.Loop+import Control.Monad+import Control.Monad.Primitive+import Control.Monad.Trans+import Control.Monad.Trans.Except+import Data.Array.Base (unsafeRead, unsafeWrite, unsafeAt)+import Data.Array.IArray+import Data.Array.IO+import Data.Array.Unboxed+import Data.Array.Unsafe+import Data.Bits+import Data.Default.Class+import qualified Data.Foldable as F+import Data.Int+import Data.IORef+import Data.Maybe+import Data.Sequence ((|>))+import qualified Data.Sequence as Seq+import Data.Typeable+import Data.Word+import System.Clock+import qualified System.Random.MWC as Rand+import qualified System.Random.MWC.Distributions as Rand+import qualified ToySolver.FileFormat.CNF as CNF+import ToySolver.Internal.Data.IOURef+import qualified ToySolver.Internal.Data.Vec as Vec+import qualified ToySolver.SAT.Types as SAT++-- -------------------------------------------------------------------++data Solver+ = Solver+ { svClauses :: !(Array ClauseId PackedClause)+ , svClauseWeights :: !(Array ClauseId CNF.Weight)+ , svClauseWeightsF :: !(UArray ClauseId Double)+ , svClauseNumTrueLits :: !(IOUArray ClauseId Int32)+ , svClauseUnsatClauseIndex :: !(IOUArray ClauseId Int)+ , svUnsatClauses :: !(Vec.UVec ClauseId)++ , svVarOccurs :: !(Array SAT.Var (UArray Int ClauseId))+ , svVarOccursState :: !(Array SAT.Var (IOUArray Int Bool))+ , svSolution :: !(IOUArray SAT.Var Bool)++ , svObj :: !(IORef CNF.Weight)++ , svRandomGen :: !(IORef Rand.GenIO)+ , svBestSolution :: !(IORef (CNF.Weight, SAT.Model))+ , svStatistics :: !(IORef Statistics)+ }++type ClauseId = Int++type PackedClause = Array Int SAT.Lit++newSolver :: CNF.CNF -> IO Solver+newSolver cnf = do+ let wcnf =+ CNF.WCNF+ { CNF.wcnfNumVars = CNF.cnfNumVars cnf+ , CNF.wcnfNumClauses = CNF.cnfNumClauses cnf+ , CNF.wcnfTopCost = fromIntegral (CNF.cnfNumClauses cnf) + 1+ , CNF.wcnfClauses = [(1,c) | c <- CNF.cnfClauses cnf]+ }+ newSolverWeighted wcnf++newSolverWeighted :: CNF.WCNF -> IO Solver+newSolverWeighted wcnf = do+ let m :: SAT.Var -> Bool+ m _ = False+ nv = CNF.wcnfNumVars wcnf++ objRef <- newIORef (0::Integer)++ cs <- liftM catMaybes $ forM (CNF.wcnfClauses wcnf) $ \(w,pc) -> do+ case SAT.normalizeClause (SAT.unpackClause pc) of+ Nothing -> return Nothing+ Just [] -> modifyIORef' objRef (w+) >> return Nothing+ Just c -> do+ let c' = listArray (0, length c - 1) c+ seq c' $ return (Just (w,c'))+ let len = length cs+ clauses = listArray (0, len - 1) (map snd cs)+ weights :: Array ClauseId CNF.Weight+ weights = listArray (0, len - 1) (map fst cs)+ weightsF :: UArray ClauseId Double+ weightsF = listArray (0, len - 1) (map (fromIntegral . fst) cs)++ (varOccurs' :: IOArray SAT.Var (Seq.Seq (Int, Bool))) <- newArray (1, nv) Seq.empty++ clauseNumTrueLits <- newArray (bounds clauses) 0+ clauseUnsatClauseIndex <- newArray (bounds clauses) (-1)+ unsatClauses <- Vec.new++ forAssocsM_ clauses $ \(c,clause) -> do+ let n = sum [1 | lit <- elems clause, SAT.evalLit m lit]+ writeArray clauseNumTrueLits c n+ when (n == 0) $ do+ i <- Vec.getSize unsatClauses+ writeArray clauseUnsatClauseIndex c i+ Vec.push unsatClauses c+ modifyIORef objRef ((weights ! c) +)+ forM_ (elems clause) $ \lit -> do+ let v = SAT.litVar lit+ let b = SAT.evalLit m lit+ seq b $ modifyArray varOccurs' v (|> (c,b))++ varOccurs <- do+ (arr::IOArray SAT.Var (UArray Int ClauseId)) <- newArray_ (1, nv)+ forM_ [1 .. nv] $ \v -> do+ s <- readArray varOccurs' v+ writeArray arr v $ listArray (0, Seq.length s - 1) (map fst (F.toList s))+ unsafeFreeze arr++ varOccursState <- do+ (arr::IOArray SAT.Var (IOUArray Int Bool)) <- newArray_ (1, nv)+ forM_ [1 .. nv] $ \v -> do+ s <- readArray varOccurs' v+ ss <- newArray_ (0, Seq.length s - 1)+ forM_ (zip [0..] (F.toList s)) $ \(j,a) -> writeArray ss j (snd a)+ writeArray arr v ss+ unsafeFreeze arr++ solution <- newListArray (1, nv) $ [SAT.evalVar m v | v <- [1..nv]]++ bestObj <- readIORef objRef+ bestSol <- freeze solution+ bestSolution <- newIORef (bestObj, bestSol)++ randGen <- newIORef =<< Rand.create++ stat <- newIORef def++ return $+ Solver+ { svClauses = clauses+ , svClauseWeights = weights+ , svClauseWeightsF = weightsF+ , svClauseNumTrueLits = clauseNumTrueLits+ , svClauseUnsatClauseIndex = clauseUnsatClauseIndex+ , svUnsatClauses = unsatClauses++ , svVarOccurs = varOccurs+ , svVarOccursState = varOccursState+ , svSolution = solution++ , svObj = objRef++ , svRandomGen = randGen+ , svBestSolution = bestSolution+ , svStatistics = stat+ }+++flipVar :: Solver -> SAT.Var -> IO ()+flipVar solver v = mask_ $ do+ let occurs = svVarOccurs solver ! v+ occursState = svVarOccursState solver ! v+ seq occurs $ seq occursState $ return ()+ modifyArray (svSolution solver) v not+ forAssocsM_ occurs $ \(j,!c) -> do+ b <- unsafeRead occursState j+ n <- unsafeRead (svClauseNumTrueLits solver) c+ unsafeWrite occursState j (not b)+ if b then do+ unsafeWrite (svClauseNumTrueLits solver) c (n-1)+ when (n==1) $ do+ i <- Vec.getSize (svUnsatClauses solver)+ Vec.push (svUnsatClauses solver) c+ unsafeWrite (svClauseUnsatClauseIndex solver) c i+ modifyIORef' (svObj solver) (+ unsafeAt (svClauseWeights solver) c)+ else do+ unsafeWrite (svClauseNumTrueLits solver) c (n+1)+ when (n==0) $ do+ s <- Vec.getSize (svUnsatClauses solver)+ i <- unsafeRead (svClauseUnsatClauseIndex solver) c+ unless (i == s-1) $ do+ let i2 = s-1+ c2 <- Vec.unsafeRead (svUnsatClauses solver) i2+ Vec.unsafeWrite (svUnsatClauses solver) i2 c+ Vec.unsafeWrite (svUnsatClauses solver) i c2+ unsafeWrite (svClauseUnsatClauseIndex solver) c2 i+ _ <- Vec.unsafePop (svUnsatClauses solver)+ modifyIORef' (svObj solver) (subtract (unsafeAt (svClauseWeights solver) c))+ return ()++setSolution :: SAT.IModel m => Solver -> m -> IO ()+setSolution solver m = do+ b <- getBounds (svSolution solver)+ forM_ (range b) $ \v -> do+ val <- readArray (svSolution solver) v+ let val' = SAT.evalVar m v+ unless (val == val') $ do+ flipVar solver v++getNumVars :: Solver -> IO Int+getNumVars solver = return $ rangeSize $ bounds (svVarOccurs solver)++getRandomGen :: Solver -> IO Rand.GenIO+getRandomGen solver = readIORef (svRandomGen solver)++setRandomGen :: Solver -> Rand.GenIO -> IO ()+setRandomGen solver gen = writeIORef (svRandomGen solver) gen++getBestSolution :: Solver -> IO (CNF.Weight, SAT.Model)+getBestSolution solver = readIORef (svBestSolution solver)++getStatistics :: Solver -> IO Statistics+getStatistics solver = readIORef (svStatistics solver)++{-# INLINE getMakeValue #-}+getMakeValue :: Solver -> SAT.Var -> IO Double+getMakeValue solver v = do+ let occurs = svVarOccurs solver ! v+ (lb,ub) = bounds occurs+ seq occurs $ seq lb $ seq ub $+ numLoopState lb ub 0 $ \ !r !i -> do+ let c = unsafeAt occurs i+ n <- unsafeRead (svClauseNumTrueLits solver) c+ return $! if n == 0 then (r + unsafeAt (svClauseWeightsF solver) c) else r++{-# INLINE getBreakValue #-}+getBreakValue :: Solver -> SAT.Var -> IO Double+getBreakValue solver v = do+ let occurs = svVarOccurs solver ! v+ occursState = svVarOccursState solver ! v+ (lb,ub) = bounds occurs+ seq occurs $ seq occursState $ seq lb $ seq ub $+ numLoopState lb ub 0 $ \ !r !i -> do+ b <- unsafeRead occursState i+ if b then do+ let c = unsafeAt occurs i+ n <- unsafeRead (svClauseNumTrueLits solver) c+ return $! if n==1 then (r + unsafeAt (svClauseWeightsF solver) c) else r+ else+ return r++-- -------------------------------------------------------------------++data Options+ = Options+ { optTarget :: !CNF.Weight+ , optMaxTries :: !Int+ , optMaxFlips :: !Int+ , optPickClauseWeighted :: Bool+ }+ deriving (Eq, Show)++instance Default Options where+ def =+ Options+ { optTarget = 0+ , optMaxTries = 1+ , optMaxFlips = 100000+ , optPickClauseWeighted = False+ }++data Callbacks+ = Callbacks+ { cbGenerateInitialSolution :: Solver -> IO SAT.Model+ , cbOnUpdateBestSolution :: Solver -> CNF.Weight -> SAT.Model -> IO ()+ }++instance Default Callbacks where+ def =+ Callbacks+ { cbGenerateInitialSolution = generateUniformRandomSolution+ , cbOnUpdateBestSolution = \_ _ _ -> return ()+ }++data Statistics+ = Statistics+ { statTotalCPUTime :: !TimeSpec+ , statFlips :: !Int+ , statFlipsPerSecond :: !Double+ }+ deriving (Eq, Show)++instance Default Statistics where+ def =+ Statistics+ { statTotalCPUTime = 0+ , statFlips = 0+ , statFlipsPerSecond = 0+ }++-- -------------------------------------------------------------------++generateUniformRandomSolution :: Solver -> IO SAT.Model+generateUniformRandomSolution solver = do+ gen <- getRandomGen solver+ n <- getNumVars solver+ (a :: IOUArray Int Bool) <- newArray_ (1,n)+ forM_ [1..n] $ \v -> do+ b <- Rand.uniform gen+ writeArray a v b+ unsafeFreeze a++checkCurrentSolution :: Solver -> Callbacks -> IO ()+checkCurrentSolution solver cb = do+ best <- readIORef (svBestSolution solver)+ obj <- readIORef (svObj solver)+ when (obj < fst best) $ do+ sol <- freeze (svSolution solver)+ writeIORef (svBestSolution solver) (obj, sol)+ cbOnUpdateBestSolution cb solver obj sol++pickClause :: Solver -> Options -> IO PackedClause+pickClause solver opt = do+ gen <- getRandomGen solver+ if optPickClauseWeighted opt then do+ obj <- readIORef (svObj solver)+ let f !j !x = do+ c <- Vec.read (svUnsatClauses solver) j+ let w = svClauseWeights solver ! c+ if x < w then+ return c+ else+ f (j + 1) (x - w)+ x <- rand obj gen+ c <- f 0 x+ return $ (svClauses solver ! c)+ else do+ s <- Vec.getSize (svUnsatClauses solver)+ j <- Rand.uniformR (0, s - 1) gen -- For integral types inclusive range is used+ liftM (svClauses solver !) $ Vec.read (svUnsatClauses solver) j++rand :: PrimMonad m => Integer -> Rand.Gen (PrimState m) -> m Integer+rand n gen+ | n <= toInteger (maxBound :: Word32) = liftM toInteger $ Rand.uniformR (0, fromIntegral n - 1 :: Word32) gen+ | otherwise = do+ a <- rand (n `shiftR` 32) gen+ (b::Word32) <- Rand.uniform gen+ return $ (a `shiftL` 32) .|. toInteger b++data Finished = Finished+ deriving (Show, Typeable)++instance Exception Finished++-- -------------------------------------------------------------------++probsat :: Solver -> Options -> Callbacks -> (Double -> Double -> Double) -> IO ()+probsat solver opt cb f = do+ gen <- getRandomGen solver+ let maxClauseLen =+ if rangeSize (bounds (svClauses solver)) == 0+ then 0+ else maximum $ map (rangeSize . bounds) $ elems (svClauses solver)+ (wbuf :: IOUArray Int Double) <- newArray_ (0, maxClauseLen-1)+ wsumRef <- newIOURef (0 :: Double)++ let pickVar :: PackedClause -> IO SAT.Var+ pickVar c = do+ writeIOURef wsumRef 0+ forAssocsM_ c $ \(k,lit) -> do+ let v = SAT.litVar lit+ m <- getMakeValue solver v+ b <- getBreakValue solver v+ let w = f m b+ writeArray wbuf k w+ modifyIOURef wsumRef (+w)+ wsum <- readIOURef wsumRef++ let go :: Int -> Double -> IO Int+ go !k !a = do+ if not (inRange (bounds c) k) then do+ return $! snd (bounds c)+ else do+ w <- readArray wbuf k+ if a <= w then+ return k+ else+ go (k + 1) (a - w)+ k <- go 0 =<< Rand.uniformR (0, wsum) gen+ return $! SAT.litVar (c ! k)++ startCPUTime <- getTime ProcessCPUTime+ flipsRef <- newIOURef (0::Int)++ -- It's faster to use Control.Exception than using Control.Monad.Except+ let body = do+ replicateM_ (optMaxTries opt) $ do+ sol <- cbGenerateInitialSolution cb solver+ setSolution solver sol+ checkCurrentSolution solver cb+ replicateM_ (optMaxFlips opt) $ do+ s <- Vec.getSize (svUnsatClauses solver)+ when (s == 0) $ throw Finished+ obj <- readIORef (svObj solver)+ when (obj <= optTarget opt) $ throw Finished+ c <- pickClause solver opt+ v <- pickVar c+ flipVar solver v+ modifyIOURef flipsRef inc+ checkCurrentSolution solver cb+ body `catch` (\(_::Finished) -> return ())++ endCPUTime <- getTime ProcessCPUTime+ flips <- readIOURef flipsRef+ let totalCPUTime = endCPUTime `diffTimeSpec` startCPUTime+ totalCPUTimeSec = fromIntegral (toNanoSecs totalCPUTime) / 10^(9::Int)+ writeIORef (svStatistics solver) $+ Statistics+ { statTotalCPUTime = totalCPUTime+ , statFlips = flips+ , statFlipsPerSecond = fromIntegral flips / totalCPUTimeSec+ }++ return ()++++walksat :: Solver -> Options -> Callbacks -> Double -> IO ()+walksat solver opt cb p = do+ gen <- getRandomGen solver+ (buf :: Vec.UVec SAT.Var) <- Vec.new++ let pickVar :: PackedClause -> IO SAT.Var+ pickVar c = do+ Vec.clear buf+ let (lb,ub) = bounds c+ r <- runExceptT $ do+ _ <- numLoopState lb ub (1.0/0.0) $ \ !b0 !i -> do+ let v = SAT.litVar (c ! i)+ b <- lift $ getBreakValue solver v+ if b <= 0 then+ throwE v -- freebie move+ else if b < b0 then do+ lift $ Vec.clear buf >> Vec.push buf v+ return b+ else if b == b0 then do+ lift $ Vec.push buf v+ return b0+ else do+ return b0+ return ()+ case r of+ Left v -> return v+ Right _ -> do+ flag <- Rand.bernoulli p gen+ if flag then do+ -- random walk move+ i <- Rand.uniformR (lb,ub) gen+ return $! SAT.litVar (c ! i)+ else do+ -- greedy move+ s <- Vec.getSize buf+ if s == 1 then+ Vec.unsafeRead buf 0+ else do+ i <- Rand.uniformR (0, s - 1) gen+ Vec.unsafeRead buf i++ startCPUTime <- getTime ProcessCPUTime+ flipsRef <- newIOURef (0::Int)++ -- It's faster to use Control.Exception than using Control.Monad.Except+ let body = do+ replicateM_ (optMaxTries opt) $ do+ sol <- cbGenerateInitialSolution cb solver+ setSolution solver sol+ checkCurrentSolution solver cb+ replicateM_ (optMaxFlips opt) $ do+ s <- Vec.getSize (svUnsatClauses solver)+ when (s == 0) $ throw Finished+ obj <- readIORef (svObj solver)+ when (obj <= optTarget opt) $ throw Finished+ c <- pickClause solver opt+ v <- pickVar c+ flipVar solver v+ modifyIOURef flipsRef inc+ checkCurrentSolution solver cb+ body `catch` (\(_::Finished) -> return ())++ endCPUTime <- getTime ProcessCPUTime+ flips <- readIOURef flipsRef+ let totalCPUTime = endCPUTime `diffTimeSpec` startCPUTime+ totalCPUTimeSec = fromIntegral (toNanoSecs totalCPUTime) / 10^(9::Int)+ writeIORef (svStatistics solver) $+ Statistics+ { statTotalCPUTime = totalCPUTime+ , statFlips = flips+ , statFlipsPerSecond = fromIntegral flips / totalCPUTimeSec+ }++ return ()++-- -------------------------------------------------------------------++{-# INLINE modifyArray #-}+modifyArray :: (MArray a e m, Ix i) => a i e -> i -> (e -> e) -> m ()+modifyArray a i f = do+ e <- readArray a i+ writeArray a i (f e)++{-# INLINE forAssocsM_ #-}+forAssocsM_ :: (IArray a e, Monad m) => a Int e -> ((Int,e) -> m ()) -> m ()+forAssocsM_ a f = do+ let (lb,ub) = bounds a+ numLoop lb ub $ \i ->+ f (i, unsafeAt a i)++{-# INLINE inc #-}+inc :: Integral a => a -> a+inc a = a+1+ +-- -------------------------------------------------------------------
src/ToySolver/SAT/Store/CNF.hs view
@@ -22,10 +22,10 @@ import Data.Primitive.MutVar import Data.Sequence (Seq, (|>)) import qualified Data.Sequence as Seq+import qualified ToySolver.FileFormat.CNF as CNF import qualified ToySolver.SAT.Types as SAT-import qualified ToySolver.Text.CNF as CNF -data CNFStore m = CNFStore (MutVar (PrimState m) Int) (MutVar (PrimState m) (Seq SAT.Clause))+data CNFStore m = CNFStore (MutVar (PrimState m) Int) (MutVar (PrimState m) (Seq SAT.PackedClause)) instance PrimMonad m => SAT.NewVar m (CNFStore m) where newVar (CNFStore ref _) = do@@ -35,7 +35,9 @@ instance PrimMonad m => SAT.AddClause m (CNFStore m) where addClause (CNFStore _ ref) clause = case SAT.normalizeClause clause of- Just clause' -> modifyMutVar' ref (|> clause')+ Just clause' -> do+ let clause'' = SAT.packClause clause'+ seq clause'' $ modifyMutVar' ref (|> clause'') Nothing -> return () newCNFStore :: PrimMonad m => m (CNFStore m)@@ -50,7 +52,7 @@ cs <- readMutVar ref2 return $ CNF.CNF- { CNF.numVars = nv- , CNF.numClauses = Seq.length cs- , CNF.clauses = F.toList cs+ { CNF.cnfNumVars = nv+ , CNF.cnfNumClauses = Seq.length cs+ , CNF.cnfClauses = F.toList cs }
src/ToySolver/SAT/Types.hs view
@@ -32,6 +32,11 @@ , evalClause , clauseToPBLinAtLeast + -- * Packed Clause+ , PackedClause+ , packClause+ , unpackClause+ -- * Cardinality Constraint , AtLeast , Exactly@@ -56,15 +61,19 @@ , evalPBLinSum , evalPBLinAtLeast , evalPBLinExactly- , pbLowerBound- , pbUpperBound- , pbSubsume- , evalPBConstraint+ , pbLinLowerBound+ , pbLinUpperBound+ , pbLinSubsume -- * Non-linear Pseudo Boolean constraint , PBTerm , PBSum , evalPBSum+ , evalPBConstraint+ , evalPBFormula+ , pbLowerBound+ , pbUpperBound+ , removeNegationFromPBSum -- * XOR Clause , XORClause@@ -90,7 +99,11 @@ import qualified Data.IntMap.Strict as IntMap import Data.IntSet (IntSet) import qualified Data.IntSet as IntSet+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe import qualified Data.Vector as V+import qualified Data.Vector.Unboxed as VU import qualified Data.PseudoBoolean as PBFile import ToySolver.Data.LBool import qualified ToySolver.Combinatorial.SubsetSum as SubsetSum@@ -211,6 +224,14 @@ clauseToPBLinAtLeast :: Clause -> PBLinAtLeast clauseToPBLinAtLeast xs = ([(1,l) | l <- xs], 1) +type PackedClause = VU.Vector Lit++packClause :: Clause -> PackedClause+packClause = VU.fromList++unpackClause :: PackedClause -> Clause+unpackClause = VU.toList+ type AtLeast = ([Lit], Int) type Exactly = ([Lit], Int) @@ -395,15 +416,15 @@ evalPBLinExactly :: IModel m => m -> PBLinAtLeast -> Bool evalPBLinExactly m (lhs,rhs) = evalPBLinSum m lhs == rhs -pbLowerBound :: PBLinSum -> Integer-pbLowerBound xs = sum [if c < 0 then c else 0 | (c,_) <- xs]+pbLinLowerBound :: PBLinSum -> Integer+pbLinLowerBound xs = sum [if c < 0 then c else 0 | (c,_) <- xs] -pbUpperBound :: PBLinSum -> Integer-pbUpperBound xs = sum [if c > 0 then c else 0 | (c,_) <- xs]+pbLinUpperBound :: PBLinSum -> Integer+pbLinUpperBound xs = sum [if c > 0 then c else 0 | (c,_) <- xs] -- (Σi ci li ≥ rhs1) subsumes (Σi di li ≥ rhs2) iff rhs1≥rhs2 and di≥ci for all i.-pbSubsume :: PBLinAtLeast -> PBLinAtLeast -> Bool-pbSubsume (lhs1,rhs1) (lhs2,rhs2) =+pbLinSubsume :: PBLinAtLeast -> PBLinAtLeast -> Bool+pbLinSubsume (lhs1,rhs1) (lhs2,rhs2) = rhs1 >= rhs2 && and [di >= ci | (ci,li) <- lhs1, let di = IntMap.findWithDefault 0 li lhs2'] where lhs2' = IntMap.fromList [(l,c) | (c,l) <- lhs2]@@ -420,6 +441,31 @@ op' = case op of PBFile.Ge -> (>=) PBFile.Eq -> (==)++evalPBFormula :: IModel m => m -> PBFile.Formula -> Maybe Integer+evalPBFormula m formula = do+ guard $ all (evalPBConstraint m) $ PBFile.pbConstraints formula+ return $ evalPBSum m $ fromMaybe [] $ PBFile.pbObjectiveFunction formula++pbLowerBound :: PBSum -> Integer+pbLowerBound xs = sum [c | (c,ls) <- xs, c < 0 || null ls]++pbUpperBound :: PBSum -> Integer+pbUpperBound xs = sum [c | (c,ls) <- xs, c > 0 || null ls]++removeNegationFromPBSum :: PBSum -> PBSum+removeNegationFromPBSum ts =+ [(c, IntSet.toList m) | (m, c) <- Map.toList $ Map.unionsWith (+) $ map f ts, c /= 0]+ where+ f :: PBTerm -> Map VarSet Integer+ f (c, ls) = IntSet.foldl' g (Map.singleton IntSet.empty c) (IntSet.fromList ls)++ g :: Map VarSet Integer -> Lit -> Map VarSet Integer+ g m l+ | l > 0 = Map.mapKeysWith (+) (IntSet.insert v) m+ | otherwise = Map.unionWith (+) m $ Map.fromListWith (+) [(IntSet.insert v xs, negate c) | (xs,c) <- Map.toList m]+ where+ v = litVar l -- | XOR clause --
+ src/ToySolver/SDP.hs view
@@ -0,0 +1,196 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE TypeFamilies #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.SDP+-- Copyright : (c) Masahiro Sakai 2017+-- License : BSD-style+--+-- Maintainer : masahiro.sakai@gmail.com+-- Stability : exprimental+-- Portability : non-portable+--+-- References:+--+-- * Convert Semidefinite program forms - Mathematics Stack Exchange+-- <https://math.stackexchange.com/questions/732658/convert-semidefinite-program-forms>+--+-----------------------------------------------------------------------------++module ToySolver.SDP+ ( dualize+ , DualizeInfo (..)+ ) where++import qualified Data.Map.Strict as Map+import Data.Scientific (Scientific)+import ToySolver.Converter.Base+import qualified ToySolver.Text.SDPFile as SDPFile++-- | Given a primal-dual pair (P), (D), it returns another primal-dual pair (P'), (D')+-- such that (P) is equivalent to (D') and (D) is equivalent to (P').+dualize :: SDPFile.Problem -> (SDPFile.Problem, DualizeInfo)+dualize origProb =+ ( SDPFile.Problem+ { SDPFile.blockStruct = blockStruct+ , SDPFile.costs = d+ , SDPFile.matrices = a0 : as+ }+ , DualizeInfo m (SDPFile.blockStruct origProb)+ )+ where+ {- original:+ (P)+ min Σ_i=1^m c_i x_i+ s.t.+ X = Σ_i=1^m F_i x_i - F_0+ X ⪰ 0+ (D)+ max F_0 • Y+ s.t.+ F_i • Y = c_i for i ∈ {1,…,m}+ Y ⪰ 0+ where+ x : variable over R^m+ c ∈ R^m+ F_0, F_1, … , F_m ∈ R^(n × n)+ -}+ m :: Int+ m = SDPFile.mDim origProb+ c :: [Scientific]+ c = SDPFile.costs origProb+ f0 :: SDPFile.Matrix+ fs :: [SDPFile.Matrix]+ f0:fs = SDPFile.matrices origProb++ {- transformed+ (P')+ min d^T・z+ s.t.+ Z = Σ_i=1^n Σ_j=1^i A_ij z_ij - A_0+ Z ⪰ 0+ (D')+ max A_0 • W+ s.t.+ A_ij • W = d_ij for i ∈ {1,…,n}, j ∈ {1,…,i}+ W ⪰ 0+ where+ z : variable over R^{n(n+1)/2}+ d_ij ∈ R for i ∈ {1,…,n}, j ∈ {1,…,i}+ d_ij = - F0 [i,j] if i=j+ = - (F0 [i,j] + F0 [j,i]) otherwise+ A_0 ∈ R^((2m+n)×(2m+n))+ A_0 = diag(-c, c, 0_{n×n})+ A_ij ∈ R^((2m+n)×(2m+n)) for i ∈ {1,…,n}, j ∈ {1,…,i}+ A_ij [ k, k] = - (if i=j then F_k [i,j] else F_k [i,j] + F_k [j,i]) for k∈{1,…,m}+ A_ij [ m+k, m+k] = (if i=j then F_k [i,j] else F_k [i,j] + F_k [j,i]) for k∈{1,…,m}+ A_ij [2m+i,2m+j] = 1+ A_ij [2m+j,2m+i] = 1+ A_ij [ _ , _ ] = 0++ correspondence:+ W = diag(x+, x-, X)+ Y [i,j] = z_ij if j≤i+ = z_ji otherwise+ Z = diag(0, 0, Y)+ -}+ blockStruct :: [Int]+ blockStruct = [-m, -m] ++ SDPFile.blockStruct origProb+ a0 :: SDPFile.Matrix+ a0 =+ [ Map.fromList [((j,j), -cj) | (j,cj) <- zip [1..m] c, cj /= 0]+ , Map.fromList [((j,j), cj) | (j,cj) <- zip [1..m] c, cj /= 0]+ ] +++ [ Map.empty | _ <- SDPFile.blockStruct origProb]+ as :: [SDPFile.Matrix]+ as =+ [ [ Map.fromList [ ((k,k), - (if i == j then v else 2*v))+ | (k,fk) <- zip [1..m] fs, let v = SDPFile.blockElem i j (fk!!b), v /= 0]+ , Map.fromList [ ((k,k), (if i == j then v else 2*v))+ | (k,fk) <- zip [1..m] fs, let v = SDPFile.blockElem i j (fk!!b), v /= 0]+ ] +++ [ if b /= b2 then+ Map.empty+ else if i == j then+ Map.singleton (i,j) 1+ else+ Map.fromList [((i,j),1), ((j,i),1)]+ | (b2, _) <- zip [0..] (SDPFile.blockStruct origProb)+ ]+ | (b,block) <- zip [0..] (SDPFile.blockStruct origProb)+ , (i,j) <- blockIndexes block+ ]+ d =+ [ - (if i == j then v else 2*v)+ | (b,block) <- zip [0..] (SDPFile.blockStruct origProb)+ , (i,j) <- blockIndexes block+ , let v = SDPFile.blockElem i j (f0 !! b)+ ]++blockIndexes :: Int -> [(Int,Int)]+blockIndexes n = if n >= 0 then [(i,j) | i <- [1..n], j <- [1..i]] else [(i,i) | i <- [1..(-n)]]++blockIndexesLen :: Int -> Int+blockIndexesLen n = if n >= 0 then n*(n+1) `div` 2 else -n+++data DualizeInfo = DualizeInfo Int [Int]+ deriving (Eq, Show, Read)++instance Transformer DualizeInfo where+ type Source DualizeInfo = SDPFile.Solution+ type Target DualizeInfo = SDPFile.Solution++instance ForwardTransformer DualizeInfo where+ transformForward (DualizeInfo _origM origBlockStruct) + SDPFile.Solution+ { SDPFile.primalVector = xV+ , SDPFile.primalMatrix = xM+ , SDPFile.dualMatrix = yM+ } =+ SDPFile.Solution+ { SDPFile.primalVector = zV+ , SDPFile.primalMatrix = zM+ , SDPFile.dualMatrix = wM+ }+ where+ zV = concat [[SDPFile.blockElem i j block | (i,j) <- blockIndexes b] | (b,block) <- zip origBlockStruct yM]+ zM = Map.empty : Map.empty : yM+ wM =+ [ Map.fromList $ zipWith (\i x -> ((i,i), if x >= 0 then x else 0)) [1..] xV+ , Map.fromList $ zipWith (\i x -> ((i,i), if x <= 0 then -x else 0)) [1..] xV+ ] ++ xM++instance BackwardTransformer DualizeInfo where+ transformBackward (DualizeInfo origM origBlockStruct)+ SDPFile.Solution+ { SDPFile.primalVector = zV+ , SDPFile.primalMatrix = _zM+ , SDPFile.dualMatrix = wM+ } =+ case wM of+ (xps:xns:xM) ->+ SDPFile.Solution+ { SDPFile.primalVector = xV+ , SDPFile.primalMatrix = xM+ , SDPFile.dualMatrix = yM+ }+ where+ xV = [SDPFile.blockElem i i xps - SDPFile.blockElem i i xns | i <- [1..origM]]+ yM = f origBlockStruct zV+ where+ f [] _ = []+ f (block : blocks) zV1 =+ case splitAt (blockIndexesLen block) zV1 of+ (vals, zV2) -> symblock (zip (blockIndexes block) vals) : f blocks zV2+ _ -> error "ToySolver.SDP.transformSolutionBackward: invalid solution"++symblock :: [((Int,Int), Scientific)] -> SDPFile.Block+symblock es = Map.fromList $ do+ e@((i,j),x) <- es+ if x == 0 then+ []+ else if i == j then+ return e+ else+ [e, ((j,i),x)]
src/ToySolver/SMT.hs view
@@ -197,7 +197,7 @@ | Unsupported deriving (Show, Typeable) -instance E.Exception Exception +instance E.Exception Exception data Solver = Solver@@ -985,7 +985,7 @@ checkSATAssuming :: Solver -> [Expr] -> IO Bool checkSATAssuming solver xs = do l <- getContextLit solver- named <- readIORef (smtNamedAssertions solver) + named <- readIORef (smtNamedAssertions solver) ref <- newIORef IntMap.empty ls <- forM xs $ \x -> do
src/ToySolver/Text/CNF.hs view
@@ -1,17 +1,20 @@-{-# LANGUAGE FlexibleContexts, OverloadedStrings #-} {-# OPTIONS_GHC -Wall #-}+-- {-# LANGUAGE BangPatterns #-}+-- {-# LANGUAGE OverloadedStrings #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Text.CNF--- Copyright : (c) Masahiro Sakai 2016+-- Copyright : (c) Masahiro Sakai 2016-2018 -- License : BSD-style -- -- Maintainer : masahiro.sakai@gmail.com -- Stability : provisional--- Portability : non-portable (FlexibleContexts, OverloadedStrings)+-- Portability : non-portable --+-- Reader and Writer for DIMACS CNF and family of similar formats.+-- ------------------------------------------------------------------------------module ToySolver.Text.CNF+module ToySolver.Text.CNF {-# DEPRECATED "Use ToySolver.FileFormat.CNF instead" #-} ( CNF (..) @@ -25,80 +28,24 @@ , cnfBuilder ) where -import Prelude hiding (writeFile)+import Prelude hiding (readFile, writeFile) import qualified Data.ByteString.Lazy.Char8 as BS import Data.ByteString.Builder-import Data.Char-import Data.Monoid-import System.IO hiding (writeFile)--import qualified ToySolver.SAT.Types as SAT--data CNF- = CNF- { numVars :: !Int- , numClauses :: !Int- , clauses :: [SAT.Clause]- }- deriving (Show, Eq, Ord)+import System.IO hiding (readFile, writeFile) -parseFile :: FilePath -> IO (Either String CNF)-parseFile filename = do- s <- BS.readFile filename- return $ parseByteString s+import ToySolver.FileFormat.CNF +-- | Parse a CNF file but returns an error message when parsing fails.+{-# DEPRECATED parseByteString "Use FileFormat.parse instead" #-} parseByteString :: BS.ByteString -> Either String CNF-parseByteString s =- case BS.words l of- (["p","cnf", nvar, nclause]) ->- Right $- CNF- { numVars = read $ BS.unpack nvar- , numClauses = read $ BS.unpack nclause- , clauses = map parseClauseBS ls- }- _ ->- Left "cannot find cnf header"- where- l :: BS.ByteString- ls :: [BS.ByteString]- (l:ls) = filter (not . isCommentBS) (BS.lines s)--parseClauseBS :: BS.ByteString -> SAT.Clause-parseClauseBS s = seqList xs $ xs- where- xs = go s- go s =- case BS.readInt (BS.dropWhile isSpace s) of- Nothing -> error "ToySolver.Text.MaxSAT: parse error"- Just (0,_) -> []- Just (i,s') -> i : go s'--isCommentBS :: BS.ByteString -> Bool-isCommentBS s =- case BS.uncons s of- Just ('c',_) -> True- _ -> False--seqList :: [a] -> b -> b-seqList [] b = b-seqList (x:xs) b = seq x $ seqList xs b--writeFile :: FilePath -> CNF -> IO ()-writeFile filepath cnf = do- withBinaryFile filepath WriteMode $ \h -> do- hSetBuffering h (BlockBuffering Nothing)- hPutBuilder h (cnfBuilder cnf)+parseByteString = parse +-- | Encode a 'CNF' to a 'Builder'+{-# DEPRECATED cnfBuilder "Use FileFormat.render instead" #-} cnfBuilder :: CNF -> Builder-cnfBuilder cnf = header <> mconcat (map f (clauses cnf))- where- header = mconcat- [ byteString "p cnf "- , intDec (numVars cnf), char7 ' '- , intDec (numClauses cnf), char7 '\n'- ]- f c = mconcat [intDec lit <> char7 ' '| lit <- c] <> byteString "0\n"+cnfBuilder = render +-- | Output a 'CNF' to a Handle.+{-# DEPRECATED hPutCNF "Use FileFormat.render instead" #-} hPutCNF :: Handle -> CNF -> IO () hPutCNF h cnf = hPutBuilder h (cnfBuilder cnf)
src/ToySolver/Text/GCNF.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -Wall #-} ----------------------------------------------------------------------------- -- |@@ -8,15 +7,10 @@ -- -- Maintainer : masahiro.sakai@gmail.com -- Stability : provisional--- Portability : non-portable (OverloadedStrings)--- --- References:--- --- * <http://www.satcompetition.org/2011/rules.pdf>---+-- Portability : portable -- ------------------------------------------------------------------------------module ToySolver.Text.GCNF+module ToySolver.Text.GCNF {-# DEPRECATED "Use ToySolver.FileFormat.CNF instead" #-} ( GCNF (..) , GroupIndex@@ -33,112 +27,22 @@ ) where import Prelude hiding (writeFile)-import qualified Data.ByteString.Lazy.Char8 as BS import Data.ByteString.Builder-import Data.Char-import Data.Monoid-import qualified ToySolver.SAT.Types as SAT+import qualified Data.ByteString.Lazy.Char8 as BL import System.IO hiding (writeFile)-import ToySolver.Internal.TextUtil--data GCNF- = GCNF- { numVars :: !Int- , numClauses :: !Int- , lastGroupIndex :: !GroupIndex- , clauses :: [GClause]- }--type GroupIndex = Int--type GClause = (GroupIndex, SAT.Clause)---parseFile :: FilePath -> IO (Either String GCNF)-parseFile filename = do- s <- BS.readFile filename- return $ parseByteString s--parseByteString :: BS.ByteString -> Either String GCNF-parseByteString s =- case BS.words l of- (["p","gcnf", nbvar', nbclauses', lastGroupIndex']) ->- Right $- GCNF- { numVars = read $ BS.unpack nbvar'- , numClauses = read $ BS.unpack nbclauses'- , lastGroupIndex = read $ BS.unpack lastGroupIndex'- , clauses = map parseLineBS ls- }- (["p","cnf", nbvar', nbclause']) ->- Right $- GCNF- { numVars = read $ BS.unpack nbvar'- , numClauses = read $ BS.unpack nbclause'- , lastGroupIndex = read $ BS.unpack nbclause'- , clauses = zip [1..] $ map parseCNFLineBS ls- }- _ ->- Left "cannot find gcnf header"- where- l :: BS.ByteString- ls :: [BS.ByteString]- (l:ls) = filter (not . isCommentBS) (BS.lines s)- -parseLineBS :: BS.ByteString -> GClause-parseLineBS s =- case BS.uncons (BS.dropWhile isSpace s) of- Just ('{', s1) ->- case BS.readInt s1 of- Just (idx,s2) | Just ('}', s3) <- BS.uncons s2 -> - let ys = parseClauseBS s3- in seq idx $ seqList ys $ (idx, ys)- _ -> error "ToySolver.Text.GCNF: parse error"- _ -> error "ToySolver.Text.GCNF: parse error"--parseCNFLineBS :: BS.ByteString -> SAT.Clause-parseCNFLineBS s = seq xs $ seqList xs $ xs- where- xs = parseClauseBS s--parseClauseBS :: BS.ByteString -> SAT.Clause-parseClauseBS s = seqList xs $ xs- where- xs = go s- go s =- case BS.readInt (BS.dropWhile isSpace s) of- Nothing -> error "ToySolver.Text.GCNF: parse error"- Just (0,_) -> []- Just (i,s') -> i : go s'--isCommentBS :: BS.ByteString -> Bool-isCommentBS s =- case BS.uncons s of- Just ('c',_) -> True- _ -> False--seqList :: [a] -> b -> b-seqList [] b = b-seqList (x:xs) b = seq x $ seqList xs b+import ToySolver.FileFormat.CNF -writeFile :: FilePath -> GCNF -> IO ()-writeFile filepath gcnf = do- withBinaryFile filepath WriteMode $ \h -> do- hSetBuffering h (BlockBuffering Nothing)- hPutGCNF h gcnf+-- | Parse a GCNF file but returns an error message when parsing fails.+{-# DEPRECATED parseByteString "Use FileFormat.parse instead" #-}+parseByteString :: BL.ByteString -> Either String GCNF+parseByteString = parse +-- | Encode a 'GCNF' to a 'Builder'+{-# DEPRECATED gcnfBuilder "Use FileFormat.render instead" #-} gcnfBuilder :: GCNF -> Builder-gcnfBuilder gcnf = header <> mconcat (map f (clauses gcnf))- where- header = mconcat- [ byteString "p gcnf "- , intDec (numVars gcnf), char7 ' '- , intDec (numClauses gcnf), char7 ' '- , intDec (lastGroupIndex gcnf), char7 '\n'- ]- f (idx,c) = char7 '{' <> intDec idx <> char7 '}' <>- mconcat [char7 ' ' <> intDec lit | lit <- c] <>- byteString " 0\n"+gcnfBuilder = render +-- | Output a 'GCNF' to a Handle.+{-# DEPRECATED hPutGCNF "Use FileFormat.render instead" #-} hPutGCNF :: Handle -> GCNF -> IO () hPutGCNF h gcnf = hPutBuilder h (gcnfBuilder gcnf)
− src/ToySolver/Text/MaxSAT.hs
@@ -1,149 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -Wall #-}--------------------------------------------------------------------------------- |--- Module : ToySolver.Text.MaxSAT--- Copyright : (c) Masahiro Sakai 2012--- License : BSD-style--- --- Maintainer : masahiro.sakai@gmail.com--- Stability : provisional--- Portability : portable--- --- References:--- --- * <http://maxsat.ia.udl.cat/requirements/>----------------------------------------------------------------------------------module ToySolver.Text.MaxSAT- (- WCNF (..)- , WeightedClause- , Weight-- -- * Parsing .cnf/.wcnf files- , parseFile- , parseByteString-- -- * Generating .wcnf files- , writeFile- , hPutWCNF- , wcnfBuilder- ) where--import Prelude hiding (writeFile)-import qualified Data.ByteString.Lazy.Char8 as BS-import Data.ByteString.Builder-import Data.Char-import Data.Monoid-import System.IO hiding (writeFile)--import qualified ToySolver.SAT.Types as SAT-import ToySolver.Internal.TextUtil--data WCNF- = WCNF- { numVars :: !Int- , numClauses :: !Int- , topCost :: !Weight- , clauses :: [WeightedClause]- }--type WeightedClause = (Weight, SAT.Clause)---- | Weigths must be greater than or equal to 1, and smaller than 2^63.-type Weight = Integer--parseFile :: FilePath -> IO (Either String WCNF)-parseFile filename = do- s <- BS.readFile filename- return $ parseByteString s--parseByteString :: BS.ByteString -> Either String WCNF-parseByteString s =- case BS.words l of- (["p","wcnf", nvar, nclause, top]) ->- Right $- WCNF- { numVars = read $ BS.unpack nvar- , numClauses = read $ BS.unpack nclause- , topCost = read $ BS.unpack top- , clauses = map parseWCNFLineBS ls- }- (["p","wcnf", nvar, nclause]) ->- Right $- WCNF- { numVars = read $ BS.unpack nvar- , numClauses = read $ BS.unpack nclause- -- top must be greater than the sum of the weights of violated soft clauses.- , topCost = fromInteger $ 2^(63::Int) - 1- , clauses = map parseWCNFLineBS ls- }- (["p","cnf", nvar, nclause]) ->- Right $- WCNF- { numVars = read $ BS.unpack nvar- , numClauses = read $ BS.unpack nclause- -- top must be greater than the sum of the weights of violated soft clauses.- , topCost = fromInteger $ 2^(63::Int) - 1- , clauses = map parseCNFLineBS ls- }- _ ->- Left "cannot find wcnf/cnf header"- where- l :: BS.ByteString- ls :: [BS.ByteString]- (l:ls) = filter (not . isCommentBS) (BS.lines s)--parseWCNFLineBS :: BS.ByteString -> WeightedClause-parseWCNFLineBS s =- case BS.readInteger (BS.dropWhile isSpace s) of- Nothing -> error "ToySolver.Text.MaxSAT: no weight"- Just (w, s') -> seq w $ seq xs $ (w, xs)- where- xs = parseClauseBS s'--parseCNFLineBS :: BS.ByteString -> WeightedClause-parseCNFLineBS s = seq xs $ (1, xs)- where- xs = parseClauseBS s--parseClauseBS :: BS.ByteString -> SAT.Clause-parseClauseBS s = seqList xs $ xs- where- xs = go s- go s =- case BS.readInt (BS.dropWhile isSpace s) of- Nothing -> error "ToySolver.Text.MaxSAT: parse error"- Just (0,_) -> []- Just (i,s') -> i : go s'--isCommentBS :: BS.ByteString -> Bool-isCommentBS s =- case BS.uncons s of- Just ('c',_) -> True- _ -> False--seqList :: [a] -> b -> b-seqList [] b = b-seqList (x:xs) b = seq x $ seqList xs b--writeFile :: FilePath -> WCNF -> IO ()-writeFile filepath wcnf = do- withBinaryFile filepath WriteMode $ \h -> do- hSetBuffering h (BlockBuffering Nothing)- hPutWCNF h wcnf--wcnfBuilder :: WCNF -> Builder-wcnfBuilder wcnf = header <> mconcat (map f (clauses wcnf))- where- header = mconcat- [ byteString "p wcnf "- , intDec (numVars wcnf), char7 ' '- , intDec (numClauses wcnf), char7 ' '- , integerDec (topCost wcnf), char7 '\n'- ]- f (w,c) = integerDec w <> mconcat [char7 ' ' <> intDec lit | lit <- c] <> byteString " 0\n"--hPutWCNF :: Handle -> WCNF -> IO ()-hPutWCNF h wcnf = hPutBuilder h (wcnfBuilder wcnf)
src/ToySolver/Text/QDimacs.hs view
@@ -1,5 +1,4 @@ {-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE OverloadedStrings #-} ----------------------------------------------------------------------------- -- | -- Module : ToySolver.Text.QDimacs@@ -10,122 +9,43 @@ -- Stability : provisional -- Portability : portable ----- References:------ * QDIMACS standard Ver. 1.1--- <http://www.qbflib.org/qdimacs.html>--- ------------------------------------------------------------------------------module ToySolver.Text.QDimacs+module ToySolver.Text.QDimacs {-# DEPRECATED "Use ToySolver.FileFormat.CNF instead" #-} ( QDimacs (..) , Quantifier (..) , QuantSet , Atom , Lit , Clause+ , PackedClause+ , packClause+ , unpackClause , parseFile , parseByteString++ -- * Generating .qdimacs files+ , writeFile+ , hPutQDimacs+ , qdimacsBuilder ) where -import Control.DeepSeq+import Prelude hiding (writeFile)+import Data.ByteString.Builder import qualified Data.ByteString.Lazy.Char8 as BL-import Data.Char--{--http://www.qbflib.org/qdimacs.html--<input> ::= <preamble> <prefix> <matrix> EOF--<preamble> ::= [<comment_lines>] <problem_line>-<comment_lines> ::= <comment_line> <comment_lines> | <comment_line> -<comment_line> ::= c <text> EOL-<problem_line> ::= p cnf <pnum> <pnum> EOL--<prefix> ::= [<quant_sets>]-<quant_sets> ::= <quant_set> <quant_sets> | <quant_set>-<quant_set> ::= <quantifier> <atom_set> 0 EOL-<quantifier> ::= e | a-<atom_set> ::= <pnum> <atom_set> | <pnum>--<matrix> ::= <clause_list>-<clause_list> ::= <clause> <clause_list> | <clause> -<clause> ::= <literal> <clause> | <literal> 0 EOL-<literal> ::= <num>--<text> ::= {A sequence of non-special ASCII characters}-<num> ::= {A 32-bit signed integer different from 0}-<pnum> ::= {A 32-bit signed integer greater than 0}--}--data QDimacs- = QDimacs- { numVars :: !Int- , numClauses :: !Int- , prefix :: [QuantSet]- , matrix :: [Clause]- }- deriving (Eq, Ord, Show, Read)--data Quantifier- = E -- ^ existential quantifier- | A -- ^ universal quantifier- deriving (Eq, Ord, Show, Read, Enum, Bounded)--type QuantSet = (Quantifier, [Atom])--type Atom = Int -- better to use Int32?--type Lit = Int -- better to use Int32--type Clause = [Lit]--parseFile :: FilePath -> IO (Either String QDimacs)-parseFile filename = do- s <- BL.readFile filename- return $ parseByteString s--parseByteString :: BL.ByteString -> (Either String QDimacs)-parseByteString = f . BL.lines- where- f [] = Left "QDimacs.parseByteString: premature end of file"- f (l : ls) =- case BL.uncons l of- Nothing -> Left "QDimacs.parseByteString: no problem line"- Just ('c', _) -> f ls- Just ('p', s) ->- case BL.words s of- ["cnf", numVars', numClauses'] ->- case parsePrefix ls of- (prefix', ls') -> Right $- QDimacs- { numVars = read $ BL.unpack numVars'- , numClauses = read $ BL.unpack numClauses'- , prefix = prefix'- , matrix = parseClauses ls'- }- _ -> Left "QDimacs.parseByteString: invalid problem line"- Just (c, _) -> Left ("QDimacs.parseByteString: invalid prefix " ++ show c)+import System.IO hiding (writeFile)+import ToySolver.FileFormat.CNF -parsePrefix :: [BL.ByteString] -> ([QuantSet], [BL.ByteString])-parsePrefix = go []- where- go result [] = (reverse result, [])- go result lls@(l : ls) =- case BL.uncons l of- Just (c,s)- | c=='a' || c=='e' ->- let atoms = readInts s- q = if c=='a' then A else E- in seq q $ deepseq atoms $ go ((q, atoms) : result) ls- | otherwise ->- (reverse result, lls)- _ -> error "QDimacs.parseProblem: invalid line"+-- | Parse a QDimacs file but returns an error message when parsing fails.+{-# DEPRECATED parseByteString "Use FileFormat.parse instead" #-}+parseByteString :: BL.ByteString -> Either String QDimacs+parseByteString = parse -parseClauses :: [BL.ByteString] -> [Clause]-parseClauses = map readInts+-- | Encode a 'QDimacs' to a 'Builder'+{-# DEPRECATED qdimacsBuilder "Use FileFormat.render instead" #-}+qdimacsBuilder :: QDimacs -> Builder+qdimacsBuilder = render -readInts :: BL.ByteString -> [Int]-readInts s =- case BL.readInt (BL.dropWhile isSpace s) of- Just (z, s') -> if z == 0 then [] else z : readInts s'- Nothing -> []+-- | Output a 'QDimacs' to a Handle.+{-# DEPRECATED hPutQDimacs "Use FileFormat.render instead" #-}+hPutQDimacs :: Handle -> QDimacs -> IO ()+hPutQDimacs h qdimacs = hPutBuilder h (qdimacsBuilder qdimacs)
src/ToySolver/Text/SDPFile.hs view
@@ -27,7 +27,15 @@ , mDim , nBlock , blockElem+ -- * The solution type+ , Solution (..)+ , evalPrimalObjective+ , evalDualObjective + -- * File I/O+ , readDataFile+ , writeDataFile+ -- * Construction , DenseMatrix , DenseBlock@@ -36,23 +44,23 @@ , diagBlock -- * Rendering- , render- , renderSparse+ , renderData+ , renderSparseData -- * Parsing , ParseError , parseData- , parseDataFile , parseSparseData- , parseSparseDataFile ) where -import Control.Applicative ((<*))+import Control.Exception import Control.Monad import qualified Data.ByteString.Lazy as BL import Data.ByteString.Builder (Builder) import qualified Data.ByteString.Builder as B import qualified Data.ByteString.Builder.Scientific as B+import Data.Char+import qualified Data.Foldable as F import Data.List (intersperse) import Data.Monoid import Data.Scientific (Scientific)@@ -62,11 +70,13 @@ import Data.Map (Map) import qualified Data.Map as Map import qualified Data.IntMap as IntMap+import System.FilePath (takeExtension)+import System.IO import qualified Text.Megaparsec as MegaParsec #if MIN_VERSION_megaparsec(6,0,0) import Data.Word import Data.Void-import Text.Megaparsec hiding (ParseError)+import Text.Megaparsec hiding (ParseError, oneOf) import Text.Megaparsec.Byte hiding (oneOf) import qualified Text.Megaparsec.Byte as MegaParsec import qualified Text.Megaparsec.Byte.Lexer as Lexer@@ -77,8 +87,11 @@ import Text.Megaparsec.Prim (MonadParsec ()) #endif -#if MIN_VERSION_megaparsec(6,0,0)+#if MIN_VERSION_megaparsec(7,0,0) type C e s m = (MonadParsec e s m, Token s ~ Word8)+type ParseError = MegaParsec.ParseErrorBundle BL.ByteString Void+#elif MIN_VERSION_megaparsec(6,0,0)+type C e s m = (MonadParsec e s m, Token s ~ Word8) type ParseError = MegaParsec.ParseError Word8 Void #elif MIN_VERSION_megaparsec(5,0,0) type C e s m = (MonadParsec e s m, Token s ~ Char)@@ -88,6 +101,11 @@ type ParseError = MegaParsec.ParseError #endif +#if MIN_VERSION_megaparsec(7,0,0)+anyChar :: C e s m => m Word8+anyChar = anySingle+#endif+ -- --------------------------------------------------------------------------- -- problem description -- ---------------------------------------------------------------------------@@ -116,6 +134,26 @@ blockElem i j b = Map.findWithDefault 0 (i,j) b -- ---------------------------------------------------------------------------+-- solution+-- ---------------------------------------------------------------------------++data Solution+ = Solution+ { primalVector :: [Scientific] -- ^ The primal variable vector x+ , primalMatrix :: Matrix -- ^ The primal variable matrix X+ , dualMatrix :: Matrix -- ^ The dual variable matrix Y+ }+ deriving (Show, Ord, Eq)++evalPrimalObjective :: Problem -> Solution -> Scientific+evalPrimalObjective prob sol = sum $ zipWith (*) (costs prob) (primalVector sol)++evalDualObjective :: Problem -> Solution -> Scientific+evalDualObjective Problem{ matrices = [] } _ = error "evalDualObjective: invalid problem data"+evalDualObjective Problem{ matrices = f0:_ } sol =+ sum $ zipWith (\blk1 blk2 -> F.sum (Map.intersectionWith (*) blk1 blk2)) f0 (dualMatrix sol)++-- --------------------------------------------------------------------------- -- construction -- --------------------------------------------------------------------------- @@ -133,6 +171,31 @@ diagBlock xs = Map.fromList [((i,i),x) | (i,x) <- zip [1..] xs] -- ---------------------------------------------------------------------------+-- File I/O+-- ---------------------------------------------------------------------------++-- | Parse a SDPA format file (.dat) or a SDPA sparse format file (.dat-s)..+readDataFile :: FilePath -> IO Problem+readDataFile fname = do+ p <- case map toLower (takeExtension fname) of+ ".dat" -> return pDataFile+ ".dat-s" -> return pSparseDataFile+ ext -> ioError $ userError $ "unknown extension: " ++ ext+ s <- BL.readFile fname+ case parse (p <* eof) fname s of+ Left e -> throw (e :: ParseError)+ Right prob -> return prob++writeDataFile :: FilePath -> Problem -> IO ()+writeDataFile fname prob = do+ isSparse <- case map toLower (takeExtension fname) of+ ".dat" -> return False+ ".dat-s" -> return True+ ext -> ioError $ userError $ "unknown extension: " ++ ext+ withBinaryFile fname WriteMode $ \h -> do+ B.hPutBuilder h $ renderImpl isSparse prob++-- --------------------------------------------------------------------------- -- parsing -- --------------------------------------------------------------------------- @@ -140,22 +203,10 @@ parseData :: String -> BL.ByteString -> Either ParseError Problem parseData = parse (pDataFile <* eof) --- | Parse a SDPA format file (.dat).-parseDataFile :: FilePath -> IO (Either ParseError Problem)-parseDataFile fname = do- s <- BL.readFile fname- return $! parse (pDataFile <* eof) fname s- -- | Parse a SDPA sparse format (.dat-s) string. parseSparseData :: String -> BL.ByteString -> Either ParseError Problem parseSparseData = parse (pSparseDataFile <* eof) --- | Parse a SDPA sparse format file (.dat-s).-parseSparseDataFile :: FilePath -> IO (Either ParseError Problem)-parseSparseDataFile fname = do- s <- BL.readFile fname- return $! parse (pSparseDataFile <* eof) fname s- pDataFile :: C e s m => m Problem pDataFile = do _ <- many pComment@@ -293,11 +344,11 @@ -- rendering -- --------------------------------------------------------------------------- -render :: Problem -> Builder-render = renderImpl False+renderData :: Problem -> Builder+renderData = renderImpl False -renderSparse :: Problem -> Builder-renderSparse = renderImpl True+renderSparseData :: Problem -> Builder+renderSparseData = renderImpl True renderImpl :: Bool -> Problem -> Builder renderImpl sparse prob = mconcat
+ src/ToySolver/Text/WCNF.hs view
@@ -0,0 +1,48 @@+{-# OPTIONS_GHC -Wall #-}+-----------------------------------------------------------------------------+-- |+-- Module : ToySolver.Text.WCNF+-- Copyright : (c) Masahiro Sakai 2012+-- License : BSD-style+-- +-- Maintainer : masahiro.sakai@gmail.com+-- Stability : provisional+-- Portability : portable+--+-----------------------------------------------------------------------------+module ToySolver.Text.WCNF {-# DEPRECATED "Use ToySolver.FileFormat.CNF instead" #-}+ (+ WCNF (..)+ , WeightedClause+ , Weight++ -- * Parsing .cnf/.wcnf files+ , parseFile+ , parseByteString++ -- * Generating .wcnf files+ , writeFile+ , hPutWCNF+ , wcnfBuilder+ ) where++import Prelude hiding (writeFile)+import Data.ByteString.Builder+import qualified Data.ByteString.Lazy.Char8 as BL+import System.IO hiding (writeFile)+import ToySolver.FileFormat.CNF++-- | Parse a WCNF file but returns an error message when parsing fails.+{-# DEPRECATED parseByteString "Use FileFormat.parse instead" #-}+parseByteString :: BL.ByteString -> Either String WCNF+parseByteString = parse++-- | Encode a 'WCNF' to a 'Builder'+{-# DEPRECATED wcnfBuilder "Use FileFormat.render instead" #-}+wcnfBuilder :: WCNF -> Builder+wcnfBuilder = render++-- | Output a 'WCNF' to a Handle.+{-# DEPRECATED hPutWCNF "Use FileFormat.render instead" #-}+hPutWCNF :: Handle -> WCNF -> IO ()+hPutWCNF h wcnf = hPutBuilder h (wcnfBuilder wcnf)
src/ToySolver/Version.hs view
@@ -21,6 +21,9 @@ #ifdef VERSION_OptDir , ("OptDir", VERSION_OptDir) #endif+#ifdef VERSION_ansi_wl_pprint+ , ("ansi-wl-pprint", VERSION_ansi_wl_pprint)+#endif #ifdef VERSION_array , ("array", VERSION_array) #endif@@ -36,6 +39,12 @@ #ifdef VERSION_bytestring_builder , ("bytestring-builder", VERSION_bytestring_builder) #endif+#ifdef VERSION_bytestring_encoding+ , ("bytestring-encoding", VERSION_bytestring_encoding)+#endif+#ifdef VERSION_case_insensitive+ , ("case-insensitive", VERSION_case_insensitive)+#endif #ifdef VERSION_clock , ("clock", VERSION_clock) #endif@@ -108,11 +117,14 @@ #ifdef VERSION_mwc_random , ("mwc-random", VERSION_mwc_random) #endif+#ifdef VERSION_optparse_applicative+ , ("optparse-applicative", VERSION_optparse_applicative)+#endif #ifdef VERSION_parsec , ("parsec", VERSION_parsec) #endif-#ifdef VERSION_prettyclass- , ("prettyclass", VERSION_prettyclass)+#ifdef VERSION_pretty+ , ("pretty", VERSION_pretty) #endif #ifdef VERSION_primes , ("primes", VERSION_primes)@@ -176,6 +188,9 @@ #endif #ifdef VERSION_xml_conduit , ("xml-conduit", VERSION_xml_conduit)+#endif+#ifdef VERSION_zlib+ , ("zlib", VERSION_zlib) #endif ]
src/ToySolver/Version/TH.hs view
@@ -9,7 +9,7 @@ import Control.Monad import Data.Time import System.Process-import Language.Haskell.TH +import Language.Haskell.TH getGitHash :: IO (Maybe String) getGitHash =@@ -26,4 +26,4 @@ compilationTimeQ :: ExpQ compilationTimeQ = do tm <- runIO getCurrentTime- [| read $(litE (stringL (show tm))) :: UTCTime |] + [| read $(litE (stringL (show tm))) :: UTCTime |]
test/Test/Arith.hs view
@@ -142,8 +142,8 @@ y = LA.var 1 t1 = 11*^x ^+^ 13*^y t2 = 7*^x ^-^ 9*^y- + genLAExpr :: [Var] -> Gen (LA.Expr Rational) genLAExpr vs = do size <- choose (0,3)@@ -412,9 +412,11 @@ prop_Simplex_backtrack = QM.monadicIO $ do (vs,cs) <- QM.pick genQFLAConj (vs2,cs2) <- QM.pick genQFLAConj+ config <- QM.pick arbitrary join $ QM.run $ do solver <- Simplex.newSolver+ Simplex.setConfig solver config m <- liftM IM.fromList $ forM (IS.toList (vs `IS.union` vs2)) $ \v -> do v2 <- Simplex.newVar solver return (v, LA.var v2)@@ -435,9 +437,11 @@ prop_Simplex_explain :: Property prop_Simplex_explain = QM.monadicIO $ do (vs,cs) <- QM.pick genQFLAConj+ config <- QM.pick arbitrary let f p = QM.run $ do solver <- Simplex.newSolver+ Simplex.setConfig solver config m <- liftM IM.fromList $ forM (IS.toList vs) $ \v -> do v2 <- Simplex.newVar solver return (v, LA.var v2)@@ -459,6 +463,19 @@ forM_ (IS.toList e) $ \i -> do ret3 <- f (`IS.member` (IS.delete i e)) QM.assert (isNothing ret3)++instance Arbitrary Simplex.Config where+ arbitrary = do+ ps <- arbitrary+ enableBoundTightening <- arbitrary+ return $+ Simplex.Config+ { Simplex.configPivotStrategy = ps+ , Simplex.configEnableBoundTightening = enableBoundTightening+ }++instance Arbitrary Simplex.PivotStrategy where+ arbitrary = arbitraryBoundedEnum ------------------------------------------------------------------------
test/Test/BitVector.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wall #-}@@ -9,10 +10,11 @@ import Test.Tasty.TH import qualified Test.QuickCheck.Monadic as QM -import Control.Applicative import Control.Monad-import Data.Monoid import Data.Maybe+#if !MIN_VERSION_base(4,11,0)+import Data.Monoid+#endif import ToySolver.Data.OrdRel import qualified ToySolver.BitVector as BV
test/Test/BoolExpr.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} module Test.BoolExpr (boolExprTestGroup) where -import Control.Applicative import Test.QuickCheck.Function import Test.Tasty import Test.Tasty.QuickCheck hiding ((.&&.), (.||.))
+ test/Test/CNF.hs view
@@ -0,0 +1,72 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+module Test.CNF (cnfTestGroup) where++import Control.Monad+import Data.ByteString.Builder+import Test.Tasty+import Test.Tasty.QuickCheck+import Test.Tasty.TH+import qualified ToySolver.FileFormat.CNF as CNF++import Test.SAT.Utils++------------------------------------------------------------------------++prop_CNF_ReadWrite_Invariance :: Property+prop_CNF_ReadWrite_Invariance = forAll arbitraryCNF $ \cnf ->+ CNF.parse (toLazyByteString (CNF.render cnf)) == Right cnf++prop_GCNF_ReadWrite_Invariance :: Property+prop_GCNF_ReadWrite_Invariance = forAll arbitraryGCNF $ \gcnf ->+ CNF.parse (toLazyByteString (CNF.render gcnf)) == Right gcnf++prop_WCNF_ReadWrite_Invariance :: Property+prop_WCNF_ReadWrite_Invariance = forAll arbitraryWCNF $ \wcnf ->+ CNF.parse (toLazyByteString (CNF.render wcnf)) == Right wcnf++prop_QDimacs_ReadWrite_Invariance :: Property+prop_QDimacs_ReadWrite_Invariance = forAll arbitraryQDimacs $ \qdimacs ->+ CNF.parse (toLazyByteString (CNF.render qdimacs)) == Right qdimacs++prop_GCNF_from_CNF :: Property+prop_GCNF_from_CNF = forAll arbitraryCNF $ \cnf ->+ case CNF.parse (toLazyByteString (CNF.render cnf)) of+ Left _ -> False+ Right gcnf -> and+ [ CNF.gcnfNumVars gcnf == CNF.cnfNumVars cnf+ , CNF.gcnfNumClauses gcnf == CNF.cnfNumClauses cnf+ , CNF.gcnfLastGroupIndex gcnf == CNF.cnfNumClauses cnf+ , CNF.gcnfClauses gcnf == zip [1..] (CNF.cnfClauses cnf)+ ]++prop_WCNF_from_CNF :: Property+prop_WCNF_from_CNF = forAll arbitraryCNF $ \cnf ->+ case CNF.parse (toLazyByteString (CNF.render cnf)) of+ Left _ -> False+ Right wcnf -> and+ [ CNF.wcnfNumVars wcnf == CNF.cnfNumVars cnf+ , CNF.wcnfNumClauses wcnf == CNF.cnfNumClauses cnf+ , CNF.wcnfTopCost wcnf > fromIntegral (CNF.cnfNumClauses cnf)+ , CNF.wcnfClauses wcnf == map (\c -> (1,c)) (CNF.cnfClauses cnf)+ ]++prop_QDimacs_from_CNF :: Property+prop_QDimacs_from_CNF = forAll arbitraryCNF $ \cnf ->+ case CNF.parse (toLazyByteString (CNF.render cnf)) of+ Left _ -> False+ Right qdimacs -> and+ [ CNF.qdimacsNumVars qdimacs == CNF.cnfNumVars cnf+ , CNF.qdimacsNumClauses qdimacs == CNF.cnfNumClauses cnf+ , CNF.qdimacsPrefix qdimacs == []+ , CNF.qdimacsMatrix qdimacs == CNF.cnfClauses cnf+ ]++------------------------------------------------------------------------+-- Test harness++cnfTestGroup :: TestTree+cnfTestGroup = $(testGroupGenerator)
+ test/Test/Converter.hs view
@@ -0,0 +1,349 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.Converter (converterTestGroup) where++import Control.Monad+import Data.Array.IArray+import qualified Data.Foldable as F+import Data.Maybe+import Data.Set (Set)+import qualified Data.Set as Set+import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet+import qualified Data.Vector.Generic as VG++import Test.Tasty+import Test.Tasty.QuickCheck hiding ((.&&.), (.||.))+import Test.Tasty.TH+import qualified Test.QuickCheck as QC+import qualified Test.QuickCheck.Monadic as QM++import ToySolver.Converter+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.MaxCut as MaxCut+import qualified ToySolver.SAT as SAT+import qualified ToySolver.SAT.Types as SAT++import qualified Data.PseudoBoolean as PBFile++import Test.SAT.Utils+++prop_sat2naesat_forward :: Property+prop_sat2naesat_forward = forAll arbitraryCNF $ \cnf ->+ let ret@(nae,info) = sat2naesat cnf+ in counterexample (show ret) $ + forAllAssignments (CNF.cnfNumVars cnf) $ \m ->+ evalCNF m cnf === evalNAESAT (transformForward info m) nae++prop_sat2naesat_backward :: Property+prop_sat2naesat_backward = forAll arbitraryCNF $ \cnf ->+ let ret@(nae,info) = sat2naesat cnf+ in counterexample (show ret) $ + forAllAssignments (fst nae) $ \m ->+ evalCNF (transformBackward info m) cnf === evalNAESAT m nae++prop_naesat2sat_forward :: Property+prop_naesat2sat_forward = forAll arbitraryNAESAT $ \nae ->+ let ret@(cnf,info) = naesat2sat nae+ in counterexample (show ret) $ + forAllAssignments (fst nae) $ \m ->+ evalNAESAT m nae === evalCNF (transformForward info m) cnf++prop_naesat2sat_backward :: Property+prop_naesat2sat_backward = forAll arbitraryNAESAT $ \nae ->+ let ret@(cnf,info) = naesat2sat nae+ in counterexample (show ret) $+ forAllAssignments (CNF.cnfNumVars cnf) $ \m ->+ evalNAESAT (transformBackward info m) nae === evalCNF m cnf++prop_naesat2naeksat_forward :: Property+prop_naesat2naeksat_forward =+ forAll arbitraryNAESAT $ \nae ->+ forAll (choose (3,10)) $ \k ->+ let ret@(nae',info) = naesat2naeksat k nae+ in counterexample (show ret) $+ property (all (\c -> VG.length c <= k) (snd nae'))+ QC..&&.+ (forAllAssignments (fst nae) $ \m ->+ evalNAESAT m nae === evalNAESAT (transformForward info m) nae')++prop_naesat2naeksat_backward :: Property+prop_naesat2naeksat_backward =+ forAll arbitraryNAESAT $ \nae ->+ forAll (choose (3,10)) $ \k ->+ let ret@(nae',info) = naesat2naeksat k nae+ in counterexample (show ret) $+ forAll (arbitraryAssignment (fst nae')) $ \m ->+ evalNAESAT (transformBackward info m) nae || not (evalNAESAT m nae')++prop_naesat2maxcut_forward :: Property+prop_naesat2maxcut_forward =+ forAll arbitraryNAESAT $ \nae ->+ let ret@((maxcut, threshold), info) = naesat2maxcut nae+ in counterexample (show ret) $+ forAllAssignments (fst nae) $ \m ->+ evalNAESAT m nae === (MaxCut.eval (transformForward info m) maxcut >= threshold)++prop_naesat2max2sat_forward :: Property+prop_naesat2max2sat_forward =+ forAll arbitraryNAESAT $ \nae ->+ let ret@((wcnf, threshold), info) = naesat2max2sat nae+ in counterexample (show ret) $+ forAllAssignments (fst nae) $ \m ->+ case evalWCNF (transformForward info m) wcnf of+ Nothing -> property False+ Just v -> evalNAESAT m nae === (v <= threshold)++------------------------------------------------------------------------++prop_satToMaxSAT2_forward :: Property+prop_satToMaxSAT2_forward =+ forAll arbitraryCNF $ \cnf ->+ let ((wcnf, threshold), info) = satToMaxSAT2 cnf+ in and+ [ evalCNF m cnf == b2+ | m <- allAssignments (CNF.cnfNumVars cnf)+ , let m2 = transformForward info m+ b2 = case evalWCNF m2 wcnf of+ Nothing -> False+ Just v -> v <= threshold+ ]++prop_simplifyMaxSAT2_forward :: Property+prop_simplifyMaxSAT2_forward =+ forAll arbitraryMaxSAT2 $ \(wcnf, th1) ->+ let r@((_n,cs,th2), info) = simplifyMaxSAT2 (wcnf, th1)+ in counterexample (show r) $ and+ [ b1 == b2+ | m1 <- allAssignments (CNF.wcnfNumVars wcnf)+ , let o1 = fromJust $ evalWCNF m1 wcnf+ b1 = o1 <= th1+ m2 = transformForward info m1+ o2 = fromIntegral $ length [()| (l1,l2) <- Set.toList cs, not (SAT.evalLit m2 l1), not (SAT.evalLit m2 l2)]+ b2 = o2 <= th2+ ]++prop_maxSAT2ToSimpleMaxCut_forward :: Property+prop_maxSAT2ToSimpleMaxCut_forward =+ forAll arbitraryMaxSAT2 $ \(wcnf, th1) ->+ let r@((maxcut, th2), info) = maxSAT2ToSimpleMaxCut (wcnf, th1)+ in counterexample (show r) $ and+ [ b1 == b2+ | m <- allAssignments (CNF.wcnfNumVars wcnf)+ , let o1 = fromJust $ evalWCNF m wcnf+ b1 = o1 <= th1+ sol2 = transformForward info m+ o2 = MaxCut.eval sol2 maxcut+ b2 = o2 >= th2+ ]++------------------------------------------------------------------------++prop_pb2sat :: Property+prop_pb2sat = QM.monadicIO $ do+ pb@(nv,cs) <- QM.pick arbitraryPB+ let f (PBRelGE,lhs,rhs) = ([(c,[l]) | (c,l) <- lhs], PBFile.Ge, rhs)+ f (PBRelLE,lhs,rhs) = ([(-c,[l]) | (c,l) <- lhs], PBFile.Ge, -rhs)+ f (PBRelEQ,lhs,rhs) = ([(c,[l]) | (c,l) <- lhs], PBFile.Eq, rhs)+ let opb = PBFile.Formula+ { PBFile.pbObjectiveFunction = Nothing+ , PBFile.pbNumVars = nv+ , PBFile.pbNumConstraints = length cs+ , PBFile.pbConstraints = map f cs+ }+ let (cnf, info) = pb2sat opb++ solver1 <- arbitrarySolver+ solver2 <- arbitrarySolver+ ret1 <- QM.run $ solvePB solver1 pb+ ret2 <- QM.run $ solveCNF solver2 cnf+ QM.assert $ isJust ret1 == isJust ret2+ case ret1 of+ Nothing -> return ()+ Just m1 -> do+ let m2 = transformForward info m1+ QM.assert $ bounds m2 == (1, CNF.cnfNumVars cnf)+ QM.assert $ evalCNF m2 cnf+ case ret2 of+ Nothing -> return ()+ Just m2 -> do+ let m1 = transformBackward info m2+ QM.assert $ bounds m1 == (1, nv)+ QM.assert $ evalPB m1 pb++prop_wbo2maxsat :: Property+prop_wbo2maxsat = QM.monadicIO $ do+ wbo1@(nv,cs,top) <- QM.pick arbitraryWBO+ let f (w,(PBRelGE,lhs,rhs)) = (w,([(c,[l]) | (c,l) <- lhs], PBFile.Ge, rhs))+ f (w,(PBRelLE,lhs,rhs)) = (w,([(-c,[l]) | (c,l) <- lhs], PBFile.Ge, -rhs))+ f (w,(PBRelEQ,lhs,rhs)) = (w,([(c,[l]) | (c,l) <- lhs], PBFile.Eq, rhs))+ let wbo1' = PBFile.SoftFormula+ { PBFile.wboNumVars = nv+ , PBFile.wboNumConstraints = length cs+ , PBFile.wboConstraints = map f cs+ , PBFile.wboTopCost = top+ }+ let (wcnf, info) = wbo2maxsat wbo1'+ wbo2 = ( CNF.wcnfNumVars wcnf+ , [ ( if w == CNF.wcnfTopCost wcnf then Nothing else Just w+ , (PBRelGE, [(1,l) | l <- SAT.unpackClause clause], 1)+ )+ | (w,clause) <- CNF.wcnfClauses wcnf+ ]+ , Nothing+ )++ solver1 <- arbitrarySolver+ solver2 <- arbitrarySolver+ method <- QM.pick arbitrary+ ret1 <- QM.run $ optimizeWBO solver1 method wbo1+ ret2 <- QM.run $ optimizeWBO solver2 method wbo2+ QM.assert $ isJust ret1 == isJust ret2+ case ret1 of+ Nothing -> return ()+ Just (m1,val) -> do+ let m2 = transformForward info m1+ QM.assert $ bounds m2 == (1, CNF.wcnfNumVars wcnf)+ QM.assert $ evalWBO m2 wbo2 == Just val+ case ret2 of+ Nothing -> return ()+ Just (m2,val) -> do+ let m1 = transformBackward info m2+ QM.assert $ bounds m1 == (1, nv)+ QM.assert $ evalWBO m1 wbo1 == Just val++prop_wbo2pb :: Property+prop_wbo2pb = QM.monadicIO $ do+ wbo@(nv,cs,top) <- QM.pick arbitraryWBO+ let f (w,(PBRelGE,lhs,rhs)) = (w,([(c,[l]) | (c,l) <- lhs], PBFile.Ge, rhs))+ f (w,(PBRelLE,lhs,rhs)) = (w,([(-c,[l]) | (c,l) <- lhs], PBFile.Ge, -rhs))+ f (w,(PBRelEQ,lhs,rhs)) = (w,([(c,[l]) | (c,l) <- lhs], PBFile.Eq, rhs))+ let wbo' = PBFile.SoftFormula+ { PBFile.wboNumVars = nv+ , PBFile.wboNumConstraints = length cs+ , PBFile.wboConstraints = map f cs+ , PBFile.wboTopCost = top+ }+ let (opb, info) = wbo2pb wbo'+ obj = fromMaybe [] $ PBFile.pbObjectiveFunction opb+ f (lhs, PBFile.Ge, rhs) = (PBRelGE, lhs, rhs)+ f (lhs, PBFile.Eq, rhs) = (PBRelEQ, lhs, rhs)+ cs2 = map f (PBFile.pbConstraints opb)+ pb = (PBFile.pbNumVars opb, obj, cs2)++ solver1 <- arbitrarySolver+ solver2 <- arbitrarySolver+ method <- QM.pick arbitrary+ ret1 <- QM.run $ optimizeWBO solver1 method wbo+ ret2 <- QM.run $ optimizePBNLC solver2 method pb+ QM.assert $ isJust ret1 == isJust ret2+ case ret1 of+ Nothing -> return ()+ Just (m1,val1) -> do+ let m2 = transformForward info m1+ QM.assert $ bounds m2 == (1, PBFile.pbNumVars opb)+ QM.assert $ evalPBNLC m2 (PBFile.pbNumVars opb, cs2)+ QM.assert $ SAT.evalPBSum m2 obj == val1+ case ret2 of+ Nothing -> return ()+ Just (m2,val2) -> do+ let m1 = transformBackward info m2+ QM.assert $ bounds m1 == (1,nv)+ QM.assert $ evalWBO m1 wbo == Just val2++prop_sat2ksat :: Property+prop_sat2ksat = QM.monadicIO $ do+ k <- QM.pick $ choose (3,10)++ cnf1 <- QM.pick arbitraryCNF+ let (cnf2, info) = sat2ksat k cnf1++ solver1 <- arbitrarySolver+ solver2 <- arbitrarySolver+ ret1 <- QM.run $ solveCNF solver1 cnf1+ ret2 <- QM.run $ solveCNF solver2 cnf2+ QM.assert $ isJust ret1 == isJust ret2+ case ret1 of+ Nothing -> return ()+ Just m1 -> do+ let m2 = transformForward info m1+ QM.assert $ bounds m2 == (1, CNF.cnfNumVars cnf2)+ QM.assert $ evalCNF m2 cnf2+ case ret2 of+ Nothing -> return ()+ Just m2 -> do+ let m1 = transformBackward info m2+ QM.assert $ bounds m1 == (1, CNF.cnfNumVars cnf1)+ QM.assert $ evalCNF m1 cnf1++prop_quadratizePB :: Property+prop_quadratizePB =+ forAll arbitraryPBFormula $ \pb ->+ let ((pb2,th), info) = quadratizePB pb+ in counterexample (show (pb2,th)) $+ conjoin+ [ property $ F.all (\t -> IntSet.size t <= 2) $ collectTerms pb2+ , property $ PBFile.pbNumConstraints pb === PBFile.pbNumConstraints pb2+ , forAll (arbitraryAssignment (PBFile.pbNumVars pb)) $ \m ->+ SAT.evalPBFormula m pb === eval2 (transformForward info m) (pb2,th)+ , forAll (arbitraryAssignment (PBFile.pbNumVars pb2)) $ \m ->+ case eval2 m (pb2,th) of+ Just o -> SAT.evalPBFormula (transformBackward info m) pb === Just o+ Nothing -> property True+ ]+ where + collectTerms :: PBFile.Formula -> Set IntSet+ collectTerms formula = Set.fromList [t' | t <- terms, let t' = IntSet.fromList t, IntSet.size t' >= 3]+ where+ sums = maybeToList (PBFile.pbObjectiveFunction formula) +++ [lhs | (lhs,_,_) <- PBFile.pbConstraints formula]+ terms = [t | s <- sums, (_,t) <- s]++ eval2 :: SAT.IModel m => m -> (PBFile.Formula, Integer) -> Maybe Integer+ eval2 m (pb,th) = do+ o <- SAT.evalPBFormula m pb+ guard $ o <= th+ return o++prop_inequalitiesToEqualitiesPB :: Property+prop_inequalitiesToEqualitiesPB = QM.monadicIO $ do+ pb@(nv,cs) <- QM.pick arbitraryPBNLC+ let f (PBRelGE,lhs,rhs) = ([(c,ls) | (c,ls) <- lhs], PBFile.Ge, rhs)+ f (PBRelLE,lhs,rhs) = ([(-c,ls) | (c,ls) <- lhs], PBFile.Ge, -rhs)+ f (PBRelEQ,lhs,rhs) = ([(c,ls) | (c,ls) <- lhs], PBFile.Eq, rhs)+ let opb = PBFile.Formula+ { PBFile.pbObjectiveFunction = Nothing+ , PBFile.pbNumVars = nv+ , PBFile.pbNumConstraints = length cs+ , PBFile.pbConstraints = map f cs+ }+ QM.monitor $ counterexample (show opb)+ let (opb2, info) = inequalitiesToEqualitiesPB opb+ pb2 = (PBFile.pbNumVars opb2, [(g op, lhs, rhs) | (lhs,op,rhs) <- PBFile.pbConstraints opb2])+ g PBFile.Ge = PBRelGE+ g PBFile.Eq = PBRelEQ+ QM.monitor $ counterexample (show opb2)++ solver1 <- arbitrarySolver+ solver2 <- arbitrarySolver+ ret1 <- QM.run $ solvePBNLC solver1 pb+ ret2 <- QM.run $ solvePBNLC solver2 pb2+ QM.assert $ isJust ret1 == isJust ret2+ case ret1 of+ Nothing -> return ()+ Just m1 -> do+ let m2 = transformForward info m1+ QM.assert $ bounds m2 == (1, PBFile.pbNumVars opb2)+ QM.assert $ evalPBNLC m2 pb2+ case ret2 of+ Nothing -> return ()+ Just m2 -> do+ let m1 = transformBackward info m2+ QM.assert $ bounds m1 == (1, nv)+ QM.assert $ evalPBNLC m1 pb+++converterTestGroup :: TestTree+converterTestGroup = $(testGroupGenerator)
test/Test/FiniteModelFinder.hs view
@@ -44,7 +44,7 @@ genLit' :: Sig -> [MF.Var] -> StateT Int Gen MF.Lit genLit' sig vs = do atom <- genAtom' sig vs- lift $ elements [MF.Pos atom, MF.Neg atom] + lift $ elements [MF.Pos atom, MF.Neg atom] genClause' :: Sig -> [MF.Var] -> StateT Int Gen MF.Clause genClause' sig vs = do
test/Test/LPFile.hs view
@@ -17,7 +17,7 @@ case_test_qcp2 = checkFile "samples/lp/test-qcp2.lp" case_test_qp = checkFile "samples/lp/test-qp.lp" case_empty_obj_1 = checkFile "samples/lp/empty_obj_1.lp"-case_empty_obj_2 = checkFile "samples/lp/empty_obj_2.lp" +case_empty_obj_2 = checkFile "samples/lp/empty_obj_2.lp" ------------------------------------------------------------------------ -- Sample data
test/Test/MIP.hs view
@@ -1,4 +1,5 @@ {-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -12,6 +13,7 @@ import Test.Tasty.HUnit import Test.Tasty.QuickCheck import Test.Tasty.TH+import ToySolver.Data.MIP (meetStatus) import qualified ToySolver.Data.MIP as MIP import qualified ToySolver.Data.MIP.Solution.CBC as CBCSol import qualified ToySolver.Data.MIP.Solution.CPLEX as CPLEXSol@@ -36,22 +38,26 @@ prop_status_meet_idempotency :: Property prop_status_meet_idempotency = forAll arbitrary $ \(x :: MIP.Status) ->- x `meet` x == x+ x `meetStatus` x == x prop_status_meet_comm :: Property prop_status_meet_comm = forAll arbitrary $ \(x :: MIP.Status) y ->- x `meet` y == y `meet` x+ x `meetStatus` y == y `meetStatus` x prop_status_meet_assoc :: Property prop_status_meet_assoc = forAll arbitrary $ \(x :: MIP.Status) y z ->- (x `meet` y) `meet` z == x `meet` (y `meet` z)+ (x `meetStatus` y) `meetStatus` z == x `meetStatus` (y `meetStatus` z) prop_status_meet_leq :: Property prop_status_meet_leq = forAll arbitrary $ \(x :: MIP.Status) y ->+#if MIN_VERSION_lattices(2,0,0)+ (x == (x `meetStatus` y)) == x `leq` y+#else x `meetLeq` y == x `leq` y+#endif instance Arbitrary MIP.Status where arbitrary = arbitraryBoundedEnum
+ test/Test/ProbSAT.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+module Test.ProbSAT (probSATTestGroup) where++import Control.Applicative+import Control.Monad+import Data.Array.IArray+import Data.Default.Class+import Data.Maybe+import Test.Tasty+import Test.Tasty.QuickCheck+import Test.Tasty.HUnit+import Test.Tasty.TH+import qualified Test.QuickCheck.Monadic as QM+import Test.QuickCheck.Modifiers+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.SAT.Types as SAT+import qualified ToySolver.SAT.SLS.ProbSAT as ProbSAT++import Test.SAT.Utils+++prop_probSAT :: Property+prop_probSAT = QM.monadicIO $ do+ cnf <- QM.pick arbitraryCNF+ opt <- QM.pick $ do+ target <- choose (0, 10)+ maxTries <- choose (0, 10)+ maxFlips <- choose (0, 1000)+ return $+ def+ { ProbSAT.optTarget = target+ , ProbSAT.optMaxTries = maxTries+ , ProbSAT.optMaxFlips = maxFlips+ }+ (obj,sol) <- QM.run $ do+ solver <- ProbSAT.newSolver cnf+ let cb = 3.6+ cm = 0.5+ f make break = cm**make / cb**break+ ProbSAT.probsat solver opt def f+ ProbSAT.getBestSolution solver+ QM.monitor (counterexample (show (obj,sol)))+ QM.assert (bounds sol == (1, CNF.cnfNumVars cnf))+ QM.assert (obj == fromIntegral (evalCNFCost sol cnf))++prop_probSAT_weighted :: Property+prop_probSAT_weighted = QM.monadicIO $ do+ wcnf <- QM.pick arbitraryWCNF+ opt <- QM.pick $ do+ target <- choose (0, 10)+ maxTries <- choose (0, 10)+ maxFlips <- choose (0, 1000)+ return $+ def+ { ProbSAT.optTarget = target+ , ProbSAT.optMaxTries = maxTries+ , ProbSAT.optMaxFlips = maxFlips+ }+ (obj,sol) <- QM.run $ do+ solver <- ProbSAT.newSolverWeighted wcnf+ let cb = 3.6+ cm = 0.5+ f make break = cm**make / cb**break+ ProbSAT.probsat solver opt def f+ ProbSAT.getBestSolution solver+ QM.monitor (counterexample (show (obj,sol)))+ QM.assert (bounds sol == (1, CNF.wcnfNumVars wcnf))+ QM.assert (obj == evalWCNFCost sol wcnf)++case_probSAT_case1 :: Assertion+case_probSAT_case1 = do+ let cnf =+ CNF.CNF+ { CNF.cnfNumVars = 1+ , CNF.cnfNumClauses = 2+ , CNF.cnfClauses = map SAT.packClause+ [ [1,-1]+ , []+ ]+ }+ solver <- ProbSAT.newSolver cnf+ let opt =+ def+ { ProbSAT.optTarget = 0+ , ProbSAT.optMaxTries = 1+ , ProbSAT.optMaxFlips = 10+ }+ cb = 3.6+ cm = 0.5+ f make break = cm**make / cb**break+ ProbSAT.probsat solver opt def f++------------------------------------------------------------------------+-- Test harness++probSATTestGroup :: TestTree+probSATTestGroup = $(testGroupGenerator)
test/Test/QBF.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} module Test.QBF (qbfTestGroup) where -import Control.Applicative((<$>)) import Control.Monad import qualified Data.IntSet as IntSet import Data.IntMap (IntMap)@@ -40,6 +39,15 @@ Just ls -> QM.assert $ sat == evalQBFNaive' (IntMap.fromList [(abs lit, lit > 0) | lit <- IntSet.toList ls]) prefix' matrix +prop_solveQE :: Property+prop_solveQE = QM.monadicIO $ do+ (nv, prefix@(_ : prefix'), matrix) <- QM.pick arbitrarySmallQBF+ (sat, cert) <- QM.run $ QBF.solveQE nv prefix matrix+ QM.assert $ sat == evalQBFNaive prefix matrix+ case cert of+ Nothing -> return ()+ Just ls ->+ QM.assert $ sat == evalQBFNaive' (IntMap.fromList [(abs lit, lit > 0) | lit <- IntSet.toList ls]) prefix' matrix {- If the innermost quantifier is a universal quantifier,
+ test/Test/QUBO.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.QUBO (quboTestGroup) where++import Control.Monad+import Data.Array.IArray+import Data.ByteString.Builder+import qualified Data.IntMap.Strict as IntMap+import Data.Maybe+import qualified Data.PseudoBoolean as PBFile+import Data.Scientific++import Test.Tasty+import Test.Tasty.QuickCheck+import Test.Tasty.HUnit+import Test.Tasty.TH+import qualified Test.QuickCheck.Monadic as QM++import ToySolver.Converter+import qualified ToySolver.FileFormat as FF+import qualified ToySolver.QUBO as QUBO+import ToySolver.Converter.QUBO+import qualified ToySolver.SAT.Types as SAT++import Test.SAT.Utils++------------------------------------------------------------------------++instance (Arbitrary a, Eq a, Num a) => Arbitrary (QUBO.Problem a) where+ arbitrary = do+ nv <- choose (1,10)+ m <- choose (0,nv*nv)+ jj <- liftM (f . IntMap.unionsWith (IntMap.unionWith (+))) $ replicateM m $ do+ i <- choose (0,nv-1)+ j <- choose (i,nv-1)+ jj_ij <- arbitrary+ return $ IntMap.singleton i $ IntMap.singleton j jj_ij+ return $+ QUBO.Problem+ { QUBO.quboNumVars = nv+ , QUBO.quboMatrix = jj+ }+ where+ f = IntMap.mapMaybe (g . IntMap.filter (/= 0))+ g m = if IntMap.null m then Nothing else Just m++arbitrarySolution :: Int -> Gen QUBO.Solution+arbitrarySolution nv =+ liftM (array (0,nv-1) . zip [0..]) $ replicateM nv arbitrary++instance (Arbitrary a, Eq a, Num a) => Arbitrary (QUBO.IsingModel a) where+ arbitrary = do+ nv <- choose (1,10)++ m <- choose (0,nv*nv)+ qq <- liftM (f . IntMap.unionsWith (IntMap.unionWith (+))) $ replicateM m $ do+ i <- choose (0,nv-1)+ j <- choose (i,nv-1)+ qq_ij <- arbitrary+ return $ IntMap.singleton i $ IntMap.singleton j qq_ij++ h <- liftM (\h -> IntMap.fromList [(i,hi)| (i, Just hi) <- zip [0..] h]) $ replicateM nv arbitrary++ return $+ QUBO.IsingModel+ { QUBO.isingNumVars = nv+ , QUBO.isingInteraction = qq+ , QUBO.isingExternalMagneticField = h+ }+ where+ f = IntMap.mapMaybe (g . IntMap.filter (/= 0))+ g m = if IntMap.null m then Nothing else Just m++------------------------------------------------------------------------++prop_QUBO_ReadWrite_Invariance :: Property+prop_QUBO_ReadWrite_Invariance = forAll g $ \qubo ->+ let s = toLazyByteString (FF.render qubo)+ in counterexample (show s) $ FF.parse s === Right qubo+ where+ g = do+ qubo <- arbitrary+ return $ fmap fromFloatDigits (qubo :: QUBO.Problem Double)++------------------------------------------------------------------------++prop_qubo2pb :: Property+prop_qubo2pb = forAll arbitrary $ \(qubo :: QUBO.Problem Integer) ->+ let (pbo,_) = qubo2pb qubo+ in Just qubo === fmap fst (pbAsQUBO pbo)++prop_pb2qubo :: Property+prop_pb2qubo = forAll arbitraryPBFormula $ \formula ->+ let ((qubo :: QUBO.Problem Integer, th), info) = pb2qubo formula+ in counterexample (show (qubo,th,info)) $+ conjoin+ [ forAll (arbitraryAssignment (PBFile.pbNumVars formula)) $ \m ->+ case SAT.evalPBFormula m formula of+ Nothing ->+ property (QUBO.eval (transformForward info m) qubo > th)+ Just o ->+ conjoin+ [ QUBO.eval (transformForward info m) qubo === transformObjValueForward info o+ , transformObjValueBackward info (transformObjValueForward info o) === o+ , property (transformObjValueForward info o <= th)+ ]+ , forAll (arbitrarySolution (QUBO.quboNumVars qubo)) $ \sol ->+ let o = QUBO.eval sol qubo+ in if (o <= th) then+ (SAT.evalPBFormula (transformBackward info sol) formula === Just (transformObjValueBackward info o))+ .&&.+ transformObjValueForward info (transformObjValueBackward info o) === o+ else+ property True+ ]++prop_qubo2ising :: Property+prop_qubo2ising = forAll arbitrary $ \(qubo :: QUBO.Problem Rational) ->+ let (ising, info) = qubo2ising qubo+ in counterexample (show ising) $+ forAll (arbitrarySolution (QUBO.quboNumVars qubo)) $ \sol ->+ transformObjValueForward info (QUBO.eval sol qubo) === QUBO.evalIsingModel sol ising++prop_ising2qubo :: Property+prop_ising2qubo = forAll arbitrary $ \(ising :: QUBO.IsingModel Integer) ->+ let (qubo, info) = ising2qubo ising+ in counterexample (show qubo) $+ forAll (arbitrarySolution (QUBO.isingNumVars ising)) $ \sol ->+ transformObjValueForward info (QUBO.evalIsingModel sol ising) === QUBO.eval sol qubo++------------------------------------------------------------------------+-- Test harness++quboTestGroup :: TestTree+quboTestGroup = $(testGroupGenerator)
test/Test/SAT.hs view
@@ -1,2039 +1,594 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}-module Test.SAT (satTestGroup) where--import Control.Monad-import Data.Array.IArray-import Data.Default.Class-import qualified Data.Foldable as F-import Data.IORef-import Data.List-import Data.Map (Map)-import qualified Data.Map as Map-import Data.Maybe-import Data.Set (Set)-import qualified Data.Set as Set-import Data.IntMap (IntMap)-import qualified Data.IntMap as IntMap-import Data.IntSet (IntSet)-import qualified Data.IntSet as IntSet-import qualified Data.Traversable as Traversable-import qualified Data.Vector as V-import qualified System.Random.MWC as Rand--import Test.Tasty-import Test.Tasty.QuickCheck hiding ((.&&.), (.||.))-import Test.Tasty.HUnit-import Test.Tasty.TH-import qualified Test.QuickCheck.Monadic as QM--import ToySolver.Data.LBool-import ToySolver.Data.BoolExpr-import ToySolver.Data.Boolean-import qualified ToySolver.SAT as SAT-import qualified ToySolver.SAT.Types as SAT-import ToySolver.SAT.TheorySolver-import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin-import qualified ToySolver.SAT.Encoder.PB as PB-import qualified ToySolver.SAT.Encoder.PB.Internal.Sorter as PBEncSorter-import qualified ToySolver.SAT.Encoder.PBNLC as PBNLC-import qualified ToySolver.SAT.MUS as MUS-import qualified ToySolver.SAT.MUS.Enum as MUSEnum-import qualified ToySolver.SAT.PBO as PBO-import qualified ToySolver.SAT.Store.CNF as CNFStore-import qualified ToySolver.SAT.ExistentialQuantification as ExistentialQuantification--import qualified Data.PseudoBoolean as PBFile-import qualified ToySolver.Converter.PB2SAT as PB2SAT-import qualified ToySolver.Converter.WBO2MaxSAT as WBO2MaxSAT-import qualified ToySolver.Converter.WBO2PB as WBO2PB-import qualified ToySolver.Converter.SAT2KSAT as SAT2KSAT-import qualified ToySolver.Text.CNF as CNF-import qualified ToySolver.Text.MaxSAT as MaxSAT--import ToySolver.Data.OrdRel-import qualified ToySolver.Data.LA as LA-import qualified ToySolver.Arith.Simplex as Simplex-import qualified ToySolver.EUF.EUFSolver as EUF--allAssignments :: Int -> [SAT.Model]-allAssignments nv = [array (1,nv) (zip [1..nv] xs) | xs <- replicateM nv [True,False]]--prop_solveCNF :: Property-prop_solveCNF = QM.monadicIO $ do- cnf <- QM.pick arbitraryCNF- solver <- arbitrarySolver- ret <- QM.run $ solveCNF solver cnf- case ret of- Just m -> QM.assert $ evalCNF m cnf- Nothing -> do- forM_ (allAssignments (CNF.numVars cnf)) $ \m -> do- QM.assert $ not (evalCNF m cnf)--solveCNF :: SAT.Solver -> CNF.CNF -> IO (Maybe SAT.Model)-solveCNF solver cnf = do- SAT.newVars_ solver (CNF.numVars cnf)- forM_ (CNF.clauses cnf) $ \c -> SAT.addClause solver c- ret <- SAT.solve solver- if ret then do- m <- SAT.getModel solver- return (Just m)- else do- return Nothing--arbitraryCNF :: Gen CNF.CNF-arbitraryCNF = do- nv <- choose (0,10)- nc <- choose (0,50)- cs <- replicateM nc $ do- len <- choose (0,10)- if nv == 0 then- return []- else- replicateM len $ choose (-nv, nv) `suchThat` (/= 0)- return $- CNF.CNF- { CNF.numVars = nv- , CNF.numClauses = nc- , CNF.clauses = cs- }--evalCNF :: SAT.Model -> CNF.CNF -> Bool-evalCNF m cnf = all (SAT.evalClause m) (CNF.clauses cnf)---prop_solvePB :: Property-prop_solvePB = QM.monadicIO $ do- prob@(nv,_) <- QM.pick arbitraryPB- solver <- arbitrarySolver- ret <- QM.run $ solvePB solver prob- case ret of- Just m -> QM.assert $ evalPB m prob- Nothing -> do- forM_ (allAssignments nv) $ \m -> do- QM.assert $ not (evalPB m prob)--data PBRel = PBRelGE | PBRelEQ | PBRelLE deriving (Eq, Ord, Enum, Bounded, Show)--instance Arbitrary PBRel where- arbitrary = arbitraryBoundedEnum --evalPBRel :: Ord a => PBRel -> a -> a -> Bool-evalPBRel PBRelGE = (>=)-evalPBRel PBRelLE = (<=)-evalPBRel PBRelEQ = (==)--solvePB :: SAT.Solver -> (Int,[(PBRel,SAT.PBLinSum,Integer)]) -> IO (Maybe SAT.Model)-solvePB solver (nv,cs) = do- SAT.newVars_ solver nv- forM_ cs $ \(o,lhs,rhs) -> do- case o of- PBRelGE -> SAT.addPBAtLeast solver lhs rhs- PBRelLE -> SAT.addPBAtMost solver lhs rhs- PBRelEQ -> SAT.addPBExactly solver lhs rhs- ret <- SAT.solve solver- if ret then do- m <- SAT.getModel solver- return (Just m)- else do- return Nothing--arbitraryPB :: Gen (Int,[(PBRel,SAT.PBLinSum,Integer)])-arbitraryPB = do- nv <- choose (0,10)- nc <- choose (0,50)- cs <- replicateM nc $ do- rel <- arbitrary- lhs <- arbitraryPBLinSum nv- rhs <- arbitrary- return $ (rel,lhs,rhs)- return (nv, cs)--arbitraryPBLinSum :: Int -> Gen SAT.PBLinSum-arbitraryPBLinSum nv = do- len <- choose (0,10)- if nv == 0 then- return []- else- replicateM len $ do- l <- choose (-nv, nv) `suchThat` (/= 0)- c <- arbitrary- return (c,l)--evalPB :: SAT.Model -> (Int,[(PBRel,SAT.PBLinSum,Integer)]) -> Bool-evalPB m (_,cs) = all (\(o,lhs,rhs) -> evalPBRel o (SAT.evalPBLinSum m lhs) rhs) cs--prop_optimizePBO :: Property-prop_optimizePBO = QM.monadicIO $ do- prob@(nv,_) <- QM.pick arbitraryPB- obj <- QM.pick $ arbitraryPBLinSum nv- solver <- arbitrarySolver- opt <- arbitraryOptimizer solver obj- ret <- QM.run $ optimizePBO solver opt prob- case ret of- Just (m, v) -> do- QM.assert $ evalPB m prob- QM.assert $ SAT.evalPBLinSum m obj == v- forM_ (allAssignments nv) $ \m2 -> do- QM.assert $ not (evalPB m2 prob) || SAT.evalPBLinSum m obj <= SAT.evalPBLinSum m2 obj- Nothing -> do- forM_ (allAssignments nv) $ \m -> do- QM.assert $ not (evalPB m prob)- -optimizePBO :: SAT.Solver -> PBO.Optimizer -> (Int,[(PBRel,SAT.PBLinSum,Integer)]) -> IO (Maybe (SAT.Model, Integer))-optimizePBO solver opt (nv,cs) = do- SAT.newVars_ solver nv- forM_ cs $ \(o,lhs,rhs) -> do- case o of- PBRelGE -> SAT.addPBAtLeast solver lhs rhs- PBRelLE -> SAT.addPBAtMost solver lhs rhs- PBRelEQ -> SAT.addPBExactly solver lhs rhs- PBO.optimize opt- PBO.getBestSolution opt--evalWBO :: SAT.Model -> (Int, [(Maybe Integer, (PBRel,SAT.PBLinSum,Integer))], Maybe Integer) -> Maybe Integer-evalWBO m (nv,cs,top) = do- cost <- liftM sum $ forM cs $ \(w,(o,lhs,rhs)) -> do- if evalPBRel o (SAT.evalPBLinSum m lhs) rhs then- return 0- else- w- case top of- Just t -> guard (cost < t)- Nothing -> return ()- return cost--arbitraryWBO :: Gen (Int, [(Maybe Integer, (PBRel,SAT.PBLinSum,Integer))], Maybe Integer)-arbitraryWBO = do- (nv,cs) <- arbitraryPB- cs2 <- forM cs $ \c -> do- b <- arbitrary- cost <- if b then return Nothing- else liftM (Just . (1+) . abs) arbitrary- return (cost, c)- b <- arbitrary- top <- if b then return Nothing- else liftM (Just . (1+) . abs) arbitrary- return (nv,cs2,top)--optimizeWBO- :: SAT.Solver- -> PBO.Method- -> (Int, [(Maybe Integer, (PBRel,SAT.PBLinSum,Integer))], Maybe Integer)- -> IO (Maybe (SAT.Model, Integer))-optimizeWBO solver method (nv,cs,top) = do- SAT.newVars_ solver nv- obj <- liftM catMaybes $ forM cs $ \(cost, (o,lhs,rhs)) -> do- case cost of- Nothing -> do- case o of- PBRelGE -> SAT.addPBAtLeast solver lhs rhs- PBRelLE -> SAT.addPBAtMost solver lhs rhs- PBRelEQ -> SAT.addPBExactly solver lhs rhs- return Nothing- Just w -> do- sel <- SAT.newVar solver- case o of- PBRelGE -> SAT.addPBAtLeastSoft solver sel lhs rhs- PBRelLE -> SAT.addPBAtMostSoft solver sel lhs rhs- PBRelEQ -> SAT.addPBExactlySoft solver sel lhs rhs- return $ Just (w,-sel)- case top of- Nothing -> return ()- Just c -> SAT.addPBAtMost solver obj (c-1)- opt <- PBO.newOptimizer solver obj- PBO.optimize opt- liftM (fmap (\(m, val) -> (SAT.restrictModel nv m, val))) $ PBO.getBestSolution opt--prop_solvePBNLC :: Property-prop_solvePBNLC = QM.monadicIO $ do- prob@(nv,_) <- QM.pick arbitraryPBNLC- solver <- arbitrarySolver- ret <- QM.run $ solvePBNLC solver prob- case ret of- Just m -> QM.assert $ evalPBNLC m prob- Nothing -> do- forM_ (allAssignments nv) $ \m -> do- QM.assert $ not (evalPBNLC m prob)--solvePBNLC :: SAT.Solver -> (Int,[(PBRel,SAT.PBSum,Integer)]) -> IO (Maybe SAT.Model)-solvePBNLC solver (nv,cs) = do- SAT.newVars_ solver nv- enc <- PBNLC.newEncoder solver =<< Tseitin.newEncoder solver- forM_ cs $ \(o,lhs,rhs) -> do- case o of- PBRelGE -> PBNLC.addPBNLAtLeast enc lhs rhs- PBRelLE -> PBNLC.addPBNLAtMost enc lhs rhs- PBRelEQ -> PBNLC.addPBNLExactly enc lhs rhs- ret <- SAT.solve solver- if ret then do- m <- SAT.getModel solver- return $ Just $ SAT.restrictModel nv m- else do- return Nothing--optimizePBNLC- :: SAT.Solver- -> PBO.Method- -> (Int, SAT.PBSum, [(PBRel,SAT.PBSum,Integer)])- -> IO (Maybe (SAT.Model, Integer))-optimizePBNLC solver method (nv,obj,cs) = do- SAT.newVars_ solver nv- enc <- PBNLC.newEncoder solver =<< Tseitin.newEncoder solver- forM_ cs $ \(o,lhs,rhs) -> do- case o of- PBRelGE -> PBNLC.addPBNLAtLeast enc lhs rhs- PBRelLE -> PBNLC.addPBNLAtMost enc lhs rhs- PBRelEQ -> PBNLC.addPBNLExactly enc lhs rhs- obj2 <- PBNLC.linearizePBSumWithPolarity enc Tseitin.polarityNeg obj- opt <- PBO.newOptimizer2 solver obj2 (\m -> SAT.evalPBSum m obj)- PBO.setMethod opt method- PBO.optimize opt- liftM (fmap (\(m, val) -> (SAT.restrictModel nv m, val))) $ PBO.getBestSolution opt--arbitraryPBNLC :: Gen (Int,[(PBRel,SAT.PBSum,Integer)])-arbitraryPBNLC = do- nv <- choose (0,10)- nc <- choose (0,50)- cs <- replicateM nc $ do- rel <- arbitrary- len <- choose (0,10)- lhs <-- if nv == 0 then- return []- else- replicateM len $ do- ls <- listOf $ choose (-nv, nv) `suchThat` (/= 0)- c <- arbitrary- return (c,ls)- rhs <- arbitrary- return $ (rel,lhs,rhs)- return (nv, cs)--evalPBNLC :: SAT.Model -> (Int,[(PBRel,SAT.PBSum,Integer)]) -> Bool-evalPBNLC m (_,cs) = all (\(o,lhs,rhs) -> evalPBRel o (SAT.evalPBSum m lhs) rhs) cs---prop_solveXOR :: Property-prop_solveXOR = QM.monadicIO $ do- prob@(nv,_) <- QM.pick arbitraryXOR- solver <- arbitrarySolver- ret <- QM.run $ solveXOR solver prob- case ret of- Just m -> QM.assert $ evalXOR m prob- Nothing -> do- forM_ (allAssignments nv) $ \m -> do- QM.assert $ not (evalXOR m prob)--solveXOR :: SAT.Solver -> (Int,[SAT.XORClause]) -> IO (Maybe SAT.Model)-solveXOR solver (nv,cs) = do- SAT.modifyConfig solver $ \config -> config{ SAT.configCheckModel = True }- SAT.newVars_ solver nv- forM_ cs $ \c -> SAT.addXORClause solver (fst c) (snd c)- ret <- SAT.solve solver- if ret then do- m <- SAT.getModel solver- return (Just m)- else do- return Nothing--arbitraryXOR :: Gen (Int,[SAT.XORClause])-arbitraryXOR = do- nv <- choose (0,10)- nc <- choose (0,50)- cs <- replicateM nc $ do- len <- choose (0,10) - lhs <-- if nv == 0 then- return []- else- replicateM len $ choose (-nv, nv) `suchThat` (/= 0)- rhs <- arbitrary- return (lhs,rhs)- return (nv, cs)--evalXOR :: SAT.Model -> (Int,[SAT.XORClause]) -> Bool-evalXOR m (_,cs) = all (SAT.evalXORClause m) cs---newTheorySolver :: CNF.CNF -> IO TheorySolver-newTheorySolver cnf = do- let nv = CNF.numVars cnf- cs = CNF.clauses cnf- solver <- SAT.newSolver- SAT.newVars_ solver nv- forM_ cs $ \c -> SAT.addClause solver c- - ref <- newIORef []- let tsolver =- TheorySolver- { thAssertLit = \_ l -> do- if abs l > nv then- return True- else do- m <- readIORef ref- case m of- [] -> SAT.addClause solver [l]- xs : xss -> writeIORef ref ((l : xs) : xss)- return True- , thCheck = \_ -> do- xs <- liftM concat $ readIORef ref- SAT.solveWith solver xs- , thExplain = \m -> do- case m of- Nothing -> SAT.getFailedAssumptions solver- Just _ -> return []- , thPushBacktrackPoint = modifyIORef ref ([] :)- , thPopBacktrackPoint = modifyIORef ref tail- , thConstructModel = return ()- }- return tsolver--prop_solveCNF_using_BooleanTheory :: Property-prop_solveCNF_using_BooleanTheory = QM.monadicIO $ do- cnf <- QM.pick arbitraryCNF- let nv = CNF.numVars cnf- nc = CNF.numClauses cnf- cs = CNF.clauses cnf- cnf1 = cnf{ CNF.clauses = [c | (i,c) <- zip [0..] cs, i `mod` 2 == 0], CNF.numClauses = nc - (nc `div` 2) }- cnf2 = cnf{ CNF.clauses = [c | (i,c) <- zip [0..] cs, i `mod` 2 /= 0], CNF.numClauses = nc `div` 2 }-- solver <- arbitrarySolver-- ret <- QM.run $ do- SAT.newVars_ solver nv-- tsolver <- newTheorySolver cnf1- SAT.setTheory solver tsolver-- forM_ (CNF.clauses cnf2) $ \c -> SAT.addClause solver c- ret <- SAT.solve solver- if ret then do- m <- SAT.getModel solver- return (Just m)- else do- return Nothing-- case ret of- Just m -> QM.assert $ evalCNF m cnf- Nothing -> do- forM_ (allAssignments nv) $ \m -> do- QM.assert $ not (evalCNF m cnf)--case_QF_LRA :: Assertion-case_QF_LRA = do- satSolver <- SAT.newSolver- lraSolver <- Simplex.newSolver-- tblRef <- newIORef $ Map.empty- defsRef <- newIORef $ IntMap.empty- let abstractLAAtom :: LA.Atom Rational -> IO SAT.Lit- abstractLAAtom atom = do- (v,op,rhs) <- Simplex.simplifyAtom lraSolver atom- tbl <- readIORef tblRef- (vLt, vEq, vGt) <-- case Map.lookup (v,rhs) tbl of- Just (vLt, vEq, vGt) -> return (vLt, vEq, vGt)- Nothing -> do- vLt <- SAT.newVar satSolver- vEq <- SAT.newVar satSolver- vGt <- SAT.newVar satSolver- SAT.addClause satSolver [vLt,vEq,vGt]- SAT.addClause satSolver [-vLt, -vEq]- SAT.addClause satSolver [-vLt, -vGt] - SAT.addClause satSolver [-vEq, -vGt]- writeIORef tblRef (Map.insert (v,rhs) (vLt, vEq, vGt) tbl)- let xs = IntMap.fromList- [ (vEq, LA.var v .==. LA.constant rhs)- , (vLt, LA.var v .<. LA.constant rhs)- , (vGt, LA.var v .>. LA.constant rhs)- , (-vLt, LA.var v .>=. LA.constant rhs)- , (-vGt, LA.var v .<=. LA.constant rhs)- ]- modifyIORef defsRef (IntMap.union xs)- return (vLt, vEq, vGt)- case op of- Lt -> return vLt- Gt -> return vGt- Eql -> return vEq- Le -> return (-vGt)- Ge -> return (-vLt)- NEq -> return (-vEq)-- abstract :: BoolExpr (Either SAT.Lit (LA.Atom Rational)) -> IO (BoolExpr SAT.Lit)- abstract = Traversable.mapM f- where- f (Left lit) = return lit- f (Right atom) = abstractLAAtom atom-- let tsolver =- TheorySolver- { thAssertLit = \_ l -> do- defs <- readIORef defsRef- case IntMap.lookup l defs of- Nothing -> return True- Just atom -> do- Simplex.assertAtomEx' lraSolver atom (Just l)- return True- , thCheck = \_ -> do- Simplex.check lraSolver- , thExplain = \m -> do- case m of- Nothing -> liftM IntSet.toList $ Simplex.explain lraSolver- Just _ -> return []- , thPushBacktrackPoint = do- Simplex.pushBacktrackPoint lraSolver- , thPopBacktrackPoint = do- Simplex.popBacktrackPoint lraSolver- , thConstructModel = do- return ()- }- SAT.setTheory satSolver tsolver-- enc <- Tseitin.newEncoder satSolver- let addFormula :: BoolExpr (Either SAT.Lit (LA.Atom Rational)) -> IO ()- addFormula c = Tseitin.addFormula enc =<< abstract c-- a <- SAT.newVar satSolver- x <- Simplex.newVar lraSolver- y <- Simplex.newVar lraSolver-- let le1 = LA.fromTerms [(2,x), (1/3,y)] .<=. LA.constant (-4) -- 2 x + (1/3) y <= -4- eq2 = LA.fromTerms [(1.5,x)] .==. LA.fromTerms [(-2,x)] -- 1.5 y = -2 x- gt3 = LA.var x .>. LA.var y -- x > y- lt4 = LA.fromTerms [(3,x)] .<. LA.fromTerms [(-1,LA.unitVar), (1/5,x), (1/5,y)] -- 3 x < -1 + (1/5) (x + y)-- c1, c2 :: BoolExpr (Either SAT.Lit (LA.Atom Rational))- c1 = ite (Atom (Left a) :: BoolExpr (Either SAT.Lit (LA.Atom Rational))) (Atom $ Right le1) (Atom $ Right eq2)- c2 = Atom (Right gt3) .||. (Atom (Left a) .<=>. Atom (Right lt4))-- addFormula c1- addFormula c2-- ret <- SAT.solve satSolver- ret @?= True-- m1 <- SAT.getModel satSolver- m2 <- Simplex.getModel lraSolver- defs <- readIORef defsRef- let f (Left lit) = SAT.evalLit m1 lit- f (Right atom) = LA.eval m2 atom- fold f c1 @?= True- fold f c2 @?= True---case_QF_EUF :: Assertion-case_QF_EUF = do- satSolver <- SAT.newSolver- eufSolver <- EUF.newSolver- enc <- Tseitin.newEncoder satSolver- - tblRef <- newIORef (Map.empty :: Map (EUF.Term, EUF.Term) SAT.Var)- defsRef <- newIORef (IntMap.empty :: IntMap (EUF.Term, EUF.Term))- eufModelRef <- newIORef (undefined :: EUF.Model)- - let abstractEUFAtom :: (EUF.Term, EUF.Term) -> IO SAT.Lit- abstractEUFAtom (t1,t2) | t1 >= t2 = abstractEUFAtom (t2,t1)- abstractEUFAtom (t1,t2) = do- tbl <- readIORef tblRef- case Map.lookup (t1,t2) tbl of- Just v -> return v- Nothing -> do- v <- SAT.newVar satSolver- writeIORef tblRef $! Map.insert (t1,t2) v tbl- modifyIORef' defsRef $! IntMap.insert v (t1,t2)- return v-- abstract :: BoolExpr (Either SAT.Lit (EUF.Term, EUF.Term)) -> IO (BoolExpr SAT.Lit)- abstract = Traversable.mapM f- where- f (Left lit) = return lit- f (Right atom) = abstractEUFAtom atom-- let tsolver =- TheorySolver- { thAssertLit = \_ l -> do- defs <- readIORef defsRef- case IntMap.lookup (SAT.litVar l) defs of- Nothing -> return True- Just (t1,t2) -> do- if SAT.litPolarity l then- EUF.assertEqual' eufSolver t1 t2 (Just l)- else- EUF.assertNotEqual' eufSolver t1 t2 (Just l)- return True- , thCheck = \callback -> do- b <- EUF.check eufSolver- when b $ do- defs <- readIORef defsRef- forM_ (IntMap.toList defs) $ \(v, (t1, t2)) -> do- b2 <- EUF.areEqual eufSolver t1 t2- when b2 $ do- callback v- return ()- return b - , thExplain = \m -> do- case m of- Nothing -> liftM IntSet.toList $ EUF.explain eufSolver Nothing- Just v -> do- defs <- readIORef defsRef- case IntMap.lookup v defs of- Nothing -> error "should not happen"- Just (t1,t2) -> do- liftM IntSet.toList $ EUF.explain eufSolver (Just (t1,t2))- , thPushBacktrackPoint = do- EUF.pushBacktrackPoint eufSolver- , thPopBacktrackPoint = do- EUF.popBacktrackPoint eufSolver- , thConstructModel = do- writeIORef eufModelRef =<< EUF.getModel eufSolver- return ()- }- SAT.setTheory satSolver tsolver-- true <- EUF.newConst eufSolver- false <- EUF.newConst eufSolver- EUF.assertNotEqual eufSolver true false- boolToTermRef <- newIORef (IntMap.empty :: IntMap EUF.Term)- termToBoolRef <- newIORef (Map.empty :: Map EUF.Term SAT.Lit)-- let connectBoolTerm :: SAT.Lit -> EUF.Term -> IO ()- connectBoolTerm lit t = do- lit1 <- abstractEUFAtom (t, true)- lit2 <- abstractEUFAtom (t, false)- SAT.addClause satSolver [-lit, lit1] -- lit -> lit1- SAT.addClause satSolver [-lit1, lit] -- lit1 -> lit- SAT.addClause satSolver [lit, lit2] -- -lit -> lit2- SAT.addClause satSolver [-lit2, -lit] -- lit2 -> -lit- modifyIORef' boolToTermRef $ IntMap.insert lit t- modifyIORef' termToBoolRef $ Map.insert t lit-- boolToTerm :: SAT.Lit -> IO EUF.Term- boolToTerm lit = do- tbl <- readIORef boolToTermRef- case IntMap.lookup lit tbl of- Just t -> return t- Nothing -> do- t <- EUF.newConst eufSolver- connectBoolTerm lit t- return t-- termToBool :: EUF.Term -> IO SAT.Lit- termToBool t = do- tbl <- readIORef termToBoolRef- case Map.lookup t tbl of- Just lit -> return lit- Nothing -> do- lit <- SAT.newVar satSolver- connectBoolTerm lit t- return lit-- let addFormula :: BoolExpr (Either SAT.Lit (EUF.Term, EUF.Term)) -> IO ()- addFormula c = Tseitin.addFormula enc =<< abstract c-- do- x <- SAT.newVar satSolver- x' <- boolToTerm x- f <- EUF.newFun eufSolver- fx <- termToBool (f x')- ftt <- abstractEUFAtom (f true, true)- ret <- SAT.solveWith satSolver [-fx, ftt]- ret @?= True-- m1 <- SAT.getModel satSolver- m2 <- readIORef eufModelRef- let e (Left lit) = SAT.evalLit m1 lit- e (Right (lhs,rhs)) = EUF.eval m2 lhs == EUF.eval m2 rhs- fold e (notB (Atom (Left fx)) .||. (Atom (Right (f true, true)))) @?= True- SAT.evalLit m1 x @?= False-- ret <- SAT.solveWith satSolver [-fx, ftt, x]- ret @?= False-- do- -- a : Bool- -- f : U -> U- -- x : U- -- y : U- -- (a or x=y)- -- f x /= f y- a <- SAT.newVar satSolver- f <- EUF.newFun eufSolver- x <- EUF.newConst eufSolver- y <- EUF.newConst eufSolver- let c1, c2 :: BoolExpr (Either SAT.Lit (EUF.Term, EUF.Term))- c1 = Atom (Left a) .||. Atom (Right (x,y))- c2 = notB $ Atom (Right (f x, f y))- addFormula c1- addFormula c2- ret <- SAT.solve satSolver- ret @?= True- m1 <- SAT.getModel satSolver- m2 <- readIORef eufModelRef- let e (Left lit) = SAT.evalLit m1 lit- e (Right (lhs,rhs)) = EUF.eval m2 lhs == EUF.eval m2 rhs- fold e c1 @?= True- fold e c2 @?= True-- ret <- SAT.solveWith satSolver [-a]- ret @?= False---- should be SAT-case_solve_SAT :: Assertion-case_solve_SAT = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addClause solver [x1, x2] -- x1 or x2- SAT.addClause solver [x1, -x2] -- x1 or not x2- SAT.addClause solver [-x1, -x2] -- not x1 or not x2- ret <- SAT.solve solver- ret @?= True---- shuld be UNSAT-case_solve_UNSAT :: Assertion-case_solve_UNSAT = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addClause solver [x1, x2] -- x1 or x2- SAT.addClause solver [-x1, x2] -- not x1 or x2- SAT.addClause solver [x1, -x2] -- x1 or not x2- SAT.addClause solver [-x1, -x2] -- not x2 or not x2- ret <- SAT.solve solver- ret @?= False---- top level でいきなり矛盾-case_root_inconsistent :: Assertion-case_root_inconsistent = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- SAT.addClause solver [x1]- SAT.addClause solver [-x1]- ret <- SAT.solve solver -- unsat- ret @?= False---- incremental に制約を追加-case_incremental_solving :: Assertion-case_incremental_solving = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addClause solver [x1, x2] -- x1 or x2- SAT.addClause solver [x1, -x2] -- x1 or not x2- SAT.addClause solver [-x1, -x2] -- not x1 or not x2- ret <- SAT.solve solver -- sat- ret @?= True-- SAT.addClause solver [-x1, x2] -- not x1 or x2- ret <- SAT.solve solver -- unsat- ret @?= False---- 制約なし-case_empty_constraint :: Assertion-case_empty_constraint = do- solver <- SAT.newSolver- ret <- SAT.solve solver- ret @?= True---- 空の節-case_empty_claue :: Assertion-case_empty_claue = do- solver <- SAT.newSolver- SAT.addClause solver []- ret <- SAT.solve solver- ret @?= False---- 自明に真な節-case_excluded_middle_claue :: Assertion-case_excluded_middle_claue = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- SAT.addClause solver [x1, -x1] -- x1 or not x1- ret <- SAT.solve solver- ret @?= True---- 冗長な節-case_redundant_clause :: Assertion-case_redundant_clause = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- SAT.addClause solver [x1,x1] -- x1 or x1- ret <- SAT.solve solver- ret @?= True--case_instantiateClause :: Assertion-case_instantiateClause = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addClause solver [x1]- SAT.addClause solver [x1,x2]- SAT.addClause solver [-x1,x2]- ret <- SAT.solve solver- ret @?= True--case_instantiateAtLeast :: Assertion-case_instantiateAtLeast = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- x4 <- SAT.newVar solver- SAT.addClause solver [x1]-- SAT.addAtLeast solver [x1,x2,x3,x4] 2- ret <- SAT.solve solver- ret @?= True-- SAT.addAtLeast solver [-x1,-x2,-x3,-x4] 2- ret <- SAT.solve solver- ret @?= True--case_inconsistent_AtLeast :: Assertion-case_inconsistent_AtLeast = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addAtLeast solver [x1,x2] 3- ret <- SAT.solve solver -- unsat- ret @?= False--case_trivial_AtLeast :: Assertion-case_trivial_AtLeast = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addAtLeast solver [x1,x2] 0- ret <- SAT.solve solver- ret @?= True-- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addAtLeast solver [x1,x2] (-1)- ret <- SAT.solve solver- ret @?= True--case_AtLeast_1 :: Assertion-case_AtLeast_1 = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- SAT.addAtLeast solver [x1,x2,x3] 2- SAT.addAtLeast solver [-x1,-x2,-x3] 2- ret <- SAT.solve solver -- unsat- ret @?= False--case_AtLeast_2 :: Assertion-case_AtLeast_2 = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- x4 <- SAT.newVar solver- SAT.addAtLeast solver [x1,x2,x3,x4] 2- SAT.addClause solver [-x1,-x2]- SAT.addClause solver [-x1,-x3]- ret <- SAT.solve solver- ret @?= True--case_AtLeast_3 :: Assertion-case_AtLeast_3 = do- forM_ [(-1) .. 3] $ \n -> do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addAtLeast solver [x1,x2] n- ret <- SAT.solve solver- assertEqual ("case_AtLeast3_" ++ show n) (n <= 2) ret---- from http://www.cril.univ-artois.fr/PB11/format.pdf-case_PB_sample1 :: Assertion-case_PB_sample1 = do- solver <- SAT.newSolver-- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- x4 <- SAT.newVar solver- x5 <- SAT.newVar solver-- SAT.addPBAtLeast solver [(1,x1),(4,x2),(-2,x5)] 2- SAT.addPBAtLeast solver [(-1,x1),(4,x2),(-2,x5)] 3- SAT.addPBAtLeast solver [(12345678901234567890,x4),(4,x3)] 10- SAT.addPBExactly solver [(2,x2),(3,x4),(2,x1),(3,x5)] 5-- ret <- SAT.solve solver- ret @?= True---- 一部の変数を否定に置き換えたもの-case_PB_sample1' :: Assertion-case_PB_sample1' = do- solver <- SAT.newSolver-- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- x4 <- SAT.newVar solver- x5 <- SAT.newVar solver-- SAT.addPBAtLeast solver [(1,x1),(4,-x2),(-2,x5)] 2- SAT.addPBAtLeast solver [(-1,x1),(4,-x2),(-2,x5)] 3- SAT.addPBAtLeast solver [(12345678901234567890,-x4),(4,x3)] 10- SAT.addPBExactly solver [(2,-x2),(3,-x4),(2,x1),(3,x5)] 5-- ret <- SAT.solve solver- ret @?= True---- いきなり矛盾したPB制約-case_root_inconsistent_PB :: Assertion-case_root_inconsistent_PB = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addPBAtLeast solver [(2,x1),(3,x2)] 6- ret <- SAT.solve solver- ret @?= False--case_pb_propagate :: Assertion-case_pb_propagate = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addPBAtLeast solver [(1,x1),(3,x2)] 3- SAT.addClause solver [-x1]- ret <- SAT.solve solver- ret @?= True--case_solveWith_1 :: Assertion-case_solveWith_1 = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- SAT.addClause solver [x1, x2] -- x1 or x2- SAT.addClause solver [x1, -x2] -- x1 or not x2- SAT.addClause solver [-x1, -x2] -- not x1 or not x2- SAT.addClause solver [-x3, -x1, x2] -- not x3 or not x1 or x2-- ret <- SAT.solve solver -- sat- ret @?= True-- ret <- SAT.solveWith solver [x3] -- unsat- ret @?= False-- ret <- SAT.solve solver -- sat- ret @?= True--case_solveWith_2 :: Assertion-case_solveWith_2 = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addClause solver [-x1, x2] -- -x1 or x2- SAT.addClause solver [x1] -- x1-- ret <- SAT.solveWith solver [x2]- ret @?= True-- ret <- SAT.solveWith solver [-x2]- ret @?= False--case_getVarFixed :: Assertion-case_getVarFixed = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- SAT.addClause solver [x1,x2]-- ret <- SAT.getVarFixed solver x1- ret @?= lUndef-- SAT.addClause solver [-x1]- - ret <- SAT.getVarFixed solver x1- ret @?= lFalse-- ret <- SAT.getLitFixed solver (-x1)- ret @?= lTrue-- ret <- SAT.getLitFixed solver x2- ret @?= lTrue--case_getAssumptionsImplications_case1 :: Assertion-case_getAssumptionsImplications_case1 = do- solver <- SAT.newSolver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- SAT.addClause solver [x1,x2,x3]-- SAT.addClause solver [-x1]- ret <- SAT.solveWith solver [-x2]- ret @?= True- xs <- SAT.getAssumptionsImplications solver- xs @?= [x3]--prop_getAssumptionsImplications :: Property-prop_getAssumptionsImplications = QM.monadicIO $ do- cnf <- QM.pick arbitraryCNF- solver <- arbitrarySolver- ls <- QM.pick $ liftM concat $ mapM (\v -> elements [[],[-v],[v]]) [1..CNF.numVars cnf]- ret <- QM.run $ do- SAT.newVars_ solver (CNF.numVars cnf)- forM_ (CNF.clauses cnf) $ \c -> SAT.addClause solver c- SAT.solveWith solver ls- when ret $ do- xs <- QM.run $ SAT.getAssumptionsImplications solver- forM_ xs $ \x -> do- ret2 <- QM.run $ SAT.solveWith solver (-x : ls)- QM.assert $ not ret2------------------------------------------------------------------------------ -4*(not x1) + 3*x1 + 10*(not x2)--- = -4*(1 - x1) + 3*x1 + 10*(not x2)--- = -4 + 4*x1 + 3*x1 + 10*(not x2)--- = 7*x1 + 10*(not x2) - 4-case_normalizePBLinSum_1 :: Assertion-case_normalizePBLinSum_1 = do- sort e @?= sort [(7,x1),(10,-x2)]- c @?= -4- where- x1 = 1- x2 = 2- (e,c) = SAT.normalizePBLinSum ([(-4,-x1),(3,x1),(10,-x2)], 0)--prop_normalizePBLinSum :: Property-prop_normalizePBLinSum = forAll g $ \(nv, (s,n)) ->- let (s2,n2) = SAT.normalizePBLinSum (s,n)- in flip all (allAssignments nv) $ \m ->- SAT.evalPBLinSum m s + n == SAT.evalPBLinSum m s2 + n2- where- g :: Gen (Int, (SAT.PBLinSum, Integer))- g = do- nv <- choose (0, 10)- s <- forM [1..nv] $ \x -> do- c <- arbitrary- p <- arbitrary- return (c, SAT.literal x p)- n <- arbitrary- return (nv, (s,n))---- -4*(not x1) + 3*x1 + 10*(not x2) >= 3--- ⇔ -4*(1 - x1) + 3*x1 + 10*(not x2) >= 3--- ⇔ -4 + 4*x1 + 3*x1 + 10*(not x2) >= 3--- ⇔ 7*x1 + 10*(not x2) >= 7--- ⇔ 7*x1 + 7*(not x2) >= 7--- ⇔ x1 + (not x2) >= 1-case_normalizePBLinAtLeast_1 :: Assertion-case_normalizePBLinAtLeast_1 = (sort lhs, rhs) @?= (sort [(1,x1),(1,-x2)], 1)- where- x1 = 1- x2 = 2- (lhs,rhs) = SAT.normalizePBLinAtLeast ([(-4,-x1),(3,x1),(10,-x2)], 3)--prop_normalizePBLinAtLeast :: Property-prop_normalizePBLinAtLeast = forAll g $ \(nv, c) ->- let c2 = SAT.normalizePBLinAtLeast c- in flip all (allAssignments nv) $ \m ->- SAT.evalPBLinAtLeast m c == SAT.evalPBLinAtLeast m c2- where- g :: Gen (Int, SAT.PBLinAtLeast)- g = do- nv <- choose (0, 10)- lhs <- forM [1..nv] $ \x -> do- c <- arbitrary- p <- arbitrary- return (c, SAT.literal x p)- rhs <- arbitrary- return (nv, (lhs,rhs))--case_normalizePBLinExactly_1 :: Assertion-case_normalizePBLinExactly_1 = (sort lhs, rhs) @?= ([], 1)- where- x1 = 1- x2 = 2- (lhs,rhs) = SAT.normalizePBLinExactly ([(6,x1),(4,x2)], 2)--case_normalizePBLinExactly_2 :: Assertion-case_normalizePBLinExactly_2 = (sort lhs, rhs) @?= ([], 1)- where- x1 = 1- x2 = 2- x3 = 3- (lhs,rhs) = SAT.normalizePBLinExactly ([(2,x1),(2,x2),(2,x3)], 3)--prop_normalizePBLinExactly :: Property-prop_normalizePBLinExactly = forAll g $ \(nv, c) ->- let c2 = SAT.normalizePBLinExactly c- in flip all (allAssignments nv) $ \m ->- SAT.evalPBLinExactly m c == SAT.evalPBLinExactly m c2- where- g :: Gen (Int, SAT.PBLinExactly)- g = do- nv <- choose (0, 10)- lhs <- forM [1..nv] $ \x -> do- c <- arbitrary- p <- arbitrary- return (c, SAT.literal x p)- rhs <- arbitrary- return (nv, (lhs,rhs))--prop_cutResolve :: Property-prop_cutResolve =- forAll (choose (1, 10)) $ \nv ->- forAll (g nv True) $ \c1 ->- forAll (g nv False) $ \c2 ->- let c3 = SAT.cutResolve c1 c2 1- in flip all (allAssignments nv) $ \m ->- not (SAT.evalPBLinExactly m c1 && SAT.evalPBLinExactly m c2) || SAT.evalPBLinExactly m c3- where- g :: Int -> Bool -> Gen SAT.PBLinExactly- g nv b = do- lhs <- forM [1..nv] $ \x -> do- if x==1 then do- c <- liftM ((1+) . abs) arbitrary- return (c, SAT.literal x b)- else do- c <- arbitrary- p <- arbitrary- return (c, SAT.literal x p)- rhs <- arbitrary- return (lhs, rhs)--case_cutResolve_1 :: Assertion-case_cutResolve_1 = (sort lhs, rhs) @?= (sort [(1,x3),(1,x4)], 1)- where- x1 = 1- x2 = 2- x3 = 3- x4 = 4- pb1 = ([(1,x1), (1,x2), (1,x3)], 1)- pb2 = ([(2,-x1), (2,-x2), (1,x4)], 3)- (lhs,rhs) = SAT.cutResolve pb1 pb2 x1--case_cutResolve_2 :: Assertion-case_cutResolve_2 = (sort lhs, rhs) @?= (sort lhs2, rhs2)- where- x1 = 1- x2 = 2- x3 = 3- x4 = 4- pb1 = ([(3,x1), (2,-x2), (1,x3), (1,x4)], 3)- pb2 = ([(1,-x3), (1,x4)], 1)- (lhs,rhs) = SAT.cutResolve pb1 pb2 x3- (lhs2,rhs2) = ([(2,x1),(1,-x2),(1,x4)],2) -- ([(3,x1),(2,-x2),(2,x4)], 3)--case_cardinalityReduction :: Assertion-case_cardinalityReduction = (sort lhs, rhs) @?= ([1,2,3,4,5],4)- where- (lhs, rhs) = SAT.cardinalityReduction ([(6,1),(5,2),(4,3),(3,4),(2,5),(1,6)], 17)--case_pbSubsume_clause :: Assertion-case_pbSubsume_clause = SAT.pbSubsume ([(1,1),(1,-3)],1) ([(1,1),(1,2),(1,-3),(1,4)],1) @?= True--case_pbSubsume_1 :: Assertion-case_pbSubsume_1 = SAT.pbSubsume ([(1,1),(1,2),(1,-3)],2) ([(1,1),(2,2),(1,-3),(1,4)],1) @?= True--case_pbSubsume_2 :: Assertion-case_pbSubsume_2 = SAT.pbSubsume ([(1,1),(1,2),(1,-3)],2) ([(1,1),(2,2),(1,-3),(1,4)],3) @?= False----------------------------------------------------------------------------case_normalizeXORClause_False =- SAT.normalizeXORClause ([],True) @?= ([],True)--case_normalizeXORClause_True =- SAT.normalizeXORClause ([],False) @?= ([],False)---- x ⊕ y ⊕ x = y-case_normalizeXORClause_case1 =- SAT.normalizeXORClause ([1,2,1],True) @?= ([2],True)---- x ⊕ ¬x = x ⊕ x ⊕ 1 = 1-case_normalizeXORClause_case2 =- SAT.normalizeXORClause ([1,-1],True) @?= ([],False)--prop_normalizeXORClause :: Property-prop_normalizeXORClause = forAll g $ \(nv, c) ->- let c2 = SAT.normalizeXORClause c- in flip all (allAssignments nv) $ \m ->- SAT.evalXORClause m c == SAT.evalXORClause m c2- where- g :: Gen (Int, SAT.XORClause)- g = do- nv <- choose (0, 10)- len <- choose (0, nv)- lhs <- replicateM len $ choose (-nv, nv) `suchThat` (/= 0)- rhs <- arbitrary- return (nv, (lhs,rhs))--case_evalXORClause_case1 =- SAT.evalXORClause (array (1,2) [(1,True),(2,True)] :: Array Int Bool) ([1,2], True) @?= False--case_evalXORClause_case2 =- SAT.evalXORClause (array (1,2) [(1,False),(2,True)] :: Array Int Bool) ([1,2], True) @?= True--case_xor_case1 = do- solver <- SAT.newSolver- SAT.modifyConfig solver $ \config -> config{ SAT.configCheckModel = True }- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- SAT.addXORClause solver [x1, x2] True -- x1 ⊕ x2 = True- SAT.addXORClause solver [x2, x3] True -- x2 ⊕ x3 = True- SAT.addXORClause solver [x3, x1] True -- x3 ⊕ x1 = True- ret <- SAT.solve solver- ret @?= False--case_xor_case2 = do- solver <- SAT.newSolver- SAT.modifyConfig solver $ \config -> config{ SAT.configCheckModel = True }- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- SAT.addXORClause solver [x1, x2] True -- x1 ⊕ x2 = True- SAT.addXORClause solver [x1, x3] True -- x1 ⊕ x3 = True- SAT.addClause solver [x2]-- ret <- SAT.solve solver- ret @?= True- m <- SAT.getModel solver- m ! x1 @?= False- m ! x2 @?= True- m ! x3 @?= True--case_xor_case3 = do- solver <- SAT.newSolver- SAT.modifyConfig solver $ \config -> config{ SAT.configCheckModel = True }- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- SAT.newVar solver- x4 <- SAT.newVar solver- SAT.addXORClause solver [x1,x2,x3,x4] True- SAT.addAtLeast solver [x1,x2,x3,x4] 2- ret <- SAT.solve solver- ret @?= True------------------------------------------------------------------------------ from "Pueblo: A Hybrid Pseudo-Boolean SAT Solver"--- clauseがunitになるレベルで、PB制約が違反状態のままという例。-case_hybridLearning_1 :: Assertion-case_hybridLearning_1 = do- solver <- SAT.newSolver- [x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11] <- replicateM 11 (SAT.newVar solver)-- SAT.addClause solver [x11, x10, x9] -- C1- SAT.addClause solver [x8, x7, x6] -- C2- SAT.addClause solver [x5, x4, x3] -- C3- SAT.addAtLeast solver [-x2, -x5, -x8, -x11] 3 -- C4- SAT.addAtLeast solver [-x1, -x4, -x7, -x10] 3 -- C5-- replicateM 3 (SAT.varBumpActivity solver x3)- SAT.setVarPolarity solver x3 False-- replicateM 2 (SAT.varBumpActivity solver x6)- SAT.setVarPolarity solver x6 False-- replicateM 1 (SAT.varBumpActivity solver x9)- SAT.setVarPolarity solver x9 False-- SAT.setVarPolarity solver x1 True-- SAT.modifyConfig solver $ \config -> config{ SAT.configLearningStrategy = SAT.LearningHybrid }- ret <- SAT.solve solver- ret @?= True---- from "Pueblo: A Hybrid Pseudo-Boolean SAT Solver"--- clauseがunitになるレベルで、PB制約が違反状態のままという例。--- さらに、学習したPB制約はunitにはならない。-case_hybridLearning_2 :: Assertion-case_hybridLearning_2 = do- solver <- SAT.newSolver- [x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12] <- replicateM 12 (SAT.newVar solver)-- SAT.addClause solver [x11, x10, x9] -- C1- SAT.addClause solver [x8, x7, x6] -- C2- SAT.addClause solver [x5, x4, x3] -- C3- SAT.addAtLeast solver [-x2, -x5, -x8, -x11] 3 -- C4- SAT.addAtLeast solver [-x1, -x4, -x7, -x10] 3 -- C5-- SAT.addClause solver [x12, -x3]- SAT.addClause solver [x12, -x6]- SAT.addClause solver [x12, -x9]-- SAT.varBumpActivity solver x12- SAT.setVarPolarity solver x12 False-- SAT.modifyConfig solver $ \config -> config{ SAT.configLearningStrategy = SAT.LearningHybrid }- ret <- SAT.solve solver- ret @?= True---- regression test for the bug triggered by normalized-blast-floppy1-8.ucl.opb.bz2-case_addPBAtLeast_regression :: Assertion-case_addPBAtLeast_regression = do- solver <- SAT.newSolver- [x1,x2,x3,x4] <- replicateM 4 (SAT.newVar solver)- SAT.addClause solver [-x1]- SAT.addClause solver [-x2, -x3]- SAT.addClause solver [-x2, -x4]- SAT.addPBAtLeast solver [(1,x1),(2,x2),(1,x3),(1,x4)] 3- ret <- SAT.solve solver- ret @?= False----------------------------------------------------------------------------case_addFormula = do- solver <- SAT.newSolver- enc <- Tseitin.newEncoder solver-- [x1,x2,x3,x4,x5] <- replicateM 5 $ liftM Atom $ SAT.newVar solver- Tseitin.addFormula enc $ orB [x1 .=>. x3 .&&. x4, x2 .=>. x3 .&&. x5]- -- x6 = x3 ∧ x4- -- x7 = x3 ∧ x5- Tseitin.addFormula enc $ x1 .||. x2- Tseitin.addFormula enc $ x4 .=>. notB x5- ret <- SAT.solve solver- ret @?= True-- Tseitin.addFormula enc $ x2 .<=>. x4- ret <- SAT.solve solver- ret @?= True-- Tseitin.addFormula enc $ x1 .<=>. x5- ret <- SAT.solve solver- ret @?= True-- Tseitin.addFormula enc $ notB x1 .=>. x3 .&&. x5- ret <- SAT.solve solver- ret @?= True-- Tseitin.addFormula enc $ notB x2 .=>. x3 .&&. x4- ret <- SAT.solve solver- ret @?= False--case_addFormula_Peirces_Law = do- solver <- SAT.newSolver- enc <- Tseitin.newEncoder solver- [x1,x2] <- replicateM 2 $ liftM Atom $ SAT.newVar solver- Tseitin.addFormula enc $ notB $ ((x1 .=>. x2) .=>. x1) .=>. x1- ret <- SAT.solve solver- ret @?= False--case_encodeConj = do- solver <- SAT.newSolver- enc <- Tseitin.newEncoder solver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- Tseitin.encodeConj enc [x1,x2]-- ret <- SAT.solveWith solver [x3]- ret @?= True- m <- SAT.getModel solver- SAT.evalLit m x1 @?= True- SAT.evalLit m x2 @?= True- SAT.evalLit m x3 @?= True-- ret <- SAT.solveWith solver [-x3]- ret @?= True- m <- SAT.getModel solver- (SAT.evalLit m x1 && SAT.evalLit m x2) @?= False- SAT.evalLit m x3 @?= False--case_encodeDisj = do- solver <- SAT.newSolver- enc <- Tseitin.newEncoder solver- x1 <- SAT.newVar solver- x2 <- SAT.newVar solver- x3 <- Tseitin.encodeDisj enc [x1,x2]-- ret <- SAT.solveWith solver [x3]- ret @?= True- m <- SAT.getModel solver- (SAT.evalLit m x1 || SAT.evalLit m x2) @?= True- SAT.evalLit m x3 @?= True-- ret <- SAT.solveWith solver [-x3]- ret @?= True- m <- SAT.getModel solver- SAT.evalLit m x1 @?= False- SAT.evalLit m x2 @?= False- SAT.evalLit m x3 @?= False--case_evalFormula = do- solver <- SAT.newSolver- xs <- SAT.newVars solver 5- let f = (x1 .=>. x3 .&&. x4) .||. (x2 .=>. x3 .&&. x5)- where- [x1,x2,x3,x4,x5] = map Atom xs- g :: SAT.Model -> Bool- g m = (not x1 || (x3 && x4)) || (not x2 || (x3 && x5))- where- [x1,x2,x3,x4,x5] = elems m- forM_ (allAssignments 5) $ \m -> do- Tseitin.evalFormula m f @?= g m--prop_PBEncoder_addPBAtLeast = QM.monadicIO $ do- let nv = 4- (lhs,rhs) <- QM.pick $ do- lhs <- arbitraryPBLinSum nv- rhs <- arbitrary- return $ SAT.normalizePBLinAtLeast (lhs, rhs)- strategy <- QM.pick arbitrary- (cnf,defs) <- QM.run $ do- db <- CNFStore.newCNFStore- SAT.newVars_ db nv- tseitin <- Tseitin.newEncoder db- pb <- PB.newEncoderWithStrategy tseitin strategy- SAT.addPBAtLeast pb lhs rhs- cnf <- CNFStore.getCNFFormula db- defs <- Tseitin.getDefinitions tseitin- return (cnf, defs)- forM_ (allAssignments 4) $ \m -> do- let m2 :: Array SAT.Var Bool- m2 = array (1, CNF.numVars cnf) $ assocs m ++ [(v, Tseitin.evalFormula m2 phi) | (v,phi) <- defs]- b1 = SAT.evalPBLinAtLeast m (lhs,rhs)- b2 = evalCNF (array (bounds m2) (assocs m2)) cnf- QM.assert $ b1 == b2--prop_PBEncoder_Sorter_genSorter :: [Int] -> Bool-prop_PBEncoder_Sorter_genSorter xs =- V.toList (PBEncSorter.sortVector (V.fromList xs)) == sort xs--prop_PBEncoder_Sorter_decode_encode :: Property-prop_PBEncoder_Sorter_decode_encode =- forAll arbitrary $ \base' ->- forAll arbitrary $ \(NonNegative x) ->- let base = [b | Positive b <- base']- in PBEncSorter.isRepresentable base x- ==>- (PBEncSorter.decode base . PBEncSorter.encode base) x == x----------------------------------------------------------------------------findMUSAssumptions_case1 :: MUS.Method -> IO ()-findMUSAssumptions_case1 method = do- solver <- SAT.newSolver- [x1,x2,x3] <- SAT.newVars solver 3- sels@[y1,y2,y3,y4,y5,y6] <- SAT.newVars solver 6- SAT.addClause solver [-y1, x1]- SAT.addClause solver [-y2, -x1]- SAT.addClause solver [-y3, -x1, x2]- SAT.addClause solver [-y4, -x2]- SAT.addClause solver [-y5, -x1, x3]- SAT.addClause solver [-y6, -x3]-- ret <- SAT.solveWith solver sels- ret @?= False-- actual <- MUS.findMUSAssumptions solver def{ MUS.optMethod = method }- let actual' = IntSet.map (\x -> x-3) actual- expected = map IntSet.fromList [[1, 2], [1, 3, 4], [1, 5, 6]]- actual' `elem` expected @?= True--case_findMUSAssumptions_Deletion = findMUSAssumptions_case1 MUS.Deletion-case_findMUSAssumptions_Insertion = findMUSAssumptions_case1 MUS.Insertion-case_findMUSAssumptions_QuickXplain = findMUSAssumptions_case1 MUS.QuickXplain----------------------------------------------------------------------------{--c http://sun.iwu.edu/~mliffito/publications/jar_liffiton_CAMUS.pdf-c φ= (x1) ∧ (¬x1) ∧ (¬x1∨x2) ∧ (¬x2) ∧ (¬x1∨x3) ∧ (¬x3)-c MUSes(φ) = {{C1, C2}, {C1, C3, C4}, {C1, C5, C6}}-c MCSes(φ) = {{C1}, {C2, C3, C5}, {C2, C3, C6}, {C2, C4, C5}, {C2, C4, C6}}-p cnf 3 6-1 0--1 0--1 2 0--2 0--1 3 0--3 0--}--allMUSAssumptions_case1 :: MUSEnum.Method -> IO ()-allMUSAssumptions_case1 method = do- solver <- SAT.newSolver- [x1,x2,x3] <- SAT.newVars solver 3- sels@[y1,y2,y3,y4,y5,y6] <- SAT.newVars solver 6- SAT.addClause solver [-y1, x1]- SAT.addClause solver [-y2, -x1]- SAT.addClause solver [-y3, -x1, x2]- SAT.addClause solver [-y4, -x2]- SAT.addClause solver [-y5, -x1, x3]- SAT.addClause solver [-y6, -x3]- (muses, mcses) <- MUSEnum.allMUSAssumptions solver sels def{ MUSEnum.optMethod = method }- Set.fromList muses @?= Set.fromList (map (IntSet.fromList . map (+3)) [[1,2], [1,3,4], [1,5,6]])- Set.fromList mcses @?= Set.fromList (map (IntSet.fromList . map (+3)) [[1], [2,3,5], [2,3,6], [2,4,5], [2,4,6]])--case_allMUSAssumptions_CAMUS = allMUSAssumptions_case1 MUSEnum.CAMUS-case_allMUSAssumptions_DAA = allMUSAssumptions_case1 MUSEnum.DAA-case_allMUSAssumptions_MARCO = allMUSAssumptions_case1 MUSEnum.MARCO-case_allMUSAssumptions_GurvichKhachiyan1999 = allMUSAssumptions_case1 MUSEnum.GurvichKhachiyan1999--{--Boosting a Complete Technique to Find MSS and MUS thanks to a Local Search Oracle-http://www.cril.univ-artois.fr/~piette/IJCAI07_HYCAM.pdf-Example 3.-C0 : (d)-C1 : (b ∨ c)-C2 : (a ∨ b)-C3 : (a ∨ ¬c)-C4 : (¬b ∨ ¬e)-C5 : (¬a ∨ ¬b)-C6 : (a ∨ e)-C7 : (¬a ∨ ¬e)-C8 : (b ∨ e)-C9 : (¬a ∨ b ∨ ¬c)-C10 : (¬a ∨ b ∨ ¬d)-C11 : (a ∨ ¬b ∨ c)-C12 : (a ∨ ¬b ∨ ¬d)--}-allMUSAssumptions_case2 :: MUSEnum.Method -> IO ()-allMUSAssumptions_case2 method = do- solver <- SAT.newSolver- [a,b,c,d,e] <- SAT.newVars solver 5- sels@[y0,y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12] <- SAT.newVars solver 13- SAT.addClause solver [-y0, d]- SAT.addClause solver [-y1, b, c]- SAT.addClause solver [-y2, a, b]- SAT.addClause solver [-y3, a, -c]- SAT.addClause solver [-y4, -b, -e]- SAT.addClause solver [-y5, -a, -b]- SAT.addClause solver [-y6, a, e]- SAT.addClause solver [-y7, -a, -e]- SAT.addClause solver [-y8, b, e]- SAT.addClause solver [-y9, -a, b, -c]- SAT.addClause solver [-y10, -a, b, -d]- SAT.addClause solver [-y11, a, -b, c]- SAT.addClause solver [-y12, a, -b, -d]-- -- Only three of the MUSes (marked with asterisks) are on the paper.- let cores =- [ [y0,y1,y2,y5,y9,y12]- , [y0,y1,y3,y4,y5,y6,y10]- , [y0,y1,y3,y5,y7,y8,y12]- , [y0,y1,y3,y5,y9,y12]- , [y0,y1,y3,y5,y10,y11]- , [y0,y1,y3,y5,y10,y12]- , [y0,y2,y3,y5,y10,y11]- , [y0,y2,y4,y5,y6,y10]- , [y0,y2,y5,y7,y8,y12]- , [y0,y2,y5,y10,y12] -- (*)- , [y1,y2,y4,y5,y6,y9]- , [y1,y3,y4,y5,y6,y7,y8]- , [y1,y3,y4,y5,y6,y9]- , [y1,y3,y5,y7,y8,y11]- , [y1,y3,y5,y9,y11] -- (*)- , [y2,y3,y5,y7,y8,y11]- , [y2,y4,y5,y6,y7,y8] -- (*)- ]-- let remove1 :: [a] -> [[a]]- remove1 [] = []- remove1 (x:xs) = xs : [x : ys | ys <- remove1 xs]- forM_ cores $ \core -> do- ret <- SAT.solveWith solver core- assertBool (show core ++ " should be a core") (not ret)- forM (remove1 core) $ \xs -> do- ret <- SAT.solveWith solver xs- assertBool (show core ++ " should be satisfiable") ret-- (actual,_) <- MUSEnum.allMUSAssumptions solver sels def{ MUSEnum.optMethod = method }- let actual' = Set.fromList actual- expected' = Set.fromList $ map IntSet.fromList $ cores- actual' @?= expected'--case_allMUSAssumptions_2_CAMUS = allMUSAssumptions_case2 MUSEnum.CAMUS-case_allMUSAssumptions_2_DAA = allMUSAssumptions_case2 MUSEnum.DAA-case_allMUSAssumptions_2_MARCO = allMUSAssumptions_case2 MUSEnum.MARCO-case_allMUSAssumptions_2_GurvichKhachiyan1999 = allMUSAssumptions_case2 MUSEnum.GurvichKhachiyan1999--case_allMUSAssumptions_2_HYCAM = do- solver <- SAT.newSolver- [a,b,c,d,e] <- SAT.newVars solver 5- sels@[y0,y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12] <- SAT.newVars solver 13- SAT.addClause solver [-y0, d]- SAT.addClause solver [-y1, b, c]- SAT.addClause solver [-y2, a, b]- SAT.addClause solver [-y3, a, -c]- SAT.addClause solver [-y4, -b, -e]- SAT.addClause solver [-y5, -a, -b]- SAT.addClause solver [-y6, a, e]- SAT.addClause solver [-y7, -a, -e]- SAT.addClause solver [-y8, b, e]- SAT.addClause solver [-y9, -a, b, -c]- SAT.addClause solver [-y10, -a, b, -d]- SAT.addClause solver [-y11, a, -b, c]- SAT.addClause solver [-y12, a, -b, -d]-- -- Only three of the MUSes (marked with asterisks) are on the paper.- let cores =- [ [y0,y1,y2,y5,y9,y12]- , [y0,y1,y3,y4,y5,y6,y10]- , [y0,y1,y3,y5,y7,y8,y12]- , [y0,y1,y3,y5,y9,y12]- , [y0,y1,y3,y5,y10,y11]- , [y0,y1,y3,y5,y10,y12]- , [y0,y2,y3,y5,y10,y11]- , [y0,y2,y4,y5,y6,y10]- , [y0,y2,y5,y7,y8,y12]- , [y0,y2,y5,y10,y12] -- (*)- , [y1,y2,y4,y5,y6,y9]- , [y1,y3,y4,y5,y6,y7,y8]- , [y1,y3,y4,y5,y6,y9]- , [y1,y3,y5,y7,y8,y11]- , [y1,y3,y5,y9,y11] -- (*)- , [y2,y3,y5,y7,y8,y11]- , [y2,y4,y5,y6,y7,y8] -- (*)- ]- mcses =- [ [y0,y1,y7]- , [y0,y1,y8]- , [y0,y3,y4]- , [y0,y3,y6]- , [y0,y4,y11]- , [y0,y6,y11]- , [y0,y7,y9]- , [y0,y8,y9]- , [y1,y2]- , [y1,y7,y10]- , [y1,y8,y10]- , [y2,y3]- , [y3,y4,y12]- , [y3,y6,y12]- , [y4,y11,y12]- , [y5]- , [y6,y11,y12]- , [y7,y9,y10]- , [y8,y9,y10]- ]-- -- HYCAM paper wrongly treated {C3,C8,C10} as a candidate MCS (CoMSS).- -- Its complement {C0,C1,C2,C4,C5,C6,C7,C9,C11,C12} is unsatisfiable- -- and hence not MSS.- ret <- SAT.solveWith solver [y0,y1,y2,y4,y5,y6,y7,y9,y11,y12]- assertBool "failed to prove the bug of HYCAM paper" (not ret)- - let cand = map IntSet.fromList [[y5], [y3,y2], [y0,y1,y2]]- (actual,_) <- MUSEnum.allMUSAssumptions solver sels def{ MUSEnum.optMethod = MUSEnum.CAMUS, MUSEnum.optKnownCSes = cand }- let actual' = Set.fromList $ actual- expected' = Set.fromList $ map IntSet.fromList cores- actual' @?= expected'----------------------------------------------------------------------------prop_ExistentialQuantification :: Property-prop_ExistentialQuantification = QM.monadicIO $ do- phi <- QM.pick arbitraryCNF- xs <- QM.pick $ liftM IntSet.fromList $ sublistOf [1 .. CNF.numVars phi]- let ys = IntSet.fromList [1 .. CNF.numVars phi] IntSet.\\ xs- psi <- QM.run $ ExistentialQuantification.project xs phi- forM_ (replicateM (IntSet.size ys) [False,True]) $ \bs -> do- let m :: SAT.Model- m = array (1, if IntSet.null ys then 0 else IntSet.findMax ys) (zip (IntSet.toList ys) bs)- b1 <- QM.run $ do- solver <- SAT.newSolver- SAT.newVars_ solver (CNF.numVars phi)- forM_ (CNF.clauses phi) $ \c -> SAT.addClause solver c- SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- IntSet.toList ys]- let b2 = evalCNF m psi- QM.assert $ b1 == b2--brauer11_phi :: CNF.CNF-brauer11_phi =- CNF.CNF- { CNF.numVars = 13- , CNF.numClauses = 23- , CNF.clauses =- [- -- μ- [-x2, -y2]- , [-y2, -y1]- , [-x4, -x6, y1]- , [-x3, y4], [x3, -y4]- , [-x4, y3], [x4, -y3]- , [-x5, y6], [x5, -y6]- , [-x6, y5], [x6, -y5]-- -- ξ- , [-x13, x1]- , [-x13, -x2]- , [-x13, x3]- , [-x13, -x4]- , [-x13, x5]- , [-x13, -x6]- , [x13, x1]- , [x13, -x2]- , [x13, -x3]- , [x13, x4]- , [x13, -x5]- , [x13, x6]- ]- }- where- [y1,y2,y3,y4,y5,y6] = [1..6]- [x1,x2,x3,x4,x5,x6,x13] = [7..13]--{--ξ(m'1) = (¬y1 ∧ ¬y3 ∧ y4 ∧ ¬y5 ∧ y6)-ξ(m'2) = (y1 ∧ ¬y2 ∧ ¬y3 ∧ y4 ∧ ¬y5 ∧ y6)-ξ(m'3) = (y1 ∧ ¬y2 ∧ y3 ∧ ¬y4 ∧ y5 ∧ ¬y6)-ω = ¬(ξ(m'1) ∨ ξ(m'2) ∨ ξ(m'3))--}-brauer11_omega :: CNF.CNF-brauer11_omega =- CNF.CNF- { CNF.numVars = 6- , CNF.numClauses = 3- , CNF.clauses =- [ [y1, y3, -y4, y5, -y6]- , [-y1, y2, y3, -y4, y5, -y6]- , [-y1, y2, -y3, y4, -y5, y6]- ]- }- where- [y1,y2,y3,y4,y5,y6] = [1..6]--case_ExistentialQuantification_project_phi :: Assertion-case_ExistentialQuantification_project_phi = do- psi <- ExistentialQuantification.project (IntSet.fromList [7..13]) brauer11_phi- forM_ (replicateM 6 [False,True]) $ \bs -> do- let m :: SAT.Model- m = array (1,13) (zip [1..] bs) - b1 <- do- solver <- SAT.newSolver- SAT.newVars_ solver (CNF.numVars brauer11_phi)- forM_ (CNF.clauses brauer11_phi) $ \c -> SAT.addClause solver c- SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]- let b2 = all (SAT.evalClause m) (CNF.clauses psi)- (b1 == b2) @?= True--case_ExistentialQuantification_project_phi' :: Assertion-case_ExistentialQuantification_project_phi' = do- let [y1,y2,y3,y4,y5,y6] = [1..6]- psi = CNF.CNF- { CNF.numVars = 6- , CNF.numClauses = 8- , CNF.clauses =- [ [-y2, y6]- , [-y3, -y6]- , [y5, y6]- , [y3, -y5]- , [y4, -y6]- , [y1, y6]- , [-y1, -y2]- , [-y4, y6]- ]- }- forM_ (replicateM 6 [False,True]) $ \bs -> do- let m :: SAT.Model- m = array (1,13) (zip [1..] bs)- b1 <- do- solver <- SAT.newSolver- SAT.newVars_ solver (CNF.numVars brauer11_phi)- forM_ (CNF.clauses brauer11_phi) $ \c -> SAT.addClause solver c- SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]- let b2 = all (SAT.evalClause m) (CNF.clauses psi) - (b1 == b2) @?= True--case_shortestImplicants_phi :: Assertion-case_shortestImplicants_phi = do- xss <- ExistentialQuantification.shortestImplicants (IntSet.fromList [1..6]) brauer11_phi- forM_ (replicateM 6 [False,True]) $ \bs -> do- let m :: SAT.Model- m = array (1,6) (zip [1..] bs)- b1 <- do- solver <- SAT.newSolver- SAT.newVars_ solver (CNF.numVars brauer11_phi)- forM_ (CNF.clauses brauer11_phi) $ \c -> SAT.addClause solver c- SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]- let b2 = any (all (SAT.evalLit m) . IntSet.toList) xss- (b1 == b2) @?= True--case_shortestImplicants_phi' :: Assertion-case_shortestImplicants_phi' = do- let [y1,y2,y3,y4,y5,y6] = [1..6]- xss = map IntSet.fromList- [ [-y1, -y3, y4, -y5, y6]- , [y1, -y2, -y3, y4, -y5, y6]- , [y1, -y2, y3, -y4, y5, -y6]- ]- forM_ (replicateM 6 [False,True]) $ \bs -> do- let m :: SAT.Model- m = array (1,6) (zip [1..] bs)- b1 <- do- solver <- SAT.newSolver- SAT.newVars_ solver (CNF.numVars brauer11_phi)- forM_ (CNF.clauses brauer11_phi) $ \c -> SAT.addClause solver c- SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]- let b2 = any (all (SAT.evalLit m) . IntSet.toList) xss- (b1 == b2) @?= True--case_shortestImplicants_omega :: Assertion-case_shortestImplicants_omega = do- xss <- ExistentialQuantification.shortestImplicants (IntSet.fromList [1..6]) brauer11_omega- forM_ (replicateM 6 [False,True]) $ \bs -> do- let m :: SAT.Model- m = array (1,6) (zip [1..] bs)- b1 <- do- solver <- SAT.newSolver- SAT.newVars_ solver (CNF.numVars brauer11_omega)- forM_ (CNF.clauses brauer11_omega) $ \c -> SAT.addClause solver c- SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]- let b2 = any (all (SAT.evalLit m) . IntSet.toList) xss- unless (b1 == b2) $ print m--case_shortestImplicants_omega' :: Assertion-case_shortestImplicants_omega' = do- let [y1,y2,y3,y4,y5,y6] = [1..6]- xss = map IntSet.fromList- [ [y2, -y6]- , [y3, y6]- , [-y5, -y6]- , [-y3, y5]- , [-y4, y6]- , [-y1, -y6]- , [y1, y2]- , [y4, -y6]- ]- forM_ (replicateM 6 [False,True]) $ \bs -> do- let m :: SAT.Model- m = array (1,6) (zip [1..] bs)- b1 <- do- solver <- SAT.newSolver- SAT.newVars_ solver (CNF.numVars brauer11_omega)- forM_ (CNF.clauses brauer11_omega) $ \c -> SAT.addClause solver c- SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]- let b2 = any (all (SAT.evalLit m) . IntSet.toList) xss- (b1 == b2) @?= True-----------------------------------------------------------------------------prop_pb2sat :: Property-prop_pb2sat = QM.monadicIO $ do- pb@(nv,cs) <- QM.pick arbitraryPB- let f (PBRelGE,lhs,rhs) = ([(c,[l]) | (c,l) <- lhs], PBFile.Ge, rhs)- f (PBRelLE,lhs,rhs) = ([(-c,[l]) | (c,l) <- lhs], PBFile.Ge, -rhs)- f (PBRelEQ,lhs,rhs) = ([(c,[l]) | (c,l) <- lhs], PBFile.Eq, rhs)- let opb = PBFile.Formula- { PBFile.pbObjectiveFunction = Nothing- , PBFile.pbNumVars = nv- , PBFile.pbNumConstraints = length cs- , PBFile.pbConstraints = map f cs- }- let (cnf, mforth, mback) = PB2SAT.convert opb-- solver1 <- arbitrarySolver- solver2 <- arbitrarySolver- ret1 <- QM.run $ solvePB solver1 pb- ret2 <- QM.run $ solveCNF solver2 cnf- QM.assert $ isJust ret1 == isJust ret2- case ret1 of- Nothing -> return ()- Just m1 -> do- let m2 = mforth m1- QM.assert $ bounds m2 == (1, CNF.numVars cnf)- QM.assert $ evalCNF m2 cnf- case ret2 of- Nothing -> return ()- Just m2 -> do- let m1 = mback m2- QM.assert $ bounds m1 == (1, nv)- QM.assert $ evalPB m1 pb--prop_wbo2maxsat :: Property-prop_wbo2maxsat = QM.monadicIO $ do- wbo1@(nv,cs,top) <- QM.pick arbitraryWBO- let f (w,(PBRelGE,lhs,rhs)) = (w,([(c,[l]) | (c,l) <- lhs], PBFile.Ge, rhs))- f (w,(PBRelLE,lhs,rhs)) = (w,([(-c,[l]) | (c,l) <- lhs], PBFile.Ge, -rhs))- f (w,(PBRelEQ,lhs,rhs)) = (w,([(c,[l]) | (c,l) <- lhs], PBFile.Eq, rhs))- let wbo1' = PBFile.SoftFormula- { PBFile.wboNumVars = nv- , PBFile.wboNumConstraints = length cs- , PBFile.wboConstraints = map f cs- , PBFile.wboTopCost = top- }- let (wcnf, mforth, mback) = WBO2MaxSAT.convert wbo1'- wbo2 = ( MaxSAT.numVars wcnf- , [ ( if w == MaxSAT.topCost wcnf then Nothing else Just w- , (PBRelGE, [(1,l) | l <- clause], 1)- )- | (w,clause) <- MaxSAT.clauses wcnf- ]- , Nothing- )-- solver1 <- arbitrarySolver- solver2 <- arbitrarySolver- method <- QM.pick arbitrary- ret1 <- QM.run $ optimizeWBO solver1 method wbo1- ret2 <- QM.run $ optimizeWBO solver2 method wbo2- QM.assert $ isJust ret1 == isJust ret2- case ret1 of- Nothing -> return ()- Just (m1,val) -> do- let m2 = mforth m1- QM.assert $ bounds m2 == (1, MaxSAT.numVars wcnf)- QM.assert $ evalWBO m2 wbo2 == Just val- case ret2 of- Nothing -> return ()- Just (m2,val) -> do- let m1 = mback m2- QM.assert $ bounds m1 == (1, nv)- QM.assert $ evalWBO m1 wbo1 == Just val--prop_wbo2pb :: Property-prop_wbo2pb = QM.monadicIO $ do- wbo@(nv,cs,top) <- QM.pick arbitraryWBO- let f (w,(PBRelGE,lhs,rhs)) = (w,([(c,[l]) | (c,l) <- lhs], PBFile.Ge, rhs))- f (w,(PBRelLE,lhs,rhs)) = (w,([(-c,[l]) | (c,l) <- lhs], PBFile.Ge, -rhs))- f (w,(PBRelEQ,lhs,rhs)) = (w,([(c,[l]) | (c,l) <- lhs], PBFile.Eq, rhs))- let wbo' = PBFile.SoftFormula- { PBFile.wboNumVars = nv- , PBFile.wboNumConstraints = length cs- , PBFile.wboConstraints = map f cs- , PBFile.wboTopCost = top- }- let (opb, mforth, mback) = WBO2PB.convert wbo'- obj = fromMaybe [] $ PBFile.pbObjectiveFunction opb- f (lhs, PBFile.Ge, rhs) = (PBRelGE, lhs, rhs)- f (lhs, PBFile.Eq, rhs) = (PBRelEQ, lhs, rhs)- cs2 = map f (PBFile.pbConstraints opb)- pb = (PBFile.pbNumVars opb, obj, cs2)-- solver1 <- arbitrarySolver- solver2 <- arbitrarySolver- method <- QM.pick arbitrary- ret1 <- QM.run $ optimizeWBO solver1 method wbo- ret2 <- QM.run $ optimizePBNLC solver2 method pb- QM.assert $ isJust ret1 == isJust ret2- case ret1 of- Nothing -> return ()- Just (m1,val1) -> do- let m2 = mforth m1- QM.assert $ bounds m2 == (1, PBFile.pbNumVars opb)- QM.assert $ evalPBNLC m2 (PBFile.pbNumVars opb, cs2)- QM.assert $ SAT.evalPBSum m2 obj == val1- case ret2 of- Nothing -> return ()- Just (m2,val2) -> do- let m1 = mback m2- QM.assert $ bounds m1 == (1,nv)- QM.assert $ evalWBO m1 wbo == Just val2--prop_sat2ksat :: Property-prop_sat2ksat = QM.monadicIO $ do- k <- QM.pick $ choose (3,10)-- cnf1 <- QM.pick arbitraryCNF- let (cnf2, mforth, mback) = SAT2KSAT.convert k cnf1-- solver1 <- arbitrarySolver- solver2 <- arbitrarySolver- ret1 <- QM.run $ solveCNF solver1 cnf1- ret2 <- QM.run $ solveCNF solver2 cnf2- QM.assert $ isJust ret1 == isJust ret2- case ret1 of- Nothing -> return ()- Just m1 -> do- let m2 = mforth m1- QM.assert $ bounds m2 == (1, CNF.numVars cnf2)- QM.assert $ evalCNF m2 cnf2- case ret2 of- Nothing -> return ()- Just m2 -> do- let m1 = mback m2- QM.assert $ bounds m1 == (1, CNF.numVars cnf1)- QM.assert $ evalCNF m1 cnf1----------------------------------------------------------------------------instance Arbitrary SAT.LearningStrategy where- arbitrary = arbitraryBoundedEnum--instance Arbitrary SAT.RestartStrategy where- arbitrary = arbitraryBoundedEnum--instance Arbitrary SAT.BranchingStrategy where- arbitrary = arbitraryBoundedEnum--instance Arbitrary SAT.PBHandlerType where- arbitrary = arbitraryBoundedEnum--instance Arbitrary SAT.Config where- arbitrary = do- restartStrategy <- arbitrary- restartFirst <- arbitrary- restartInc <- liftM ((1.01 +) . abs) arbitrary- learningStrategy <- arbitrary- learntSizeFirst <- arbitrary- learntSizeInc <- liftM ((1.01 +) . abs) arbitrary- branchingStrategy <- arbitrary- erwaStepSizeFirst <- choose (0, 1)- erwaStepSizeMin <- choose (0, 1)- erwaStepSizeDec <- choose (0, 1)- pbhandler <- arbitrary- ccmin <- choose (0,2)- phaseSaving <- arbitrary- forwardSubsumptionRemoval <- arbitrary- backwardSubsumptionRemoval <- arbitrary- randomFreq <- choose (0,1)- splitClausePart <- arbitrary- return $ def- { SAT.configRestartStrategy = restartStrategy- , SAT.configRestartFirst = restartFirst- , SAT.configRestartInc = restartInc- , SAT.configLearningStrategy = learningStrategy- , SAT.configLearntSizeFirst = learntSizeFirst- , SAT.configLearntSizeInc = learntSizeInc- , SAT.configPBHandlerType = pbhandler- , SAT.configCCMin = ccmin- , SAT.configBranchingStrategy = branchingStrategy- , SAT.configERWAStepSizeFirst = erwaStepSizeFirst- , SAT.configERWAStepSizeDec = erwaStepSizeDec- , SAT.configERWAStepSizeMin = erwaStepSizeMin- , SAT.configEnablePhaseSaving = phaseSaving- , SAT.configEnableForwardSubsumptionRemoval = forwardSubsumptionRemoval- , SAT.configEnableBackwardSubsumptionRemoval = backwardSubsumptionRemoval- , SAT.configRandomFreq = randomFreq- , SAT.configEnablePBSplitClausePart = splitClausePart- }--arbitrarySolver :: QM.PropertyM IO SAT.Solver-arbitrarySolver = do- seed <- QM.pick arbitrary- config <- QM.pick arbitrary- QM.run $ do- solver <- SAT.newSolverWithConfig config{ SAT.configCheckModel = True }- SAT.setRandomGen solver =<< Rand.initialize (V.singleton seed)- return solver--arbitraryOptimizer :: SAT.Solver -> SAT.PBLinSum -> QM.PropertyM IO PBO.Optimizer-arbitraryOptimizer solver obj = do- method <- QM.pick arbitrary- QM.run $ do- opt <- PBO.newOptimizer solver obj- PBO.setMethod opt method- return opt--instance Arbitrary PBO.Method where- arbitrary = arbitraryBoundedEnum--instance Arbitrary PB.Strategy where- arbitrary = arbitraryBoundedEnum---- -----------------------------------------------------------------------#if !MIN_VERSION_QuickCheck(2,8,0)-sublistOf :: [a] -> Gen [a]-sublistOf xs = filterM (\_ -> choose (False, True)) xs-#endif+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.SAT (satTestGroup) where++import Control.Monad+import Data.Array.IArray+import Data.Default.Class+import qualified Data.Vector as V+import qualified System.Random.MWC as Rand++import Test.Tasty+import Test.Tasty.QuickCheck+import Test.Tasty.HUnit+import Test.Tasty.TH+import qualified Test.QuickCheck.Monadic as QM++import ToySolver.Data.LBool+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.SAT as SAT++import Test.SAT.Utils++prop_solveCNF :: Property+prop_solveCNF = QM.monadicIO $ do+ cnf <- QM.pick arbitraryCNF+ solver <- arbitrarySolver+ ret <- QM.run $ solveCNF solver cnf+ case ret of+ Just m -> QM.assert $ evalCNF m cnf+ Nothing -> do+ forM_ (allAssignments (CNF.cnfNumVars cnf)) $ \m -> do+ QM.assert $ not (evalCNF m cnf)++prop_solvePB :: Property+prop_solvePB = QM.monadicIO $ do+ prob@(nv,_) <- QM.pick arbitraryPB+ solver <- arbitrarySolver+ ret <- QM.run $ solvePB solver prob+ case ret of+ Just m -> QM.assert $ evalPB m prob+ Nothing -> do+ forM_ (allAssignments nv) $ \m -> do+ QM.assert $ not (evalPB m prob)++prop_optimizePBO :: Property+prop_optimizePBO = QM.monadicIO $ do+ prob@(nv,_) <- QM.pick arbitraryPB+ obj <- QM.pick $ arbitraryPBLinSum nv+ solver <- arbitrarySolver+ opt <- arbitraryOptimizer solver obj+ ret <- QM.run $ optimizePBO solver opt prob+ case ret of+ Just (m, v) -> do+ QM.assert $ evalPB m prob+ QM.assert $ SAT.evalPBLinSum m obj == v+ forM_ (allAssignments nv) $ \m2 -> do+ QM.assert $ not (evalPB m2 prob) || SAT.evalPBLinSum m obj <= SAT.evalPBLinSum m2 obj+ Nothing -> do+ forM_ (allAssignments nv) $ \m -> do+ QM.assert $ not (evalPB m prob)++prop_solvePBNLC :: Property+prop_solvePBNLC = QM.monadicIO $ do+ prob@(nv,_) <- QM.pick arbitraryPBNLC+ solver <- arbitrarySolver+ ret <- QM.run $ solvePBNLC solver prob+ case ret of+ Just m -> QM.assert $ evalPBNLC m prob+ Nothing -> do+ forM_ (allAssignments nv) $ \m -> do+ QM.assert $ not (evalPBNLC m prob)+++prop_solveXOR :: Property+prop_solveXOR = QM.monadicIO $ do+ prob@(nv,_) <- QM.pick arbitraryXOR+ solver <- arbitrarySolver+ ret <- QM.run $ solveXOR solver prob+ case ret of+ Just m -> QM.assert $ evalXOR m prob+ Nothing -> do+ forM_ (allAssignments nv) $ \m -> do+ QM.assert $ not (evalXOR m prob)++solveXOR :: SAT.Solver -> (Int,[SAT.XORClause]) -> IO (Maybe SAT.Model)+solveXOR solver (nv,cs) = do+ SAT.modifyConfig solver $ \config -> config{ SAT.configCheckModel = True }+ SAT.newVars_ solver nv+ forM_ cs $ \c -> SAT.addXORClause solver (fst c) (snd c)+ ret <- SAT.solve solver+ if ret then do+ m <- SAT.getModel solver+ return (Just m)+ else do+ return Nothing++-- should be SAT+case_solve_SAT :: Assertion+case_solve_SAT = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addClause solver [x1, x2] -- x1 or x2+ SAT.addClause solver [x1, -x2] -- x1 or not x2+ SAT.addClause solver [-x1, -x2] -- not x1 or not x2+ ret <- SAT.solve solver+ ret @?= True++-- shuld be UNSAT+case_solve_UNSAT :: Assertion+case_solve_UNSAT = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addClause solver [x1, x2] -- x1 or x2+ SAT.addClause solver [-x1, x2] -- not x1 or x2+ SAT.addClause solver [x1, -x2] -- x1 or not x2+ SAT.addClause solver [-x1, -x2] -- not x2 or not x2+ ret <- SAT.solve solver+ ret @?= False++-- top level でいきなり矛盾+case_root_inconsistent :: Assertion+case_root_inconsistent = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ SAT.addClause solver [x1]+ SAT.addClause solver [-x1]+ ret <- SAT.solve solver -- unsat+ ret @?= False++-- incremental に制約を追加+case_incremental_solving :: Assertion+case_incremental_solving = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addClause solver [x1, x2] -- x1 or x2+ SAT.addClause solver [x1, -x2] -- x1 or not x2+ SAT.addClause solver [-x1, -x2] -- not x1 or not x2+ ret <- SAT.solve solver -- sat+ ret @?= True++ SAT.addClause solver [-x1, x2] -- not x1 or x2+ ret <- SAT.solve solver -- unsat+ ret @?= False++-- 制約なし+case_empty_constraint :: Assertion+case_empty_constraint = do+ solver <- SAT.newSolver+ ret <- SAT.solve solver+ ret @?= True++-- 空の節+case_empty_claue :: Assertion+case_empty_claue = do+ solver <- SAT.newSolver+ SAT.addClause solver []+ ret <- SAT.solve solver+ ret @?= False++-- 自明に真な節+case_excluded_middle_claue :: Assertion+case_excluded_middle_claue = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ SAT.addClause solver [x1, -x1] -- x1 or not x1+ ret <- SAT.solve solver+ ret @?= True++-- 冗長な節+case_redundant_clause :: Assertion+case_redundant_clause = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ SAT.addClause solver [x1,x1] -- x1 or x1+ ret <- SAT.solve solver+ ret @?= True++case_instantiateClause :: Assertion+case_instantiateClause = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addClause solver [x1]+ SAT.addClause solver [x1,x2]+ SAT.addClause solver [-x1,x2]+ ret <- SAT.solve solver+ ret @?= True++case_instantiateAtLeast :: Assertion+case_instantiateAtLeast = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ x4 <- SAT.newVar solver+ SAT.addClause solver [x1]++ SAT.addAtLeast solver [x1,x2,x3,x4] 2+ ret <- SAT.solve solver+ ret @?= True++ SAT.addAtLeast solver [-x1,-x2,-x3,-x4] 2+ ret <- SAT.solve solver+ ret @?= True++case_inconsistent_AtLeast :: Assertion+case_inconsistent_AtLeast = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addAtLeast solver [x1,x2] 3+ ret <- SAT.solve solver -- unsat+ ret @?= False++case_trivial_AtLeast :: Assertion+case_trivial_AtLeast = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addAtLeast solver [x1,x2] 0+ ret <- SAT.solve solver+ ret @?= True++ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addAtLeast solver [x1,x2] (-1)+ ret <- SAT.solve solver+ ret @?= True++case_AtLeast_1 :: Assertion+case_AtLeast_1 = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ SAT.addAtLeast solver [x1,x2,x3] 2+ SAT.addAtLeast solver [-x1,-x2,-x3] 2+ ret <- SAT.solve solver -- unsat+ ret @?= False++case_AtLeast_2 :: Assertion+case_AtLeast_2 = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ x4 <- SAT.newVar solver+ SAT.addAtLeast solver [x1,x2,x3,x4] 2+ SAT.addClause solver [-x1,-x2]+ SAT.addClause solver [-x1,-x3]+ ret <- SAT.solve solver+ ret @?= True++case_AtLeast_3 :: Assertion+case_AtLeast_3 = do+ forM_ [(-1) .. 3] $ \n -> do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addAtLeast solver [x1,x2] n+ ret <- SAT.solve solver+ assertEqual ("case_AtLeast3_" ++ show n) (n <= 2) ret++-- from http://www.cril.univ-artois.fr/PB11/format.pdf+case_PB_sample1 :: Assertion+case_PB_sample1 = do+ solver <- SAT.newSolver++ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ x4 <- SAT.newVar solver+ x5 <- SAT.newVar solver++ SAT.addPBAtLeast solver [(1,x1),(4,x2),(-2,x5)] 2+ SAT.addPBAtLeast solver [(-1,x1),(4,x2),(-2,x5)] 3+ SAT.addPBAtLeast solver [(12345678901234567890,x4),(4,x3)] 10+ SAT.addPBExactly solver [(2,x2),(3,x4),(2,x1),(3,x5)] 5++ ret <- SAT.solve solver+ ret @?= True++-- 一部の変数を否定に置き換えたもの+case_PB_sample1' :: Assertion+case_PB_sample1' = do+ solver <- SAT.newSolver++ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ x4 <- SAT.newVar solver+ x5 <- SAT.newVar solver++ SAT.addPBAtLeast solver [(1,x1),(4,-x2),(-2,x5)] 2+ SAT.addPBAtLeast solver [(-1,x1),(4,-x2),(-2,x5)] 3+ SAT.addPBAtLeast solver [(12345678901234567890,-x4),(4,x3)] 10+ SAT.addPBExactly solver [(2,-x2),(3,-x4),(2,x1),(3,x5)] 5++ ret <- SAT.solve solver+ ret @?= True++-- いきなり矛盾したPB制約+case_root_inconsistent_PB :: Assertion+case_root_inconsistent_PB = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addPBAtLeast solver [(2,x1),(3,x2)] 6+ ret <- SAT.solve solver+ ret @?= False++case_pb_propagate :: Assertion+case_pb_propagate = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addPBAtLeast solver [(1,x1),(3,x2)] 3+ SAT.addClause solver [-x1]+ ret <- SAT.solve solver+ ret @?= True++case_solveWith_1 :: Assertion+case_solveWith_1 = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ SAT.addClause solver [x1, x2] -- x1 or x2+ SAT.addClause solver [x1, -x2] -- x1 or not x2+ SAT.addClause solver [-x1, -x2] -- not x1 or not x2+ SAT.addClause solver [-x3, -x1, x2] -- not x3 or not x1 or x2++ ret <- SAT.solve solver -- sat+ ret @?= True++ ret <- SAT.solveWith solver [x3] -- unsat+ ret @?= False++ ret <- SAT.solve solver -- sat+ ret @?= True++case_solveWith_2 :: Assertion+case_solveWith_2 = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addClause solver [-x1, x2] -- -x1 or x2+ SAT.addClause solver [x1] -- x1++ ret <- SAT.solveWith solver [x2]+ ret @?= True++ ret <- SAT.solveWith solver [-x2]+ ret @?= False++case_getVarFixed :: Assertion+case_getVarFixed = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ SAT.addClause solver [x1,x2]++ ret <- SAT.getVarFixed solver x1+ ret @?= lUndef++ SAT.addClause solver [-x1]+ + ret <- SAT.getVarFixed solver x1+ ret @?= lFalse++ ret <- SAT.getLitFixed solver (-x1)+ ret @?= lTrue++ ret <- SAT.getLitFixed solver x2+ ret @?= lTrue++case_getAssumptionsImplications_case1 :: Assertion+case_getAssumptionsImplications_case1 = do+ solver <- SAT.newSolver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ SAT.addClause solver [x1,x2,x3]++ SAT.addClause solver [-x1]+ ret <- SAT.solveWith solver [-x2]+ ret @?= True+ xs <- SAT.getAssumptionsImplications solver+ xs @?= [x3]++prop_getAssumptionsImplications :: Property+prop_getAssumptionsImplications = QM.monadicIO $ do+ cnf <- QM.pick arbitraryCNF+ solver <- arbitrarySolver+ ls <- QM.pick $ liftM concat $ mapM (\v -> elements [[],[-v],[v]]) [1..CNF.cnfNumVars cnf]+ ret <- QM.run $ do+ SAT.newVars_ solver (CNF.cnfNumVars cnf)+ forM_ (CNF.cnfClauses cnf) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ SAT.solveWith solver ls+ when ret $ do+ xs <- QM.run $ SAT.getAssumptionsImplications solver+ forM_ xs $ \x -> do+ ret2 <- QM.run $ SAT.solveWith solver (-x : ls)+ QM.assert $ not ret2++------------------------------------------------------------------------++case_xor_case1 :: Assertion+case_xor_case1 = do+ solver <- SAT.newSolver+ SAT.modifyConfig solver $ \config -> config{ SAT.configCheckModel = True }+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ SAT.addXORClause solver [x1, x2] True -- x1 ⊕ x2 = True+ SAT.addXORClause solver [x2, x3] True -- x2 ⊕ x3 = True+ SAT.addXORClause solver [x3, x1] True -- x3 ⊕ x1 = True+ ret <- SAT.solve solver+ ret @?= False++case_xor_case2 :: Assertion+case_xor_case2 = do+ solver <- SAT.newSolver+ SAT.modifyConfig solver $ \config -> config{ SAT.configCheckModel = True }+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ SAT.addXORClause solver [x1, x2] True -- x1 ⊕ x2 = True+ SAT.addXORClause solver [x1, x3] True -- x1 ⊕ x3 = True+ SAT.addClause solver [x2]++ ret <- SAT.solve solver+ ret @?= True+ m <- SAT.getModel solver+ m ! x1 @?= False+ m ! x2 @?= True+ m ! x3 @?= True++case_xor_case3 :: Assertion+case_xor_case3 = do+ solver <- SAT.newSolver+ SAT.modifyConfig solver $ \config -> config{ SAT.configCheckModel = True }+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- SAT.newVar solver+ x4 <- SAT.newVar solver+ SAT.addXORClause solver [x1,x2,x3,x4] True+ SAT.addAtLeast solver [x1,x2,x3,x4] 2+ ret <- SAT.solve solver+ ret @?= True++------------------------------------------------------------------------++-- from "Pueblo: A Hybrid Pseudo-Boolean SAT Solver"+-- clauseがunitになるレベルで、PB制約が違反状態のままという例。+case_hybridLearning_1 :: Assertion+case_hybridLearning_1 = do+ solver <- SAT.newSolver+ [x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11] <- replicateM 11 (SAT.newVar solver)++ SAT.addClause solver [x11, x10, x9] -- C1+ SAT.addClause solver [x8, x7, x6] -- C2+ SAT.addClause solver [x5, x4, x3] -- C3+ SAT.addAtLeast solver [-x2, -x5, -x8, -x11] 3 -- C4+ SAT.addAtLeast solver [-x1, -x4, -x7, -x10] 3 -- C5++ replicateM_ 3 (SAT.varBumpActivity solver x3)+ SAT.setVarPolarity solver x3 False++ replicateM_ 2 (SAT.varBumpActivity solver x6)+ SAT.setVarPolarity solver x6 False++ replicateM_ 1 (SAT.varBumpActivity solver x9)+ SAT.setVarPolarity solver x9 False++ SAT.setVarPolarity solver x1 True++ SAT.modifyConfig solver $ \config -> config{ SAT.configLearningStrategy = SAT.LearningHybrid }+ ret <- SAT.solve solver+ ret @?= True++-- from "Pueblo: A Hybrid Pseudo-Boolean SAT Solver"+-- clauseがunitになるレベルで、PB制約が違反状態のままという例。+-- さらに、学習したPB制約はunitにはならない。+case_hybridLearning_2 :: Assertion+case_hybridLearning_2 = do+ solver <- SAT.newSolver+ [x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12] <- replicateM 12 (SAT.newVar solver)++ SAT.addClause solver [x11, x10, x9] -- C1+ SAT.addClause solver [x8, x7, x6] -- C2+ SAT.addClause solver [x5, x4, x3] -- C3+ SAT.addAtLeast solver [-x2, -x5, -x8, -x11] 3 -- C4+ SAT.addAtLeast solver [-x1, -x4, -x7, -x10] 3 -- C5++ SAT.addClause solver [x12, -x3]+ SAT.addClause solver [x12, -x6]+ SAT.addClause solver [x12, -x9]++ SAT.varBumpActivity solver x12+ SAT.setVarPolarity solver x12 False++ SAT.modifyConfig solver $ \config -> config{ SAT.configLearningStrategy = SAT.LearningHybrid }+ ret <- SAT.solve solver+ ret @?= True++-- regression test for the bug triggered by normalized-blast-floppy1-8.ucl.opb.bz2+case_addPBAtLeast_regression :: Assertion+case_addPBAtLeast_regression = do+ solver <- SAT.newSolver+ [x1,x2,x3,x4] <- replicateM 4 (SAT.newVar solver)+ SAT.addClause solver [-x1]+ SAT.addClause solver [-x2, -x3]+ SAT.addClause solver [-x2, -x4]+ SAT.addPBAtLeast solver [(1,x1),(2,x2),(1,x3),(1,x4)] 3+ ret <- SAT.solve solver+ ret @?= False++-- https://github.com/msakai/toysolver/issues/22+case_issue22 :: Assertion+case_issue22 = do+ let config = def+ { SAT.configLearningStrategy = SAT.LearningHybrid+ , SAT.configCCMin = 2+ , SAT.configBranchingStrategy = SAT.BranchingLRB+ , SAT.configRandomFreq = 0.2816351099559239+ , SAT.configPBHandlerType = SAT.PBHandlerTypeCounter+ }+ solver <- SAT.newSolverWithConfig config+ _ <- SAT.newVars solver 14+ SAT.addClause solver [-7,-1]+ SAT.addClause solver [-9,-4]+ SAT.addClause solver [-9,1]+ SAT.addClause solver [-10,-1]+ SAT.addClause solver [-11,-1]+ SAT.addClause solver [-12,-4]+ SAT.addClause solver [-12,4]+ SAT.addClause solver [-13,-3]+ SAT.addClause solver [-13,-1]+ SAT.addClause solver [-13,3]+ SAT.addClause solver [-14,-1]+ SAT.addPBAtLeast solver [ (1,-14), (10,13), (7,12), (13,-11), (14,-10), (16,9), (8,8), (9,-7)] 38+ SAT.addPBAtLeast solver [(-1,-14),(-10,13),(-7,12),(-13,-11),(-14,-10),(-16,9),(-8,8),(-9,-7)] (-38)+ SAT.setRandomGen solver =<< Rand.initialize (V.singleton 71)+ _ <- SAT.solve solver+ return ()+{-+Scenario:+decide 4@1+deduce -12 by [-12,-4]+deduce -9 by [-9,-4]+decide 1@2+deduce -14 by [-14,-1]+deduce -13 by [-13,-1]+deduce -11 by [-11,-1]+deduce -10 by [-10,-1]+deduce -7 by [-7,-1]+deduce 8 by [(16,9),(14,-10),(13,-11),(10,13),(9,-7),(8,8),(7,12),(1,-14)] >= 38+conflict: [(16,-9),(14,10),(13,11),(10,-13),(9,7),(8,-8),(7,-12),(1,14)] >= 40+conflict analysis yields+ [-1,9,12] @1, and+ [(1,14),(2,-13),(1,12),(8,-9),(17,-1)],17) >= 17 @1 (but it should be @0)+backtrack to @1+deduce -1 by [-1,9,12]+decide 3@3+deduce -13 by [-13,-3]+deduce -10, -11, -7, 8 by [(16,9),(14,-10),(13,-11),(10,13),(9,-7),(8,8),(7,12),(1,-14)] >= 38+conflict [(16,-9),(14,10),(13,11),(10,-13),(9,7),(8,-8),(7,-12),(1,14)] >= 40+conflict analysis yields+ [13,9,12] @1 and+ [(1,14),(7,13),(7,12),(7,9)] >= 7 @1 (but it should be @0)+backtrack to @1+deduce 13 by [13,9,12]+deduce 3 by [3,-13]+conflict [-3,-13]+conflict analysis yields+ -13 @ 0+decide -7@1+decide -14@2+deduce -1 by [(17,-1),(8,-9),(2,-13),(1,14),(1,12)] >= 17+deduce -9 by [-9,1]+deduce 12 by [12,9,13]+deduce 4 by [4,-12]+conflict: [-4,-12]+conflict analysis yields [] and that causes error+-} ------------------------------------------------------------------------ -- Test harness
+ test/Test/SAT/Encoder.hs view
@@ -0,0 +1,193 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.SAT.Encoder (satEncoderTestGroup) where++import Control.Monad+import Data.Array.IArray+import Data.List+import Data.Maybe+import qualified Data.Vector as V++import Test.Tasty+import Test.Tasty.QuickCheck hiding ((.&&.), (.||.))+import Test.Tasty.HUnit+import Test.Tasty.TH+import qualified Test.QuickCheck.Monadic as QM++import ToySolver.Data.BoolExpr+import ToySolver.Data.Boolean+import qualified ToySolver.FileFormat.CNF as CNF+import qualified ToySolver.SAT as SAT+import qualified ToySolver.SAT.Types as SAT+import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin+import qualified ToySolver.SAT.Encoder.Cardinality as Cardinality+import qualified ToySolver.SAT.Encoder.PB as PB+import qualified ToySolver.SAT.Encoder.PB.Internal.Sorter as PBEncSorter+import qualified ToySolver.SAT.Store.CNF as CNFStore++import Test.SAT.Utils++case_addFormula :: Assertion+case_addFormula = do+ solver <- SAT.newSolver+ enc <- Tseitin.newEncoder solver++ [x1,x2,x3,x4,x5] <- replicateM 5 $ liftM Atom $ SAT.newVar solver+ Tseitin.addFormula enc $ orB [x1 .=>. x3 .&&. x4, x2 .=>. x3 .&&. x5]+ -- x6 = x3 ∧ x4+ -- x7 = x3 ∧ x5+ Tseitin.addFormula enc $ x1 .||. x2+ Tseitin.addFormula enc $ x4 .=>. notB x5+ ret <- SAT.solve solver+ ret @?= True++ Tseitin.addFormula enc $ x2 .<=>. x4+ ret <- SAT.solve solver+ ret @?= True++ Tseitin.addFormula enc $ x1 .<=>. x5+ ret <- SAT.solve solver+ ret @?= True++ Tseitin.addFormula enc $ notB x1 .=>. x3 .&&. x5+ ret <- SAT.solve solver+ ret @?= True++ Tseitin.addFormula enc $ notB x2 .=>. x3 .&&. x4+ ret <- SAT.solve solver+ ret @?= False++case_addFormula_Peirces_Law :: Assertion+case_addFormula_Peirces_Law = do+ solver <- SAT.newSolver+ enc <- Tseitin.newEncoder solver+ [x1,x2] <- replicateM 2 $ liftM Atom $ SAT.newVar solver+ Tseitin.addFormula enc $ notB $ ((x1 .=>. x2) .=>. x1) .=>. x1+ ret <- SAT.solve solver+ ret @?= False++case_encodeConj :: Assertion+case_encodeConj = do+ solver <- SAT.newSolver+ enc <- Tseitin.newEncoder solver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- Tseitin.encodeConj enc [x1,x2]++ ret <- SAT.solveWith solver [x3]+ ret @?= True+ m <- SAT.getModel solver+ SAT.evalLit m x1 @?= True+ SAT.evalLit m x2 @?= True+ SAT.evalLit m x3 @?= True++ ret <- SAT.solveWith solver [-x3]+ ret @?= True+ m <- SAT.getModel solver+ (SAT.evalLit m x1 && SAT.evalLit m x2) @?= False+ SAT.evalLit m x3 @?= False++case_encodeDisj :: Assertion+case_encodeDisj = do+ solver <- SAT.newSolver+ enc <- Tseitin.newEncoder solver+ x1 <- SAT.newVar solver+ x2 <- SAT.newVar solver+ x3 <- Tseitin.encodeDisj enc [x1,x2]++ ret <- SAT.solveWith solver [x3]+ ret @?= True+ m <- SAT.getModel solver+ (SAT.evalLit m x1 || SAT.evalLit m x2) @?= True+ SAT.evalLit m x3 @?= True++ ret <- SAT.solveWith solver [-x3]+ ret @?= True+ m <- SAT.getModel solver+ SAT.evalLit m x1 @?= False+ SAT.evalLit m x2 @?= False+ SAT.evalLit m x3 @?= False++case_evalFormula :: Assertion+case_evalFormula = do+ solver <- SAT.newSolver+ xs <- SAT.newVars solver 5+ let f = (x1 .=>. x3 .&&. x4) .||. (x2 .=>. x3 .&&. x5)+ where+ [x1,x2,x3,x4,x5] = map Atom xs+ g :: SAT.Model -> Bool+ g m = (not x1 || (x3 && x4)) || (not x2 || (x3 && x5))+ where+ [x1,x2,x3,x4,x5] = elems m+ forM_ (allAssignments 5) $ \m -> do+ Tseitin.evalFormula m f @?= g m++prop_PBEncoder_addPBAtLeast :: Property+prop_PBEncoder_addPBAtLeast = QM.monadicIO $ do+ let nv = 4+ (lhs,rhs) <- QM.pick $ do+ lhs <- arbitraryPBLinSum nv+ rhs <- arbitrary+ return $ SAT.normalizePBLinAtLeast (lhs, rhs)+ strategy <- QM.pick arbitrary+ (cnf,defs) <- QM.run $ do+ db <- CNFStore.newCNFStore+ SAT.newVars_ db nv+ tseitin <- Tseitin.newEncoder db+ pb <- PB.newEncoderWithStrategy tseitin strategy+ SAT.addPBAtLeast pb lhs rhs+ cnf <- CNFStore.getCNFFormula db+ defs <- Tseitin.getDefinitions tseitin+ return (cnf, defs)+ forM_ (allAssignments 4) $ \m -> do+ let m2 :: Array SAT.Var Bool+ m2 = array (1, CNF.cnfNumVars cnf) $ assocs m ++ [(v, Tseitin.evalFormula m2 phi) | (v,phi) <- defs]+ b1 = SAT.evalPBLinAtLeast m (lhs,rhs)+ b2 = evalCNF (array (bounds m2) (assocs m2)) cnf+ QM.assert $ b1 == b2++prop_PBEncoder_Sorter_genSorter :: [Int] -> Bool+prop_PBEncoder_Sorter_genSorter xs =+ V.toList (PBEncSorter.sortVector (V.fromList xs)) == sort xs++prop_PBEncoder_Sorter_decode_encode :: Property+prop_PBEncoder_Sorter_decode_encode =+ forAll arbitrary $ \base' ->+ forAll arbitrary $ \(NonNegative x) ->+ let base = [b | Positive b <- base']+ in PBEncSorter.isRepresentable base x+ ==>+ (PBEncSorter.decode base . PBEncSorter.encode base) x == x++prop_CardinalityEncoder_addAtLeast :: Property+prop_CardinalityEncoder_addAtLeast = QM.monadicIO $ do+ let nv = 4+ (lhs,rhs) <- QM.pick $ do+ lhs <- liftM catMaybes $ forM [1..nv] $ \i -> do+ b <- arbitrary+ if b then+ Just <$> elements [i, -i]+ else+ return Nothing+ rhs <- choose (-1, nv+2)+ return $ (lhs, rhs)+ strategy <- QM.pick arbitrary+ (cnf,defs) <- QM.run $ do+ db <- CNFStore.newCNFStore+ SAT.newVars_ db nv+ tseitin <- Tseitin.newEncoder db+ card <- Cardinality.newEncoderWithStrategy tseitin strategy+ SAT.addAtLeast card lhs rhs+ cnf <- CNFStore.getCNFFormula db+ defs <- Tseitin.getDefinitions tseitin+ return (cnf, defs)+ forM_ (allAssignments nv) $ \m -> do+ let m2 :: Array SAT.Var Bool+ m2 = array (1, CNF.cnfNumVars cnf) $ assocs m ++ [(v, Tseitin.evalFormula m2 phi) | (v,phi) <- defs]+ b1 = SAT.evalAtLeast m (lhs,rhs)+ b2 = evalCNF (array (bounds m2) (assocs m2)) cnf+ QM.assert $ b1 == b2++satEncoderTestGroup :: TestTree+satEncoderTestGroup = $(testGroupGenerator)
+ test/Test/SAT/ExistentialQuantification.hs view
@@ -0,0 +1,204 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.SAT.ExistentialQuantification (satExistentialQuantificationTestGroup) where++import Control.Monad+import qualified Data.IntSet as IntSet++import Test.Tasty+import Test.Tasty.QuickCheck+import Test.Tasty.HUnit+import Test.Tasty.TH+import qualified Test.QuickCheck.Monadic as QM++import qualified ToySolver.SAT as SAT+import qualified ToySolver.SAT.ExistentialQuantification as ExistentialQuantification+import qualified ToySolver.FileFormat.CNF as CNF++import Test.SAT.Utils++prop_ExistentialQuantification :: Property+prop_ExistentialQuantification = QM.monadicIO $ do+ phi <- QM.pick arbitraryCNF+ xs <- QM.pick $ liftM IntSet.fromList $ sublistOf [1 .. CNF.cnfNumVars phi]+ let ys = IntSet.fromList [1 .. CNF.cnfNumVars phi] IntSet.\\ xs+ psi <- QM.run $ ExistentialQuantification.project xs phi+ forM_ (allAssignments (if IntSet.null ys then 0 else IntSet.findMax ys)) $ \m -> do+ b1 <- QM.run $ do+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars phi)+ forM_ (CNF.cnfClauses phi) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- IntSet.toList ys]+ let b2 = evalCNF m psi+ QM.assert $ b1 == b2++brauer11_phi :: CNF.CNF+brauer11_phi =+ CNF.CNF+ { CNF.cnfNumVars = 13+ , CNF.cnfNumClauses = 23+ , CNF.cnfClauses = fmap SAT.packClause+ [+ -- μ+ [-x2, -y2]+ , [-y2, -y1]+ , [-x4, -x6, y1]+ , [-x3, y4], [x3, -y4]+ , [-x4, y3], [x4, -y3]+ , [-x5, y6], [x5, -y6]+ , [-x6, y5], [x6, -y5]++ -- ξ+ , [-x13, x1]+ , [-x13, -x2]+ , [-x13, x3]+ , [-x13, -x4]+ , [-x13, x5]+ , [-x13, -x6]+ , [x13, x1]+ , [x13, -x2]+ , [x13, -x3]+ , [x13, x4]+ , [x13, -x5]+ , [x13, x6]+ ]+ }+ where+ [y1,y2,y3,y4,y5,y6] = [1..6]+ [x1,x2,x3,x4,x5,x6,x13] = [7..13]++{-+ξ(m'1) = (¬y1 ∧ ¬y3 ∧ y4 ∧ ¬y5 ∧ y6)+ξ(m'2) = (y1 ∧ ¬y2 ∧ ¬y3 ∧ y4 ∧ ¬y5 ∧ y6)+ξ(m'3) = (y1 ∧ ¬y2 ∧ y3 ∧ ¬y4 ∧ y5 ∧ ¬y6)+ω = ¬(ξ(m'1) ∨ ξ(m'2) ∨ ξ(m'3))+-}+brauer11_omega :: CNF.CNF+brauer11_omega =+ CNF.CNF+ { CNF.cnfNumVars = 6+ , CNF.cnfNumClauses = 3+ , CNF.cnfClauses = map SAT.packClause+ [ [y1, y3, -y4, y5, -y6]+ , [-y1, y2, y3, -y4, y5, -y6]+ , [-y1, y2, -y3, y4, -y5, y6]+ ]+ }+ where+ [y1,y2,y3,y4,y5,y6] = [1..6]++case_ExistentialQuantification_project_phi :: Assertion+case_ExistentialQuantification_project_phi = do+ psi <- ExistentialQuantification.project (IntSet.fromList [7..13]) brauer11_phi+ forM_ (allAssignments 6) $ \m -> do+ b1 <- do+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars brauer11_phi)+ forM_ (CNF.cnfClauses brauer11_phi) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]+ let b2 = all (SAT.evalClause m . SAT.unpackClause) (CNF.cnfClauses psi)+ (b1 == b2) @?= True++case_ExistentialQuantification_project_phi' :: Assertion+case_ExistentialQuantification_project_phi' = do+ let [y1,y2,y3,y4,y5,y6] = [1..6]+ psi = CNF.CNF+ { CNF.cnfNumVars = 6+ , CNF.cnfNumClauses = 8+ , CNF.cnfClauses = map SAT.packClause+ [ [-y2, y6]+ , [-y3, -y6]+ , [y5, y6]+ , [y3, -y5]+ , [y4, -y6]+ , [y1, y6]+ , [-y1, -y2]+ , [-y4, y6]+ ]+ }+ forM_ (allAssignments 6) $ \m -> do+ b1 <- do+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars brauer11_phi)+ forM_ (CNF.cnfClauses brauer11_phi) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]+ let b2 = all (SAT.evalClause m . SAT.unpackClause) (CNF.cnfClauses psi) + (b1 == b2) @?= True++case_shortestImplicantsE_phi :: Assertion+case_shortestImplicantsE_phi = do+ xss <- ExistentialQuantification.shortestImplicantsE (IntSet.fromList [7..13]) brauer11_phi+ forM_ (allAssignments 6) $ \m -> do+ b1 <- do+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars brauer11_phi)+ forM_ (CNF.cnfClauses brauer11_phi) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]+ let b2 = any (all (SAT.evalLit m) . IntSet.toList) xss+ (b1 == b2) @?= True++case_shortestImplicantsE_phi' :: Assertion+case_shortestImplicantsE_phi' = do+ let [y1,y2,y3,y4,y5,y6] = [1..6]+ xss = map IntSet.fromList+ [ [-y1, -y3, y4, -y5, y6]+ , [y1, -y2, -y3, y4, -y5, y6]+ , [y1, -y2, y3, -y4, y5, -y6]+ ]+ forM_ (allAssignments 6) $ \m -> do+ b1 <- do+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars brauer11_phi)+ forM_ (CNF.cnfClauses brauer11_phi) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]+ let b2 = any (all (SAT.evalLit m) . IntSet.toList) xss+ (b1 == b2) @?= True++case_shortestImplicantsE_omega :: Assertion+case_shortestImplicantsE_omega = do+ xss <- ExistentialQuantification.shortestImplicantsE IntSet.empty brauer11_omega+ forM_ (allAssignments 6) $ \m -> do+ b1 <- do+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars brauer11_omega)+ forM_ (CNF.cnfClauses brauer11_omega) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]+ let b2 = any (all (SAT.evalLit m) . IntSet.toList) xss+ unless (b1 == b2) $ print m++case_shortestImplicantsE_omega' :: Assertion+case_shortestImplicantsE_omega' = do+ let [y1,y2,y3,y4,y5,y6] = [1..6]+ xss = map IntSet.fromList+ [ [y2, -y6]+ , [y3, y6]+ , [-y5, -y6]+ , [-y3, y5]+ , [-y4, y6]+ , [-y1, -y6]+ , [y1, y2]+ , [y4, -y6]+ ]+ forM_ (allAssignments 6) $ \m -> do+ b1 <- do+ solver <- SAT.newSolver+ SAT.newVars_ solver (CNF.cnfNumVars brauer11_omega)+ forM_ (CNF.cnfClauses brauer11_omega) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ SAT.solveWith solver [if SAT.evalLit m y then y else -y | y <- [1..6]]+ let b2 = any (all (SAT.evalLit m) . IntSet.toList) xss+ (b1 == b2) @?= True++prop_negateCNF :: Property+prop_negateCNF = QM.monadicIO $ do+ phi <- QM.pick arbitraryCNF+ psi <- QM.run $ ExistentialQuantification.negateCNF phi+ QM.monitor (counterexample $ show psi)+ forM_ (allAssignments (CNF.cnfNumVars phi)) $ \m -> do+ let b1 = evalCNF m phi+ b2 = evalCNF m psi+ unless (b1 /= b2) $ QM.monitor (counterexample $ show m)+ QM.assert $ b1 /= b2++satExistentialQuantificationTestGroup :: TestTree+satExistentialQuantificationTestGroup = $(testGroupGenerator)
+ test/Test/SAT/MUS.hs view
@@ -0,0 +1,254 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.SAT.MUS (satMUSTestGroup) where++import Control.Monad+import Data.Default.Class+import qualified Data.Set as Set+import qualified Data.IntSet as IntSet++import Test.Tasty+import Test.Tasty.HUnit+import Test.Tasty.TH++import qualified ToySolver.SAT as SAT+import qualified ToySolver.SAT.MUS as MUS+import qualified ToySolver.SAT.MUS.Enum as MUSEnum++------------------------------------------------------------------------++findMUSAssumptions_case1 :: MUS.Method -> IO ()+findMUSAssumptions_case1 method = do+ solver <- SAT.newSolver+ [x1,x2,x3] <- SAT.newVars solver 3+ sels@[y1,y2,y3,y4,y5,y6] <- SAT.newVars solver 6+ SAT.addClause solver [-y1, x1]+ SAT.addClause solver [-y2, -x1]+ SAT.addClause solver [-y3, -x1, x2]+ SAT.addClause solver [-y4, -x2]+ SAT.addClause solver [-y5, -x1, x3]+ SAT.addClause solver [-y6, -x3]++ ret <- SAT.solveWith solver sels+ ret @?= False++ actual <- MUS.findMUSAssumptions solver def{ MUS.optMethod = method }+ let actual' = IntSet.map (\x -> x-3) actual+ expected = map IntSet.fromList [[1, 2], [1, 3, 4], [1, 5, 6]]+ actual' `elem` expected @?= True++case_findMUSAssumptions_Deletion :: Assertion+case_findMUSAssumptions_Deletion = findMUSAssumptions_case1 MUS.Deletion++case_findMUSAssumptions_Insertion :: Assertion+case_findMUSAssumptions_Insertion = findMUSAssumptions_case1 MUS.Insertion++case_findMUSAssumptions_QuickXplain :: Assertion+case_findMUSAssumptions_QuickXplain = findMUSAssumptions_case1 MUS.QuickXplain++------------------------------------------------------------------------++{-+c http://sun.iwu.edu/~mliffito/publications/jar_liffiton_CAMUS.pdf+c φ= (x1) ∧ (¬x1) ∧ (¬x1∨x2) ∧ (¬x2) ∧ (¬x1∨x3) ∧ (¬x3)+c MUSes(φ) = {{C1, C2}, {C1, C3, C4}, {C1, C5, C6}}+c MCSes(φ) = {{C1}, {C2, C3, C5}, {C2, C3, C6}, {C2, C4, C5}, {C2, C4, C6}}+p cnf 3 6+1 0+-1 0+-1 2 0+-2 0+-1 3 0+-3 0+-}++allMUSAssumptions_case1 :: MUSEnum.Method -> IO ()+allMUSAssumptions_case1 method = do+ solver <- SAT.newSolver+ [x1,x2,x3] <- SAT.newVars solver 3+ sels@[y1,y2,y3,y4,y5,y6] <- SAT.newVars solver 6+ SAT.addClause solver [-y1, x1]+ SAT.addClause solver [-y2, -x1]+ SAT.addClause solver [-y3, -x1, x2]+ SAT.addClause solver [-y4, -x2]+ SAT.addClause solver [-y5, -x1, x3]+ SAT.addClause solver [-y6, -x3]+ (muses, mcses) <- MUSEnum.allMUSAssumptions solver sels def{ MUSEnum.optMethod = method }+ Set.fromList muses @?= Set.fromList (map (IntSet.fromList . map (+3)) [[1,2], [1,3,4], [1,5,6]])+ Set.fromList mcses @?= Set.fromList (map (IntSet.fromList . map (+3)) [[1], [2,3,5], [2,3,6], [2,4,5], [2,4,6]])++case_allMUSAssumptions_CAMUS :: Assertion+case_allMUSAssumptions_CAMUS = allMUSAssumptions_case1 MUSEnum.CAMUS++case_allMUSAssumptions_DAA :: Assertion+case_allMUSAssumptions_DAA = allMUSAssumptions_case1 MUSEnum.DAA++case_allMUSAssumptions_MARCO :: Assertion+case_allMUSAssumptions_MARCO = allMUSAssumptions_case1 MUSEnum.MARCO++case_allMUSAssumptions_GurvichKhachiyan1999 :: Assertion+case_allMUSAssumptions_GurvichKhachiyan1999 = allMUSAssumptions_case1 MUSEnum.GurvichKhachiyan1999++{-+Boosting a Complete Technique to Find MSS and MUS thanks to a Local Search Oracle+http://www.cril.univ-artois.fr/~piette/IJCAI07_HYCAM.pdf+Example 3.+C0 : (d)+C1 : (b ∨ c)+C2 : (a ∨ b)+C3 : (a ∨ ¬c)+C4 : (¬b ∨ ¬e)+C5 : (¬a ∨ ¬b)+C6 : (a ∨ e)+C7 : (¬a ∨ ¬e)+C8 : (b ∨ e)+C9 : (¬a ∨ b ∨ ¬c)+C10 : (¬a ∨ b ∨ ¬d)+C11 : (a ∨ ¬b ∨ c)+C12 : (a ∨ ¬b ∨ ¬d)+-}+allMUSAssumptions_case2 :: MUSEnum.Method -> IO ()+allMUSAssumptions_case2 method = do+ solver <- SAT.newSolver+ [a,b,c,d,e] <- SAT.newVars solver 5+ sels@[y0,y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12] <- SAT.newVars solver 13+ SAT.addClause solver [-y0, d]+ SAT.addClause solver [-y1, b, c]+ SAT.addClause solver [-y2, a, b]+ SAT.addClause solver [-y3, a, -c]+ SAT.addClause solver [-y4, -b, -e]+ SAT.addClause solver [-y5, -a, -b]+ SAT.addClause solver [-y6, a, e]+ SAT.addClause solver [-y7, -a, -e]+ SAT.addClause solver [-y8, b, e]+ SAT.addClause solver [-y9, -a, b, -c]+ SAT.addClause solver [-y10, -a, b, -d]+ SAT.addClause solver [-y11, a, -b, c]+ SAT.addClause solver [-y12, a, -b, -d]++ -- Only three of the MUSes (marked with asterisks) are on the paper.+ let cores =+ [ [y0,y1,y2,y5,y9,y12]+ , [y0,y1,y3,y4,y5,y6,y10]+ , [y0,y1,y3,y5,y7,y8,y12]+ , [y0,y1,y3,y5,y9,y12]+ , [y0,y1,y3,y5,y10,y11]+ , [y0,y1,y3,y5,y10,y12]+ , [y0,y2,y3,y5,y10,y11]+ , [y0,y2,y4,y5,y6,y10]+ , [y0,y2,y5,y7,y8,y12]+ , [y0,y2,y5,y10,y12] -- (*)+ , [y1,y2,y4,y5,y6,y9]+ , [y1,y3,y4,y5,y6,y7,y8]+ , [y1,y3,y4,y5,y6,y9]+ , [y1,y3,y5,y7,y8,y11]+ , [y1,y3,y5,y9,y11] -- (*)+ , [y2,y3,y5,y7,y8,y11]+ , [y2,y4,y5,y6,y7,y8] -- (*)+ ]++ let remove1 :: [a] -> [[a]]+ remove1 [] = []+ remove1 (x:xs) = xs : [x : ys | ys <- remove1 xs]+ forM_ cores $ \core -> do+ ret <- SAT.solveWith solver core+ assertBool (show core ++ " should be a core") (not ret)+ forM (remove1 core) $ \xs -> do+ ret <- SAT.solveWith solver xs+ assertBool (show core ++ " should be satisfiable") ret++ (actual,_) <- MUSEnum.allMUSAssumptions solver sels def{ MUSEnum.optMethod = method }+ let actual' = Set.fromList actual+ expected' = Set.fromList $ map IntSet.fromList $ cores+ actual' @?= expected'++case_allMUSAssumptions_2_CAMUS :: Assertion+case_allMUSAssumptions_2_CAMUS = allMUSAssumptions_case2 MUSEnum.CAMUS++case_allMUSAssumptions_2_DAA :: Assertion+case_allMUSAssumptions_2_DAA = allMUSAssumptions_case2 MUSEnum.DAA++case_allMUSAssumptions_2_MARCO :: Assertion+case_allMUSAssumptions_2_MARCO = allMUSAssumptions_case2 MUSEnum.MARCO++case_allMUSAssumptions_2_GurvichKhachiyan1999 :: Assertion+case_allMUSAssumptions_2_GurvichKhachiyan1999 = allMUSAssumptions_case2 MUSEnum.GurvichKhachiyan1999++case_allMUSAssumptions_2_HYCAM :: Assertion+case_allMUSAssumptions_2_HYCAM = do+ solver <- SAT.newSolver+ [a,b,c,d,e] <- SAT.newVars solver 5+ sels@[y0,y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12] <- SAT.newVars solver 13+ SAT.addClause solver [-y0, d]+ SAT.addClause solver [-y1, b, c]+ SAT.addClause solver [-y2, a, b]+ SAT.addClause solver [-y3, a, -c]+ SAT.addClause solver [-y4, -b, -e]+ SAT.addClause solver [-y5, -a, -b]+ SAT.addClause solver [-y6, a, e]+ SAT.addClause solver [-y7, -a, -e]+ SAT.addClause solver [-y8, b, e]+ SAT.addClause solver [-y9, -a, b, -c]+ SAT.addClause solver [-y10, -a, b, -d]+ SAT.addClause solver [-y11, a, -b, c]+ SAT.addClause solver [-y12, a, -b, -d]++ -- Only three of the MUSes (marked with asterisks) are on the paper.+ let cores =+ [ [y0,y1,y2,y5,y9,y12]+ , [y0,y1,y3,y4,y5,y6,y10]+ , [y0,y1,y3,y5,y7,y8,y12]+ , [y0,y1,y3,y5,y9,y12]+ , [y0,y1,y3,y5,y10,y11]+ , [y0,y1,y3,y5,y10,y12]+ , [y0,y2,y3,y5,y10,y11]+ , [y0,y2,y4,y5,y6,y10]+ , [y0,y2,y5,y7,y8,y12]+ , [y0,y2,y5,y10,y12] -- (*)+ , [y1,y2,y4,y5,y6,y9]+ , [y1,y3,y4,y5,y6,y7,y8]+ , [y1,y3,y4,y5,y6,y9]+ , [y1,y3,y5,y7,y8,y11]+ , [y1,y3,y5,y9,y11] -- (*)+ , [y2,y3,y5,y7,y8,y11]+ , [y2,y4,y5,y6,y7,y8] -- (*)+ ]+ mcses =+ [ [y0,y1,y7]+ , [y0,y1,y8]+ , [y0,y3,y4]+ , [y0,y3,y6]+ , [y0,y4,y11]+ , [y0,y6,y11]+ , [y0,y7,y9]+ , [y0,y8,y9]+ , [y1,y2]+ , [y1,y7,y10]+ , [y1,y8,y10]+ , [y2,y3]+ , [y3,y4,y12]+ , [y3,y6,y12]+ , [y4,y11,y12]+ , [y5]+ , [y6,y11,y12]+ , [y7,y9,y10]+ , [y8,y9,y10]+ ]++ -- HYCAM paper wrongly treated {C3,C8,C10} as a candidate MCS (CoMSS).+ -- Its complement {C0,C1,C2,C4,C5,C6,C7,C9,C11,C12} is unsatisfiable+ -- and hence not MSS.+ ret <- SAT.solveWith solver [y0,y1,y2,y4,y5,y6,y7,y9,y11,y12]+ assertBool "failed to prove the bug of HYCAM paper" (not ret)+ + let cand = map IntSet.fromList [[y5], [y3,y2], [y0,y1,y2]]+ (actual,_) <- MUSEnum.allMUSAssumptions solver sels def{ MUSEnum.optMethod = MUSEnum.CAMUS, MUSEnum.optKnownCSes = cand }+ let actual' = Set.fromList $ actual+ expected' = Set.fromList $ map IntSet.fromList cores+ actual' @?= expected'++------------------------------------------------------------------------++satMUSTestGroup :: TestTree+satMUSTestGroup = $(testGroupGenerator)
+ test/Test/SAT/TheorySolver.hs view
@@ -0,0 +1,358 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.SAT.TheorySolver (satTheorySolverTestGroup) where++import Control.Monad+import Data.IORef+import Data.Map (Map)+import qualified Data.Map as Map+import Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap+import qualified Data.IntSet as IntSet+import qualified Data.Traversable as Traversable++import Test.Tasty+import Test.Tasty.QuickCheck hiding ((.&&.), (.||.))+import Test.Tasty.HUnit+import Test.Tasty.TH+import qualified Test.QuickCheck.Monadic as QM++import ToySolver.Data.BoolExpr+import ToySolver.Data.Boolean+import qualified ToySolver.SAT as SAT+import ToySolver.SAT.TheorySolver+import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin+import qualified ToySolver.FileFormat.CNF as CNF+import ToySolver.Data.OrdRel+import qualified ToySolver.Data.LA as LA+import qualified ToySolver.Arith.Simplex as Simplex+import qualified ToySolver.EUF.EUFSolver as EUF++import Test.SAT.Utils+++newTheorySolver :: CNF.CNF -> IO TheorySolver+newTheorySolver cnf = do+ let nv = CNF.cnfNumVars cnf+ cs = CNF.cnfClauses cnf+ solver <- SAT.newSolver+ SAT.newVars_ solver nv+ forM_ cs $ \c -> SAT.addClause solver (SAT.unpackClause c)+ + ref <- newIORef []+ let tsolver =+ TheorySolver+ { thAssertLit = \_ l -> do+ if abs l > nv then+ return True+ else do+ m <- readIORef ref+ case m of+ [] -> SAT.addClause solver [l]+ xs : xss -> writeIORef ref ((l : xs) : xss)+ return True+ , thCheck = \_ -> do+ xs <- liftM concat $ readIORef ref+ SAT.solveWith solver xs+ , thExplain = \m -> do+ case m of+ Nothing -> SAT.getFailedAssumptions solver+ Just _ -> return []+ , thPushBacktrackPoint = modifyIORef ref ([] :)+ , thPopBacktrackPoint = modifyIORef ref tail+ , thConstructModel = return ()+ }+ return tsolver++prop_solveCNF_using_BooleanTheory :: Property+prop_solveCNF_using_BooleanTheory = QM.monadicIO $ do+ cnf <- QM.pick arbitraryCNF+ let nv = CNF.cnfNumVars cnf+ nc = CNF.cnfNumClauses cnf+ cs = CNF.cnfClauses cnf+ cnf1 = cnf{ CNF.cnfClauses = [c | (i,c) <- zip [(0::Int)..] cs, i `mod` 2 == 0], CNF.cnfNumClauses = nc - (nc `div` 2) }+ cnf2 = cnf{ CNF.cnfClauses = [c | (i,c) <- zip [(0::Int)..] cs, i `mod` 2 /= 0], CNF.cnfNumClauses = nc `div` 2 }++ solver <- arbitrarySolver++ ret <- QM.run $ do+ SAT.newVars_ solver nv++ tsolver <- newTheorySolver cnf1+ SAT.setTheory solver tsolver++ forM_ (CNF.cnfClauses cnf2) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ ret <- SAT.solve solver+ if ret then do+ m <- SAT.getModel solver+ return (Just m)+ else do+ return Nothing++ case ret of+ Just m -> QM.assert $ evalCNF m cnf+ Nothing -> do+ forM_ (allAssignments nv) $ \m -> do+ QM.assert $ not (evalCNF m cnf)++case_QF_LRA :: Assertion+case_QF_LRA = do+ satSolver <- SAT.newSolver+ lraSolver <- Simplex.newSolver++ tblRef <- newIORef $ Map.empty+ defsRef <- newIORef $ IntMap.empty+ let abstractLAAtom :: LA.Atom Rational -> IO SAT.Lit+ abstractLAAtom atom = do+ (v,op,rhs) <- Simplex.simplifyAtom lraSolver atom+ tbl <- readIORef tblRef+ (vLt, vEq, vGt) <-+ case Map.lookup (v,rhs) tbl of+ Just (vLt, vEq, vGt) -> return (vLt, vEq, vGt)+ Nothing -> do+ vLt <- SAT.newVar satSolver+ vEq <- SAT.newVar satSolver+ vGt <- SAT.newVar satSolver+ SAT.addClause satSolver [vLt,vEq,vGt]+ SAT.addClause satSolver [-vLt, -vEq]+ SAT.addClause satSolver [-vLt, -vGt] + SAT.addClause satSolver [-vEq, -vGt]+ writeIORef tblRef (Map.insert (v,rhs) (vLt, vEq, vGt) tbl)+ let xs = IntMap.fromList+ [ (vEq, LA.var v .==. LA.constant rhs)+ , (vLt, LA.var v .<. LA.constant rhs)+ , (vGt, LA.var v .>. LA.constant rhs)+ , (-vLt, LA.var v .>=. LA.constant rhs)+ , (-vGt, LA.var v .<=. LA.constant rhs)+ ]+ modifyIORef defsRef (IntMap.union xs)+ return (vLt, vEq, vGt)+ case op of+ Lt -> return vLt+ Gt -> return vGt+ Eql -> return vEq+ Le -> return (-vGt)+ Ge -> return (-vLt)+ NEq -> return (-vEq)++ abstract :: BoolExpr (Either SAT.Lit (LA.Atom Rational)) -> IO (BoolExpr SAT.Lit)+ abstract = Traversable.mapM f+ where+ f (Left lit) = return lit+ f (Right atom) = abstractLAAtom atom++ let tsolver =+ TheorySolver+ { thAssertLit = \_ l -> do+ defs <- readIORef defsRef+ case IntMap.lookup l defs of+ Nothing -> return True+ Just atom -> do+ Simplex.assertAtomEx' lraSolver atom (Just l)+ return True+ , thCheck = \_ -> do+ Simplex.check lraSolver+ , thExplain = \m -> do+ case m of+ Nothing -> liftM IntSet.toList $ Simplex.explain lraSolver+ Just _ -> return []+ , thPushBacktrackPoint = do+ Simplex.pushBacktrackPoint lraSolver+ , thPopBacktrackPoint = do+ Simplex.popBacktrackPoint lraSolver+ , thConstructModel = do+ return ()+ }+ SAT.setTheory satSolver tsolver++ enc <- Tseitin.newEncoder satSolver+ let addFormula :: BoolExpr (Either SAT.Lit (LA.Atom Rational)) -> IO ()+ addFormula c = Tseitin.addFormula enc =<< abstract c++ a <- SAT.newVar satSolver+ x <- Simplex.newVar lraSolver+ y <- Simplex.newVar lraSolver++ let le1 = LA.fromTerms [(2,x), (1/3,y)] .<=. LA.constant (-4) -- 2 x + (1/3) y <= -4+ eq2 = LA.fromTerms [(1.5,x)] .==. LA.fromTerms [(-2,x)] -- 1.5 y = -2 x+ gt3 = LA.var x .>. LA.var y -- x > y+ lt4 = LA.fromTerms [(3,x)] .<. LA.fromTerms [(-1,LA.unitVar), (1/5,x), (1/5,y)] -- 3 x < -1 + (1/5) (x + y)++ c1, c2 :: BoolExpr (Either SAT.Lit (LA.Atom Rational))+ c1 = ite (Atom (Left a) :: BoolExpr (Either SAT.Lit (LA.Atom Rational))) (Atom $ Right le1) (Atom $ Right eq2)+ c2 = Atom (Right gt3) .||. (Atom (Left a) .<=>. Atom (Right lt4))++ addFormula c1+ addFormula c2++ ret <- SAT.solve satSolver+ ret @?= True++ m1 <- SAT.getModel satSolver+ m2 <- Simplex.getModel lraSolver+ let f (Left lit) = SAT.evalLit m1 lit+ f (Right atom) = LA.eval m2 atom+ fold f c1 @?= True+ fold f c2 @?= True+++case_QF_EUF :: Assertion+case_QF_EUF = do+ satSolver <- SAT.newSolver+ eufSolver <- EUF.newSolver+ enc <- Tseitin.newEncoder satSolver+ + tblRef <- newIORef (Map.empty :: Map (EUF.Term, EUF.Term) SAT.Var)+ defsRef <- newIORef (IntMap.empty :: IntMap (EUF.Term, EUF.Term))+ eufModelRef <- newIORef (undefined :: EUF.Model)+ + let abstractEUFAtom :: (EUF.Term, EUF.Term) -> IO SAT.Lit+ abstractEUFAtom (t1,t2) | t1 >= t2 = abstractEUFAtom (t2,t1)+ abstractEUFAtom (t1,t2) = do+ tbl <- readIORef tblRef+ case Map.lookup (t1,t2) tbl of+ Just v -> return v+ Nothing -> do+ v <- SAT.newVar satSolver+ writeIORef tblRef $! Map.insert (t1,t2) v tbl+ modifyIORef' defsRef $! IntMap.insert v (t1,t2)+ return v++ abstract :: BoolExpr (Either SAT.Lit (EUF.Term, EUF.Term)) -> IO (BoolExpr SAT.Lit)+ abstract = Traversable.mapM f+ where+ f (Left lit) = return lit+ f (Right atom) = abstractEUFAtom atom++ let tsolver =+ TheorySolver+ { thAssertLit = \_ l -> do+ defs <- readIORef defsRef+ case IntMap.lookup (SAT.litVar l) defs of+ Nothing -> return True+ Just (t1,t2) -> do+ if SAT.litPolarity l then+ EUF.assertEqual' eufSolver t1 t2 (Just l)+ else+ EUF.assertNotEqual' eufSolver t1 t2 (Just l)+ return True+ , thCheck = \callback -> do+ b <- EUF.check eufSolver+ when b $ do+ defs <- readIORef defsRef+ forM_ (IntMap.toList defs) $ \(v, (t1, t2)) -> do+ b2 <- EUF.areEqual eufSolver t1 t2+ when b2 $ do+ _ <- callback v+ return ()+ return b + , thExplain = \m -> do+ case m of+ Nothing -> liftM IntSet.toList $ EUF.explain eufSolver Nothing+ Just v -> do+ defs <- readIORef defsRef+ case IntMap.lookup v defs of+ Nothing -> error "should not happen"+ Just (t1,t2) -> do+ liftM IntSet.toList $ EUF.explain eufSolver (Just (t1,t2))+ , thPushBacktrackPoint = do+ EUF.pushBacktrackPoint eufSolver+ , thPopBacktrackPoint = do+ EUF.popBacktrackPoint eufSolver+ , thConstructModel = do+ writeIORef eufModelRef =<< EUF.getModel eufSolver+ return ()+ }+ SAT.setTheory satSolver tsolver++ cTrue <- EUF.newConst eufSolver+ cFalse <- EUF.newConst eufSolver+ EUF.assertNotEqual eufSolver cTrue cFalse+ boolToTermRef <- newIORef (IntMap.empty :: IntMap EUF.Term)+ termToBoolRef <- newIORef (Map.empty :: Map EUF.Term SAT.Lit)++ let connectBoolTerm :: SAT.Lit -> EUF.Term -> IO ()+ connectBoolTerm lit t = do+ lit1 <- abstractEUFAtom (t, cTrue)+ lit2 <- abstractEUFAtom (t, cFalse)+ SAT.addClause satSolver [-lit, lit1] -- lit -> lit1+ SAT.addClause satSolver [-lit1, lit] -- lit1 -> lit+ SAT.addClause satSolver [lit, lit2] -- -lit -> lit2+ SAT.addClause satSolver [-lit2, -lit] -- lit2 -> -lit+ modifyIORef' boolToTermRef $ IntMap.insert lit t+ modifyIORef' termToBoolRef $ Map.insert t lit++ boolToTerm :: SAT.Lit -> IO EUF.Term+ boolToTerm lit = do+ tbl <- readIORef boolToTermRef+ case IntMap.lookup lit tbl of+ Just t -> return t+ Nothing -> do+ t <- EUF.newConst eufSolver+ connectBoolTerm lit t+ return t++ termToBool :: EUF.Term -> IO SAT.Lit+ termToBool t = do+ tbl <- readIORef termToBoolRef+ case Map.lookup t tbl of+ Just lit -> return lit+ Nothing -> do+ lit <- SAT.newVar satSolver+ connectBoolTerm lit t+ return lit++ let addFormula :: BoolExpr (Either SAT.Lit (EUF.Term, EUF.Term)) -> IO ()+ addFormula c = Tseitin.addFormula enc =<< abstract c++ do+ x <- SAT.newVar satSolver+ x' <- boolToTerm x+ f <- EUF.newFun eufSolver+ fx <- termToBool (f x')+ ftt <- abstractEUFAtom (f cTrue, cTrue)+ ret <- SAT.solveWith satSolver [-fx, ftt]+ ret @?= True++ m1 <- SAT.getModel satSolver+ m2 <- readIORef eufModelRef+ let e (Left lit) = SAT.evalLit m1 lit+ e (Right (lhs,rhs)) = EUF.eval m2 lhs == EUF.eval m2 rhs+ fold e (notB (Atom (Left fx)) .||. (Atom (Right (f cTrue, cTrue)))) @?= True+ SAT.evalLit m1 x @?= False++ ret <- SAT.solveWith satSolver [-fx, ftt, x]+ ret @?= False++ do+ -- a : Bool+ -- f : U -> U+ -- x : U+ -- y : U+ -- (a or x=y)+ -- f x /= f y+ a <- SAT.newVar satSolver+ f <- EUF.newFun eufSolver+ x <- EUF.newConst eufSolver+ y <- EUF.newConst eufSolver+ let c1, c2 :: BoolExpr (Either SAT.Lit (EUF.Term, EUF.Term))+ c1 = Atom (Left a) .||. Atom (Right (x,y))+ c2 = notB $ Atom (Right (f x, f y))+ addFormula c1+ addFormula c2+ ret <- SAT.solve satSolver+ ret @?= True+ m1 <- SAT.getModel satSolver+ m2 <- readIORef eufModelRef+ let e (Left lit) = SAT.evalLit m1 lit+ e (Right (lhs,rhs)) = EUF.eval m2 lhs == EUF.eval m2 rhs+ fold e c1 @?= True+ fold e c2 @?= True++ ret <- SAT.solveWith satSolver [-a]+ ret @?= False+++satTheorySolverTestGroup :: TestTree+satTheorySolverTestGroup = $(testGroupGenerator)
+ test/Test/SAT/Types.hs view
@@ -0,0 +1,254 @@+{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.SAT.Types (satTypesTestGroup) where++import Control.Monad+import Data.Array.IArray+import Data.List++import Test.Tasty+import Test.Tasty.QuickCheck+import Test.Tasty.HUnit+import Test.Tasty.TH++import qualified ToySolver.SAT.Types as SAT++import Test.SAT.Utils++------------------------------------------------------------------------++-- -4*(not x1) + 3*x1 + 10*(not x2)+-- = -4*(1 - x1) + 3*x1 + 10*(not x2)+-- = -4 + 4*x1 + 3*x1 + 10*(not x2)+-- = 7*x1 + 10*(not x2) - 4+case_normalizePBLinSum_1 :: Assertion+case_normalizePBLinSum_1 = do+ sort e @?= sort [(7,x1),(10,-x2)]+ c @?= -4+ where+ x1 = 1+ x2 = 2+ (e,c) = SAT.normalizePBLinSum ([(-4,-x1),(3,x1),(10,-x2)], 0)++prop_normalizePBLinSum :: Property+prop_normalizePBLinSum = forAll g $ \(nv, (s,n)) ->+ let (s2,n2) = SAT.normalizePBLinSum (s,n)+ in flip all (allAssignments nv) $ \m ->+ SAT.evalPBLinSum m s + n == SAT.evalPBLinSum m s2 + n2+ where+ g :: Gen (Int, (SAT.PBLinSum, Integer))+ g = do+ nv <- choose (0, 10)+ s <- forM [1..nv] $ \x -> do+ c <- arbitrary+ p <- arbitrary+ return (c, SAT.literal x p)+ n <- arbitrary+ return (nv, (s,n))++-- -4*(not x1) + 3*x1 + 10*(not x2) >= 3+-- ⇔ -4*(1 - x1) + 3*x1 + 10*(not x2) >= 3+-- ⇔ -4 + 4*x1 + 3*x1 + 10*(not x2) >= 3+-- ⇔ 7*x1 + 10*(not x2) >= 7+-- ⇔ 7*x1 + 7*(not x2) >= 7+-- ⇔ x1 + (not x2) >= 1+case_normalizePBLinAtLeast_1 :: Assertion+case_normalizePBLinAtLeast_1 = (sort lhs, rhs) @?= (sort [(1,x1),(1,-x2)], 1)+ where+ x1 = 1+ x2 = 2+ (lhs,rhs) = SAT.normalizePBLinAtLeast ([(-4,-x1),(3,x1),(10,-x2)], 3)++prop_normalizePBLinAtLeast :: Property+prop_normalizePBLinAtLeast = forAll g $ \(nv, c) ->+ let c2 = SAT.normalizePBLinAtLeast c+ in flip all (allAssignments nv) $ \m ->+ SAT.evalPBLinAtLeast m c == SAT.evalPBLinAtLeast m c2+ where+ g :: Gen (Int, SAT.PBLinAtLeast)+ g = do+ nv <- choose (0, 10)+ lhs <- forM [1..nv] $ \x -> do+ c <- arbitrary+ p <- arbitrary+ return (c, SAT.literal x p)+ rhs <- arbitrary+ return (nv, (lhs,rhs))++case_normalizePBLinExactly_1 :: Assertion+case_normalizePBLinExactly_1 = (sort lhs, rhs) @?= ([], 1)+ where+ x1 = 1+ x2 = 2+ (lhs,rhs) = SAT.normalizePBLinExactly ([(6,x1),(4,x2)], 2)++case_normalizePBLinExactly_2 :: Assertion+case_normalizePBLinExactly_2 = (sort lhs, rhs) @?= ([], 1)+ where+ x1 = 1+ x2 = 2+ x3 = 3+ (lhs,rhs) = SAT.normalizePBLinExactly ([(2,x1),(2,x2),(2,x3)], 3)++prop_normalizePBLinExactly :: Property+prop_normalizePBLinExactly = forAll g $ \(nv, c) ->+ let c2 = SAT.normalizePBLinExactly c+ in flip all (allAssignments nv) $ \m ->+ SAT.evalPBLinExactly m c == SAT.evalPBLinExactly m c2+ where+ g :: Gen (Int, SAT.PBLinExactly)+ g = do+ nv <- choose (0, 10)+ lhs <- forM [1..nv] $ \x -> do+ c <- arbitrary+ p <- arbitrary+ return (c, SAT.literal x p)+ rhs <- arbitrary+ return (nv, (lhs,rhs))++prop_cutResolve :: Property+prop_cutResolve =+ forAll (choose (1, 10)) $ \nv ->+ forAll (g nv True) $ \c1 ->+ forAll (g nv False) $ \c2 ->+ let c3 = SAT.cutResolve c1 c2 1+ in flip all (allAssignments nv) $ \m ->+ not (SAT.evalPBLinExactly m c1 && SAT.evalPBLinExactly m c2) || SAT.evalPBLinExactly m c3+ where+ g :: Int -> Bool -> Gen SAT.PBLinExactly+ g nv b = do+ lhs <- forM [1..nv] $ \x -> do+ if x==1 then do+ c <- liftM ((1+) . abs) arbitrary+ return (c, SAT.literal x b)+ else do+ c <- arbitrary+ p <- arbitrary+ return (c, SAT.literal x p)+ rhs <- arbitrary+ return (lhs, rhs)++case_cutResolve_1 :: Assertion+case_cutResolve_1 = (sort lhs, rhs) @?= (sort [(1,x3),(1,x4)], 1)+ where+ x1 = 1+ x2 = 2+ x3 = 3+ x4 = 4+ pb1 = ([(1,x1), (1,x2), (1,x3)], 1)+ pb2 = ([(2,-x1), (2,-x2), (1,x4)], 3)+ (lhs,rhs) = SAT.cutResolve pb1 pb2 x1++case_cutResolve_2 :: Assertion+case_cutResolve_2 = (sort lhs, rhs) @?= (sort lhs2, rhs2)+ where+ x1 = 1+ x2 = 2+ x3 = 3+ x4 = 4+ pb1 = ([(3,x1), (2,-x2), (1,x3), (1,x4)], 3)+ pb2 = ([(1,-x3), (1,x4)], 1)+ (lhs,rhs) = SAT.cutResolve pb1 pb2 x3+ (lhs2,rhs2) = ([(2,x1),(1,-x2),(1,x4)],2) -- ([(3,x1),(2,-x2),(2,x4)], 3)++case_cardinalityReduction :: Assertion+case_cardinalityReduction = (sort lhs, rhs) @?= ([1,2,3,4,5],4)+ where+ (lhs, rhs) = SAT.cardinalityReduction ([(6,1),(5,2),(4,3),(3,4),(2,5),(1,6)], 17)++prop_pbLinUpperBound :: Property+prop_pbLinUpperBound =+ forAll (choose (0,10)) $ \nv ->+ forAll (arbitraryPBLinSum nv) $ \s ->+ forAll (arbitraryAssignment nv) $ \m -> + let ub = SAT.pbLinUpperBound s+ in counterexample (show ub) $ SAT.evalPBLinSum m s <= ub++prop_pbLinLowerBound :: Property+prop_pbLinLowerBound =+ forAll (choose (0,10)) $ \nv ->+ forAll (arbitraryPBLinSum nv) $ \s ->+ forAll (arbitraryAssignment nv) $ \m -> + let lb = SAT.pbLinLowerBound s+ in counterexample (show lb) $ lb <= SAT.evalPBLinSum m s++case_pbLinSubsume_clause :: Assertion+case_pbLinSubsume_clause = SAT.pbLinSubsume ([(1,1),(1,-3)],1) ([(1,1),(1,2),(1,-3),(1,4)],1) @?= True++case_pbLinSubsume_1 :: Assertion+case_pbLinSubsume_1 = SAT.pbLinSubsume ([(1,1),(1,2),(1,-3)],2) ([(1,1),(2,2),(1,-3),(1,4)],1) @?= True++case_pbLinSubsume_2 :: Assertion+case_pbLinSubsume_2 = SAT.pbLinSubsume ([(1,1),(1,2),(1,-3)],2) ([(1,1),(2,2),(1,-3),(1,4)],3) @?= False++prop_removeNegationFromPBSum :: Property+prop_removeNegationFromPBSum =+ forAll (choose (0,10)) $ \nv ->+ forAll (arbitraryPBSum nv) $ \s ->+ let s' = SAT.removeNegationFromPBSum s+ in counterexample (show s') $ + forAll (arbitraryAssignment nv) $ \m -> SAT.evalPBSum m s === SAT.evalPBSum m s'++prop_pbUpperBound :: Property+prop_pbUpperBound =+ forAll (choose (0,10)) $ \nv ->+ forAll (arbitraryPBSum nv) $ \s ->+ forAll (arbitraryAssignment nv) $ \m -> + let ub = SAT.pbUpperBound s+ in counterexample (show ub) $ SAT.evalPBSum m s <= ub++prop_pbLowerBound :: Property+prop_pbLowerBound =+ forAll (choose (0,10)) $ \nv ->+ forAll (arbitraryPBSum nv) $ \s ->+ forAll (arbitraryAssignment nv) $ \m -> + let lb = SAT.pbLowerBound s+ in counterexample (show lb) $ lb <= SAT.evalPBSum m s++------------------------------------------------------------------------++case_normalizeXORClause_False :: Assertion+case_normalizeXORClause_False =+ SAT.normalizeXORClause ([],True) @?= ([],True)++case_normalizeXORClause_True :: Assertion+case_normalizeXORClause_True =+ SAT.normalizeXORClause ([],False) @?= ([],False)++-- x ⊕ y ⊕ x = y+case_normalizeXORClause_case1 :: Assertion+case_normalizeXORClause_case1 =+ SAT.normalizeXORClause ([1,2,1],True) @?= ([2],True)++-- x ⊕ ¬x = x ⊕ x ⊕ 1 = 1+case_normalizeXORClause_case2 :: Assertion+case_normalizeXORClause_case2 =+ SAT.normalizeXORClause ([1,-1],True) @?= ([],False)++prop_normalizeXORClause :: Property+prop_normalizeXORClause = forAll g $ \(nv, c) ->+ let c2 = SAT.normalizeXORClause c+ in flip all (allAssignments nv) $ \m ->+ SAT.evalXORClause m c == SAT.evalXORClause m c2+ where+ g :: Gen (Int, SAT.XORClause)+ g = do+ nv <- choose (0, 10)+ len <- choose (0, nv)+ lhs <- replicateM len $ choose (-nv, nv) `suchThat` (/= 0)+ rhs <- arbitrary+ return (nv, (lhs,rhs))++case_evalXORClause_case1 :: Assertion+case_evalXORClause_case1 =+ SAT.evalXORClause (array (1,2) [(1,True),(2,True)] :: Array Int Bool) ([1,2], True) @?= False++case_evalXORClause_case2 :: Assertion+case_evalXORClause_case2 =+ SAT.evalXORClause (array (1,2) [(1,False),(2,True)] :: Array Int Bool) ([1,2], True) @?= True++------------------------------------------------------------------------++satTypesTestGroup :: TestTree+satTypesTestGroup = $(testGroupGenerator)
+ test/Test/SAT/Utils.hs view
@@ -0,0 +1,548 @@+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, FlexibleContexts #-}+module Test.SAT.Utils where++import Control.Monad+import Data.Array.IArray+import Data.Default.Class+import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet+import Data.List+import Data.Maybe+import qualified Data.Vector as V+import qualified System.Random.MWC as Rand++import Test.Tasty.QuickCheck+import qualified Test.QuickCheck.Monadic as QM++import qualified ToySolver.SAT as SAT+import qualified ToySolver.SAT.Types as SAT+import qualified ToySolver.SAT.Encoder.Cardinality as Cardinality+import qualified ToySolver.SAT.Encoder.PB as PB+import qualified ToySolver.SAT.Encoder.PBNLC as PBNLC+import qualified ToySolver.SAT.Encoder.Tseitin as Tseitin+import qualified ToySolver.SAT.PBO as PBO++import qualified Data.PseudoBoolean as PBFile+import ToySolver.Converter+import qualified ToySolver.FileFormat.CNF as CNF++-- ---------------------------------------------------------------------++allAssignments :: Int -> [SAT.Model]+allAssignments nv = [array (1,nv) (zip [1..nv] xs) | xs <- replicateM nv [True,False]]++forAllAssignments :: Testable prop => Int -> (SAT.Model -> prop) -> Property+forAllAssignments nv p = conjoin [counterexample (show m) (p m) | m <- allAssignments nv]++arbitraryAssignment :: Int -> Gen SAT.Model+arbitraryAssignment nv = do+ bs <- replicateM nv arbitrary+ return $ array (1,nv) (zip [1..] bs)++-- --------------------------------------------------------------------- ++arbitraryCNF :: Gen CNF.CNF+arbitraryCNF = do+ nv <- choose (0,10)+ nc <- choose (0,50)+ cs <- replicateM nc $ do+ len <- choose (0,10)+ if nv == 0 then+ return $ SAT.packClause []+ else+ SAT.packClause <$> (replicateM len $ choose (-nv, nv) `suchThat` (/= 0))+ return $+ CNF.CNF+ { CNF.cnfNumVars = nv+ , CNF.cnfNumClauses = nc+ , CNF.cnfClauses = cs+ }+++evalCNF :: SAT.Model -> CNF.CNF -> Bool+evalCNF m cnf = all (SAT.evalClause m . SAT.unpackClause) (CNF.cnfClauses cnf)++evalCNFCost :: SAT.Model -> CNF.CNF -> Int+evalCNFCost m cnf = sum $ map f (CNF.cnfClauses cnf)+ where+ f c = if SAT.evalClause m (SAT.unpackClause c) then 0 else 1+++arbitraryGCNF :: Gen CNF.GCNF+arbitraryGCNF = do+ nv <- choose (0,10)+ nc <- choose (0,50)+ ng <- choose (0,10)+ cs <- replicateM nc $ do+ g <- choose (0,ng) -- inclusive range+ c <-+ if nv == 0 then do+ return $ SAT.packClause []+ else do+ len <- choose (0,10)+ SAT.packClause <$> (replicateM len $ choose (-nv, nv) `suchThat` (/= 0))+ return (g,c)+ return $+ CNF.GCNF+ { CNF.gcnfNumVars = nv+ , CNF.gcnfNumClauses = nc+ , CNF.gcnfLastGroupIndex = ng+ , CNF.gcnfClauses = cs+ }+++arbitraryWCNF :: Gen CNF.WCNF+arbitraryWCNF = do+ nv <- choose (0,10)+ nc <- choose (0,50)+ cs <- replicateM nc $ do+ w <- arbitrary+ c <- do+ if nv == 0 then do+ return $ SAT.packClause []+ else do+ len <- choose (0,10)+ SAT.packClause <$> (replicateM len $ choose (-nv, nv) `suchThat` (/= 0))+ return (fmap getPositive w, c)+ let topCost = sum [w | (Just w, _) <- cs] + 1+ return $+ CNF.WCNF+ { CNF.wcnfNumVars = nv+ , CNF.wcnfNumClauses = nc+ , CNF.wcnfTopCost = topCost+ , CNF.wcnfClauses = [(fromMaybe topCost w, c) | (w,c) <- cs]+ }+++evalWCNF :: SAT.Model -> CNF.WCNF -> Maybe Integer+evalWCNF m wcnf = foldl' (liftM2 (+)) (Just 0)+ [ if SAT.evalClause m (SAT.unpackClause c) then+ Just 0+ else if w == CNF.wcnfTopCost wcnf then+ Nothing+ else+ Just w+ | (w,c) <- CNF.wcnfClauses wcnf+ ]+++evalWCNFCost :: SAT.Model -> CNF.WCNF -> Integer+evalWCNFCost m wcnf = sum $ do+ (w,c) <- CNF.wcnfClauses wcnf+ guard $ not $ SAT.evalClause m (SAT.unpackClause c)+ return w+++arbitraryQDimacs :: Gen CNF.QDimacs+arbitraryQDimacs = do+ nv <- choose (0,10)+ nc <- choose (0,50)+ prefix <- arbitraryPrefix $ IntSet.fromList [1..nv]++ cs <- replicateM nc $ do+ if nv == 0 then+ return $ SAT.packClause []+ else do+ len <- choose (0,10)+ SAT.packClause <$> (replicateM len $ choose (-nv, nv) `suchThat` (/= 0))+ return $+ CNF.QDimacs+ { CNF.qdimacsNumVars = nv+ , CNF.qdimacsNumClauses = nc+ , CNF.qdimacsPrefix = prefix+ , CNF.qdimacsMatrix = cs+ }++arbitraryPrefix :: IntSet -> Gen [CNF.QuantSet]+arbitraryPrefix xs = do+ if IntSet.null xs then+ return []+ else do+ b <- arbitrary+ if b then+ return []+ else do+ xs1 <- subsetof xs `suchThat` (not . IntSet.null)+ let xs2 = xs IntSet.\\ xs1+ q <- elements [CNF.E, CNF.A]+ ((q, IntSet.toList xs1) :) <$> arbitraryPrefix xs2++subsetof :: IntSet -> Gen IntSet+subsetof xs = (IntSet.fromList . catMaybes) <$> sequence [elements [Just x, Nothing] | x <- IntSet.toList xs]+++data PBRel = PBRelGE | PBRelEQ | PBRelLE deriving (Eq, Ord, Enum, Bounded, Show)++instance Arbitrary PBRel where+ arbitrary = arbitraryBoundedEnum++evalPBRel :: Ord a => PBRel -> a -> a -> Bool+evalPBRel PBRelGE = (>=)+evalPBRel PBRelLE = (<=)+evalPBRel PBRelEQ = (==)+++type PBLin = (Int,[(PBRel,SAT.PBLinSum,Integer)])++arbitraryPB :: Gen PBLin+arbitraryPB = do+ nv <- choose (0,10)+ nc <- choose (0,50)+ cs <- replicateM nc $ do+ rel <- arbitrary+ lhs <- arbitraryPBLinSum nv+ rhs <- arbitrary+ return $ (rel,lhs,rhs)+ return (nv, cs)++arbitraryPBLinSum :: Int -> Gen SAT.PBLinSum+arbitraryPBLinSum nv = do+ len <- choose (0,10)+ if nv == 0 then+ return []+ else+ replicateM len $ do+ l <- choose (-nv, nv) `suchThat` (/= 0)+ c <- arbitrary+ return (c,l)++evalPB :: SAT.Model -> PBLin -> Bool+evalPB m (_,cs) = all (\(o,lhs,rhs) -> evalPBRel o (SAT.evalPBLinSum m lhs) rhs) cs+++type WBOLin = (Int, [(Maybe Integer, (PBRel,SAT.PBLinSum,Integer))], Maybe Integer)++evalWBO :: SAT.Model -> WBOLin -> Maybe Integer+evalWBO m (_nv,cs,top) = do+ cost <- liftM sum $ forM cs $ \(w,(o,lhs,rhs)) -> do+ if evalPBRel o (SAT.evalPBLinSum m lhs) rhs then+ return 0+ else+ w+ case top of+ Just t -> guard (cost < t)+ Nothing -> return ()+ return cost++arbitraryWBO :: Gen WBOLin+arbitraryWBO = do+ (nv,cs) <- arbitraryPB+ cs2 <- forM cs $ \c -> do+ b <- arbitrary+ cost <- if b then return Nothing+ else liftM (Just . (1+) . abs) arbitrary+ return (cost, c)+ b <- arbitrary+ top <- if b then return Nothing+ else liftM (Just . (1+) . abs) arbitrary+ return (nv,cs2,top)+++arbitraryPBNLC :: Gen (Int,[(PBRel,SAT.PBSum,Integer)])+arbitraryPBNLC = do+ nv <- choose (0,10)+ nc <- choose (0,50)+ cs <- replicateM nc $ do+ rel <- arbitrary+ len <- choose (0,10)+ lhs <-+ if nv == 0 then+ return []+ else+ replicateM len $ do+ ls <- listOf $ choose (-nv, nv) `suchThat` (/= 0)+ c <- arbitrary+ return (c,ls)+ rhs <- arbitrary+ return $ (rel,lhs,rhs)+ return (nv, cs)++evalPBNLC :: SAT.Model -> (Int,[(PBRel,SAT.PBSum,Integer)]) -> Bool+evalPBNLC m (_,cs) = all (\(o,lhs,rhs) -> evalPBRel o (SAT.evalPBSum m lhs) rhs) cs+++arbitraryXOR :: Gen (Int,[SAT.XORClause])+arbitraryXOR = do+ nv <- choose (0,10)+ nc <- choose (0,50)+ cs <- replicateM nc $ do+ len <- choose (0,10) + lhs <-+ if nv == 0 then+ return []+ else+ replicateM len $ choose (-nv, nv) `suchThat` (/= 0)+ rhs <- arbitrary+ return (lhs,rhs)+ return (nv, cs)++evalXOR :: SAT.Model -> (Int,[SAT.XORClause]) -> Bool+evalXOR m (_,cs) = all (SAT.evalXORClause m) cs+++arbitraryNAESAT :: Gen NAESAT+arbitraryNAESAT = do+ cnf <- arbitraryCNF+ return (CNF.cnfNumVars cnf, CNF.cnfClauses cnf)+++arbitraryMaxSAT2 :: Gen (CNF.WCNF, Integer)+arbitraryMaxSAT2 = do+ nv <- choose (0,5)+ nc <- choose (0,10)+ cs <- replicateM nc $ do+ len <- choose (0,2)+ c <- if nv == 0 then+ return $ SAT.packClause []+ else+ SAT.packClause <$> (replicateM len $ choose (-nv, nv) `suchThat` (/= 0))+ return (1,c)+ th <- choose (0,nc)+ return $+ ( CNF.WCNF+ { CNF.wcnfNumVars = nv+ , CNF.wcnfNumClauses = nc+ , CNF.wcnfClauses = cs+ , CNF.wcnfTopCost = fromIntegral nc + 1+ }+ , fromIntegral th+ )++------------------------------------------------------------------------++solveCNF :: SAT.Solver -> CNF.CNF -> IO (Maybe SAT.Model)+solveCNF solver cnf = do+ SAT.newVars_ solver (CNF.cnfNumVars cnf)+ forM_ (CNF.cnfClauses cnf) $ \c -> SAT.addClause solver (SAT.unpackClause c)+ ret <- SAT.solve solver+ if ret then do+ m <- SAT.getModel solver+ return (Just m)+ else do+ return Nothing+++solvePB :: SAT.Solver -> PBLin -> IO (Maybe SAT.Model)+solvePB solver (nv,cs) = do+ SAT.newVars_ solver nv+ forM_ cs $ \(o,lhs,rhs) -> do+ case o of+ PBRelGE -> SAT.addPBAtLeast solver lhs rhs+ PBRelLE -> SAT.addPBAtMost solver lhs rhs+ PBRelEQ -> SAT.addPBExactly solver lhs rhs+ ret <- SAT.solve solver+ if ret then do+ m <- SAT.getModel solver+ return (Just m)+ else do+ return Nothing+++optimizePBO :: SAT.Solver -> PBO.Optimizer -> PBLin -> IO (Maybe (SAT.Model, Integer))+optimizePBO solver opt (nv,cs) = do+ SAT.newVars_ solver nv+ forM_ cs $ \(o,lhs,rhs) -> do+ case o of+ PBRelGE -> SAT.addPBAtLeast solver lhs rhs+ PBRelLE -> SAT.addPBAtMost solver lhs rhs+ PBRelEQ -> SAT.addPBExactly solver lhs rhs+ PBO.optimize opt+ PBO.getBestSolution opt+++optimizeWBO+ :: SAT.Solver+ -> PBO.Method+ -> WBOLin+ -> IO (Maybe (SAT.Model, Integer))+optimizeWBO solver method (nv,cs,top) = do+ SAT.newVars_ solver nv+ obj <- liftM catMaybes $ forM cs $ \(cost, (o,lhs,rhs)) -> do+ case cost of+ Nothing -> do+ case o of+ PBRelGE -> SAT.addPBAtLeast solver lhs rhs+ PBRelLE -> SAT.addPBAtMost solver lhs rhs+ PBRelEQ -> SAT.addPBExactly solver lhs rhs+ return Nothing+ Just w -> do+ sel <- SAT.newVar solver+ case o of+ PBRelGE -> SAT.addPBAtLeastSoft solver sel lhs rhs+ PBRelLE -> SAT.addPBAtMostSoft solver sel lhs rhs+ PBRelEQ -> SAT.addPBExactlySoft solver sel lhs rhs+ return $ Just (w,-sel)+ case top of+ Nothing -> return ()+ Just c -> SAT.addPBAtMost solver obj (c-1)+ opt <- PBO.newOptimizer solver obj+ PBO.setMethod opt method+ PBO.optimize opt+ liftM (fmap (\(m, val) -> (SAT.restrictModel nv m, val))) $ PBO.getBestSolution opt+++solvePBNLC :: SAT.Solver -> (Int,[(PBRel,SAT.PBSum,Integer)]) -> IO (Maybe SAT.Model)+solvePBNLC solver (nv,cs) = do+ SAT.newVars_ solver nv+ enc <- PBNLC.newEncoder solver =<< Tseitin.newEncoder solver+ forM_ cs $ \(o,lhs,rhs) -> do+ case o of+ PBRelGE -> PBNLC.addPBNLAtLeast enc lhs rhs+ PBRelLE -> PBNLC.addPBNLAtMost enc lhs rhs+ PBRelEQ -> PBNLC.addPBNLExactly enc lhs rhs+ ret <- SAT.solve solver+ if ret then do+ m <- SAT.getModel solver+ return $ Just $ SAT.restrictModel nv m+ else do+ return Nothing+++optimizePBNLC+ :: SAT.Solver+ -> PBO.Method+ -> (Int, SAT.PBSum, [(PBRel,SAT.PBSum,Integer)])+ -> IO (Maybe (SAT.Model, Integer))+optimizePBNLC solver method (nv,obj,cs) = do+ SAT.newVars_ solver nv+ enc <- PBNLC.newEncoder solver =<< Tseitin.newEncoder solver+ forM_ cs $ \(o,lhs,rhs) -> do+ case o of+ PBRelGE -> PBNLC.addPBNLAtLeast enc lhs rhs+ PBRelLE -> PBNLC.addPBNLAtMost enc lhs rhs+ PBRelEQ -> PBNLC.addPBNLExactly enc lhs rhs+ obj2 <- PBNLC.linearizePBSumWithPolarity enc Tseitin.polarityNeg obj+ opt <- PBO.newOptimizer2 solver obj2 (\m -> SAT.evalPBSum m obj)+ PBO.setMethod opt method+ PBO.optimize opt+ liftM (fmap (\(m, val) -> (SAT.restrictModel nv m, val))) $ PBO.getBestSolution opt++------------------------------------------------------------------------++instance Arbitrary SAT.LearningStrategy where+ arbitrary = arbitraryBoundedEnum++instance Arbitrary SAT.RestartStrategy where+ arbitrary = arbitraryBoundedEnum++instance Arbitrary SAT.BranchingStrategy where+ arbitrary = arbitraryBoundedEnum++instance Arbitrary SAT.PBHandlerType where+ arbitrary = arbitraryBoundedEnum++instance Arbitrary SAT.Config where+ arbitrary = do+ restartStrategy <- arbitrary+ restartFirst <- arbitrary+ restartInc <- liftM ((1.01 +) . abs) arbitrary+ learningStrategy <- arbitrary+ learntSizeFirst <- arbitrary+ learntSizeInc <- liftM ((1.01 +) . abs) arbitrary+ branchingStrategy <- arbitrary+ erwaStepSizeFirst <- choose (0, 1)+ erwaStepSizeMin <- choose (0, 1)+ erwaStepSizeDec <- choose (0, 1)+ pbhandler <- arbitrary+ ccmin <- choose (0,2)+ phaseSaving <- arbitrary+ forwardSubsumptionRemoval <- arbitrary+ backwardSubsumptionRemoval <- arbitrary+ randomFreq <- choose (0,1)+ splitClausePart <- arbitrary+ return $ def+ { SAT.configRestartStrategy = restartStrategy+ , SAT.configRestartFirst = restartFirst+ , SAT.configRestartInc = restartInc+ , SAT.configLearningStrategy = learningStrategy+ , SAT.configLearntSizeFirst = learntSizeFirst+ , SAT.configLearntSizeInc = learntSizeInc+ , SAT.configPBHandlerType = pbhandler+ , SAT.configCCMin = ccmin+ , SAT.configBranchingStrategy = branchingStrategy+ , SAT.configERWAStepSizeFirst = erwaStepSizeFirst+ , SAT.configERWAStepSizeDec = erwaStepSizeDec+ , SAT.configERWAStepSizeMin = erwaStepSizeMin+ , SAT.configEnablePhaseSaving = phaseSaving+ , SAT.configEnableForwardSubsumptionRemoval = forwardSubsumptionRemoval+ , SAT.configEnableBackwardSubsumptionRemoval = backwardSubsumptionRemoval+ , SAT.configRandomFreq = randomFreq+ , SAT.configEnablePBSplitClausePart = splitClausePart+ }++arbitrarySolver :: QM.PropertyM IO SAT.Solver+arbitrarySolver = do+ seed <- QM.pick arbitrary+ config <- QM.pick arbitrary+ QM.run $ do+ solver <- SAT.newSolverWithConfig config{ SAT.configCheckModel = True }+ SAT.setRandomGen solver =<< Rand.initialize (V.singleton seed)+ return solver++arbitraryOptimizer :: SAT.Solver -> SAT.PBLinSum -> QM.PropertyM IO PBO.Optimizer+arbitraryOptimizer solver obj = do+ method <- QM.pick arbitrary+ QM.run $ do+ opt <- PBO.newOptimizer solver obj+ PBO.setMethod opt method+ return opt++instance Arbitrary PBO.Method where+ arbitrary = arbitraryBoundedEnum++instance Arbitrary Cardinality.Strategy where+ arbitrary = arbitraryBoundedEnum++instance Arbitrary PB.Strategy where+ arbitrary = arbitraryBoundedEnum++arbitraryPBSum :: Int -> Gen SAT.PBSum+arbitraryPBSum nv = do+ nt <- choose (0,10)+ replicateM nt $ do+ ls <-+ if nv==0+ then return []+ else do+ m <- choose (0,nv)+ replicateM m $ do+ x <- choose (1,m)+ b <- arbitrary+ return $ if b then x else -x+ c <- arbitrary+ return (c,ls)++arbitraryPBFormula :: Gen PBFile.Formula+arbitraryPBFormula = do+ nv <- choose (0,10)+ obj <- do+ b <- arbitrary+ if b then+ liftM Just $ arbitraryPBSum nv+ else+ return Nothing+ nc <- choose (0,10)+ cs <- replicateM nc $ do+ lhs <- arbitraryPBSum nv+ op <- arbitrary+ rhs <- arbitrary+ return (lhs,op,rhs)+ return $+ PBFile.Formula+ { PBFile.pbObjectiveFunction = obj+ , PBFile.pbNumVars = nv+ , PBFile.pbNumConstraints = nc+ , PBFile.pbConstraints = cs+ }++instance Arbitrary PBFile.Op where+ arbitrary = arbitraryBoundedEnum++-- ---------------------------------------------------------------------++#if !MIN_VERSION_QuickCheck(2,8,0)+sublistOf :: [a] -> Gen [a]+sublistOf xs = filterM (\_ -> choose (False, True)) xs+#endif
test/Test/SDPFile.hs view
@@ -50,22 +50,22 @@ case_test1 = checkParsed example1b example1 where- s = toLazyByteString $ render example1+ s = toLazyByteString $ renderData example1 example1b = parseData "" s case_test2 = checkParsed example1b example1 where- s = toLazyByteString $ renderSparse example1+ s = toLazyByteString $ renderSparseData example1 example1b = parseSparseData "" s case_test3 = checkParsed example2b example2 where- s = toLazyByteString $ render example2+ s = toLazyByteString $ renderData example2 example2b = parseData "" s case_test4 = checkParsed example2b example2 where- s = toLazyByteString $ renderSparse example2+ s = toLazyByteString $ renderSparseData example2 example2b = parseSparseData "" s -- checkParsed :: Either ParseError Problem -> Problem -> Assertion
test/Test/SMT.hs view
@@ -47,7 +47,7 @@ case_QF_EUF_1 = do solver <- SMT.newSolver x <- SMT.declareConst solver "x" SMT.sBool- f <- SMT.declareFun solver "f" [SMT.sBool] SMT.sBool + f <- SMT.declareFun solver "f" [SMT.sBool] SMT.sBool let c1 = f true .==. true c2 = notB (f x)@@ -72,7 +72,7 @@ a <- SMT.declareConst solver "a" SMT.sBool x <- SMT.declareConst solver "x" sU y <- SMT.declareConst solver "y" sU- f <- SMT.declareFun solver "f" [sU] sU + f <- SMT.declareFun solver "f" [sU] sU let c1 = a .||. (x .==. y) c2 = f x ./=. f y
test/Test/SimplexTextbook.hs view
@@ -63,7 +63,7 @@ let (ret,result) = phaseI example_5_3_phase1 (IntSet.fromList [6,7]) assertBool "phase1 failed" ret assertBool "invalid tableau" (isValidTableau result)- assertBool "infeasible tableau" (isFeasible result) + assertBool "infeasible tableau" (isFeasible result) -- 退化して巡回の起こるKuhnの7変数3制約の例 kuhn_7_3 :: Tableau Rational
test/Test/Smtlib.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} module Test.Smtlib (smtlibTestGroup) where -import Control.Applicative import Control.DeepSeq import Control.Monad import qualified Data.Set as Set@@ -17,9 +16,8 @@ import Smtlib.Parsers.CommonParsers import Smtlib.Parsers.CommandsParsers import Smtlib.Parsers.ResponseParsers-import Text.Parsec (parse, ParseError)+import Text.Parsec (parse) -import Debug.Trace prop_parseTerm :: Property prop_parseTerm = forAll arbitrary $ \(t :: Term) ->@@ -367,7 +365,7 @@ where g :: Gen Char g = oneof [elements (Set.toList xs), choose (toEnum 128, maxBound)]- xs = Set.fromList (['\t','\n','\r'] ++ [' ' .. toEnum 126]) `Set.difference` Set.fromList ['\\', '|'] + xs = Set.fromList (['\t','\n','\r'] ++ [' ' .. toEnum 126]) `Set.difference` Set.fromList ['\\', '|'] genKeyword :: Gen String genKeyword = oneof
test/TestPolynomial.hs view
@@ -573,7 +573,7 @@ polynomials = do size <- choose (0, 5) xs <- replicateM size genTerms- return $ sum $ map P.fromTerm xs + return $ sum $ map P.fromTerm xs umonicMonomials :: Gen UMonomial umonicMonomials = do@@ -593,7 +593,7 @@ upolynomials = do size <- choose (0, 5) xs <- replicateM size genUTerms- return $ sum $ map P.fromTerm xs + return $ sum $ map P.fromTerm xs genUTermsZ :: Gen (UTerm Integer) genUTermsZ = do@@ -605,7 +605,7 @@ upolynomialsZ = do size <- choose (0, 5) xs <- replicateM size genUTermsZ- return $ sum $ map P.fromTerm xs + return $ sum $ map P.fromTerm xs ------------------------------------------------------------------------
test/TestSuite.hs view
@@ -9,6 +9,8 @@ import Test.BoolExpr import Test.CongruenceClosure import Test.ContiTraverso+import Test.Converter+import Test.CNF import Test.Delta import Test.FiniteModelFinder import Test.GraphShortestPath@@ -19,10 +21,17 @@ import Test.MIPSolver import Test.MIPSolver2 import Test.MPSFile+import Test.ProbSAT import Test.SDPFile import Test.Misc import Test.QBF+import Test.QUBO import Test.SAT+import Test.SAT.Encoder+import Test.SAT.ExistentialQuantification+import Test.SAT.MUS+import Test.SAT.TheorySolver+import Test.SAT.Types import Test.Simplex import Test.SimplexTextbook import Test.SMT@@ -39,6 +48,8 @@ , bitVectorTestGroup , boolExprTestGroup , ccTestGroup+ , cnfTestGroup+ , converterTestGroup , ctTestGroup , deltaTestGroup , fmfTestGroup@@ -51,8 +62,15 @@ , mipSolverTestGroup , mipSolver2TestGroup , mpsTestGroup+ , probSATTestGroup , qbfTestGroup+ , quboTestGroup , satTestGroup+ , satEncoderTestGroup+ , satExistentialQuantificationTestGroup+ , satMUSTestGroup+ , satTheorySolverTestGroup+ , satTypesTestGroup , sdpTestGroup , simplexTestGroup , simplexTextbookTestGroup
toysolver.cabal view
@@ -1,19 +1,21 @@ Name: toysolver-Version: 0.5.0+Version: 0.6.0 License: BSD3 License-File: COPYING Author: Masahiro Sakai (masahiro.sakai@gmail.com) Maintainer: masahiro.sakai@gmail.com-Category: Algorithms, Optimisation, Optimization, Theorem Provers, Constraints, Logic, Formal Methods-Cabal-Version: >= 1.18+Category: Algorithms, Optimisation, Optimization, Theorem Provers, Constraints, Logic, Formal Methods, SMT+Cabal-Version: 1.18 Synopsis: Assorted decision procedures for SAT, SMT, Max-SAT, PB, MIP, etc-Description: Toy-level implementation of some decision procedures+Description: Toy-level solver implementation of various problems including SAT, SMT, Max-SAT, PBS/PBO (Pseudo Boolean Satisfaction/Optimization), MILP (Mixed Integer Linear Programming) and non-linear real arithmetic. Homepage: https://github.com/msakai/toysolver/ Bug-Reports: https://github.com/msakai/toysolver/issues Tested-With:- GHC ==7.8.4 GHC ==7.10.3- GHC ==8.0.1+ GHC ==8.0.2+ GHC ==8.2.2+ GHC ==8.4.4+ GHC ==8.6.4 Extra-Source-Files: README.md CHANGELOG.markdown@@ -26,12 +28,15 @@ misc/build_bdist_maxsat_evaluation.sh misc/build_bdist_pb_evaluation.sh misc/build_bdist_qbf_evaluation.sh+ misc/build_bdist_smtcomp.sh misc/maxsat/toysat/README.md misc/maxsat/toysat/toysat misc/maxsat/toysat_ls/README.md misc/maxsat/toysat_ls/toysat_ls misc/pb/README.md misc/qbf/README.md+ misc/smtcomp/bin/starexec_run_default+ misc/smtcomp/starexec_description.txt src/ToySolver/Data/Polyhedron.hs src/ToySolver/SAT/MessagePassing/SurveyPropagation/sp.cl samples/gcnf/*.cnf@@ -86,6 +91,11 @@ Default: False Manual: True +Flag WithZlib+ Description: Use zlib package to support gzipped files+ Default: True+ Manual: True+ Flag BuildToyFMF Description: build toyfmf command Default: False@@ -155,9 +165,12 @@ Hs-source-dirs: src Build-Depends: array >=0.4.0.0,- base >=4.7 && <5,+ -- GHC >=7.10+ base >=4.8 && <5, bytestring >=0.9.2.1 && <0.11, bytestring-builder,+ bytestring-encoding,+ case-insensitive, clock >=0.7.1, -- IntMap.mergeWithKey and IntMap.toDescList require containers >=0.5.0 containers >=0.5.0,@@ -177,11 +190,12 @@ mtl >=2.1.2, multiset, -- createSystemRandom requires mwc-random >=0.13.1.0- mwc-random >=0.13.1 && <0.14,+ mwc-random >=0.13.1 && <0.15, OptDir, lattices,- megaparsec >=4 && <7,- prettyclass >=1.0.0,+ megaparsec >=4 && <8,+ -- Text.PrettyPrint.HughesPJClass is available on pretty >=1.1.2.0+ pretty >=1.1.2.0 && <1.2, primes, primitive >=0.6, process >=1.1.0.2,@@ -201,6 +215,9 @@ vector, vector-space >=0.8.6, xml-conduit+ if flag(WithZlib)+ Build-Depends: zlib+ CPP-Options: "-DWITH_ZLIB" if flag(OpenCL) Build-Depends: OpenCL >=1.0.3.4 Exposed-Modules: ToySolver.SAT.MessagePassing.SurveyPropagation.OpenCL@@ -274,24 +291,24 @@ ToySolver.Combinatorial.Knapsack.DPDense ToySolver.Combinatorial.Knapsack.DPSparse ToySolver.Combinatorial.SubsetSum+ ToySolver.Converter+ ToySolver.Converter.Base ToySolver.Converter.GCNF2MaxSAT ToySolver.Converter.ObjType ToySolver.Converter.MIP2PB ToySolver.Converter.MIP2SMT- ToySolver.Converter.MaxSAT2IP- ToySolver.Converter.MaxSAT2WBO+ ToySolver.Converter.NAESAT+ ToySolver.Converter.PB ToySolver.Converter.PB2IP- ToySolver.Converter.PBLinearization ToySolver.Converter.PB2LSP- ToySolver.Converter.PB2SAT- ToySolver.Converter.PB2WBO ToySolver.Converter.PBSetObj ToySolver.Converter.PB2SMP+ ToySolver.Converter.QBF2IPC+ ToySolver.Converter.QUBO ToySolver.Converter.SAT2KSAT- ToySolver.Converter.SAT2PB- ToySolver.Converter.SAT2IP- ToySolver.Converter.WBO2MaxSAT- ToySolver.Converter.WBO2PB+ ToySolver.Converter.SAT2MaxCut+ ToySolver.Converter.SAT2MaxSAT+ ToySolver.Converter.Tseitin ToySolver.Data.AlgebraicNumber.Complex ToySolver.Data.AlgebraicNumber.Real ToySolver.Data.AlgebraicNumber.Root@@ -308,6 +325,7 @@ ToySolver.Data.LBool ToySolver.Data.MIP ToySolver.Data.MIP.Base+ ToySolver.Data.MIP.FileUtils ToySolver.Data.MIP.LPFile ToySolver.Data.MIP.MPSFile ToySolver.Data.MIP.Solution.CBC@@ -335,8 +353,13 @@ ToySolver.Data.Polynomial.Factorization.Zassenhaus ToySolver.Data.Polynomial.GroebnerBasis ToySolver.Data.Polynomial.Interpolation.Lagrange+ ToySolver.FileFormat+ ToySolver.FileFormat.Base+ ToySolver.FileFormat.CNF ToySolver.Graph.ShortestPath+ ToySolver.MaxCut ToySolver.QBF+ ToySolver.QUBO ToySolver.SAT ToySolver.SAT.Config ToySolver.SAT.Encoder.Integer@@ -345,6 +368,9 @@ ToySolver.SAT.Encoder.PB.Internal.BDD ToySolver.SAT.Encoder.PB.Internal.Sorter ToySolver.SAT.Encoder.PBNLC+ ToySolver.SAT.Encoder.Cardinality+ ToySolver.SAT.Encoder.Cardinality.Internal.Naive+ ToySolver.SAT.Encoder.Cardinality.Internal.ParallelCounter ToySolver.SAT.Encoder.Tseitin ToySolver.SAT.ExistentialQuantification ToySolver.SAT.MessagePassing.SurveyPropagation@@ -358,17 +384,19 @@ ToySolver.SAT.PBO.BCD2 ToySolver.SAT.PBO.MSU4 ToySolver.SAT.PBO.UnsatBased+ ToySolver.SAT.SLS.ProbSAT ToySolver.SAT.Store.CNF ToySolver.SAT.Store.PB ToySolver.SAT.TheorySolver ToySolver.SAT.Types ToySolver.SAT.Printer+ ToySolver.SDP ToySolver.SMT ToySolver.Text.CNF ToySolver.Text.GCNF- ToySolver.Text.MaxSAT ToySolver.Text.QDimacs ToySolver.Text.SDPFile+ ToySolver.Text.WCNF ToySolver.Internal.Data.IndexedPriorityQueue ToySolver.Internal.Data.IOURef ToySolver.Internal.Data.PriorityQueue@@ -380,6 +408,8 @@ ToySolver.Wang ToySolver.Version Other-Modules:+ ToySolver.Converter.PB.Internal.LargestIntersectionFinder+ ToySolver.Converter.PB.Internal.Product ToySolver.Data.AlgebraicNumber.Graeffe ToySolver.Data.Polynomial.Base ToySolver.SAT.MUS.Base@@ -402,6 +432,7 @@ data-default-class, filepath, OptDir,+ optparse-applicative, pseudo-boolean, scientific, toysolver@@ -430,6 +461,7 @@ filepath, megaparsec, mwc-random,+ optparse-applicative, process >=1.1.0.2, pseudo-boolean, scientific,@@ -444,6 +476,8 @@ -- GHC-Prof-Options: -auto-all if flag(ForceChar8) CPP-OPtions: "-DFORCE_CHAR8"+ if flag(WithZlib)+ CPP-Options: "-DWITH_ZLIB" if flag(LinuxStatic) GHC-Options: -static -optl-static -optl-pthread @@ -463,10 +497,10 @@ Build-Depends: base, containers,- data-default-class, -- TODO: remove intern dependency intern, mtl,+ optparse-applicative, parsec >=3.1.2 && <4, toysolver, text,@@ -491,6 +525,7 @@ base, containers, data-default-class,+ optparse-applicative, toysolver Default-Language: Haskell2010 Other-Extensions: ScopedTypeVariables, CPP@@ -514,6 +549,7 @@ -- logic-TPTP <=0.4.3 has build error on ghc <7.9 and transformers >=0.5.1. -- https://github.com/DanielSchuessler/logic-TPTP/pull/4 logic-TPTP >=0.4.4.0,+ optparse-applicative, text, toysolver -- logic-TPTP <=0.4.4.0 is not compatible with transformers-compat >=0.5@@ -524,6 +560,9 @@ else Build-Depends: transformers-compat <0.5+ -- for Semigroup (as superclass of) Monoid Proposal+ if impl(ghc >=8.4)+ Build-Depends: logic-TPTP >=0.4.6.0 Default-Language: Haskell2010 Other-Extensions: CPP GHC-Options: -rtsopts@@ -540,10 +579,12 @@ HS-Source-Dirs: app Build-Depends: base,+ ansi-wl-pprint, bytestring, bytestring-builder, data-default-class, filepath,+ optparse-applicative, pseudo-boolean, scientific, text,@@ -554,6 +595,8 @@ -- GHC-Prof-Options: -auto-all if flag(ForceChar8) CPP-OPtions: "-DFORCE_CHAR8"+ if flag(WithZlib)+ CPP-Options: "-DWITH_ZLIB" if flag(LinuxStatic) GHC-Options: -static -optl-static -optl-pthread @@ -757,6 +800,28 @@ if flag(LinuxStatic) GHC-Options: -static -optl-static -optl-pthread +Executable probsat+ if !flag(BuildSamplePrograms)+ Buildable: False + Main-is: probsat.hs+ HS-Source-Dirs: samples/programs/probsat+ Build-Depends:+ base,+ clock,+ data-default-class,+ mwc-random,+ optparse-applicative,+ vector,+ toysolver+ Default-Language: Haskell2010+ Other-Extensions: CPP+ GHC-Options: -rtsopts+ -- GHC-Prof-Options: -auto-all+ if flag(ForceChar8)+ CPP-Options: "-DFORCE_CHAR8"+ if flag(LinuxStatic)+ GHC-Options: -static -optl-static -optl-pthread+ -- Misc Programs Executable pigeonhole@@ -766,6 +831,7 @@ HS-Source-Dirs: app Build-Depends: base,+ bytestring, containers, pseudo-boolean, toysolver@@ -826,10 +892,10 @@ containers, data-interval, finite-field >=0.7.0 && <1.0.0,- prettyclass >=1.0.0,+ pretty, tasty >=0.10.1,- tasty-hunit ==0.9.*,- tasty-quickcheck >=0.8 && <0.10,+ tasty-hunit >=0.9 && <0.11,+ tasty-quickcheck >=0.8 && <0.11, tasty-th, toysolver Default-Language: Haskell2010@@ -846,8 +912,10 @@ Test.BitVector Test.BoolExpr Test.BipartiteMatching+ Test.CNF Test.CongruenceClosure Test.ContiTraverso+ Test.Converter Test.Delta Test.FiniteModelFinder Test.GraphShortestPath@@ -859,8 +927,16 @@ Test.MIPSolver Test.MIPSolver2 Test.MPSFile+ Test.ProbSAT Test.QBF+ Test.QUBO Test.SAT+ Test.SAT.Encoder+ Test.SAT.ExistentialQuantification+ Test.SAT.MUS+ Test.SAT.TheorySolver+ Test.SAT.Types+ Test.SAT.Utils Test.SDPFile Test.Simplex Test.SimplexTextbook@@ -893,9 +969,10 @@ parsec >=3.1.2 && <4, pseudo-boolean, QuickCheck >=2.5 && <3,+ scientific, tasty >=0.10.1,- tasty-hunit ==0.9.*,- tasty-quickcheck >=0.8 && <0.10,+ tasty-hunit >=0.9 && <0.11,+ tasty-quickcheck >=0.8 && <0.11, tasty-th, text, toysolver,@@ -926,7 +1003,7 @@ build-depends: array, base,- criterion >=1.0 && <1.3,+ criterion >=1.0 && <1.6, data-default-class, toysolver Default-Language: Haskell2010@@ -937,7 +1014,7 @@ main-is: BenchmarkKnapsack.hs build-depends: base,- criterion >=1.0 && <1.3,+ criterion >=1.0 && <1.6, toysolver Default-Language: Haskell2010 @@ -947,7 +1024,7 @@ main-is: BenchmarkSubsetSum.hs build-depends: base,- criterion >=1.0 && <1.3,+ criterion >=1.0 && <1.6, toysolver, vector Default-Language: Haskell2010