Разобрать строку с формулой высказываний в CNF для вложенного списка DIMACS int в haskell
Я хотел бы проанализировать строку с формулой высказываний в CNF для DIMACS, таким образом, вложенный список int в haskell. Формат подходит для привязок пикосата haskell, которые кажутся более производительными, чем другие варианты решения SAT.
Проблема в том, что мой код, который реализовал это, слишком сложен, и теперь я ищу возможную ошибку, которая не очевидна для поиска.
(Мой подход состоял в том, чтобы использовать пакет hastll hatt, изменить пакет таким образом, чтобы он использовал Strings вместо одиночных символов для имен переменных, проанализировать выражение с помощью hatt и затем преобразовать полученное выражение в формат DIMACS.)
Я хочу проанализировать строку, которая представляет пропозициональную формулу в нотации CNF (см. Пример ниже). Результатом должен быть вложенный список целых чисел. Таким образом, результат должен быть подходящим для solveAll
это часть привязок haskell picosat.
Входные данные:
-- "&" ... conjunction
-- "|" ... disjunction
-- "-" ... not operator
let myCNF = "GP & (-GP | Ma) & (-Ma | GP) & (-Ma | TestP) & (-Ma | Alg) & (-Ma | Src) & (-Ma | Hi) & (-Ma | Wg | X | Y) & -Z"
Результат:
-- DIMACS format
-- every Variable (e.g., "GP") gets a number, say GP gets the int num 1
-- in case of not GP (i.e., "-GP") the int number representing the variable is negative, thus -1
-- Note: I hope I didn't do a type
let myresult = [[1], [-1, 2], [-2, 1], [-2, 3], [-2, 3], [-2, 5], [-2, 6], [-2, 7, 8, 9], [-10]]
let myvars = ["GP", "Ma", "TestP", "Alg", "Src", "Hi", "Wg", "X", "Y", "Z"]
-- or alternativly the variables are stored in an associative array
let varOtherWay = (1, GP), (2, Ma) (3, TestP), (4, Alg), (5, Src), (6, Hi), (7, Wg), (8, X), (9, Y), (10, Z)
2 ответа
Вам, вероятно, не нужен parsec для чтения операторов в CNF, вы можете извлечь переменные с помощью map (splitOn "|") . splitOn "&"
- остальное - просто присвоение целых чисел переменным:
import qualified Data.Map as M
import Data.List.Split
import Control.Monad.State
deleteMany c [] = []
deleteMany c (x:xs)
| x`elem`c = deleteMany c xs
| otherwise = x : deleteMany c xs
parseStatement :: String -> ( [[Int]] , M.Map Int String )
parseStatement =
f . flip runState (M.empty, 1) . mapM (mapM (readVar . toVar)) . varsOf . deleteMany "() "
where
f (r, (m, _)) = (r , M.fromList $ map (uncurry (flip (,))) $ M.toList m)
varsOf = map (splitOn "|") . splitOn "&"
toVar ('-':v) = (True , v)
toVar v = (False, v)
readVar (b,v) = do
(m, c) <- get
case M.lookup v m of
Nothing -> put (M.insert v c m, c+1) >> return (neg b c)
Just n -> return (neg b n)
neg True = negate
neg False = id
Возможно, версия parsec по-прежнему интересна для обработки ошибок или интеграции в больший парсер:
parseStatement = parse statement "" . deleteMany " "
statement = sepBy1 clause (char '&')
clause = between (char '(') (char ')') x <|> x
where x = sepBy1 var (char '|')
var = do
n <- maybe False (const True) <$> optionMaybe (char '-')
v <- many1 (noneOf "&|-() ")
return (n, v)
Я поддерживаю привязки Picosat, которые вы упомянули. Увидев этот вопрос, я добавил новый picologic
пакет для Hackage, который строится поверх picosat
анализировать и преобразовывать с помощью пропозициональных формул и передавать их в решатель SAT.
import Picologic
p, q, r :: Expr
p = readExpr "~(A | B)"
q = readExpr "(A | ~B | C) & (B | D | E) & (D | F)"
r = readExpr "(φ <-> ψ)"
ps = ppExprU p
-- ¬(A ∨ B)
qs = ppExprU q
-- ((((A ∨ ¬B) ∨ C) ∧ ((B ∨ D) ∨ E)) ∧ (D ∨ F))
rs = ppExprU (cnf r)
-- ((φ ∧ (φ ∨ ¬ψ)) ∧ ((ψ ∨ ¬φ) ∧ ψ))
main :: IO ()
main = solveProp p >>= putStrLn . ppSolutions
-- ¬A ¬B
-- ¬A B
-- A ¬B