-
Notifications
You must be signed in to change notification settings - Fork 41
feat(multivariate): add degree-bound and partial-evaluation primitives #241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /- | ||
| Copyright (c) 2026 CompPoly Contributors. All rights reserved. | ||
| Released under Apache 2.0 license as described in the file LICENSE. | ||
| Authors: ArkLib Contributors | ||
| -/ | ||
| import CompPoly.Multivariate.CMvPolynomial | ||
| import CompPoly.Univariate.ToPoly.Impl | ||
|
|
||
| /-! | ||
| # Degree-Bounded Computable Polynomials | ||
|
|
||
| Generic wrappers for computable univariate and multivariate polynomials bundled | ||
| with degree bounds. | ||
| -/ | ||
|
|
||
| open CPoly Std | ||
|
|
||
| namespace CPoly.CMvPolynomial | ||
|
|
||
| variable {n : ℕ} {R : Type} [CommSemiring R] [BEq R] [LawfulBEq R] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typeclass assumptions much stronger than needed (verified: the minimized file compiles). Everything in this file needs only variable {n : ℕ} {R : Type*} [Zero R]
...
def CDegreeLE (R : Type*) [Zero R] (d : ℕ) :=
{ p : CPolynomial R // p.natDegree ≤ d }
def CMvDegreeLE (R : Type*) [Zero R] (n d : ℕ) :=
{ p : CMvPolynomial n R // CMvPolynomial.IndividualDegreeLE (R := R) d p }
|
||
|
|
||
| /-- `p` has individual degree at most `deg` when every monomial exponent is | ||
| bounded by `deg` in every coordinate. -/ | ||
| def IndividualDegreeLE (deg : ℕ) (p : CMvPolynomial n R) : Prop := | ||
| letI := Classical.decEq R | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dead code (verified: removal compiles). Nothing in the body needs Separately (verified), the predicate is equivalent to a bound on the existing theorem individualDegreeLE_iff {deg : ℕ} (p : CMvPolynomial n R) :
IndividualDegreeLE deg p ↔ ∀ i, CMvPolynomial.degreeOf i p ≤ deg := by
unfold IndividualDegreeLE CMvPolynomial.degreeOf
constructor
· intro h i
exact Finset.sup_le fun m hm => h i m (by simpa using hm)
· intro h i m hm
exact (Finset.le_sup (s := (Lawful.monomials p).toFinset)
(f := fun m => CMvMonomial.degreeOf m i) (by simpa using hm)).trans (h i) |
||
| ∀ i : Fin n, ∀ mono ∈ Lawful.monomials p, mono.degreeOf i ≤ deg | ||
|
|
||
| end CPoly.CMvPolynomial | ||
|
|
||
| namespace CompPoly | ||
|
|
||
| /-- A computable univariate polynomial with `natDegree ≤ d`. -/ | ||
| def CDegreeLE (R : Type) [BEq R] [Semiring R] [LawfulBEq R] (d : ℕ) := | ||
| { p : CPolynomial R // p.natDegree ≤ d } | ||
|
|
||
| /-- A computable multivariate polynomial with individual degree at most `d` in | ||
| every coordinate. -/ | ||
| def CMvDegreeLE | ||
| (R : Type) [BEq R] [CommSemiring R] [LawfulBEq R] (n d : ℕ) := | ||
| { p : CMvPolynomial n R // CMvPolynomial.IndividualDegreeLE (R := R) d p } | ||
|
|
||
| end CompPoly | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -454,6 +454,44 @@ attribute [grind =] | |
| aeval_C aeval_X aeval_add aeval_mul | ||
| aeval_zero aeval_one aeval_pow aeval_neg aeval_sub | ||
|
|
||
|
|
||
| /-- The computable substitution `bind₁` agrees with Mathlib substitution after | ||
| transporting through `fromCMvPolynomial`. -/ | ||
| theorem fromCMvPolynomial_bind₁ {n m : ℕ} {R : Type*} [CommSemiring R] [BEq R] | ||
| [LawfulBEq R] (f : Fin n → CMvPolynomial m R) (p : CMvPolynomial n R) : | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider spelling the RHS with fromCMvPolynomial (bind₁ f p) =
MvPolynomial.aeval (fun i => fromCMvPolynomial (f i)) (fromCMvPolynomial p)This mirrors CompPoly's own Nit: double blank line above the docstring (line 458). |
||
| fromCMvPolynomial (bind₁ f p) = | ||
| MvPolynomial.eval₂ MvPolynomial.C (fun i => fromCMvPolynomial (f i)) | ||
| (fromCMvPolynomial p) := by | ||
| rw [bind₁_eq_aeval] | ||
| unfold aeval | ||
| have h := MvPolynomial.map_eval₂Hom | ||
| (f := algebraMap R (CMvPolynomial m R)) | ||
| (g := f) | ||
| (φ := (CPoly.polyRingEquiv (n := m) (R := R)).toRingHom) | ||
| (p := fromCMvPolynomial p) | ||
| have hcomp : | ||
| ((CPoly.polyRingEquiv (n := m) (R := R)).toRingHom).comp | ||
| (algebraMap R (CMvPolynomial m R)) = MvPolynomial.C := by | ||
| ext r μ | ||
| rw [RingHom.comp_apply] | ||
| change MvPolynomial.coeff μ | ||
| (fromCMvPolynomial (algebraMap R (CMvPolynomial m R) r)) = | ||
| MvPolynomial.coeff μ (MvPolynomial.C r) | ||
| rw [show (algebraMap R (CMvPolynomial m R)) r = CMvPolynomial.C (n := m) r from rfl] | ||
| rw [fromCMvPolynomial_C] | ||
| rw [eval₂_equiv (p := p) (f := algebraMap R (CMvPolynomial m R)) (vals := f)] | ||
| have h' : | ||
| fromCMvPolynomial | ||
| (MvPolynomial.eval₂ (algebraMap R (CMvPolynomial m R)) f | ||
| (fromCMvPolynomial p)) = | ||
| MvPolynomial.eval₂ | ||
| (((CPoly.polyRingEquiv (n := m) (R := R)).toRingHom).comp | ||
| (algebraMap R (CMvPolynomial m R))) | ||
| (fun i => fromCMvPolynomial (f i)) | ||
| (fromCMvPolynomial p) := by | ||
| simpa [CPoly.polyRingEquiv, CPoly.polyEquiv] using h | ||
| rw [hcomp] at h' | ||
| exact h' | ||
| end CMvPolynomial | ||
|
|
||
| namespace Lawful | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| /- | ||
| Copyright (c) 2026 CompPoly Contributors. All rights reserved. | ||
| Released under Apache 2.0 license as described in the file LICENSE. | ||
| Authors: ArkLib Contributors | ||
| -/ | ||
| import CompPoly.Data.MvPolynomial.Notation | ||
| import CompPoly.Multivariate.DegreeBound | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two unused imports (verified: removal compiles). Nothing in this file uses the |
||
| import CompPoly.Multivariate.Operations | ||
| import CompPoly.Multivariate.MvPolyEquiv.Eval | ||
|
|
||
| /-! | ||
| # Partial evaluation of the first variable of a `CMvPolynomial` | ||
|
|
||
| `partialEvalFirst a p` fixes variable `0` of a multivariate computable | ||
| polynomial to a scalar value, together with its evaluation lemma and the | ||
| per-variable degree-bound lemma. | ||
| -/ | ||
|
|
||
| open CompPoly Std | ||
|
|
||
| namespace CPoly | ||
|
|
||
| namespace CMvPolynomial | ||
|
|
||
| variable {n : ℕ} {R : Type} [CommSemiring R] [BEq R] [LawfulBEq R] | ||
|
|
||
| omit [BEq R] [LawfulBEq R] in | ||
| private lemma partialEvalFirst_subst_degreeOf_le [Nontrivial R] (a : R) | ||
| (i : Fin n) (j : Fin (n + 1)) : | ||
| MvPolynomial.degreeOf i | ||
| (Fin.cases (MvPolynomial.C a) (fun k : Fin n => MvPolynomial.X k) j : | ||
| MvPolynomial (Fin n) R) ≤ if j = i.succ then 1 else 0 := by | ||
| cases j using Fin.cases with | ||
| | zero => | ||
| simp [MvPolynomial.degreeOf_C] | ||
| | succ j => | ||
| by_cases h : j = i | ||
| · subst h | ||
| simp | ||
| · have hsucc : Fin.succ j ≠ i.succ := by | ||
| intro h' | ||
| exact h ((Fin.succ_injective n) h') | ||
| have hi_ne_j : i ≠ j := fun hij => h hij.symm | ||
| simp [hsucc, hi_ne_j, MvPolynomial.degreeOf_X] | ||
|
|
||
| omit [BEq R] [LawfulBEq R] in | ||
| private lemma partialEvalFirst_eval₂_monomial_degreeOf_le [Nontrivial R] {deg : ℕ} | ||
| (a : R) (i : Fin n) (s : Fin (n + 1) →₀ ℕ) (c : R) | ||
| (hs : s i.succ ≤ deg) : | ||
| MvPolynomial.degreeOf i | ||
| (MvPolynomial.eval₂ MvPolynomial.C | ||
| (fun j : Fin (n + 1) => | ||
| Fin.cases (MvPolynomial.C a) (fun k : Fin n => MvPolynomial.X k) j) | ||
| (MvPolynomial.monomial s c) : MvPolynomial (Fin n) R) ≤ deg := by | ||
| rw [MvPolynomial.eval₂_monomial] | ||
| refine (MvPolynomial.degreeOf_C_mul_le _ i c).trans ?_ | ||
| rw [Finsupp.prod] | ||
| refine (MvPolynomial.degreeOf_prod_le i s.support | ||
| (fun j => (Fin.cases (MvPolynomial.C a) (fun k : Fin n => MvPolynomial.X k) j : | ||
| MvPolynomial (Fin n) R) ^ s j)).trans ?_ | ||
| calc | ||
| (∑ x ∈ s.support, | ||
| MvPolynomial.degreeOf i | ||
| ((Fin.cases (MvPolynomial.C a) (fun k : Fin n => MvPolynomial.X k) x : | ||
| MvPolynomial (Fin n) R) ^ s x)) | ||
| ≤ ∑ x ∈ s.support, s x * (if x = i.succ then 1 else 0) := by | ||
| refine Finset.sum_le_sum ?_ | ||
| intro x hx | ||
| exact (MvPolynomial.degreeOf_pow_le i | ||
| (Fin.cases (MvPolynomial.C a) (fun k : Fin n => MvPolynomial.X k) x : | ||
| MvPolynomial (Fin n) R) (s x)).trans | ||
| (Nat.mul_le_mul_left _ (partialEvalFirst_subst_degreeOf_le a i x)) | ||
| _ ≤ s i.succ := by | ||
| classical | ||
| by_cases hi : i.succ ∈ s.support | ||
| · rw [Finset.sum_eq_single i.succ] | ||
| · simp | ||
| · intro x hx hne | ||
| simp [hne] | ||
| · intro hnot | ||
| exact False.elim (hnot hi) | ||
| · rw [Finset.sum_eq_zero] | ||
| · simp | ||
| · intro x hx | ||
| have hne : x ≠ i.succ := fun h => hi (h ▸ hx) | ||
| simp [hne] | ||
| _ ≤ deg := hs | ||
|
|
||
| omit [BEq R] [LawfulBEq R] in | ||
| private lemma partialEvalFirst_eval₂_degreeOf_le [Nontrivial R] {deg : ℕ} | ||
| (a : R) (i : Fin n) (p : MvPolynomial (Fin (n + 1)) R) | ||
| (hDeg : ∀ s ∈ p.support, s i.succ ≤ deg) : | ||
| MvPolynomial.degreeOf i | ||
| (MvPolynomial.eval₂ MvPolynomial.C | ||
| (fun j : Fin (n + 1) => | ||
| Fin.cases (MvPolynomial.C a) (fun k : Fin n => MvPolynomial.X k) j) | ||
| p : MvPolynomial (Fin n) R) ≤ deg := by | ||
| rw [MvPolynomial.eval₂_eq] | ||
| refine (MvPolynomial.degreeOf_sum_le i p.support | ||
| (fun s => MvPolynomial.C (p.coeff s) * | ||
| ∏ x ∈ s.support, | ||
| (Fin.cases (MvPolynomial.C a) (fun k : Fin n => MvPolynomial.X k) x : | ||
| MvPolynomial (Fin n) R) ^ s x)).trans ?_ | ||
| apply Finset.sup_le | ||
| intro s hs | ||
| simpa [MvPolynomial.eval₂_monomial, Finsupp.prod] using | ||
| partialEvalFirst_eval₂_monomial_degreeOf_le (n := n) (R := R) | ||
| (deg := deg) a i s (p.coeff s) (hDeg s hs) | ||
|
|
||
| /-! ## Core operation -/ | ||
|
|
||
| /-- Fix variable 0 of a multivariate polynomial to a scalar value `a`. -/ | ||
| def partialEvalFirst (a : R) (p : CMvPolynomial (n + 1) R) : CMvPolynomial n R := | ||
| bind₁ (Fin.cons (C a) X) p | ||
|
|
||
| /-! ## Evaluation lemma -/ | ||
|
|
||
| /-- `partialEvalFirst a p` correctly implements partial evaluation. -/ | ||
| theorem partialEvalFirst_eval (a : R) (p : CMvPolynomial (n + 1) R) (v : Fin n → R) : | ||
| (partialEvalFirst a p).eval v = p.eval (Fin.cons a v) := by | ||
| unfold partialEvalFirst | ||
| rw [eval_equiv, fromCMvPolynomial_bind₁] | ||
| rw [MvPolynomial.eval₂_comp_left] | ||
| have hc : (MvPolynomial.eval v).comp MvPolynomial.C = RingHom.id R := by | ||
| ext r | ||
| simp | ||
| have hv : | ||
| (⇑(MvPolynomial.eval v) ∘ | ||
| fun i => fromCMvPolynomial | ||
| (((Fin.cons (CMvPolynomial.C (n := n) a) | ||
| (fun i : Fin n => CMvPolynomial.X (R := R) i)) : | ||
| Fin (n + 1) → CMvPolynomial n R) i)) = | ||
| Fin.cons a v := by | ||
| funext i | ||
| cases i using Fin.cases with | ||
| | zero => simp [fromCMvPolynomial_C] | ||
| | succ i => simp [fromCMvPolynomial_X] | ||
| rw [hc, hv] | ||
| exact (eval_equiv (p := p) (vals := Fin.cons a v)).symm | ||
|
|
||
| /-! ## Degree preservation -/ | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
theorem partialEvalFirst_degreeOf_le {deg : ℕ} (a : R)
(i : Fin n) (p : CMvPolynomial (n + 1) R)
(hDeg : ∀ mono ∈ Lawful.monomials p, mono.degreeOf i.succ ≤ deg) :
∀ mono ∈ Lawful.monomials (partialEvalFirst a p), mono.degreeOf i ≤ deg := by
intro mono hmono
rcases subsingleton_or_nontrivial R with hR | hR
· have hmem : mono ∈ (partialEvalFirst a p).1 := by simpa using hmono
have hne := (partialEvalFirst a p).2 mono
rw [getElem?_pos _ _ hmem,
Subsingleton.elim ((partialEvalFirst a p).1[mono]'hmem) 0] at hne
exact absurd rfl hne
· <original proof body>This matters downstream: on the ArkLib consumer branch, Note I'm deliberately not suggesting restating this lemma via theorem partialEvalFirst_degreeOf_le' {deg : ℕ} (a : R) (i : Fin n)
(p : CMvPolynomial (n + 1) R) (h : CMvPolynomial.degreeOf i.succ p ≤ deg) :
CMvPolynomial.degreeOf i (partialEvalFirst a p) ≤ deg := ...
theorem partialEvalFirst_individualDegreeLE {deg : ℕ} (a : R)
(p : CMvPolynomial (n + 1) R) (h : IndividualDegreeLE deg p) :
IndividualDegreeLE deg (partialEvalFirst a p) :=
fun i => partialEvalFirst_degreeOf_le a i p (fun mono hmono => h i.succ mono hmono)The second is exactly ArkLib's wrapper in |
||
| /-- `partialEvalFirst` preserves degree bounds for each remaining variable. -/ | ||
| theorem partialEvalFirst_degreeOf_le [Nontrivial R] {deg : ℕ} (a : R) | ||
| (i : Fin n) (p : CMvPolynomial (n + 1) R) | ||
| (hDeg : ∀ mono ∈ Lawful.monomials p, mono.degreeOf i.succ ≤ deg) : | ||
| ∀ mono ∈ Lawful.monomials (partialEvalFirst a p), mono.degreeOf i ≤ deg := by | ||
| intro mono hmono | ||
| have hSupport : | ||
| ∀ s ∈ (fromCMvPolynomial p).support, s i.succ ≤ deg := by | ||
| apply MvPolynomial.degreeOf_le_iff.mp | ||
| have hdegree := congrFun (degreeOf_equiv (p := p) (S := R)) i.succ | ||
| rw [← hdegree] | ||
| unfold CMvPolynomial.degreeOf | ||
| apply Finset.sup_le | ||
| intro mono hmono | ||
| exact hDeg mono (by simpa using hmono) | ||
| have hEval : | ||
| MvPolynomial.degreeOf i (fromCMvPolynomial (partialEvalFirst a p)) ≤ deg := by | ||
| unfold partialEvalFirst | ||
| rw [fromCMvPolynomial_bind₁] | ||
| have hvars : | ||
| (fun i : Fin (n + 1) => | ||
| fromCMvPolynomial | ||
| (((Fin.cons (CMvPolynomial.C (n := n) a) | ||
| (fun i : Fin n => CMvPolynomial.X (R := R) i)) : | ||
| Fin (n + 1) → CMvPolynomial n R) i)) = | ||
| (fun j : Fin (n + 1) => | ||
| Fin.cases (MvPolynomial.C a) (fun k : Fin n => MvPolynomial.X k) j) := by | ||
| funext j | ||
| cases j using Fin.cases with | ||
| | zero => simp [fromCMvPolynomial_C] | ||
| | succ j => simp [fromCMvPolynomial_X] | ||
| rw [hvars] | ||
| exact partialEvalFirst_eval₂_degreeOf_le (n := n) (R := R) a i | ||
| (fromCMvPolynomial p) hSupport | ||
| have hdegree := congrFun (degreeOf_equiv (p := partialEvalFirst a p) (S := R)) i | ||
| rw [← hdegree] at hEval | ||
| exact (Finset.le_sup (s := (Lawful.monomials (partialEvalFirst a p)).toFinset) | ||
| (f := fun m => m.degreeOf i) (by simpa using hmono)).trans hEval | ||
|
|
||
| end CMvPolynomial | ||
|
|
||
| end CPoly | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import (verified: removal compiles).
CPolynomial.natDegreelives inCompPoly/Univariate/Basic.lean, which is already transitively imported viaCompPoly.Multivariate.CMvPolynomial—ToPoly.Implpulls in the whole MathlibPolynomialbridge for nothing.