【Kind2(基于SMT的自动模型检查器)学习笔记】进阶范例

245 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

范例一:三位二进制转十进制

代码:

-- 将布尔结果转换为数值,true为1,false为0
function ToBit(b: bool) returns (n: int) ;
let
   n = if b then 1 else 0 ;
tel

-- 主函数,将输入的布尔值转换为数值
-- 输入参数:b2代表第三位,b1代表第二位,b0代表第一位
-- 输出参数:n表示转换的结果
node ToInt(b2, b1, b0: bool) returns (n: int) ;
let
   n = 4 * ToBit(b2) + 2 * ToBit(b1) + ToBit(b0) ;
tel

Simulate执行结果:

在这里插入图片描述

范例二:递减计数器

----------------------------------------------------------------------
-- 如果输入的Restart的值为true,则返回输入的Time的值
-- 否则判断前一时刻返回值Ctr是否大于0
-- 如果Ctr大于0,则返回上一时刻返回值-1后的值
-- 否则返回上一时刻返回值
----------------------------------------------------------------------

node DownCounter( Time : int; Restart : bool ) returns ( Ctr : int );
let
  Ctr = Time -> if Restart then Time
                else if (pre Ctr) > 0 then (pre Ctr) - 1
                else pre Ctr;
tel

Simulate执行结果:

在这里插入图片描述

范例三:两个互不影响的递减计数器

代码:


node DownCounter( Time : int; Restart : bool ) returns ( Ctr : int );
let
  Ctr = Time -> if Restart then Time
                else if (pre Ctr) > 0 then (pre Ctr) - 1
                else pre Ctr;
tel


node DownCounters( Time1,Time2 : int; Restart : bool )
returns ( Ctr1,Ctr2 : int );
let
  Ctr1 = DownCounter(Time1,Restart);
  Ctr2 = DownCounter(Time2,Restart);
tel

Simulate执行结果: 在这里插入图片描述

范例四:按递增顺序,依次在每个时刻输出,大于初始时刻输入值的偶数

代码:


node Even(N: int) returns (B: bool);
let
  B = (N mod 2 = 0);
tel


node EvensFrom (X: int) returns (E: int);
let
-- 这里注意一下,初始时刻,执行E = if Even(X) then X + 2 else X + 1,以后的时刻,执行的都是E = (pre E) + 2
  E = (if Even(X) then X + 2 else X + 1) -> (pre E) + 2;
tel

Simulate执行结果:

在这里插入图片描述

EvensFrom的输出结果E只与上一时刻的E有关,而初始时刻的E只与初始时刻的输入X有关,因此,任意时刻的E都由初始时刻的X决定,而与以后时刻的X无关

范例五:从0开始计算每一个数的阶乘

代码:


node fact(_ : bool) returns (F: int);
var N: int;
let
-- 数字从0开始进行阶乘,后面逐一加一
  N = 0 -> (pre N) + 1;
-- 0的阶乘为0,因此阶乘结果从1开始,以后逐一乘前一时刻的数值
  F = 1 ->  N * (pre F);
tel

Simulate执行结果: 在这里插入图片描述

当我们不需要结点的输入值时,我们用_ : bool来表示一个没有意义的输入,哪怕我们在Simulate的运行面板里面填写了true或false,对结点的运行也不会有任何影响

范例六:斐波纳契

node Fibonacci(_:bool) returns(Fib: int);
let
   Fib = 1 -> pre (1 -> Fib + pre Fib);
tel

Simulate执行结果: 在这里插入图片描述 因为用到了较多的pre和->,不易理解,下面对语句进行拆分解释

  • Fib在时间轴上的序列为:1,1,2,3,5,8,13,21,...
  • pre Fib在时间轴上的序列为:0,1,1,2,3,5,8,13,21,...
  • Fib + pre Fib在时间轴上的序列为:1,2,3,5,8,13,21,...
  • 1 -> Fib + pre Fib表示初始时刻的值为1,以后时刻的值为Fib + pre Fib,在时间轴上的序列为:1,2,3,5,8,13,21,...
  • pre (1 -> Fib + pre Fib)表示当前时刻的1 -> Fib + pre Fib的值的前一个值,如果是初始时刻,则为0或false,在时间轴上的序列为:0,1,2,3,5,8,13,21,...
  • Fib = 1 -> pre (1 -> Fib + pre Fib)表示初始时刻的值为1,以后时刻的值为pre (1 -> Fib + pre Fib),在时间轴上的序列为:1,1,2,3,5,8,13,21,...

