Theory Leadsto
section ‹Checking Leads-To Properties›
subsection ‹Abstract Implementation›
theory Leadsto
  imports Liveness_Subsumption Unified_PW
begin
context Subsumption_Graph_Pre_Nodes
begin
context
  assumes finite_V: "finite {x. V x}"
begin
lemma steps_cycle_mono:
  assumes "G'.steps (x # ws @ y # xs @ [y])" "x ≼ x'" "V x" "V x'"
  shows "∃ y' xs' ys'. y ≼ y' ∧ G'.steps (x' # xs' @ y' # ys' @ [y'])"
proof -
  let ?n  = "card {x. V x} + 1"
  let ?xs = "y # concat (replicate ?n (xs @ [y]))"
  from assms(1) have "G'.steps (x # ws @ [y])" "G'.steps (y # xs @ [y])"
    by (auto intro: Graph_Start_Defs.graphI_aggressive2)
  with G'.steps_replicate[of "y # xs @ [y]" ?n] have "G'.steps ?xs"
    by auto
  from steps_mono[OF ‹G'.steps (x # ws @ [y])› ‹x ≼ x'› ‹V x› ‹V x'›] obtain ys where
    "G'.steps (x' # ys)" "list_all2 (≼) (ws @ [y]) ys"
    by auto
  then obtain y' ws' where "G'.steps (x' # ws' @ [y'])" "y ≼ y'"
    unfolding list_all2_append1 list_all2_Cons1 by auto
  with ‹G'.steps (x # ws @ [y])› have "V y" "V y'"
    subgoal
      using G'_steps_V_last assms(3) by fastforce
    using G'_steps_V_last ‹G'.steps (x' # ws' @ [y'])› assms(4) by fastforce
  with steps_mono[OF ‹G'.steps ?xs› ‹y ≼ y'›] obtain ys where ys:
    "list_all2 (≼) (concat (replicate ?n (xs @ [y]))) ys" "G'.steps (y' # ys)"
    by auto
  let ?ys = "filter ((≼) y) ys"
  have "length ?ys ≥ ?n"
    using list_all2_replicate_elem_filter[OF ys(1), of y]
    by auto
  have "set ?ys ⊆ set ys"
    by auto
  also have "… ⊆ {x. V x}"
    using ‹G'.steps (y' # _)› ‹V y'› by (auto simp: list_all_iff dest: G'_steps_V_all)
  finally have "¬ distinct ?ys"
    using distinct_card[of ?ys] ‹_ >= ?n›
    by - (rule ccontr; drule distinct_length_le[OF finite_V]; simp)
  from not_distinct_decomp[OF this] obtain as y'' bs cs where "?ys = as @ [y''] @ bs @ [y''] @ cs"
    by auto
  then obtain as' bs' cs' where
    "ys = as' @ [y''] @ bs' @ [y''] @ cs'"
    apply atomize_elim
    apply simp
    apply (drule filter_eq_appendD filter_eq_ConsD filter_eq_appendD[OF sym], clarify)+
    apply clarsimp
    subgoal for as1 as2 bs1 bs2 cs'
      by (inst_existentials "as1 @ as2" "bs1 @ bs2") simp
    done
  from ‹G'.steps (y' # _)› have "G'.steps (y' # as' @ y'' # bs' @ [y''])"
    unfolding ‹ys = _› by (force intro: Graph_Start_Defs.graphI_aggressive2)
  moreover from ‹?ys = _› have "y ≼ y''"
  proof -
    from ‹?ys = _› have "y'' ∈ set ?ys" by auto
    then show ?thesis by auto
  qed
  ultimately show ?thesis
    using ‹G'.steps (x' # ws' @ [y'])›
    by (inst_existentials y'' "ws' @ y' # as'" bs';
        fastforce intro: Graph_Start_Defs.graphI_aggressive1
        )
qed
lemma reaches_cycle_mono:
  assumes "G'.reaches x y" "y →⇧+ y" "x ≼ x'" "V x" "V x'"
  shows "∃ y'. y ≼ y' ∧ G'.reaches x' y' ∧ y' →⇧+ y'"
proof -
  from assms obtain xs ys where *: "G'.steps (x # xs)" "y = last (x # xs)" "G'.steps (y # ys @ [y])"
    apply atomize_elim
    including reaches_steps_iff
    apply safe
    subgoal for xs xs'
      by (inst_existentials "tl xs" xs') auto
    done
  have **: "∃as. G'.steps (x # as @ last list # ys @ [last list])"
    if a1: "G'.steps (x # a # list @ ys @ [last list])" "list ≠ []"
    for a :: 'a and list :: "'a list"
  proof -
    from that have "butlast (a # list) @ [last list] = a # list"
      by (metis (no_types) append_butlast_last_id last_ConsR list.simps(3))
    then show ?thesis
      using a1 by (metis (no_types) Cons_eq_appendI append.assoc self_append_conv2)
  qed
  from * obtain ws where "G'.steps (x # ws @ y # ys @ [y])"
    apply atomize_elim
    apply (cases xs)
     apply (inst_existentials ys)
     apply simp
      apply rotate_tac
     apply (rule G'.steps_append', assumption+, simp+)
    apply safe
     apply (inst_existentials "[] :: 'a list")
     apply (solves ‹auto dest: G'.steps_append›)
    apply (drule G'.steps_append)
      apply assumption
     apply simp
    apply simp
    by (rule **)
    from steps_cycle_mono[OF this ‹x ≼ x'› ‹V x› ‹V x'›] obtain y' xs' ys' where
      "y ≼ y'"
      "G'.steps (x' # xs' @ y' # ys' @ [y'])"
      by safe
    then have "G'.steps (x' # xs' @ [y'])" "G'.steps (y' # ys' @ [y'])"
      by (force intro: Graph_Start_Defs.graphI_aggressive2)+
    with ‹y ≼ y'› show ?thesis
      including reaches_steps_iff by force
  qed
end 
end 
locale Leadsto_Search_Space =
  A: Search_Space'_finite E a⇩0 _ "(≼)" empty
  for E a⇩0 empty and subsumes :: "'a ⇒ 'a ⇒ bool" (infix ‹≼› 50)
  +
  fixes P Q :: "'a ⇒ bool"
  assumes P_mono: "a ≼ a' ⟹ ¬ empty a ⟹ P a ⟹ P a'"
  assumes Q_mono: "a ≼ a' ⟹ ¬ empty a ⟹ Q a ⟹ Q a'"
  fixes succs_Q :: "'a ⇒ 'a list"
  assumes succs_Q_correct: "A.reachable a ⟹ set (succs_Q a) = {y. E a y ∧ Q y ∧ ¬ empty y}"
begin
sublocale A': Search_Space'_finite E a⇩0 "λ _. False" "(≼)" empty
  apply standard
          apply (rule A.refl A.trans A.mono A.empty_subsumes A.empty_mono A.empty_E; assumption)+
    apply assumption
   apply blast
  apply (rule A.finite_reachable)
  done
sublocale B:
  Liveness_Search_Space
  "λ x y. E x y ∧ Q y ∧ ¬ empty y" a⇩0 "λ _. False" "(≼)" "λ x. A.reachable x ∧ ¬ empty x"
  succs_Q
  apply standard
       apply (rule A.refl A.trans; assumption)+
  subgoal for a b a'
    by safe (drule A.mono; auto intro: Q_mono dest: A.mono A.empty_mono)
    apply blast
   apply (solves ‹auto intro: A.finite_reachable›)
  subgoal
    apply (subst succs_Q_correct)
    unfolding Subgraph_Node_Defs.E'_def by auto
  done
context
  fixes a⇩1 :: 'a
begin
interpretation B':
  Liveness_Search_Space
  "λ x y. E x y ∧ Q y ∧ ¬ empty y" a⇩1 "λ _. False" "(≼)" "λ x. A.reachable x ∧ ¬ empty x" succs_Q
  by standard
definition has_cycle where
  "has_cycle = B'.dfs"
end 
definition leadsto :: "bool nres" where
  "leadsto = do {
    (r, passed) ← A'.pw_algo;
    let P = {x. x ∈ passed ∧ P x ∧ Q x};
    (r, _) ←
              FOREACH⇩C P (λ(b,_). ¬b) (λv' (_,P). has_cycle v' P) (False,{});
    RETURN r
  }"
definition
  "reaches_cycle a =
    (∃ b. (λ x y. E x y ∧ Q y ∧ ¬ empty y)⇧*⇧* a b ∧ (λ x y. E x y ∧ Q y ∧ ¬ empty y)⇧+⇧+ b b)"
definition leadsto_spec where
  "leadsto_spec = SPEC (λ r. r ⟷ (∃ a. A.reachable a ∧ ¬ empty a ∧ P a ∧ Q a ∧ reaches_cycle a))"
lemma
  "leadsto ≤ leadsto_spec"
proof -
  define inv where
    "inv ≡ λ passed it (r, passed').
       (r ⟶ (∃ a. A.reachable a ∧ ¬ empty a ∧ P a ∧ Q a ∧ reaches_cycle a))
     ∧ (¬ r ⟶
          (∀ a ∈ passed - it. ¬ reaches_cycle a)
         ∧ B.liveness_compatible passed'
         ∧ passed' ⊆ {x. A.reachable x ∧ ¬ empty x}
       )
    "
  have [simp, intro]:
    "¬ A'.F_reachable"
    unfolding A'.F_reachable_def by simp
  have B_reaches_empty:
    "¬ empty b" if "¬ empty a" "B.reaches a b" for a b
    using that(2,1)by induction auto
  interpret Subgraph_Start E a⇩0 "λ a x. E a x ∧ Q x ∧ ¬ empty x"
    by standard auto
  have B_A_reaches:
    "A.reaches a b" if "B.reaches a b" for a b
    using that by (rule reaches)
  have reaches_iff: "B.reaches a x ⟷ B.G.G'.reaches a x"
    if "A.reachable a" "¬ empty a" for a x
    unfolding reaches_cycle_def
    apply standard
    using that
      apply (rotate_tac 3)
     apply (induction rule: rtranclp.induct)
      apply blast
     apply (rule rtranclp.rtrancl_into_rtrancl)
      apply assumption
     apply (subst B.G.E'_def)
    subgoal for a b c
      by (auto dest: B_reaches_empty)
    subgoal
      by (rule B.G.reaches)
    done
  have reaches1_iff: "B.reaches1 a x ⟷ B.G.G'.reaches1 a x"
    if "A.reachable a" "¬ empty a" for a x
    unfolding reaches_cycle_def
    apply standard
    subgoal
      using that
        apply (rotate_tac 3)
      apply (induction rule: tranclp.induct)
       apply (solves ‹rule tranclp.intros(1), auto simp: B.G.E'_def›)
      apply (
          rule tranclp.intros(2);
          auto 4 3 simp: B.G.E'_def dest:B_reaches_empty tranclp_into_rtranclp
          )
      done
    subgoal
      by (rule B.G.reaches1)
    done
  have reaches_cycle_iff: "reaches_cycle a ⟷ (∃x. B.G.G'.reaches a x ∧ B.G.G'.reaches1 x x)"
    if "A.reachable a" "¬ empty a" for a
    unfolding reaches_cycle_def
    apply (subst reaches_iff[OF that])
    using reaches1_iff B.G.G'_reaches_V that by blast
  have aux1:
    "¬ reaches_cycle x"
    if
      "∀a. A.reachable a ∧ ¬ empty a ⟶ (∃x∈passed. a ≼ x)"
      "passed ⊆ {a. A.reachable a ∧ ¬ empty a}"
      "∀x ∈ passed. P x ∧ Q x ⟶ ¬ reaches_cycle x"
      "A.reachable x" "¬ empty x" "P x" "Q x"
    for x passed
  proof (rule ccontr, unfold not_not)
    assume "reaches_cycle x"
    from that obtain x' where "x' ∈ passed" "x ≼ x'"
      by auto
    with that have "P x'" "Q x'"
      by (auto intro: P_mono Q_mono)
    with ‹x' ∈ passed› that(3) have "¬ reaches_cycle x'"
      by auto
    have "A.reachable x'" "¬ empty x'"
      using ‹x' ∈ passed› that(2) A.empty_mono ‹x ≼ x'› that(5) by auto
    note reaches_cycle_iff' = reaches_cycle_iff[OF this] reaches_iff[OF this] reaches1_iff[OF this]
    from ‹reaches_cycle x› obtain y where "B.reaches x y" "B.reaches1 y y"
      unfolding reaches_cycle_def by atomize_elim
    interpret
      Subsumption_Graph_Pre_Nodes
        "(≼)" A.subsumes_strictly "λ x y. E x y ∧ Q y ∧ ¬ empty y"
        "λ x. A.reachable x ∧ ¬ empty x"
      by standard (rule B.mono[simplified]; assumption)
    from ‹B.reaches x y› ‹x ≼ x'› ‹B.reaches1 y y› reaches_cycle_mono[OF B.finite_V] obtain y' where
      "y ≼ y'" "B.G.G'.reaches x' y'" "B.G.G'.reaches1 y' y'"
      apply atomize_elim
      apply (subst (asm) reaches_iff[rotated 2])
        defer
        defer
        apply (subst (asm) reaches1_iff)
          defer
          defer
      using ‹A.reachable x› ‹¬ empty x› ‹A.reachable x'› ‹¬ empty x'› ‹B.reaches1 y y›
      by (auto simp: B.reaches1_reaches_iff2 dest!: B.G.G'_reaches_V)
    with ‹A.reachable x'› ‹¬ empty x'› have "reaches_cycle x'"
      unfolding reaches_cycle_iff'
      by auto
    with ‹¬ reaches_cycle x'› show False ..
  qed
  show ?thesis
    unfolding leadsto_def leadsto_spec_def
    apply (refine_rcg refine_vcg)
      subgoal for _ r passed
      apply (refine_vcg
            FOREACHc_rule'[where I = "inv {x ∈ passed. P x ∧ Q x}"]
            )
      
      subgoal
        by (auto intro: finite_subset[OF _ A.finite_reachable])
      
      subgoal
        unfolding inv_def B.liveness_compatible_def by auto
      
      subgoal for a⇩1 it σ a passed'
        apply clarsimp
          subgoal premises prems
          proof -
            interpret B':
              Liveness_Search_Space
              "λ x y. E x y ∧ Q y ∧ ¬ empty y" a⇩1 "λ _. False" "(≼)"
              "λ x. A.reachable x ∧ ¬ empty x" succs_Q
              by standard
            from ‹inv _ _ _› have
              "B'.liveness_compatible passed'" "passed' ⊆ {x. A.reachable x ∧ ¬ empty x}"
              unfolding inv_def by auto
            from B'.dfs_correct[OF _ this] ‹passed ⊆ _› ‹a⇩1 ∈ _› ‹it ⊆ _› have
              "B'.dfs passed' ≤ B'.dfs_spec"
              by auto
            then show ?thesis
              unfolding has_cycle_def
              apply (rule order.trans)
              unfolding B'.dfs_spec_def
              apply clarsimp
              subgoal for r passed'
                apply (cases r)
                 apply simp
                subgoal
                  unfolding inv_def
                  using ‹passed ⊆ _› ‹a⇩1 ∈ _› ‹it ⊆ _›
                  apply simp
                    apply (inst_existentials a⇩1)
                  by (auto 4 3 simp: reaches_cycle_iff)
                  subgoal
                    using ‹inv _ _ _› ‹passed ⊆ _› reaches_cycle_iff unfolding inv_def by blast
                  done
                done
            qed
            done
          
          subgoal for σ a b
            unfolding inv_def by (auto dest!: aux1)
          
          subgoal for it σ a b
            unfolding inv_def by auto
          done
        done
    qed
definition leadsto_spec_alt where
  "leadsto_spec_alt =
    SPEC (λ r.
      r ⟷
      (∃ a. (λ x y. E x y ∧ ¬ empty y)⇧*⇧* a⇩0 a ∧ ¬ empty a ∧ P a ∧ Q a ∧ reaches_cycle a)
    )"
lemma E_reaches_non_empty:
  "(λ x y. E x y ∧ ¬ empty y)⇧*⇧* a b" if "a →* b" "A.reachable a" "¬ empty b" for a b
  using that
proof induction
  case base
  then show ?case by blast
next
  case (step y z)
  from ‹a →* y› ‹A.reachable a› have "A.reachable y"
    by - (rule A.reachable_reaches)
  have "¬ empty y"
  proof (rule ccontr, unfold not_not)
    assume "empty y"
    from A.empty_E[OF ‹A.reachable y› ‹empty y› ‹E y z›] ‹¬ empty z› show False by blast
  qed
  with step show ?case
    by (auto intro: rtranclp.intros(2))
qed
lemma leadsto_spec_leadsto_spec_alt:
  "leadsto_spec ≤ leadsto_spec_alt"
  unfolding leadsto_spec_def leadsto_spec_alt_def
  by (auto
      intro: Subgraph.intro Subgraph.reaches[rotated] E_reaches_non_empty[OF _ A.start_reachable]
      simp: A.reachable_def
     )
end 
end