Zulip Chat Archive

Stream: mathlib4

Topic: Polynomials may be made computable without big effort.


Ma, Jia-Jun (Jul 22 2025 at 08:23):

It is well known that polynomials are currently noncomputable in Mathlib. I was curious about the reason for this, and after reading the source code, I found that it is primarily due to the unnecessary use of Classical.decEq in Finsupp.onFinset_support.

To illustrate this idea, I have reimplemented some functions of Polynomial by modifying the original versions.
Making objects derived from Finsupp computable has tremendous benefits—for example, in the context of direct sums of modules, group rings, and more. I propose that motivated contributors work together to revise the implementation of Finsupp to enable computability.

See link or below for examples.

import Mathlib

/-
failed to compile definition, consider marking it as 'noncomputable' because it depends on 'Polynomial.X', and it does not have executable code
def x : Polynomial ℕ:= Polynomial.X

-/

section
namespace Finsupp'
variable {α : Type*} [DecidableEq α] [DecidableEq M]

section
variable [Zero M] [Zero N] [Zero P] [DecidableEq P]


def single (a : α)  (b :M): α →₀ M  where
  support := if b = 0 then  else {a}
  toFun := fun i => if i = a  then b else 0
  mem_support_toFun := by
    intro i
    by_cases h : b = 0 <;> simp [h]

/-- `Finsupp.onFinset s f hf` is the finsupp function representing `f` restricted to the finset `s`.
The function must be `0` outside of `s`. Use this when the set needs to be filtered anyways,
otherwise a better set representation is often available. -/
def onFinset (s : Finset α) (f : α  M) (hf :  a, f a  0  a  s) : α →₀ M where
    support := s.filter (f ·  0)
    toFun := f
    mem_support_toFun := by
      simpa


def zipWith (f : M  N  P) (hf : f 0 0 = 0) (g₁ : α →₀ M) (g₂ : α →₀ N) : α →₀ P :=
  onFinset
    (g₁.support  g₂.support)
    (fun a => f (g₁ a) (g₂ a))
    fun a (H : f _ _  0) => by
      rw [Finset.mem_union, Finsupp.mem_support_iff, Finsupp.mem_support_iff,  not_and_or]
      rintro h₁, h₂⟩; rw [h₁, h₂] at H; exact H hf

def mapRange [DecidableEq N] (f : M  N) (hf : f 0 = 0) (g : α →₀ M) : α →₀ N :=
  onFinset g.support (f  g) fun a => by
    rw [Finsupp.mem_support_iff, not_imp_not]; exact fun H => (congr_arg f H).trans hf

end

section

variable [AddZeroClass M]

#check add_zero

instance (priority := 6000) instAdd : Add (α →₀ M) :=
  Finsupp'.zipWith (fun (a b :M) => a + b ) (add_zero 0)

@[simp, norm_cast] lemma coe_zero : (0 : α →₀ M) = 0 := rfl

@[simp, norm_cast] lemma coe_add (f g : α →₀ M) : (f + g) = f + g := rfl

instance instAddZeroClass : AddZeroClass (α →₀ M) :=
   DFunLike.coe_injective.addZeroClass _ coe_zero coe_add

@[simps]
noncomputable def coeFnAddHom : (α →₀ M) →+ α  M where
  toFun := ()
  map_zero' := coe_zero
  map_add' := coe_add
end


section

variable [AddMonoid M]

/-- Note the general `SMul` instance for `Finsupp` doesn't apply as `ℕ` is not distributive
unless `F i`'s addition is commutative. -/
instance (priority := 1000) instNatSMul : SMul  (α →₀ M) where smul n v := Finsupp'.mapRange (n  ·) (nsmul_zero _) v


instance (priority := 1000) instAddMonoid : AddMonoid (α →₀ M) :=
  DFunLike.coe_injective.addMonoid _ coe_zero coe_add fun _ _ => rfl


end


instance (priority := 1000) instAddCommMonoid [AddCommMonoid M] : AddCommMonoid (α →₀ M) :=
  DFunLike.coe_injective.addCommMonoid
    DFunLike.coe coe_zero coe_add (fun _ _ => rfl)


section

variable [Semiring k] [Mul G] [DecidableEq G] [DecidableEq k]

open BigOperators in
def mul'' (f g : MonoidAlgebra k G) :  MonoidAlgebra k G :=
 by
  unfold MonoidAlgebra at *
  exact Finset.sum  f.support <| fun a =>  Finset.sum   g.support fun b =>  (Finsupp'.single (a * b) (f a * g b))

instance (priority := 6000) instMul : Mul (MonoidAlgebra k G) where
  mul := mul''

end

section

variable [Semiring k] [Add G] [DecidableEq G] [DecidableEq k]

open BigOperators in
def amul'' (f g : MonoidAlgebra k G) :  MonoidAlgebra k G :=
 by
  unfold MonoidAlgebra at *
  exact Finset.sum  f.support <| fun a =>  Finset.sum   g.support fun b =>  (Finsupp'.single (a + b) (f a * g b))

instance (priority := 6000) instAddMul : Mul (MonoidAlgebra k G) where
  mul := amul''

end


end Finsupp'
end




section
open Finsupp'
variable [Semiring R] [DecidableEq R]



instance (priority := 6000) Polynomial.instAdd : Add (Polynomial R) where
  add := fun x y =>
    by
      let a := x.1
      let b := y.1
      rw [AddMonoidAlgebra] at *
      exact a + b


#synth Mul (Polynomial R)

instance (priority := 6000) Polynomial.instMul' : Mul (Polynomial R) where
  mul := fun x y =>
    by
      let a := x.1
      let b := y.1
      rw [AddMonoidAlgebra] at *
      exact Finsupp'.amul'' a  b

instance (priority := 6000) instNatSMul : SMul R (Polynomial R) where smul r v := Finsupp'.mapRange (r * ·) (by simp) v.toFinsupp

def C : R  Polynomial R :=
  fun r => Finsupp'.single 0 r
def X := Polynomial.ofFinsupp <| Finsupp'.single 1 (1:R)

end


section

def x : Polynomial := X

#eval (C 2) + (C 3)
#eval ((x + x + x) : Polynomial )
#eval (C 2 + (x+x+x)*x + x : Polynomial )
#eval (C 1 + x) * (C 1 + x)
#eval (2 :  )  x

example : (x + x) * x =  x * (x + x) := by decide

example : (C 1 + x) * (C 1 + x) = C 1 + (2 : )  x + x*x := by decide

end

Eric Wieser (Jul 22 2025 at 08:29):

Have you looked at https://github.com/leanprover-community/mathlib4/wiki/Computation-models-for-polynomials-and-finitely-supported-functions?

Eric Wieser (Jul 22 2025 at 08:30):

The solution at the bottom of that page doesn't need the [DecidableEq M] that yours needs

Ma, Jia-Jun (Jul 22 2025 at 08:36):

So, is there any plan to rewrite Finsupp?

Eric Wieser (Jul 22 2025 at 08:37):

#25324 is the next part of my plan

Eric Wieser (Jul 22 2025 at 08:37):

Generally, fill in the DFinsupp API to match the FInsupp one


Last updated: Dec 20 2025 at 21:32 UTC