Skip to main content

Dividing In Lagrange basis when one of the points is zero - Generalised

Problem​

We restate the problem, however we do not assume that the elements in the domain are roots of unity.

We have f(X)g(X)=f(X)X−xm=∑i=0d−1fiLi(X)X−xm\frac{f(X)}{g(X)} = \frac{f(X)}{X - x_m} = \sum_{i=0}^{d-1} {f_i\frac{\mathcal{L_i(X)}}{X - x_m}}

Lagrange polynomial​

We briefly restate the formula for a lagrange polynomial:

Li(X)=∏j≠i,j=0X−xjxi−xj\mathcal{L_i}(X) = \prod_{j \neq i, j = 0}\frac{X -x_j}{x_i - x_j}

The i'th lagrange polynomial evaluated at xix_i is 1 and 0 everywhere else on the domain

First form of the barycentric interpolation formula​

We introduce the polynomial A(X)=(X−x0)(X−x1)...(X−xn)A(X) = (X - x_0)(X - x_1)...(X-x_n).

We also introduce the derivative of A′(X)=∑j=0d−1∏i≠j(X−xi)A'(X) = \sum_{j=0}^{d-1}\prod_{i \neq j}(X - x_i) .

You can derive this yourself by generalising the product rule: https://en.wikipedia.org/wiki/Product_rule#Product_of_more_than_two_factors

In general this derivative does not have a succint/sparse form. We do have a succint form if the domain is the roots of unity!

Now note that A′(xj)=∏i=0,i≠j(xj−xi)A'(x_j) = \prod_{i=0,i \neq j}(x_j - x_i)

If we plug in xkx_k into A′(X)A'(X) all the terms with X−xkX - x_k will vanish, this is why the sum disappears into a single product.

We can use AA and A′A' to re-define our lagrange polynomial as :

