Library ALEA.Prog

Prog.v: Composition of distributions


Add Rec LoadPath "." as ALEA.

Require Export Probas.



Conditional


Definition Mif (A:Type) (b:distr bool) (m1 m2: distr A)
    := Mlet b (fun x:bool => if x then m1 else m2).

Lemma Mif_le_compat : forall (A:Type) (b1 b2:distr bool) (m1 m2 n1 n2: distr A),
    b1 <= b2 -> m1 <= m2 -> n1 <= n2 -> Mif b1 m1 n1 <= Mif b2 m2 n2.

Hint Resolve Mif_le_compat.

Instance Mif_mon2 : forall (A:Type) b, monotonic2 (Mif (A:=A) b).
Save.

Definition MIf : forall (A:Type), distr bool -m> distr A -m> distr A -m> distr A.
Defined.

Lemma MIf_simpl : forall A b d1 d2, MIf A b d1 d2 = Mif b d1 d2.

Instance if_mon : forall `{o:ord A} (b:bool), monotonic2 (fun (x y:A) => if b then x else y).
Save.

Definition If `{o:ord A} (b:bool) : A -m> A -m> A := mon2 (fun (x y:A) => if b then x else y).

Instance Mif_continuous2 : forall (A:Type) b, continuous2 (MIf A b).
Save.

Hint Resolve Mif_continuous2.

Instance Mif_cond_continuous : forall (A:Type), continuous (MIf A).
Save.

Hint Resolve Mif_cond_continuous.

Add Parametric Morphism (A:Type) : (Mif (A:=A))
  with signature Oeq ==> Oeq ==> Oeq ==> Oeq
as Mif_eq_compat.
Save.
Hint Immediate Mif_eq_compat.

Add Parametric Morphism (A:Type) : (Mif (A:=A))
  with signature Ole ==> Ole ==> Ole ==> Ole
as Mif_le_compat_morph.
Save.

Lemma Mif_lub_eq_left : forall (A:Type) b h (d: distr A),
    Mif b (lub h) d == lub (MIf _ b @ h) d.

Lemma Mif_lub_eq_right : forall (A:Type) b h (d: distr A),
    Mif b d (lub h) == lub (MIf _ b d @ h).

Lemma Mif_lub_eq2 : forall (A:Type) b (h1 h2 : nat -m> distr A),
    Mif b (lub h1) (lub h2) == lub ((MIf _ b @2 h1) h2).

Instance Mif_term : forall (A:Type) b (d1 d2:distr A)
     {Tb : Term b} {T1:Term d1} {T2:Term d2}, Term (Mif b d1 d2).
Save.
Hint Resolve Mif_term.

Probabilistic choice

The distribution associated to pchoice p m1 m2 is f --> p (m1 f) + (1-p) (m2 f)

Definition pchoice : forall A, U -> M A -> M A -> M A.
Defined.

Lemma pchoice_simpl : forall A p (m1 m2:M A) f,
      pchoice p m1 m2 f = p * m1 f + [1-]p * m2 f.

Definition Mchoice (A:Type) (p:U) (m1 m2: distr A) : distr A.
Defined.

Lemma Mchoice_simpl : forall A p (m1 m2:distr A) f,
      mu (Mchoice p m1 m2) f = p * mu m1 f + [1-]p * mu m2 f.

Lemma Mchoice_le_compat : forall (A:Type) (p:U) (m1 m2 n1 n2: distr A),
        m1<=m2 -> n1<=n2 -> Mchoice p m1 n1 <= Mchoice p m2 n2.
Hint Resolve Mchoice_le_compat.

Add Parametric Morphism (A:Type) : (Mchoice (A:=A))
  with signature Oeq ==> Oeq ==> Oeq ==> Oeq
as Mchoice_eq_compat.
Save.
Hint Immediate Mchoice_eq_compat.

Instance Mchoice_mon2 : forall (A:Type) (p:U), monotonic2 (Mchoice (A:=A) p).
Save.

Definition MChoice A (p:U) : distr A -m> distr A -m> distr A :=
               mon2 (Mchoice (A:=A) p).

Lemma MChoice_simpl : forall A (p:U) (m1 m2 : distr A),
MChoice A p m1 m2 = Mchoice p m1 m2.