范例七:开关按钮

问题描述:Set为ture表示开,Reset为ture表示关,Init只在初始时刻起作用

代码

node Switch( Set, Reset, Init : bool ) returns ( X : bool );
let
    X =      if Set then true
        else if Reset then false
        else (Init -> pre X);
tel

Simulate执行结果: 在这里插入图片描述

这里解释一下else (Init -> pre X)这段代码:Init -> pre X表示Time=0的时候,如果执行这段代码,结果为Init的值,其余时刻,执行这段代码,结果为pre X的值。重点注意,时TIme=0,而不是第一次执行,就是Time=0的时候如果没有执行到else (Init -> pre X),那么,以后执行到这里时,都是执行pre X,不再执行Init。

范例八:验证开关按钮代码

node Switch( Set, Reset, Init : bool ) returns ( X : bool );
let
    X =      if Set then true
        else if Reset then false
        else (Init -> pre X);
tel


node SwitchReq( Set, Reset, Reset2, Init : bool )
returns ( R1, R2, R3, R4 : bool ) ;
var X : bool;
let
-- 调用开关按钮,将结果赋值给X
-- X中文表示为开关是否打开,ture为开,false为关
  X = Switch( Set, Reset, Init ) ;
  
-- 验证在任一时刻,如果Set的值为true,那么X的值也为true
-- 如果 Set => X的结果为true,表示式子有效,--%PROPERTY R1的结果为valid
-- 如果 Set => X的结果为false,表示式子无效,--%PROPERTY R1的结果为falsevalid
-- R1的式子应该是有效的,因为当我们按下打开按钮时,开关会被打开
  R1 = Set => X ;

-- 验证在任一时刻,如果Set的值不为true且Reset的值为true,那么X的值也不为true
  R2 = (not Set and Reset) => not X ;

-- 验证在任一时刻,如果Set的值不为true且Reset的值不为true,那么X的值等于上一时刻的X的值
  R3 = true -> ((not Set and not Reset) => X = pre X) ;

-- 验证在任一时刻,如果Set的值为true,那么Switch(Set, Reset, Init)和Switch(Set, Reset2, Init)相同
  R4 = Set => Switch(Set, Reset, Init) = Switch(Set, Reset2, Init) ;

--%MAIN;
--%PROPERTY R1;
--%PROPERTY R2;
--%PROPERTY R3;
--%PROPERTY R4;
tel

Check验证结果: 在这里插入图片描述

范例九:交通灯

问题描述:

  • 按钮规则:初始状态,绿色信号灯亮起(Green的值为true);按下按钮(Button的值为true),当前时刻,黄色信号灯亮起(Yellow的值为true),下一时刻,红色信号灯亮起(Red的值为true),十个时刻之后,绿色信号灯一直保持亮起状态。
  • 行人是否可以通行规则:
    • 当绿色或黄色的信号灯亮起的所有时刻,行人不可以通行(DontWalk的值为true)
    • 当绿色的信号灯在下一时刻将要亮起时,当前时刻,行人不可以通行(DontWalk的值为true)
    • 当红色的信号灯刚刚亮起时,当前时刻,行人不可以通行(DontWalk的值为true)
    • 当红色的信号灯刚刚亮起时,下一时刻,行人可以通行(Walk的值为true)

代码:

node TrafficLight( Button: bool )
returns ( Red, Yellow, Green, Walk, DontWalk: bool );

var Phase, prePhase: int;
let
  prePhase = 0 -> pre Phase;
  Phase    = if Button then
               1
             else if 0 < prePhase and prePhase < 10 then
               prePhase + 1
             else
               0;

  Green    = Phase = 0;
  Yellow   = Phase = 1;
  Red      = Phase > 1;

  Walk     = Phase > 2 and Phase < 10;
  DontWalk = not Walk;
tel

Simulate执行结果:

在这里插入图片描述

  • 我们可以看到,在Time=3的时刻,输入的Button的值为true,此时信号灯Yellow的值变为true,然后下一时刻,信号灯Red的值变为true,直到Time=13的时刻,信号灯Green的值变为true
  • 回到题目中的四个时刻:
    • Time = 0-3和13-29:当绿色或黄色的信号灯亮起的所有时刻,行人不可以通行(DontWalk的值为true)
    • Time = 12:当绿色的信号灯在下一时刻将要亮起时,当前时刻,行人不可以通行(DontWalk的值为true)
    • Time = 4:当红色的信号灯刚刚亮起时,当前时刻,行人不可以通行(DontWalk的值为true)
    • Time = 4:当红色的信号灯刚刚亮起时,下一时刻,行人可以通行(Walk的值为true)

