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

Haskell 用SBV解决祖先关系约束的尝试,haskell,z3,sbv,Haskell,Z3,Sbv,我正在尝试使用(7.12版)解决以下涉及Haskell中祖先关系的csp问题: 给我一组不是斯蒂芬的后裔。 我的解决方案(见下文)得到以下异常 *** Exception: SBV.Mergeable.List: No least-upper-bound for lists of differing size (1,0) 问题:是否可以使用SBV/SMT解算器解决此类约束,如果-我需要如何制定问题 我的解决方案尝试: {-# LANGUAGE TemplateHaskell #-} {

我正在尝试使用(7.12版)解决以下涉及Haskell中祖先关系的csp问题:

给我一组不是斯蒂芬的后裔。

我的解决方案(见下文)得到以下异常

*** 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)
{-#语言模板haskell}
{-#语言独立派生}
{-#语言派生数据类型化}
{-#语言派生类{-}
模块主要在哪里
导入数据.SBV
数据员
=玛丽
|理查德
|克劳迪娅
|基督教徒
|斯蒂芬
mkSymbolicEnumeration“个人
--person构造函数的符号速记
[玛丽、理查德、克劳迪娅、克里斯蒂安、斯蒂芬]=
地图文字[Mary,Richard,Claudia,Christian,Stephen]
子女::[(人,人)]
childOf=[
(玛丽、理查德),
(基督教理查德),
(克里斯汀·斯蒂芬)]
GetFounders::Person->[Person]
getp=gochildofp[]
哪里
go[]uuACC=nub acc
go((p1,p2):相对)a acc
|p1==p=go rels p(p2:acc)++getp2
|否则=go rels a acc
--GetFounders的符号版本
getSAncestors::SBV人员->[SBV人员]
getSAncestors p=ite(p.==mary)(映射文字(getSAncestors-mary))
$ite(p.==richard)(映射文字(getrichard))
$ite(p.==claudia)(映射文字(getClaudia))
$ite(p.==christian)(地图文字(getChristian))
(映射文字(gets))
CSP::IO AllSatResult
csp=allSat$do

(person::SBV person)您从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
函数中)返回
SBV人员的Haskell列表时,它无法合并这些分支,除非
ite
的每个分支具有完全相同的元素数

问题 当然,你可能会问,为什么会有这样的限制。考虑下面的表达式:

ite cond [s0, s1] [s2, s3, s4]
直觉上,这意味着:

[ite cond s0 s2, ite cond s1 s3, ite cond ??? s4]
不幸的是,SBV无法替代
,因此出现了错误消息。我希望这是有道理的

两类列表 SBV实际上有两种方式来表示符号项列表。一个是一个很好的Haskell符号值列表,正如您使用的那样;它受我上面为每个符号ite描述的基数约束。另一个是完全符号列表,映射到SMTLib序列。请注意,在这两种情况下,列表的大小都是任意的,但有限的,但我们是否象征性地对待列表的脊线是有区别的

脊椎混凝土符号列表 当您使用像
[SBV a]
这样的类型时,您实际上是在说“此列表的脊椎是具体的,而元素本身是符号性的。”当您确切地知道每个分支上有多少个元素并且它们的大小完全相同时,此数据类型最合适

这些列表通过后端映射到一个简单得多的表示,本质上“列表”部分都在Haskell中处理,元素以符号方式逐点表示。这允许使用许多SMT解算器,即使是那些不理解符号序列的解算器。另一方面,您不能像发现的那样拥有符号脊椎

脊椎符号列表 第二类,正如您所猜测的,是脊椎本身是符号的列表,因此可以支持没有基数约束的任意ite条件。这些直接映射到SMTLib序列,并且更加灵活。不利的一面是,并非所有SMT解算器都支持序列,而且序列逻辑通常不可判定,因此,如果查询超出其算法所能处理的范围,解算器可能会响应
unknown
。(另一个不利的方面是,z3和cvc4支持的序列逻辑相当不成熟,因此解算器本身可能有bug。但这些bug总是可以报告的!)

解决方案 要解决您的问题,只需使用SBV的脊椎符号列表,即
SList
。示例程序所需的修改相对简单:

{-#语言模板haskell}
{-#语言独立派生}
{-#语言派生数据类型化}
{-#语言派生类{-}
{-#语言范围类型变量#-}
导入数据.SBV
导入数据。列表
将Data.SBV.List导入为L
数据员
=玛丽
|理查德
|克劳迪娅
|基督教徒
|斯蒂芬
mkSymbolicEnumeration“个人
--person构造函数的符号速记
[玛丽、理查德、克劳迪娅、克里斯蒂安、斯蒂芬]=
地图文字[Mary,Richard,Claudia,Christian,Stephen]
子女::[(人,人)]
childOf=[
(玛丽、理查德),
(基督教理查德),
(克里斯汀·斯蒂芬)]
GetFounders::Person->[Person]
getp=gochildofp[]
哪里
go[]uuACC=nub acc
go((p1,p2):相对)a acc
|p1==p=go rels p(p2:acc)++getp2
|否则=go rels a acc
--GetFounders的符号版本
getSAncestors::SBV人员->SList人员
getSAncestors p=ite(p.==mary)(字面意思(getSAncestors-mary))
$ite(p.==richard)(文字(getrichard))
$ite(p.==claudia)(字面意思(claudia))
$ite(p.==christian)(字面意思(基督教徒))
(字面意义(获取)
CSP::IO AllSatResult
csp=allSat$do
(人::SBV
*Main> cspAncestors
Solution #1:
  person = Claudia :: Person
Solution #2:
  person = Stephen :: Person
Found 2 different solutions.