Theory AST_SAS_Plus_Equivalence

(*
  Author: Mohammad Abdulaziz
*)
theory AST_SAS_Plus_Equivalence
  imports "AI_Planning_Languages_Semantics.SASP_Semantics" "SAS_Plus_Semantics" "List-Index.List_Index" 
begin                                                               

section ‹Proving Equivalence of SAS+ representation and Fast-Downward's Multi-Valued Problem
         Representation›

subsection ‹Translating Fast-Downward's represnetation to SAS+›


type_synonym nat_sas_plus_problem = "(nat, nat) sas_plus_problem" 
type_synonym nat_sas_plus_operator = "(nat, nat) sas_plus_operator" 
type_synonym nat_sas_plus_plan = "(nat, nat) sas_plus_plan" 
type_synonym nat_sas_plus_state = "(nat, nat) state" 


definition is_standard_effect :: "ast_effect  bool"
  where "is_standard_effect  λ(pre, _, _, _). pre = []" 

definition is_standard_operator :: "ast_operator  bool"
  where "is_standard_operator  λ(_, _, effects, _). list_all is_standard_effect effects"

fun rem_effect_implicit_pres:: "ast_effect  ast_effect" where
  "rem_effect_implicit_pres (preconds, v, implicit_pre, eff) = (preconds, v, None, eff)" 

fun rem_implicit_pres :: "ast_operator  ast_operator" where
  "rem_implicit_pres (name, preconds, effects, cost) =
     (name, (implicit_pres effects) @ preconds, map rem_effect_implicit_pres effects, cost)"

fun rem_implicit_pres_ops :: "ast_problem  ast_problem" where
  "rem_implicit_pres_ops (vars, init, goal, ops) = (vars, init, goal, map rem_implicit_pres ops)"

definition "consistent_map_lists xs1 xs2  ((x1,x2)  set xs1. (y1,y2) set xs2. x1 = y1  x1 = y2)"

lemma map_add_comm: "(x. x  dom m1  x  dom m2  m1 x = m2 x)  m1 ++ m2 = m2 ++ m1"
  by (fastforce simp add: map_add_def split: option.splits)

lemma first_map_add_submap: "(x. x  dom m1  x  dom m2  m1 x = m2 x) 
        m1 ++ m2 m x  m1 m x"
  using map_add_le_mapE map_add_comm
  by force

lemma subsuming_states_map_add:
  "(x. x  dom m1  dom m2  m1 x = m2 x) 
  m1 ++ m2 m s  (m1 m s  m2 m s)"
  by(auto simp: map_add_le_mapI intro: first_map_add_submap map_add_le_mapE)

lemma consistent_map_lists:
  "distinct (map fst (xs1 @ xs2)); x  dom (map_of xs1)  dom (map_of xs2)  
     (map_of xs1) x = (map_of xs2) x"
  apply(induction xs1)
   apply (simp_all add: consistent_map_lists_def image_def)
  using map_of_SomeD
  by fastforce

lemma subsuming_states_append: 
  "distinct (map fst (xs @ ys))  
     (map_of (xs @ ys)) m s  ((map_of ys) m s  (map_of xs) m s)"
  unfolding map_of_append
  apply(intro subsuming_states_map_add)
  apply (auto simp add: image_def)
  by (metis (mono_tags, lifting) IntI empty_iff fst_conv mem_Collect_eq)

definition consistent_pres_op where
  "consistent_pres_op op  (case op of (name, pres, effs, cost)  
                               distinct (map fst (pres @ (implicit_pres effs)))
                                consistent_map_lists pres (implicit_pres effs))"

definition consistent_pres_op' where
  "consistent_pres_op' op  (case op of (name, pres, effs, cost)  
                               consistent_map_lists pres (implicit_pres effs))"