范例十:验证交通灯代码

代码:

node TrafficLight( Button: bool )
returns ( Red, Yellow, Green, Walk, DontWalk: bool );

var Phase, prePhase: int;
let
  prePhase = 0 -> pre Phase;
  Phase    = if Button then
               1
             else if 0 < prePhase and prePhase < 10 then
               prePhase + 1
             else
               0;

  Green    = Phase = 0;
  Yellow   = Phase = 1;
  Red      = Phase > 1;

  Walk     = Phase > 2 and Phase < 10;
  DontWalk = not Walk;
tel


node ReqTrafficLight( Button : bool )
returns (R1, R2, R3, R4, R5, R6, R7, R8, R9, R10: bool);

var  CarsAllowed, Red, Yellow, Green, Walk, DontWalk : bool;
let
-- 根据按钮Button是否被按下,获取信号灯和能否通行的值
  (Red, Yellow, Green, Walk, DontWalk) = TrafficLight(Button);

-- CarsAllowed表示车辆是否可以通行
  CarsAllowed = Green or Yellow;

-- Walk是根据Phase的值获取的,表示行人是否可以通行
-- CarsAllowed是根据信号灯的值获取的,表示车辆是否可以通行
-- 因为按照规则,在任一时刻,行人和车辆不允许同时通行的
-- 所以我们用R1来验证代码是否符合这一规则
-- 如果验证结果为valid,则表示代码符合这一规则
  R1 = not (CarsAllowed and Walk);
  
-- R2验证在任一时刻,红灯和绿灯不可能会同时出现
-- 只要R2的结果为ture,那就表示没有同时出现
-- 按照规则他们是不可以同时出现的
-- 如果验证结果为valid,则表示代码符合这一规则
  R2 = not (Red and Green);
  
-- R3验证在任一时刻,至少一个信号灯会一直显示
-- 如果验证结果为valid,则表示代码符合这一规则
-- 要注意,R3并不能证明只有一个信号灯会一直显示,多个信号灯同时显示的话,R3也为true
  R3 = Red or Yellow or Green;
  
-- R4验证在任一时刻,当行人可以通行时,红色信号灯一定是亮的
-- 验证结果应该为valid
  R4 = Walk => Red;
  
-- R5验证在任一时刻,行人可以通行和不可以通行的情况是否没有可能同时出现
-- 验证结果应该为valid
  R5 = Walk xor DontWalk;
  
-- R6验证在任一时刻,都不满足,当前时刻红灯亮且前一时刻绿灯亮
-- 验证结果应该为valid
  R6 = true -> not (Red and pre Green);
  
  
-- R7验证在任一时刻,都不满足,当前时刻行人可以通行且前一时刻车辆可以通行
-- 验证结果应该为valid
  R7 = true -> not (Walk and pre CarsAllowed);
  
-- R8验证在任一时刻,都不满足,当前时刻车辆可以通行且前一时刻行人可以通行
-- 验证结果应该为falsifiable
-- 因为在行人可以通行的一个时刻,是车辆和行人均不可通行的时刻
  R8 = true -> not (CarsAllowed and pre Walk);
  
-- R9验证在任一时刻,都不满足,当前时刻和前一时刻黄色信号灯都在亮
-- 验证结果应该为falsifiable
-- 因为如果连续两个时刻按下按钮(Button的值为true),则此时当前时刻和前一时刻黄色信号灯都在亮
  R9 = true -> not (Yellow and pre Yellow);
  
-- R10验证在任一时刻,都满足,只要前一时刻红色信号灯在亮且当前时刻红色信号灯不亮
-- 则当前时刻绿色信号灯就会亮
-- 验证结果应该为falsifiable
-- 因为前一时刻红色信号灯在亮,当前时刻红色信号灯不亮
-- 如果当前时刻按下按钮,亮的也可能是黄色信号灯
  R10 = true -> (pre Red and not Red) => Green;
  
--%MAIN;
--%PROPERTY R1;
--%PROPERTY R2;
--%PROPERTY R3;
--%PROPERTY R4;
--%PROPERTY R5;
--%PROPERTY R6;
--%PROPERTY R7;
--%PROPERTY R8;
--%PROPERTY R9;
--%PROPERTY R10;
tel

Check验证结果: 在这里插入图片描述