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 \<^term>‹M› adds some information over \<^term>‹sat›, 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 \<^term>‹M› 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"
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: "∀i∈fv. 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