(* ========================================================================= *)
(* Geometric algebra. *)
(* *)
(* (c) Copyright, John Harrison 1998-2008 *)
(* ========================================================================= *)
needs "Multivariate/vectors.ml";;
needs "Library/binary.ml";;
prioritize_real();;
(* ------------------------------------------------------------------------- *)
(* Some basic lemmas, mostly set theory. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Index type for "multivectors" (k-vectors for all k <= N). *)
(* ------------------------------------------------------------------------- *)
let multivector_tybij =
new_type_definition "multivector" ("mk_multivector","dest_multivector")
multivector_tybij_th;;
let DEST_MK_MULTIVECTOR = prove
(`!s. s
SUBSET 1..dimindex(:N)
==> dest_multivector(mk_multivector s :(N)multivector) = s`,
REPEAT STRIP_TAC THEN GEN_REWRITE_TAC I [GSYM multivector_tybij] THEN
ASM_REWRITE_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* The bijections we use for indexing. *)
(* *)
(* Note that we need a *single* bijection over the entire space that also *)
(* works for the various subsets. Hence the tedious explicit construction. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Indexing directly via subsets. *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("$$",(25,"left"));;
parse_as_binder "lambdas";;
(* ------------------------------------------------------------------------- *)
(* Crucial properties. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Also componentwise operations; they all work in this style. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Basis vectors indexed by subsets of 1..N. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Linear and bilinear functions are determined by their effect on basis. *)
(* ------------------------------------------------------------------------- *)
let LINEAR_EQ_MBASIS = prove
(`!f:real^(M)multivector->real^N g b s.
linear f /\ linear g /\
(!s. s
SUBSET 1..dimindex(:M) ==> f(mbasis s) = g(mbasis s))
==> f = g`,
let BILINEAR_EQ_MBASIS = prove
(`!f:real^(M)multivector->real^(N)multivector->real^P g b s.
bilinear f /\ bilinear g /\
(!s t. s
SUBSET 1..dimindex(:M) /\ t
SUBSET 1..dimindex(:N)
==> f (mbasis s) (mbasis t) = g (mbasis s) (mbasis t))
==> f = g`,
REPEAT STRIP_TAC THEN SUBGOAL_THEN
`!x y. x
IN UNIV /\ y
IN UNIV
==> (f:real^(M)multivector->real^(N)multivector->real^P) x y = g x y`
(fun th -> MP_TAC th THEN REWRITE_TAC[
FUN_EQ_THM;
IN_UNIV]) THEN
MATCH_MP_TAC
BILINEAR_EQ THEN
EXISTS_TAC `{mbasis s :real^(M)multivector | s
SUBSET 1..dimindex(:M)}` THEN
EXISTS_TAC `{mbasis t :real^(N)multivector | t
SUBSET 1..dimindex(:N)}` THEN
ASM_REWRITE_TAC[
SPAN_MBASIS;
SUBSET_REFL;
IN_ELIM_THM] THEN
ASM_MESON_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* A way of proving linear properties by extension from basis. *)
(* ------------------------------------------------------------------------- *)
let MBASIS_EXTENSION = prove
(`!P. (!s. s
SUBSET 1..dimindex(:N) ==> P(mbasis s)) /\
(!c x. P x ==> P(c % x)) /\ (!x y. P x /\ P y ==> P(x + y))
==> !x:real^(N)multivector. P x`,
(* ------------------------------------------------------------------------- *)
(* Injection from regular vectors. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Subspace of k-vectors. *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("multivector",(12,"right"));;
(* ------------------------------------------------------------------------- *)
(* k-grade part of a multivector. *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("grade",(22,"right"));;
(* ------------------------------------------------------------------------- *)
(* General product construct. *)
(* ------------------------------------------------------------------------- *)
let Product_DEF = new_definition
`(Product mult op
:real^(N)multivector->real^(N)multivector->real^(N)multivector) x y =
vsum {s | s SUBSET 1..dimindex(:N)}
(\s. vsum {s | s SUBSET 1..dimindex(:N)}
(\t. (x$$s * y$$t * mult s t) % mbasis (op s t)))`;;
(* ------------------------------------------------------------------------- *)
(* This is always bilinear. *)
(* ------------------------------------------------------------------------- *)
let PRODUCT_LADD = (MATCH_MP BILINEAR_LADD o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_RADD = (MATCH_MP BILINEAR_RADD o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_LMUL = (MATCH_MP BILINEAR_LMUL o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_RMUL = (MATCH_MP BILINEAR_RMUL o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_LNEG = (MATCH_MP BILINEAR_LNEG o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_RNEG = (MATCH_MP BILINEAR_RNEG o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_LZERO = (MATCH_MP BILINEAR_LZERO o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_RZERO = (MATCH_MP BILINEAR_RZERO o SPEC_ALL) BILINEAR_PRODUCT;;
(* ------------------------------------------------------------------------- *)
(* Under suitable conditions, it's also associative. *)
(* ------------------------------------------------------------------------- *)
let PRODUCT_ASSOCIATIVE = prove
(`!op mult. (!s t. s
SUBSET 1..dimindex(:N) /\ t
SUBSET 1..dimindex(:N)
==> (op s t)
SUBSET 1..dimindex(:N)) /\
(!s t u. op s (op t u) = op (op s t) u) /\
(!s t u. mult t u * mult s (op t u) = mult s t * mult (op s t) u)
==> !x y z:real^(N)multivector.
Product mult op x (Product mult op y z) =
Product mult op (Product mult op x y) z`,
let SUM_SWAP_POWERSET =
SIMP_RULE[
FINITE_POWERSET;
FINITE_NUMSEG]
(repeat(SPEC `{s | s
SUBSET 1..dimindex(:N)}`)
(ISPEC `f:(num->bool)->(num->bool)->real`
SUM_SWAP)) in
let SWAP_TAC cnv n =
GEN_REWRITE_TAC (cnv o funpow n BINDER_CONV) [SUM_SWAP_POWERSET] THEN
REWRITE_TAC[] in
let SWAPS_TAC cnv ns x =
MAP_EVERY (SWAP_TAC cnv) ns THEN MATCH_MP_TAC
SUM_EQ THEN X_GEN_TAC x THEN
REWRITE_TAC[
IN_ELIM_THM] THEN STRIP_TAC in
REWRITE_TAC[
Product_DEF] THEN REPEAT STRIP_TAC THEN
SIMP_TAC[
MULTIVECTOR_EQ;
MULTIVECTOR_VSUM_COMPONENT;
MBASIS_COMPONENT;
MULTIVECTOR_MUL_COMPONENT] THEN
SIMP_TAC[GSYM
SUM_LMUL; GSYM
SUM_RMUL] THEN
X_GEN_TAC `r:num->bool` THEN STRIP_TAC THEN
SWAPS_TAC RAND_CONV [1;0] `s:num->bool` THEN
SWAP_TAC LAND_CONV 0 THEN SWAPS_TAC RAND_CONV [1;0] `t:num->bool` THEN
SWAP_TAC RAND_CONV 0 THEN SWAPS_TAC LAND_CONV [0] `u:num->bool` THEN
REWRITE_TAC[GSYM REAL_MUL_ASSOC;
REAL_ARITH `(if p then a else &0) * b = if p then a * b else &0`;
REAL_ARITH `a * (if p then b else &0) = if p then a * b else &0`] THEN
SIMP_TAC[
SUM_DELTA] THEN ASM_SIMP_TAC[
IN_ELIM_THM] THEN
COND_CASES_TAC THEN ASM_REWRITE_TAC[REAL_MUL_LID;
REAL_MUL_RID] THEN
ASM_REWRITE_TAC[] THEN REWRITE_TAC[
REAL_MUL_AC]);;
(* ------------------------------------------------------------------------- *)
(* Geometric product. *)
(* ------------------------------------------------------------------------- *)
overload_interface
("*",
`geom_mul:real^(N)multivector->real^(N)multivector->real^(N)multivector`);;
let geom_mul = new_definition
`(x:real^(N)multivector) * y =
Product (\s t. --(&1) pow CARD {i,j | i IN 1..dimindex(:N) /\
j IN 1..dimindex(:N) /\
i IN s /\ j IN t /\ i > j})
(\s t. (s DIFF t) UNION (t DIFF s))
x y`;;
let GEOM_LADD = (MATCH_MP BILINEAR_LADD o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_RADD = (MATCH_MP BILINEAR_RADD o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_LMUL = (MATCH_MP BILINEAR_LMUL o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_RMUL = (MATCH_MP BILINEAR_RMUL o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_LNEG = (MATCH_MP BILINEAR_LNEG o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_RNEG = (MATCH_MP BILINEAR_RNEG o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_LZERO = (MATCH_MP BILINEAR_LZERO o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_RZERO = (MATCH_MP BILINEAR_RZERO o SPEC_ALL) BILINEAR_GEOM;;
(* ------------------------------------------------------------------------- *)
(* Outer product. *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("outer",(20,"right"));;
let OUTER = prove
(`!x y:real^(N)multivector.
x outer y =
Product (\s t. if ~(s
INTER t = {}) then &0
else --(&1) pow
CARD {i,j | i
IN 1..dimindex(:N) /\
j
IN 1..dimindex(:N) /\
i
IN s /\ j
IN t /\ i > j})
(
UNION)
x y`,
let OUTER_LADD = (MATCH_MP BILINEAR_LADD o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_RADD = (MATCH_MP BILINEAR_RADD o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_LMUL = (MATCH_MP BILINEAR_LMUL o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_RMUL = (MATCH_MP BILINEAR_RMUL o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_LNEG = (MATCH_MP BILINEAR_LNEG o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_RNEG = (MATCH_MP BILINEAR_RNEG o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_LZERO = (MATCH_MP BILINEAR_LZERO o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_RZERO = (MATCH_MP BILINEAR_RZERO o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_ASSOC = prove
(`!x y z:real^(N)multivector. x outer (y outer z) = (x outer y) outer z`,
(* ------------------------------------------------------------------------- *)
(* Inner product. *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("inner",(20,"right"));;
let inner = new_definition
`!x y:real^(N)multivector.
x inner y =
Product (\s t. if s = {} \/ t = {} \/
~((s DIFF t) = {} /\ ~(t DIFF s = {}))
then &0
else --(&1) pow CARD {i,j | i IN 1..dimindex(:N) /\
j IN 1..dimindex(:N) /\
i IN s /\ j IN t /\ i > j})
(\s t. (s DIFF t) UNION (t DIFF s))
x y`;;
let INNER_LADD = (MATCH_MP BILINEAR_LADD o SPEC_ALL) BILINEAR_INNER;;
let INNER_RADD = (MATCH_MP BILINEAR_RADD o SPEC_ALL) BILINEAR_INNER;;
let INNER_LMUL = (MATCH_MP BILINEAR_LMUL o SPEC_ALL) BILINEAR_INNER;;
let INNER_RMUL = (MATCH_MP BILINEAR_RMUL o SPEC_ALL) BILINEAR_INNER;;
let INNER_LNEG = (MATCH_MP BILINEAR_LNEG o SPEC_ALL) BILINEAR_INNER;;
let INNER_RNEG = (MATCH_MP BILINEAR_RNEG o SPEC_ALL) BILINEAR_INNER;;
let INNER_LZERO = (MATCH_MP BILINEAR_LZERO o SPEC_ALL) BILINEAR_INNER;;
let INNER_RZERO = (MATCH_MP BILINEAR_RZERO o SPEC_ALL) BILINEAR_INNER;;
(* ------------------------------------------------------------------------- *)
(* Actions of products on basis and singleton basis. *)
(* ------------------------------------------------------------------------- *)
let PRODUCT_MBASIS = prove
(`!s t. Product mult op (mbasis s) (mbasis t) :real^(N)multivector =
if s
SUBSET 1..dimindex(:N) /\ t
SUBSET 1..dimindex(:N)
then mult s t % mbasis(op s t)
else vec 0`,
let PRODUCT_MBASIS_SING = prove
(`!i j. Product mult op (mbasis{i}) (mbasis{j}) :real^(N)multivector =
if i
IN 1..dimindex(:N) /\ j
IN 1..dimindex(:N)
then mult {i} {j} % mbasis(op {i} {j})
else vec 0`,
let GEOM_MBASIS_SING = prove
(`!i j. mbasis{i} * mbasis{j} :real^(N)multivector =
if i
IN 1..dimindex(:N) /\ j
IN 1..dimindex(:N)
then if i = j then mbasis{}
else if i < j then mbasis{i,j}
else --(mbasis{i,j})
else vec 0`,
let OUTER_MBASIS_SING = prove
(`!i j. mbasis{i} outer mbasis{j} :real^(N)multivector =
if i
IN 1..dimindex(:N) /\ j
IN 1..dimindex(:N) /\ ~(i = j)
then if i < j then mbasis{i,j} else --(mbasis{i,j})
else vec 0`,
(* ------------------------------------------------------------------------- *)
(* Some simple consequences. *)
(* ------------------------------------------------------------------------- *)
let OUTER_MBASIS_SKEWSYM = prove
(`!i j. mbasis{i} outer mbasis{j} = --(mbasis{j} outer mbasis{i})`,
REPEAT GEN_TAC THEN REWRITE_TAC[
OUTER_MBASIS_SING] THEN
ASM_CASES_TAC `i:num = j` THEN ASM_REWRITE_TAC[
VECTOR_NEG_0] THEN
FIRST_ASSUM(DISJ_CASES_TAC o MATCH_MP (ARITH_RULE
`~(i:num = j) ==> i < j /\ ~(j < i) \/ j < i /\ ~(i < j)`)) THEN
ASM_REWRITE_TAC[
CONJ_ACI] THEN COND_CASES_TAC THEN
ASM_REWRITE_TAC[
VECTOR_NEG_NEG;
VECTOR_NEG_0] THEN
REPEAT AP_TERM_TAC THEN SET_TAC[]);;
(* ------------------------------------------------------------------------- *)
(* Just for generality, normalize a set enumeration. *)
(* ------------------------------------------------------------------------- *)
let SETENUM_NORM_CONV =
let conv =
GEN_REWRITE_CONV I [EXTENSION] THENC
GEN_REWRITE_CONV TOP_SWEEP_CONV [IN_SING; IN_INSERT] THENC
BINDER_CONV(EQT_INTRO o DISJ_ACI_RULE) THENC
GEN_REWRITE_CONV I [FORALL_SIMP] in
fun tm ->
let nums = dest_setenum tm in
let nums' = map mk_numeral (sort (</) (map dest_numeral (setify nums))) in
if nums' = nums then REFL tm else
let eq = mk_eq(tm,mk_setenum(nums',fst(dest_fun_ty(type_of tm)))) in
EQT_ELIM(conv eq);;
(* ------------------------------------------------------------------------- *)
(* Conversion to split extended basis combinations. *)
(* *)
(* Also 1-step merge from left, which can be DEPTH_CONV'd. In this case the *)
(* order must be correct. *)
(* ------------------------------------------------------------------------- *)
let MBASIS_SPLIT_CONV,MBASIS_MERGE_CONV =
let setlemma = SET_RULE
`((!x:num. x IN {} ==> a < x) <=> T) /\
((!x:num. x IN (y INSERT s) ==> a < x) <=>
a < y /\ (!x. x IN s ==> a < x))` in
let SET_CHECK_CONV =
GEN_REWRITE_CONV TOP_SWEEP_CONV [setlemma] THENC NUM_REDUCE_CONV
and INST_SPLIT = PART_MATCH (lhs o rand) MBASIS_SPLIT
and INST_MERGE = PART_MATCH (lhs o rand) (GSYM MBASIS_SPLIT) in
let rec conv tm =
if length(dest_setenum(rand tm)) <= 1 then REFL tm else
let th = MP_CONV SET_CHECK_CONV (INST_SPLIT tm) in
let th' = RAND_CONV conv (rand(concl th)) in
TRANS th th' in
(fun tm ->
try let op,se = dest_comb tm in
if fst(dest_const op) = "mbasis" & forall is_numeral (dest_setenum se)
then (RAND_CONV SETENUM_NORM_CONV THENC conv) tm
else fail()
with Failure _ -> failwith "MBASIS_SPLIT_CONV"),
(fun tm -> try MP_CONV SET_CHECK_CONV (INST_MERGE tm)
with Failure _ -> failwith "MBASIS_MERGE_CONV");;
(* ------------------------------------------------------------------------- *)
(* Convergent (if slow) rewrite set to bubble into position. *)
(* ------------------------------------------------------------------------- *)
let OUTER_ACI = prove
(`(!x y z. (x outer y) outer z = x outer (y outer z)) /\
(!i j. i > j
==> mbasis{i} outer mbasis{j} =
--(&1) % (mbasis{j} outer mbasis{i})) /\
(!i j x. i > j
==> mbasis{i} outer mbasis{j} outer x =
--(&1) % (mbasis{j} outer mbasis{i} outer x)) /\
(!i. mbasis{i} outer mbasis{i} = vec 0) /\
(!i x. mbasis{i} outer mbasis{i} outer x = vec 0) /\
(!x. mbasis{} outer x = x) /\
(!x. x outer mbasis{} = x)`,
(* ------------------------------------------------------------------------- *)
(* Group the final "c1 % mbasis s1 + ... + cn % mbasis sn". *)
(* ------------------------------------------------------------------------- *)
let MBASIS_GROUP_CONV tm =
let tms = striplist(dest_binary "vector_add") tm in
if length tms = 1 then LAND_CONV REAL_POLY_CONV tm else
let vadd_tm = rator(rator tm) in
let mk_vadd = mk_binop vadd_tm in
let mbs = map (snd o dest_binary "%") tms in
let tmbs = zip mbs tms and mset = setify mbs in
let grps = map (fun x -> map snd (filter (fun (x',_) -> x' = x) tmbs))
mset in
let tm' = end_itlist mk_vadd (map (end_itlist mk_vadd) grps) in
let th1 = AC VECTOR_ADD_AC (mk_eq(tm,tm'))
and th2 =
(GEN_REWRITE_CONV DEPTH_CONV [GSYM VECTOR_ADD_RDISTRIB] THENC
DEPTH_BINOP_CONV vadd_tm (LAND_CONV REAL_POLY_CONV)) tm' in
TRANS th1 th2;;
(* ------------------------------------------------------------------------- *)
(* Overall conversion. *)
(* ------------------------------------------------------------------------- *)
let OUTER_CANON_CONV =
ONCE_DEPTH_CONV MBASIS_SPLIT_CONV THENC
GEN_REWRITE_CONV TOP_DEPTH_CONV
[VECTOR_SUB; VECTOR_NEG_MINUS1;
OUTER_LADD; OUTER_RADD; OUTER_LMUL; OUTER_RMUL; OUTER_LZERO; OUTER_RZERO;
VECTOR_ADD_LDISTRIB; VECTOR_ADD_RDISTRIB; VECTOR_MUL_ASSOC;
VECTOR_MUL_LZERO; VECTOR_MUL_RZERO] THENC
REAL_RAT_REDUCE_CONV THENC
PURE_SIMP_CONV[OUTER_ACI; ARITH_GT; ARITH_GE; OUTER_LMUL; OUTER_RMUL;
OUTER_LZERO; OUTER_RZERO] THENC
PURE_REWRITE_CONV[VECTOR_MUL_LZERO; VECTOR_MUL_RZERO;
VECTOR_ADD_LID; VECTOR_ADD_RID; VECTOR_MUL_ASSOC] THENC
GEN_REWRITE_CONV I [GSYM VECTOR_MUL_LID] THENC
PURE_REWRITE_CONV
[VECTOR_ADD_LDISTRIB; VECTOR_ADD_RDISTRIB; VECTOR_MUL_ASSOC] THENC
REAL_RAT_REDUCE_CONV THENC PURE_REWRITE_CONV[GSYM VECTOR_ADD_ASSOC] THENC
DEPTH_CONV MBASIS_MERGE_CONV THENC
MBASIS_GROUP_CONV THENC
GEN_REWRITE_CONV DEPTH_CONV [GSYM VECTOR_ADD_RDISTRIB] THENC
REAL_RAT_REDUCE_CONV;;
(* ------------------------------------------------------------------------- *)
(* Iterated operation in order. *)
(* I guess this ought to be added to the core... *)
(* ------------------------------------------------------------------------- *)
let seqiterate_EXISTS = prove
(`!op f. ?h.
!s. h s = if
INFINITE s \/ s = {} then neutral op else
let i = minimal x. x
IN s in
if s = {i} then f(i) else op (f i) (h (s
DELETE i))`,
REPEAT GEN_TAC THEN REWRITE_TAC[
INFINITE] THEN
MATCH_MP_TAC(MATCH_MP
WF_REC (ISPEC `CARD:(num->bool)->num`
WF_MEASURE)) THEN
REPEAT STRIP_TAC THEN COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN
LET_TAC THEN CONV_TAC(ONCE_DEPTH_CONV let_CONV) THEN
COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN AP_TERM_TAC THEN
FIRST_X_ASSUM MATCH_MP_TAC THEN REWRITE_TAC[
MEASURE] THEN
RULE_ASSUM_TAC(REWRITE_RULE[DE_MORGAN_THM]) THEN
SUBGOAL_THEN `?i:num. i
IN s` MP_TAC THENL
[ASM_MESON_TAC[
MEMBER_NOT_EMPTY]; ALL_TAC] THEN
GEN_REWRITE_TAC LAND_CONV [
MINIMAL] THEN
ASM_SIMP_TAC[
CARD_DELETE;
CARD_EQ_0; ARITH_RULE `n - 1 < n <=> ~(n = 0)`]);;
let EXISTS_SWAP = prove
(`!P. (?f. P f) <=> (?f:A->B->C. P (\b a. f a b))`,
GEN_TAC THEN EQ_TAC THEN DISCH_THEN CHOOSE_TAC THENL
[EXISTS_TAC `\a b. (f:B->A->C) b a` THEN ASM_REWRITE_TAC[ETA_AX];
ASM_MESON_TAC[]]);;
let SEQITERATE_CLAUSES = prove
(`(!op f. seqiterate op {} f = neutral op) /\
(!op f i. seqiterate op {i} f = f(i)) /\
(!op f i s.
FINITE s /\ ~(s = {}) /\ (!j. j
IN s ==> i < j)
==> seqiterate op (i
INSERT s) f =
op (f i) (seqiterate op s f))`,
(* ------------------------------------------------------------------------- *)
(* In the "common" case this agrees with ordinary iteration. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Outermorphism extension. *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Reversion operation. *)
(* ------------------------------------------------------------------------- *)