|
| 1 | +/- |
| 2 | +Copyright (c) 2025 CompPoly. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Dimitris Mitsios |
| 5 | +-/ |
| 6 | +import CompPoly.Multivariate.MvPolyEquiv |
| 7 | +import Mathlib.Algebra.MvPolynomial.Rename |
| 8 | + |
| 9 | +/-! |
| 10 | +# Properties of variable renaming for `CMvPolynomial` |
| 11 | +
|
| 12 | +This file proves properties of the `rename` function defined in `CMvPolynomial.lean`, |
| 13 | +by transferring results from Mathlib's `MvPolynomial.rename` through the |
| 14 | +`polyRingEquiv : CMvPolynomial n R ≃+* MvPolynomial (Fin n) R` equivalence. |
| 15 | +
|
| 16 | +## Main results |
| 17 | +
|
| 18 | +* `fromCMvPolynomial_rename` — correspondence between `CMvPolynomial.rename` and |
| 19 | + `MvPolynomial.rename` |
| 20 | +* `rename_C`, `rename_X`, `rename_add`, `rename_mul`, `rename_id`, `rename_rename` |
| 21 | + — algebraic properties |
| 22 | +* `CMvPolynomial.renameEquiv` — ring isomorphism for bijective variable renaming |
| 23 | +
|
| 24 | +-/ |
| 25 | + |
| 26 | +namespace CPoly |
| 27 | + |
| 28 | +open Std CMvPolynomial |
| 29 | + |
| 30 | +variable {n m : ℕ} {R : Type} [CommSemiring R] [BEq R] [LawfulBEq R] |
| 31 | + |
| 32 | +/-! ### Helper lemmas for `fromCMvPolynomial_rename` -/ |
| 33 | + |
| 34 | +/-- In a commutative additive monoid, the order of addition in a list fold |
| 35 | +does not matter. -/ |
| 36 | +lemma list_foldl_add_comm {β K V : Type} [AddCommMonoid β] |
| 37 | + (g : K → V → β) (l : List (K × V)) (init : β) : |
| 38 | + List.foldl (fun acc pair => acc + g pair.1 pair.2) init l = |
| 39 | + List.foldl (fun acc pair => g pair.1 pair.2 + acc) init l := by |
| 40 | + induction l generalizing init with |
| 41 | + | nil => rfl |
| 42 | + | cons h t ih => |
| 43 | + simp only [List.foldl_cons] |
| 44 | + rw [show init + g h.1 h.2 = g h.1 h.2 + init from add_comm _ _] |
| 45 | + exact ih _ |
| 46 | + |
| 47 | +/-- Swapping addition order in `ExtTreeMap.foldl` does not change the result. -/ |
| 48 | +lemma foldl_add_comm' {β : Type} [AddCommMonoid β] {k : ℕ} |
| 49 | + {R' : Type} (g : CMvMonomial k → R' → β) |
| 50 | + (t : Std.ExtTreeMap (CMvMonomial k) R') : |
| 51 | + Std.ExtTreeMap.foldl (fun acc m c => acc + g m c) (0 : β) t = |
| 52 | + Std.ExtTreeMap.foldl (fun acc m c => g m c + acc) (0 : β) t := by |
| 53 | + simp only [Std.ExtTreeMap.foldl_eq_foldl_toList] |
| 54 | + exact list_foldl_add_comm g t.toList 0 |
| 55 | + |
| 56 | +/-- Applying `CMvMonomial.toFinsupp` at index `i` equals `Vector.get`. -/ |
| 57 | +lemma toFinsupp_apply (mono : CMvMonomial n) (i : Fin n) : |
| 58 | + (CMvMonomial.toFinsupp mono) i = mono.get i := rfl |
| 59 | + |
| 60 | +/-- Monomial renaming via `Vector.ofFn` corresponds to `Finsupp.mapDomain` |
| 61 | +on the `Finsupp` side. -/ |
| 62 | +lemma renameMonomial_eq (f : Fin n → Fin m) (mono : CMvMonomial n) : |
| 63 | + (Vector.ofFn (fun j => (Finset.univ.filter (fun i => f i = j)).sum |
| 64 | + (fun i => mono.get i)) : CMvMonomial m) = |
| 65 | + CMvMonomial.ofFinsupp |
| 66 | + (Finsupp.mapDomain f (CMvMonomial.toFinsupp mono)) := by |
| 67 | + unfold CMvMonomial.ofFinsupp |
| 68 | + congr 1; funext j |
| 69 | + simp only [Finsupp.mapDomain, Finsupp.sum_apply, Finsupp.single_apply] |
| 70 | + rw [Finset.sum_filter] |
| 71 | + rw [Finsupp.sum] |
| 72 | + -- Extend the sum from `support` to `Finset.univ` |
| 73 | + symm |
| 74 | + apply Finset.sum_subset (Finset.subset_univ _) |
| 75 | + intro x _ hxs |
| 76 | + rw [Finsupp.notMem_support_iff] at hxs |
| 77 | + simp [hxs] |
| 78 | + |
| 79 | +/-- `fromCMvPolynomial` maps `CMvPolynomial.monomial` to |
| 80 | +`MvPolynomial.monomial`. -/ |
| 81 | +lemma fromCMvPolynomial_monomial {k : ℕ} (mono : CMvMonomial k) (c : R) : |
| 82 | + fromCMvPolynomial (CMvPolynomial.monomial mono c) = |
| 83 | + MvPolynomial.monomial (CMvMonomial.toFinsupp mono) c := by |
| 84 | + by_cases hc : c = 0 |
| 85 | + · subst hc; simp [CMvPolynomial.monomial, map_zero] |
| 86 | + · ext μ |
| 87 | + rw [coeff_eq, MvPolynomial.coeff_monomial] |
| 88 | + unfold CMvPolynomial.coeff CMvPolynomial.monomial |
| 89 | + simp only [show (c == (0 : R)) = false from by simp [hc]] |
| 90 | + unfold Lawful.fromUnlawful |
| 91 | + erw [Unlawful.filter_get] |
| 92 | + simp only [Unlawful.ofList] |
| 93 | + by_cases hm : CMvMonomial.toFinsupp mono = μ |
| 94 | + · subst hm; rw [if_pos rfl, CMvMonomial.ofFinsupp_toFinsupp] |
| 95 | + erw [ExtTreeMap.getElem?_ofList_of_mem |
| 96 | + (k := mono) (k_eq := compare_self) (v := c) |
| 97 | + (mem := by simp) (distinct := ?distinct)] |
| 98 | + · simp |
| 99 | + case distinct => simp |
| 100 | + · rw [if_neg hm] |
| 101 | + have hne : CMvMonomial.ofFinsupp μ ≠ mono := |
| 102 | + fun h => hm (h ▸ CMvMonomial.toFinsupp_ofFinsupp) |
| 103 | + erw [ExtTreeMap.getElem?_ofList_of_contains_eq_false |
| 104 | + (by simp [hne])] |
| 105 | + rfl |
| 106 | + |
| 107 | +/-- `fromCMvPolynomial` distributes over `Finsupp.sum`. -/ |
| 108 | +lemma fromCMvPolynomial_finsupp_sum {k : ℕ} |
| 109 | + (g : (Fin n →₀ ℕ) → R → CMvPolynomial k R) |
| 110 | + (a : CMvPolynomial n R) : |
| 111 | + fromCMvPolynomial (Finsupp.sum (fromCMvPolynomial a) g) = |
| 112 | + Finsupp.sum (fromCMvPolynomial a) |
| 113 | + (fun μ c => fromCMvPolynomial (g μ c)) := by |
| 114 | + unfold Finsupp.sum; ext |
| 115 | + simp [MvPolynomial.coeff_sum, coeff_eq, coeff_sum] |
| 116 | + |
| 117 | +/-! ### Main correspondence lemma -/ |
| 118 | + |
| 119 | +/-- `CMvPolynomial.rename` agrees with `MvPolynomial.rename` under the |
| 120 | +`fromCMvPolynomial` equivalence. -/ |
| 121 | +lemma fromCMvPolynomial_rename (f : Fin n → Fin m) |
| 122 | + (p : CMvPolynomial n R) : |
| 123 | + fromCMvPolynomial (CMvPolynomial.rename f p) = |
| 124 | + MvPolynomial.rename f (fromCMvPolynomial p) := by |
| 125 | + -- Express rename as a `Finsupp.sum` via `foldl_eq_sum` |
| 126 | + have step1 : CMvPolynomial.rename f p = |
| 127 | + Finsupp.sum (fromCMvPolynomial p) (fun μ c => |
| 128 | + CMvPolynomial.monomial |
| 129 | + (CMvMonomial.ofFinsupp (Finsupp.mapDomain f μ)) c) := by |
| 130 | + show Std.ExtTreeMap.foldl |
| 131 | + (fun acc mono c => acc + CMvPolynomial.monomial |
| 132 | + (Vector.ofFn fun j => |
| 133 | + (Finset.univ.filter fun i => f i = j).sum |
| 134 | + fun i => mono.get i) c) |
| 135 | + 0 p.1 = _ |
| 136 | + simp_rw [renameMonomial_eq f] |
| 137 | + rw [foldl_add_comm' (fun mono c => |
| 138 | + CMvPolynomial.monomial (CMvMonomial.ofFinsupp |
| 139 | + (Finsupp.mapDomain f (CMvMonomial.toFinsupp mono))) c)] |
| 140 | + rw [foldl_eq_sum] |
| 141 | + congr 1; ext μ c |
| 142 | + simp only [Function.comp_def, CMvMonomial.toFinsupp_ofFinsupp] |
| 143 | + rw [step1] |
| 144 | + rw [fromCMvPolynomial_finsupp_sum] |
| 145 | + simp only [fromCMvPolynomial_monomial, |
| 146 | + CMvMonomial.toFinsupp_ofFinsupp] |
| 147 | + -- Conclude using Mathlib's `MvPolynomial.rename_monomial` |
| 148 | + conv_rhs => |
| 149 | + rw [← MvPolynomial.support_sum_monomial_coeff |
| 150 | + (fromCMvPolynomial p)] |
| 151 | + rw [map_sum (MvPolynomial.rename f)] |
| 152 | + simp only [MvPolynomial.rename_monomial] |
| 153 | + rfl |
| 154 | + |
| 155 | +/-! ### Bridging lemmas for `C` and `X` -/ |
| 156 | + |
| 157 | +/-- `CMvPolynomial.C c` equals `CMvPolynomial.monomial 0 c`. -/ |
| 158 | +lemma C_eq_monomial {k : ℕ} (c : R) : |
| 159 | + CMvPolynomial.C (n := k) c = CMvPolynomial.monomial 0 c := by |
| 160 | + by_cases hc : c = 0 |
| 161 | + · subst hc; ext m |
| 162 | + unfold CMvPolynomial.coeff CMvPolynomial.C CMvPolynomial.monomial |
| 163 | + Lawful.C Unlawful.C |
| 164 | + grind |
| 165 | + · ext m |
| 166 | + show (CMvPolynomial.C c).coeff m = |
| 167 | + (CMvPolynomial.monomial (0 : CMvMonomial k) c).coeff m |
| 168 | + unfold CMvPolynomial.coeff CMvPolynomial.C Lawful.C |
| 169 | + CMvPolynomial.monomial Unlawful.C |
| 170 | + simp only [show (c == (0 : R)) = false from by simp [hc], |
| 171 | + hc, ite_false, MonoR.C] |
| 172 | + show (Unlawful.ofList [(CMvMonomial.zero, c)])[m]?.getD 0 = |
| 173 | + (Lawful.fromUnlawful |
| 174 | + (Unlawful.ofList [(CMvMonomial.zero, c)])).1[m]?.getD 0 |
| 175 | + unfold Lawful.fromUnlawful |
| 176 | + erw [Unlawful.filter_get] |
| 177 | + |
| 178 | +/-- The `Finsupp` of the zero monomial is zero. -/ |
| 179 | +lemma toFinsupp_zero {k : ℕ} : |
| 180 | + CMvMonomial.toFinsupp (0 : CMvMonomial k) = 0 := by |
| 181 | + ext i |
| 182 | + simp [CMvMonomial.toFinsupp, Vector.get] |
| 183 | + |
| 184 | +/-- `fromCMvPolynomial` maps `CMvPolynomial.C` to `MvPolynomial.C`. -/ |
| 185 | +lemma fromCMvPolynomial_C {k : ℕ} (c : R) : |
| 186 | + fromCMvPolynomial (CMvPolynomial.C (n := k) c) = |
| 187 | + MvPolynomial.C c := by |
| 188 | + rw [C_eq_monomial, fromCMvPolynomial_monomial, toFinsupp_zero, |
| 189 | + ← MvPolynomial.C_apply] |
| 190 | + |
| 191 | +/-! ### Algebraic properties of rename -/ |
| 192 | + |
| 193 | +/-- Constants are unchanged under renaming. -/ |
| 194 | +@[simp] |
| 195 | +lemma rename_C (f : Fin n → Fin m) (c : R) : |
| 196 | + CMvPolynomial.rename f (CMvPolynomial.C c) = |
| 197 | + CMvPolynomial.C (n := m) c := by |
| 198 | + apply fromCMvPolynomial_injective |
| 199 | + rw [fromCMvPolynomial_rename, fromCMvPolynomial_C, |
| 200 | + fromCMvPolynomial_C] |
| 201 | + exact MvPolynomial.rename_C f c |
| 202 | + |
| 203 | +/-- `CMvPolynomial.X i` equals `CMvPolynomial.monomial eᵢ 1` |
| 204 | +where `eᵢ` is the `i`-th standard basis vector. -/ |
| 205 | +lemma X_eq_monomial {k : ℕ} (i : Fin k) : |
| 206 | + CMvPolynomial.X (R := R) i = CMvPolynomial.monomial |
| 207 | + (Vector.ofFn (fun j => if j = i then 1 else 0)) |
| 208 | + (1 : R) := by |
| 209 | + unfold CMvPolynomial.X CMvPolynomial.monomial |
| 210 | + by_cases h : (1 : R) = 0 |
| 211 | + · ext m; unfold CMvPolynomial.coeff Lawful.fromUnlawful |
| 212 | + erw [Unlawful.filter_get]; simp [h]; grind |
| 213 | + · simp only [show ((1 : R) == 0) = false from by simp [h]] |
| 214 | + exact (if_neg (by decide)).symm |
| 215 | + |
| 216 | +/-- The `Finsupp` of the `i`-th standard basis monomial is |
| 217 | +`Finsupp.single i 1`. -/ |
| 218 | +lemma toFinsupp_unitMono {k : ℕ} (i : Fin k) : |
| 219 | + CMvMonomial.toFinsupp |
| 220 | + (Vector.ofFn (fun j : Fin k => |
| 221 | + if j = i then 1 else 0)) = |
| 222 | + Finsupp.single i 1 := by |
| 223 | + ext j |
| 224 | + simp [CMvMonomial.toFinsupp, Vector.get, |
| 225 | + Finsupp.single_apply, eq_comm] |
| 226 | + |
| 227 | +/-- `fromCMvPolynomial` maps `CMvPolynomial.X i` to |
| 228 | +`MvPolynomial.X i`. -/ |
| 229 | +lemma fromCMvPolynomial_X {k : ℕ} (i : Fin k) : |
| 230 | + fromCMvPolynomial (CMvPolynomial.X (R := R) i) = |
| 231 | + MvPolynomial.X i := by |
| 232 | + rw [X_eq_monomial, fromCMvPolynomial_monomial, |
| 233 | + toFinsupp_unitMono] |
| 234 | + rfl |
| 235 | + |
| 236 | +/-- Renaming maps variable `X i` to `X (f i)`. -/ |
| 237 | +@[simp] |
| 238 | +lemma rename_X (f : Fin n → Fin m) (i : Fin n) : |
| 239 | + CMvPolynomial.rename f (CMvPolynomial.X (R := R) i) = |
| 240 | + CMvPolynomial.X (f i) := by |
| 241 | + apply fromCMvPolynomial_injective |
| 242 | + rw [fromCMvPolynomial_rename, fromCMvPolynomial_X, |
| 243 | + fromCMvPolynomial_X] |
| 244 | + exact MvPolynomial.rename_X f i |
| 245 | + |
| 246 | +/-- Renaming preserves addition. -/ |
| 247 | +@[simp] |
| 248 | +lemma rename_add (f : Fin n → Fin m) |
| 249 | + (p q : CMvPolynomial n R) : |
| 250 | + CMvPolynomial.rename f (p + q) = |
| 251 | + CMvPolynomial.rename f p + |
| 252 | + CMvPolynomial.rename f q := by |
| 253 | + apply fromCMvPolynomial_injective |
| 254 | + simp only [fromCMvPolynomial_rename, CPoly.map_add] |
| 255 | + exact _root_.map_add (MvPolynomial.rename f) |
| 256 | + (fromCMvPolynomial p) (fromCMvPolynomial q) |
| 257 | + |
| 258 | +/-- Renaming preserves multiplication. -/ |
| 259 | +@[simp] |
| 260 | +lemma rename_mul (f : Fin n → Fin m) |
| 261 | + (p q : CMvPolynomial n R) : |
| 262 | + CMvPolynomial.rename f (p * q) = |
| 263 | + CMvPolynomial.rename f p * |
| 264 | + CMvPolynomial.rename f q := by |
| 265 | + apply fromCMvPolynomial_injective |
| 266 | + simp only [fromCMvPolynomial_rename, CPoly.map_mul] |
| 267 | + exact _root_.map_mul (MvPolynomial.rename f) |
| 268 | + (fromCMvPolynomial p) (fromCMvPolynomial q) |
| 269 | + |
| 270 | +/-- Renaming by the identity function is the identity. -/ |
| 271 | +@[simp] |
| 272 | +lemma rename_id (p : CMvPolynomial n R) : |
| 273 | + CMvPolynomial.rename id p = p := by |
| 274 | + apply fromCMvPolynomial_injective |
| 275 | + rw [fromCMvPolynomial_rename] |
| 276 | + exact MvPolynomial.rename_id_apply (fromCMvPolynomial p) |
| 277 | + |
| 278 | +/-- Composing two renamings equals renaming by the composition. -/ |
| 279 | +@[simp] |
| 280 | +lemma rename_rename {k : ℕ} (f : Fin n → Fin m) |
| 281 | + (g : Fin m → Fin k) (p : CMvPolynomial n R) : |
| 282 | + CMvPolynomial.rename g (CMvPolynomial.rename f p) = |
| 283 | + CMvPolynomial.rename (g ∘ f) p := by |
| 284 | + apply fromCMvPolynomial_injective |
| 285 | + rw [fromCMvPolynomial_rename, fromCMvPolynomial_rename, |
| 286 | + fromCMvPolynomial_rename] |
| 287 | + exact MvPolynomial.rename_rename f g (fromCMvPolynomial p) |
| 288 | + |
| 289 | +/-- Ring equivalence for variable renaming when the function is |
| 290 | +a bijection. -/ |
| 291 | +noncomputable def CMvPolynomial.renameEquiv |
| 292 | + (f : Fin n ≃ Fin m) : |
| 293 | + CMvPolynomial n R ≃+* CMvPolynomial m R where |
| 294 | + toFun := CMvPolynomial.rename f |
| 295 | + invFun := CMvPolynomial.rename f.symm |
| 296 | + left_inv p := by |
| 297 | + rw [rename_rename, f.symm_comp_self, rename_id] |
| 298 | + right_inv q := by |
| 299 | + rw [rename_rename, f.self_comp_symm, rename_id] |
| 300 | + map_add' p q := rename_add f p q |
| 301 | + map_mul' p q := rename_mul f p q |
| 302 | + |
| 303 | +end CPoly |
0 commit comments