Automate Polynomial: An Experiment in Reflection for Polynomials

3 Univariate System

3.1 Inference Rules

We define type classes that declare groups of inference rules. These classes may be extended to construct specifications for inference procedures requiring assumptions of varying strenghts (e.g. from nonsensitive to very sensitive). An implementation of an inference procudure must meet its specificaitions by implementing its inference rules.

Because we differentiate between inference rules for bases cases and inference rules for recursive cases of polynomials, inference procedures may be sensitive to varying degrees. To demonstrate:

  • Nonsensitive: greedy degree upper bound inference is nonsensitive as it never requires additional assumptions beyond the input instances

  • Somewhat sensitive: leading coefficient inference is sensitive in recursive cases but nonsensitive in base cases

  • Very sensitive: exact degree inference is sensitive as it always requires additional assumptions beyond the input instances

It should also be emphasized that when we assert that we require some type in the following definitions, we mean that we must be provided a way to compute a member of that type, not just that such a member exists, given the current assumptions as input.

Definition 3.1 Nonsensitive rules for base cases
#

Given a semiring \(R\) and a property of polynomials \(T:R[x] \to \mathtt{Type}\), nonsensitive inference of \(T\) for base cases of \(R[x]\) requires that we have the following.

  • \(T(0)\)

  • \(T(c)\) for \(c \in R\)

  • \(T(x)\)

  • \(T(x ^n)\) for \(n \in \mathbb {N}\)

The \(T(x ^n)\) case is not strictly necessary here and could have been analyzed as a result of recursive cases. However, \(T(x ^n)\) often has a nice closed form so it is included here for efficiency.

Definition 3.2 Nonsensitive rules for recursive cases
#

Given a semiring \(R\) and a property of polynomials \(T:R[x] \to \mathtt{Type}\), nonsensitive inference of \(T\) for recursive cases of \(R[x]\) requires that we have the following.

  • \(T(p^n)\) given \(T(p)\) for \(p \in R[x]\) and \(n \in \mathbb {N}\)

  • \(T(pq)\) given \(T(p)\) and \(T(q)\) for \(p,q \in R[x]\)

  • \(T(p+q)\) given \(T(p)\) and \(T(q)\) for \(p,q \in R[x]\)

Definition 3.3 Sensitive rules for base cases
#

Given a semiring \(R\) and a property of polynomials \(T:R[x] \to \mathtt{Type}\), sensitive inference of \(T\) for base cases of \(R[x]\) requires that we have the following.

  • \(T(0)\)

  • \(T(c)\) for \(c \in R \setminus \{ 0\} \)

  • \(T(x)\) when \(R\) is nontrivial

  • and \(T(x ^n)\) for \(n \in \mathbb {N}\) when \(R\) is nontrivial

The \(T(x ^n)\) case is not strictly necessary here and could have been analyzed as a result of recursive cases. However, \(T(x ^n)\) often has a nice closed form so so it is included here for efficiency.

Definition 3.4 Sensitive rules for recursive cases
#

Given a semiring \(R\) and a property of polynomials \(T:R[x] \to \mathtt{Type}\), sensitive inference of \(T\) for recursive cases of \(R[x]\) requires that we have the following.

  • \(T(p^n)\) given \(T(p)\) for \(p \in R[x]\) and \(n \in \mathbb {N}\) when \(\mathtt{leadingCoefficient}(p^n) \neq 0\)

  • \(T(pq)\) given \(T(p)\) and \(T(q)\) for \(p,q \in R[x]\) when \(\mathtt{leadingCoefficient}(p)\mathtt{leadingCoefficient}(q) \neq 0\)

  • \(T(p+q)\) given \(T(p)\) for \(p,q \in R[x]\) when \(\mathtt{degree}(p) {\gt} \mathtt{degree}(q)\)

  • \(T(p+q)\) given \(T(q)\) for \(p,q \in R[x]\) when \(\mathtt{degree}(p) {\lt} \mathtt{degree}(q)\)

  • \(T(p+q)\) given \(T(p)\) and \(T(q)\) for \(p,q \in R[x]\) when \(\mathtt{degree}(p) = \mathtt{degree}(q)\) and \(\mathtt{leadingCoefficient}(p) + \mathtt{leadingCoefficient}(q) \neq 0\)

Many useful representations have a simplified or normal form when different members of that representation are equivalent up to the target property. For instance, lists of polynomial coefficients are equivalent up to trailing zeros and can typically be simplified to an equivalent normal form with no trailing zeros. In this case, degrees and leading coefficients are easily computed from a normal form, which is reflected in Definition 3.6.

Definition 3.5 Type with a normal form
#