Lemma Mchoice_sym_le : forall (A:Type) (p:U) (m1 m2: distr A),
            Mchoice p m1 m2 <= Mchoice ([1-]p) m2 m1.
Hint Resolve Mchoice_sym_le.

Lemma Mchoice_sym : forall (A:Type) (p:U) (m1 m2: distr A),
            Mchoice p m1 m2 == Mchoice ([1-]p) m2 m1.

Lemma Mchoice_continuous_right
    : forall (A:Type) (p:U) (m: distr A), continuous (D1:=distr A) (D2:=distr A) (MChoice A p m).
Hint Resolve Mchoice_continuous_right.

Lemma Mchoice_continuous_left : forall (A:Type) (p:U),
    continuous (D1:=distr A) (D2:=distr A -m> distr A) (MChoice A p).

Lemma Mchoice_continuous :
forall (A:Type) (p:U), continuous2 (D1:=distr A) (D2:=distr A) (D3:=distr A) (MChoice A p).

Instance Mchoice_term : forall A p (d1 d2:distr A) {T1:Term d1} {T2:Term d2},
     Term (Mchoice p d1 d2).
Save.
Hint Resolve Mchoice_term.

Image distribution


Definition im_distr (A B : Type) (f:A -> B) (m:distr A) : distr B :=
    Mlet m (fun a => Munit (f a)).

Lemma im_distr_simpl : forall A B (f:A -> B) (m:distr A)(h:B -> U),
     mu (im_distr f m) h = mu m (fun a => h (f a)).

Add Parametric Morphism (A B : Type) : (im_distr (A:=A) (B:=B))
  with signature (feq (A:=A) (B:=B)) ==> Oeq ==> Oeq
  as im_distr_eq_compat.
Save.

Lemma im_distr_comp : forall A B C (f:A -> B) (g:B -> C) (m:distr A),
            im_distr g (im_distr f m) == im_distr (fun a => g (f a)) m.

Lemma im_distr_id : forall A (f:A -> A) (m:distr A), (forall x, f x = x) ->
            im_distr f m == m.

Instance im_distr_term : forall A B (f:A->B)(d:distr A){T:Term d},
              Term (im_distr f d).
Save.
Hint Resolve im_distr_term.

Product distribution


Definition prod_distr (A B : Type)(d1:distr A)(d2:distr B) : distr (A*B) :=
                Mlet d1 (fun x => Mlet d2 (fun y => Munit (x,y))).

Add Parametric Morphism (A B : Type) : (prod_distr (A:=A) (B:=B))
with signature Ole ++> Ole ++> Ole
as prod_distr_le_compat.
Save.
Hint Resolve prod_distr_le_compat.

Add Parametric Morphism (A B : Type) : (prod_distr (A:=A) (B:=B))
with signature Oeq ==> Oeq ==> Oeq
as prod_distr_eq_compat.
Save.
Hint Immediate prod_distr_eq_compat.

Instance prod_distr_mon2 : forall (A B :Type), monotonic2 (prod_distr (A:=A) (B:=B)).
Save.

Definition Prod_distr (A B :Type): distr A -m> distr B -m> distr (A*B) :=
     mon2 (prod_distr (A:=A) (B:=B)).

Lemma Prod_distr_simpl : forall (A B:Type)(d1: distr A) (d2:distr B),
     Prod_distr A B d1 d2 = prod_distr d1 d2.

Lemma prod_distr_rect : forall (A B : Type) (d1: distr A) (d2:distr B) (f:A -> U)(g:B -> U),
         mu (prod_distr d1 d2) (fun xy => f (fst xy) * g (snd xy)) == mu d1 f * mu d2 g.

Lemma prod_distr_fst : forall (A B : Type) (d1: distr A) (d2:distr B) (f:A -> U),
             mu (prod_distr d1 d2) (fun xy => f (fst xy)) == pone d2 * mu d1 f.

Lemma prod_distr_snd : forall (A B : Type) (d1: distr A) (d2:distr B) (g:B -> U),
             mu (prod_distr d1 d2) (fun xy => g (snd xy)) == pone d1 * mu d2 g.

