尝试解决SBV对祖先关系的约束

时间:2019-01-24 17:19:22

标签: haskell z3 sbv

我正在尝试使用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)

非常感谢!

1 个答案:

答案 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使其更易于使用!