从DieHard分析TLA+是如何计算状态的

简介: 从DieHard分析TLA+是如何计算状态的

TLC计算状态的算法

TLC维护两个变量:seen和sq。seen用来记录已经处理过的状态;sq记录将要计算的状态;

问题

  • 描述两个水壶所有可能的状态,通过TLA+的宽度遍历机制,暴力搜索所有的状态序列(TLA+中称为behavior)
  • 给 3 加仑水壶灌满水
  • 给 5 加仑水壶灌满水
  • 清空 3 加仑水壶
  • 清空 5 加仑水壶
  • 将 3 加仑水壶的水倒到 5 加仑水壶
  • 将 5 加仑水壶的水倒到 3 加仑水壶

TLA+代码

为了方便调试,在每个子句开始时打印了状态。

-------------------------- MODULE DieHard  --------------------------
EXTENDS Naturals, TLC
VARIABLES small,big
S == <<small, big>>
Init == /\ Print("Init", TRUE)
        /\ small = 0
        /\ big = 0
TypeOK == /\ Print("TypeOK", TRUE)   
          /\ Print(S, TRUE) 
          /\ small \in 0..3
          /\ big \in 0..5
FillSmall == /\ Print("FillSmall", TRUE) 
             /\ Print(S, TRUE) 
             /\ small' = 3
             /\ big' = big
             /\ Print(S, TRUE) 
FillBig == /\ Print("FillBig", TRUE)  
           /\ Print(S, TRUE) 
           /\ small' = small
           /\ big' = 5
           /\ Print(S, TRUE) 
EmptySmall == /\ Print("EmptySmall", TRUE)  
              /\ Print(S, TRUE) 
              /\ small' = 0
              /\ big' = big
              /\ Print(S, TRUE) 
EmptyBig ==  /\ Print("EmptyBig", TRUE)
             /\ Print(S, TRUE)   
             /\ big' = 0
             /\ small' = small
             /\ Print(S, TRUE) 
SmallToBig ==  /\ Print("SmallToBig", TRUE)
               /\ Print(S, TRUE)  
               /\ IF small + big < 5
                  THEN
                    /\ big' = small + big
                    /\ small' = 0
                    /\ Print(S, TRUE) 
                  ELSE
                    /\ big' = 5
                    /\ small' = small - (5 - big)
                    /\ Print(S, TRUE) 
BigToSmall == /\ Print("BigToSmall", TRUE)
              /\ Print(S, TRUE)  
              /\ IF small + big < 3
                  THEN
                    /\ small' = small + big
                    /\ big' = 0
                    /\ Print(S, TRUE) 
                  ELSE
                    /\ small' = 3
                    /\ big' = big - (3 - small)
                    /\ Print(S, TRUE) 
Next == \/ FillSmall
        \/ FillBig
        \/ SmallToBig
        \/ BigToSmall
        \/ EmptyBig
        \/ EmptySmall
=============================================================================
\* Modification History
\* Last modified Wed Jul 18 15:47:26 CST 2018 by max
\* Created Wed Apr 25 21:42:59 CST 2018 by max

运行结果分析

  1. Init生成的状态加入sq中;
  2. 从sq中取出一个状态,一次执行Next的几个子句,每执行一个子句就会得到一个新状态,加入sq中;
  3. 每次得到新状态都要进行invariant的校验;
  4. 如果新状态曾经被计算过(seen),直接丢掉,也不进行invariant的计算;
