11

Consider the following inverse triangular formula

$$\left( \begin{array}{ccccc} & & & & N_{i-p,p}\left(u_0\right) \\ & & N_{i-2,2}\left(u_0\right) & & \\ & N_{i-1,1}\left(u_0\right) & & & \\ \color{red}{N_{i,0}\left(u_0\right)=1} & & N_{i-1,2}\left(u_0\right) & \cdots & \vdots \\ & N_{i,1}\left(u_0\right) & & & \\ & & N_{i,2}\left(u_0\right) & & \\ & & & & N_{i,p}\left(u_0\right) \end{array} \right)$$

where, $N_{i,0}=1$, and enter image description here

In addition, $u_0 \in [u_i,u_{i+1})$ knots = $\{u_0,u_1, \cdots, u_m\}, 0\leq u_i \leq u_j$


Here is a procedural implementaion calculate $\color{blue}{N_{i-p,p}(u_0),B_{i-p+1,p}(u_0), \cdots, N_{i,p}(u_0)}$

  • Search the index $i$ by the auxiliary function searchSpan

  • enter image description here

In the code, I use the following local array to store the values of $N_{m,n}$

$$ \left( \begin{array}{ccccc} & & & & N_{i-p,p}\left(u_0\right) \\ & & & & \\ & & N_{i-2,2}\left(u_0\right) & & \vdots \\ & N_{i-1,1}\left(u_0\right) & N_{i-1,2}\left(u_0\right) & \cdots & \\ N_{i,0}\left(u_0\right) & N_{i,1}\left(u_0\right) & N_{i,2}\left(u_0\right) & \cdots & N_{i,p}\left(u_0\right) \end{array} \right)_{(p+1)\times (p+1)} $$

where $N_{m,n}$ was stored in the position $(p+1-i+m,n+1)$ of local array

Search the index of span $[u_i,u_{i+1})$

