signature AKRA_BAZZI = sig val master_theorem_tac : stringoption -> bool -> thm option -> term option -> term option ->
term option -> Proof.context -> int -> tactic
val master_theorem_function_tac : bool -> Proof.context -> int -> tactic val akra_bazzi_term_tac : Proof.context -> int -> tactic val akra_bazzi_sum_tac : Proof.context -> int -> tactic
val akra_bazzi_relation_tac : Proof.context -> int -> tactic val akra_bazzi_measure_tac : Proof.context -> int -> tactic val akra_bazzi_termination_tac : Proof.context -> int -> tactic
val setup_master_theorem : Context.generic * Token.T list ->
(Proof.context -> Method.method) * (Context.generic * Token.T list) end
structure Akra_Bazzi: AKRA_BAZZI = struct
fun changed_conv conv ct = let val thm = conv ct in if Thm.is_reflexive thm thenraise CTERM ("changed_conv", [ct]) else thm end
val allowed_consts =
[@{term "(+) :: nat => nat => nat"}, @{term "Suc :: nat => nat"},
@{term "(+) :: real => real => real"}, @{term "(-) :: real => real => real"},
@{term "uminus :: real => real"}, @{term "(*) :: real => real => real"},
@{term "(/) :: real => real => real"}, @{term "inverse :: real => real"},
@{term "(powr) :: real => real => real"}, @{term "real_of_nat"}, @{term "real :: nat => real"},
@{term "Num.numeral :: num => real"}, @{term "Num.Bit0"}, @{term "Num.Bit1"},
@{term "0 :: real"}, @{term "1 :: real"}, @{term "Num.One"}, @{term "Num.BitM"},
@{term "Num.numeral :: num => nat"}, @{term "0 :: nat"}, @{term "1 :: nat"},
@{term "atLeastAtMost :: real => real => real set"},
@{term "greaterThanAtMost :: real => real => real set"},
@{term "atLeastLessThan :: real => real => real set"},
@{term "greaterThanLessThan :: real => real => real set"},
@{term "(=) :: real => real => bool"}, @{term "(\<le>) :: real => real => bool"},
@{term "(<) :: real => real => bool"}, @{term "(\<in>) :: real => real set => bool"},
@{term "(=) :: nat => nat => bool"}, @{term "(\<le>) :: nat => nat => bool"},
@{term "(<) :: nat => nat => bool"}, @{term "Trueprop"},
@{term "HOL.Not"}, @{term "HOL.conj"}, @{term "HOL.disj"}, @{term "HOL.implies"}]
fun contains_only_numerals t = let fun go (s $ t) = letval _ = go s in go t end
| go (Const c) = if member (op =) allowed_consts (Const c) then
() else raise TERM ("contains_only_numerals", [Const c])
| go t = raise TERM ("contains_only_numerals", [t]) val _ = go t in true end handle TERM _ => false
fun dest_nat_number t = case HOLogic.dest_number t of
(ty, n) => if ty = @{typ "Nat.nat"} then n elseraise TERM ("dest_nat_number", [t])
fun num_to_Suc n = let fun go acc 0 = acc
| go acc n = go (@{term "Suc :: Nat.nat => Nat.nat"} $ acc) (n - 1) in
go @{term "0 :: Nat.nat"} n end
(* Termination *)
datatype breadcrumb = Fst | Snd
(* extract all natural number types from a product type *) fun pick_natT_leaf T = let fun go crumbs T acc = casetry HOLogic.dest_prodT T of
SOME (T1, T2) => acc |> go (Snd :: crumbs) T2 |> go (Fst :: crumbs) T1
| NONE => if T = HOLogic.natT then crumbs :: acc else acc in
go [] T [] end
fun breadcrumb Fst = fst
| breadcrumb Snd = snd
(* construct an extractor function using fst/snd from a path in a product type *) fun mk_extractor T crumbs = let fun aux c f (T', acc) = let val T'' = HOLogic.dest_prodT T' |> f in
(T'', Const (c, T' --> T'') $ acc) end fun go [] (_, acc) = Abs ("n", T, acc)
| go (Fst :: crumbs) acc = acc |> aux @{const_name fst} fst |> go crumbs
| go (Snd :: crumbs) acc = acc |> aux @{const_name snd} snd |> go crumbs in
go (rev crumbs) (T, Bound 0) end
fun extract_from_prod [] t = t
| extract_from_prod (c :: cs) t = extract_from_prod cs t |> HOLogic.dest_prod |> breadcrumb c
fun ANY' tacs n = fold (curry op APPEND) (map (fn t => t n) tacs) no_tac
(* find recursion variable, set up a measure function for it and apply it to prove termination *) fun akra_bazzi_relation_tac ctxt = case Proof_Context.lookup_fact ctxt "local.termination"of
SOME {thms = [thm], ...} => let val prems = Thm.prems_of thm val relT = prems |> hd |> dest_comb |> snd |> dest_comb |> snd |> dest_Var |> snd val T = relT |> HOLogic.dest_setT |> HOLogic.dest_prodT |> fst val prems = prems |> tl |> map Term.strip_all_body val calls = prems |> map (Logic.strip_imp_concl #> HOLogic.dest_Trueprop #>
dest_comb #> fst #> dest_comb #> snd #> HOLogic.dest_prod #> fst) val leaves = pick_natT_leaf T val calls = map (fn path => (path, map (extract_from_prod path) calls)) leaves val calls = calls |> filter (snd #> filter is_Bound #> null) |> map fst val measureC = Const (@{const_name Wellfounded.measure}, (T --> HOLogic.natT) --> relT) fun mk_relation f = measureC $ f val relations = map (mk_relation o mk_extractor T) calls in
(ANY' (map (Function_Relation.relation_tac ctxt o K) relations) THEN' resolve_tac ctxt @{thms wf_measure})
THEN_ALL_NEW (rewrite_tac ctxt @{thms measure_prod_conv measure_prod_conv'}) end
| _ => K no_tac
val measure_postproc_simps =
@{thms arith_simps of_nat_0 of_nat_1 of_nat_numeral
Suc_numeral of_nat_Suc}
(* prove that the measure decreases in a recursive call by showing that the recursive call is
an Akra-Bazzi term. Apply simple post-processing to make the numbers less ugly *) fun akra_bazzi_measure_tac ctxt =
rewrite_tac ctxt term_preps THEN' eresolve_tac ctxt @{thms akra_bazzi_term_measure} THEN' resolve_tac ctxt (get_term_intros ctxt)
THEN_ALL_NEW rewrite_tac ctxt measure_postproc_simps
(* prove termination of an Akra-Bazzi function *) fun akra_bazzi_termination_tac ctxt =
akra_bazzi_relation_tac ctxt
THEN_ALL_NEW akra_bazzi_measure_tac ctxt
(* General preprocessing *)
fun nat_numeral_conv ctxt ct = let val t = Thm.term_of ct val prop = Logic.mk_equals (t, num_to_Suc (dest_nat_number t)) fun tac goal_ctxt =
SOLVE (Local_Defs.unfold_tac goal_ctxt @{thms eval_nat_numeral BitM.simps One_nat_def} THEN resolve_tac goal_ctxt @{thms Pure.reflexive} 1); in case \<^try>\<open>Goal.prove ctxt [] [] prop (tac o #context)\<close> of
NONE => raise CTERM ("nat_numeral_conv", [ct])
| SOME thm => thm end
fun akra_bazzi_sum_tac ctxt i = let fun conv ctxt = Conv.try_conv (akra_bazzi_sum_conv ctxt) val thms = @{thms eval_akra_bazzi_sum eval_akra_bazzi_sum'
mult_1_left mult_1_right add_0_left add_0_right} in
CHANGED (CONVERSION (Conv.top_conv conv ctxt) i THEN
REPEAT (EqSubst.eqsubst_tac ctxt [0] thms i)) end
fun akra_bazzi_terms_tac ctxt =
REPEAT_ALL_NEW (fn i => DETERM (resolve_tac ctxt @{thms akra_bazzi_termsI'} i))
THEN_ALL_NEW akra_bazzi_term_tac ctxt
fun analyze_terminal ctxt f x inv t_orig t acc = let val thy = Proof_Context.theory_of ctxt val patvar = ("x",1) val pat = Term.betapply (f, Var (patvar, HOLogic.natT)) funmatch t = let val matcher = Pattern.first_order_match thy (pat, t) (Vartab.empty, Vartab.empty) val x' = Vartab.lookup (snd matcher) patvar |> the |> snd in if Term.exists_subterm (fn x' => x = x') x' then x'elseraise Pattern.MATCH end fun add_term t (acc, acc_inv, call) = if inv then (acc, t :: acc_inv, call) else (t :: acc, acc_inv, call) fun go t (acc, acc_inv, call) = let val call' = match t in case call of
NONE => (acc, acc_inv, SOME call')
| SOME _ => raise TERM ("Non-linear occurrence of recursive call", [t_orig]) end handle Pattern.MATCH => add_term t (acc, acc_inv, call) in
go t acc end;
fun mk_prod [] = @{term "1 :: real"}
| mk_prod (x::xs) =
fold (fn x => fn acc => @{term "(*) :: real => real => real"} $ x $ acc) xs x
fun mk_quot (xs, []) = mk_prod xs
| mk_quot ([], ys) = @{term "inverse :: real => real"} $ mk_prod ys
| mk_quot (xs, ys) = @{term "(/) :: real => real => real"} $ mk_prod xs $ mk_prod ys
fun analyze_prod ctxt f x minus t (acc1, acc2) = let fun go inv (oper $ t1 $ t2) acc = if oper = @{term "(*) :: real => real => real"} then
go inv t1 (go inv t2 acc) elseif oper = @{term "(/) :: real => real => real"} then
go inv t1 (go (not inv) t2 acc) else
analyze_terminal ctxt f x inv t (oper $ t1 $ t2) acc
| go inv (oper $ t') acc = if oper = @{term "inverse :: real => real"} then
go (not inv) t' acc else
analyze_terminal ctxt f x inv t (oper $ t') acc
| go inv t' acc = analyze_terminal ctxt f x inv t t' acc in case go false t ([], [], NONE) of
(_, _, NONE) => (acc1, (if minus then @{term "uminus :: real => real"} $ t else t) :: acc2)
| (xs, ys, SOME call) => let val r = mk_quot (xs, ys) val r' = (if minus then @{term "uminus :: real => real"} $ r else r, call) in
(r' :: acc1, acc2) end end
fun mk_sum [] = @{term "0 :: real"}
| mk_sum (x::xs) = let fun go (f $ x) acc = if f = @{term "uminus :: real => real"} then
@{term "(-) :: real => real => real"} $ acc $ x else
@{term "(+) :: real => real => real"} $ acc $ (f $ x)
| go x acc = @{term "(+) :: real => real => real"} $ acc $ x in
fold go xs x end
fun analyze_sum ctxt f x t = let fun go minus (oper $ t1 $ t2) acc = if oper = @{term "(+) :: real => real => real"} then
go minus t1 (go minus t2 acc) elseif oper = @{term "(-) :: real => real => real"} then
go minus t1 (go (not minus) t2 acc) else analyze_prod ctxt f x minus (oper $ t1 $ t2) acc
| go minus (oper $ t) acc = if oper = @{term "uminus :: real => real"} then
go (not minus) t acc else
analyze_prod ctxt f x minus (oper $ t) acc
| go minus t acc = analyze_prod ctxt f x minus t acc in case go false t ([],[]) of
(xs,ys) => (xs, mk_sum ys) end
fun extract_coeff ctxt t = let val thy = Proof_Context.theory_of ctxt val t' = Simplifier.rewrite_term thy
@{thms akra_bazzi_termination_simps[THEN eq_reflection]} [] t fun aux thm = let val (tmp, pat) = thm |> Thm.concl_of |> HOLogic.dest_Trueprop |> Term.dest_comb val var = tmp |> Term.dest_comb |> snd |> Term.dest_Var |> fst val (_, insts) = Pattern.first_order_match thy (pat, t') (Vartab.empty, Vartab.empty) val coeff = Option.map snd (Vartab.lookup insts var) in
coeff end handle Pattern.MATCH => NONE in case get_first aux (get_term_intros ctxt) of
NONE => raise TERM ("extract_coeff", [t'])
| SOME coeff => coeff end
fun get_var_name (Free (x, _)) = x
| get_var_name (Var ((x,_),_)) = x
| get_var_name t = raise TERM ("get_var_name", [t])
fun analyze_call ctxt f x t = let val (ts, g) = analyze_sum ctxt f x t val k = length ts val a_coeffs = HOLogic.mk_list HOLogic.realT (map fst ts) fun abstract t = Abs (get_var_name x, HOLogic.natT, abstract_over (x, t)) val ts = map (abstract o snd) ts val b_coeffs = HOLogic.mk_list HOLogic.realT (map (extract_coeff ctxt) ts) val ts = HOLogic.mk_list (HOLogic.natT --> HOLogic.natT) ts in
{k = HOLogic.mk_nat k, a_coeffs = a_coeffs, b_coeffs = b_coeffs, ts = ts, g = abstract g} end
fun analyze_equation ctxt f eq funvar = let val rhs = eq |> Thm.concl_of |> HOLogic.dest_Trueprop |> HOLogic.dest_eq |> snd val {k, a_coeffs, b_coeffs, ts, g} = analyze_call ctxt f funvar rhs in
{k = k, a_coeffs = a_coeffs, b_coeffs = b_coeffs, ts = ts, f = f, g = g} end
fun akra_bazzi_instantiate_tac ctxt f eq x0 x1 funvar p' = let val {k, a_coeffs, b_coeffs, ts, g, ...} = analyze_equation ctxt f eq funvar val insts =
([SOME x0, SOME x1, SOME k, SOME a_coeffs, SOME b_coeffs, SOME ts, SOME f, SOME g] @
(filterOption.isSome [p'])) in
resolve_tac ctxt o instantiate_akra_bazzi ctxt insts end
funeval_real_numeral_convctxtct= caseThm.term_ofctof @{term"Pure.imp"}$_$_=>Conv.implies_concl_conv(eval_real_numeral_convctxt)ct |_=> let valt=Thm.term_ofct valprop=Logic.mk_equals(t,@{term"TruepropTrue"}) funtacgoal_ctxt= letvalctxt'=put_simpset(simpset_of@{context})goal_ctxtin Local_Defs.unfold_tacctxt'@{thmsgreaterThanLessThan_iff} THEN(SOLVE(Simplifier.full_simp_tacctxt'1)ORELSEcode_simp_tacctxt'1) end in ifcontains_only_numeralstthen case\<^try>\<open>Goal.provectxt[][]prop(taco#context)\<close>of SOMEthm=>thm |NONE=>raiseCTERM("eval_real_numeral_conv",[ct]) else raiseCTERM("eval_real_numeral_conv",[ct])
end *)
fun eval_numeral_tac ctxt =
SUBGOAL (fn (goal, i) => if contains_only_numerals (Logic.strip_imp_concl goal) then
Eval_Numeral.eval_numeral_tac ctxt i else
all_tac)
fun intros_tac ctxt thms = DETERM o TRY o REPEAT_ALL_NEW (DETERM o match_tac ctxt thms)
fun master_theorem_tac' caseno simp f eq funvar x0 x1 p' ctxt =
(
preprocess_tac ctxt THEN' akra_bazzi_instantiate_tac ctxt f eq x0 x1 funvar p' (get_intros caseno (p' <> NONE)) THEN' DETERM o master_theorem_function_tac simp ctxt)
THEN_ALL_NEW intros_tac ctxt @{thms ball_set_intros akra_bazzi_p_rel_intros}
THEN_ALL_NEW (DETERM o (
unfold_simps ctxt THEN' TRY o akra_bazzi_sum_tac ctxt THEN' unfold_simps ctxt THEN' postprocess_tac ctxt THEN' (if simp then TRY o eval_numeral_tac ctxt else K all_tac) THEN' intros_tac ctxt @{thms allI ballI conjI impI}
THEN_ALL_NEW (TRY o eresolve_tac ctxt @{thms atLeastLessThanE})))
fun find_function goal = case goal |> Logic.strip_imp_concl |> HOLogic.dest_Trueprop of
@{term "Set.member :: (nat => real) => _ set => bool"} $ f $ _ => f
| _ => raise TERM ("find_function", [goal])
fun find_x1 var eq = let fun get_x1 prem = case prem |> HOLogic.dest_Trueprop of
rel $ x1 $ x => if x = var andalso rel = leq_nat then
SOME x1 elseif x = var andalso rel = less_nat then
SOME (@{term "Suc"} $ x1) else
NONE
| _ => NONE in
get_first get_x1 (Thm.prems_of eq) end handle TERM _ => NONE
fun analyze_funeq pat var ctxt eq = let val lhs = eq |> Thm.concl_of |> HOLogic.dest_Trueprop |> HOLogic.dest_eq |> fst val matcher = Thm.first_order_match (Thm.cterm_of ctxt lhs, pat) val eq = Thm.instantiate matcher eq in case find_x1 var eq of
NONE => NONE
| SOME _ => SOME eq end handle Pattern.MATCH => NONE
fun find_funeq ctxt f = let val n = ("n", 0) val n' = Var (n, HOLogic.natT) val pat = Thm.cterm_of ctxt (Term.betapply (f, n')) fun get_function_info (Abs (_, _, body)) = get_function_info body
| get_function_info (t as f $ _) =
(Function.get_info ctxt t handle Empty => get_function_info f)
| get_function_info t = Function.get_info ctxt t val {simps = simps, ...} = get_function_info f handleList.Empty => raise TERM ("Could not obtain function info record.", [f]) val simps = case simps of
SOME simps => simps
| NONE => raise TERM ("Function has no simp rules.", [f]) in case get_first (analyze_funeq pat n' ctxt) simps of
SOME s => (s, n')
| NONE => raise TERM ("Could not determine recurrence from simp rules.", [f]) end
fun master_theorem_tac caseno simp eq x0 x1 p' ctxt =
SUBGOAL (fn (goal, i) => Subgoal.FOCUS (fn {context = ctxt, ...} => let val f = find_function goal val (eq, funvar) = case eq of
SOME eq => ( let val n = Var (("n", 0), HOLogic.natT) in case (analyze_funeq (Thm.cterm_of ctxt (f $ n)) n ctxt eq) of
SOME eq => (eq, n)
| NONE => raise TERM ("", []) end handle TERM _ => raise TERM ( "Could not determine recursion variable from recurrence theorem.",
[Thm.prop_of eq]))
| NONE => find_funeq ctxt f val x0 = Option.getOpt (x0, @{term "0::nat"}) val x1 = ifOption.isSome x1 then x1 else find_x1 funvar eq val x1 = case x1 of
SOME x1 => x1
| NONE => raise TERM ("Could not determine x1 from recurrence theorem",
[goal, Thm.prop_of eq, funvar]) val p' = if caseno = NONE orelse caseno = SOME "1" orelse caseno = SOME "2.1" then p'else NONE in
HEADGOAL (master_theorem_tac' caseno simp f eq funvar x0 x1 p' ctxt) end
) ctxt i) handle TERM _ => K no_tac
fun read_term_with_type ctxt T = Syntax.check_term ctxt o Type.constraint T o Syntax.parse_term ctxt
val parse_param = let fun term_with_type T =
Scan.peek (fn context => Parse.embedded_inner_syntax >>
read_term_with_type (Context.proof_of context) T); in
parse_assoc "recursion" Attrib.thm >> (fn x => (SOME x, NONE, NONE, NONE)) ||
parse_assoc "x0" (term_with_type HOLogic.natT) >> (fn x => (NONE, SOME x, NONE, NONE)) ||
parse_assoc "x1" (term_with_type HOLogic.natT) >> (fn x => (NONE, NONE, SOME x, NONE)) ||
parse_assoc "p'" (term_with_type HOLogic.realT) >> (fn x => (NONE, NONE, NONE, SOME x)) end
val parse_params = let fun add_option NONE y = y
| add_option x _ = x fun go (a,b,c,d) (a',b',c',d') =
(add_option a a', add_option b b', add_option c c', add_option d d') in
Scan.repeat parse_param >> (fn xs => fold go xs (NONE, NONE, NONE, NONE)) end
val setup_master_theorem =
Scan.option (Scan.lift (Parse.number || Parse.float_number))
-- Scan.lift (Args.parens (Args.$$$ "nosimp") >> K false || Scan.succeed true)
-- parse_params
>>
(fn ((caseno, simp), (thm, x0, x1, p')) => fn ctxt => let val _ = case caseno of
SOME caseno => if member (op =) ["1","2.1","2.2","2.3","3"] caseno then
() else
cat_error "illegal Master theorem case: " caseno
| NONE => () in
SIMPLE_METHOD' (master_theorem_tac caseno simp thm x0 x1 p' ctxt) end)
end;
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.3 Sekunden
(vorverarbeitet am 2026-06-10)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.