PADEC - Coq Library
Library Padec.MessagePassing.ExecMP
From Coq Require Import Lia.
From Coq Require Import PeanoNat.
From Coq Require Import SetoidList.
From Coq Require Import SetoidClass.
From Coq Require Import Bool.
From Coq Require Import Streams.
From Coq Require Import PeanoNat.
From Coq Require Import SetoidList.
From Coq Require Import SetoidClass.
From Coq Require Import Bool.
From Coq Require Import Streams.
From Padec Require Import SetoidUtil.
From Padec Require Import OptionUtil.
From Padec Require Import AlgorithmMP.
From Padec Require Import RelModelMP.
Open Scope signature_scope.
Set Implicit Arguments.
Section Exec.
Context {InChans: InChannels} {OutChans: OutChannels} {DNet: DirNetwork} {Algo: MPAlgorithm}.
From Padec Require Import OptionUtil.
From Padec Require Import AlgorithmMP.
From Padec Require Import RelModelMP.
Open Scope signature_scope.
Set Implicit Arguments.
Section Exec.
Context {InChans: InChannels} {OutChans: OutChannels} {DNet: DirNetwork} {Algo: MPAlgorithm}.
Execution
An execution describes an infinite sequence of steps of the algorithm.Type Exec:
An Exec is an infinite list of interleaved Env and Event. More precisely, an Exec is an infinite list of pairs (Env, Event), where the Event in the pair is the event occuring from the Env.
Under the FIFO hypothesis, a received message from an
incoming channel must be the oldest pending message
of this particular channel.
Definition h_fifo_step (g:Env) (ev:Event) :=
match getMessage g (recv_id ev) with
| None => True
| Some message =>
Forall (fun m => ~(status m = pending /\
destination m == destination message))
(firstn (recv_id ev) (Channel_env g))
end.
Definition h_fifo_exec (exec : Exec) :=
ForAll (fun e => h_fifo_step (fst (hd e)) (snd (hd e))) exec.
match getMessage g (recv_id ev) with
| None => True
| Some message =>
Forall (fun m => ~(status m = pending /\
destination m == destination message))
(firstn (recv_id ev) (Channel_env g))
end.
Definition h_fifo_exec (exec : Exec) :=
ForAll (fun e => h_fifo_step (fst (hd e)) (snd (hd e))) exec.
If channels have unbounded capacity, then no messages are rejected.
Definition h_non_bounded_step (env : Env) :=
Forall (fun m => ~(status m = rejected)) (Channel_env env).
Definition h_non_bounded_exec (exec : Exec) :=
ForAll (fun e => h_non_bounded_step (fst (hd e))) exec.
Forall (fun m => ~(status m = rejected)) (Channel_env env).
Definition h_non_bounded_exec (exec : Exec) :=
ForAll (fun e => h_non_bounded_step (fst (hd e))) exec.
Messages are rejected before they are potentially lost, i.e.,
((RUN p1) -> g2, Pending -> Rejected) = (RUN p1,Pending) - capa
Definition h_bounded_init (capa : nat) (g1 : Env) :=
forall dest, count_tag (Channel_env g1) dest pending_wt <= capa.
Definition h_bounded_step (capa : nat) (gg : Env) (g2 : Env) :=
forall dest, count_tag (Channel_env g2) dest rejected_wt
- count_tag (Channel_env gg) dest rejected_wt
=
count_tag (Channel_env gg) dest pending_wt
- capa.
Definition h_bounded_exec (capa : nat) (exec : Exec) :=
ForAll (fun e => h_bounded_init capa (fst (hd e)) /\
h_bounded_step capa (RUN (hd e)) (fst (hd (tl e)))) exec.
forall dest, count_tag (Channel_env g1) dest pending_wt <= capa.
Definition h_bounded_step (capa : nat) (gg : Env) (g2 : Env) :=
forall dest, count_tag (Channel_env g2) dest rejected_wt
- count_tag (Channel_env gg) dest rejected_wt
=
count_tag (Channel_env gg) dest pending_wt
- capa.
Definition h_bounded_exec (capa : nat) (exec : Exec) :=
ForAll (fun e => h_bounded_init capa (fst (hd e)) /\
h_bounded_step capa (RUN (hd e)) (fst (hd (tl e)))) exec.
Process fairness:
Every node is activated infinitely often.
Definition ProcessFairness (exec: Exec): Prop :=
forall n, ForAll (Exists (fun e => active_node (snd (hd e)) == n)) exec.
Lemma ProcessFairness_always: forall p e, ProcessFairness (Cons p e) -> ProcessFairness e.
forall n, ForAll (Exists (fun e => active_node (snd (hd e)) == n)) exec.
Lemma ProcessFairness_always: forall p e, ProcessFairness (Cons p e) -> ProcessFairness e.
Message transmission:
Every pending message is eventually lost, rejected, or received.
Definition MessageTransmission (exec: Exec): Prop :=
ForAll (fun e =>
forall msgId, ~(getMessage (fst (hd e)) msgId == None) ->
Exists (fun ee =>
match getMessage (fst (hd ee)) msgId with
| None => False
| Some message => ~(status message = pending)
end) e
) exec.
ForAll (fun e =>
forall msgId, ~(getMessage (fst (hd e)) msgId == None) ->
Exists (fun ee =>
match getMessage (fst (hd ee)) msgId with
| None => False
| Some message => ~(status message = pending)
end) e
) exec.
is_exec:
an infinite sequence such that any two consecutive configurations in
the sequence actually form a Step of the algorithm
Definition is_exec (e: Exec) : Prop :=
ForAll (fun e' => Step (hd (tl e')) (hd e')) e
/\
ProcessFairness e.
Lemma is_exec_cons: forall p e, is_exec (Cons p e) -> is_exec e.
Lemma is_exec_tl: forall e, is_exec e -> is_exec (tl e).
Under the non-lossy hypothesis, no messages are lost.
Definition h_non_lossy_step (env : Env) :=
Forall (fun m => ~(status m = lost)) (Channel_env env).
Definition h_non_lossy_exec (exec : Exec) :=
ForAll (fun e => h_non_lossy_step (fst (hd e))) exec.
Forall (fun m => ~(status m = lost)) (Channel_env env).
Definition h_non_lossy_exec (exec : Exec) :=
ForAll (fun e => h_non_lossy_step (fst (hd e))) exec.
Fair lossy channels: for every channel, if infinitely many messages
are inserted in the channel, then infinitely many messages are received
by the destination node of the channel.
The message msgId is sent at the beginning of the execution
and is not immediately rejected.
Definition is_sent (exec: Exec) (dest: Node * InChannel) (msgId:nat) : Prop :=
let env1 := fst (hd exec) in
let env2 := fst (hd (tl exec)) in
getMessage env1 msgId == None
/\
PoptionA (fun m => destination m == dest /\ status m <> rejected) (getMessage env2 msgId).
let env1 := fst (hd exec) in
let env2 := fst (hd (tl exec)) in
getMessage env1 msgId == None
/\
PoptionA (fun m => destination m == dest /\ status m <> rejected) (getMessage env2 msgId).
The message msgId has been received.
Definition is_received (exec: Exec) (dest: Node * InChannel) (msgId:nat) : Prop :=
(active_node (snd (hd exec)) == fst dest) /\
(recv_id (snd (hd exec)) == msgId) /\
~(getMessage (fst (hd exec)) msgId == None).
(active_node (snd (hd exec)) == fst dest) /\
(recv_id (snd (hd exec)) == msgId) /\
~(getMessage (fst (hd exec)) msgId == None).
For every destination, if infinitely messages are sent and not immediately rejected,
then infinitely messages are received.
Definition FairLossy (exec: Exec): Prop :=
forall dest,
(ForAll (Exists (fun e => exists msgId, is_sent e dest msgId)) exec)
->
(ForAll (Exists (fun e => exists msgId, is_received e dest msgId)) exec).
forall dest,
(ForAll (Exists (fun e => exists msgId, is_sent e dest msgId)) exec)
->
(ForAll (Exists (fun e => exists msgId, is_received e dest msgId)) exec).
The message msgId is sent at the beginning of the execution
(and might be immediately rejected).
Definition is_sent' (exec: Exec) (dest: Node * InChannel) (msgId:nat) : Prop :=
let env1 := fst (hd exec) in
let env2 := fst (hd (tl exec)) in
getMessage env1 msgId == None
/\
PoptionA (fun m => destination m == dest) (getMessage env2 msgId).
let env1 := fst (hd exec) in
let env2 := fst (hd (tl exec)) in
getMessage env1 msgId == None
/\
PoptionA (fun m => destination m == dest) (getMessage env2 msgId).
For every destination, if infinitely messages are sent,
then infinitely messages are received.
Definition FairLossy' (exec: Exec): Prop :=
forall dest,
(ForAll (Exists (fun e => exists msgId, is_sent' e dest msgId)) exec)
->
(ForAll (Exists (fun e => exists msgId, is_received e dest msgId)) exec).
Lemma FairLossy'_tl (e : Exec) :
FairLossy' e -> FairLossy' (tl e).
forall dest,
(ForAll (Exists (fun e => exists msgId, is_sent' e dest msgId)) exec)
->
(ForAll (Exists (fun e => exists msgId, is_received e dest msgId)) exec).
Lemma FairLossy'_tl (e : Exec) :
FairLossy' e -> FairLossy' (tl e).
Stronger Exists (for the first time)
Inductive FExists (A : Type) (P : Stream A -> Prop) (x : Stream A) : Prop :=
FHere : P x -> FExists P x | FFurther : ~(P x) -> FExists P (tl x) -> FExists P x.
FHere : P x -> FExists P x | FFurther : ~(P x) -> FExists P (tl x) -> FExists P x.
If the property is always decidable, then Exists implies FEexists.
Lemma first_exists:
forall (A : Type) (P : Stream A -> Prop) (x : Stream A),
(forall (y : Stream A), {P y} + {~(P y)}) -> Exists P x -> FExists P x.
forall (A : Type) (P : Stream A -> Prop) (x : Stream A),
(forall (y : Stream A), {P y} + {~(P y)}) -> Exists P x -> FExists P x.
"tl" can be transferred from the second argument of FExists to inside the first
Lemma tl_FExists_comm:
forall (A : Type) (P : Stream A -> Prop) (x : Stream A),
(FExists (fun y : Stream A => P y) (tl x))
->
(FExists (fun y : Stream A => P (tl y)) x).
forall (A : Type) (P : Stream A -> Prop) (x : Stream A),
(FExists (fun y : Stream A => P y) (tl x))
->
(FExists (fun y : Stream A => P (tl y)) x).
Small tool to make "\/" and "Exists" commute
Lemma exists_or:
forall (A : Type) (P P' : Stream A -> Prop) (x : Stream A),
Exists (fun y => P y \/ P' y) x <-> Exists P x \/ Exists P' x.
forall (A : Type) (P P' : Stream A -> Prop) (x : Stream A),
Exists (fun y => P y \/ P' y) x <-> Exists P x \/ Exists P' x.
By definition, is_sent implies is_sent' in all models
Lemma is_sent_is_sent' :
forall e dest,
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId)) e
->
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId)) e.
forall e dest,
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId)) e
->
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId)) e.
For unbounded channels, is_sent' implies is_sent (now)
Lemma is_sent'_is_sent_unbounded_now :
forall e, h_non_bounded_exec e ->
forall dest,
(exists msgId : nat, is_sent' e dest msgId)
->
(exists msgId : nat, is_sent e dest msgId).
forall e, h_non_bounded_exec e ->
forall dest,
(exists msgId : nat, is_sent' e dest msgId)
->
(exists msgId : nat, is_sent e dest msgId).
For unbounded channels, is_sent' implies is_sent (eventually)
Lemma is_sent'_is_sent_unbounded_eventually :
forall e, h_non_bounded_exec e ->
forall dest,
Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId) e
->
Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId) e.
forall e, h_non_bounded_exec e ->
forall dest,
Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId) e
->
Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId) e.
For unbounded channels, is_sent' implies is_sent (always eventually)
Lemma is_sent'_is_sent_unbounded :
forall e, h_non_bounded_exec e ->
forall dest,
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId)) e
->
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId)) e.
forall e, h_non_bounded_exec e ->
forall dest,
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId)) e
->
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId)) e.
If a pending message becomes non pending during a step, then the number
of pending messages in the initial list of messages decreases
Lemma step_pending_decr (msgId msgId': nat) (l0 l1 l1': list Message) (dest: Node * InChannel):
l1 = mark_as_read l0 msgId'
->
eqlistA RchangeTag l1 l1'
->
PoptionA (fun m => destination m == dest /\ status m = pending) (nth_error l0 msgId)
->
PoptionA (fun m => ~ status m = pending) (nth_error l1' msgId)
->
count_tag l1' dest pending_wt < count_tag l0 dest pending_wt.
l1 = mark_as_read l0 msgId'
->
eqlistA RchangeTag l1 l1'
->
PoptionA (fun m => destination m == dest /\ status m = pending) (nth_error l0 msgId)
->
PoptionA (fun m => ~ status m = pending) (nth_error l1' msgId)
->
count_tag l1' dest pending_wt < count_tag l0 dest pending_wt.
If a pending message becomes non-pending in a step but the
total number of pending messages does not decrease, then
a message has been sent during the step and is still
pending at the end of the step.
Lemma non_decr_sent (capa_1 : nat) (e: Exec) (dest: Node * InChannel) (msgId: nat):
is_exec e
->
PoptionA (fun m => destination m == dest /\ status m = pending) (getMessage (fst (hd e)) msgId)
->
PoptionA (fun m => ~ status m = pending) (getMessage (fst (hd (tl e))) msgId)
->
count_tag (Channel_env (fst (hd e))) dest pending_wt
<= count_tag (Channel_env (fst (hd (tl e)))) dest pending_wt
->
exists msgId', is_sent e dest msgId'.
is_exec e
->
PoptionA (fun m => destination m == dest /\ status m = pending) (getMessage (fst (hd e)) msgId)
->
PoptionA (fun m => ~ status m = pending) (getMessage (fst (hd (tl e))) msgId)
->
count_tag (Channel_env (fst (hd e))) dest pending_wt
<= count_tag (Channel_env (fst (hd (tl e)))) dest pending_wt
->
exists msgId', is_sent e dest msgId'.
Under bounded capacity, in any execution and for any dest,
there exists a point in time when
the channel is not full or a message has just been sent
(without being immediately rejected)
Lemma under_capa (capa_1 : nat) (e: Exec) (dest: Node * InChannel):
is_exec e -> MessageTransmission e -> h_bounded_exec (S capa_1) e ->
Exists (fun e' : Exec =>
count_tag (Channel_env (fst (hd e'))) dest pending_wt < S capa_1
\/
exists msgId : nat, is_sent e' dest msgId) e.
is_exec e -> MessageTransmission e -> h_bounded_exec (S capa_1) e ->
Exists (fun e' : Exec =>
count_tag (Channel_env (fst (hd e'))) dest pending_wt < S capa_1
\/
exists msgId : nat, is_sent e' dest msgId) e.
Given a destination, it is decidable
whether a message has been sent to this destination
Lemma is_sent'_dec (dest : Node * InChannel) (e : Exec):
{exists msgId : nat, is_sent' e dest msgId}
+
{~ (exists msgId : nat, is_sent' e dest msgId)}.
{exists msgId : nat, is_sent' e dest msgId}
+
{~ (exists msgId : nat, is_sent' e dest msgId)}.
If not full capacity, next is_sent' implies is_sent
Lemma is_sent'_is_sent_bounded_now :
forall capa_1 e, is_exec e -> h_bounded_exec (S capa_1) e ->
forall dest,
count_tag (Channel_env (fst (hd e))) dest pending_wt < S capa_1 ->
(exists msgId : nat, is_sent' e dest msgId)
->
exists msgId : nat, is_sent e dest msgId.
forall capa_1 e, is_exec e -> h_bounded_exec (S capa_1) e ->
forall dest,
count_tag (Channel_env (fst (hd e))) dest pending_wt < S capa_1 ->
(exists msgId : nat, is_sent' e dest msgId)
->
exists msgId : nat, is_sent e dest msgId.
If not full capacity, next is_sent' implies is_sent
Lemma capacity_not_full_stable :
forall capa_1 e, is_exec e -> h_bounded_exec (S capa_1) e ->
forall dest,
count_tag (Channel_env (fst (hd e))) dest pending_wt < S capa_1 ->
~ (exists msgId : nat, is_sent' e dest msgId)
->
count_tag (Channel_env (fst (hd (tl e)))) dest pending_wt < S capa_1.
forall capa_1 e, is_exec e -> h_bounded_exec (S capa_1) e ->
forall dest,
count_tag (Channel_env (fst (hd e))) dest pending_wt < S capa_1 ->
~ (exists msgId : nat, is_sent' e dest msgId)
->
count_tag (Channel_env (fst (hd (tl e)))) dest pending_wt < S capa_1.
If not full capacity, next is_sent' implies is_sent
Lemma is_sent'_is_sent_bounded_exists :
forall capa_1 e, is_exec e -> h_bounded_exec (S capa_1) e ->
forall dest,
count_tag (Channel_env (fst (hd e))) dest pending_wt < S capa_1 ->
Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId) e
->
Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId) e.
forall capa_1 e, is_exec e -> h_bounded_exec (S capa_1) e ->
forall dest,
count_tag (Channel_env (fst (hd e))) dest pending_wt < S capa_1 ->
Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId) e
->
Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId) e.
Under bounded capacity, is_sent' implies is_sent
Lemma is_sent'_is_sent_bounded :
forall capa_1 e, is_exec e -> MessageTransmission e -> h_bounded_exec (S capa_1) e ->
forall dest,
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId)) e
->
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId)) e.
Lemma fairlossy'_equiv :
forall capa_1 e, is_exec e -> MessageTransmission e -> (h_bounded_exec (S capa_1) e \/ h_non_bounded_exec e) ->
FairLossy e <-> FairLossy' e.
End Exec.
Close Scope signature_scope.
Unset Implicit Arguments.
forall capa_1 e, is_exec e -> MessageTransmission e -> h_bounded_exec (S capa_1) e ->
forall dest,
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent' e' dest msgId)) e
->
ForAll (Exists (fun e' : Stream (Env * Event) => exists msgId : nat, is_sent e' dest msgId)) e.
Lemma fairlossy'_equiv :
forall capa_1 e, is_exec e -> MessageTransmission e -> (h_bounded_exec (S capa_1) e \/ h_non_bounded_exec e) ->
FairLossy e <-> FairLossy' e.
End Exec.
Close Scope signature_scope.
Unset Implicit Arguments.