"断言可能会失败"并且前提条件没有解决它

时间:2018-01-29 12:00:22

标签: gnat spark-ada spark-2014

我有一个功能,通过简单检查信号是否在给定的容差范围内来监控受控信号。该函数称为is_within_limits。我有一个名为is_within_expanded_limits的第二个函数执行相同操作,但事先将扩展因子(扩展公差带)应用于配置的限制。通过目标值和该值的最大偏差或下阈值和上阈值,在monitor.config中配置限制。枚举monitor.config.monitoring_mode指定如何指定有效范围。

首先看一下代码。

规范

subtype Float_Signed1000 is Float range -1_000.0 .. 1_000.0;
subtype Float_Signed10000 is Float range -10_000.0 .. 10_000.0;

type Monitor_Config_T is record
   monitoring_mode : Monitoring_Mode_T := mean_based;

   mean : Float_Signed1000 := 0.0;
   maximum_deviation : Float range Float'Small .. 1_000.0 := 100.0e-3;
   lower_threshold : Float_Signed1000 := -100.0e-3;
   upper_threshold : Float_Signed1000 := 100.0e-3;

   settling_tolerance_expansion : Float range (1.0 + Float'Small) .. 2.0 := 1.2;

   startup_time : Time_Span := Milliseconds (5);
   settling_time : Time_Span := Milliseconds (2);
   violation_time : Time_Span := Milliseconds (5);
   retry_time : Time_Span := Milliseconds (100);
end record;

type Monitor_T is record
   config : Monitor_Config_T;
   timer : Time_Span := Milliseconds (0);
   current_state : Monitor_State_T := reset;
   next_state : Monitor_State_T := reset;
end record;

function is_within_expanded_limits (monitor : in Monitor_T; 
                                 signal_value : in Float_Signed1000) 
                                 return Boolean
  with Pre => (if monitor.config.monitoring_mode = mean_based then
                 monitor.config.maximum_deviation > 0.0)
              and then (if monitor.config.monitoring_mode = threshold_based then
                           monitor.config.lower_threshold < 
                               monitor.config.upper_threshold)
              and then monitor.config.settling_tolerance_expansion > 1.0;

实施

function is_within_expanded_limits (monitor : in Monitor_T; 
                                  signal_value : in Float_Signed1000) 
                                  return Boolean is
     within_expanded_limits : Boolean := False;

     expanded_lower_threshold : Float Float_Signed10000;
     expanded_upper_threshold : Float Float_Signed10000;
begin
   case monitor.config.monitoring_mode is
      when mean_based =>
         if abs (monitor.config.mean - signal_value) <= 
            (monitor.config.maximum_deviation * 
                monitor.config.settling_tolerance_expansion) then
           within_expanded_limits := True;
        end if;

      when threshold_based =>
         --  Added due to recommendation by Jacob Sparre Andersen
         --  Assertion is proved successfully
         --  pragma Assert (monitor.config.lower_threshold < monitor.config.upper_threshold);

         --  Added due to recommendation by Martin Becker
         --  Adding this assumption still causes the assertion to fail
         --  pragma Assume (monitor.config.lower_threshold = 10.0);

         --  Adding this assumption still causes the assertion to fail
         --  pragma Assume (monitor.config.upper_threshold = 11.0);

         --  Replacing the assumption with this one results in the assertion being proved
         --  pragma Assume (monitor.config.upper_threshold = 20.0);

         --  Calculate expanded thresholds
         if monitor.config.lower_threshold >= 0.0 then
            expanded_lower_threshold := monitor.config.lower_threshold / 
                monitor.config.settling_tolerance_expansion;
         else
            expanded_lower_threshold := monitor.config.lower_threshold * 
               monitor.config.settling_tolerance_expansion;
         end if;

         if monitor.config.upper_threshold >= 0.0 then
            expanded_upper_threshold := monitor.config.upper_threshold * 
               monitor.config.settling_tolerance_expansion;
         else
            expanded_upper_threshold := monitor.config.upper_threshold / 
               monitor.config.settling_tolerance_expansion;
         end if;

         --  @TODO why does this assertion fail?
         pragma Assert (expanded_lower_threshold < expanded_upper_threshold);

         --  Check limits with expanded thresholds
         if signal_value >= expanded_lower_threshold and signal_value <= 
             expanded_upper_threshold then
            within_expanded_limits := True;
         end if;
   end case;

   return within_expanded_limits;

end is_within_expanded_limits;

我的问题是断言pragma Assert (expanded_lower_threshold < expanded_upper_threshold)被标记为可能会失败而我不明白为什么。我添加了断言来检查我的代码没有做任何奇怪的事情,比如反转低阈值和高阈值的关系,但主要是尝试断言。前置条件monitor.config.lower_threshold < monitor.config.upper_threshold与计算expanded_lower_thresholdexpanded_upper_threshold的代码一起应该保证断言始终成立。问题在哪里,我该如何解决?

1 个答案:

答案 0 :(得分:1)

我的推测是你的推荐者只是暂停了。我试过你的例子,这就是发生在我身上的事。

通常,用户界面无法区分证明失败的不同原因。如果你遇到超时并且没有注意到,那么 它使您的代码看起来有问题,尽管它可能不是。由于您必须使用GNATprove(目前没有其他工具),请进入您的构建文件夹并查找名为“gnatprove”的子文件夹。在那里,你会找到一个名为your_package_name.spark的文件。这是一种JSON格式,可以为您提供投诉人真正报告的信息。我相信你会发现超时或限制。我认为你的代码可能没有缺陷,因为我会在一分钟内证实。

但首先,您的代码没有编译。你必须使用合适的类型 在函数is_within_expanded_limits中定义参数signal_value的范围。由于范围相同,您可以使用Float_Signed1000。或者,添加一个前置条件(但类型更好,因为那时不在SPARK_Mode中的调用者也会得到一些类型/范围检查)。

然后,考虑将比较更改为“&lt; =”,因为舍入错误可能会反驳严格的排序。我不确定在这种情况下这是否有问题,但要注意SPARK模拟符合IEEE754标准的计算,无论你有一个数字1.2(无法精确表示),或1.25(可以),它都会有所不同。因此,我对您的代码做了一点改动。

现在回到我的说法,尽管证据失败,你的代码仍然是正确的。将其添加到案例的顶部:

pragma Assume (monitor.config.lower_threshold = 10.0);

警告:除了弄清证明失败的原因外,不要使用假设编译指示。如果使用不当,事情会发生严重错误。

现在选择最慢的模式进行验证,你的断言检出(证明者有较少的“cominations”来检查)。您可以根据需要更改assume语句中的值,但要注意该值与类型范围和前置条件一致,否则所有内容都会被错误地证明(例如,尝试Float'Last,然后放置{{1}假设后立即)。 如果某些事情没有得到证实,那就去看看为什么如上所述。在你知道证明者真正得出结论之前,改变你的代码毫无意义。

您可以试用SPARK引理库,它可以启用这些顽固的证据:http://www.spark-2014.org/entries/detail/gnatprove-tips-and-tricks-using-the-lemma-library