简体   繁体   English

“断言可能失败”并且前提条件无法解决它

[英]“Assertion might Fail” and Precondition doesn't solve it

I have a function that monitors a controlled signal by applying a simple check whether the signal is within a given tolerance band. 我具有通过简单检查信号是否在给定公差带内来监视受控信号的功能。 The function is called is_within_limits . 该函数称为is_within_limits I have a second function called is_within_expanded_limits that does the same but applies an expansion factor (widens the tolerance band) to the configured limits beforehand. 我有一个名为is_within_expanded_limits的第二个函数,该函数执行相同的操作,但事先将扩展因子(扩大公差带)应用于配置的极限。 The limits are configured in monitor.config by means of a target value and a maximum deviation from that value or lower and upper thresholds. 这些限制是在monitor.config通过目标值以及与该值的最大偏差或上下阈值进行配置的。 The enumeration monitor.config.monitoring_mode specifies how the valid range is specified. 枚举monitor.config.monitoring_mode指定如何指定有效范围。

Have a look at the code first. 首先看一下代码。

Specification 规格

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;

Implementation 履行

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;

My problem is that the assertion pragma Assert (expanded_lower_threshold < expanded_upper_threshold) is marked as might fail and I don't understand why. 我的问题是断言pragma Assert (expanded_lower_threshold < expanded_upper_threshold)被标记为可能失败 ,我不明白为什么。 I added that assertion to check that my code does nothing weird like inverting the relation of lower and upper threshold but primarily to try out assertions. 我添加了该断言,以检查我的代码没有像倒数上下限的关系那样奇怪,而是主要尝试断言。 The precondition monitor.config.lower_threshold < monitor.config.upper_threshold in conjunction with the code that calculates expanded_lower_threshold and expanded_upper_threshold should guarantee that the assertion always holds true. 前提条件monitor.config.lower_threshold < monitor.config.upper_threshold与计算expanded_lower_thresholdexpanded_upper_threshold的代码一起应确保断言始终为真。 Where is the problem and how can I fix it? 问题出在哪里,我该如何解决?

My conjecture is that your provers simply ran into a timeout. 我的猜想是,您的证明者只会遇到超时问题。 I tried your example and that is what happend to me. 我尝试了您的示例,这就是我发生的事情。

Usually, the user interface is not distinguishing between different causes of failing proofs. 通常,用户界面无法区分失败证明的不同原因。 If you run into a timeout and don't notice, then it makes it look like something is wrong with your code, although it maybe isn't. 如果您遇到超时而没有注意到,那么它看起来好像您的代码有问题,尽管可能并非如此。 Since you must be using GNATprove (there is no other tool at the moment), go into your build folder and look for a subfolder called "gnatprove". 由于您必须使用GNATprove(目前没有其他工具),因此进入您的build文件夹并查找一个名为“ gnatprove”的子文件夹。 There, you will find a file called your_package_name.spark. 在那里,您将找到一个名为your_package_name.spark的文件。 This is a JSON format, which gives you information what the provers really reported. 这是JSON格式,可为您提供验证者真正报告的信息。 I am sure you will find a timeout or step limit there. 我相信您会在此处找到超时或步长限制。 I think your code might be defect-free, as I will substantiate in a minute. 我认为您的代码可能没有缺陷,因为我会在一分钟内证实。

But first of all, your code is not compiling. 但首先,您的代码未编译。 You have to use a proper type to define the range of parameter signal_value in function is_within_expanded_limits . 您必须使用适当的类型在函数is_within_expanded_limits中定义is_within_expanded_limits参数的范围。 Since the range is the same, you could use Float_Signed1000 . 由于范围相同,因此可以使用Float_Signed1000 Alternatively, add a precondition (but the type is better, since then also callers that are not in SPARK_Mode get some type/range checking). 或者,添加一个前提条件(但是类型更好,因为这样,不在SPARK_Mode中的调用者也可以进行某种类型/范围检查)。

Then, consider changing the comparison to "<=", since it might be possible that rounding errors refute a strict ordering. 然后,考虑将比较值更改为“ <=”,因为舍入错误可能会反驳严格的排序。 I am not sure whether this is problematic in this case, but be aware that SPARK models IEEE754-compliant computations, and there it can make a difference whether you have a number 1.2 (which cannot be precisely represented), or 1.25 (which can). 我不确定在这种情况下这是否有问题,但是请注意,SPARK可以模拟符合IEEE754的计算,因此无论您使用数字1.2(无法精确表示)还是1.25(可以)都可以有所作为。 。 Thus, I made this little change to your code. 因此,我对您的代码进行了少许更改。

Now back to my claim that your code could be correct despite failing proofs. 现在回到我的主张,尽管证明不正确,您的代码还是正确的。 Add this to the top of your case: 将其添加到案例的顶部:

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

Warning: don't use assume pragmas other than for figuring out why a proof fails. 警告:除了弄清楚证明失败的原因外,不要使用假设的用法。 Things can go horribly wrong if they are used incorrectly. 如果使用不正确,可能会导致严重错误。

Now select the slowest mode for verification, and your assertion checks out (the prover has less "cominations" to check). 现在,选择最慢的模式进行验证,然后您的断言将检出(证明者需要检查的“组合”较少)。 You can change the value in the assume statement as you like, but be careful that the value is agreeing with type ranges and precondition, otherwise everything gets proven wrongfully (eg, try Float'Last , and then place a pragma assert (False) immediately after the assume). 您可以根据自己的喜好更改前提语句中的值,但要注意该值与类型范围和前提条件Float'Last ,否则所有内容都会被错误证明(例如,尝试Float'Last ,然后立即放置pragma assert (False)假设之后)。 If something does not get proven, go and see why as described above. 如果没有得到证实,请按照上述说明查看原因。 There is no point in changing your code before you know that the prover actually came to a conclusion. 在您知道证明者确实得出结论之前,更改代码没有任何意义。

You can try out the SPARK lemma library, which can enable these kind of stubborn proofs: http://www.spark-2014.org/entries/detail/gnatprove-tips-and-tricks-using-the-lemma-library 您可以尝试SPARK引理库,该库可以启用以下这类顽固的证明: http : //www.spark-2014.org/entries/detail/gnatprove-tips-and-tricks-using-the-lemma-library

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM