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 (* Finite subgraph *)

end (* Subsumption Graph Pre Nodes *)

locale Leadsto_Search_Space =
  A: Search_Space'_finite E a0 _ "(≼)" empty
  for E a0 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 a0 "λ _. 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" a0 "λ _. 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 a1 :: 'a
begin

interpretation B':
  Liveness_Search_Space
  "λ x y. E x y  Q y  ¬ empty y" a1 "λ _. False" "(≼)" "λ x. A.reachable x  ¬ empty x" succs_Q
  by standard

definition has_cycle where
  "has_cycle = B'.dfs"

end (* Second start state *)

definition leadsto :: "bool nres" where
  "leadsto = do {
    (r, passed)  A'.pw_algo;
    let P = {x. x  passed  P x  Q x};
    (r, _) 
              FOREACHC 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 a0 "λ 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  (xpassed. 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}"]
            )

      (* Result of first reachability computation is finite *)
      subgoal
        by (auto intro: finite_subset[OF _ A.finite_reachable])

      (* Invariant holds initially *)
      subgoal
        unfolding inv_def B.liveness_compatible_def by auto

      (* Invariant is preserved *)
      subgoal for a1 it σ a passed'
        apply clarsimp
          subgoal premises prems
          proof -
            interpret B':
              Liveness_Search_Space
              "λ x y. E x y  Q y  ¬ empty y" a1 "λ _. 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  _ a1  _ 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  _ a1  _ it  _
                  apply simp
                    apply (inst_existentials a1)
                  by (auto 4 3 simp: reaches_cycle_iff)
                  subgoal
                    using inv _ _ _ passed  _ reaches_cycle_iff unfolding inv_def by blast
                  done
                done
            qed
            done

          (* I ∧ ¬ b ⟶ post *)
          subgoal for σ a b
            unfolding inv_def by (auto dest!: aux1)

          (* I ∧ b ⟶ post *)
          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)** a0 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 (* Leadsto Search Space *)

end (* Theory *)