========================   这是第1轮 ========================
==== 初始状态是<<0, 0>>
"Init"  TRUE
"TypeOK"  TRUE
<<0, 0>>  TRUE
"FillSmall"  TRUE
<<0, 0>>  TRUE
<<0, 0>>  TRUE
"TypeOK"  TRUE
<<3, 0>>  TRUE 得到新状态<<3, 0>>
"FillBig"  TRUE
<<0, 0>>  TRUE
<<0, 0>>  TRUE
"TypeOK"  TRUE
<<0, 5>>  TRUE  得到新状态<<0, 5>>
"SmallToBig"  TRUE
<<0, 0>>  TRUE
<<0, 0>>  TRUE
"BigToSmall"  TRUE
<<0, 0>>  TRUE
<<0, 0>>  TRUE  \* <<0, 0>>这个状态在INIT之后就已经在seen中了,被处理过了。因此这个地方直接丢掉这个状态:不进入sq;不进行invariant校验;
"EmptyBig"  TRUE
<<0, 0>>  TRUE
<<0, 0>>  TRUE
"EmptySmall"  TRUE
<<0, 0>>  TRUE
<<0, 0>>  TRUE
第1轮总结:本轮新增状态<<3, 0>> 和 <<0, 5>>
========================   开始第2轮 ========================
==== 2.1 从sq中取出第一个状态<<3, 0>>,开始计算Next
"FillSmall"  TRUE
<<3, 0>>  TRUE
<<3, 0>>  TRUE
"FillBig"  TRUE
<<3, 0>>  TRUE
<<3, 0>>  TRUE
"TypeOK"  TRUE
<<3, 5>>  TRUE  得到新状态<<3, 5>>
"SmallToBig"  TRUE
<<3, 0>>  TRUE
<<3, 0>>  TRUE
"TypeOK"  TRUE
<<0, 3>>  TRUE  得到新状态<<0, 3>>
"BigToSmall"  TRUE
<<3, 0>>  TRUE
<<3, 0>>  TRUE
"EmptyBig"  TRUE
<<3, 0>>  TRUE
<<3, 0>>  TRUE
"EmptySmall"  TRUE
<<3, 0>>  TRUE
<<3, 0>>  TRUE
==== 2.2 从sq中取出第一个状态<<0, 5>>,开始计算Next
"FillSmall"  TRUE
<<0, 5>>  TRUE
<<0, 5>>  TRUE
"FillBig"  TRUE
<<0, 5>>  TRUE
<<0, 5>>  TRUE
"SmallToBig"  TRUE
<<0, 5>>  TRUE
<<0, 5>>  TRUE
"BigToSmall"  TRUE
<<0, 5>>  TRUE
<<0, 5>>  TRUE
"TypeOK"  TRUE   
<<3, 2>>  TRUE  得到新状态<<3,2>>
"EmptyBig"  TRUE
<<0, 5>>  TRUE
<<0, 5>>  TRUE
"EmptySmall"  TRUE
<<0, 5>>  TRUE
<<0, 5>>  TRUE
"FillSmall"  TRUE
<<3, 5>>  TRUE
<<3, 5>>  TRUE
"FillBig"  TRUE
<<3, 5>>  TRUE
<<3, 5>>  TRUE
第2轮总结:本轮新增状态 <<3, 5>>,  <<0, 3>>, <<3,2>>
========================   开始第3轮 ========================
==== 3.1 从sq中取出第一个状态<<3, 5>>,开始计算Next
"SmallToBig"  TRUE
<<3, 5>>  TRUE
<<3, 5>>  TRUE
"BigToSmall"  TRUE
<<3, 5>>  TRUE
<<3, 5>>  TRUE
"EmptyBig"  TRUE
<<3, 5>>  TRUE
<<3, 5>>  TRUE
"EmptySmall"  TRUE
<<3, 5>>  TRUE
<<3, 5>>  TRUE
==== 3.2 从sq中取出第一个状态<<0, 3>>,开始计算Next
"FillSmall"  TRUE
<<0, 3>>  TRUE
<<0, 3>>  TRUE
"TypeOK"  TRUE
<<3, 3>>  TRUE  得到新状态<<3, 3>>
"FillBig"  TRUE
<<0, 3>>  TRUE
<<0, 3>>  TRUE
"SmallToBig"  TRUE
<<0, 3>>  TRUE
<<0, 3>>  TRUE
"BigToSmall"  TRUE
<<0, 3>>  TRUE
<<0, 3>>  TRUE
"EmptyBig"  TRUE
<<0, 3>>  TRUE
<<0, 3>>  TRUE
"EmptySmall"  TRUE
<<0, 3>>  TRUE
<<0, 3>>  TRUE
==== 3.3 从sq中取出第一个状态<<3, 2>>,开始计算Next
"FillSmall"  TRUE
<<3, 2>>  TRUE
<<3, 2>>  TRUE
"FillBig"  TRUE
<<3, 2>>  TRUE
<<3, 2>>  TRUE
"SmallToBig"  TRUE
<<3, 2>>  TRUE
<<3, 2>>  TRUE
"BigToSmall"  TRUE
<<3, 2>>  TRUE
<<3, 2>>  TRUE
"EmptyBig"  TRUE
<<3, 2>>  TRUE
<<3, 2>>  TRUE
"EmptySmall"  TRUE
<<3, 2>>  TRUE
<<3, 2>>  TRUE
"TypeOK"  TRUE
<<0, 2>>  TRUE  得到新状态<<0, 2>>
第3轮总结:本轮新增状态<<3, 3>>和<<0, 2>>
========================   开始第4轮 ========================
==== 4.1 从sq中取出第一个状态<<3, 3>>,开始计算Next
"FillSmall"  TRUE
<<3, 3>>  TRUE
<<3, 3>>  TRUE
"FillBig"  TRUE
<<3, 3>>  TRUE
<<3, 3>>  TRUE
"SmallToBig"  TRUE
<<3, 3>>  TRUE
<<3, 3>>  TRUE
"TypeOK"  TRUE
<<1, 5>>  TRUE  得到新状态<<1, 5>>
"BigToSmall"  TRUE
<<3, 3>>  TRUE
<<3, 3>>  TRUE
"EmptyBig"  TRUE
<<3, 3>>  TRUE
<<3, 3>>  TRUE
"EmptySmall"  TRUE
<<3, 3>>  TRUE
<<3, 3>>  TRUE
==== 4.2 从sq中取出第一个状态<<0, 2>>,开始计算Next
"FillSmall"  TRUE
<<0, 2>>  TRUE
<<0, 2>>  TRUE
"FillBig"  TRUE
<<0, 2>>  TRUE
<<0, 2>>  TRUE
"SmallToBig"  TRUE
<<0, 2>>  TRUE
<<0, 2>>  TRUE
"BigToSmall"  TRUE
<<0, 2>>  TRUE
<<0, 2>>  TRUE
"TypeOK"  TRUE
<<2, 0>>  TRUE  得到新状态<<2, 0>>
"EmptyBig"  TRUE
<<0, 2>>  TRUE
<<0, 2>>  TRUE
"EmptySmall"  TRUE
<<0, 2>>  TRUE
<<0, 2>>  TRUE
第4轮总结:本轮新增状态<<1, 5>>和<<2, 0>>
========================   开始第5轮 ========================
==== 5.1 从sq中取出第一个状态<<1, 5>>,开始计算Next
"FillSmall"  TRUE
<<1, 5>>  TRUE
<<1, 5>>  TRUE
"FillBig"  TRUE
<<1, 5>>  TRUE
<<1, 5>>  TRUE
"SmallToBig"  TRUE
<<1, 5>>  TRUE
<<1, 5>>  TRUE
"BigToSmall"  TRUE
<<1, 5>>  TRUE
<<1, 5>>  TRUE
"EmptyBig"  TRUE
<<1, 5>>  TRUE
<<1, 5>>  TRUE
"TypeOK"  TRUE
<<1, 0>>  TRUE   得到新状态<<1, 0>>
"EmptySmall"  TRUE
<<1, 5>>  TRUE
<<1, 5>>  TRUE
==== 5.1 从sq中取出第一个状态<<2, 0>>,开始计算Next
"FillSmall"  TRUE
<<2, 0>>  TRUE
<<2, 0>>  TRUE
"FillBig"  TRUE
<<2, 0>>  TRUE
<<2, 0>>  TRUE
"TypeOK"  TRUE
<<2, 5>>  TRUE  得到新状态<<2, 5>>
"SmallToBig"  TRUE
<<2, 0>>  TRUE
<<2, 0>>  TRUE
"BigToSmall"  TRUE
<<2, 0>>  TRUE
<<2, 0>>  TRUE
"EmptyBig"  TRUE
<<2, 0>>  TRUE
<<2, 0>>  TRUE
"EmptySmall"  TRUE
<<2, 0>>  TRUE
<<2, 0>>  TRUE
第5轮总结:本轮新增状态 得到新状态<<1, 0>>和<<2, 5>>
========================   开始第6轮 ========================
==== 6.1 从sq中取出第一个状态<<1, 0>>,开始计算Next
"FillSmall"  TRUE
<<1, 0>>  TRUE
<<1, 0>>  TRUE
"FillBig"  TRUE
<<1, 0>>  TRUE
<<1, 0>>  TRUE
"SmallToBig"  TRUE
<<1, 0>>  TRUE
<<1, 0>>  TRUE
"TypeOK"  TRUE
<<0, 1>>  TRUE   得到新状态<<0, 1>>
"BigToSmall"  TRUE
<<1, 0>>  TRUE
<<1, 0>>  TRUE
"EmptyBig"  TRUE
<<1, 0>>  TRUE
<<1, 0>>  TRUE
"EmptySmall"  TRUE
<<1, 0>>  TRUE
<<1, 0>>  TRUE
==== 6.3 从sq中取出第一个状态<<2, 5>>,开始计算Next
"FillSmall"  TRUE
<<2, 5>>  TRUE
<<2, 5>>  TRUE
"FillBig"  TRUE
<<2, 5>>  TRUE
<<2, 5>>  TRUE
"SmallToBig"  TRUE
<<2, 5>>  TRUE
<<2, 5>>  TRUE
"BigToSmall"  TRUE
<<2, 5>>  TRUE
<<2, 5>>  TRUE
"TypeOK"  TRUE
<<3, 4>>  TRUE 得到新状态 <<3, 4>>

