{-| Module : MZinHaskell Description : Integration of MiniZinc 2.0 in Haskell License : BSD3 Maintainer : Klara Marntirosian Stability : experimental This module provides IO functionality for running the Haskell representation of a MiniZinc model and getting back the solutions in Haskell values. -} module Interfaces.MZinHaskell ( -- iTestModel, iRunModel, runModel, Interfaces.MZPrinter.layout, -- testModelWithData, -- testModelWithParser, writeData ) where import Data.List import Data.Char import System.Process import System.FilePath import System.Directory import Interfaces.MZAuxiliary import Interfaces.MZASTBase (MZModel, Item(Comment)) import Interfaces.MZAST (GItem(..)) import Interfaces.MZPrinter import Interfaces.FZSolutionParser (Solution, trySolutionsDefault, getAllSolutions) import Text.Parsec.Error import Text.Parsec.String (Parser) {- -- | Same as `testModel` but accepts one more argument for the data of the model. testModelWithData :: [GItem 'OK] -- ^ The model -> [GItem 'OK] -- ^ The data to be used by the model -> FilePath -- ^ Path of the file in which the FlatZinc translation will be printed (without ".fzn" extension) -> Int -- ^ Chosen solver (@1@ for the G12/FD built-in solver or @2@ for choco3) -> Int -- ^ Number of solutions to be returned -> IO (Either ParseError [Solution]) testModelWithData model mdata path solver num = let fdata = [Comment' "Model\'s data"] ++ mdata ++ [Comment' "End of model\'s data"] in testModel (fdata ++ model) path solver num -} -- | Same as `testModel`, but interactive. -- -- Interactively runs a constraint model and outputs its solution(s). The -- function first prompts the user for the working directory: the FlatZinc file -- will be created in that directory. Then, for a name for the constraint model: -- the created FlatZinc file will be named after this. Also asks the user to -- choose between supported solvers and the desired number of solutions. Returns -- either a parse error or a list of solutions of the constraint model. The length -- of the list is at most equal to the number of solutions requested. iRunModel :: [GItem a] -> IO (Either ParseError [Solution]) iRunModel m = do putStrLn "Enter working directory:" dirpath <- getLine putStr "Enter model\'s name: " name <- getLine putStr "Choose a solver from the list below:\r\n\t1. G12/FD\r\n\t2. choco3\r\n\r\nEnter the number associated with the solver: " str_solver <- getLine putStr $ if (str_solver /= "2") then "Number of solutions to be returned: " else "Return all solutions? Y/N: " str_ns <- getLine; let solver = read str_solver ns = if (solver == 2) then if (read ("\"" ++ str_ns ++ "\"") == "Y") then 0 else 1 else read str_ns path = joinPath [dirpath, name] runModel m path solver ns -- | Runs a model and parses its solution(s). Use this function if the model contains no -- @output@ item, so that the solutions have the default format. runModel :: [GItem a] -- ^ The model -> FilePath -- ^ The path of the file in which the FlatZinc translation will be printed (without ".fzn" extension) -> Int -- ^ The chosen solver (@1@ for the G12/FD built-in solver or @2@ for choco3) -> Int -- ^ The number of solutions to be returned -> IO (Either ParseError [Solution]) runModel = testModelWithParser trySolutionsDefault -- | Runs a model and parses its solution(s) with the use of the specified parser. Use -- this function if the model outputs its solutions in a different format than the -- default. testModelWithParser :: Parser [Solution] -- ^ The parser with which solutions will be -- parsed -> [GItem a] -- ^ The model -> FilePath -- ^ The path of the file in which the FlatZinc -- translation will be printed (without ".fzn" -- extension) -> Int -- ^ The chosen solver (@1@ for the G12/FD -- built-in solver or @2@ for choco3) -> Int -- ^ The number of solutions to be returned -> IO (Either ParseError [Solution]) testModelWithParser p m mpath s n = do -- Get configuration and set filepaths configuration <- parseConfig let mz_dir = addTrailingPathSeparator $ case minizinc configuration of "" -> "." str -> str let mzn_fp = spaceFix $ mpath ++ ".mzn" let fzn_fp = spaceFix $ mpath ++ ".fzn" let res_fp = spaceFix $ mpath ++ ".res" -- Write mzn file writeFile mzn_fp (layout m) let mzn2fzn = proc (mz_dir ++ "mzn2fzn") ["-O-" ,"-o", fzn_fp , mzn_fp] (ec1, out1, err1) <- readCreateProcessWithExitCode mzn2fzn "" res <- case err1 of "" -> case s of -- G12/FD solver 1 -> do let fz_options = ["-b", "fd"] ++ case (n > 0) of True -> ["-n", show n] _ -> [] ++ [fzn_fp] let flatzinc = proc (mz_dir ++ "flatzinc") fz_options (ec2, out2, err2) <- readCreateProcessWithExitCode flatzinc "" return $ case err2 of "" -> out2 _ -> "flatzinc error: " ++ err2 ++ "." -- Choco solver 2 -> let antlr = antlr_path configuration chocoParser = chocoparser configuration chocoSolver = chocosolver configuration all_or_first = if (n == 0) then "-a " else "" in readCreateProcess (shell $ "java -cp ." ++ (intercalate [searchPathSeparator] [chocoSolver, chocoParser, antlr]) ++ " org.chocosolver.parser.flatzinc.ChocoFZN " ++ all_or_first ++ mpath ++ ".fzn") "" _ -> readIO ("mzn2fzn error: " ++ err1 ++ ".") writeFile res_fp res -- Comment lines below for debugging removeFile res_fp removeFile mzn_fp removeFile fzn_fp return $ getAllSolutions p res -- | Writes the model's data file. The 'MZModel' of the argument must contain -- only 'Interfaces.MZASTBase.Assign' items. writeData :: MZModel -> IO () writeData m = do putStrLn "Enter datafile's filepath:" datapath <- getLine writeFile datapath (Prelude.show $ printModel m)