searchSpan[{deg_, knots_}, u0_] :=
 Module[{biSearch},
  biSearch =
   Function[{low, high},
    With[{mid = Floor[(low + high)/2]}, 
     If[u0 < knots[[mid]], {low, mid}, {mid, high}]]
   ];(*Do bisection search*)
  First@
   NestWhile[
    biSearch[Sequence @@ #, u0] &,
    {deg + 1, Length@knots - deg}, Subtract @@ # != -1 &] - 1
]

NonzeroBasis[{deg_, knots_}, u0_] :=
 Module[{coeff, basis, i},
  coeff =
   (u0 - knots[[#1 + 1]])/(knots[[#1 + #2 + 1]] - knots[[#1 + 1]]) &;
  basis = ConstantArray[1, {deg + 1, deg + 1}];
  i = searchSpan[{deg, knots}, u0];
  Do[
   basis[[deg + 1 - k, k + 1]] =
    (1 - coeff[i - k + 1, k]) basis[[deg + 2 - k, k]];
   With[{m = deg + 1 - i},
    Do[
     basis[[m + j, k + 1]] =
      {coeff[j, k], 1 - coeff[j + 1, k]}.{basis[[m + j, k]], basis[[m + j + 1, k]]},
     {j, i - k + 1, i - 1}]
    ];
   basis[[deg + 1, k + 1]] =
    coeff[i, k] basis[[deg + 1, k]],
   {k, deg}];
   basis
 ]

Test

knots = {0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 10};
deg = 4;
NonzeroBasis[{deg, knots}, 5/2] // MatrixForm

$\left( \begin{array}{ccccc} 1 & 1 & 1 & 1 & \frac{1}{288} \\ 1 & 1 & 1 & \frac{1}{48} & \frac{227}{1152} \\ 1 & 1 & \frac{1}{8} & \frac{23}{48} & \frac{205}{384} \\ 1 & \frac{1}{2} & \frac{3}{4} & \frac{15}{32} & \frac{25}{96} \\ 1 & \frac{1}{2} & \frac{1}{8} & \frac{1}{32} & \frac{1}{192} \end{array} \right)$


Performance test

knots0 = 
 Join[ConstantArray[0, 3001], Range[1, 5000], ConstantArray[5001, 3001]];
deg0 = 3000;
NonzeroBasis[{deg0, knots0}, 2.5]; // AbsoluteTiming

enter image description here

Question

  • How to implement this triangular formula in a non-procedural(like functional or rule-based) method?
  • How to improve the efficience of NonzeroBasis?

Update

Another example I found today was the calculation of Bernstein function

$$B_{n,i}(u)=\binom n i u^i(1-u)^{n-i}$$, where $0 \leq u \leq 1$

In addition, Bernstein function owns the following recursive relationship:

$$B_{n,i}(u)=(1-u) B_{n-1,i}(u)+uB_{n-1,i-1}(u)$$

where, $B_{n,i}(u)=0$ when $i<0$ or $i>n$

So we can use the following triangular schematic digram to calculate $\color{blue}{B_{n,0}(u),B_{n,0}(u), \cdots, B_{n,n}(u)}$

$$\left( \begin{array}{ccccc} \text{} & \text{} & \text{} & \text{} & B_{n,0} (u) \\ \text{} & \text{} & \text{} & .\cdot{}^{\cdot} & \text{} \\ \text{} & \text{} & B_{2,0}(u)& \text{} & \text{} \\ \text{} & B_{1,0} (u) & \text{} & \text{} & \text{} \\ \color{red}{B_{0,0} (u)=1} & \text{} & B_{2,1}(u)& \vdots & \vdots \\ \text{} & B_{1,0}(u) & \text{} & \text{} & \text{} \\ \text{} & \text{} & B_{2,2}(u)& \text{ } & \text{} \\ \text{} & \text{} & \text{} & \ddots & \text{} \\ \text{} & \text{} & \text{} & \text{} & B_{n,n} (u) \\ \end{array} \right)$$

Related question

xyz
  • 605
  • 4
  • 38
  • 117
  • I find your explanation confusing. Not blaming, perhaps I just need another point of view. Do you perchance have any link explaining the "inverse triangular recursion"? – Dr. belisarius Aug 14 '15 at 12:08
  • @belisarius, Sorry for my confusing description. In J.M's question, triangular recursion owns the following style $$\begin{array}{}T_0^{(0)}&T_1^{(0)}&T_2^{(0)}&T_3^{(0)}\T_0^{(1)}&T_1^{(1)}&T_2^{(1)}&\T_0^{(2)}&T_1^{(2)}&&\T_0^{(3)}&&&\end{array}$$So I add a prefix inverse before the triangular recursion to distinguish them. – xyz Aug 14 '15 at 12:12
  • Ok, thanks a lot. – Dr. belisarius Aug 14 '15 at 12:18
  • Is knots always a sorted array? 2. Is the exact result necessary i.e. MachinePrecision real number can't be used?
  • – xzczd Aug 16 '15 at 09:42
  • @xzczd, (1) Yes, knots is always a sorted vector that owns the style ${u_0,u_1,\cdots, u_m}$, where, $0 \leq u_i \leq u_j$ (2) No, theMachinePrecisionreal number could be used. In addition, I tried the Compile, but it failed. – xyz Aug 16 '15 at 10:05
  • Why your searchSpan outputs 6 for 5/2? I think it'll be better to make it output 7, so we can implement the definition at the beginning directly. Currently, something like `coelist = With[{u = knots, p = deg},
  • Table[{(u0 - u[[j]])/(u[[j + k]] - u[[j]]), 1 - (u0 - u[[j + 1]])/(u[[j + k + 1]] - u[[j + 1]])}, {k, 1, p}, {j, i - k, i}]]` won't give the correct result.

    – xzczd Aug 16 '15 at 11:12
  • @xzczd, searchSpan outputs 6 for 5/2 is right. Because $5/2 \in [2,3)$ in knots = {0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 10}; and $u_6=2$. Here, the first $0$ in knots is $u_0$, not $u_1$ – xyz Aug 16 '15 at 11:18
  • …OK, I see, but personally I think beginning from $0$ isn't very convenient here, and there's already another $u_0$ in the formula, right? – xzczd Aug 16 '15 at 11:46
  • @xzczd, Um, I use the symbol u0 to denonte the value of the varible $u$, rather than the first value of knots.:) – xyz Aug 16 '15 at 12:30