We say that a type \(t\) has a normal form if there is a set \(N \subseteq \{ m:t\} \) and an idempotent function \(f:t \to N\). Let \(m : t\). \(m\) is called normal if \(m \in N\). \(f(m)\) is called the normalization of \(m\).

Definition 3.6 Rules for representations with a normal form
#

Given a semiring \(R\) and a property of polynomials \(T:R[x] \to \mathtt{Type}\), inference of \(T\) for representations with a normal form requires that we have the following when equality in \(R\) is decidable.

  • a normal form for \(T(p)\) for \(p \in R[x]\)

  • DegreeEq\((p)\) given a normal \(T(p)\) for \(p \in R[x]\)

  • LeadingCoeff\((p)\) given a normal \(T(p)\) for \(p \in R[x]\)

It is also often useful to rewrite a polynomial in a standard form defined by the representation, such as with expanded and ordered terms for example.

Definition 3.7 Rules for representations that support rewriting in a standard form
#

Given a semiring \(R[x]\) and a property of polynomials \(T:R[x] \to \mathtt{Type}\), inference of \(T\) for representations that support rewriting to a standard form requires the following.

  • \(q \in R[x]\) given \(T(p)\) for \(p \in R[x]\) such that \(q = p\)

3.2 Reflection Classes

TODO (see Polynomial/Defs.lean)

3.3 Representations

TODO (see Polynomial/CoeffList.lean)

3.4 Tactics

3.4.1 Utility tactics

Utility tactics perform useful rewriting and last-mile simplification steps. They appear frequently in those reflection tactics that perform the inference procedure.

Tactic 3.1 poly_rfl_rw
#

Performs trivial rewrites to transform polynomials into the form expected by the inference procedure. For instance, 0 is rewritten as C 0.

Tactic 3.2 poly_rfl_dsimp
#

Simplifies the expression resulting from the inference procedure so that the computable representation is visible. This consists primarily of unfolding reflection class instances into the values they contain.

Tactic 3.3 poly_rfl_with
#

Frames a reflection tactic t with rewriting and last-mile simplification steps from Tactic 3.1 and Tactic 3.2. The syntax is poly_rfl_with <:> t.

Tactic 3.4 poly_infer_try
#

For use as a helper tactic in the sensitive inference procedure (see Tactic 2.1). Invokes Tactic 3.2 to unfold the expression resulting from the previous step of the sensitive inference procedure, preparing the goal for the next step.

3.4.2 Reflection tactics

Reflection tactics perform the inference procedure to resolve target goals by transforming them so that they can be resolved with last-mile automation tactics such as trivial, simp, and norm_num. The code samples are adapted from Demo/Polynomial.lean and rely on the following preamble.

import AutomatePolynomial.Reflection.Polynomial.Basic
open Polynomial Rfl
Tactic 3.5 poly_rfl_degree_le
#

Resolves goals of the form \(\mathtt{degree}(p) \leq n\) for univariate polynomials \(p\) and some \(n\) which is either a natural number or the bottom member \(\bot \).

section DegreeLe
variable [Semiring R]
example : (0     : R[X]).degree   := by poly_rfl_degree_le; trivial
example : (1     : R[X]).degree  0 := by poly_rfl_degree_le; trivial
example : (X     : R[X]).degree  1 := by poly_rfl_degree_le; trivial
example : (X ^ 2 : R[X]).degree  2 := by poly_rfl_degree_le; trivial
example : (X + 1 : R[X]).degree  1 := by poly_rfl_degree_le; trivial
end DegreeLe

Since the following tactics deal with properties that depend on whether leading terms cancel—degrees and leading coefficients—they will rely on the sensitive inference procedure (see Section 2.3) in cases where the polynomial contains recursive cases such as +, *, or ^ (disregarding X ^ n which is handled separately). Tactics with the suffix _trying perform the sensitive inference procedure to handle these cases. Alternatively, the sensitive inference procedure may be avoided by instead infering the coefficients of the polynomial and then deriving the target property from that. Tactics with the suffix _of_coeffs use this alternative when provided the the target representation of the coefficients, such as CoeffsList.

Resolves goals of the form \(\mathtt{degree}(p) = n\) for univariate polynomials \(p\) and some \(n\) which is either a natural number or the bottom member \(\bot \). In any case where we want \(\mathtt{degree}(p) \neq \bot \), we must admit that the the semiring is nontrivial, as in section DegreeEqNontrivial.

section DegreeEq
variable [Semiring R]
example : (0 : R[X]).degree =  := by poly_rfl_degree_eq
end DegreeEq

