# Theory Trace

section ‹Traces›

text ‹Traces extend transitions to finitely many intermediate events.›

theory Trace
imports
"HOL-Library.Sublist"
Distributed_System

begin

context distributed_system

begin

text ‹We can think of a trace as the transitive closure of the next
relation. A trace consists of initial and final configurations $c$ and
$c'$, with an ordered list of events $t$ occurring sequentially on $c$,
yielding $c'$.›

inductive (in distributed_system) trace where
tr_init: "trace c [] c"
| tr_step: "⟦ c ⊢ ev ↦ c'; trace c' t c'' ⟧
⟹ trace c (ev # t) c''"

subsection ‹Properties of traces›

lemma trace_trans:
shows
"⟦ trace c t c';
trace c' t' c''
⟧ ⟹ trace c (t @ t') c''"
proof (induct c t c' rule:trace.induct)
case tr_init
then show ?case by simp
next
case tr_step
then show ?case using trace.tr_step by auto
qed

assumes
"trace c (ev # t) c'"
shows
"∃c''. c ⊢ ev ↦ c'' ∧ trace c'' t c'"
using assms trace.simps by blast

lemma trace_decomp_tail:
shows
"trace c (t @ [ev]) c' ⟹ ∃c''. trace c t c'' ∧ c'' ⊢ ev ↦ c'"
proof (induct t arbitrary: c)
case Nil
then show ?case
by (metis (mono_tags, lifting) append_Nil distributed_system.trace.simps distributed_system_axioms list.discI list.sel(1) list.sel(3))
next
case (Cons ev' t)
then obtain d where step: "c ⊢ ev' ↦ d" and "trace d (t @ [ev]) c'" using trace_decomp_head by force
then obtain d' where tr: "trace d t d'" and "d' ⊢ ev ↦ c'" using Cons.hyps by blast
moreover have "trace c (ev' # t) d'" using step tr trace.tr_step by simp
ultimately show ?case by auto
qed

lemma trace_snoc:
assumes
"trace c t c'" and
"c' ⊢ ev ↦ c''"
shows
"trace c (t @ [ev]) c''"
using assms(1) assms(2) tr_init tr_step trace_trans by auto

lemma trace_rev_induct [consumes 1, case_names tr_rev_init tr_rev_step]:
"⟦ trace c t c';
(⋀c. P c [] c);
(⋀c t c' ev c''. trace c t c' ⟹ P c t c' ⟹ c' ⊢ ev ↦ c'' ⟹ P c (t @ [ev]) c'')
⟧ ⟹ P c t c'"
proof (induct t arbitrary: c' rule:rev_induct)
case Nil
then show ?case
using distributed_system.trace.cases distributed_system_axioms by blast
next
case (snoc ev t)
then obtain c'' where "trace c t c''" "c'' ⊢ ev ↦ c'" using trace_decomp_tail by blast
then show ?case using snoc by simp
qed

lemma trace_and_start_determines_end:
shows
"trace c t c' ⟹ trace c t d' ⟹ c' = d'"
proof (induct c t c' arbitrary: d' rule:trace_rev_induct)
case tr_rev_init
then show ?case using trace.cases by fastforce
next
case (tr_rev_step c t c' ev c'')
then obtain d'' where "trace c t d''" "d'' ⊢ ev ↦ d'" using trace_decomp_tail by blast
then show ?case using tr_rev_step state_and_event_determine_next by simp
qed

lemma suffix_split_trace:
shows
"⟦ trace c t c';
suffix t' t
⟧ ⟹ ∃c''. trace c'' t' c'"
proof (induct t arbitrary: c)
case Nil
then have "t' = []" by simp
then have "trace c' t' c'" using tr_init by simp
then show ?case by blast
next
case (Cons ev t'')
from Cons.prems have q: "suffix t' t'' ∨ t' = ev # t''" by (meson suffix_Cons)
thus ?case
proof (cases "suffix t' t''")
case True
then show ?thesis using Cons.hyps Cons.prems(1) trace.simps by blast
next
case False
hence "t' = ev # t''" using q by simp
thus ?thesis using Cons.hyps Cons.prems by blast
qed
qed

lemma prefix_split_trace:
fixes
c :: "('p, 's, 'm) configuration" and
t :: "('p, 's, 'm) trace"
shows
"⟦ ∃c'. trace c t c';
prefix t' t
⟧ ⟹ ∃c''. trace c t' c''"
proof (induct t rule:rev_induct)
case Nil
then show ?case by simp
next
case (snoc ev t'')
from snoc.prems have q: "prefix t' t'' ∨ t' = t'' @ [ev]" by auto
thus ?case
proof (cases "prefix t' t''")
case True
thus ?thesis using trace_decomp_tail using snoc.hyps snoc.prems(1) trace.simps by blast
next
case False
thus ?thesis using q snoc.prems by fast
qed
qed

lemma split_trace:
shows
"⟦ trace c t c';
t = t' @ t''
⟧ ⟹ ∃c''. trace c t' c'' ∧ trace c'' t'' c'"
proof (induct t'' arbitrary: t')
case Nil
then show ?case using tr_init by auto
next
case (Cons ev t'')
obtain c'' where p: "trace c (t' @ [ev]) c''"
using Cons.prems prefix_split_trace rotate1.simps(2) by force
then have "trace c'' t'' c'"
using Cons.hyps Cons.prems trace_and_start_determines_end by force
then show ?case
by (meson distributed_system.tr_step distributed_system.trace_decomp_tail distributed_system_axioms p)
qed

subsection ‹Describing intermediate configurations›

definition construct_fun_from_rel :: "('a * 'b) set ⇒ 'a ⇒ 'b" where
"construct_fun_from_rel R x = (THE y. (x,y) ∈ R)"

definition trace_rel where
"trace_rel ≡ {((x, t'), y). trace x t' y}"

shows
"single_valued R ⟹ x ∈ Domain R
⟹ (x, construct_fun_from_rel R x) ∈ R"
unfolding construct_fun_from_rel_def
by (rule theI') (auto simp add: single_valued_def)

lemma single_valued_trace_rel:
shows
"single_valued trace_rel"
proof (rule single_valuedI)
fix x y y'
assume asm: "(x, y) ∈ trace_rel" "(x, y') ∈ trace_rel"
then obtain x' t where "x = (x', t)"
by (meson surj_pair)
then have "trace x' t y" "trace x' t y'"
using asm trace_rel_def by auto
then show "y = y'"
using trace_and_start_determines_end by blast
qed

definition run_trace where
"run_trace ≡ construct_fun_from_rel trace_rel"

text ‹In order to describe intermediate configurations
of a trace we introduce the $s$ function definition, which,
given an initial configuration $c$, a trace $t$ and an index $i \in \mathbb{N}$,
determines the unique state after the first $i$ events of $t$.›

definition s where
"s c t i = (THE c'. trace c (take i t) c')"

lemma s_is_partial_execution:
shows
"s c t i = run_trace (c, take i t)"
unfolding s_def run_trace_def
construct_fun_from_rel_def trace_rel_def
by auto

lemma exists_trace_for_any_i:
assumes
"∃c'. trace c t c'"
shows
"trace c (take i t) (s c t i)"
proof -
have "prefix (take i t) t" using take_is_prefix by auto
then obtain c'' where tr: "trace c (take i t) c''" using assms prefix_split_trace by blast
then show ?thesis
proof -
have "((c, take i t), s c t i) ∈ trace_rel"
unfolding s_def trace_rel_def construct_fun_from_rel_def
by (metis case_prod_conv distributed_system.trace_and_start_determines_end distributed_system_axioms mem_Collect_eq the_equality tr)
then show ?thesis by (simp add: trace_rel_def)
qed
qed

lemma exists_trace_for_any_i_j:
assumes
"∃c'. trace c t c'" and
"i ≤ j"
shows
"trace (s c t i) (take (j - i) (drop i t)) (s c t j)"
proof -
have "trace c (take j t) (s c t j)" using exists_trace_for_any_i assms by simp
from ‹j ≥ i› have "take j t = take i t @ (take (j - i) (drop i t))"
then have "trace c (take i t) (s c t i) ∧ trace (s c t i) (take (j - i) (drop i t)) (s c t j)"
by (metis (no_types, lifting) assms(1) exists_trace_for_any_i split_trace trace_and_start_determines_end)
then show ?thesis by simp
qed

lemma step_Suc:
assumes
"i < length t" and
valid: "trace c t c'"
shows "(s c t i) ⊢ (t ! i) ↦ (s c t (Suc i))"
proof -
have ex_trace: "trace (s c t i) (take (Suc i - i) (drop i t)) (s c t (Suc i))"
using exists_trace_for_any_i_j le_less valid by blast
moreover have "Suc i - i = 1" by auto
moreover have "take 1 (drop i t) = [t ! i]"
by (metis ‹Suc i - i = 1› assms(1) hd_drop_conv_nth le_add_diff_inverse lessI nat_less_le same_append_eq take_add take_hd_drop)
ultimately show ?thesis
qed

subsection ‹Trace-related lemmas›

lemma snapshot_state_unchanged_trace:
assumes
"trace c t c'" and
"ps c p = Some u"
shows
"ps c' p = Some u"
using assms snapshot_state_unchanged by (induct c t c', auto)

lemma no_state_change_if_only_nonregular_events:
shows
"⟦ trace c t c';
∄ev. ev ∈ set t ∧ regular_event ev ∧ occurs_on ev = p;
states c p = st
⟧ ⟹ states c' p = st"
proof (induct c t c' rule:trace_rev_induct)
case (tr_rev_init c)
then show ?case by simp
next
case (tr_rev_step c t c' ev c'')
then have "states c' p = st"
proof -
have "∄ev. ev : set t ∧ regular_event ev ∧ occurs_on ev = p"
using tr_rev_step by auto
then show ?thesis using tr_rev_step by blast
qed
then show ?case
using tr_rev_step no_state_change_if_no_event no_state_change_if_nonregular_event
by auto
qed

lemma message_must_be_delivered_2_trace:
assumes
"trace c t c'" and
"m : set (msgs c i)" and
"m ∉ set (msgs c' i)" and
"channel i = Some (q, p)"
shows
"∃ev ∈ set t. (∃p q. ev = RecvMarker i p q ∧ m = Marker) ∨ (∃p q s s' m'. ev = Recv i q p s s' m' ∧ m = Msg m')"
proof (rule ccontr)
assume "~ (∃ev ∈ set t. (∃p q. ev = RecvMarker i p q ∧ m = Marker) ∨ (∃p q s s' m'. ev = Recv i q p s s' m' ∧ m = Msg m'))" (is ?P)
have "⟦ trace c t c'; m : set (msgs c i); ?P ⟧ ⟹ m : set (msgs c' i)"
proof (induct c t c' rule:trace_rev_induct)
case (tr_rev_init c)
then show ?case by simp
next
case (tr_rev_step c t d ev c')
then have m_in_set: "m : set (msgs d i)"
using tr_rev_step by auto
then show ?case
proof (cases ev)
case (Snapshot r)
then show ?thesis
using distributed_system.message_must_be_delivered_2 distributed_system_axioms m_in_set tr_rev_step.hyps(3) by blast
next
case (RecvMarker i' r s)
then show ?thesis
proof (cases "m = Marker")
case True
then have "i' ≠ i" using tr_rev_step RecvMarker by simp
then show ?thesis
using RecvMarker m_in_set message_must_be_delivered_2 tr_rev_step.hyps(3) by blast
next
case False
then show ?thesis
using RecvMarker tr_rev_step.hyps(3) m_in_set message_must_be_delivered_2 by blast
qed
next
case (Trans r u u')
then show ?thesis
using tr_rev_step.hyps(3) m_in_set by auto
next
case (Send i' r s u u' m')
then show ?thesis
using distributed_system.message_must_be_delivered_2 distributed_system_axioms m_in_set tr_rev_step.hyps(3) by blast
next
case (Recv i' r s u u' m')
then show ?thesis
proof (cases "Msg m' = m")
case True
then have "i' ≠ i" using Recv tr_rev_step by auto
then show ?thesis
using Recv m_in_set tr_rev_step(3) by auto
next
case False
then show ?thesis
by (metis Recv event.distinct(17) event.inject(3) m_in_set message_must_be_delivered_2 tr_rev_step.hyps(3))
qed
qed
qed
then have "m : set (msgs c' i)" using assms ‹?P› by auto
then show False using assms by simp
qed

lemma marker_must_be_delivered_2_trace:
assumes
"trace c t c'" and
"Marker : set (msgs c i)" and
"Marker ∉ set (msgs c' i)" and
"channel i = Some (p, q)"
shows
"∃ev ∈ set t. (∃p q. ev = RecvMarker i p q)"
proof -
show "∃ev ∈ set t. (∃p q. ev = RecvMarker i p q)"
using assms message_must_be_delivered_2_trace by fast
qed

lemma snapshot_stable:
shows
"⟦ trace c t c';
has_snapshotted c p
⟧ ⟹ has_snapshotted c' p"
proof (induct c t c' rule:trace_rev_induct)
case (tr_rev_init c)
then show ?case by blast
next
case (tr_rev_step c t c' ev c'')
then show ?case
proof (cases ev)
case (Snapshot q)
then have "p ≠ q" using tr_rev_step next_snapshot can_occur_def by auto
then show ?thesis using Snapshot tr_rev_step by auto
next
case (RecvMarker i q r)
with tr_rev_step show ?thesis
by (cases "p = q"; auto)
qed simp_all
qed

lemma snapshot_stable_2:
shows
"trace c t c' ⟹ ~ has_snapshotted c' p ⟹ ~ has_snapshotted c p"
using snapshot_stable by blast

lemma no_markers_if_all_snapshotted:
shows
"⟦ trace c t c';
∀p. has_snapshotted c p;
Marker ∉ set (msgs c i)
⟧ ⟹ Marker ∉ set (msgs c' i)"
proof (induct c t c' rule:trace_rev_induct)
case (tr_rev_init c)
then show ?case by simp
next
case (tr_rev_step c t c' ev c'')
have all_snapshotted: "∀p. has_snapshotted c' p" using snapshot_stable tr_rev_step by auto
have no_marker: "Marker ∉ set (msgs c' i)" using tr_rev_step by blast
then show ?case
proof (cases ev)
case (Snapshot r)
then show ?thesis using can_occur_def tr_rev_step all_snapshotted by auto
next
case (RecvMarker i' r s)
have "i' ≠ i"
proof (rule ccontr)
assume "~ i' ≠ i"
then have "Marker : set (msgs c i)"
using can_occur_def RecvMarker tr_rev_step  RecvMarker_implies_Marker_in_set by blast
then show False using tr_rev_step by simp
qed
then show ?thesis using tr_rev_step all_snapshotted no_marker RecvMarker by auto
next
case (Trans p s s')
then show ?thesis using tr_rev_step no_marker by auto
next
case (Send i' r s u u' m)
then show ?thesis
proof (cases "i' = i")
case True
then have "msgs c'' i = msgs c' i @ [Msg m]" using tr_rev_step Send by auto
then show ?thesis using no_marker by auto
next
case False
then show ?thesis using Send tr_rev_step no_marker by auto
qed
next
case (Recv i' r s u u' m)
then show ?thesis
proof (cases "i = i'")
case True
then have "msgs c'' i = tl (msgs c' i)" using tr_rev_step Recv by auto
then show ?thesis using no_marker by (metis list.sel(2) list.set_sel(2))
next
case False
then show ?thesis using Recv tr_rev_step no_marker by auto
qed
qed
qed

lemma event_stays_valid_if_no_occurrence_trace:
shows
"⟦ trace c t c';
list_all (λev. occurs_on ev ≠ occurs_on ev') t;
can_occur ev' c
⟧ ⟹ can_occur ev' c'"
proof (induct c t c' rule:trace_rev_induct)
case tr_rev_init
then show ?case by blast
next
case tr_rev_step
then show ?case using event_stays_valid_if_no_occurrence by auto
qed

lemma event_can_go_back_if_no_sender_trace:
shows
"⟦ trace c t c';
list_all (λev. occurs_on ev ≠ occurs_on ev') t;
can_occur ev' c';
~ isRecvMarker ev';
list_all (λev. ~ isSend ev) t
⟧ ⟹ can_occur ev' c"
proof (induct c t c' rule:trace_rev_induct)
case tr_rev_init
then show ?case by blast
next
case tr_rev_step
then show ?case using event_can_go_back_if_no_sender by auto
qed

lemma done_only_from_recv_marker_trace:
assumes
"trace c t c'" and
"t ≠ []" and
"snd (cs c cid) ≠ Done" and
"snd (cs c' cid) = Done" and
"channel cid = Some (p, q)"
shows
"RecvMarker cid q p ∈ set t"
proof (rule ccontr)
assume "~ RecvMarker cid q p ∈ set t"
moreover have "⟦ trace c t c'; ~ RecvMarker cid q p ∈ set t; snd (cs c cid) ≠ Done; channel cid = Some (p, q) ⟧
⟹ snd (cs c' cid) ≠ Done"
proof (induct t arbitrary: c' rule:rev_induct)
case Nil
then show ?case
by (metis list.discI trace.simps)
next
case (snoc ev t)
then obtain d where ind: "trace c t d" and step: "d ⊢ ev ↦ c'"
using trace_decomp_tail by blast
then have "snd (cs d cid) ≠ Done"
proof -
have "~ RecvMarker cid q p ∈ set t"
using snoc.prems(2) by auto
then show ?thesis using snoc ind by blast
qed
then show ?case
using done_only_from_recv_marker local.step snoc.prems(2) snoc.prems(4) by auto
qed
ultimately have "snd (cs c' cid) ≠ Done" using assms by blast
then show False using assms by simp
qed

lemma cs_not_not_started_stable_trace:
shows
"⟦ trace c t c'; snd (cs c cid) ≠ NotStarted; channel cid = Some (p, q) ⟧ ⟹ snd (cs c' cid) ≠ NotStarted"
proof (induct t arbitrary:c' rule:rev_induct)
case Nil
then show ?case
by (metis list.discI trace.simps)
next
case (snoc ev t)
then obtain d where tr: "trace c t d" and step: "d ⊢ ev ↦ c'"
using trace_decomp_tail by blast
then have "snd (cs d cid) ≠ NotStarted" using snoc by blast
then show ?case using cs_not_not_started_stable snoc step by blast
qed

lemma no_messages_introduced_if_no_channel:
assumes
trace: "trace init t final" and
no_msgs_if_no_channel: "∀i. channel i = None ⟶ msgs init i = []"
shows
"channel cid = None ⟹ msgs (s init t i) cid = []"
proof (induct i)
case 0
then show ?case
by (metis assms exists_trace_for_any_i no_msgs_if_no_channel take0 tr_init trace_and_start_determines_end)
next
case (Suc n)
have f: "trace (s init t n) (take ((Suc n) - n) (drop n t)) (s init t (Suc n))"
using exists_trace_for_any_i_j order_le_less trace assms by blast
then show ?case
proof (cases "drop n t = Nil")
case True
then show ?thesis using Suc.hyps Suc.prems
by (metis f tr_init trace_and_start_determines_end take_Nil)
next
case False
have suc_n_minus_n: "Suc n - n = 1" by auto
then have "length (take ((Suc n) - n) (drop n t)) = 1" using False by auto
then obtain ev where "ev # Nil = take ((Suc n) - n) (drop n t)"
by (metis False One_nat_def suc_n_minus_n length_greater_0_conv self_append_conv2 take_eq_Nil take_hd_drop)
then have g: "(s init t n) ⊢ ev ↦ (s init t (Suc n))"
by (metis f tr_init trace_and_start_determines_end trace_decomp_head)
then show ?thesis
proof (cases ev)
case (Snapshot r)
then show ?thesis
using Suc.hyps Suc.prems g by auto
next
case (RecvMarker cid' sr r)
have "cid' ≠ cid" using RecvMarker can_occur_def g Suc by auto
with RecvMarker Suc g show ?thesis by (cases "has_snapshotted (s init t n) sr", auto)
next
case (Trans r u u')
then show ?thesis
by (metis Suc.hyps Suc.prems g next_trans)
next
case (Send cid' r s u u' m)
have "cid' ≠ cid" using Send can_occur_def g Suc by auto
then show ?thesis using Suc g Send by simp
next
case (Recv cid' s r u u' m)
have "cid' ≠ cid" using Recv can_occur_def g Suc by auto
then show ?thesis using Suc g Recv by simp
qed
qed
qed

end (* context distributed_system *)

end (* theory Trace *)