Theory Abstract_Monitor

(*<*)
theory Abstract_Monitor
  imports Trace Table
begin
(*>*)

section ‹Abstract monitors and slicing›

subsection ‹First-order specifications›

text ‹
  We abstract from first-order trace specifications by referring only to their
  semantics. A first-order specification is described by a finite set of free
  variables and a satisfaction function that defines for every trace the pairs
  of valuations and time-points for which the specification is satisfied.
›

locale fo_spec =
  fixes
    nfv :: nat and fv :: "nat set" and
    sat :: "'a trace  'b list  nat  bool"
  assumes
    fv_less_nfv: "x  fv  x < nfv" and
    sat_fv_cong: "(x. x  fv  v!x = v'!x)  sat σ v i = sat σ v' i"
begin

definition verdicts :: "'a trace  (nat × 'b tuple) set" where
  "verdicts σ = {(i, v). wf_tuple nfv fv v  sat σ (map the v) i}"

end

text ‹
  We usually employ a monitor to find the ‹violations› of a specification.
  That is, the monitor should output the satisfactions of its negation.
  Moreover, all monitor implementations must work with finite prefixes.
  We are therefore interested in co-safety properties, which allow us to
  identify all satisfactions on finite prefixes.
›

locale cosafety_fo_spec = fo_spec +
  assumes cosafety_lr: "sat σ v i  π. prefix_of π σ  (σ'. prefix_of π σ'  sat σ' v i)"
begin

lemma cosafety: "sat σ v i  (π. prefix_of π σ  (σ'. prefix_of π σ'  sat σ' v i))"
  using cosafety_lr by blast

end

subsection ‹Monitor function›

text ‹
  We model monitors abstractly as functions from prefixes to verdict sets.
  The following locale specifies a minimal set of properties that any
  reasonable monitor should have.
›

locale monitor = fo_spec +
  fixes M :: "'a prefix  (nat × 'b tuple) set"
  assumes
    mono_monitor: "π  π'  M π  M π'" and
    wf_monitor: "(i, v)  M π  wf_tuple nfv fv v" and
    sound_monitor: "(i, v)  M π  prefix_of π σ  sat σ (map the v) i" and
    complete_monitor: "prefix_of π σ  wf_tuple nfv fv v 
      (σ. prefix_of π σ  sat σ (map the v) i)  π'. prefix_of π' σ  (i, v)  M π'"

text ‹
  A monitor for a co-safety specification computes precisely the set of all
  satisfactions in the limit:
›

abbreviation (in monitor) "M_limit σ  {M π | π. prefix_of π σ}"

locale cosafety_monitor = cosafety_fo_spec + monitor
begin

lemma M_limit_eq: "M_limit σ = verdicts σ"
proof
  show "{M π | π. prefix_of π σ}  verdicts σ"
    by (auto simp: verdicts_def wf_monitor sound_monitor)
next
  show "{M π | π. prefix_of π σ}  verdicts σ"
    unfolding verdicts_def
  proof safe
    fix i v
    assume "wf_tuple nfv fv v" and "sat σ (map the v) i"
    then obtain π where "prefix_of π σ  (σ'. prefix_of π σ'  sat σ' (map the v) i)"
      using cosafety_lr by blast
    with wf_tuple nfv fv v obtain π' where "prefix_of π' σ  (i, v)  M π'"
      using complete_monitor by blast
    then show "(i, v)  {M π | π. prefix_of π σ}"
      by blast
  qed
qed

end

text ‹
  The monitor function termM adds some information over termsat, namely
  when a verdict is output. One possible behavior is that the monitor outputs
  its verdicts for one time-point at a time, in increasing order of
  time-points. Then termM is uniquely defined by a progress function, which
  returns for every prefix the time-point up to which all verdicts are computed.
›

locale progress = fo_spec _ _ sat for sat :: "'a trace  'b list  nat  bool" +
  fixes progress :: "'a prefix  nat"
  assumes
    progress_mono: "π  π'  progress π  progress π'" and
    ex_progress_ge: "π. prefix_of π σ  x  progress π" and
    progress_sat_cong: "prefix_of π σ  prefix_of π σ'  i < progress π 
      sat σ v i  sat σ' v i"
    ― ‹The last condition is not necessary to obtain a proper monitor function.
      However, it corresponds to the intuitive understanding of monitor progress,
      and it results in a stronger characterisation. In particular, it implies that
      the specification is co-safety, as we will show below.›
