"Утверждение может потерпеть неудачу", и предварительное условие не решает его

У меня есть функция, которая контролирует контролируемый сигнал, применяя простую проверку, находится ли сигнал в заданном диапазоне допуска. Функция называется 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_threshold а также expanded_upper_threshold должен гарантировать, что утверждение всегда верно. Где проблема и как я могу это исправить?

1 ответ

Я предполагаю, что ваши пруверы просто столкнулись с таймаутом. Я попробовал ваш пример, и вот что случилось со мной.

Обычно пользовательский интерфейс не различает разные причины неудачных доказательств. Если вы сталкиваетесь с тайм-аутом и не замечаете этого, то кажется, что с вашим кодом что-то не так, хотя, возможно, это не так. Поскольку вы должны использовать GNATprove (в данный момент другого инструмента нет), перейдите в папку сборки и найдите подпапку с именем "gnatprove". Там вы найдете файл с именем your_package_name.spark. Это формат JSON, который дает вам информацию о том, что действительно сообщили проверяющие. Я уверен, что вы найдете там тайм-аут или ограничение шага. Я думаю, что ваш код может быть бездефектным, как я это уточню через минуту.

Но, прежде всего, ваш код не компилируется. Вы должны использовать правильный тип, чтобы определить диапазон параметра signal_value в функции is_within_expanded_limits, Поскольку диапазон одинаковый, вы можете использовать Float_Signed1000, В качестве альтернативы, добавьте предварительное условие (но тип лучше, так как тогда вызывающие, которые не находятся в SPARK_Mode, получают некоторую проверку типа / диапазона).

Затем рассмотрите возможность изменения сравнения на "<=", поскольку возможно, что ошибки округления опровергают строгий порядок. Я не уверен, является ли это проблематичным в этом случае, но имейте в виду, что SPARK моделирует вычисления, совместимые с IEEE754, и там может иметь значение, есть ли у вас число 1,2 (которое не может быть точно представлено), или 1,25 (которое может), Таким образом, я сделал это небольшое изменение в вашем коде.

Теперь вернемся к моему утверждению, что ваш код мог быть верным, несмотря на неудачные доказательства. Добавьте это в начало вашего дела:

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

Предупреждение: не используйте допустимые прагмы, кроме как для выяснения причины неудачи доказательства. Вещи могут пойти ужасно неправильно, если они используются неправильно.

Теперь выберите самый медленный режим для проверки, и ваше утверждение подтвердится (у проверяющего меньше "приказов" для проверки). Вы можете изменить значение в операторе предположения по своему усмотрению, но будьте осторожны, чтобы значение соответствовало диапазонам типов и предварительным условиям, в противном случае все будет доказано неверно (например, попробуйте Float'Last, а затем поместите pragma assert (False) сразу после принятия) Если что-то не доказано, пойди и посмотри почему, как описано выше. Нет смысла менять ваш код, пока вы не узнаете, что проверщик действительно пришел к выводу.

Вы можете попробовать библиотеку лемм SPARK, которая может включать такие упрямые доказательства: http://www.spark-2014.org/entries/detail/gnatprove-tips-and-tricks-using-the-lemma-library

Другие вопросы по тегам