Lemma prod_distr_fst_eq : forall (A B : Type) (d1: distr A) (d2:distr B),
     pone d2 == 1 -> im_distr (fst (A:=A) (B:=B)) (prod_distr d1 d2) == d1.

Lemma prod_distr_snd_eq : forall (A B : Type) (d1: distr A) (d2:distr B),
       pone d1 == 1 -> im_distr (snd (A:=A) (B:=B)) (prod_distr d1 d2) == d2.

Definition swap A B (x:A*B) : B * A := (snd x,fst x).

Definition arg_swap A B (f : MF (A*B)) : MF (B*A) := fun z => f (swap z).

Definition Arg_swap A B : MF (A*B) -m> MF (B*A).
Defined.

Lemma Arg_swap_simpl : forall A B f, Arg_swap A B f = arg_swap f.

Definition prod_distr_com A B (d1: distr A) (d2:distr B) (f : MF (A * B)) :=
      mu (prod_distr d1 d2) f == mu (prod_distr d2 d1) (arg_swap f).

Lemma prod_distr_com_eq_compat : forall A B (d1: distr A) (d2:distr B) (f g: MF (A * B)),
  f == g -> prod_distr_com d1 d2 f -> prod_distr_com d1 d2 g.

Lemma prod_distr_com_rect : forall (A B : Type) (d1: distr A) (d2:distr B) (f:A -> U)(g:B -> U),
         prod_distr_com d1 d2 (fun xy => f (fst xy) * g (snd xy)).

Lemma prod_distr_com_cte : forall (A B : Type) (d1: distr A) (d2:distr B) (c:U),
         prod_distr_com d1 d2 (fcte (A*B) c).

Lemma prod_distr_com_one : forall (A B : Type) (d1: distr A) (d2:distr B),
         prod_distr_com d1 d2 (fone (A*B)).

Lemma prod_distr_com_plus : forall (A B : Type) (d1: distr A) (d2:distr B) (f g:MF (A*B)),
         fplusok f g ->
         prod_distr_com d1 d2 f -> prod_distr_com d1 d2 g ->
         prod_distr_com d1 d2 (fplus f g).

Lemma prod_distr_com_mult : forall (A B : Type) (d1: distr A) (d2:distr B) (k:U)(f:MF (A*B)),
         prod_distr_com d1 d2 f -> prod_distr_com d1 d2 (fmult k f).

Lemma prod_distr_com_inv : forall (A B : Type) (d1: distr A) (d2:distr B) (f:MF (A*B)),
         prod_distr_com d1 d2 f -> prod_distr_com d1 d2 (finv f).

Lemma prod_distr_com_lub : forall (A B : Type) (d1: distr A) (d2:distr B) (f:nat -m> MF (A*B)),
         (forall n, prod_distr_com d1 d2 (f n)) -> prod_distr_com d1 d2 (lub f).

Lemma prod_distr_com_sym : forall A B (d1:distr A) (d2:distr B) (f:MF (A*B)),
     prod_distr_com d1 d2 f -> prod_distr_com d2 d1 (arg_swap f).

Lemma discrete_commute : forall A B (d1:distr A) (d2:distr B) (f:MF (A*B)),
    is_discrete d1 -> prod_distr_com d1 d2 f.

Lemma is_discrete_swap: forall A B C (d1:distr A) (d2:distr B) (f:A -> B -> distr C),
   is_discrete d1 ->
   Mlet d1 (fun x => Mlet d2 (fun y => f x y)) == Mlet d2 (fun y => Mlet d1 (fun x => f x y)).

Lemma is_discrete_swap_mu : forall A B (d1:distr A) (d2:distr B) (f:A -> B -> U),
   is_discrete d1 ->
   mu d1 (fun x : A => mu d2 (fun y : B => f x y)) ==
   mu d2 (fun y : B => mu d1 (fun x : A => f x y)).

Definition fst_distr A B (m : distr (A*B)) : distr A := im_distr (fst (B:=B)) m.
Definition snd_distr A B (m : distr (A*B)) : distr B := im_distr (snd (B:=B)) m.

Add Parametric Morphism (A B : Type) : (fst_distr (A:=A) (B:=B))
   with signature Oeq ==> Oeq as fst_distr_eq_compat.
Save.

