Theory MonadSE

(*****************************************************************************
 * Clean
 *                                                                            
 * HOL-TestGen --- theorem-prover based test case generation
 *                 http://www.brucker.ch/projects/hol-testgen/
 *                                                                            
 * Copyright (c) 2005-2007 ETH Zurich, Switzerland
 *               2009-2017 Univ. Paris-Sud, France 
 *               2009-2012 Achim D. Brucker, Germany
 *               2015-2017 University Sheffield, UK
 *               2018-2019 Université Paris-Saclay, Univ. Paris-Sud, France
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 *     * Neither the name of the copyright holders nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************)

(*
 * Monads --- a base testing theory for sequential computations.
 * This file is part of HOL-TestGen.
 *)

theory MonadSE
  imports Main
begin
        
section‹Definition : Standard State Exception Monads›
text‹State exception monads in our sense are a direct, pure formulation
of automata with a partial transition function.›

subsection‹Definition : Core Types and Operators›

type_synonym ('o, ) MONSE = "  ('o × )" (* = 'σ ⇒ ('o × 'σ)option *)       
      
definition bind_SE :: "('o,)MONSE  ('o  ('o',)MONSE)  ('o',)MONSE" 
where     "bind_SE f g = (λσ. case f σ of None  None 
                                        | Some (out, σ')  g out σ')"

notation bind_SE ("bindSE")

syntax    (xsymbols)
          "_bind_SE" :: "[pttrn,('o,)MONSE,('o',)MONSE]  ('o',)MONSE" 
          ("(2 _  _; _)" [5,8,8]8)
translations 
          "x  f; g" == "CONST bind_SE f (% x . g)"

definition unit_SE :: "'o  ('o, )MONSE"   ("(result _)" 8) 
where     "unit_SE e = (λσ. Some(e,σ))"
notation   unit_SE ("unitSE")

text‹In the following, we prove the required Monad-laws›

lemma bind_right_unit[simp]: "(x  m; result x) = m"
  apply (simp add:  unit_SE_def bind_SE_def)
  apply (rule ext)
  apply (case_tac "m σ", simp_all)
  done

lemma bind_left_unit [simp]: "(x  result c; P x) = P c"
  by (simp add: unit_SE_def bind_SE_def)
  
lemma bind_assoc[simp]: "(y  (x  m; k x); h y) = (x  m; (y  k x; h y))"
  apply (simp add: unit_SE_def bind_SE_def, rule ext)
  apply (case_tac "m σ", simp_all)
  apply (case_tac "a", simp_all)
  done
    
subsection‹Definition : More Operators and their Properties›

definition fail_SE :: "('o, )MONSE"
where     "fail_SE = (λσ. None)" 
notation   fail_SE ("failSE")

definition assert_SE :: "(  bool)  (bool, )MONSE"
where     "assert_SE P = (λσ. if P σ then Some(True,σ) else None)"
notation   assert_SE ("assertSE")

definition assume_SE :: "(  bool)  (unit, )MONSE"
where     "assume_SE P = (λσ. if σ . P σ then Some((), SOME σ . P σ) else None)"
notation   assume_SE ("assumeSE")


lemma bind_left_fail_SE[simp] : "(x  failSE; P x) = failSE"
  by (simp add: fail_SE_def bind_SE_def)


text‹We also provide a "Pipe-free" - variant of the bind operator.
Just a "standard" programming sequential operator without output frills.›
(* TODO: Eliminate/Modify this. Is a consequence of the Monad-Instantiation. *)


definition bind_SE' :: "(, )MONSE  (, )MONSE  (, )MONSE" (infixr ";-" 10)
where     "(f ;- g) = (_  f ; g)"