begin

definition M :: "'a prefix  (nat × 'b tuple) set" where
  "M π = {(i, v). i < progress π  wf_tuple nfv fv v 
    (σ. prefix_of π σ  sat σ (map the v) i)}"

lemma M_alt: "M π = {(i, v). i < progress π  wf_tuple nfv fv v 
    (σ. prefix_of π σ  sat σ (map the v) i)}"
  using ex_prefix_of[of π]
  by (auto simp: M_def cong: progress_sat_cong)

end

sublocale progress  cosafety_monitor _ _ _ M
proof
  fix i v and σ :: "'a trace"
  assume "sat σ v i"
  moreover obtain π where *: "prefix_of π σ" "i < progress π"
    using ex_progress_ge by (auto simp: less_eq_Suc_le)
  ultimately have "sat σ' v i" if "prefix_of π σ'" for σ'
    using that by (simp cong: progress_sat_cong)
  with * show "π. prefix_of π σ  (σ'. prefix_of π σ'  sat σ' v i)"
    by blast
next
  fix π π' :: "'a prefix"
  assume "π  π'"
  then show "M π  M π'"
    by (auto simp: M_def intro: progress_mono prefix_of_antimono
        elim: order.strict_trans2)
next
  fix i v π and σ :: "'a trace"
  assume *: "(i, v)  M π"
  then show "wf_tuple nfv fv v"
    by (simp add: M_def)
  assume "prefix_of π σ"
  with * show "sat σ (map the v) i"
    by (simp add: M_def)
next
  fix i v π and σ :: "'a trace"
  assume *: "prefix_of π σ" "wf_tuple nfv fv v" "σ'. prefix_of π σ'  sat σ' (map the v) i"
  show "π'. prefix_of π' σ  (i, v)  M π'"
  proof (cases "i < progress π")
    case True
    with * show ?thesis by (auto simp: M_def)
  next
    case False
    obtain π' where **: "prefix_of π' σ  i < progress π'"
      using ex_progress_ge by (auto simp: less_eq_Suc_le)
    then have "π  π'"
      using prefix_of π σ prefix_of_imp_linear False progress_mono
      by (blast intro: order.strict_trans2)
    with * ** show ?thesis
      by (auto simp: M_def intro: prefix_of_antimono)
  qed
qed

subsection ‹Slicing›

text ‹Sliceable specifications can be evaluated meaningfully on a subset of events.›

locale abstract_slicer =
  fixes relevant_events :: "'b list set  'a set"
begin

abbreviation slice :: "'b list set  'a trace  'a trace" where
  "slice S  map_Γ (λD. D  relevant_events S)"

abbreviation pslice :: "'b list set  'a prefix  'a prefix" where
  "pslice S  pmap_Γ (λD. D  relevant_events S)"

lemma prefix_of_psliceI: "prefix_of π σ  prefix_of (pslice S π) (slice S σ)"
  by (transfer fixing: S) auto

lemma plen_pslice[simp]: "plen (pslice S π) = plen π"
  by (transfer fixing: S) simp

lemma pslice_pnil[simp]: "pslice S pnil = pnil"
  by (transfer fixing: S) simp

lemma last_ts_pslice[simp]: "last_ts (pslice S π) = last_ts π"
  by (transfer fixing: S) (simp add: last_map case_prod_beta split: list.split)

abbreviation verdict_filter :: "'b list set  (nat × 'b tuple) set  (nat × 'b tuple) set" where
  "verdict_filter S V  {(i, v)  V. mem_restr S v}"

end

locale sliceable_fo_spec = fo_spec _ _ sat + abstract_slicer relevant_events
  for relevant_events :: "'b list set  'a set" and sat :: "'a trace  'b list  nat  bool" +
  assumes sliceable: "v  S  sat (slice S σ) v i  sat σ v i"
begin

lemma union_verdicts_slice:
  assumes part: "𝒮 = UNIV"
  shows "((λS. verdict_filter S (verdicts (slice S σ))) ` 𝒮) = verdicts σ"