Add Parametric Morphism (A B : Type) : (snd_distr (A:=A) (B:=B))
   with signature Oeq ==> Oeq as snd_distr_eq_compat.
Save.

Lemma fst_prod_distr : forall A B (m1:distr A) (m2:distr B),
           fst_distr (prod_distr m1 m2) == distr_scale (pone m2) m1.

Lemma snd_prod_distr : forall A B (m1:distr A) (m2:distr B),
          snd_distr (prod_distr m1 m2) == distr_scale (pone m1) m2.

Lemma pone_prod : forall A B (m1:distr A) (m2:distr B),
          pone (prod_distr m1 m2) == pone m1 * pone m2.

Instance prod_distr_term : forall A B (d1:distr A) (d2:distr B)
     {T1:Term d1} {T2:Term d2}, Term (prod_distr d1 d2).
Save.
Hint Resolve prod_distr_term.

Lemma fst_prod_distr_term : forall A B (d1:distr A) (d2:distr B) {T2:Term d2},
           fst_distr (prod_distr d1 d2) == d1.

Lemma snd_prod_distr_term : forall A B (d1:distr A) (d2:distr B) {T1:Term d1},
           snd_distr (prod_distr d1 d2) == d2.
Hint Resolve fst_prod_distr_term snd_prod_distr_term.

Independance of distribution


Definition prod_indep A B (m:distr (A*B)) :=
        distr_scale (pone m) m == prod_distr (fst_distr m) (snd_distr m).

Lemma prod_distr_indep : forall A B (m1:distr A) (m2:distr B), prod_indep (prod_distr m1 m2).

Add Parametric Morphism A B : (prod_indep (A:=A) (B:=B))
   with signature Oeq ==> Basics.impl
   as prod_indep_eq_compat.
Save.
Hint Resolve prod_indep_eq_compat.

Lemma distr_indep_mult
   : forall A B (m:distr (A*B)), prod_indep m ->
                     forall (f1 : MF A) (f2:MF B),
                     pone m * mu m (fun p => f1 (fst p) * f2 (snd p)) ==
                     mu (fst_distr m) f1 * mu (snd_distr m) f2.

Range of a distribution


Definition range A (P:A -> Prop) (d: distr A) :=
   forall f, (forall x, P x -> 0 == f x) -> 0 == mu d f.

Lemma range_le : forall A (P: A -> Prop) (d:distr A), range P d ->
       forall f g, (forall a, P a -> f a <= g a) -> mu d f <= mu d g.

Lemma range_eq : forall A (P: A -> Prop) (d:distr A), range P d ->
       forall f g, (forall a, P a -> f a == g a) -> mu d f == mu d g.

Lemma im_range A B (f : A -> B) :
    forall (d : distr A) (P : B -> Prop),
    range (fun x => P (f x)) d -> range P (im_distr f d).
Hint Resolve im_range.

Lemma range_impl A (P Q : A -> Prop) :
  forall (d:distr A), (forall x, P x -> Q x)
    -> range P d -> range Q d.

Lemma im_range_map A B (f : A -> B) :
    forall (d : distr A) (P : B -> Prop) (Q:A -> Prop),
    (forall x, Q x -> P (f x)) ->
    range Q d -> range P (im_distr f d).

Lemma im_range_prop A B (f : A -> B) :
    forall (d : distr A) (P : B -> Prop),
    (forall x, P (f x)) -> range P (im_distr f d).

Lemma range_le_compat : forall A (P : A -> Prop) (d1 d2 : distr A),
    d1 <= d2 -> range P d2 -> range P d1.

Add Parametric Morphism A (P : A -> Prop) : (range P)
    with signature Oeq ==> iff as range_distr_morph.
Save.


Prog.v: Axiomatic semantics

Definition of correctness judgements

  • ok p e q is defined as p <= mu e q
  • up p e q is defined as mu e q <= p

Definition ok (A:Type) (p:U) (e:distr A) (q:A -> U) := p <= mu e q.

Definition okfun (A B:Type)(p:A -> U)(e:A -> distr B)(q:A -> B -> U)
  := forall x:A, ok (p x) (e x) (q x).

Definition okup (A:Type) (p:U) (e:distr A) (q:A -> U) := mu e q <= p.