section DegreeEqNontrivial
variable [Semiring R] [Nontrivial R]
example : (1     : R[X]).degree = 0 := by poly_rfl_degree_eq
example : (X     : R[X]).degree = 1 := by poly_rfl_degree_eq
example : (X ^ 2 : R[X]).degree = 2 := by poly_rfl_degree_eq
example : (X + 1 : R[X]).degree = 1 := by poly_rfl_degree_eq_trying <:> poly_infer_try
end DegreeEqNontrivial

section DegreeEqOfCoeffs
example : (X + 1 : [X]).degree = 1 := by poly_rfl_degree_eq_of_coeffs VIA CoeffsList; simp; trivial
end DegreeEqOfCoeffs

Resolves goals of the form \(\mathtt{leadingCoefficient}(p) = c\) for univariate polynomials \(p\) and members of the relevant semiring \(c\). When the definition of \(p\) contains recursive cases such as +, *, or ^ (disregarding X ^ n which is handled separately), we must admit that the semiring is nontrivial, as in section LeadingCoeffNontrivial.

section LeadingCoeff
variable [Semiring R]
example : (0     : R[X]).leadingCoeff = 0 := by poly_rfl_leading_coeff
example : (1     : R[X]).leadingCoeff = 1 := by poly_rfl_leading_coeff
example : (X     : R[X]).leadingCoeff = 1 := by poly_rfl_leading_coeff
example : (X ^ 2 : R[X]).leadingCoeff = 1 := by poly_rfl_leading_coeff
end LeadingCoeff

section LeadingCoeffNontrivial
variable [Semiring R] [Nontrivial R]
example : (X + 1 : R[X]).leadingCoeff = 1 := by poly_rfl_leading_coeff_trying <:> poly_infer_try
end LeadingCoeffNontrivial

section LeadingCoeffEqOfCoeffs
example : (X + 1 : [X]).leadingCoeff = 1 := by poly_rfl_leading_coeff_of_coeffs VIA CoeffsList; simp
end LeadingCoeffEqOfCoeffs

The remaining tactics are generic to the representation of the target property and require the user to provide the target representation, such as CoeffsList or EvalArrow.

Tactic 3.8 poly_rfl_coeff
#

Resolves goals of the form \(\mathtt{nthCoefficient}_n(p) = c\) for univariate polynomials \(p\), natural numbers \(n\), and members of the relevant semiring \(c\). Note that we admit that the semiring is commutative.

section Coeffs
variable [CommSemiring R]
example : (0     : R[X]).coeff 1 = 0 := by poly_rfl_coeff VIA CoeffsList
example : (1     : R[X]).coeff 1 = 0 := by poly_rfl_coeff VIA CoeffsList; trivial
example : (X     : R[X]).coeff 1 = 1 := by poly_rfl_coeff VIA CoeffsList; trivial
example : (X ^ 2 : R[X]).coeff 1 = 0 := by poly_rfl_coeff VIA CoeffsList; trivial
example : (X + 1 : R[X]).coeff 1 = 1 := by poly_rfl_coeff VIA CoeffsList; simp
end Coeffs
Tactic 3.9 poly_rfl_eval
#

Resolves goals of the form \(p(c) = c'\) for univariate polynomials \(p\) and members of the relevant semiring \(c\) and \(c'\). Note that we admit that the semiring is commutative.

section Eval
variable [CommSemiring R]
example : (0     : R[X]).eval 1 = 0 := by poly_rfl_eval VIA EvalArrow
example : (1     : R[X]).eval 1 = 1 := by poly_rfl_eval VIA EvalArrow
example : (X     : R[X]).eval 1 = 1 := by poly_rfl_eval VIA EvalArrow
example : (X ^ 2 : R[X]).eval 1 = 1 := by poly_rfl_eval VIA EvalArrow; simp
example : (X + 1 : R[X]).eval 1 = 2 := by poly_rfl_eval VIA EvalArrow; norm_num
end Eval
Tactic 3.10 poly_rfl_expand
#

Resolves goals of the form \(p = q\) for a univariate polynomial \(p\) and an equivalent polynomial \(q\) in expanded form. Note that we admit that the semiring is commutative.

section Expand
variable [CommSemiring R]
example : (C 2 + X : R[X]) = X + C 2 := by poly_rfl_expand VIA CoeffsList; simp; poly_unfold_expand; simp
example : (X * C 2 : R[X]) = C 2 * X := by poly_rfl_expand VIA CoeffsList; simp; poly_unfold_expand; simp
example : (X + X   : R[X]) = C 2 * X := by poly_rfl_expand VIA CoeffsList; simp; poly_unfold_expand; norm_num
end Expand