proof safe
  fix S i and v :: "'b tuple"
  assume "(i, v)  verdicts (slice S σ)"
  then have tuple: "wf_tuple nfv fv v" and "sat (slice S σ) (map the v) i"
    by (auto simp: verdicts_def)
  assume "mem_restr S v"
  then obtain v' where "v'  S" and 1: "ifv. v ! i = Some (v' ! i)"
    using tuple by (auto simp: fv_less_nfv elim: mem_restrE)
  then have "sat (slice S σ) v' i"
    using sat (slice S σ) (map the v) i tuple
    by (auto simp: wf_tuple_length fv_less_nfv cong: sat_fv_cong)
  then have "sat σ v' i"
    using sliceable[OF v'  S] by simp
  then have "sat σ (map the v) i"
    using tuple 1
    by (auto simp: wf_tuple_length fv_less_nfv cong: sat_fv_cong)
  then show "(i, v)  verdicts σ"
    using tuple by (simp add: verdicts_def)
next
  fix i and v :: "'b tuple"
  assume "(i, v)  verdicts σ"
  then have tuple: "wf_tuple nfv fv v" and "sat σ (map the v) i"
    by (auto simp: verdicts_def)
  from part obtain S where "S  𝒮" and "map the v  S"
    by blast
  then have "mem_restr S v"
    using mem_restrI[of "map the v" S nfv fv] tuple
    by (auto simp: wf_tuple_def fv_less_nfv)
  moreover have "sat (slice S σ) (map the v) i"
    using sat σ (map the v) i sliceable[OF map the v  S] by simp
  then have "(i, v)  verdicts (slice S σ)"
    using tuple by (simp add: verdicts_def)
  ultimately show "(i, v)  (S𝒮. verdict_filter S (verdicts (slice S σ)))"
    using S  𝒮 by blast
qed

end

text ‹
  We define a similar notion for monitors. It is potentially stronger because
  the time-point at which verdicts are output must not change.
›

locale sliceable_monitor = monitor _ _ sat M + abstract_slicer relevant_events
  for relevant_events :: "'b list set  'a set" and sat :: "'a trace  'b list  nat  bool" and M +
  assumes sliceable_M: "mem_restr S v  (i, v)  M (pslice S π)  (i, v)  M π"
begin

lemma union_M_pslice:
  assumes part: "𝒮 = UNIV"
  shows "((λS. verdict_filter S (M (pslice S π))) ` 𝒮) = M π"
proof safe
  fix S i and v :: "'b tuple"
  assume "mem_restr S v" and "(i, v)  M (pslice S π)"
  then show "(i, v)  M π" using sliceable_M by blast
next
  fix i and v :: "'b tuple"
  assume "(i, v)  M π"
  then have tuple: "wf_tuple nfv fv v"
    by (rule wf_monitor)
  from part obtain S where "S  𝒮" and "map the v  S"
    by blast
  then have "mem_restr S v"
    using mem_restrI[of "map the v" S nfv fv] tuple
    by (auto simp: wf_tuple_def fv_less_nfv)
  then have "(i, v)  M (pslice S π)"
    using (i, v)  M π sliceable_M by blast
  then show "(i, v)  (S𝒮. verdict_filter S (M (pslice S π)))"
    using S  𝒮 mem_restr S v by blast
qed

end

text ‹
  If the specification is sliceable and the monitor's progress depends only on
  time-stamps, then also the monitor itself is sliceable.
›

locale timed_progress = progress +
  assumes progress_time_conv: "pts π = pts π'  progress π = progress π'"

locale sliceable_timed_progress = sliceable_fo_spec + timed_progress
begin

lemma progress_pslice[simp]: "progress (pslice S π) = progress π"
  by (simp cong: progress_time_conv)

end

sublocale sliceable_timed_progress  sliceable_monitor _ _ _ _ M
proof
  fix S :: "'a list set" and v i π
  assume *: "mem_restr S v"
  show "(i, v)  M (pslice S π)  (i, v)  M π" (is "?L  ?R")
  proof
    assume ?L
    with * show ?R
      by (auto 0 4 simp: M_def wf_tuple_def elim!: mem_restrE
          box_equals[OF sliceable sat_fv_cong sat_fv_cong, THEN iffD1, rotated -1]
          intro: prefix_of_psliceI dest: fv_less_nfv spec[of _ "slice S _"])
  next
    assume ?R
    with * show ?L
      by (auto 0 4 simp: M_alt wf_tuple_def elim!: mem_restrE
        box_equals[OF sliceable sat_fv_cong sat_fv_cong, THEN iffD2, rotated -1]
        intro: exI[of _ "slice S _"] prefix_of_psliceI dest: fv_less_nfv)
  qed
qed

(*<*)
end
(*>*)