【问题标题】:Sequence of states in Haskell SBV doesn't satisfy constraintsHaskell SBV 中的状态序列不满足约束
【发布时间】:2021-02-08 18:51:18
【问题描述】:

我有一个如下符号枚举:

data State = Start | Dot
mkSymbolicEnumeration ''State

评估一个状态在序列中是否有效的函数,相对于前一个状态,被定义为sDot前面只能有sStartsStart前面只能有sDot ——理论上,这意味着我们的序列中不应该有两个连续的sStartsDot

validSequence :: SList State -> SInteger -> SBool
validSequence seq i = case seq .!! i of
    sStart -> p1 .== sDot      -- sStart can only be preceded by sDot
    sDot   -> p1 .== sStart    -- sDot can only be preceded by sStart
    where p1 = seq .!! (i-1)

然后,声明了两组约束。第一个声明seq 的长度应为n,第二组声明比每个seq !! ii /= 0 都应该满足validSequence

-- sequence should be of length n
constrain $ L.length seq .== fromIntegral n

-- apply a validSequence constraint for every i in [1..n]
mapM_ (constrain . (validSequence seq) . fromIntegral) [1..n]

当我将此模块加载到ghci 时,我得到的结果与我预期的不同:

runSMT $ answer 10
-- expecting this: [Dot, Start, Dot, Start, Dot, Start, Dot, Start, Dot, Start]
-- or this:        [Start, Dot, Start, Dot, Start, Dot, Start, Dot, Start, Dot]
-- actual result:  [Dot, Dot, Dot, Dot, Dot, Dot, Dot, Dot, Dot, Dot]

我不明白的:

  • 为什么实际结果不满足Dot 只能跟随Start 的约束
  • 特别是,我在validSequence 中做错了吗?
  • 或者,我是否以错误的方式使用了mapM_ 调用?

完整的可复现代码如下(需要SBV library):

{-# LANGUAGE DeriveAnyClass      #-}
{-# LANGUAGE DeriveDataTypeable  #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving  #-}
{-# LANGUAGE TemplateHaskell     #-}

module Sandbox where

import           Data.SBV
import           Data.SBV.Control

import           Data.SBV.List    ((.!!))
import qualified Data.SBV.List    as L


data State = Start | Dot
mkSymbolicEnumeration ''State

validSequence :: SList State -> SInteger -> SBool
validSequence seq i = case seq .!! i of
    sStart -> p1 .== sDot      -- sStart can only be preceded by sDot
    sDot   -> p1 .== sStart    -- sDot can only be preceded by sStart
    where p1 = seq .!! (i-1)


answer :: Int -> Symbolic [State]
answer n = do
    seq <- sList "seq"

    -- sequence should be of length n
    constrain $ L.length seq .== fromIntegral n

    -- apply a validSequence constraint for every i in [1..n]
    mapM_ (constrain . (validSequence seq) . fromIntegral) [1..n]

    query $ do cs <- checkSat
               case cs of
                    Unk    -> error "Solver returned unknown!"
                    DSat{} -> error "Unexpected dsat result!"
                    Unsat  -> error "Solver couldn't find a satisfiable solution"
                    Sat    -> getValue seq

【问题讨论】:

    标签: haskell solver smt sat sbv


    【解决方案1】:
    validSequence :: SList State -> SInteger -> SBool
    validSequence seq i = case seq .!! i of
        sStart -> p1 .== sDot      -- sStart can only be preceded by sDot
        sDot   -> p1 .== sStart    -- sDot can only be preceded by sStart
        where p1 = seq .!! (i-1)
    

    等价于

    validSequence :: SList State -> SInteger -> SBool
    validSequence seq i = case seq .!! i of
        _  -> p1 .== sDot
        where p1 = seq .!! (i-1)
    

    因为sStart 是一个新的局部变量的名称,它与任何其他同名变量都没有关系。在 GHC 中打开警告应该会报告此名称阴影。

    我无法建议如何解决此问题,因为我不熟悉 SBV。特别是,我看不到您尝试进行的检查 (seq .!! i) == sStart 是否可以在 Haskell 级别完成,或者必须在 SBV 级别执行,以便它生成正确的公式以传递给 SMT 求解器.

    也许你需要类似(伪代码):

    validSequence seq i = 
        (p2 .== sStart .&& p1 .== sDot) .||
        (p1 .== sStart .&& p2 .== sDot)
       where p1 = seq .!! (i-1)
             p2 = seq .!! i
    

    编辑:基于上述伪代码的实际工作实现,但遵循 SBV 的 DSL:

    validSequence :: SList State -> SInteger -> SBool
    validSequence seq i =
          ite (cur .== sStart) (prev `sElem` [sDot])
        $ ite (cur .== sDot)   (prev `sElem` [sStart])
          sFalse
        where cur  = seq .!! i
              prev = seq .!! (i-1)
    

    【讨论】:

    • 哇!我不敢相信我犯了这样的错误......随后的问题(这里是明显的 Haskell 菜鸟)是,我怎样才能解决这个问题并正确匹配 sStatesDot
    • 我设法根据您的伪代码实现了一个可行的解决方案
    猜你喜欢
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    相关资源
    最近更新 更多