Li(X)=A(X)A′(xi)(X−xi)\mathcal{L_i}(X) = \frac{A(X)}{A'(x_i) (X - x_i)}

Looking at the original lagrange formula, A′(xi)A'(x_i) is the denominator and A(X)X−xi\frac{A(X)}{X - x_i} is the numerator.

The first barycentric form for a polynomial f(X)f(X) can now be defined as :

f(X)=∑i=0d−1A(X)A′(xi)(X−xi)fif(X) = \sum_{i=0}^{d-1}{\frac{A(X)}{A'(x_i) (X - x_i)} f_i}

Remarks​

  • A(X)A(X) is not dependent on the values of fif_i and so can be brought out of the summation.
  • A′(X)A'(X) is only dependent on the domain, so it can be precomputed, along with A(X)A(X)

Re-defining the quotient​

Note that our original problem was that:

∑i=0d−1fiLi(X)X−xm\sum_{i=0}^{d-1} {f_i\frac{\mathcal{L_i(X)}}{X - x_m}}

Had a X−xmX - x_m term in the denominator. We will use the first form as a way to get rid of this.

First we rewrite Li(X)X−xm\frac{\mathcal{L_i(X)}}{X - x_m} using the first form:

Li(X)X−xm=A(X)A′(xi)(X−xi)(X−xm)\frac{\mathcal{L_i}(X)}{X - x_m} = \frac{A(X)}{A'(x_i) (X - x_i)(X-x_m)}

We then note that:

A(X)=Lm(X)⋅A′(xm)⋅(X−xm)A(X) = \mathcal{L_m}(X) \cdot A'(x_m) \cdot (X - x_m)

I just re-arranged the formula for the first form to equal A(X)A(X) for Lm(X)\mathcal{L_m}(X)

We can hence plug this into our previous equation:

Li(X)X−xm=Lm(X)⋅A′(xm)⋅(X−xm)A′(xi)(X−xi)(X−xm)\frac{\mathcal{L_i}(X)}{X - x_m} = \frac{\mathcal{L_m}(X) \cdot A'(x_m) \cdot (X - x_m)}{A'(x_i) (X - x_i)(X-x_m)}

Simplifying since we have a X−xmX - x_m in the numerator and denominator:

Li(X)X−xm=A′(xm)⋅Lm(X)A′(xi)⋅(X−xi)\frac{\mathcal{L_i}(X)}{X - x_m} = \frac{A'(x_m) \cdot \mathcal{L_m}(X) }{A'(x_i)\cdot (X - x_i)}

This is the exact same formula that we had in Dankrad's post, but generalised! To see this, we note that when the elements in the domain are roots of unity; A′(xk)=d(xk)d−1=dx−kA'(x_k) = d(x^k)^{d-1} = dx^{-k}

The nice simplifcation here is due to two reasons: roots of unity form a cyclic group, and we can succintly represent the d'th roots of unity in a sparse equation Xd−1X^d -1 which is nice to derivate.

We have now re-defined q(X)q(X) to not include X−xmX-x_m !

We now summarise and state that:

q(X)=∑i=0d−1fiLi(X)X−xm=fiA′(xm)⋅Lm(X)A′(xi)⋅(X−xi)q(X) = \sum_{i=0}^{d-1} f_i \frac{\mathcal{L_i}(X)}{X - x_m} = f_i \frac{A'(x_m) \cdot \mathcal{L_m}(X) }{A'(x_i)\cdot (X - x_i)}

Explicit formulas for each case​

Computing qmq_m​

when dealing with the point which vanishes on zero, the above formula becomes:

Note: Lm(xm)=1\mathcal{L_m}(x_m) = 1

qm=q(xm)=∑i=0d−1A′(xm)A′(xi)fixm−xiq_m = q(x_m) = \sum_{i=0}^{d-1}\frac{A'(x_m)}{A'(x_i)} \frac{f_i}{x_m - x_i}

Computing qjq_j​

For the case that the evaluation does not vanish on the domain, we can use the original formula.

For all j≠mj \neq m

qj=q(xj)=∑i=0d−1fiLi(xj)xj−xmq_j = q(x_j) = \sum_{i=0}^{d-1} f_i \frac{\mathcal{L_i}(x_j)}{x_j - x_m}

We note that the terms of the sum are zero, except for when i=ji=j, hence we can simplify this to be:

qj=fjxj−xmq_j = \frac{f_j}{x_j - x_m}

Optimisations​

If we use the formulas as shown above, qmq_m will take dd steps due to the sum, and qjq_j will take d−1d-1 steps. We describe a way to reduce this complexity in the code.

1. Rewrite qmq_m in terms of qjq_j​

Note that if we multiply qmq_m by −1−1\frac{-1}{-1} we get:

qm=q(xm)=−∑i=0d−1A′(xm)A′(xi)fixi−xmq_m = q(x_m) = -\sum_{i=0}^{d-1}\frac{A'(x_m)}{A'(x_i)} \frac{f_i}{x_i - x_m}

We can now substite in qiq_i

qm=q(xm)=−∑i=0d−1A′(xm)A′(xi)qiq_m = q(x_m) = -\sum_{i=0}^{d-1}\frac{A'(x_m)}{A'(x_i)} q_i

2. Removing field inversions in qjq_j​

Note that qjq_j has a division which is many times more expensive than a field multiplication. We now show a way to precompute in such a way that we do not need to invert elements.

With the roots of unity, we were able to use the fact that they formed a group.

Again note that:

qj=fjxj−xmq_j = \frac{f_j}{x_j - x_m}

(Dankrad):

For our case where the domain is the discrete interval [0,255][0, 255]

We only need to precompute 1xi\frac{1}{x_i} for xi∈[−255,255]x_i \in [-255, 255]. This is 510 values, so we would store 510 * 32 = 16Kb.

How would I lookup these values?

First we imagine that we have stored the values in an array:

[11,12,13,14...1255,1−1,1−2,...1−255][\frac{1}{1}, \frac{1}{2}, \frac{1}{3}, \frac{1}{4}... \frac{1}{255},\frac{1}{-1},\frac{1}{-2},...\frac{1}{-255}]

We first note that we can easily get from 1k\frac{1}{k} to 1−k\frac{1}{-k} in the array by jumping forward 255 indices. Our strategy will be to find 1k\frac{1}{k} then jump to 1k\frac{1}{k} if we need to.

Example

We want to compute 10−255\frac{1}{0 - 255}.

  • Compute the abs(0−255)=255=iabs(0-255) = 255 = i

In practice, we can use an if statement to check whether 255 or 0 is larger, and subtract accordingly.

  • Note that 1i\frac{1}{i} is at index i−1i-1
  • Since our original computation was 0−2550 - 255 which is negative, we need to get the element at index i−1+255i - 1 + 255

3. Precompute A′(xm)A′(xi)\frac{A'(x_m)}{A'(x_i)}​

With the roots of unity, we did not need this optimisation as A′(xm)A′(xi)\frac{A'(x_m)}{A'(x_i)} equaled ωiωm\frac{\omega^i}{\omega^m} which was trivial to fetch from the domain.

For this, we will need to store precomputed values, if we want to efficiently compute qmq_m in O(d)O(d) steps, and to also avoid inversions.

(Dankrad): We precompute A′(xi)A'(x_i) and 1A′(xi)\frac{1}{A'(x_i)}. Given that we have 256 points in the domain. This will cost us 256 2 32 bytes = 16kB.

How would I lookup these values?

Similar to the previous optimisation, we store A′(xi)A'(x_i) in an array.

[A′(0),A′(1),A′(2),A′(3)...A′(255),1A′(0),1A′(1),1A′(2),...1A′(255)][A'(0), A'(1), A'(2), A'(3)... A'(255),\frac{1}{A'(0)},\frac{1}{A'(1)},\frac{1}{A'(2)},...\frac{1}{A'(255)}]

Example

We want to compute A′(0)A′(5)\frac{A'(0)}{A'(5)}

  • We can fetch A′(0)A'(0) by looking up the element at index 00 in the array.
  • We can fetch 1A′(5)\frac{1}{A'(5)} by looking up the element at index 5, then jumping forward 256 positions.

In general:

  • To fetch A(xi)A(x_i) we need to fetch the element at index ii
  • To fetch 1A(xi)\frac{1}{A(x_i)} we need to fetch the element at index i+256i + 256

Evaluate polynomial in evaluation form on a point outside of the domain​

Suppose zz is a point outside of the domain.

f(z)=∑i=0d−1fiLi(z)=∑i=0d−1A(z)A′(xi)(z−xi)fi=A(z)∑i=0d−1fiA′(xi)(z−xi)f(z) = \sum_{i=0}^{d-1}f_i\mathcal{L_i}(z) = \sum_{i=0}^{d-1}{\frac{A(z)}{A'(x_i) (z - x_i)} f_i} = A(z)\sum_{i=0}^{d-1}\frac{f_i}{A'(x_i)(z-x_i)}
  • We already store precomputations for 1A′(xi)\frac{1}{A'(x_i)}
  • We should compute z−xiz-x_i separately, then batch invert using the montgomery trick, so that we only pay for one inversion.