lemma consistent_pres_op_then': "consistent_pres_op op  consistent_pres_op' op"
  by(auto simp add: consistent_pres_op'_def consistent_pres_op_def)

lemma rem_implicit_pres_ops_valid_states:
   "ast_problem.valid_states (rem_implicit_pres_ops prob) = ast_problem.valid_states prob"
  apply(cases prob)
  by(auto simp add: ast_problem.valid_states_def ast_problem.Dom_def 
                       ast_problem.numVars_def ast_problem.astDom_def
                       ast_problem.range_of_var_def ast_problem.numVals_def)

lemma rem_implicit_pres_ops_lookup_op_None:
  "ast_problem.lookup_operator (vars, init, goal, ops) name = None  
   ast_problem.lookup_operator (rem_implicit_pres_ops (vars, init, goal, ops)) name = None"
  by (induction ops) (auto simp: ast_problem.lookup_operator_def ast_problem.astδ_def)

lemma rem_implicit_pres_ops_lookup_op_Some_1:
  "ast_problem.lookup_operator (vars, init, goal, ops) name = Some (n,p,vp,e) 
   ast_problem.lookup_operator (rem_implicit_pres_ops (vars, init, goal, ops)) name =
     Some (rem_implicit_pres (n,p,vp,e))"
  by (induction ops) (fastforce simp: ast_problem.lookup_operator_def ast_problem.astδ_def)+

lemma rem_implicit_pres_ops_lookup_op_Some_1':
  "ast_problem.lookup_operator prob name = Some (n,p,vp,e) 
   ast_problem.lookup_operator (rem_implicit_pres_ops prob) name =
     Some (rem_implicit_pres (n,p,vp,e))"
  apply(cases prob)
  using rem_implicit_pres_ops_lookup_op_Some_1
  by simp

lemma implicit_pres_empty: "implicit_pres (map rem_effect_implicit_pres effs) = []"
  by (induction effs) (auto simp: implicit_pres_def)

lemma rem_implicit_pres_ops_lookup_op_Some_2:
  "ast_problem.lookup_operator (rem_implicit_pres_ops (vars, init, goal, ops)) name = Some op
      op'. ast_problem.lookup_operator (vars, init, goal, ops) name = Some op' 
               (op = rem_implicit_pres op')"
  by (induction ops) (auto simp: ast_problem.lookup_operator_def ast_problem.astδ_def implicit_pres_empty image_def)

lemma rem_implicit_pres_ops_lookup_op_Some_2':
  "ast_problem.lookup_operator (rem_implicit_pres_ops prob) name = Some (n,p,e,c)
      op'. ast_problem.lookup_operator prob name = Some op' 
               ((n,p,e,c) = rem_implicit_pres op')"
  apply(cases prob)
  using rem_implicit_pres_ops_lookup_op_Some_2
  by auto

lemma subsuming_states_def':
  "s  ast_problem.subsuming_states prob ps = (s  (ast_problem.valid_states prob)  ps m s)"
  by (auto simp add: ast_problem.subsuming_states_def)

lemma rem_implicit_pres_ops_enabled_1:
  "(op. op  set (ast_problem.astδ prob)  consistent_pres_op op);
        ast_problem.enabled prob name s 
     ast_problem.enabled (rem_implicit_pres_ops prob) name s"
  by (fastforce simp: ast_problem.enabled_def rem_implicit_pres_ops_valid_states subsuming_states_def'
                      implicit_pres_empty
                 intro!: map_add_le_mapI
                 dest: rem_implicit_pres_ops_lookup_op_Some_1'
                 split: option.splits)+

context ast_problem
begin

lemma lookup_Some_inδ: "lookup_operator π = Some op  opset astδ"
    by(auto simp: find_Some_iff in_set_conv_nth lookup_operator_def)

end

lemma rem_implicit_pres_ops_enabled_2:
  assumes "(op. op  set (ast_problem.astδ prob)  consistent_pres_op op)"
  shows "ast_problem.enabled (rem_implicit_pres_ops prob) name s  
           ast_problem.enabled prob name s"
  using assms[OF ast_problem.lookup_Some_inδ, unfolded consistent_pres_op_def]
  apply(auto simp: subsuming_states_append rem_implicit_pres_ops_valid_states subsuming_states_def'
                   ast_problem.enabled_def
             dest!: rem_implicit_pres_ops_lookup_op_Some_2'
             split: option.splits)
  using subsuming_states_map_add consistent_map_lists
  apply (metis Map.map_add_comm dom_map_of_conv_image_fst map_add_le_mapE)
  using map_add_le_mapE by blast

lemma rem_implicit_pres_ops_enabled:
  "(op. op  set (ast_problem.astδ prob)  consistent_pres_op op) 
        ast_problem.enabled (rem_implicit_pres_ops prob) name s = ast_problem.enabled prob name s"
  using rem_implicit_pres_ops_enabled_1 rem_implicit_pres_ops_enabled_2
  by blast

context ast_problem
begin

lemma std_eff_enabled[simp]:
  "is_standard_operator (name, pres, effs, layer)  s  valid_states  (filter (eff_enabled s) effs) = effs"
  by (induction effs) (auto simp: is_standard_operator_def is_standard_effect_def eff_enabled_def subsuming_states_def)

end

lemma is_standard_operator_rem_implicit: "is_standard_operator (n,p,vp,v)  
         is_standard_operator (rem_implicit_pres (n,p,vp,v))"
  by (induction vp) (auto simp: is_standard_operator_def is_standard_effect_def)

lemma is_standard_operator_rem_implicit_pres_ops:
   "(op. op  set (ast_problem.astδ (a,b,c,d))  is_standard_operator op);
       op  set (ast_problem.astδ (rem_implicit_pres_ops (a,b,c,d)))
        is_standard_operator op"
  by (induction d) (fastforce simp add: ast_problem.astδ_def image_def dest!: is_standard_operator_rem_implicit)+

lemma is_standard_operator_rem_implicit_pres_ops':
   "op  set (ast_problem.astδ (rem_implicit_pres_ops prob));
    (op. op  set (ast_problem.astδ prob)  is_standard_operator op)
       is_standard_operator op"
  apply(cases prob)
  using is_standard_operator_rem_implicit_pres_ops
  by blast

lemma in_rem_implicit_pres_δ:
  "op  set (ast_problem.astδ prob) 
     rem_implicit_pres op  set (ast_problem.astδ (rem_implicit_pres_ops prob))"
  by(auto simp add: ast_problem.astδ_def)

lemma rem_implicit_pres_ops_execute:
  assumes
    "(op. op  set (ast_problem.astδ prob)  is_standard_operator op)" and
    "s  ast_problem.valid_states prob"
  shows "ast_problem.execute (rem_implicit_pres_ops prob) name s = ast_problem.execute prob name s"
proof-
  have "(n,ps,es,c)  set (ast_problem.astδ prob) 
       (filter (ast_problem.eff_enabled prob s) es) = es" for n ps es c
    using assms(2)
    by (auto simp add: ast_problem.std_eff_enabled dest!: assms(1))
  moreover have "(n,ps,es,c)  set (ast_problem.astδ prob) 
       (filter (ast_problem.eff_enabled (rem_implicit_pres_ops prob) s) (map rem_effect_implicit_pres es))
            = map rem_effect_implicit_pres es" for n ps es c
    using assms
    by (fastforce simp add: ast_problem.std_eff_enabled rem_implicit_pres_ops_valid_states
        dest!: is_standard_operator_rem_implicit_pres_ops'
        dest: in_rem_implicit_pres_δ)
  moreover have "map_of (map ((λ(_,x,_,v). (x,v)) o rem_effect_implicit_pres) effs) =
                    map_of (map (λ(_,x,_,v). (x,v)) effs)" for effs
    by (induction effs) auto
  ultimately show ?thesis
    by(auto simp add: ast_problem.execute_def rem_implicit_pres_ops_lookup_op_Some_1'
        split: option.splits
        dest: rem_implicit_pres_ops_lookup_op_Some_2' ast_problem.lookup_Some_inδ)
qed

lemma rem_implicit_pres_ops_path_to:
   "wf_ast_problem prob 
       (op. op  set (ast_problem.astδ prob)  consistent_pres_op op) 
       (op. op  set (ast_problem.astδ prob)  is_standard_operator op) 
       s  ast_problem.valid_states prob 
       ast_problem.path_to (rem_implicit_pres_ops prob) s πs s' = ast_problem.path_to prob s πs s'"
  by (induction πs arbitrary: s)
     (auto simp: rem_implicit_pres_ops_execute rem_implicit_pres_ops_enabled
                 ast_problem.path_to.simps wf_ast_problem.execute_preserves_valid)

lemma rem_implicit_pres_ops_astG[simp]: "ast_problem.astG (rem_implicit_pres_ops prob) =
           ast_problem.astG prob"
  apply(cases prob)
  by (auto simp add: ast_problem.astG_def)

lemma rem_implicit_pres_ops_goal[simp]: "ast_problem.G (rem_implicit_pres_ops prob) = ast_problem.G prob"
  apply(cases prob)
  using rem_implicit_pres_ops_valid_states
  by (auto simp add: ast_problem.G_def ast_problem.astG_def subsuming_states_def')

lemma rem_implicit_pres_ops_astI[simp]:
   "ast_problem.astI (rem_implicit_pres_ops prob) = ast_problem.astI prob"
  apply(cases prob)
  by (auto simp add: ast_problem.I_def ast_problem.astI_def subsuming_states_def')

lemma rem_implicit_pres_ops_init[simp]: "ast_problem.I (rem_implicit_pres_ops prob) = ast_problem.I prob"
  apply(cases prob)
  by (auto simp add: ast_problem.I_def ast_problem.astI_def)

lemma rem_implicit_pres_ops_valid_plan:
  assumes "wf_ast_problem prob"
       "(op. op  set (ast_problem.astδ prob)  consistent_pres_op op)"
       "(op. op  set (ast_problem.astδ prob)  is_standard_operator op)"
  shows "ast_problem.valid_plan (rem_implicit_pres_ops prob) πs = ast_problem.valid_plan prob πs"
  using wf_ast_problem.I_valid[OF assms(1)] rem_implicit_pres_ops_path_to[OF assms]
  by (simp add: ast_problem.valid_plan_def rem_implicit_pres_ops_goal rem_implicit_pres_ops_init)

lemma rem_implicit_pres_ops_numVars[simp]:
  "ast_problem.numVars (rem_implicit_pres_ops prob) = ast_problem.numVars prob"
  by (cases prob) (simp add: ast_problem.numVars_def ast_problem.astDom_def)

lemma rem_implicit_pres_ops_numVals[simp]:
  "ast_problem.numVals (rem_implicit_pres_ops prob) x = ast_problem.numVals prob x"
  by (cases prob) (simp add: ast_problem.numVals_def ast_problem.astDom_def)

lemma in_implicit_pres: 
  "(x, a)  set (implicit_pres effs)  (epres v vp. (epres,x,vp,v) set effs  vp = Some a)"
  by (induction effs) (fastforce simp: implicit_pres_def image_def split: if_splits)+

lemma pair4_eqD: "(a1,a2,a3,a4) = (b1,b2,b3,b4)  a3 = b3"
  by simp  

lemma rem_implicit_pres_ops_wf_partial_state:
   "ast_problem.wf_partial_state (rem_implicit_pres_ops prob) s =
         ast_problem.wf_partial_state prob s"
  by (auto simp: ast_problem.wf_partial_state_def)

lemma rem_implicit_pres_wf_operator:
  assumes "consistent_pres_op op"
    "ast_problem.wf_operator prob op"
  shows
    "ast_problem.wf_operator (rem_implicit_pres_ops prob) (rem_implicit_pres op)"
proof-
  obtain name pres effs cost where op: "op = (name, pres, effs, cost)"
    by (cases op)
  hence asses: "consistent_pres_op (name, pres, effs, cost)"
    "ast_problem.wf_operator prob (name, pres, effs, cost)"
    using assms
    by auto
  hence "distinct (map fst ((implicit_pres effs) @ pres))"
    by (simp only: consistent_pres_op_def) auto
  moreover have "x < ast_problem.numVars (rem_implicit_pres_ops prob)"
    "v < ast_problem.numVals (rem_implicit_pres_ops prob) x"
    if "(x,v)  set ((implicit_pres effs) @ pres)" for x v
    using that asses
    by (auto dest!: in_implicit_pres simp: ast_problem.wf_partial_state_def ast_problem.wf_operator_def)
  ultimately have "ast_problem.wf_partial_state (rem_implicit_pres_ops prob) ((implicit_pres effs) @ pres)"
    by (auto simp only: ast_problem.wf_partial_state_def)
  moreover have "(map (λ(_, v, _, _). v) effs) = 
                        (map (λ(_, v, _, _). v) (map rem_effect_implicit_pres effs))"
    by auto
  hence "distinct (map (λ(_, v, _, _). v) (map rem_effect_implicit_pres effs))"
    using assms(2)
    by (auto simp only: op ast_problem.wf_operator_def rem_implicit_pres.simps dest!: pair4_eqD)
  moreover have "(vp. (epres,x,vp,v)set effs)  (epres,x,None,v)set (map rem_effect_implicit_pres effs)"
    for epres x v
    by force
  ultimately show ?thesis
    using assms(2)
    by (auto simp: op ast_problem.wf_operator_def rem_implicit_pres_ops_wf_partial_state 
             split: prod.splits)      
qed

lemma rem_implicit_pres_ops_inδD: "op  set (ast_problem.astδ (rem_implicit_pres_ops prob))
         (op'. op'  set (ast_problem.astδ prob)  op = rem_implicit_pres op')"
  by (cases prob) (force simp: ast_problem.astδ_def)

lemma rem_implicit_pres_ops_well_formed:
  assumes "(op. op  set (ast_problem.astδ prob)  consistent_pres_op op)"
        "ast_problem.well_formed prob"
  shows "ast_problem.well_formed (rem_implicit_pres_ops prob)"
proof-
  have "map fst (ast_problem.astδ (rem_implicit_pres_ops prob)) = map fst (ast_problem.astδ prob)"
    by (cases prob) (auto simp: ast_problem.astδ_def)
  thus ?thesis
   using assms
   by(auto simp add: ast_problem.well_formed_def rem_implicit_pres_ops_wf_partial_state
           simp del: rem_implicit_pres.simps
           dest!: rem_implicit_pres_ops_inδD
           intro!: rem_implicit_pres_wf_operator)
qed

definition is_standard_effect'
  :: "ast_effect  bool"
  where "is_standard_effect'  λ(pre, _, vpre, _). pre = []  vpre = None" 

definition is_standard_operator'
  :: "ast_operator  bool"
  where "is_standard_operator'  λ(_, _, effects, _). list_all is_standard_effect' effects"

lemma rem_implicit_pres_is_standard_operator':
  "is_standard_operator (n,p,es,c)  is_standard_operator' (rem_implicit_pres (n,p,es,c))"
  by (induction es) (auto simp: is_standard_operator'_def is_standard_operator_def is_standard_effect_def
                                is_standard_effect'_def)

lemma rem_implicit_pres_ops_is_standard_operator':
  "(op. op  set (ast_problem.astδ (vs, I, G, ops))  is_standard_operator op) 
    πset (ast_problem.astδ (rem_implicit_pres_ops (vs, I, G, ops)))  is_standard_operator' π"
  by (cases ops) (auto simp: ast_problem.astδ_def dest!: rem_implicit_pres_is_standard_operator')

locale abs_ast_prob = wf_ast_problem + 
  assumes no_cond_effs: "πset astδ. is_standard_operator' π"

context ast_problem
begin

definition "abs_ast_variable_section = [0..<(length astDom)]"

definition abs_range_map
  :: "(nat  nat list)"
  where "abs_range_map  
        map_of (zip abs_ast_variable_section 
                    (map ((λvals. [0..<length vals]) o snd o snd)
                         astDom))"

end

context abs_ast_prob
begin
      
lemma is_valid_vars_1: "astDom  []  abs_ast_variable_section  []"
  by(simp add: abs_ast_variable_section_def)

end

lemma upt_eq_Nil_conv'[simp]: "([] = [i..<j]) = (j = 0  j  i)"
  by(induct j)simp_all

lemma map_of_zip_map_Some: 
     "v < length xs
         (map_of (zip [0..<length xs] (map f xs)) v) = Some (f (xs ! v))"
  by (induction xs rule: rev_induct) (auto simp add: nth_append map_add_Some_iff)

lemma map_of_zip_Some:
     "v < length xs
         (map_of (zip [0..<length xs] xs) v) = Some (xs ! v)"
  by (induction xs rule: rev_induct) (auto simp add: nth_append map_add_Some_iff)

lemma in_set_zip_lengthE:
  "(x,y)  set(zip [0..<length xs] xs)  ( x < length xs; xs ! x =y   R)  R"
  by (induction xs rule: rev_induct) (auto simp add: nth_append map_add_Some_iff)

context abs_ast_prob
begin

lemma is_valid_vars_2:
  shows "list_all (λv. abs_range_map v  None) abs_ast_variable_section"
  by (auto simp add: abs_range_map_def abs_ast_variable_section_def list.pred_set)
end

context ast_problem
begin

definition abs_ast_initial_state
  :: "nat_sas_plus_state" 
  where "abs_ast_initial_state  map_of (zip [0..<length astI] astI)"

end

context abs_ast_prob
begin

lemma valid_abs_init_1: "abs_ast_initial_state v  None  v  set abs_ast_variable_section"
  by (simp add: abs_ast_variable_section_def numVars_def wf_initial(1) abs_ast_initial_state_def)

lemma abs_range_map_Some:
  shows "v  set abs_ast_variable_section 
            (abs_range_map v) = Some [0..<length (snd (snd (astDom ! v)))]"
  by (simp add: numVars_def abs_range_map_def o_def abs_ast_variable_section_def map_of_zip_map_Some)

lemma in_abs_v_sec_length: "v  set abs_ast_variable_section  v < length astDom"
  by (simp add: abs_ast_variable_section_def)

lemma [simp]: "v < length astDom  (abs_ast_initial_state v) = Some (astI ! v)"
  using wf_initial(1)[simplified numVars_def, symmetric]
  by (auto simp add: map_of_zip_Some abs_ast_initial_state_def split: prod.splits)

lemma [simp]: "v < length astDom  astI ! v < length (snd (snd (astDom ! v)))"
  using wf_initial(1)[simplified numVars_def, symmetric] wf_initial
  by (auto simp add: numVals_def abs_ast_initial_state_def
              split: prod.splits)

lemma [intro!]: "v  set abs_ast_variable_section  x < length (snd (snd (astDom ! v))) 
                 x  set (the (abs_range_map v))"
  using abs_range_map_Some
  by (auto simp add: )

lemma [intro!]: "x<length astDom  astI ! x < length (snd (snd (astDom ! x)))"
  using wf_initial[unfolded numVars_def numVals_def]
  by auto

lemma [simp]: "abs_ast_initial_state v = Some a  a < length (snd (snd (astDom ! v)))"
  by(auto simp add: abs_ast_initial_state_def
                    wf_initial(1)[unfolded numVars_def numVals_def, symmetric]
          elim!: in_set_zip_lengthE)

lemma valid_abs_init_2:
  "abs_ast_initial_state v  None  (the (abs_ast_initial_state v))  set (the (abs_range_map v))"
  using valid_abs_init_1
  by auto

end

context ast_problem
begin

definition abs_ast_goal
  :: "nat_sas_plus_state" 
  where "abs_ast_goal  map_of astG"

end

context abs_ast_prob
begin

lemma [simp]: "wf_partial_state s  (v, a)  set s  v  set abs_ast_variable_section"
  by (auto simp add: wf_partial_state_def abs_ast_variable_section_def numVars_def
           split: prod.splits)

lemma valid_abs_goal_1: "abs_ast_goal v  None  v  set abs_ast_variable_section"
  using wf_goal
  by (auto simp add: abs_ast_goal_def dest!: map_of_SomeD)

lemma in_abs_rangeI: "wf_partial_state s  (v, a)  set s  (a  set (the (abs_range_map v)))"
  by (auto simp add: abs_range_map_Some wf_partial_state_def numVals_def split: prod.splits)

lemma valid_abs_goal_2:
  "abs_ast_goal v  None  (the (abs_ast_goal v))  set (the (abs_range_map v))"
  using wf_goal 
  by (auto simp add: map_of_SomeD weak_map_of_SomeI abs_ast_goal_def intro!: in_abs_rangeI)

end

context ast_problem
begin

definition abs_ast_operator
  :: "ast_operator  nat_sas_plus_operator"
  where "abs_ast_operator  λ(name, preconditions, effects, cost). 
        precondition_of = preconditions, 
         effect_of = [(v, x). (_, v, _, x)  effects] "

end

context abs_ast_prob
begin

lemma abs_rangeI: "wf_partial_state s  (v, a)  set s  (abs_range_map v  None)"
  by (auto simp add: wf_partial_state_def abs_range_map_def abs_ast_variable_section_def list.pred_set
                     numVars_def
           split: prod.splits)

lemma abs_valid_operator_1[intro!]:
  "wf_operator op  list_all (λ(v, a). ListMem v abs_ast_variable_section)
   (precondition_of (abs_ast_operator op))"
  by (cases op; auto simp add: abs_ast_operator_def wf_operator_def list.pred_set ListMem_iff)

lemma wf_operator_preD: "wf_operator (name, pres, effs, cost)  wf_partial_state pres"
  by (simp add: wf_operator_def)

lemma abs_valid_operator_2[intro!]:
  "wf_operator op  
    list_all (λ(v, a). (y. abs_range_map v = Some y)  ListMem a (the (abs_range_map v)))
             (precondition_of (abs_ast_operator op))"
  by(cases op, 
     auto dest!: wf_operator_preD simp: list.pred_set ListMem_iff abs_ast_operator_def
          intro!: abs_rangeI[simplified not_None_eq] in_abs_rangeI)

lemma wf_operator_effE: "wf_operator (name, pres, effs, cost) 
          (distinct (map (λ(_, v, _, _). v) effs);
            epres x vp v. (epres,x,vp,v)set effs  wf_partial_state epres; 
            epres x vp v.(epres,x,vp,v)set effs  x < numVars;
            epres x vp v. (epres,x,vp,v)set effs  v < numVals x;
            epres x vp v. (epres,x,vp,v)set effs  
                    case vp of None  True | Some v  v<numVals x
              P)
            P"
  unfolding wf_operator_def
  by (auto split: prod.splits)
  
lemma abs_valid_operator_3':
  "wf_operator (name, pre, eff, cost) 
     list_all (λ(v, a). ListMem v abs_ast_variable_section) (map (λ(_, v, _, a). (v, a)) eff)"
  by (fastforce simp add: list.pred_set ListMem_iff abs_ast_variable_section_def image_def numVars_def
                elim!: wf_operator_effE split: prod.splits)

lemma abs_valid_operator_3[intro!]:
  "wf_operator op 
     list_all (λ(v, a). ListMem v abs_ast_variable_section) (effect_of (abs_ast_operator op))"
  by (cases op, simp add: abs_ast_operator_def abs_valid_operator_3')

lemma wf_abs_eff: "wf_operator (name, pre, eff, cost)  wf_partial_state (map (λ(_, v, _, a). (v, a)) eff)"
  by (elim wf_operator_effE, induction eff)
     (fastforce simp: wf_partial_state_def image_def o_def split: prod.split_asm)+
  
lemma abs_valid_operator_4':
  "wf_operator (name, pre, eff, cost) 
     list_all (λ(v, a). (abs_range_map v  None)  ListMem a (the (abs_range_map v))) (map (λ(_, v, _, a). (v, a)) eff)"
  apply(subst list.pred_set ListMem_iff)+
  apply(drule wf_abs_eff)
  by (metis (mono_tags, lifting) abs_rangeI case_prodI2 in_abs_rangeI)

lemma abs_valid_operator_4[intro!]:
  "wf_operator op 
     list_all (λ(v, a). (y. abs_range_map v = Some y)  ListMem a (the (abs_range_map v)))
              (effect_of (abs_ast_operator op))"
  using abs_valid_operator_4'
  by (cases op, simp add: abs_ast_operator_def)

lemma consistent_list_set: "wf_partial_state s 
   list_all (λ(v, a). list_all (λ(v', a'). v  v'  a = a') s) s"
  by (auto simp add: list.pred_set wf_partial_state_def eq_key_imp_eq_value split: prod.splits)

lemma abs_valid_operator_5':
  "wf_operator (name, pre, eff, cost) 
     list_all (λ(v, a). list_all (λ(v', a'). v  v'  a = a') pre) pre"
  apply(drule wf_operator_preD)
  by (intro consistent_list_set)

lemma abs_valid_operator_5[intro!]:
  "wf_operator op 
     list_all (λ(v, a). list_all (λ(v', a'). v  v'  a = a') (precondition_of (abs_ast_operator op)))
              (precondition_of (abs_ast_operator op))"
  using abs_valid_operator_5'
  by (cases op, simp add: abs_ast_operator_def)

lemma consistent_list_set_2: "distinct (map fst s) 
   list_all (λ(v, a). list_all (λ(v', a'). v  v'  a = a') s) s"
  by (auto simp add: list.pred_set wf_partial_state_def eq_key_imp_eq_value split: prod.splits)

lemma abs_valid_operator_6':
  assumes "wf_operator (name, pre, eff, cost)"
  shows "list_all (λ(v, a). list_all (λ(v', a'). v  v'  a = a') (map (λ(_, v, _, a). (v, a)) eff))
              (map (λ(_, v, _, a). (v, a)) eff)"
proof-
  have *: "map fst (map (λ(_, v, _, a). (v, a)) eff) = (map (λ(_, v,_,_). v) eff)"
    by (induction eff) auto
  show ?thesis
    using assms
    apply(elim wf_operator_effE)
    apply(intro consistent_list_set_2)
    by (subst *)
qed

lemma abs_valid_operator_6[intro!]:
  "wf_operator op  
     list_all (λ(v, a). list_all (λ(v', a'). v  v'  a = a') (effect_of (abs_ast_operator op)))
              (effect_of (abs_ast_operator op))"
  using abs_valid_operator_6'
  by (cases op, simp add: abs_ast_operator_def)

end

context ast_problem
begin

definition abs_ast_operator_section
  :: "nat_sas_plus_operator list" 
  where "abs_ast_operator_section  [abs_ast_operator op. op  astδ]" 

definition abs_prob :: "nat_sas_plus_problem"
  where "abs_prob =  
    variables_of = abs_ast_variable_section,
    operators_of = abs_ast_operator_section,
    initial_of = abs_ast_initial_state,
    goal_of = abs_ast_goal,
    range_of = abs_range_map
  " 

end

context abs_ast_prob
begin

lemma [simp]: "op  set astδ  (is_valid_operator_sas_plus abs_prob) (abs_ast_operator op)"
  apply(cases op)
  apply(subst is_valid_operator_sas_plus_def Let_def)+
  using wf_operators(2)
  by(fastforce simp add: abs_prob_def)+

lemma abs_ast_operator_section_valid: 
   "list_all (is_valid_operator_sas_plus abs_prob) abs_ast_operator_section"
  by (auto simp: abs_ast_operator_section_def list.pred_set)

lemma abs_prob_valid: "is_valid_problem_sas_plus abs_prob"
  using valid_abs_goal_1 valid_abs_goal_2 valid_abs_init_1 is_valid_vars_2
        abs_ast_operator_section_valid[unfolded abs_prob_def]
  by (auto simp add: is_valid_problem_sas_plus_def Let_def ListMem_iff abs_prob_def)

definition abs_ast_plan 
  :: " SASP_Semantics.plan  nat_sas_plus_plan"
  where "abs_ast_plan πs 
     map (abs_ast_operator o the o lookup_operator) πs" 

lemma std_then_implici_effs[simp]: "is_standard_operator' (name, pres, effs, layer)  implicit_pres effs = []"
  apply(induction effs)
  by (auto simp add: is_standard_operator'_def implicit_pres_def is_standard_effect'_def)

lemma [simp]: "enabled π s  lookup_operator π = Some (name, pres, effs, layer) 
       is_standard_operator' (name, pres, effs, layer) 
       (filter (eff_enabled s) effs) = effs"
  by(auto simp add: enabled_def is_standard_operator'_def eff_enabled_def is_standard_effect'_def filter_id_conv list.pred_set)
  
lemma effs_eq_abs_effs: "(effect_of (abs_ast_operator (name, pres, effs, layer))) = 
                           (map (λ(_,x,_,v). (x,v)) effs)"
  by (auto simp add: abs_ast_operator_def
           split: option.splits prod.splits)

lemma exect_eq_abs_execute:
      "enabled π s; lookup_operator π = Some (name, preconds, effs, layer);
        is_standard_operator'(name, preconds, effs, layer) 
       execute π s = (execute_operator_sas_plus s ((abs_ast_operator o the o lookup_operator) π))"
  using effs_eq_abs_effs
  by (auto simp add: execute_def execute_operator_sas_plus_def)

lemma enabled_then_sas_applicable:
  "enabled π s  SAS_Plus_Representation.is_operator_applicable_in s ((abs_ast_operator o the o lookup_operator) π)"
  by (auto simp add: subsuming_states_def enabled_def lookup_operator_def
                     SAS_Plus_Representation.is_operator_applicable_in_def abs_ast_operator_def                     
           split: option.splits prod.splits)

lemma path_to_then_exec_serial: "πset πs. lookup_operator π  None 
        path_to s πs s' 
        s' m execute_serial_plan_sas_plus s (abs_ast_plan πs)"
proof(induction πs arbitrary: s s')
  case (Cons a πs)
  then show ?case
    by (force simp: exect_eq_abs_execute abs_ast_plan_def lookup_Some_inδ no_cond_effs
              dest: enabled_then_sas_applicable)
qed (auto simp: execute_serial_plan_sas_plus_def abs_ast_plan_def)

lemma map_of_eq_None_iff:
  "(None = map_of xys x) = (x  fst ` (set xys))"
by (induct xys) simp_all

lemma [simp]: "I = abs_ast_initial_state"
  apply(intro HOL.ext)
  by (auto simp: map_of_eq_None_iff set_map[symmetric] I_def abs_ast_initial_state_def map_of_zip_Some
           dest: map_of_SomeD)

lemma [simp]: "π  set πs. lookup_operator π  None 
          opset (abs_ast_plan πs)  op  set abs_ast_operator_section"
  by (induction πs) (auto simp: abs_ast_plan_def abs_ast_operator_section_def lookup_Some_inδ)

end

context ast_problem
begin

lemma path_to_then_lookup_Some: "(s'G. path_to s πs s')  (π  set πs. lookup_operator π  None)"
  by (induction πs arbitrary: s) (force simp add: enabled_def split: option.splits)+

lemma valid_plan_then_lookup_Some: "valid_plan πs  (π  set πs. lookup_operator π  None)"
  using path_to_then_lookup_Some
  by(simp add: valid_plan_def)

end

context abs_ast_prob
begin

theorem valid_plan_then_is_serial_sol:
  assumes "valid_plan πs"
  shows "is_serial_solution_for_problem abs_prob (abs_ast_plan πs)"
  using valid_plan_then_lookup_Some[OF assms] assms
  by (auto simp add: is_serial_solution_for_problem_def valid_plan_def initial_of_def
                       abs_prob_def abs_ast_goal_def G_def subsuming_states_def list_all_iff
                       ListMem_iff map_le_trans path_to_then_exec_serial
           simp del: sas_plus_problem.select_defs)

end

subsection ‹Translating SAS+ represnetation to Fast-Downward's›

context ast_problem
begin

definition lookup_action:: "nat_sas_plus_operator  ast_operator option" where
 "lookup_action op 
    find (λ(_, pres, effs, _). precondition_of op = pres 
                               map (λ(v,a). ([], v, None, a)) (effect_of op) = effs)
         astδ"

end

context abs_ast_prob
begin

lemma find_Some: "find P xs = Some x  x  set xs  P x"
  by (auto simp add: find_Some_iff)

lemma distinct_find: "distinct (map f xs)  x  set xs  find (λx'. f x' = f x) xs = Some x"
  by (induction xs) (auto simp: image_def)

lemma lookup_operator_find: "lookup_operator nme = find (λop. fst op = nme) astδ"
  by (auto simp: lookup_operator_def intro!: arg_cong[where f = "(λx. find x astδ)"])

lemma lookup_operator_works_1: "lookup_action op = Some π'  lookup_operator (fst π') = Some π'"
  by (auto simp: wf_operators(1) lookup_operator_find lookup_action_def dest: find_Some intro: distinct_find)

lemma lookup_operator_works_2: 
  "lookup_action (abs_ast_operator (name, pres, effs, layer)) = Some (name', pres', effs', layer')
    pres = pres'"
  by (auto simp: lookup_action_def abs_ast_operator_def dest!: find_Some)

lemma [simp]: "is_standard_operator' (name, pres, effs, layer) 
       map (λ(v,a). ([], v, None, a)) (effect_of (abs_ast_operator (name, pres, effs, layer))) = effs"
  by (induction effs) (auto simp: is_standard_operator'_def  abs_ast_operator_def is_standard_effect'_def)

lemma lookup_operator_works_3:
  "is_standard_operator' (name, pres, effs, layer)  (name, pres, effs, layer)  set astδ 
   lookup_action (abs_ast_operator (name, pres, effs, layer)) = Some (name', pres', effs', layer')
    effs = effs'"
  by(auto simp: is_standard_operator'_def lookup_action_def dest!: find_Some)

lemma mem_find_Some: "x  set xs  P x  x'. find P xs = Some x'"
  by (induction xs) auto

lemma [simp]: "precondition_of (abs_ast_operator (x1, a, aa, b)) = a"
  by(simp add: abs_ast_operator_def)

lemma std_lookup_action: "is_standard_operator' ast_op  ast_op  set astδ  
                          ast_op'. lookup_action (abs_ast_operator ast_op) = Some ast_op'"
  unfolding lookup_action_def
  apply(intro mem_find_Some)
  by (auto split: prod.splits simp: o_def)

lemma is_applicable_then_enabled_1:
      "ast_op  set astδ 
       ast_op'. lookup_operator ((fst o the o lookup_action o abs_ast_operator) ast_op) = Some ast_op'"
  using lookup_operator_works_1 std_lookup_action no_cond_effs
  by auto

lemma lookup_action_Some_in_δ: "lookup_action op = Some ast_op  ast_op  set astδ"
  using lookup_operator_works_1 lookup_Some_inδ by fastforce

lemma lookup_operator_eq_name: "lookup_operator name = Some (name', pres, effs, layer)  name = name'"
  using lookup_operator_wf(2)
  by fastforce

lemma eq_name_eq_pres: "(name, pres, effs, layer)  set astδ  (name, pres', effs', layer')  set astδ
   pres = pres'"
  using  eq_key_imp_eq_value[OF wf_operators(1)]
  by auto

lemma eq_name_eq_effs: 
  "name = name'  (name, pres, effs, layer)  set astδ  (name', pres', effs', layer')  set astδ
   effs = effs'"
  using eq_key_imp_eq_value[OF wf_operators(1)]
  by auto

lemma is_applicable_then_subsumes:
      "s  valid_states  
       SAS_Plus_Representation.is_operator_applicable_in s (abs_ast_operator (name, pres, effs, layer)) 
       s  subsuming_states (map_of pres)"
  by (simp add: subsuming_states_def SAS_Plus_Representation.is_operator_applicable_in_def
                  abs_ast_operator_def)

lemma eq_name_eq_pres':
  "s  valid_states ; is_standard_operator' (name, pres, effs, layer); (name, pres, effs, layer)  set astδ ;
    lookup_operator ((fst o the o lookup_action o abs_ast_operator) (name, pres, effs, layer)) = Some (name', pres', effs', layer')
     pres = pres'"
  using lookup_operator_eq_name lookup_operator_works_2      
  by (fastforce dest!: std_lookup_action
                simp: eq_name_eq_pres[OF lookup_action_Some_in_δ lookup_Some_inδ])

lemma is_applicable_then_enabled_2:
  "s  valid_states ; ast_op  set astδ ;
    SAS_Plus_Representation.is_operator_applicable_in s (abs_ast_operator ast_op);
    lookup_operator ((fst o the o lookup_action o abs_ast_operator) ast_op) = Some (name, pres, effs, layer)
     ssubsuming_states (map_of pres)"
  apply(cases ast_op)
  using eq_name_eq_pres' is_applicable_then_subsumes no_cond_effs
  by fastforce
  
lemma is_applicable_then_enabled_3:
  "s  valid_states;
    lookup_operator ((fst o the o lookup_action o abs_ast_operator) ast_op) = Some (name, pres, effs, layer)
    ssubsuming_states (map_of (implicit_pres effs))"
  apply(cases ast_op)
  using no_cond_effs
  by (auto dest!: std_then_implici_effs std_lookup_action lookup_Some_inδ
           simp: subsuming_states_def)

lemma is_applicable_then_enabled:
 "s  valid_states; ast_op  set astδ;
   SAS_Plus_Representation.is_operator_applicable_in s (abs_ast_operator ast_op)
    enabled ((fst o the o lookup_action o abs_ast_operator) ast_op) s"
  using is_applicable_then_enabled_1 is_applicable_then_enabled_2 is_applicable_then_enabled_3
  by(simp add: enabled_def split: option.splits)

lemma eq_name_eq_effs':
  assumes "lookup_operator ((fst o the o lookup_action o abs_ast_operator) (name, pres, effs, layer)) =
             Some (name', pres', effs', layer')"
          "is_standard_operator' (name, pres, effs, layer)" "(name, pres, effs, layer)  set astδ"
          "s  valid_states"
  shows "effs = effs'"
  using std_lookup_action[OF assms(2,3)] assms
  by (auto simp: lookup_operator_works_3[OF assms(2,3)] 
                 eq_name_eq_effs[OF lookup_operator_eq_name lookup_action_Some_in_δ lookup_Some_inδ])

lemma std_eff_enabled'[simp]:
  "is_standard_operator' (name, pres, effs, layer)  s  valid_states  (filter (eff_enabled s) effs) = effs"
  by (induction effs) (auto simp: is_standard_operator'_def is_standard_effect'_def eff_enabled_def subsuming_states_def)

lemma execute_abs:
  "s  valid_states; ast_op  set astδ;
    SAS_Plus_Representation.is_operator_applicable_in s (abs_ast_operator ast_op) 
    execute ((fst o the o lookup_action o abs_ast_operator) ast_op) s =
      execute_operator_sas_plus s (abs_ast_operator ast_op)"
  using no_cond_effs
  by(cases ast_op)
    (fastforce simp add: execute_def execute_operator_sas_plus_def effs_eq_abs_effs
               dest: is_applicable_then_enabled_1 eq_name_eq_effs'[unfolded o_def]
               split: option.splits)+

fun sat_preconds_as where
  "sat_preconds_as s [] = True"
| "sat_preconds_as s (op#ops) = 
     (SAS_Plus_Representation.is_operator_applicable_in s op 
      sat_preconds_as (execute_operator_sas_plus s op) ops)"

lemma exec_serial_then_path_to':
  "s  valid_states;
   opset ops. ast_op set astδ. op = abs_ast_operator ast_op;
   (sat_preconds_as s ops) 
   path_to s (map (fst o the o lookup_action) ops) (execute_serial_plan_sas_plus s ops)"
proof(induction ops arbitrary: s)
  case (Cons a ops)
  then show ?case
    using execute_abs is_applicable_then_enabled execute_preserves_valid
    apply simp
    by metis
qed auto

end

fun rem_condless_ops where
  "rem_condless_ops s [] = []"
| "rem_condless_ops s (op#ops) = 
     (if SAS_Plus_Representation.is_operator_applicable_in s op then
      op # (rem_condless_ops (execute_operator_sas_plus s op) ops)
      else [])"

context abs_ast_prob
begin

lemma exec_rem_consdless: "execute_serial_plan_sas_plus s (rem_condless_ops s ops) = execute_serial_plan_sas_plus s ops"
  by (induction ops arbitrary: s) auto

lemma rem_conless_sat: "sat_preconds_as s (rem_condless_ops s ops)"
  by (induction ops arbitrary: s) auto

lemma set_rem_condlessD: "x  set (rem_condless_ops s ops)  x  set ops"
  by (induction ops arbitrary: s) auto

lemma exec_serial_then_path_to:
  "s  valid_states;
   opset ops. ast_op set astδ. op = abs_ast_operator ast_op 
   path_to s (((map (fst o the o lookup_action)) o rem_condless_ops s) ops)
             (execute_serial_plan_sas_plus s ops)"
  using  rem_conless_sat
  by (fastforce dest!: set_rem_condlessD
                intro!: exec_serial_then_path_to'
                          [where s = s and ops = "rem_condless_ops s ops",
                           unfolded exec_rem_consdless])

lemma is_serial_solution_then_abstracted:
  "is_serial_solution_for_problem abs_prob ops
    opset ops. ast_op set astδ. op = abs_ast_operator ast_op"
  by(auto simp: is_serial_solution_for_problem_def abs_prob_def Let_def list.pred_set
                    ListMem_iff abs_ast_operator_section_def
          split: if_splits)

lemma lookup_operator_works_1': "lookup_action op = Some π'  op. lookup_operator (fst π') = op"
  using lookup_operator_works_1 by auto

lemma is_serial_sol_then_valid_plan_1:
 "is_serial_solution_for_problem abs_prob ops;
   π  set ((map (fst o the o lookup_action) o rem_condless_ops I) ops) 
  lookup_operator π  None"
  using std_lookup_action lookup_operator_works_1 no_cond_effs
  by (fastforce dest!: set_rem_condlessD is_serial_solution_then_abstracted
                simp: valid_plan_def list.pred_set ListMem_iff)

lemma is_serial_sol_then_valid_plan_2:
 "is_serial_solution_for_problem abs_prob ops 
   (s'G. path_to I ((map (fst o the o lookup_action) o rem_condless_ops I) ops) s')"
  using I_valid
  by (fastforce intro: path_to_pres_valid exec_serial_then_path_to
                intro!: bexI[where x = "execute_serial_plan_sas_plus I ops"]
                dest: is_serial_solution_then_abstracted
                simp: list.pred_set ListMem_iff abs_ast_operator_section_def
                      G_def subsuming_states_def is_serial_solution_for_problem_def
                      abs_prob_def abs_ast_goal_def)+

end

context ast_problem
begin

definition "decode_abs_plan  (map (fst o the o lookup_action) o rem_condless_ops I)"

end

context abs_ast_prob
begin

theorem is_serial_sol_then_valid_plan:
  "is_serial_solution_for_problem abs_prob ops 
   valid_plan (decode_abs_plan ops)"
  using is_serial_sol_then_valid_plan_1 is_serial_sol_then_valid_plan_2
  by(simp add: valid_plan_def decode_abs_plan_def)

end

end