我正在尝试使用SBV Library(版本7.12)解决在Haskell中涉及祖先关系的以下csp问题:
请给我所有不来自 Stephen 的人的集合。
我的解决方案(如下所示)出现以下异常
*** Exception: SBV.Mergeable.List: No least-upper-bound for lists of differing size (1,0)
问题:是否可以使用SBV / SMT解算器来解决这样的约束?如果-我该如何提出问题?
我试图解决的问题:
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveAnyClass #-}
module Main where
import Data.SBV
data Person
= Mary
| Richard
| Claudia
| Christian
| Stephen
mkSymbolicEnumeration ''Person
-- symbolic shorthands for person constructors
[mary, richard, claudia, christian, stephen] =
map literal [Mary, Richard, Claudia, Christian, Stephen]
childOf :: [(Person, Person)]
childOf = [
(Mary, Richard) ,
(Richard, Christian),
(Christian, Stephen)]
getAncestors :: Person -> [Person]
getAncestors p = go childOf p []
where
go [] _ acc = nub acc
go ((p1, p2): rels) a acc
| p1 == p = go rels p (p2:acc) ++ getAncestors p2
| otherwise = go rels a acc
-- symbolic version of getAncestors
getSAncestors :: SBV Person -> [SBV Person]
getSAncestors p = ite (p .== mary) (map literal (getAncestors Mary))
$ ite (p .== richard) (map literal (getAncestors Richard))
$ ite (p .== claudia) (map literal (getAncestors Claudia))
$ ite (p .== christian) (map literal (getAncestors Christian))
(map literal (getAncestors Stephen))
cspAncestors :: IO AllSatResult
cspAncestors = allSat $ do
(person :: SBV Person) <- free_
constrain $ bnot $ stephen `sElem` (getSAncestors person)
非常感谢!
答案 0 :(得分:2)
您从SBV收到的错误消息确实是神秘的,不幸的是,它实际上并没有帮助。我刚刚将修补程序推送到github,我希望新的错误消息会更好:
*** Exception:
*** Data.SBV.Mergeable: Cannot merge instances of lists.
*** While trying to do a symbolic if-then-else with incompatible branch results.
***
*** Branches produce different sizes: 1 vs 0
***
*** Hint: Use the 'SList' type (and Data.SBV.List routines) to model fully symbolic lists.
SBV试图告诉您的是,当您具有符号if-then-else(如您的getSAncestor
函数中)时,它将返回{{1}的 Haskell 列表},那么除非SBV Person
的每个分支具有完全相同数量的元素,否则它无法合并这些分支。
您当然可以问为什么有这样的限制。考虑以下表达式:
ite
直觉上,这意味着:
ite cond [s0, s1] [s2, s3, s4]
不幸的是,SBV不能代替[ite cond s0 s2, ite cond s1 s3, ite cond ??? s4]
,因此也没有错误消息。我希望这是有道理的!
SBV实际上有两种表示符号项列表的方式。一个是您使用过的旧的Haskell符号值的 list ;上面我针对每个符号项目描述了基数约束。另一个是完全符号列表,映射到SMTLib序列。请注意,在这两种情况下,列表的大小都是任意的,但是是有限的,但是在我们是否象征性地对待列表的书脊方面存在差异。
使用???
之类的类型时,实际上是在说“此列表的主干是具体的,而元素本身是符号的”。当您确切知道每个分支有多少个元素且它们的大小完全相同时,此数据类型最合适。
这些列表通过后端映射到一个更简单的表示形式,实质上,“列表”部分全部在Haskell中处理,并且元素以符号方式逐点表示。这允许使用许多SMT求解器,甚至是那些不了解符号序列的求解器。另一方面,您不会发现符号脊柱。
您可以猜到的第二种是脊椎本身是象征性的列表,因此可以支持不受基数约束的任意ite条件。这些直接映射到SMTLib序列,并且更加灵活。不利的一面是,并非所有SMT求解器都支持序列,并且序列逻辑通常无法确定,因此,如果查询超出了算法可处理的范围,则求解器可能会响应[SBV a]
。 (另一个不利的方面是,z3和cvc4支持的序列逻辑还不成熟,因此求解器本身可能有bug。但是这些bug总是可报告的!)
要解决您的问题,您只需要使用SBV的书脊符号列表,称为unknown
。示例程序所需的修改相对简单:
SList
(注意:由于我使用的是可从hackage获得的SBV 8.0,因此我不得不将{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.SBV
import Data.List
import Data.SBV.List as L
data Person
= Mary
| Richard
| Claudia
| Christian
| Stephen
mkSymbolicEnumeration ''Person
-- symbolic shorthands for person constructors
[mary, richard, claudia, christian, stephen] =
map literal [Mary, Richard, Claudia, Christian, Stephen]
childOf :: [(Person, Person)]
childOf = [
(Mary, Richard) ,
(Richard, Christian),
(Christian, Stephen)]
getAncestors :: Person -> [Person]
getAncestors p = go childOf p []
where
go [] _ acc = nub acc
go ((p1, p2): rels) a acc
| p1 == p = go rels p (p2:acc) ++ getAncestors p2
| otherwise = go rels a acc
-- symbolic version of getAncestors
getSAncestors :: SBV Person -> SList Person
getSAncestors p = ite (p .== mary) (literal (getAncestors Mary))
$ ite (p .== richard) (literal (getAncestors Richard))
$ ite (p .== claudia) (literal (getAncestors Claudia))
$ ite (p .== christian) (literal (getAncestors Christian))
(literal (getAncestors Stephen))
cspAncestors :: IO AllSatResult
cspAncestors = allSat $ do
(person :: SBV Person) <- free "person"
constrain $ sNot $ L.singleton stephen `L.isInfixOf` (getSAncestors person)
更改为bnot
;该名称已更改。如果您使用的是7.12,则应保留{ {1}}。还请注意使用sNot
而不是bnot
,后者告诉SBV使用书脊符号列表。)
运行此命令时,我得到:
SList Person
我还没有真正检查过答案是否正确,但是我相信它应该是正确的! (如果没有,请报告。)
我希望能概述一下该问题以及如何解决。虽然SMT求解器无法击败自定义CSP求解器,但我认为如果没有专用算法,它可能是一个很好的选择。希望Haskell / SBV使其更易于使用!