lemma bind_assoc'[simp]: "((m ;- k);- h ) = (m;- (k;- h))"
by(simp add:bind_SE'_def)


lemma bind_left_unit' [simp]: "((result c);- P) = P"
  by (simp add:  bind_SE'_def)
  

lemma bind_left_fail_SE'[simp]: "(failSE;- P) = failSE"
  by (simp add: bind_SE'_def)

lemma bind_right_unit'[simp]: "(m;- (result ())) = m"
  by (simp add:  bind_SE'_def)
          
text‹The bind-operator in the state-exception monad yields already
       a semantics for the concept of an input sequence on the meta-level:›
lemma     syntax_test: "(o1  f1 ; o2  f2; result (post o1 o2)) = X"
oops
  
definition yieldC :: "('a   'b)   ('b,'a ) MONSE"
    where "yieldC f  (λσ. Some(f σ, σ))"


definition try_SE :: "('o,) MONSE  ('o option,) MONSE" ("trySE")
where     "trySE ioprog = (λσ. case ioprog σ of
                                      None  Some(None, σ)
                                    | Some(outs, σ')  Some(Some outs, σ'))" 
text‹In contrast, mbind as a failure safe operator can roughly be seen 
       as a foldr on bind - try:
       m1 ; try m2 ; try m3; ... Note, that the rough equivalence only holds for
       certain predicates in the sequence - length equivalence modulo None,
       for example. However, if a conditional is added, the equivalence
       can be made precise:›
  
text‹On this basis, a symbolic evaluation scheme can be established
  that reduces mbind-code to try\_SE\_code and ite-cascades.›

definition alt_SE :: "[('o, )MONSE, ('o, )MONSE]  ('o, )MONSE"   (infixl "SE" 10)
where     "(f SE g) = (λ σ. case f σ of None  g σ
                                      | Some H  Some H)"

definition malt_SE :: "('o, )MONSE list  ('o, )MONSE"
where     "malt_SE S = foldr alt_SE S failSE"
notation   malt_SE ("SE")

lemma malt_SE_mt [simp]: "SE [] = failSE"
by(simp add: malt_SE_def)

lemma malt_SE_cons [simp]: "SE (a # S) = (a SE (SE S))"
by(simp add: malt_SE_def)


subsection‹Definition : Programming Operators and their Properties›

definition  "skipSE = unitSE ()"

definition if_SE :: "[  bool, (, )MONSE, (, )MONSE]  (, )MONSE"
where     "if_SE c E F = (λσ. if c σ then E σ else F σ)" 

syntax    (xsymbols)
          "_if_SE" :: "[  bool,('o,)MONSE,('o',)MONSE]  ('o',)MONSE" 
          ("(ifSE _ then _ else _fi)" [5,8,8]8)
translations 
          "(ifSE cond then T1 else T2 fi)" == "CONST if_SE cond T1 T2"


subsection‹Theory of a Monadic While›

text‹Prerequisites›
fun replicator :: "[('a, )MONSE, nat]  (unit, )MONSE" (infixr "^^^" 60)
where     "f ^^^ 0      = (result ())"
        | "f ^^^ (Suc n) = (f ;- f ^^^  n)"


fun replicator2 :: "[('a, )MONSE, nat, ('b, )MONSE]  ('b, )MONSE" (infixr "^:^" 60)
where     "(f ^:^ 0) M      = (M )"
        | "(f ^:^ (Suc n)) M = (f ;- ((f ^:^  n) M))"


text‹First Step : Establishing an embedding between partial functions and relations› 
(* plongement *)
definition Mon2Rel :: "(unit, )MONSE  ( × ) set"
where "Mon2Rel f = {(x, y). (f x = Some((), y))}"
(* ressortir *)
definition Rel2Mon :: " ( × ) set  (unit, )MONSE "
where "Rel2Mon S = (λ σ. if σ'. (σ, σ')  S then Some((), SOME σ'. (σ, σ')  S) else None)"

lemma Mon2Rel_Rel2Mon_id: assumes det:"single_valued R" shows "(Mon2Rel  Rel2Mon) R = R"
apply (simp add: comp_def Mon2Rel_def Rel2Mon_def,auto)
apply (case_tac "σ'. (a, σ')  R", auto)
apply (subst (2) some_eq_ex) 
using det[simplified single_valued_def] by auto


lemma Rel2Mon_Id: "(Rel2Mon  Mon2Rel) x = x"
apply (rule ext)
apply (auto simp: comp_def Mon2Rel_def Rel2Mon_def)
apply (erule contrapos_pp, drule HOL.not_sym, simp)
done

lemma single_valued_Mon2Rel: "single_valued (Mon2Rel B)"
by (auto simp: single_valued_def Mon2Rel_def)

text‹Second Step : Proving an induction principle allowing to establish that lfp remains
       deterministic› 


(* A little complete partial order theory due to Tobias Nipkow *)
definition chain :: "(nat  'a set)  bool" 
where     "chain S = (i. S i  S(Suc i))"

lemma chain_total: "chain S ==> S i  S j  S j  S i"
by (metis chain_def le_cases lift_Suc_mono_le)

definition cont :: "('a set => 'b set) => bool" 
where     "cont f = (S. chain S  f(UN n. S n) = (UN n. f(S n)))"

lemma mono_if_cont: fixes f :: "'a set  'b set"
  assumes "cont f" shows "mono f"
proof
  fix a b :: "'a set" assume "a  b"
  let ?S = "λn::nat. if n=0 then a else b"
  have "chain ?S" using a  b by(auto simp: chain_def)
  hence "f(UN n. ?S n) = (UN n. f(?S n))"
    using assms by (metis cont_def)
  moreover have "(UN n. ?S n) = b" using a  b by (auto split: if_splits)
  moreover have "(UN n. f(?S n)) = f a  f b" by (auto split: if_splits)
  ultimately show "f a  f b" by (metis Un_upper1)
qed

lemma chain_iterates: fixes f :: "'a set  'a set"
  assumes "mono f" shows "chain(λn. (f^^n) {})"
proof-
  { fix n have "(f ^^ n) {}  (f ^^ Suc n) {}" using assms
    by(induction n) (auto simp: mono_def) }
  thus ?thesis by(auto simp: chain_def)
qed

theorem lfp_if_cont:
  assumes "cont f" shows "lfp f =  (n. (f ^^ n) {})" (is "_ = ?U")
proof
  show "lfp f  ?U"
  proof (rule lfp_lowerbound)
    have "f ?U = (UN n. (f^^Suc n){})"
      using chain_iterates[OF mono_if_cont[OF assms]] assms
      by(simp add: cont_def)
    also have " = (f^^0){}  " by simp
    also have " = ?U"
      apply(auto simp del: funpow.simps)
      by (metis empty_iff funpow_0 old.nat.exhaust)
    finally show "f ?U  ?U" by simp
  qed
next
  { fix n p assume "f p  p"
    have "(f^^n){}  p"
    proof(induction n)
      case 0 show ?case by simp
    next
      case Suc
      from monoD[OF mono_if_cont[OF assms] Suc] f p  p
      show ?case by simp
    qed
  }
  thus "?U  lfp f" by(auto simp: lfp_def)
qed


lemma single_valued_UN_chain:
  assumes "chain S" "(!!n. single_valued (S n))"
  shows "single_valued(UN n. S n)"
proof(auto simp: single_valued_def)
  fix m n x y z assume "(x, y)  S m" "(x, z)  S n"
  with chain_total[OF assms(1), of m n] assms(2)
  show "y = z" by (auto simp: single_valued_def)
qed

lemma single_valued_lfp: 
fixes f :: "('a × 'a) set  ('a × 'a) set"
assumes "cont f" "r. single_valued r  single_valued (f r)"
shows "single_valued(lfp f)"
unfolding lfp_if_cont[OF assms(1)]
proof(rule single_valued_UN_chain[OF chain_iterates[OF mono_if_cont[OF assms(1)]]])
  fix n show "single_valued ((f ^^ n) {})"
  by(induction n)(auto simp: assms(2))
qed


text‹Third Step: Definition of the Monadic While 
definition Γ :: "[  bool,( × ) set]  (( × ) set  ( × ) set)" 
where     "Γ b cd = (λcw. {(s,t). if b s then (s, t)  cd O cw else s = t})"


definition while_SE :: "[  bool, (unit, )MONSE]  (unit, )MONSE"
where     "while_SE c B  (Rel2Mon(lfp(Γ c (Mon2Rel B))))"

syntax    (xsymbols)
          "_while_SE" :: "[  bool, (unit, )MONSE]  (unit, )MONSE" 
          ("(whileSE _ do _ od)" [8,8]8)
translations 
          "whileSE c do b od" == "CONST while_SE c b"

lemma cont_Γ: "cont (Γ c b)"
by (auto simp: cont_def Γ_def)

text‹The fixpoint theory now allows us to establish that the lfp constructed over
       @{term Mon2Rel} remains deterministic›

theorem single_valued_lfp_Mon2Rel: "single_valued (lfp(Γ c (Mon2Rel B)))"
apply(rule single_valued_lfp, simp_all add: cont_Γ)
apply(auto simp: Γ_def single_valued_def)
apply(metis single_valued_Mon2Rel[of "B"] single_valued_def)
done


lemma Rel2Mon_if:
  "Rel2Mon {(s, t). if b s then (s, t)  Mon2Rel c O lfp (Γ b (Mon2Rel c)) else s = t} σ =
  (if b σ then Rel2Mon (Mon2Rel c O lfp (Γ b (Mon2Rel c))) σ else Some ((), σ))"
by (simp add: Rel2Mon_def)

lemma Rel2Mon_homomorphism:
  assumes determ_X: "single_valued X" and determ_Y: "single_valued Y"
  shows   "Rel2Mon (X O Y) = ((Rel2Mon X) ;- (Rel2Mon Y))"
proof - 
    have relational_partial_next_in_O: "x E F. (y. (x, y)  (E O F))  (y. (x, y)  E)" 
                        by (auto)
    have some_eq_intro: " X x y . single_valued X  (x, y)  X  (SOME y. (x, y)  X) = y"
                        by (auto simp: single_valued_def)

    show ?thesis
apply (simp add: Rel2Mon_def bind_SE'_def bind_SE_def)
apply (rule ext, rename_tac "σ")
apply (case_tac "  σ'. (σ, σ')  X O Y")
apply (simp only: HOL.if_True)
apply (frule relational_partial_next_in_O)
apply (auto simp: single_valued_relcomp some_eq_intro determ_X determ_Y relcomp.relcompI)
by blast
qed



text‹Putting everything together, the theory of embedding and the invariance of
       determinism of the while-body, gives us the usual unfold-theorem:›
theorem while_SE_unfold:
"(whileSE b do c od) = (ifSE b then (c ;- (whileSE b do c od)) else result () fi)"
apply (simp add: if_SE_def bind_SE'_def while_SE_def unit_SE_def)
apply (subst lfp_unfold [OF mono_if_cont, OF cont_Γ])
apply (rule ext)
apply (subst Γ_def)
apply (auto simp: Rel2Mon_if Rel2Mon_homomorphism bind_SE'_def Rel2Mon_Id [simplified comp_def] 
                  single_valued_Mon2Rel single_valued_lfp_Mon2Rel )
done
  

lemma bind_cong : " f σ = g σ   (x  f ; M x)σ = (x  g ; M x)σ"
  unfolding bind_SE'_def bind_SE_def  by simp

lemma bind'_cong : " f σ = g σ   (f ;- M )σ = (g ;- M )σ"
  unfolding bind_SE'_def bind_SE_def  by simp

  
  
lemma ifSE_True [simp]: "(ifSE (λ x. True) then c else d fi) = c" 
  apply(rule ext) by (simp add: MonadSE.if_SE_def) 

lemma ifSE_False [simp]: "(ifSE (λ x. False) then c else d fi) = d" 
  apply(rule ext) by (simp add: MonadSE.if_SE_def) 
  
    
lemma ifSE_cond_cong : "f σ = g σ  
                           (ifSE f then c else d fi) σ = 
                           (ifSE g then c else d fi) σ"
  unfolding if_SE_def  by simp
 
lemma whileSE_skip[simp] : "(whileSE (λ x. False) do c od) = skipSE" 
  apply(rule ext,subst MonadSE.while_SE_unfold)
  by (simp add: MonadSE.if_SE_def skipSE_def)
  
    
end