1-- Grouping in two stages equals grouping along the composite map (ZF).2-- Part of Cumulus, the Grace17 mathematics library.3-- Copyright (c) 2026 Grant Passmore.4-- Released under the Apache License 2.0. See LICENSE in the repository root.5-- Authors: Grant Passmore67module Grace.Cumulus.Algebra.Ring.PushforwardComposition8import Regrouping910section Grouping11variable (D C B A add zero neg mul one h k w : Set)12hypothesisring : IsCommRing(A, add, zero, neg, mul, one)13hypothesis finiteD : Finite(D)14hypothesis weights : w : D → A15hypothesis firstMap : h : D → C16hypothesis secondMap : k : C → B1718-- Pointwise filtering can move across the first grouping operation.19theorem finitePushforward.filter_composition (b : Set)20shows finitePushforward(D, C, A, add, zero, neg, mul, one, h,21 pushforwardWeights(D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b)) =22 pushforwardWeights(C, A, add, zero, neg, mul, one, k,23 finitePushforward(D, C, A, add, zero, neg, mul, one, h, w), b)24proof25have ordinary := IsCommRing.ring withring26have zeroIn := IsRing.zero_in_carrier with ordinary27have filtered := pushforwardWeights.function at D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b with*28have grouped := finitePushforward.function at D, C, A, add, zero, neg, mul, one, h, w with*29have leftFunction := finitePushforward.function at D, C, A, add, zero, neg, mul, one, h, pushforwardWeights(D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b) with*30have rightFunction := pushforwardWeights.function at C, A, add, zero, neg, mul, one, k, finitePushforward(D, C, A, add, zero, neg, mul, one, h, w), b with*31apply FunctionBetween.ext with leftFunction, rightFunction32intros c hc33have rowW := pushforwardWeights.function at D, A, add, zero, neg, mul, one, h, w, c with*34have rowV := pushforwardWeights.function at D, A, add, zero, neg, mul, one, h, pushforwardWeights(D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b), c with*35have entries : ∀ d ∈ D,36 pushforwardWeights(D, A, add, zero, neg, mul, one, h, pushforwardWeights(D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b), c)(d) =37 singleValue(A, zero, pushforwardWeights(D, A, add, zero, neg, mul, one, h, w, c)(d), k(c), b)38intros d hd39have coefficient := FunctionBetween.apply_closed at w, D, A, d with*40have composite := compose.apply at k, h, D, C, B, d with*41have selected := pushforwardWeights.value at D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b, d with*42haveleft:= pushforwardWeights.value at D, A, add, zero, neg, mul, one, h, pushforwardWeights(D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b), c, d with*43have row := pushforwardWeights.value at D, A, add, zero, neg, mul, one, h, w, c, d with*44have iteration := singleValue.iterated at A, zero, w(d), k, h(d), c, b with*45cc46have selection := finiteRingSum.select with*47haveleft:= finitePushforward.value at D, C, A, add, zero, neg, mul, one, h, pushforwardWeights(D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b), c with*48have row := finitePushforward.value at D, C, A, add, zero, neg, mul, one, h, w, c with*49haveright:= pushforwardWeights.value at C, A, add, zero, neg, mul, one, k, finitePushforward(D, C, A, add, zero, neg, mul, one, h, w), b, c with*50cc5152theorem finitePushforward.compose53requires finiteC : Finite(C)54shows finitePushforward(C, B, A, add, zero, neg, mul, one, k,55 finitePushforward(D, C, A, add, zero, neg, mul, one, h, w)) =56 finitePushforward(D, B, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w)57proof58have grouped := finitePushforward.function at D, C, A, add, zero, neg, mul, one, h, w with*59have leftFunction := finitePushforward.function at C, B, A, add, zero, neg, mul, one, k, finitePushforward(D, C, A, add, zero, neg, mul, one, h, w) with*60have rightFunction := finitePushforward.function at D, B, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w with*61apply FunctionBetween.ext with leftFunction, rightFunction62intros b hb63have filtered := pushforwardWeights.function at D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b with*64have commuted := finitePushforward.filter_composition at D, C, B, A, add, zero, neg, mul, one, h, k, w, b with*65have regrouped := finitePushforward.sum at D, C, A, add, zero, neg, mul, one, h, pushforwardWeights(D, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b) with*66haveleft:= finitePushforward.value at C, B, A, add, zero, neg, mul, one, k, finitePushforward(D, C, A, add, zero, neg, mul, one, h, w), b with*67haveright:= finitePushforward.value at D, B, A, add, zero, neg, mul, one, compose(k, h, D, C, B), w, b with*68cc6970end Grouping71theorem finitePushforward.identity (D A add zero neg mul one w : Set)72requiresring : IsCommRing(A, add, zero, neg, mul, one)73requires finite : Finite(D)74requires weights : w : D → A75shows finitePushforward(D, D, A, add, zero, neg, mul, one, identity(D), w) = w76proof77have ordinary := IsCommRing.ring withring78have zeroIn := IsRing.zero_in_carrier with ordinary79have grouped := finitePushforward.function at D, D, A, add, zero, neg, mul, one, identity(D), w with*80apply FunctionBetween.ext with grouped, weights81intros b hb82have coefficient := FunctionBetween.apply_closed at w, D, A, b with*83have selected := pushforwardWeights.function at D, A, add, zero, neg, mul, one, identity(D), w, b with*84have single := coefficientSingle.function at D, A, zero, w(b), b with*85have equal : pushforwardWeights(D, A, add, zero, neg, mul, one, identity(D), w, b) = coefficientSingle(D, A, zero, w(b), b)86apply FunctionBetween.ext with selected, single87intros d hd88have coefficientD := FunctionBetween.apply_closed at w, D, A, d with*89have id := identity.apply at d, D with*90haveleft:= pushforwardWeights.value at D, A, add, zero, neg, mul, one, identity(D), w, b, d with*91haveright:= coefficientSingle.value at D, A, zero, w(b), b, d with*92have alternatives : d = b ∨ d ≠ b bytauto93cases alternatives with same | different94 . cc95 . have reversed : b ≠ d96intro equal97have reverseEqual : d = b98 symm99exact equal100exact different with reverseEqual101have leftZero := singleValue.other at A, zero, w(d), d, b with*102have rightZero := singleValue.other at A, zero, w(b), b, d with*103cc104have sum := finiteRingSum.single at D, A, add, zero, neg, mul, one, w(b), b with*105have value := finitePushforward.value at D, D, A, add, zero, neg, mul, one, identity(D), w, b with*106cc107108theorem finitePushforward.index_congr (D C A add zero neg mul one h k w : Set)109requiresring : IsCommRing(A, add, zero, neg, mul, one)110requires finite : Finite(D)111requires weights : w : D → A112requires indices : ∀ d ∈ D, h(d) = k(d)113shows finitePushforward(D, C, A, add, zero, neg, mul, one, h, w) = finitePushforward(D, C, A, add, zero, neg, mul, one, k, w)114proof115have leftFunction := finitePushforward.function at D, C, A, add, zero, neg, mul, one, h, w with*116have rightFunction := finitePushforward.function at D, C, A, add, zero, neg, mul, one, k, w with*117apply FunctionBetween.ext with leftFunction, rightFunction118intros c hc119have leftWeights := pushforwardWeights.function at D, A, add, zero, neg, mul, one, h, w, c with*120have rightWeights := pushforwardWeights.function at D, A, add, zero, neg, mul, one, k, w, c with*121have equal : pushforwardWeights(D, A, add, zero, neg, mul, one, h, w, c) = pushforwardWeights(D, A, add, zero, neg, mul, one, k, w, c)122apply FunctionBetween.ext with leftWeights, rightWeights123intros d hd124have index := indices with d, hd125haveleft:= pushforwardWeights.value at D, A, add, zero, neg, mul, one, h, w, c, d with*126haveright:= pushforwardWeights.value at D, A, add, zero, neg, mul, one, k, w, c, d with*127cc128haveleft:= finitePushforward.value at D, C, A, add, zero, neg, mul, one, h, w, c with*129haveright:= finitePushforward.value at D, C, A, add, zero, neg, mul, one, k, w, c with*130cc131