【问题标题】:How to prove equivalence of two functions?如何证明两个函数的等价性?
【发布时间】:2020-11-08 21:25:42
【问题描述】:

我有两个函数:InefficientEuler1Sum 和 InefficientEuler1Sum2。我想证明它们都是等价的(给定相同输入的相同输出)。 当我运行 SPARK -> Prove File(在 GNAT Studio 中)时,我在文件 euler1.adb 中收到关于行 pragma Loop_Invariant(Sum = InefficientEuler1Sum(I)); 的此类消息:

  1. loop invariant might fail in first iteration
  2. loop invariant might not be preserved by an arbitrary iteration

似乎(例如,在尝试手动证明时)函数 InefficientEuler1Sum2 不知道 InefficientEuler1Sum 的结构。向它提供这些信息的最佳方式是什么?

文件 euler1.ads:

package Euler1 with
   SPARK_Mode
is

   function InefficientEuler1Sum (N: Natural) return Natural with
     Ghost,
     Pre => (N <= 1000);

   function InefficientEuler1Sum2 (N: Natural) return Natural with
     Ghost,
     Pre => (N <= 1000),
     Post => (InefficientEuler1Sum2'Result = InefficientEuler1Sum (N));

end Euler1;

文件euler1.adb:

package body Euler1 with
   SPARK_Mode
is

   function InefficientEuler1Sum(N: Natural) return Natural is
      Sum: Natural := 0;
   begin
      for I in 0..N loop
         if I mod 3 = 0 or I mod 5 = 0 then
            Sum := Sum + I;
         end if;
         pragma Loop_Invariant(Sum <= I * (I + 1) / 2);
      end loop;
      return Sum;
   end InefficientEuler1Sum;

   function InefficientEuler1Sum2 (N: Natural) return Natural is
      Sum: Natural := 0;
   begin
      for I in 0..N loop
         if I mod 3 = 0 then
            Sum := Sum + I;
         end if;
         if I mod 5 = 0 then
            Sum := Sum + I;
         end if;
         if I mod 15 = 0 then
            Sum := Sum - I;
         end if;
         pragma Loop_Invariant(Sum <= 2 * I * I);
         pragma Loop_Invariant(Sum = InefficientEuler1Sum(I));
      end loop;
      return Sum;
   end InefficientEuler1Sum2;

end Euler1;

【问题讨论】:

  • 看来你的两个函数的post条件都不充分。 InefficientEuler1Sum 没有后置条件,并且 InefficientEuler1Sum2 的现有后置条件不将其输入与其输出相关联。只有指定了输出,才能证明这两个函数具有相等的输出。
  • 你建议为 InefficientEuler1Sum 写什么样的后置条件? (让我们假设我不知道它的封闭形式表达式。)也不同意 InefficientEuler1Sum2 的后置条件,因为这里显然有 InefficientEuler1Sum2'Result ......无论如何,我可能是错的......你能,如果可能,说明你将如何证明等价性?
  • 鉴于两个函数中的循环不变量,很明显这两个函数通常会产生不同的结果。对于相同的输入值 N,Sum
  • @JimRogers 这不正确。 不清楚“这两个函数通常会产生不同的结果”。循环不变量断言Sum 的值在两个函数中保持低于(或处于)某个界限。他们没有断言Sum 的具体值,也没有证明给定输入N 的函数的具体结果,除了一个范围。 OP 只是在 InefficientEuler1Sum 中使用了不同的(不太保守的)界限(基于算术级数,实际上非常好)。

标签: ada spark-ada


【解决方案1】:

使用如下断言证明这两个函数是等价的:

pragma Assert
  (for all I in 0 .. 1000 =>
     Inefficient_Euler_1_Sum (I) = Inefficient_Euler_1_Sum_2 (I));

似乎有点难。这样的断言需要两个函数的后置条件,以使证明者相信这样的条件成立。我现在不知道该怎么做(其他人可能知道)。

旁注:我在这里看到的主要困难是如何制定一个描述函数输入和输出之间关系的后置条件(在任一函数上),以及,同时,可以使用合适的循环不变量来证明。制定这些合适的循环不变量似乎具有挑战性,因为Sum 变量的更新模式在多次迭代中是周期性的(InefficientEuler1Sum 的周期是 5,InefficientEuler1Sum2 的周期是 15)。我不确定(此时)如何制定一个可以处理这种行为的循环不变量。

因此,另一种(虽然不那么令人兴奋的方法)是通过将它们放在一个公共循环中,然后断言每个方法的累加的等价性来显示两种 方法 的等价性 (Sum ) 循环不变和最终断言中的变量(如下所示)。其中一个变量被标记为"ghost" variable,因为实际上计算两次总和是没有意义的:您需要第二个Sum 变量仅用于证明。

对于以下示例:包装规格。和另一个 SO answer 中的主文件。

testing.adb

package body Testing with SPARK_Mode is
   
   -------------------------------
   -- Inefficient_Euler_1_Sum_2 --
   -------------------------------
   
   function Inefficient_Euler_1_Sum_2 (N : Domain) return Natural is      
      Sum_1 : Natural := 0;
      Sum_2 : Natural := 0 with Ghost;
   begin
      
      for I in 0 .. N loop

         --  Method 1
         begin
            if I mod 3 = 0 then
               Sum_1 := Sum_1 + I;
            end if;
            if I mod 5 = 0 then
               Sum_1 := Sum_1 + I;
            end if;
            if I mod 15 = 0 then
               Sum_1 := Sum_1 - I;
            end if;
         end;
         
         --  Method2
         begin
            if I mod 3 = 0 or I mod 5 = 0 then
               Sum_2 := Sum_2 + I;
            end if;
         end; 
         
         pragma Loop_Invariant (Sum_1 <= (2 * I) * I);
         pragma Loop_Invariant (Sum_2 <= I * (I + 1) / 2);
         pragma Loop_Invariant (Sum_1 = Sum_2); 
         
      end loop;
            
      pragma Assert (Sum_1 = Sum_2);      
      return Sum_1;
      
   end Inefficient_Euler_1_Sum_2;

end Testing;

输出

$ gnatprove -Pdefault.gpr -j0 --level=1 --report=all
Phase 1 of 2: generation of Global contracts ...
Phase 2 of 2: flow analysis and proof ...
main.adb:5:19: info: assertion proved
testing.adb:18:18: info: division check proved
testing.adb:19:31: info: overflow check proved
testing.adb:21:18: info: division check proved
testing.adb:22:31: info: overflow check proved
testing.adb:24:18: info: division check proved
testing.adb:25:31: info: overflow check proved
testing.adb:25:31: info: range check proved
testing.adb:31:18: info: division check proved
testing.adb:31:33: info: division check proved
testing.adb:32:31: info: overflow check proved
testing.adb:36:33: info: loop invariant initialization proved
testing.adb:36:33: info: loop invariant preservation proved
testing.adb:36:45: info: overflow check proved
testing.adb:36:50: info: overflow check proved
testing.adb:37:33: info: loop invariant initialization proved
testing.adb:37:33: info: loop invariant preservation proved
testing.adb:37:44: info: overflow check proved
testing.adb:37:49: info: overflow check proved
testing.adb:37:54: info: division check proved
testing.adb:38:33: info: loop invariant initialization proved
testing.adb:38:33: info: loop invariant preservation proved
testing.adb:42:22: info: assertion proved
testing.ads:18:19: info: postcondition proved
Summary logged in /obj/gnatprove/gnatprove.out

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-12
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    • 2017-03-27
    • 1970-01-01
    • 2018-04-01
    • 2011-04-07
    相关资源
    最近更新 更多