从时序的角度看TLA的实行过程

Next == A \/ B \/ C

TLC使用宽度遍历:

1. 第1层遍历后得到的时序是:T1 = A 或 B 或 C;

2. 第2层遍历是基于第1层时序的:T2 = T1 x (A B C),也就是 T2 = AA 或 AB 或 AC 或 BA 或 BB 或 BC 或 CA 或 CB 或CC;

3. 第3层遍历是基于第2层时序的:T3 = T2 x (A B C),也就是 T3 = AAA ABA ACA BAA BBA BCA CAA CBA CCA | AAB ABB ACB BAB BBB BCB CAB CBB CCB | AAC ABC ACC BAC BBC BCC CAC CBC CCC

可以看到,TLC会遍历所有可能出现的时序组合。对于一个子句来说,可以在任意时刻发生这个动作。

相关文章
|
7月前
|
资源调度 并行计算 数据可视化
【视频】马尔可夫链原理可视化解释与R语言区制转换MRS实例|数据分享
【视频】马尔可夫链原理可视化解释与R语言区制转换MRS实例|数据分享
|
7月前
|
存储 算法 数据挖掘
R语言和Python用泊松过程扩展:霍克斯过程Hawkes Processes分析比特币交易数据订单到达自激过程时间序列
R语言和Python用泊松过程扩展:霍克斯过程Hawkes Processes分析比特币交易数据订单到达自激过程时间序列
|
7月前
|
存储 消息中间件 分布式计算
流计算中的状态管理是什么?请解释其作用和常用方法。
流计算中的状态管理是什么?请解释其作用和常用方法。
96 0
|
存储 数据可视化 数据挖掘
知识点丨重测序数据进行kinship亲缘关系分析、构建IBS矩阵的方法与介绍
知识点丨重测序数据进行kinship亲缘关系分析、构建IBS矩阵的方法与介绍
知识点丨重测序数据进行kinship亲缘关系分析、构建IBS矩阵的方法与介绍
|
算法 机器人 数据挖掘
量化合约套利机器人策略开发部署执行逻辑源码实例分析
量化合约套利机器人策略开发部署执行逻辑源码实例分析
|
监控 程序员 C++
[虚幻引擎] UE里面监控每帧循环里面 C++ 函数的性能,监控函数效率,函数执行时间。
在使用C++开发UE引擎,有时候需要监控函数的执行的执行效率,这个时候有两种方式可以使用。
213 0
|
运维 监控 算法
【状态估计】用于描述符 LTI 和 LPV 系统的分析、状态估计和故障检测的算法(Matlab代码实现)
【状态估计】用于描述符 LTI 和 LPV 系统的分析、状态估计和故障检测的算法(Matlab代码实现)
134 0
HDLBits练习汇总-11-时序逻辑设计测试--计数器
HDLBits练习汇总-11-时序逻辑设计测试--计数器
218 0
HDLBits练习汇总-11-时序逻辑设计测试--计数器
SAP QM初阶之检验批系统状态中的CTCM该如何消除?
SAP QM初阶之检验批系统状态中的CTCM该如何消除?
SAP QM初阶之检验批系统状态中的CTCM该如何消除?
|
机器学习/深度学习 算法 Windows
【计算理论】计算复杂性 ( 阶段总结 | 计算理论内容概览 | 计算问题的有效性 | 语言与算法模型 | 可计算性与可判定性 | 可判定性与有效性 | 语言分类 ) ★
【计算理论】计算复杂性 ( 阶段总结 | 计算理论内容概览 | 计算问题的有效性 | 语言与算法模型 | 可计算性与可判定性 | 可判定性与有效性 | 语言分类 ) ★
198 0
【计算理论】计算复杂性 ( 阶段总结 | 计算理论内容概览 | 计算问题的有效性 | 语言与算法模型 | 可计算性与可判定性 | 可判定性与有效性 | 语言分类 ) ★