3

InefficientEuler1Sum と InefficientEuler1Sum2 の 2 つの関数があります。両方が同等であることを証明したい (同じ入力に対して同じ出力)。SPARK -> Prove File (GNAT Studio 内) を実行するpragma Loop_Invariant(Sum = InefficientEuler1Sum(I));と、ファイル euler1.adb の行に関する次のようなメッセージが表示されます。

  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;
4

1 に答える 1

4

次のようなアサーションを使用して、2 つの関数が同等であることを証明します。

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

なんか大変そう。そのようなアサーションには、そのような条件が成り立つことを証明者に納得させる両方の関数の事後条件が必要です。現時点ではこれを行う方法がわかりません(他の人は知っているかもしれませんが)。

補足: ここで見られる主な難点は、関数の入力と出力の関係を記述し、同時に、適切なループ不変式を使用して証明できる (いずれかの関数で) 事後条件を定式化する方法です。 . これらの適切なループ不変式を定式化することは、Sum変数の更新パターンが複数の反復にわたって周期的であるため、困難に思えます (InefficientEuler1Sum周期は 5 で、InefficientEuler1Sum215 です)。この種の動作に対処できるループ不変条件を定式化する方法が (現時点では) わかりません。

したがって、別の (あまり刺激的ではないアプローチですが) 両方のメソッドの同等性を示すには、それらを共通のループに入れてからSum、ループの不変式と最終的なアサーション (以下に示すように) でメソッドのそれぞれの Accumulation ( ) 変数の同等性をアサートします。 )。実際に合計を 2 回計算しても意味がないため、変数の 1 つは「ゴースト」変数としてマークされていますSum。証明のためだけに 2 つ目の変数が必要です。

以下の例: パッケージ仕様。そして、他のSO answerのようにメインファイル。

テスト.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
于 2020-11-09T19:10:44.220 に答える