Definition upfun (A B:Type)(p:A -> U)(e:A -> distr B)(q:A -> B -> U)
  := forall x:A, okup (p x) (e x) (q x).

Stability properties

Lemma ok_le_compat : forall (A:Type) (p p':U) (e:distr A) (q q':A -> U),
    p' <= p -> q <= q' -> ok p e q -> ok p' e q'.

Lemma ok_eq_compat : forall (A:Type) (p p':U) (e e':distr A) (q q':A -> U),
    p' == p -> q == q' -> e == e' -> ok p e q -> ok p' e' q'.

Add Parametric Morphism (A:Type) : (@ok A)
  with signature Ole --> Oeq ==> Ole ==> Basics.impl
  as ok_le_morphism.
Save.

Add Parametric Morphism (A:Type) : (@ok A)
  with signature Oeq --> Oeq ==> Oeq ==> iff
  as ok_eq_morphism.
Save.

Lemma okfun_le_compat :
forall (A B:Type) (p p':A -> U) (e:A -> distr B) (q q':A -> B -> U),
    p' <= p -> q <= q' -> okfun p e q -> okfun p' e q'.

Lemma okfun_eq_compat :
forall (A B:Type) (p p':A -> U) (e e':A -> distr B) (q q':A -> B -> U),
    p' == p -> q == q' -> e == e' -> okfun p e q -> okfun p' e' q'.

Add Parametric Morphism (A B:Type) : (@okfun A B)
  with signature Ole --> Oeq ==> Ole ==> Basics.impl
  as okfun_le_morphism.
Save.

Add Parametric Morphism (A B:Type) : (@okfun A B)
  with signature Oeq --> Oeq ==> Oeq ==> iff
  as okfun_eq_morphism.
Save.

Lemma ok_mult : forall (A:Type)(k p:U)(e:distr A)(f : A -> U),
                           ok p e f -> ok (k*p) e (fmult k f).

Lemma ok_inv : forall (A:Type)(p:U)(e:distr A)(f : A -> U),
             ok p e f -> mu e (finv f) <= [1-]p.

Lemma okup_le_compat : forall (A:Type) (p p':U) (e:distr A) (q q':A -> U),
    p <= p' -> q' <= q -> okup p e q -> okup p' e q'.

Lemma okup_eq_compat : forall (A:Type) (p p':U) (e e':distr A) (q q':A -> U),
    p == p' -> q == q' -> e == e' -> okup p e q -> okup p' e' q'.

Lemma upfun_le_compat : forall (A B:Type) (p p':A -> U) (e:A -> distr B)
    (q q':A -> B -> U),
    p <= p' -> q'<=q -> upfun p e q -> upfun p' e q'.

Lemma okup_mult : forall (A:Type)(k p:U)(e:distr A)(f : A -> U), okup p e f -> okup (k*p) e (fmult k f).

Basic rules

Rules for application:

  • ok r a p and forall x, ok (p x) (f x) q implies ok r (f a) q
  • up r a p and forall x, up (p x) (f x) q implies up r (f a) q

Lemma apply_rule : forall (A B:Type)(a:(distr A))(f:A -> distr B)(r:U)(p:A -> U)(q:B -> U),
    ok r a p -> okfun p f (fun x => q) -> ok r (Mlet a f) q.

Lemma okup_apply_rule : forall (A B:Type)(a:distr A)(f:A -> distr B)(r:U)(p:A -> U)(q:B -> U),
    okup r a p -> upfun p f (fun x => q) -> okup r (Mlet a f) q.

Rules for abstraction

Lemma lambda_rule : forall (A B:Type)(f:A -> distr B)(p:A -> U)(q:A -> B -> U),
   (forall x:A, ok (p x) (f x) (q x)) -> okfun p f q.

Lemma okup_lambda_rule : forall (A B:Type)(f:A -> distr B)(p:A -> U)(q:A -> B -> U),
   (forall x:A, okup (p x) (f x) (q x)) -> upfun p f q.


Rules for conditional

  • ok p1 e1 q and ok p2 e2 q implies ok (p1 * mu b (χ true) + p2 * mu b (χ false) (if b then e1 else e2) q
  • up p1 e1 q and up p2 e2 q implies up (p1 * mu b (χ true) + p2 * mu b (χ false) (if b then e1 else e2) q

Lemma combiok : forall (A:Type) p q (f1 f2 : A -> U), p <= [1-]q -> fplusok (fmult p f1) (fmult q f2).
Hint Extern 1 => apply combiok.

Lemma fmult_fplusok : forall (A:Type) p q (f1 f2 : A -> U), fplusok f1 f2 -> fplusok (fmult p f1) (fmult q f2).
Hint Resolve fmult_fplusok.

Lemma ifok : forall f1 f2, fplusok (fmult f1 B2U) (fmult f2 NB2U).
Hint Resolve ifok.

Lemma Mif_eq : forall (A:Type)(b:(distr bool))(f1 f2:distr A)(q:MF A),
        mu (Mif b f1 f2) q == (mu f1 q) * (mu b B2U) + (mu f2 q) * (mu b NB2U).

Lemma Mif_eq2 : forall (A : Type) (b : distr bool) (f1 f2 : distr A) (q : MF A),
  mu (Mif b f1 f2) q == mu b B2U * mu f1 q + mu b NB2U * mu f2 q.

Lemma ifrule :
  forall (A:Type)(b:(distr bool))(f1 f2:distr A)(p1 p2:U)(q:A -> U),
       ok p1 f1 q -> ok p2 f2 q
       -> ok (p1 * (mu b B2U) + p2 * (mu b NB2U)) (Mif b f1 f2) q.

Lemma okup_ifrule :
  forall (A:Type)(b:(distr bool))(f1 f2:distr A)(p1 p2:U)(q:A -> U),
       okup p1 f1 q -> okup p2 f2 q
       -> okup (p1 * (mu b B2U) + p2 * (mu b NB2U)) (Mif b f1 f2) q.


Rule for fixpoints

with φ x = F φ x, p an increasing sequence of functions starting from 0
forall f i, (forall x, ok (p i x) f q => forall x, ok p (i+1) x (F f x) q) implies forall x, ok (lub p x) (φ x) q
Section Fixrule.
Variables A B : Type.

Variable F : (A -> distr B) -m> (A -> distr B).

Section Ruleseq.
Variable q : A -> B -> U.

Lemma fixrule_Ulub : forall (p : A -> nat -> U),
   (forall x:A, p x O == 0)->
   (forall (i:nat) (f:A -> distr B),
      (okfun (fun x => p x i) f q) -> okfun (fun x => p x (S i)) (fun x => F f x) q)
   -> okfun (fun x => Ulub (p x)) (Mfix F) q.

Lemma fixrule : forall (p : A -> nat -m> U),
   (forall x:A, p x O == 0)->
   (forall (i:nat) (f:A -> distr B),
      (okfun (fun x => p x i) f q) -> okfun (fun x => p x (S i)) (fun x => F f x) q)
   -> okfun (fun x => lub (p x)) (Mfix F) q.

Lemma fixrule_up_Ulub : forall (p : A -> nat -> U),
   (forall (i:nat) (f:A -> distr B),
      (upfun (fun x => p x i) f q) -> upfun (fun x => p x (S i)) (fun x => F f x) q)
   -> upfun (fun x => Ulub (p x)) (Mfix F) q.

Lemma fixrule_up_lub : forall (p : A -> nat -m> U),
   (forall (i:nat) (f:A -> distr B),
      (upfun (fun x => p x i) f q) -> upfun (fun x => p x (S i)) (fun x => F f x) q)
   -> upfun (fun x => lub (p x)) (Mfix F) q.

Lemma okup_fixrule_glb :
   forall p : A -> nat -m-> U,
   (forall (i:nat) (f:A -> distr B),
       (upfun (fun x => p x i) f q) -> upfun (fun x => p x (S i)) (fun x => F f x) q)
   -> upfun (fun x => glb (p x)) (Mfix F) q.
End Ruleseq.

Lemma okup_fixrule_inv : forall (p : A -> U) (q : A -> B -> U),
   (forall (f:A -> distr B), upfun p f q -> upfun p (fun x => F f x) q)
           -> upfun p (Mfix F) q.


Rules using commutation properties


Section TransformFix.

Section Fix_muF.
Variable q : A -> B -> U.
Variable muF : MF A -m> MF A.

Definition admissible (P:(A -> distr B) -> Prop) := P 0 /\ forall f, P f -> P (F f).

Lemma admissible_true : admissible (fun f => True).

Lemma admissible_le_fix :
  continuous (D1:=A -> distr B) (D2:=A -> distr B) F -> admissible (fun f => f <= Mfix F).
BUG: rewrite fails

Lemma muF_stable : stable muF.

Definition mu_muF_commute_le :=
  forall f x, f <= Mfix F -> mu (F f x) (q x) <= muF (fun y => mu (f y) (q y)) x.
Hint Unfold mu_muF_commute_le.

Section F_muF_results.
Hypothesis F_muF_le : mu_muF_commute_le.

Lemma mu_mufix_le : forall x, mu (Mfix F x) (q x) <= mufix muF x.
Hint Resolve mu_mufix_le.

Lemma muF_le : forall f, muF f <= f
      -> forall x, mu (Mfix F x) (q x) <= f x.

Hypothesis muF_F_le :
    forall f x, f <= Mfix F -> muF (fun y => mu (f y) (q y)) x <= mu (F f x) (q x).

Lemma mufix_mu_le : forall x, mufix muF x <= mu (Mfix F x) (q x).

End F_muF_results.
Hint Resolve mu_mufix_le mufix_mu_le.

Lemma mufix_mu :
   (forall f x, f <= Mfix F -> mu (F f x) (q x) == muF (fun y => mu (f y) (q y)) x)
   -> forall x, mufix muF x == mu (Mfix F x) (q x).
Hint Resolve mufix_mu.
End Fix_muF.

Section Fix_Term.

Definition pterm : MF A := fun (x:A) => mu (Mfix F x) (fone B).
Variable muFone : MF A -m> MF A.

Hypothesis F_muF_eq_one :
  forall f x, f <= Mfix F -> mu (F f x) (fone B) == muFone (fun y => mu (f y) (fone B)) x.

Hypothesis muF_cont : continuous muFone.

Lemma muF_pterm : pterm == muFone pterm.
Hint Resolve muF_pterm.
End Fix_Term.

Section Fix_muF_Term.

Variable q : A -> B -> U.
Definition qinv x y := [1-]q x y.

Variable muFqinv : MF A -m> MF A.

Hypothesis F_muF_le_inv : mu_muF_commute_le qinv muFqinv.

Lemma muF_le_term : forall f, muFqinv (finv f) <= finv f ->
    forall x, f x & pterm x <= mu (Mfix F x) (q x).

Lemma muF_le_term_minus :
forall f, f <= pterm -> muFqinv (fminus pterm f) <= fminus pterm f ->
    forall x, f x <= mu (Mfix F x) (q x).

Variable muFq : MF A -m> MF A.

Hypothesis F_muF_le : mu_muF_commute_le q muFq.

Lemma muF_eq : forall f, muFq f <= f -> muFqinv (finv f) <= finv f ->
    forall x, pterm x == 1 -> mu (Mfix F x) (q x) == f x.

End Fix_muF_Term.
End TransformFix.

Section LoopRule.
Variable q : A -> B -> U.
Variable stop : A -> distr bool.
Variable step : A -> distr A.
Variable a : U.

Definition Loop : MF A -m> MF A.
Defined.

Lemma Loop_eq :
    forall f x, Loop f x = mu (stop x) (fun b => if b then a else mu (step x) f).

Definition loop := mufix Loop.

Lemma Mfixvar :
  (forall (f:A -> distr B),
      okfun (fun x => Loop (fun y => mu (f y) (q y)) x) (fun x => F f x) q)
 -> okfun loop (Mfix F) q.

Definition up_loop : MF A := nufix Loop.

Lemma Mfixvar_up :
  (forall (f:A -> distr B),
      upfun (fun x => Loop (fun y => mu (f y) (q y)) x) (fun x => F f x) q)
 -> upfun up_loop (Mfix F) q.

End LoopRule.

End Fixrule.


Rules for Flip


Lemma Flip_true : mu Flip B2U == [1/2].

Lemma Flip_false : mu Flip NB2U == [1/2].

Lemma ok_Flip : forall q : bool -> U, ok ([1/2] * q true + [1/2] * q false) Flip q.

Lemma okup_Flip : forall q : bool -> U, okup ([1/2] * q true + [1/2] * q false) Flip q.

Hint Resolve ok_Flip okup_Flip Flip_true Flip_false.

Lemma Flip_eq : forall q : bool -> U, mu Flip q == [1/2] * q true + [1/2] * q false.
Hint Resolve Flip_eq.

Rules for total (well-founded) fixpoints


Section Wellfounded.
Variables A B : Type.
Variable R : A -> A -> Prop.
Hypothesis Rwf : well_founded R.
Variable F : forall x, (forall y, R y x -> distr B) -> distr B.

Definition WfFix : A -> distr B := Fix Rwf (fun _ => distr B) F.

Hypothesis Fext : forall x f g, (forall y (p:R y x), f y p == g y p) -> F f == F g.

Lemma Acc_iter_distr :
   forall x, forall r s : Acc R x, Acc_iter (fun _=> distr B) F r == Acc_iter (fun _=> distr B) F s.

Lemma WfFix_eq : forall x, WfFix x == F (fun (y:A) (p:R y x) => WfFix y).

Variable P : distr B -> Prop.
Hypothesis Pext : forall m1 m2, m1 == m2 -> P m1 -> P m2.

Lemma WfFix_ind :
     (forall x f, (forall y (p:R y x), P (f y p)) -> P (F f))
  -> forall x, P (WfFix x).

End Wellfounded.

Ltac distrsimpl := match goal with
 | |- (Ole (fmont (mu ?d1) ?f) (fmont (mu ?d2) ?g)) => apply (mu_le_compat (m1:=d1) (m2:=d2) (Ole_refl d1) (f:=f) (g:=g)); intro
 | |- (Oeq (fmont (mu ?d1) ?f) (fmont (mu ?d2) ?g)) => apply (mu_eq_compat (m1:=d1) (m2:=d2) (Oeq_refl d1) (f:=f) (g:=g)); intro
 | |- (Oeq (Munit ?x) (Munit ?y)) => apply (Munit_eq_compat x y)
 | |- (Oeq (Mlet ?x1 ?f) (Mlet ?x2 ?g))
              => apply (Mlet_eq_compat (m1:=x1) (m2:=x2) (M1:=f) (M2:=g) (Oeq_refl x1)); intro
 | |- (Ole (Mlet ?x1 ?f) (Mlet ?x2 ?g))
              => apply (Mlet_le_compat (m1:=x1) (m2:=x2) (M1:=f) (M2:=g) (Ole_refl x1)); intro
 | |- context [(fmont (mu (Mlet ?m ?M)) ?f)] => rewrite (Mlet_simpl m M f)
 | |- context [(fmont (mu (Munit ?x)) ?f)] => rewrite (Munit_simpl f x)
 | |- context [(Mlet (Mlet ?m ?M) ?f)] => rewrite (Mlet_assoc m M f)
 | |- context [(Mlet (Munit ?x) ?f)] => rewrite (Mlet_unit x f)
 | |- context [(fmont (mu Flip) ?f)] => rewrite (Flip_simpl f)
 | |- context [(fmont (mu (Discrete ?d)) ?f)] => rewrite (Discrete_simpl d);
                                                           rewrite (discrete_simpl (coeff d) (points d) f)
 | |- context [(fmont (mu (Random ?n)) ?f)] => rewrite (Random_simpl n);
                                                           rewrite (random_simpl n f)
 | |- context [(fmont (mu (Mif ?b ?f ?g)) ?h)] => unfold Mif
 | |- context [(fmont (mu (Mchoice ?p ?m1 ?m2)) ?f)] => rewrite (Mchoice_simpl p m1 m2 f)
 | |- context [(fmont (mu (im_distr ?f ?m)) ?h)] => rewrite (im_distr_simpl f m h)
 | |- context [(fmont (mu (prod_distr ?m1 ?m2)) ?h)] => unfold prod_distr
 | |- context [((mon ?f (fmonotonic:=?mf)) ?x)] => rewrite (mon_simpl f (mf:=mf) x)
end.