doc-next-gen

Mathlib.Analysis.Calculus.ContDiff.FaaDiBruno

Module docstring

{"# Faa di Bruno formula

The Faa di Bruno formula gives the iterated derivative of g ∘ f in terms of those of g and f. It is expressed in terms of partitions I of {0, ..., n-1}. For such a partition, denote by k its number of parts, write the parts as I₀, ..., Iₖ₋₁ ordered so that max I₀ < ... < max Iₖ₋₁, and let iₘ be the number of elements of Iₘ. Then D^n (g ∘ f) (x) (v₀, ..., vₙ₋₁) = ∑_{I partition of {0, ..., n-1}} D^k g (f x) (D^{i₀} f (x) (v_{I₀}), ..., D^{iₖ₋₁} f (x) (v_{Iₖ₋₁})) where by v_{Iₘ} we mean the vectors vᵢ with indices in Iₘ, i.e., the composition of v with the increasing embedding of Fin iₘ into Fin n with range Iₘ.

For instance, for n = 2, there are 2 partitions of {0, 1}, given by {0}, {1} and {0, 1}, and therefore D^2(g ∘ f) (x) (v₀, v₁) = D^2 g (f x) (Df (x) v₀, Df (x) v₁) + Dg (f x) (D^2f (x) (v₀, v₁)).

The formula is straightforward to prove by induction, as differentiating D^k g (f x) (D^{i₀} f (x) (v_{I₀}), ..., D^{iₖ₋₁} f (x) (v_{Iₖ₋₁})) gives a sum with k + 1 terms where one differentiates either D^k g (f x), or one of the D^{iₘ} f (x), amounting to adding to the partition I either a new atom {-1} to its left, or extending Iₘ by adding -1 to it. In this way, one obtains bijectively all partitions of {-1, ..., n}, and the proof can go on (up to relabelling).

The main difficulty is to write things down in a precise language, namely to write D^k g (f x) (D^{i₀} f (x) (v_{I₀}), ..., D^{iₖ₋₁} f (x) (v_{Iₖ₋₁})) as a continuous multilinear map of the vᵢ. For this, instead of working with partitions of {0, ..., n-1} and ordering their parts, we work with partitions in which the ordering is part of the data -- this is equivalent, but much more convenient to implement. We call these OrderedFinpartition n.

Note that the implementation of OrderedFinpartition is very specific to the Faa di Bruno formula: as testified by the formula above, what matters is really the embedding of the parts in Fin n, and moreover the parts have to be ordered by max I₀ < ... < max Iₖ₋₁ for the formula to hold in the general case where the iterated differential might not be symmetric. The defeqs with respect to Fin.cons are also important when doing the induction. For this reason, we do not expect this class to be useful beyond the Faa di Bruno formula, which is why it is in this file instead of a dedicated file in the Combinatorics folder.

Main results

Given c : OrderedFinpartition n and two formal multilinear series q and p, we define c.compAlongOrderedFinpartition q p as an n-multilinear map given by the formula above, i.e., (v₁, ..., vₙ) ↦ qₖ (p_{i₁} (v_{I₁}), ..., p_{iₖ} (v_{Iₖ})).

Then, we define q.taylorComp p as a formal multilinear series whose n-th term is the sum of c.compAlongOrderedFinpartition q p over all ordered finpartitions of size n.

Finally, we prove in HasFTaylorSeriesUptoOn.comp that, if two functions g and f have Taylor series up to n given by q and p, then g ∘ f also has a Taylor series, given by q.taylorComp p.

Implementation

A first technical difficulty is to implement the extension process of OrderedFinpartition corresponding to adding a new atom, or appending an atom to an existing part, and defining the associated increasing parameterizations that show up in the definition of compAlongOrderedFinpartition.

Then, one has to show that the ordered finpartitions thus obtained give exactly all ordered finpartitions of order n+1. For this, we define the inverse process (shrinking a finpartition of n+1 by erasing 0, either as an atom or from the part that contains it), and we show that these processes are inverse to each other, yielding an equivalence between (c : OrderedFinpartition n) × Option (Fin c.length) and OrderedFinpartition (n + 1). This equivalence shows up prominently in the inductive proof of Faa di Bruno formula to identify the sums that show up. ","### Basic API for ordered finpartitions ","### Extending and shrinking ordered finpartitions

We show how an ordered finpartition can be extended to the left, either by adding a new atomic part (in extendLeft) or adding the new element to an existing part (in extendMiddle). Conversely, one can shrink a finpartition by deleting the element to the left, with a different behavior if it was an atomic part (in eraseLeft, in which case the number of parts decreases by one) or if it belonged to a non-atomic part (in eraseMiddle, in which case the number of parts stays the same).

These operations are inverse to each other, giving rise to an equivalence between ((c : OrderedFinpartition n) × Option (Fin c.length)) and OrderedFinpartition (n + 1) called OrderedFinPartition.extendEquiv. ","### Applying ordered finpartitions to multilinear maps ","### The Faa di Bruno formula "}

OrderedFinpartition structure
(n : ℕ)
Full source
/-- A partition of `Fin n` into finitely many nonempty subsets, given by the increasing
parameterization of these subsets. We order the subsets by increasing greatest element.
This definition is tailored-made for the Faa di Bruno formula, and probably not useful elsewhere,
because of the specific parameterization by `Fin n` and the peculiar ordering. -/
@[ext]
structure OrderedFinpartition (n : ) where
  /-- The number of parts in the partition -/
  length : 
  /-- The size of each part -/
  partSize : Fin length → 
  partSize_pos : ∀ m, 0 < partSize m
  /-- The increasing parameterization of each part -/
  emb : ∀ m, (Fin (partSize m)) → Fin n
  emb_strictMono : ∀ m, StrictMono (emb m)
  /-- The parts are ordered by increasing greatest element. -/
  parts_strictMono :
    StrictMono fun m ↦ emb m ⟨partSize m - 1, Nat.sub_one_lt_of_lt (partSize_pos m)⟩
  /-- The parts are disjoint -/
  disjoint : PairwiseDisjoint univ fun m ↦ range (emb m)
  /-- The parts cover everything -/
  cover x : ∃ m, x ∈ range (emb m)
  deriving DecidableEq
Ordered Finite Partition of $\{0, \dots, n-1\}$
Informal description
An ordered finite partition of the set $\{0, \dots, n-1\}$ into nonempty subsets, where the subsets are parameterized by increasing functions and ordered by their maximum elements. This structure is specifically designed for use in the Faa di Bruno formula, due to its particular parameterization and ordering requirements.
instDecidableEqOrderedFinpartition instance
{n✝} : DecidableEq✝ (@OrderedFinpartition✝ n✝)
Full source
DecidableEq
Decidable Equality for Ordered Finite Partitions
Informal description
For any natural number \( n \), the type of ordered finite partitions of \(\{0, \dots, n-1\}\) has decidable equality. This means that given any two ordered finite partitions \( c_1 \) and \( c_2 \) of \(\{0, \dots, n-1\}\), there is an algorithm to determine whether \( c_1 = c_2 \).
OrderedFinpartition.atomic definition
(n : ℕ) : OrderedFinpartition n
Full source
/-- The ordered finpartition of `Fin n` into singletons. -/
@[simps -fullyApplied]
def atomic (n : ) : OrderedFinpartition n where
  length := n
  partSize _ :=  1
  partSize_pos _ := _root_.zero_lt_one
  emb m _ := m
  emb_strictMono _ := Subsingleton.strictMono _
  parts_strictMono := strictMono_id
  disjoint _ _ _ _ h := by simpa using h
  cover m := by simp
Singleton partition of $\{0, \dots, n-1\}$
Informal description
The ordered finite partition of the set $\{0, \dots, n-1\}$ where each part is a singleton, i.e., the partition $\{\{0\}, \{1\}, \dots, \{n-1\}\}$.
OrderedFinpartition.instInhabited instance
: Inhabited (OrderedFinpartition n)
Full source
instance : Inhabited (OrderedFinpartition n) := ⟨atomic n⟩
Inhabitedness of Ordered Finite Partitions
Informal description
For any natural number $n$, the type of ordered finite partitions of $\{0, \dots, n-1\}$ is inhabited. In particular, the default partition is the atomic partition where each element is in its own singleton part.
OrderedFinpartition.default_eq theorem
: (default : OrderedFinpartition n) = atomic n
Full source
@[simp]
theorem default_eq : (default : OrderedFinpartition n) = atomic n := rfl
Default Ordered Partition is Atomic
Informal description
The default ordered finite partition of the set $\{0, \dots, n-1\}$ is the atomic partition where each element is in its own singleton part, i.e., $\text{default} = \text{atomic}\ n$.
OrderedFinpartition.partSize_le theorem
(m : Fin c.length) : c.partSize m ≤ n
Full source
lemma partSize_le (m : Fin c.length) : c.partSize m ≤ n := by
  simpa only [Fintype.card_fin] using Fintype.card_le_of_injective _ (c.emb_strictMono m).injective
Upper Bound on Part Sizes in Ordered Finite Partition
Informal description
For any ordered finite partition $c$ of the set $\{0, \dots, n-1\}$ and any part index $m$ (where $m$ ranges over the number of parts in $c$), the size of the $m$-th part of $c$ is at most $n$.
OrderedFinpartition.embSigma definition
(n : ℕ) : OrderedFinpartition n → (Σ (l : Fin (n + 1)), Σ (p : Fin l → Fin (n + 1)), Π (i : Fin l), (Fin (p i) → Fin n))
Full source
/-- Embedding of ordered finpartitions in a sigma type. The sigma type on the right is quite big,
but this is enough to get finiteness of ordered finpartitions. -/
def embSigma (n : ) : OrderedFinpartition n →
    (Σ (l : Fin (n + 1)), Σ (p : Fin l → Fin (n + 1)), Π (i : Fin l), (Fin (p i) → Fin n)) :=
  fun c ↦ ⟨⟨c.length, Order.lt_add_one_iff.mpr c.length_le⟩,
    fun m ↦ ⟨c.partSize m, Order.lt_add_one_iff.mpr (c.partSize_le m)⟩, fun j ↦ c.emb j⟩
Embedding of Ordered Finite Partitions into Sigma Type
Informal description
For a given natural number \( n \), the function maps an ordered finite partition \( c \) of the set \(\{0, \dots, n-1\}\) to a sigma type consisting of: 1. A natural number \( l \) (the number of parts in the partition) bounded by \( n + 1 \), 2. A function \( p \) that assigns to each part index \( m \) a natural number \( p(m) \) (the size of the \( m \)-th part) bounded by \( n + 1 \), and 3. For each part index \( j \), an increasing embedding from \( \text{Fin}(p(j)) \) to \( \text{Fin}(n) \) representing the \( j \)-th part of the partition. This embedding is injective and is used to establish finiteness properties of ordered finite partitions.
OrderedFinpartition.injective_embSigma theorem
(n : ℕ) : Injective (embSigma n)
Full source
lemma injective_embSigma (n : ) : Injective (embSigma n) := by
  rintro ⟨plength, psize, -, pemb, -, -, -, -⟩ ⟨qlength, qsize, -, qemb, -, -, -, -⟩
  intro hpq
  simp_all only [Sigma.mk.inj_iff, heq_eq_eq, true_and, mk.injEq, and_true, Fin.mk.injEq, embSigma]
  have : plength = qlength := hpq.1
  subst this
  simp_all only [Sigma.mk.inj_iff, heq_eq_eq, true_and, mk.injEq, and_true, Fin.mk.injEq, embSigma]
  ext i
  exact mk.inj_iff.mp (congr_fun hpq.1 i)
Injectivity of the Ordered Finite Partition Embedding $\mathrm{embSigma}_n$
Informal description
For any natural number $n$, the embedding function $\mathrm{embSigma}_n$ from the set of ordered finite partitions of $\{0, \dots, n-1\}$ to the sigma type $\Sigma (l : \mathrm{Fin}(n+1)), \Sigma (p : \mathrm{Fin}(l) \to \mathrm{Fin}(n+1)), \Pi (i : \mathrm{Fin}(l)), (\mathrm{Fin}(p(i)) \to \mathrm{Fin}(n))$ is injective. In other words, distinct ordered finite partitions of $\{0, \dots, n-1\}$ are mapped to distinct elements in the sigma type representation.
OrderedFinpartition.instUniqueZero instance
: Unique (OrderedFinpartition 0)
Full source
instance instUniqueZero : Unique (OrderedFinpartition 0) := by
  have : Subsingleton (OrderedFinpartition 0) :=
    Fintype.card_le_one_iff_subsingleton.mp (Fintype.card_le_of_injective _ (injective_embSigma 0))
  exact Unique.mk' (OrderedFinpartition 0)
Unique Ordered Partition of the Empty Set
Informal description
The type of ordered finite partitions of the empty set $\{ \}$ is uniquely inhabited, meaning there is exactly one such partition.
OrderedFinpartition.exists_inverse theorem
{n : ℕ} (c : OrderedFinpartition n) (j : Fin n) : ∃ p : Σ m, Fin (c.partSize m), c.emb p.1 p.2 = j
Full source
lemma exists_inverse {n : } (c : OrderedFinpartition n) (j : Fin n) :
    ∃ p : Σ m, Fin (c.partSize m), c.emb p.1 p.2 = j := by
  rcases c.cover j with ⟨m, r, hmr⟩
  exact ⟨⟨m, r⟩, hmr⟩
Existence of Preimage under Ordered Finite Partition Embedding
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$ and any index $j \in \{0, \dots, n-1\}$, there exists a pair $(m, k)$ where $m$ is a part index and $k$ is an index within the $m$-th part, such that the embedding function $c.\text{emb}$ maps $(m, k)$ to $j$.
OrderedFinpartition.emb_injective theorem
: Injective (fun (p : Σ m, Fin (c.partSize m)) ↦ c.emb p.1 p.2)
Full source
lemma emb_injective : Injective (fun (p : Σ m, Fin (c.partSize m)) ↦ c.emb p.1 p.2) := by
  rintro ⟨m, r⟩ ⟨m', r'⟩ (h : c.emb m r = c.emb m' r')
  have : m = m' := by
    contrapose! h
    have A : Disjoint (range (c.emb m)) (range (c.emb m')) :=
      c.disjoint (mem_univ m) (mem_univ m') h
    apply disjoint_iff_forall_ne.1 A (mem_range_self r) (mem_range_self r')
  subst this
  simpa using (c.emb_strictMono m).injective h
Injectivity of the Embedding in Ordered Finite Partitions
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$, the embedding function that maps a pair $(m, k)$ (where $m$ is a part index and $k$ is an index within the $m$-th part) to an element of $\{0, \dots, n-1\}$ is injective. In other words, if $(m_1, k_1)$ and $(m_2, k_2)$ map to the same element under this embedding, then $(m_1, k_1) = (m_2, k_2)$.
OrderedFinpartition.emb_ne_emb_of_ne theorem
{i j : Fin c.length} {a : Fin (c.partSize i)} {b : Fin (c.partSize j)} (h : i ≠ j) : c.emb i a ≠ c.emb j b
Full source
lemma emb_ne_emb_of_ne {i j : Fin c.length} {a : Fin (c.partSize i)} {b : Fin (c.partSize j)}
    (h : i ≠ j) : c.emb i a ≠ c.emb j b :=
  c.emb_injective.ne (a₁ := ⟨i, a⟩) (a₂ := ⟨j, b⟩) (by simp [h])
Distinct Parts Embed to Distinct Elements in Ordered Finite Partition
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$, if two part indices $i$ and $j$ are distinct ($i \neq j$), then for any elements $a$ in the $i$-th part and $b$ in the $j$-th part, the embedded elements $c.\text{emb}(i, a)$ and $c.\text{emb}(j, b)$ are also distinct.
OrderedFinpartition.index definition
(j : Fin n) : Fin c.length
Full source
/-- Given `j : Fin n`, the index of the part to which it belongs. -/
noncomputable def index (j : Fin n) : Fin c.length :=
  (c.exists_inverse j).choose.1
Index of the part containing an element in an ordered finite partition
Informal description
For any ordered finite partition \( c \) of \( \{0, \dots, n-1\} \) and any element \( j \in \{0, \dots, n-1\} \), the function returns the index of the part in \( c \) that contains \( j \).
OrderedFinpartition.invEmbedding definition
(j : Fin n) : Fin (c.partSize (c.index j))
Full source
/-- The inverse of `c.emb` for `c : OrderedFinpartition`. It maps `j : Fin n` to the point in
`Fin (c.partSize (c.index j))` which is mapped back to `j` by `c.emb (c.index j)`. -/
noncomputable def invEmbedding (j : Fin n) :
    Fin (c.partSize (c.index j)) := (c.exists_inverse j).choose.2
Preimage under ordered finite partition embedding
Informal description
For an ordered finite partition \( c \) of \( \{0, \dots, n-1\} \) and an element \( j \in \{0, \dots, n-1\} \), the function returns the preimage of \( j \) under the embedding \( c.\text{emb} \) restricted to the part containing \( j \). Specifically, it gives the position \( k \) within the part \( c.\text{index}(j) \) such that \( c.\text{emb}(c.\text{index}(j), k) = j \).
OrderedFinpartition.emb_invEmbedding theorem
(j : Fin n) : c.emb (c.index j) (c.invEmbedding j) = j
Full source
@[simp] lemma emb_invEmbedding (j : Fin n) :
    c.emb (c.index j) (c.invEmbedding j) = j :=
  (c.exists_inverse j).choose_spec
Embedding-Inverse Embedding Identity for Ordered Finite Partitions
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$ and any element $j \in \{0, \dots, n-1\}$, the embedding function $c.\text{emb}$ maps the pair $(m, k)$ to $j$, where $m$ is the index of the part containing $j$ and $k$ is the position of $j$ within that part. In other words, $c.\text{emb}(c.\text{index}(j), c.\text{invEmbedding}(j)) = j$.
OrderedFinpartition.equivSigma definition
: ((i : Fin c.length) × Fin (c.partSize i)) ≃ Fin n
Full source
/-- An ordered finpartition gives an equivalence between `Fin n` and the disjoint union of the
parts, each of them parameterized by `Fin (c.partSize i)`. -/
noncomputable def equivSigma : ((i : Fin c.length) × Fin (c.partSize i)) ≃ Fin n where
  toFun p := c.emb p.1 p.2
  invFun i := ⟨c.index i, c.invEmbedding i⟩
  right_inv _ := by simp
  left_inv _ := by apply c.emb_injective; simp
Equivalence between Disjoint Union of Partition Parts and Original Set
Informal description
For an ordered finite partition \( c \) of the set \( \{0, \dots, n-1\} \), the function `OrderedFinpartition.equivSigma` provides an equivalence between the disjoint union of the parts (each parameterized by `Fin (c.partSize i)`) and the original set \( \text{Fin } n \). Specifically, it consists of: - A forward map that takes a pair \((i, j)\) where \( i \) is the index of a part and \( j \) is an element within that part, and returns the corresponding element in \( \text{Fin } n \) via the embedding \( c.\text{emb} \). - An inverse map that takes an element \( k \in \text{Fin } n \) and returns the pair \((i, j)\) where \( i \) is the index of the part containing \( k \) and \( j \) is the position of \( k \) within that part. This equivalence is bijective, meaning the forward and inverse maps are mutual inverses.
OrderedFinpartition.prod_sigma_eq_prod theorem
{α : Type*} [CommMonoid α] (v : Fin n → α) : ∏ (m : Fin c.length), ∏ (r : Fin (c.partSize m)), v (c.emb m r) = ∏ i, v i
Full source
@[to_additive] lemma prod_sigma_eq_prod {α : Type*} [CommMonoid α] (v : Fin n → α) :
    ∏ (m : Fin c.length), ∏ (r : Fin (c.partSize m)), v (c.emb m r) = ∏ i, v i := by
  rw [Finset.prod_sigma']
  exact Fintype.prod_equiv c.equivSigma _ _ (fun p ↦ rfl)
Product Equality over Ordered Finite Partition and Original Set in a Commutative Monoid
Informal description
Let $\alpha$ be a commutative monoid and $v \colon \{0, \dots, n-1\} \to \alpha$ be a function. For any ordered finite partition $c$ of $\{0, \dots, n-1\}$, the product of $v$ over all elements in each part of $c$ equals the product of $v$ over all elements of $\{0, \dots, n-1\}$. More precisely, for each part $I_m$ of $c$ (where $m \in \{0, \dots, k-1\}$ and $k$ is the number of parts), let $i_m$ be the size of $I_m$. Then: \[ \prod_{m=0}^{k-1} \prod_{r=0}^{i_m-1} v(c.\text{emb}(m, r)) = \prod_{i=0}^{n-1} v(i), \] where $c.\text{emb}(m, r)$ is the embedding of the $r$-th element of the $m$-th part into $\{0, \dots, n-1\}$.
OrderedFinpartition.length_pos theorem
(h : 0 < n) : 0 < c.length
Full source
lemma length_pos (h : 0 < n) : 0 < c.length := Nat.zero_lt_of_lt (c.index ⟨0, h⟩).2
Nonzero Length of Ordered Finite Partition for Positive $n$
Informal description
For any ordered finite partition $c$ of the set $\{0, \dots, n-1\}$ where $n > 0$, the number of parts in the partition is strictly positive, i.e., $c.\text{length} > 0$.
OrderedFinpartition.neZero_length theorem
[NeZero n] (c : OrderedFinpartition n) : NeZero c.length
Full source
lemma neZero_length [NeZero n] (c : OrderedFinpartition n) : NeZero c.length :=
  ⟨(c.length_pos pos').ne'⟩
Nonzero Length Property of Ordered Finite Partitions for Nonzero $n$
Informal description
For any ordered finite partition $c$ of the set $\{0, \dots, n-1\}$ where $n$ is a nonzero natural number, the number of parts in the partition is also nonzero, i.e., $c.\text{length} \neq 0$.
OrderedFinpartition.neZero_partSize theorem
(c : OrderedFinpartition n) (i : Fin c.length) : NeZero (c.partSize i)
Full source
lemma neZero_partSize (c : OrderedFinpartition n) (i : Fin c.length) : NeZero (c.partSize i) :=
  .of_pos (c.partSize_pos i)
Nonzero Size of Parts in Ordered Finite Partition
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$ and any index $i$ in the range of the partition's length, the size of the $i$-th part of $c$ is nonzero, i.e., $c.\text{partSize}(i) \neq 0$.
OrderedFinpartition.instUniqueOne instance
: Unique (OrderedFinpartition 1)
Full source
instance instUniqueOne : Unique (OrderedFinpartition 1) where
  uniq c := by
    have h₁ : c.length = 1 := le_antisymm c.length_le (c.length_pos Nat.zero_lt_one)
    have h₂ (i) : c.partSize i = 1 := le_antisymm (c.partSize_le _) (c.partSize_pos _)
    have h₃ (i j) : c.emb i j = 0 := Subsingleton.elim _ _
    rcases c with ⟨length, partSize, _, emb, _, _, _, _⟩
    subst h₁
    obtain rfl : partSize = fun _ ↦ 1 := funext h₂
    simpa [OrderedFinpartition.ext_iff, funext_iff, Fin.forall_fin_one] using h₃ _ _
Uniqueness of the Ordered Finite Partition of $\{0\}$
Informal description
For the set $\{0\}$, there is exactly one ordered finite partition, which is the trivial partition consisting of the single element $\{0\}$.
OrderedFinpartition.emb_zero theorem
[NeZero n] : c.emb (c.index 0) 0 = 0
Full source
lemma emb_zero [NeZero n] : c.emb (c.index 0) 0 = 0 := by
  apply le_antisymm _ (Fin.zero_le' _)
  conv_rhs => rw [← c.emb_invEmbedding 0]
  apply (c.emb_strictMono _).monotone (Fin.zero_le' _)
Embedding of Zero in Ordered Finite Partition
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$ where $n$ is a nonzero natural number, the embedding function $c.\text{emb}$ maps the pair $(c.\text{index}(0), 0)$ to $0$. In other words, the first element of the part containing $0$ is mapped to $0$ itself.
OrderedFinpartition.partSize_eq_one_of_range_emb_eq_singleton theorem
(c : OrderedFinpartition n) {i : Fin c.length} {j : Fin n} (hc : range (c.emb i) = { j }) : c.partSize i = 1
Full source
lemma partSize_eq_one_of_range_emb_eq_singleton
    (c : OrderedFinpartition n) {i : Fin c.length} {j : Fin n}
    (hc : range (c.emb i) = {j}) :
    c.partSize i = 1 := by
  have : Fintype.card (range (c.emb i)) = Fintype.card (Fin (c.partSize i)) :=
    card_range_of_injective (c.emb_strictMono i).injective
  simpa [hc] using this.symm
Singleton Range Implies Part Size One in Ordered Finite Partition
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$, if the range of the embedding function $c.\text{emb}$ for some part index $i$ is a singleton $\{j\}$, then the size of the $i$-th part is equal to $1$.
OrderedFinpartition.one_lt_partSize_index_zero theorem
(c : OrderedFinpartition (n + 1)) (hc : range (c.emb 0) ≠ {0}) : 1 < c.partSize (c.index 0)
Full source
/-- If the left-most part is not `{0}`, then the part containing `0` has at least two elements:
either because it's the left-most part, and then it's not just `0` by assumption, or because it's
not the left-most part and then, by increasingness of maximal elements in parts, it contains
a positive element. -/
lemma one_lt_partSize_index_zero (c : OrderedFinpartition (n + 1)) (hc : rangerange (c.emb 0) ≠ {0}) :
    1 < c.partSize (c.index 0) := by
  have : c.partSize (c.index 0) = Nat.card (range (c.emb (c.index 0))) := by
    rw [Nat.card_range_of_injective (c.emb_strictMono _).injective]; simp
  rw [this]
  rcases eq_or_ne (c.index 0) 0 with h | h
  · rw [← h] at hc
    have : {0}{0} ⊂ range (c.emb (c.index 0)) := by
      apply ssubset_of_subset_of_ne ?_ hc.symm
      simpa only [singleton_subset_iff, mem_range] using ⟨0, emb_zero c⟩
    simpa using Set.Finite.card_lt_card (finite_range _) this
  · apply one_lt_two.trans_le
    have : {c.emb (c.index 0) 0,
        c.emb (c.index 0) ⟨c.partSize (c.index 0) - 1, Nat.sub_one_lt_of_lt (c.partSize_pos _)⟩}{c.emb (c.index 0) 0,
        c.emb (c.index 0) ⟨c.partSize (c.index 0) - 1, Nat.sub_one_lt_of_lt (c.partSize_pos _)⟩}
          ⊆ range (c.emb (c.index 0)) := by simp [insert_subset]
    simp only [emb_zero] at this
    convert Nat.card_mono Subtype.finite this
    simp only [Nat.card_eq_fintype_card, Fintype.card_ofFinset, toFinset_singleton]
    apply (Finset.card_pair ?_).symm
    exact ((Fin.zero_le _).trans_lt (c.parts_strictMono ((pos_iff_ne_zero' (c.index 0)).mpr h))).ne
Size of Part Containing Zero is Greater Than One When Not Singleton $\{0\}$ in Ordered Finite Partition
Informal description
Let $c$ be an ordered finite partition of $\{0, \dots, n\}$ such that the range of the embedding function for the first part is not the singleton $\{0\}$. Then the size of the part containing $0$ is strictly greater than $1$, i.e., $1 < c.\text{partSize}(c.\text{index}(0))$.
OrderedFinpartition.extendLeft definition
(c : OrderedFinpartition n) : OrderedFinpartition (n + 1)
Full source
/-- Extend an ordered partition of `n` entries, by adding a new singleton part to the left. -/
@[simps -fullyApplied length partSize]
def extendLeft (c : OrderedFinpartition n) : OrderedFinpartition (n + 1) where
  length := c.length + 1
  partSize := Fin.cons 1 c.partSize
  partSize_pos := Fin.cases (by simp) (by simp [c.partSize_pos])
  emb := Fin.cases (fun _ ↦ 0) (fun m ↦ Fin.succFin.succ ∘ c.emb m)
  emb_strictMono := by
    refine Fin.cases ?_ (fun i ↦ ?_)
    · exact @Subsingleton.strictMono _ _ _ _ (by simp; infer_instance) _
    · exact strictMono_succ.comp (c.emb_strictMono i)
  parts_strictMono i j hij := by
    induction j using Fin.induction with
    | zero => simp at hij
    | succ j => induction i using Fin.induction with
      | zero => simp
      | succ i =>
        simp only [cons_succ, cases_succ, comp_apply, succ_lt_succ_iff]
        exact c.parts_strictMono (by simpa using hij)
  disjoint i hi j hj hij := by
    wlog h : j < i generalizing i j
    · exact .symm
        (this j (mem_univ j) i (mem_univ i) hij.symm (lt_of_le_of_ne (le_of_not_lt h) hij))
    induction i using Fin.induction with
    | zero => simp at h
    | succ i =>
      induction j using Fin.induction with
      | zero =>
        simp only [onFun, cases_succ, cases_zero]
        apply Set.disjoint_iff_forall_ne.2
        simp only [mem_range, comp_apply, exists_prop', cons_zero, ne_eq, and_imp,
          Nonempty.forall, forall_const, forall_eq', forall_exists_index, forall_apply_eq_imp_iff]
        exact fun _ ↦ succ_ne_zero _
      | succ j =>
        simp only [onFun, cases_succ]
        apply Set.disjoint_iff_forall_ne.2
        simp only [mem_range, comp_apply, ne_eq, forall_exists_index, forall_apply_eq_imp_iff,
          succ_inj]
        intro a b
        apply c.emb_ne_emb_of_ne (by simpa using hij)
  cover := by
    refine Fin.cases ?_ (fun i ↦ ?_)
    · simp only [mem_iUnion, mem_range]
      exact ⟨0, ⟨0, by simp⟩, by simp⟩
    · simp only [mem_iUnion, mem_range]
      exact ⟨Fin.succ (c.index i), Fin.cast (by simp) (c.invEmbedding i), by simp⟩
Left extension of an ordered finite partition
Informal description
Given an ordered finite partition $c$ of the set $\{0, \dots, n-1\}$, the function `extendLeft` constructs a new ordered finite partition of $\{0, \dots, n\}$ by adding the singleton $\{0\}$ as a new leftmost part. The remaining parts are obtained by shifting the original parts of $c$ by 1, i.e., each element $i$ in the original partition becomes $i+1$ in the new partition.
OrderedFinpartition.range_extendLeft_zero theorem
(c : OrderedFinpartition n) : range (c.extendLeft.emb 0) = {0}
Full source
@[simp] lemma range_extendLeft_zero (c : OrderedFinpartition n) :
    range (c.extendLeft.emb 0) = {0} := by
  simp [extendLeft]
  apply @range_const _ _ (by simp; infer_instance)
Range of First Part Embedding in Left-Extended Partition is $\{0\}$
Informal description
For any ordered finite partition $c$ of the set $\{0, \dots, n-1\}$, the range of the embedding function for the first part of the left-extended partition $c.\text{extendLeft}$ is the singleton set $\{0\}$.
OrderedFinpartition.extendMiddle definition
(c : OrderedFinpartition n) (k : Fin c.length) : OrderedFinpartition (n + 1)
Full source
/-- Extend an ordered partition of `n` entries, by adding to the `i`-th part a new point to the
left. -/
@[simps -fullyApplied length partSize]
def extendMiddle (c : OrderedFinpartition n) (k : Fin c.length) : OrderedFinpartition (n + 1) where
  length := c.length
  partSize := update c.partSize k (c.partSize k + 1)
  partSize_pos m := by
    rcases eq_or_ne m k with rfl | hm
    · simp
    · simpa [hm] using c.partSize_pos m
  emb := by
    intro m
    by_cases h : m = k
    · have : update c.partSize k (c.partSize k + 1) m = c.partSize k + 1 := by rw [h]; simp
      exact Fin.casesFin.cases 0 (succ ∘ c.emb k) ∘ Fin.cast this
    · have : update c.partSize k (c.partSize k + 1) m = c.partSize m := by simp [h]
      exact succsucc ∘ c.emb m ∘ Fin.cast this
  emb_strictMono := by
    intro m
    rcases eq_or_ne m k with rfl | hm
    · suffices ∀ (a' b' : Fin (c.partSize m + 1)), a' < b' →
          (cases (motive := fun _ ↦ Fin (n + 1)) 0 (succsucc ∘ c.emb m)) a' <
          (cases (motive := fun _ ↦ Fin (n + 1)) 0 (succsucc ∘ c.emb m)) b' by
        simp only [↓reduceDIte, comp_apply]
        intro a b hab
        exact this _ _ hab
      intro a' b' h'
      induction b' using Fin.induction with
      | zero => simp at h'
      | succ b =>
        induction a' using Fin.induction with
        | zero => simp
        | succ a' =>
          simp only [cases_succ, comp_apply, succ_lt_succ_iff]
          exact c.emb_strictMono m (by simpa using h')
    · simp only [hm, ↓reduceDIte]
      exact strictMono_succ.comp ((c.emb_strictMono m).comp (by exact fun ⦃a b⦄ h ↦ h))
  parts_strictMono := by
    convert strictMono_succ.comp c.parts_strictMono with m
    rcases eq_or_ne m k with rfl | hm
    · simp only [↓reduceDIte, update_self, add_tsub_cancel_right, comp_apply, cast_mk,
        Nat.succ_eq_add_one]
      let a : Fin (c.partSize m + 1) := ⟨c.partSize m, lt_add_one (c.partSize m)⟩
      let b : Fin (c.partSize m) := ⟨c.partSize m - 1, Nat.sub_one_lt_of_lt (c.partSize_pos m)⟩
      change (cases (motive := fun _ ↦ Fin (n + 1)) 0 (succsucc ∘ c.emb m)) a = succ (c.emb m b)
      have : a = succ b := by
        simpa [a, b, succ] using (Nat.sub_eq_iff_eq_add (c.partSize_pos m)).mp rfl
      simp [this]
    · simp [hm]
  disjoint i hi j hj hij := by
    wlog h : i ≠ k generalizing i j
    · apply Disjoint.symm
        (this j (mem_univ j) i (mem_univ i) hij.symm ?_)
      simp only [ne_eq, Decidable.not_not] at h
      simpa [h] using hij.symm
    rcases eq_or_ne j k with rfl | hj
    · simp only [onFun, ↓reduceDIte, Ne.symm hij]
      suffices ∀ (a' : Fin (c.partSize i)) (b' : Fin (c.partSize j + 1)),
          succsucc (c.emb i a') ≠ cases (motive := fun _ ↦ Fin (n + 1)) 0 (succ ∘ c.emb j) b' by
        apply Set.disjoint_iff_forall_ne.2
        simp only [hij, ↓reduceDIte, mem_range, comp_apply, ne_eq, forall_exists_index,
          forall_apply_eq_imp_iff]
        intro a b
        apply this
      intro a' b'
      induction b' using Fin.induction with
      | zero => simp
      | succ b' =>
        simp only [Nat.succ_eq_add_one, cases_succ, comp_apply, ne_eq, succ_inj]
        apply c.emb_ne_emb_of_ne hij
    · simp only [onFun, h, ↓reduceDIte, hj]
      apply Set.disjoint_iff_forall_ne.2
      simp only [mem_range, comp_apply, ne_eq, forall_exists_index, forall_apply_eq_imp_iff,
        succ_inj]
      intro a b
      apply c.emb_ne_emb_of_ne hij
  cover := by
    refine Fin.cases ?_ (fun i ↦ ?_)
    · simp only [mem_iUnion, mem_range]
      exact ⟨k, ⟨0, by simp⟩, by simp⟩
    · simp only [mem_iUnion, mem_range]
      rcases eq_or_ne (c.index i) k with rfl | hi
      · have A : update c.partSize (c.index i) (c.partSize (c.index i) + 1) (c.index i) =
          c.partSize (c.index i) + 1 := by simp
        exact ⟨c.index i, (succ (c.invEmbedding i)).cast A.symm , by simp⟩
      · have A : update c.partSize k (c.partSize k + 1) (c.index i) = c.partSize (c.index i) := by
          simp [hi]
        exact ⟨c.index i, (c.invEmbedding i).cast A.symm, by simp [hi]⟩
Extension of an ordered finite partition by adding an element to a specified part
Informal description
Given an ordered finite partition \( c \) of the set \( \{0, \dots, n-1\} \) and an index \( k \) of one of its parts, the function `OrderedFinpartition.extendMiddle` extends the partition to \( \{0, \dots, n\} \) by adding the new element \( n \) to the \( k \)-th part of \( c \). The resulting partition has the same number of parts as \( c \), but the size of the \( k \)-th part is increased by one. The embedding functions for the parts are adjusted accordingly to maintain the ordering and injectivity properties.
OrderedFinpartition.index_extendMiddle_zero theorem
(c : OrderedFinpartition n) (i : Fin c.length) : (c.extendMiddle i).index 0 = i
Full source
lemma index_extendMiddle_zero (c : OrderedFinpartition n) (i : Fin c.length) :
    (c.extendMiddle i).index 0 = i := by
  have : (c.extendMiddle i).emb i 0 = 0 := by simp [extendMiddle]
  conv_rhs at this => rw [← (c.extendMiddle i).emb_invEmbedding 0]
  contrapose! this
  exact (c.extendMiddle i).emb_ne_emb_of_ne (Ne.symm this)
Index of Zero in Middle-Extended Partition Equals Original Part Index
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$ and any index $i$ of a part in $c$, the index of the element $0$ in the extended partition $c.\text{extendMiddle}\,i$ is equal to $i$.
OrderedFinpartition.range_emb_extendMiddle_ne_singleton_zero theorem
(c : OrderedFinpartition n) (i j : Fin c.length) : range ((c.extendMiddle i).emb j) ≠ {0}
Full source
lemma range_emb_extendMiddle_ne_singleton_zero (c : OrderedFinpartition n) (i j : Fin c.length) :
    rangerange ((c.extendMiddle i).emb j) ≠ {0} := by
  intro h
  rcases eq_or_ne j i with rfl | hij
  · have : Fin.succFin.succ (c.emb j 0) ∈ ({0} : Set (Fin n.succ)) := by
      rw [← h]
      simp only [Nat.succ_eq_add_one, mem_range]
      have A : (c.extendMiddle j).partSize j = c.partSize j + 1 := by simp [extendMiddle]
      refine ⟨Fin.cast A.symm (succ 0), ?_⟩
      simp only [extendMiddle, ↓reduceDIte, comp_apply, cast_trans, cast_eq_self, cases_succ]
    simp only [mem_singleton_iff] at this
    exact Fin.succ_ne_zero _ this
  · have : (c.extendMiddle i).emb j 0 ∈ range ((c.extendMiddle i).emb j) :=
      mem_range_self 0
    rw [h] at this
    simp only [extendMiddle, hij, ↓reduceDIte, comp_apply, cast_zero, mem_singleton_iff] at this
    exact Fin.succ_ne_zero _ this
Non-singleton Range Property for Extended Partition Embeddings
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$ and any indices $i, j$ of its parts, the range of the embedding function for the $j$-th part in the extended partition $c.\text{extendMiddle}\,i$ is not the singleton set $\{0\}$.
OrderedFinpartition.extend definition
(c : OrderedFinpartition n) (i : Option (Fin c.length)) : OrderedFinpartition (n + 1)
Full source
/-- Extend an ordered partition of `n` entries, by adding singleton to the left or appending it
to one of the existing part. -/
def extend (c : OrderedFinpartition n) (i : Option (Fin c.length)) : OrderedFinpartition (n + 1) :=
  match i with
  | none => c.extendLeft
  | some i => c.extendMiddle i
Extension of an ordered finite partition by adding a new element
Informal description
Given an ordered finite partition \( c \) of the set \( \{0, \dots, n-1\} \) and an optional index \( i \) (either `none` or `some k` where \( k \) is an index of a part in \( c \)), the function `OrderedFinpartition.extend` constructs a new ordered finite partition of \( \{0, \dots, n\} \) as follows: - If \( i = \text{none} \), it adds the singleton \( \{0\} \) as a new leftmost part (equivalent to `extendLeft`). - If \( i = \text{some } k \), it adds the new element \( 0 \) to the \( k \)-th part of \( c \) (equivalent to `extendMiddle`).
OrderedFinpartition.extend_none theorem
(c : OrderedFinpartition n) : c.extend none = c.extendLeft
Full source
@[simp] lemma extend_none (c : OrderedFinpartition n) : c.extend none = c.extendLeft := rfl
Extension with None Equals Left Extension for Ordered Finite Partitions
Informal description
For any ordered finite partition $c$ of the set $\{0, \dots, n-1\}$, the extension of $c$ with the option `none` is equal to the left extension of $c$, i.e., $c.\text{extend}\, \text{none} = c.\text{extendLeft}$.
OrderedFinpartition.extend_some theorem
(c : OrderedFinpartition n) (i : Fin c.length) : c.extend i = c.extendMiddle i
Full source
@[simp]
lemma extend_some (c : OrderedFinpartition n) (i : Fin c.length) : c.extend i = c.extendMiddle i :=
  rfl
Equality of General and Specific Partition Extensions
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$ and any index $i$ of a part in $c$, the extension of $c$ by adding the new element $n$ to the $i$-th part (via `extend`) is equal to the extension obtained by specifically adding $n$ to the $i$-th part (via `extendMiddle`).
OrderedFinpartition.eraseLeft definition
(c : OrderedFinpartition (n + 1)) (hc : range (c.emb 0) = {0}) : OrderedFinpartition n
Full source
/-- Given an ordered finpartition of `n+1`, with a leftmost atom equal to `{0}`, remove this
atom to form an ordered finpartition of `n`. -/
def eraseLeft (c : OrderedFinpartition (n + 1)) (hc : range (c.emb 0) = {0}) :
    OrderedFinpartition n where
  length := c.length - 1
  partSize := by
    have : c.length - 1 + 1 = c.length := Nat.sub_add_cancel (c.length_pos (Nat.zero_lt_succ n))
    exact fun i ↦ c.partSize (Fin.cast this (succ i))
  partSize_pos i := c.partSize_pos _
  emb i j := by
    have : c.length - 1 + 1 = c.length := Nat.sub_add_cancel (c.length_pos (Nat.zero_lt_succ n))
    refine Fin.pred (c.emb (Fin.cast this (succ i)) j) ?_
    have := c.disjoint (mem_univ (Fin.cast this (succ i))) (mem_univ 0) (ne_of_beq_false rfl)
    exact Set.disjoint_iff_forall_ne.1 this (by simp) (by simp only [mem_singleton_iff, hc])
  emb_strictMono i a b hab := by
    simp only [pred_lt_pred_iff, Nat.succ_eq_add_one]
    apply c.emb_strictMono _ hab
  parts_strictMono := by
    intro i j hij
    simp only [pred_lt_pred_iff, Nat.succ_eq_add_one]
    apply c.parts_strictMono (cast_strictMono _ (strictMono_succ hij))
  disjoint i _ j _ hij := by
    apply Set.disjoint_iff_forall_ne.2
    simp only [mem_range, ne_eq, forall_exists_index, forall_apply_eq_imp_iff, pred_inj]
    intro a b
    exact c.emb_ne_emb_of_ne ((cast_injective _).ne (by simpa using hij))
  cover x := by
    simp only [mem_iUnion, mem_range]
    obtain ⟨i, j, hij⟩ : ∃ (i : Fin c.length), ∃ (j : Fin (c.partSize i)), c.emb i j = succ x :=
      ⟨c.index (succ x), c.invEmbedding (succ x), by simp⟩
    have A : c.length = c.length - 1 + 1 :=
      (Nat.sub_add_cancel (c.length_pos (Nat.zero_lt_succ n))).symm
    have i_ne : i ≠ 0 := by
      intro h
      have : succsucc x ∈ range (c.emb i) := by rw [← hij]; apply mem_range_self
      rw [h, hc, mem_singleton_iff] at this
      exact Fin.succ_ne_zero _ this
    refine ⟨pred (Fin.cast A i) (by simpa using i_ne), Fin.cast (by simp) j, ?_⟩
    have : x = pred (succ x) (succ_ne_zero x) := rfl
    rw [this]
    congr
    rw [← hij]
    congr 1
    · simp
    · simp [Fin.heq_ext_iff]
Removal of leftmost singleton part from an ordered finite partition
Informal description
Given an ordered finite partition $c$ of the set $\{0, \dots, n\}$ where the leftmost part is the singleton $\{0\}$, the function `eraseLeft` removes this singleton part to produce an ordered finite partition of $\{1, \dots, n\}$. The resulting partition has one fewer part than the original partition, and the remaining parts are reindexed accordingly.
OrderedFinpartition.eraseMiddle definition
(c : OrderedFinpartition (n + 1)) (hc : range (c.emb 0) ≠ {0}) : OrderedFinpartition n
Full source
/-- Given an ordered finpartition of `n+1`, with a leftmost atom different from `{0}`, remove `{0}`
from the atom that contains it, to form an ordered finpartition of `n`. -/
def eraseMiddle (c : OrderedFinpartition (n + 1)) (hc : rangerange (c.emb 0) ≠ {0}) :
    OrderedFinpartition n where
  length := c.length
  partSize := update c.partSize (c.index 0) (c.partSize (c.index 0) - 1)
  partSize_pos i := by
    rcases eq_or_ne i (c.index 0) with rfl | hi
    · simpa using c.one_lt_partSize_index_zero hc
    · simp only [ne_eq, hi, not_false_eq_true, update_of_ne]
      exact c.partSize_pos i
  emb i j := by
    by_cases h : i = c.index 0
    · refine Fin.pred (c.emb i (Fin.cast ?_ (succ j))) ?_
      · rw [h]
        simpa using Nat.sub_add_cancel (c.partSize_pos (c.index 0))
      · have : 0 ≤ c.emb i 0 := Fin.zero_le _
        exact (this.trans_lt (c.emb_strictMono _ (succ_pos _))).ne'
    · refine Fin.pred (c.emb i (Fin.cast ?_ j)) ?_
      · simp [h]
      · conv_rhs => rw [← c.emb_invEmbedding 0]
        exact c.emb_ne_emb_of_ne h
  emb_strictMono i a b hab := by
    rcases eq_or_ne i (c.index 0) with rfl | hi
    · simp only [↓reduceDIte, Nat.succ_eq_add_one, pred_lt_pred_iff]
      exact (c.emb_strictMono _).comp (cast_strictMono _) (by simpa using hab)
    · simp only [hi, ↓reduceDIte, pred_lt_pred_iff, Nat.succ_eq_add_one]
      exact (c.emb_strictMono _).comp (cast_strictMono _) hab
  parts_strictMono i j hij := by
    simp only [Fin.lt_iff_val_lt_val]
    rw [← Nat.add_lt_add_iff_right (k := 1)]
    convert Fin.lt_iff_val_lt_val.1 (c.parts_strictMono hij)
    · rcases eq_or_ne i (c.index 0) with rfl | hi
      · simp only [↓reduceDIte, Nat.succ_eq_add_one, update_self, succ_mk, cast_mk, coe_pred]
        have A := c.one_lt_partSize_index_zero hc
        rw [Nat.sub_add_cancel]
        · congr; omega
        · rw [Order.one_le_iff_pos]
          conv_lhs => rw [show (0 : ) = c.emb (c.index 0) 0 by simp [emb_zero]]
          rw [← lt_iff_val_lt_val]
          apply c.emb_strictMono
          simp [lt_iff_val_lt_val]
      · simp only [hi, ↓reduceDIte, ne_eq, not_false_eq_true, update_of_ne, cast_mk, coe_pred]
        apply Nat.sub_add_cancel
        have : c.emb i ⟨c.partSize i - 1, Nat.sub_one_lt_of_lt (c.partSize_pos i)⟩
            ≠ c.emb (c.index 0) 0 := c.emb_ne_emb_of_ne hi
        simp only [c.emb_zero, ne_eq, ← val_eq_val, val_zero] at this
        omega
    · rcases eq_or_ne j (c.index 0) with rfl | hj
      · simp only [↓reduceDIte, Nat.succ_eq_add_one, update_self, succ_mk, cast_mk, coe_pred]
        have A := c.one_lt_partSize_index_zero hc
        rw [Nat.sub_add_cancel]
        · congr; omega
        · rw [Order.one_le_iff_pos]
          conv_lhs => rw [show (0 : ) = c.emb (c.index 0) 0 by simp [emb_zero]]
          rw [← lt_iff_val_lt_val]
          apply c.emb_strictMono
          simp [lt_iff_val_lt_val]
      · simp only [hj, ↓reduceDIte, ne_eq, not_false_eq_true, update_of_ne, cast_mk, coe_pred]
        apply Nat.sub_add_cancel
        have : c.emb j ⟨c.partSize j - 1, Nat.sub_one_lt_of_lt (c.partSize_pos j)⟩
            ≠ c.emb (c.index 0) 0 := c.emb_ne_emb_of_ne hj
        simp only [c.emb_zero, ne_eq, ← val_eq_val, val_zero] at this
        omega
  disjoint i _ j _ hij := by
    wlog h : i ≠ c.index 0 generalizing i j
    · apply Disjoint.symm
        (this j (mem_univ j) i (mem_univ i) hij.symm ?_)
      simp only [ne_eq, Decidable.not_not] at h
      simpa [h] using hij.symm
    rcases eq_or_ne j (c.index 0) with rfl | hj
    · simp only [onFun, hij, ↓reduceDIte, Nat.succ_eq_add_one]
      apply Set.disjoint_iff_forall_ne.2
      simp only [mem_range, ne_eq, forall_exists_index, forall_apply_eq_imp_iff, pred_inj]
      intro a b
      exact c.emb_ne_emb_of_ne hij
    · simp only [onFun, h, ↓reduceDIte, hj]
      apply Set.disjoint_iff_forall_ne.2
      simp only [mem_range, ne_eq, forall_exists_index, forall_apply_eq_imp_iff, pred_inj]
      intro a b
      exact c.emb_ne_emb_of_ne hij
  cover x := by
    simp only [mem_iUnion, mem_range]
    obtain ⟨i, j, hij⟩ : ∃ (i : Fin c.length), ∃ (j : Fin (c.partSize i)), c.emb i j = succ x :=
      ⟨c.index (succ x), c.invEmbedding (succ x), by simp⟩
    rcases eq_or_ne i (c.index 0) with rfl | hi
    · refine ⟨c.index 0, ?_⟩
      have j_ne : j ≠ 0 := by
        rintro rfl
        simp only [c.emb_zero] at hij
        exact (Fin.succ_ne_zero _).symm hij
      have je_ne' : (j : ℕ) ≠ 0 := by simpa
      simp only [↓reduceDIte, Nat.succ_eq_add_one]
      have A : c.partSize (c.index 0) - 1 + 1 = c.partSize (c.index 0) :=
        Nat.sub_add_cancel (c.partSize_pos _)
      have B : update c.partSize (c.index 0) (c.partSize (c.index 0) - 1) (c.index 0) =
        c.partSize (c.index 0) - 1 := by simp
      refine ⟨Fin.cast B.symm (pred (Fin.cast A.symm j) ?_), ?_⟩
      · simpa using j_ne
      · have : x = pred (succ x) (succ_ne_zero x) := rfl
        rw [this]
        simp only [pred_inj, ← hij]
        congr 1
        rw [← val_eq_val]
        simp only [coe_cast, val_succ, coe_pred]
        omega
    · have A : update c.partSize (c.index 0) (c.partSize (c.index 0) - 1) i = c.partSize i := by
        simp [hi]
      exact ⟨i, Fin.cast A.symm j, by simp [hi, hij]⟩
Removal of a non-isolated element from an ordered finite partition
Informal description
Given an ordered finite partition \( c \) of \( \{0, \dots, n\} \) where the element \( 0 \) is not an isolated part (i.e., the range of the embedding for the part containing \( 0 \) is not the singleton \( \{0\} \)), the function `eraseMiddle` constructs a new ordered finite partition of \( \{0, \dots, n-1\} \) by removing \( 0 \) from its part. Specifically: - The number of parts remains the same as in \( c \). - The size of the part that contained \( 0 \) is decreased by 1. - The sizes of all other parts remain unchanged. - The embeddings are adjusted to reflect the removal of \( 0 \), ensuring the new partition maintains the required ordering and disjointness properties.
OrderedFinpartition.extendEquiv definition
(n : ℕ) : ((c : OrderedFinpartition n) × Option (Fin c.length)) ≃ OrderedFinpartition (n + 1)
Full source
/-- Extending the ordered partitions of `Fin n` bijects with the ordered partitions
of `Fin (n+1)`. -/
@[simps apply]
def extendEquiv (n : ) :
    ((c : OrderedFinpartition n) × Option (Fin c.length)) ≃ OrderedFinpartition (n + 1) where
  toFun c := c.1.extend c.2
  invFun c := if h : range (c.emb 0) = {0} then ⟨c.eraseLeft h, none⟩ else
    ⟨c.eraseMiddle h, some (c.index 0)⟩
  left_inv := by
    rintro ⟨c, o⟩
    match o with
    | none =>
      simp only [extend, range_extendLeft_zero, ↓reduceDIte, Sigma.mk.inj_iff, heq_eq_eq,
        and_true]
      rfl
    | some i =>
      simp only [extend, range_emb_extendMiddle_ne_singleton_zero, ↓reduceDIte,
        Sigma.mk.inj_iff, heq_eq_eq, and_true, eraseMiddle, Nat.succ_eq_add_one,
        index_extendMiddle_zero]
      ext
      · rfl
      · simp only [Nat.succ_eq_add_one, ne_eq, id_eq, heq_eq_eq, index_extendMiddle_zero]
        ext j
        rcases eq_or_ne i j with rfl | hij
        · simp [extendMiddle]
        · simp [hij.symm, extendMiddle]
      · refine HEq.symm (hfunext rfl ?_)
        simp only [Nat.succ_eq_add_one, heq_eq_eq, forall_eq']
        intro a
        rcases eq_or_ne a i with rfl | hij
        · refine (Fin.heq_fun_iff ?_).mpr ?_
          · rw [index_extendMiddle_zero]
            simp [extendMiddle]
          · simp [extendMiddle]
        · refine (Fin.heq_fun_iff ?_).mpr ?_
          · rw [index_extendMiddle_zero]
            simp [extendMiddle]
          · simp [extendMiddle, hij]
  right_inv c := by
    by_cases h : range (c.emb 0) = {0}
    · have A : c.length - 1 + 1 = c.length := Nat.sub_add_cancel (c.length_pos (Nat.zero_lt_succ n))
      dsimp only
      rw [dif_pos h]
      simp only [extend, extendLeft, eraseLeft]
      ext
      · exact A
      · refine (Fin.heq_fun_iff A).mpr (fun i ↦ ?_)
        simp [A]
        induction i using Fin.induction with
        | zero => change 1 = c.partSize 0; simp [c.partSize_eq_one_of_range_emb_eq_singleton h]
        | succ i => simp only [cons_succ, val_succ]; rfl
      · refine hfunext (congrArg Fin A) ?_
        simp only [id_eq]
        intro i i' h'
        have : i' = Fin.cast A i := eq_of_val_eq (by apply val_eq_val_of_heq h'.symm)
        subst this
        refine (Fin.heq_fun_iff ?_).mpr ?_
        · induction i using Fin.induction with
          | zero => simp [c.partSize_eq_one_of_range_emb_eq_singleton h]
          | succ i => simp
        · intro j
          induction i using Fin.induction with
          | zero =>
            simp only [cases_zero, cast_zero, val_eq_zero]
            exact (apply_eq_of_range_eq_singleton h _).symm
          | succ i => simp
    · dsimp only
      rw [dif_neg h]
      have B : c.partSize (c.index 0) - 1 + 1 = c.partSize (c.index 0) :=
        Nat.sub_add_cancel (c.partSize_pos (c.index 0))
      simp only [extend, extendMiddle, eraseMiddle, Nat.succ_eq_add_one, ↓reduceDIte]
      ext
      · rfl
      · simp only [update_self, update_idem, heq_eq_eq, update_eq_self_iff, B]
      · refine hfunext rfl ?_
        simp only [heq_eq_eq, forall_eq']
        intro i
        refine ((Fin.heq_fun_iff ?_).mpr ?_).symm
        · simp only [update_self, B, update_idem, update_eq_self]
        · intro j
          rcases eq_or_ne i (c.index 0) with rfl | hi
          · simp only [↓reduceDIte, comp_apply]
            rcases eq_or_ne j 0 with rfl | hj
            · simpa using c.emb_zero
            · let j' := Fin.pred (j.cast B.symm) (by simpa using hj)
              have : j = (succ j').cast B := by simp [j']
              simp only [this, coe_cast, val_succ, cast_mk, cases_succ', comp_apply, succ_mk,
                Nat.succ_eq_add_one, succ_pred]
              rfl
          · simp [hi]
Bijection between extended ordered partitions and partitions of $\{0, \dots, n\}$
Informal description
The equivalence `OrderedFinpartition.extendEquiv` establishes a bijection between the disjoint union of all ordered finite partitions of $\{0, \dots, n-1\}$ paired with an optional index (either `none` or `some k` where $k$ is an index of a part in the partition) and the set of all ordered finite partitions of $\{0, \dots, n\}$. Specifically: - The forward direction (`toFun`) extends a given partition $c$ of $\{0, \dots, n-1\}$ by either: - Adding $\{0\}$ as a new leftmost part (when the optional index is `none`), or - Adding $0$ to an existing part indexed by $k$ (when the optional index is `some k`). - The inverse direction (`invFun`) shrinks a partition $c$ of $\{0, \dots, n\}$ by either: - Removing the singleton part $\{0\}$ (if it exists), or - Removing $0$ from its containing part (if $\{0\}$ is not a singleton part). This bijection is used in the inductive proof of the Faa di Bruno formula to relate partitions of different sizes.
OrderedFinpartition.applyOrderedFinpartition definition
(p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F) : (Fin n → E) → Fin c.length → F
Full source
/-- Given a formal multilinear series `p`, an ordered partition `c` of `n` and the index `i` of a
block of `c`, we may define a function on `Fin n → E` by picking the variables in the `i`-th block
of `n`, and applying the corresponding coefficient of `p` to these variables. This function is
called `p.applyOrderedFinpartition c v i` for `v : Fin n → E` and `i : Fin c.k`. -/
def applyOrderedFinpartition (p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F) :
    (Fin n → E) → Fin c.length → F :=
  fun v m ↦ p m (v ∘ c.emb m)
Evaluation of a multilinear series along an ordered partition
Informal description
Given a formal multilinear series $p$, an ordered partition $c$ of $\{0, \dots, n-1\}$, and a vector $v : \text{Fin } n \to E$, the function $\text{applyOrderedFinpartition}$ evaluates the $i$-th component of $p$ on the subset of variables $v$ indexed by the $i$-th block of the partition $c$. Specifically, for each $m : \text{Fin } c.\text{length}$, it applies the multilinear map $p_m$ to the composition $v \circ c.\text{emb}_m$, where $c.\text{emb}_m$ is the increasing embedding of $\text{Fin } (c.\text{partSize } m)$ into $\text{Fin } n$ corresponding to the $m$-th block of $c$.
OrderedFinpartition.applyOrderedFinpartition_apply theorem
(p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F) (v : Fin n → E) : c.applyOrderedFinpartition p v = (fun m ↦ p m (v ∘ c.emb m))
Full source
lemma applyOrderedFinpartition_apply (p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F)
    (v : Fin n → E) :
  c.applyOrderedFinpartition p v = (fun m ↦ p m (v ∘ c.emb m)) := rfl
Evaluation Formula for Multilinear Series Along Ordered Partition
Informal description
Let $c$ be an ordered finite partition of $\{0, \dots, n-1\}$, and let $p$ be a family of continuous multilinear maps $p_i : E^{\times c.\text{partSize } i} \to F$ for each $i \in \text{Fin } c.\text{length}$. For any vector $v : \text{Fin } n \to E$, the evaluation of the multilinear series $p$ along the partition $c$ at $v$ is given by: \[ c.\text{applyOrderedFinpartition } p \, v = \left( \lambda m \mapsto p_m (v \circ c.\text{emb}_m) \right), \] where $c.\text{emb}_m$ is the increasing embedding of $\text{Fin } (c.\text{partSize } m)$ into $\text{Fin } n$ corresponding to the $m$-th block of the partition $c$.
OrderedFinpartition.norm_applyOrderedFinpartition_le theorem
(p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F) (v : Fin n → E) (m : Fin c.length) : ‖c.applyOrderedFinpartition p v m‖ ≤ ‖p m‖ * ∏ i : Fin (c.partSize m), ‖v (c.emb m i)‖
Full source
theorem norm_applyOrderedFinpartition_le (p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F)
    (v : Fin n → E) (m : Fin c.length) :
    ‖c.applyOrderedFinpartition p v m‖‖p m‖ * ∏ i : Fin (c.partSize m), ‖v (c.emb m i)‖ :=
  (p m).le_opNorm _
Norm Bound for Multilinear Evaluation Along Ordered Partition
Informal description
Let $c$ be an ordered finite partition of $\{0, \dots, n-1\}$, $p$ a family of continuous multilinear maps $p_i : E^{\times c.\text{partSize } i} \to F$ for each $i \in \text{Fin } c.\text{length}$, and $v : \text{Fin } n \to E$ a vector. Then for any $m \in \text{Fin } c.\text{length}$, the norm of the evaluation of $p$ along the partition $c$ at $v$ satisfies: \[ \|c.\text{applyOrderedFinpartition } p \, v \, m\| \leq \|p_m\| \cdot \prod_{i \in \text{Fin } (c.\text{partSize } m)} \|v (c.\text{emb}_m i)\|. \]
OrderedFinpartition.applyOrderedFinpartition_update_right theorem
(p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F) (j : Fin n) (v : Fin n → E) (z : E) : c.applyOrderedFinpartition p (update v j z) = update (c.applyOrderedFinpartition p v) (c.index j) (p (c.index j) (Function.update (v ∘ c.emb (c.index j)) (c.invEmbedding j) z))
Full source
/-- Technical lemma stating how `c.applyOrderedFinpartition` commutes with updating variables. This
will be the key point to show that functions constructed from `applyOrderedFinpartition` retain
multilinearity. -/
theorem applyOrderedFinpartition_update_right
    (p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F)
    (j : Fin n) (v : Fin n → E) (z : E) :
    c.applyOrderedFinpartition p (update v j z) =
      update (c.applyOrderedFinpartition p v) (c.index j)
        (p (c.index j)
          (Function.update (v ∘ c.emb (c.index j)) (c.invEmbedding j) z)) := by
  ext m
  by_cases h : m = c.index j
  · rw [h]
    simp only [applyOrderedFinpartition, update_self]
    congr
    rw [← Function.update_comp_eq_of_injective]
    · simp
    · exact (c.emb_strictMono (c.index j)).injective
  · simp only [applyOrderedFinpartition, ne_eq, h, not_false_eq_true,
      update_of_ne]
    congr
    apply Function.update_comp_eq_of_not_mem_range
    have A : Disjoint (range (c.emb m)) (range (c.emb (c.index j))) :=
      c.disjoint (mem_univ m) (mem_univ (c.index j)) h
    have : j ∈ range (c.emb (c.index j)) := mem_range.2 ⟨c.invEmbedding j, by simp⟩
    exact Set.disjoint_right.1 A this
Update Rule for Multilinear Evaluation Along Ordered Partition
Informal description
Let $c$ be an ordered finite partition of $\{0, \dots, n-1\}$, $p$ a family of continuous multilinear maps $p_i : E^{\times c.\text{partSize } i} \to F$ for each $i \in \text{Fin } c.\text{length}$, $v : \text{Fin } n \to E$ a vector, $j \in \text{Fin } n$ an index, and $z \in E$ a value. Then updating $v$ at $j$ with $z$ and applying $p$ along $c$ is equivalent to updating the original evaluation of $p$ along $c$ at $v$ in the part containing $j$, where the update is given by applying $p_{\text{index}(j)}$ to the restriction of $v$ to that part, but with the component corresponding to $j$ updated to $z$. In symbols: \[ c.\text{applyOrderedFinpartition } p \, (\text{update } v \, j \, z) = \text{update } (c.\text{applyOrderedFinpartition } p \, v) \, (c.\text{index } j) \, \left(p_{c.\text{index } j} (\text{update } (v \circ c.\text{emb}_{c.\text{index } j}) \, (c.\text{invEmbedding } j) \, z)\right) \]
OrderedFinpartition.applyOrderedFinpartition_update_left theorem
(p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F) (m : Fin c.length) (v : Fin n → E) (q : E [×c.partSize m]→L[𝕜] F) : c.applyOrderedFinpartition (update p m q) v = update (c.applyOrderedFinpartition p v) m (q (v ∘ c.emb m))
Full source
theorem applyOrderedFinpartition_update_left (p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F)
    (m : Fin c.length) (v : Fin n → E) (q : E [×c.partSize m]→L[𝕜] F) :
    c.applyOrderedFinpartition (update p m q) v
      = update (c.applyOrderedFinpartition p v) m (q (v ∘ c.emb m)) := by
  ext d
  by_cases h : d = m
  · rw [h]
    simp [applyOrderedFinpartition]
  · simp [h, applyOrderedFinpartition]
Update Property of Multilinear Evaluation Along Ordered Partition
Informal description
Let $c$ be an ordered finite partition of $\{0, \dots, n-1\}$, $p$ a family of continuous multilinear maps indexed by the parts of $c$, $m$ an index of a part in $c$, $v$ a vector in $E^n$, and $q$ a continuous multilinear map replacing $p_m$. Then evaluating the updated family $\text{update } p \, m \, q$ on $v$ is equal to updating the evaluation of $p$ on $v$ at index $m$ with $q$ applied to the restriction of $v$ to the $m$-th part of the partition. In symbols: \[ c.\text{applyOrderedFinpartition} (\text{update } p \, m \, q) \, v = \text{update } (c.\text{applyOrderedFinpartition} \, p \, v) \, m \, (q (v \circ c.\text{emb}_m)) \]
OrderedFinpartition.compAlongOrderedFinpartition definition
(f : F [×c.length]→L[𝕜] G) (p : ∀ i, E [×c.partSize i]→L[𝕜] F) : E [×n]→L[𝕜] G
Full source
/-- Given a an ordered finite partition `c` of `n`, a continuous multilinear map `f` in `c.length`
variables, and for each `m` a continuous multilinear map `p m` in `c.partSize m` variables,
one can form a continuous multilinear map in `n`
variables by applying `p m` to each part of the partition, and then
applying `f` to the resulting vector. It is called `c.compAlongOrderedFinpartition f p`. -/
def compAlongOrderedFinpartition (f : F [×c.length]→L[𝕜] G) (p : ∀ i, E [×c.partSize i]→L[𝕜] F) :
    E[×n]→L[𝕜] G where
  toMultilinearMap :=
    MultilinearMap.mk' (fun v ↦ f (c.applyOrderedFinpartition p v))
      (fun v i x y ↦ by
        simp only [applyOrderedFinpartition_update_right,
          ContinuousMultilinearMap.map_update_add])
      (fun v i c x ↦ by
        simp only [applyOrderedFinpartition_update_right,
          ContinuousMultilinearMap.map_update_smul])
  cont := by
    apply f.cont.comp
    change Continuous (fun v m ↦ p m (v ∘ c.emb m))
    fun_prop
Composition of multilinear maps along an ordered partition
Informal description
Given an ordered finite partition $c$ of $\{0, \dots, n-1\}$, a continuous multilinear map $f$ in $c.\text{length}$ variables, and for each $m$ a continuous multilinear map $p_m$ in $c.\text{partSize}\, m$ variables, the function $\text{compAlongOrderedFinpartition}$ constructs a continuous multilinear map in $n$ variables by applying each $p_m$ to the corresponding block of variables (as determined by the partition $c$) and then applying $f$ to the resulting vector. More precisely, for any input vector $v = (v_0, \dots, v_{n-1})$, the map evaluates to: $$ f\big(p_0(v_{I_0}), \dots, p_{k-1}(v_{I_{k-1}})\big) $$ where $I_0, \dots, I_{k-1}$ are the blocks of the partition $c$ ordered by their maximum elements, and $v_{I_m}$ denotes the components of $v$ indexed by $I_m$.
OrderedFinpartition.compAlongOrderFinpartition_apply theorem
(f : F [×c.length]→L[𝕜] G) (p : ∀ i, E [×c.partSize i]→L[𝕜] F) (v : Fin n → E) : c.compAlongOrderedFinpartition f p v = f (c.applyOrderedFinpartition p v)
Full source
@[simp] lemma compAlongOrderFinpartition_apply (f : F [×c.length]→L[𝕜] G)
    (p : ∀ i, E[×c.partSize i]→L[𝕜] F) (v : Fin n → E) :
    c.compAlongOrderedFinpartition f p v = f (c.applyOrderedFinpartition p v) := rfl
Evaluation Formula for Composition Along Ordered Partition
Informal description
Let $c$ be an ordered finite partition of $\{0, \dots, n-1\}$, $f$ a continuous multilinear map from $F^{c.\text{length}}$ to $G$, and $p$ a family of continuous multilinear maps where $p_i$ maps $E^{c.\text{partSize}\, i}$ to $F$ for each $i$. Then for any vector $v \in E^n$, the evaluation of the composition along the partition satisfies: \[ c.\text{compAlongOrderedFinpartition}\, f\, p\, v = f\big(c.\text{applyOrderedFinpartition}\, p\, v\big) \] where $c.\text{applyOrderedFinpartition}\, p\, v$ denotes the vector obtained by applying each $p_i$ to the components of $v$ corresponding to the $i$-th block of the partition $c$.
OrderedFinpartition.norm_compAlongOrderedFinpartition_le theorem
(f : F [×c.length]→L[𝕜] G) (p : ∀ i, E [×c.partSize i]→L[𝕜] F) : ‖c.compAlongOrderedFinpartition f p‖ ≤ ‖f‖ * ∏ i, ‖p i‖
Full source
theorem norm_compAlongOrderedFinpartition_le (f : F [×c.length]→L[𝕜] G)
    (p : ∀ i, E [×c.partSize i]→L[𝕜] F) :
    ‖c.compAlongOrderedFinpartition f p‖‖f‖ * ∏ i, ‖p i‖ := by
  refine ContinuousMultilinearMap.opNorm_le_bound (by positivity) fun v ↦ ?_
  rw [compAlongOrderFinpartition_apply, mul_assoc, ← c.prod_sigma_eq_prod,
    ← Finset.prod_mul_distrib]
  exact f.le_opNorm_mul_prod_of_le <| c.norm_applyOrderedFinpartition_le _ _
Operator Norm Bound for Composition Along Ordered Partition
Informal description
Let $c$ be an ordered finite partition of $\{0, \dots, n-1\}$, $f$ a continuous multilinear map from $F^{c.\text{length}}$ to $G$, and $p$ a family of continuous multilinear maps where $p_i$ maps $E^{c.\text{partSize}\, i}$ to $F$ for each $i$. Then the operator norm of the composition along the partition satisfies: \[ \|c.\text{compAlongOrderedFinpartition}\, f\, p\| \leq \|f\| \cdot \prod_{i} \|p_i\| \]
OrderedFinpartition.compAlongOrderedFinpartitionₗ definition
: (F [×c.length]→L[𝕜] G) →ₗ[𝕜] MultilinearMap 𝕜 (fun i : Fin c.length ↦ E [×c.partSize i]→L[𝕜] F) (E [×n]→L[𝕜] G)
Full source
/-- Bundled version of `compAlongOrderedFinpartition`, depending linearly on `f`
and multilinearly on `p`. -/
@[simps! apply_apply]
def compAlongOrderedFinpartitionₗ :
    (F [×c.length]→L[𝕜] G) →ₗ[𝕜]
      MultilinearMap 𝕜 (fun i : Fin c.length ↦ E[×c.partSize i]→L[𝕜] F) (E[×n]→L[𝕜] G) where
  toFun f :=
    MultilinearMap.mk' (fun p ↦ c.compAlongOrderedFinpartition f p)
      (fun p m q q' ↦ by
        ext v
        simp [applyOrderedFinpartition_update_left])
      (fun p m a q ↦ by
        ext v
        simp [applyOrderedFinpartition_update_left])
  map_add' _ _ := rfl
  map_smul' _ _ :=  rfl
Linearized composition of multilinear maps along an ordered partition
Informal description
Given an ordered finite partition $c$ of $\{0, \dots, n-1\}$, the linear map $\text{compAlongOrderedFinpartition}_\mathbb{K}$ takes a continuous multilinear map $f$ from $F^{c.\text{length}}$ to $G$ and constructs a multilinear map from $\prod_{i} \text{ContinuousMultilinearMap}\, \mathbb{K}\, E^{c.\text{partSize}\, i}\, F$ to $\text{ContinuousMultilinearMap}\, \mathbb{K}\, E^n\, G$. More precisely, for each $f$, the map associates a multilinear map that, when applied to a family of continuous multilinear maps $(p_i)_{i=0}^{k-1}$ (where each $p_i$ maps $E^{c.\text{partSize}\, i}$ to $F$), produces the continuous multilinear map in $n$ variables given by: \[ v \mapsto f\big(p_0(v_{I_0}), \dots, p_{k-1}(v_{I_{k-1}})\big) \] where $I_0, \dots, I_{k-1}$ are the blocks of the partition $c$ ordered by their maximum elements, and $v_{I_m}$ denotes the components of $v$ indexed by $I_m$.
OrderedFinpartition.compAlongOrderedFinpartitionL definition
: (F [×c.length]→L[𝕜] G) →L[𝕜] ContinuousMultilinearMap 𝕜 (fun i ↦ E [×c.partSize i]→L[𝕜] F) (E [×n]→L[𝕜] G)
Full source
/-- Bundled version of `compAlongOrderedFinpartition`, depending continuously linearly on `f`
and continuously multilinearly on `p`. -/
noncomputable def compAlongOrderedFinpartitionL :
    (F [×c.length]→L[𝕜] G) →L[𝕜]
      ContinuousMultilinearMap 𝕜 (fun i ↦ E[×c.partSize i]→L[𝕜] F) (E[×n]→L[𝕜] G) := by
  refine MultilinearMap.mkContinuousLinear c.compAlongOrderedFinpartitionₗ 1 fun f p ↦ ?_
  simp only [one_mul, compAlongOrderedFinpartitionₗ_apply_apply]
  apply norm_compAlongOrderedFinpartition_le
Continuous linear composition along an ordered finite partition
Informal description
Given an ordered finite partition \( c \) of \(\{0, \dots, n-1\}\), the continuous linear map \(\text{compAlongOrderedFinpartitionL}\) takes a continuous multilinear map \( f \) from \( F^{c.\text{length}} \) to \( G \) and constructs a continuous multilinear map from \(\prod_{i} \text{ContinuousMultilinearMap}\, \mathbb{K}\, E^{c.\text{partSize}\, i}\, F\) to \(\text{ContinuousMultilinearMap}\, \mathbb{K}\, E^n\, G\). More precisely, for each \( f \), the map associates a continuous multilinear map that, when applied to a family of continuous multilinear maps \((p_i)_{i=0}^{k-1}\) (where each \( p_i \) maps \( E^{c.\text{partSize}\, i} \) to \( F \)), produces the continuous multilinear map in \( n \) variables given by: \[ v \mapsto f\big(p_0(v_{I_0}), \dots, p_{k-1}(v_{I_{k-1}})\big) \] where \( I_0, \dots, I_{k-1} \) are the blocks of the partition \( c \) ordered by their maximum elements, and \( v_{I_m} \) denotes the components of \( v \) indexed by \( I_m \).
OrderedFinpartition.compAlongOrderedFinpartitionL_apply theorem
(f : F [×c.length]→L[𝕜] G) (p : ∀ (i : Fin c.length), E [×c.partSize i]→L[𝕜] F) : c.compAlongOrderedFinpartitionL 𝕜 E F G f p = c.compAlongOrderedFinpartition f p
Full source
@[simp] lemma compAlongOrderedFinpartitionL_apply (f : F [×c.length]→L[𝕜] G)
    (p : ∀ (i : Fin c.length), E[×c.partSize i]→L[𝕜] F) :
    c.compAlongOrderedFinpartitionL 𝕜 E F G f p = c.compAlongOrderedFinpartition f p := rfl
Evaluation of Continuous Linear Composition Along Ordered Partition
Informal description
For any continuous multilinear map $f \colon F^{\times c.\text{length}} \to G$ and family of continuous multilinear maps $(p_i \colon E^{\times c.\text{partSize}\, i} \to F)_{i=0}^{k-1}$, the evaluation of the continuous linear composition map $\text{compAlongOrderedFinpartitionL}$ at $f$ and $(p_i)$ equals the composition of $f$ with $(p_i)$ along the ordered partition $c$. That is, \[ \text{compAlongOrderedFinpartitionL}\, \mathbb{K}\, E\, F\, G\, f\, p = \text{compAlongOrderedFinpartition}\, f\, p. \]
OrderedFinpartition.norm_compAlongOrderedFinpartitionL_le theorem
: ‖c.compAlongOrderedFinpartitionL 𝕜 E F G‖ ≤ 1
Full source
theorem norm_compAlongOrderedFinpartitionL_le :
    ‖c.compAlongOrderedFinpartitionL 𝕜 E F G‖ ≤ 1 :=
  MultilinearMap.mkContinuousLinear_norm_le _ zero_le_one _
Operator Norm Bound for Composition Along Ordered Partition: $\|\text{compAlongOrderedFinpartitionL}\| \leq 1$
Informal description
For any ordered finite partition $c$ of $\{0, \dots, n-1\}$, the operator norm of the continuous linear map $\text{compAlongOrderedFinpartitionL}$ from $F^{c.\text{length}}$ to $G$ is bounded by 1, i.e., \[ \|\text{compAlongOrderedFinpartitionL}_{\mathbb{K},E,F,G}(c)\| \leq 1. \]
FormalMultilinearSeries.compAlongOrderedFinpartition definition
{n : ℕ} (q : FormalMultilinearSeries 𝕜 F G) (p : FormalMultilinearSeries 𝕜 E F) (c : OrderedFinpartition n) : E [×n]→L[𝕜] G
Full source
/-- Given two formal multilinear series `q` and `p` and a composition `c` of `n`, one may
form a continuous multilinear map in `n` variables by applying the right coefficient of `p` to each
block of the composition, and then applying `q c.length` to the resulting vector. It is
called `q.compAlongComposition p c`. -/
def compAlongOrderedFinpartition {n : } (q : FormalMultilinearSeries 𝕜 F G)
    (p : FormalMultilinearSeries 𝕜 E F) (c : OrderedFinpartition n) :
    E [×n]→L[𝕜] G :=
  c.compAlongOrderedFinpartition (q c.length) (fun m ↦ p (c.partSize m))
Composition of formal multilinear series along an ordered partition
Informal description
Given a formal multilinear series $q$ of type $F \to G$, a formal multilinear series $p$ of type $E \to F$, and an ordered finite partition $c$ of $\{0, \dots, n-1\}$, the function $\text{compAlongOrderedFinpartition}$ constructs a continuous multilinear map in $n$ variables from $E$ to $G$ by applying the appropriate coefficient of $p$ to each block of the partition $c$ and then applying the coefficient of $q$ corresponding to the number of parts in $c$ to the resulting vector. More precisely, for any input vector $v = (v_0, \dots, v_{n-1})$, the map evaluates to: $$ q_{k}\big(p_{i_0}(v_{I_0}), \dots, p_{i_{k-1}}(v_{I_{k-1}})\big) $$ where $k$ is the number of parts in $c$, $I_0, \dots, I_{k-1}$ are the blocks of the partition $c$ ordered by their maximum elements, $i_m$ is the size of $I_m$, and $v_{I_m}$ denotes the components of $v$ indexed by $I_m$.
FormalMultilinearSeries.compAlongOrderedFinpartition_apply theorem
{n : ℕ} (q : FormalMultilinearSeries 𝕜 F G) (p : FormalMultilinearSeries 𝕜 E F) (c : OrderedFinpartition n) (v : Fin n → E) : (q.compAlongOrderedFinpartition p c) v = q c.length (c.applyOrderedFinpartition (fun m ↦ (p (c.partSize m))) v)
Full source
@[simp]
theorem compAlongOrderedFinpartition_apply {n : } (q : FormalMultilinearSeries 𝕜 F G)
    (p : FormalMultilinearSeries 𝕜 E F) (c : OrderedFinpartition n) (v : Fin n → E) :
    (q.compAlongOrderedFinpartition p c) v =
      q c.length (c.applyOrderedFinpartition (fun m ↦ (p (c.partSize m))) v) :=
  rfl
Evaluation of Composition of Formal Multilinear Series Along an Ordered Partition
Informal description
Let $E$, $F$, and $G$ be normed spaces over a nontrivially normed field $\mathbb{K}$. Given a formal multilinear series $q$ from $F$ to $G$, a formal multilinear series $p$ from $E$ to $F$, and an ordered finite partition $c$ of $\{0, \dots, n-1\}$, the evaluation of the composition $q \circ p$ along the partition $c$ at a vector $v = (v_0, \dots, v_{n-1}) \in E^n$ is given by: \[ (q \circ p)_c(v) = q_k \big( p_{i_0}(v_{I_0}), \dots, p_{i_{k-1}}(v_{I_{k-1}}) \big), \] where $k$ is the number of parts in $c$, $I_0, \dots, I_{k-1}$ are the blocks of $c$ ordered by their maximum elements, $i_m = |I_m|$ is the size of $I_m$, and $v_{I_m}$ denotes the components of $v$ indexed by $I_m$.
FormalMultilinearSeries.taylorComp definition
(q : FormalMultilinearSeries 𝕜 F G) (p : FormalMultilinearSeries 𝕜 E F) : FormalMultilinearSeries 𝕜 E G
Full source
/-- Taylor formal composition of two formal multilinear series. The `n`-th coefficient in the
composition is defined to be the sum of `q.compAlongOrderedFinpartition p c` over all
ordered partitions of `n`.
In other words, this term (as a multilinear function applied to `v₀, ..., vₙ₋₁`) is
`∑'_{k} ∑'_{I₀ ⊔ ... ⊔ Iₖ₋₁ = {0, ..., n-1}} qₖ (p_{i₀} (...), ..., p_{iₖ₋₁} (...))`, where
`iₘ` is the size of `Iₘ` and one puts all variables of `Iₘ` as arguments to `p_{iₘ}`, in
increasing order. The sets `I₀, ..., Iₖ₋₁` are ordered so that `max I₀ < max I₁ < ... < max Iₖ₋₁`.

This definition is chosen so that the `n`-th derivative of `g ∘ f` is the Taylor composition of
the iterated derivatives of `g` and of `f`.

Not to be confused with another notion of composition for formal multilinear series, called just
`FormalMultilinearSeries.comp`, appearing in the composition of analytic functions.
-/
protected noncomputable def taylorComp
    (q : FormalMultilinearSeries 𝕜 F G) (p : FormalMultilinearSeries 𝕜 E F) :
    FormalMultilinearSeries 𝕜 E G :=
  fun n ↦ ∑ c : OrderedFinpartition n, q.compAlongOrderedFinpartition p c
Taylor composition of formal multilinear series
Informal description
Given two formal multilinear series $q$ (from $F$ to $G$) and $p$ (from $E$ to $F$), their Taylor composition $q \circ_{\text{Taylor}} p$ is a formal multilinear series from $E$ to $G$ whose $n$-th term is the sum over all ordered finite partitions $c$ of $\{0, \dots, n-1\}$ of the composition of $q$ and $p$ along $c$. More precisely, for each $n$, the $n$-th term of $q \circ_{\text{Taylor}} p$ is the continuous multilinear map: \[ \sum_{c \in \text{OrderedFinpartition } n} q_k \big( p_{i_0}(v_{I_0}), \dots, p_{i_{k-1}}(v_{I_{k-1}}) \big) \] where $k$ is the number of parts in $c$, $I_0, \dots, I_{k-1}$ are the blocks of $c$ ordered by their maximum elements, $i_m$ is the size of $I_m$, and $v_{I_m}$ denotes the components of $v$ indexed by $I_m$.
analyticOn_taylorComp theorem
(hq : ∀ (n : ℕ), AnalyticOn 𝕜 (fun x ↦ q x n) t) (hp : ∀ n, AnalyticOn 𝕜 (fun x ↦ p x n) s) {f : E → F} (hf : AnalyticOn 𝕜 f s) (h : MapsTo f s t) (n : ℕ) : AnalyticOn 𝕜 (fun x ↦ (q (f x)).taylorComp (p x) n) s
Full source
theorem analyticOn_taylorComp
    (hq : ∀ (n : ), AnalyticOn 𝕜 (fun x ↦ q x n) t)
    (hp : ∀ n, AnalyticOn 𝕜 (fun x ↦ p x n) s) {f : E → F}
    (hf : AnalyticOn 𝕜 f s) (h : MapsTo f s t) (n : ) :
    AnalyticOn 𝕜 (fun x ↦ (q (f x)).taylorComp (p x) n) s := by
  apply Finset.analyticOn_sum _ (fun c _ ↦ ?_)
  let B := c.compAlongOrderedFinpartitionL 𝕜 E F G
  change AnalyticOn 𝕜
    ((fun p ↦ B p.1 p.2) ∘ (fun x ↦ (q (f x) c.length, fun m ↦ p x (c.partSize m)))) s
  apply B.analyticOnNhd_uncurry_of_multilinear.comp_analyticOn ?_ (mapsTo_univ _ _)
  apply AnalyticOn.prod
  · exact (hq c.length).comp hf h
  · exact AnalyticOn.pi (fun i ↦ hp _)
Analyticity of Taylor Composition of Formal Multilinear Series
Informal description
Let $E$, $F$, and $G$ be normed spaces over a complete normed field $\mathbb{K}$, and let $s \subseteq E$, $t \subseteq F$ be sets. Given: 1. A family of formal multilinear series $q(x) : \text{FormalMultilinearSeries}\, \mathbb{K}\, F\, G$ for $x \in t$, where for each $n \in \mathbb{N}$, the map $x \mapsto q(x)_n$ is analytic on $t$, 2. A family of formal multilinear series $p(x) : \text{FormalMultilinearSeries}\, \mathbb{K}\, E\, F$ for $x \in s$, where for each $n \in \mathbb{N}$, the map $x \mapsto p(x)_n$ is analytic on $s$, 3. An analytic function $f : E \to F$ on $s$ that maps $s$ into $t$, then for each $n \in \mathbb{N}$, the $n$-th term of the Taylor composition series $(q(f(x))) \circ_{\text{Taylor}} (p(x))$ is analytic on $s$ as a function of $x$.
HasFTaylorSeriesUpToOn.comp theorem
{n : WithTop ℕ∞} {g : F → G} {f : E → F} (hg : HasFTaylorSeriesUpToOn n g q t) (hf : HasFTaylorSeriesUpToOn n f p s) (h : MapsTo f s t) : HasFTaylorSeriesUpToOn n (g ∘ f) (fun x ↦ (q (f x)).taylorComp (p x)) s
Full source
/-- *Faa di Bruno* formula: If two functions `g` and `f` have Taylor series up to `n` given by
`q` and `p`, then `g ∘ f` also has a Taylor series, given by `q.taylorComp p`. -/
theorem HasFTaylorSeriesUpToOn.comp {n : WithTop ℕ∞} {g : F → G} {f : E → F}
    (hg : HasFTaylorSeriesUpToOn n g q t) (hf : HasFTaylorSeriesUpToOn n f p s) (h : MapsTo f s t) :
    HasFTaylorSeriesUpToOn n (g ∘ f) (fun x ↦ (q (f x)).taylorComp (p x)) s := by
  /- One has to check that the `m+1`-th term is the derivative of the `m`-th term. The `m`-th term
  is a sum, that one can differentiate term by term. Each term is a linear map into continuous
  multilinear maps, applied to parts of `p` and `q`. One knows how to differentiate such a map,
  thanks to `HasFDerivWithinAt.linear_multilinear_comp`. The terms that show up are matched, using
  `faaDiBruno_aux1` and `faaDiBruno_aux2`, with terms of the same form at order `m+1`. Then, one
  needs to check that one gets each term once and exactly once, which is given by the bijection
  `OrderedFinpartition.extendEquiv m`. -/
  classical
  constructor
  · intro x hx
    simp [FormalMultilinearSeries.taylorComp, default, HasFTaylorSeriesUpToOn.zero_eq' hg (h hx)]
  · intro m hm x hx
    have A (c : OrderedFinpartition m) :
      HasFDerivWithinAt (fun x ↦ (q (f x)).compAlongOrderedFinpartition (p x) c)
        (∑ i : Option (Fin c.length),
          ((q (f x)).compAlongOrderedFinpartition (p x) (c.extend i)).curryLeft) s x := by
      let B := c.compAlongOrderedFinpartitionL 𝕜 E F G
      change HasFDerivWithinAt (fun y ↦ B (q (f y) c.length) (fun i ↦ p y (c.partSize i)))
        (∑ i : Option (Fin c.length),
          ((q (f x)).compAlongOrderedFinpartition (p x) (c.extend i)).curryLeft) s x
      have cm : (c.length : WithTop ℕ∞) ≤ m := mod_cast OrderedFinpartition.length_le c
      have cp i : (c.partSize i : WithTop ℕ∞) ≤ m := by
        exact_mod_cast OrderedFinpartition.partSize_le c i
      have I i : HasFDerivWithinAt (fun x ↦ p x (c.partSize i))
          (p x (c.partSize i).succ).curryLeft s x :=
        hf.fderivWithin (c.partSize i) ((cp i).trans_lt hm) x hx
      have J : HasFDerivWithinAt (fun x ↦ q x c.length) (q (f x) c.length.succ).curryLeft
        t (f x) := hg.fderivWithin c.length (cm.trans_lt hm) (f x) (h hx)
      have K : HasFDerivWithinAt f ((continuousMultilinearCurryFin1 𝕜 E F) (p x 1)) s x :=
        hf.hasFDerivWithinAt (le_trans (mod_cast Nat.le_add_left 1 m)
          (ENat.add_one_natCast_le_withTop_of_lt hm)) hx
      convert HasFDerivWithinAt.linear_multilinear_comp (J.comp x K h) I B
      simp only [B, Nat.succ_eq_add_one, Fintype.sum_option, comp_apply, faaDiBruno_aux1,
        faaDiBruno_aux2]
    have B : HasFDerivWithinAt (fun x ↦ (q (f x)).taylorComp (p x) m)
        (∑ c : OrderedFinpartition m, ∑ i : Option (Fin c.length),
          ((q (f x)).compAlongOrderedFinpartition (p x) (c.extend i)).curryLeft) s x :=
      HasFDerivWithinAt.sum (fun c _ ↦ A c)
    suffices ∑ c : OrderedFinpartition m, ∑ i : Option (Fin c.length),
          ((q (f x)).compAlongOrderedFinpartition (p x) (c.extend i)) =
        (q (f x)).taylorComp (p x) (m + 1) by
      rw [← this]
      convert B
      ext v
      simp only [Nat.succ_eq_add_one, Fintype.sum_option, ContinuousMultilinearMap.curryLeft_apply,
        ContinuousMultilinearMap.sum_apply, ContinuousMultilinearMap.add_apply,
        FormalMultilinearSeries.compAlongOrderedFinpartition_apply, ContinuousLinearMap.coe_sum',
        Finset.sum_apply, ContinuousLinearMap.add_apply]
    rw [Finset.sum_sigma']
    exact Fintype.sum_equiv (OrderedFinpartition.extendEquiv m) _ _ (fun p ↦ rfl)
  · intro m hm
    apply continuousOn_finset_sum _ (fun c _ ↦ ?_)
    let B := c.compAlongOrderedFinpartitionL 𝕜 E F G
    change ContinuousOn
      ((fun p ↦ B p.1 p.2) ∘ (fun x ↦ (q (f x) c.length, fun i ↦ p x (c.partSize i)))) s
    apply B.continuous_uncurry_of_multilinear.comp_continuousOn (ContinuousOn.prodMk ?_ ?_)
    · have : (c.length : WithTop ℕ∞) ≤ m := mod_cast OrderedFinpartition.length_le c
      exact (hg.cont c.length (this.trans hm)).comp hf.continuousOn h
    · apply continuousOn_pi.2 (fun i ↦ ?_)
      have : (c.partSize i : WithTop ℕ∞) ≤ m := by
        exact_mod_cast OrderedFinpartition.partSize_le c i
      exact hf.cont _ (this.trans hm)
Faa di Bruno Formula for Taylor Series of Composition
Informal description
Let $n$ be an extended natural number (possibly $\infty$), $g \colon F \to G$ and $f \colon E \to F$ be functions, and $s \subseteq E$, $t \subseteq F$ be subsets. Suppose $g$ has a Taylor series expansion up to order $n$ on $t$ given by $q$, and $f$ has a Taylor series expansion up to order $n$ on $s$ given by $p$, with $f$ mapping $s$ into $t$. Then the composition $g \circ f$ has a Taylor series expansion up to order $n$ on $s$, given by the Taylor composition of $q$ and $p$ at each point $x \in s$.