Basis Functions
This topic describes how to write your own basis functions. For those applications where the built-in polynomial and hyperplane are not applicable, this method allows you to use a custom basis function written in a script. To use a custom basis function, simply declare the function in the script and register its name using CLsqfit:SetBasisFunc . In combination with calling this method, your script must call CLsqFit:SetNumCoefs to specify the number of terms being fit in each coordinate.
NOTE: As with the built-in basis functions, the custom basis function is fit about the mean value in each coordinate dimension. The calculated means are automatically included when evaluating the fit using CLsqfit:Eval and other methods in the CLsqFit class. See the CLsqFit: Fit method.
Before getting into the details of writing a custom basis function, let's review the role of the basis function in least squares fitting. Nowhere in your script will you actually specify the function being fit. Instead, you realize the function being fit through a basis function that returns a vector whose elements each represent one term being fit.
In computing the linear least squares solution, the input data are accrued in a so-called "product-sum" matrix consisting of a functional evaluation of the each coordinate being fit. These terms are calculated by a basis function that returns a vector consisting of 1 element corresponding to evaluation of the coordinate for each term of the fitting function. For example, consider fitting a 2-dimensional polynomial with 4 terms in x and 2 terms in y. Then the least squares fit has 8 terms in total, and the basis vector likewise has 8 elements. The job of the basis function is to accrue the elements of the product-sum matrix by evaluating each point in the function being fit. Although the function may fit multiple coordinate dimensions, the basis vector is always returned by the basis function as a 1-dimensional array.
Note that the basis functions may include cross terms you do not want to fit, for example, with a multidimensional polynomial. For example, you may not want to fit the xy cross term when fitting the 2-dimensional polynomial f(x,y) = a[1] + a[2] x + a[3] y + a[4] xy. In this case, there are still 4 coefficients in the fit, but one of them is to be disregarded by forcing its value to 0. Any term in the basis function that you do not wish to fit can be excluded by calling CLsqFit: ForceCoef.
Writing a custom basis function requires understanding what it must do. In essence, the basis function turns the coordinate values of a point into a vector of basis values corresponding to each term of the fit. Your custom basis function is declared in the script asfunction MyFunc( n, V ), and is called to process each point used in the fit.
MyFunc |
Replace this with the actual name of your basis function. |
n |
This is the number of coefficients in the fit, including forced coefficients. This is the total number of coefficients set by the SetNumCoefs method. For example, if fitting a 4x2 polynomial, the value n=8 is passed to the basis function. |
V |
This is a 1-dimensional array containing all coordinate values for the point being fit. The number of elements in V is the number of fit dimensions set by SetNumCoefs . For example, if fitting a 3 dimensional function, the coordinate vector V has 3 elements, one for each each coordinate (e.g., V[1] = x, V[2] = y, and V[3] = z). |
return value |
MyFunc must return the basis vector as a 1-dimensional array containing n elements as passed to the basis function. |
The basis function returns a 1-dimensional array containing the evaluation of each point for each element of the fit. Even If fitting more than 1 dimension (i.e., more than 1 variable), the basis vector is accrued as a flat 1-dimensional array. This flattening is done in hierarchical order from the lowest order term to the highest order term, in order of the fit coefficients.
Using a 4x2 term (i.e., 2-dimensional) polynomial fit as an example, the coefficientsa[] are fit to the following function:
f(x,y) = a[1] + a[2] x + a[3] x^2 + a[4] x^3 + a[5] y + a[6] xy + a[7] x^2y + a[8] x^3y.
The number of coefficients are expressed as the number of terms in the basis vectorb[]. Hence the basis vector involves 8 elements in the following order:
b[1] = 1, b[2] = x, b[3] = x^2, b[4] = x^3, b[5] = y, b[6] = xy, b[7] = x^2y, b[8] = x^3y
The basis functions of a general n-dimensional polynomial can be written as a matrix or tensor. For the case of a 2-dimensional polynomial, the basis functions can be shown as a matrix with the x-axis terms in the top row and the y-axis terms in the left column. Each of the other elements is obtained by multiplying the marginal column and row terms as follows:
1 |
x |
x^2 |
x^3 |
y |
xy |
x^2y |
x^3y |
Notice that the top row and left column do not change. But the second row is filled-in by multiplying the marginal terms. These basis functions are returned with the matrix rows flattened into a 1-dimensional vector containing 8 elements.
Taking the 4x2 polynomial example further, suppose you wish to add a y^3 term to the fit. This would add another row to the matrix, making 12 elements in total. The third row elements are given as the top row multiplied by the new y^3 term in the left column of the 3rd row. This third row would also be added to the basis vector b[] as elements b[9] throughb[12].
The next example shows a polynomial fit with 9 coefficients a[] consisting of 3 terms in x and 3 terms in y, but with several of the polynomial orders missing. Consider this function:
f(x,y) = a[1] + a[2] x^2 + a[3] x^4 + a[4] y + a[5] x^2y + a[6] x^4y + a[7] y^3 + a[8] x^2y^3 + a[9] x^4y^3.
Written as a matrix, the terms are organized like this:
1 |
x^2 |
x^4 |
y |
x^2y |
x^4y |
y^3 |
x^2y^3 |
x^4y^3 |
The elements of basis vector b[] are then b[1] = 1, b[2] = x^2, b[3] = x^4, b[4] = y, b[5] = x^2y, b[6] = x^4y, b[7] = y^3, b[8] = x^2y^3, b[9] = x^4y^3.
Finally, there is the general case of fitting basis functions that do not follow any symmetric rules, such as fitting 5 coefficients to 3 coordinate values. An example of such a function would be a fit to f(x,y,z) using the function
f = a[1] + a[2] x + a[3] xy + a[4] z + a[5] z^2.
In this case, the basis functions are b[1] = 1, b[2] = x, b[3] = xy, b[4] = z, and b[5] = z^2. For each point (x,y,z,f), your custom basis function would receive the values x, y, and z as a 3 element vector. It would then return a 5 element basis vector with each of the terms b[i] evaluated in the basis function.
Here are some final considerations for writing your own basis functions: When a constant term is part of the fit, as in the examples above, the basis vector requires a "1" at the coefficient index. This constant term is part of both the polynomial and hyperplane functions provided in the CLsqFit class. However, the constant term is not required when writing your own basis function. In that case, your basis vector does not need to return "1" for one of the basis functions. Likewise, the polynomials described above include cross terms. However, your custom multi-dimensional basis function is not required to use cross terms.
In your script, the basis function takes the following form:
|
-- declare the function by name |
|
-- create a local table for the basis vector |
|
-- evaluate each element b[i] at V |
|
-- return the basis vector b |
|
-- end of the basis function |
|
-- create a CLsqFit object |
|
-- declare your basis function |
Note: Always declare the basis vector b as local inside the function to avoid the name b being confused with any other nameb used outside the basis function.
Several examples of a scripted basis function are given below. Always remember that coordinate vector element V[1] is used for b[2], and so on. This is required because b[1] is always the constant term.
To fit a line, the basis function would look like this:
|
-- n=2 is the number of coefs; V is the coordinate Vector |
|
-- declare a 1-dimensional array for the basis vector |
|
-- basis vector element [1] is 1 |
|
-- basis vector element [2] is V[1], or the x coordinate |
|
-- return the basis vector |
|
-- end of the basis function |
To fit a quadratic about the origin, the basis function would look like this:
|
-- n=3 is the number of coefs; V is the coordinate Vector |
|
-- declare a 1-dimensional array for the basis vector |
|
-- basis vector element [1] is 1 |
|
-- basis vector element [2] is V (the x coordinate) |
|
-- basis vector element [3] is V^2 |
|
-- return the basis vector |
|
-- end of the basis function |
To see how the parameter n might be used, consider fitting a polynomial of order n-1 (the order of a polynomial is the highest power in the polynomial). Including the "constant" term, the polynomial of order n-1 has n coefficients. Here is the basis function:
|
-- n is the number of coefs; V is the coordinate Vector |
|
-- declare a 1-dimensional array for the basis vector |
|
-- basis vector element [1] is 1 |
|
-- compute each power of the polynomial recursively |
|
-- basis vector element [n] is V^(n-1) |
|
-- end of loop |
|
-- return the basis vector |
|
-- end of the basis function |
Note that the table V can contain many elements for fitting as many as 100 coefficients and 10 independent variables, or "basis dimensions". For example, if fitting to x,y coordinates, the table V has 2 elements, V[1] for the x coordinate and V[2] for the y coordinate. The following basis function fits a warped plan to an f(x,y) surface:
|
-- n=4 is the number of coefs; V is the coordinate Vector |
|
-- declare a 1-dimensional array for the basis vector |
|
-- basis vector element b[1] is 1 (the constant term) |
|
-- b[2] = x |
|
-- b[3] = y |
|
-- b[4] = x * y |
|
-- return the basis vector |
|
-- end of the basis function |
This case is handled by the built-in, n-dimensional polynomial basis function selected using SetBasisFunc( 1 ). Mira defaults to this basis function if you do not explicitly select a basis function in the script. In this example, you would then specify a 2x2 coefficient fit using SetNumCoefs( {2,2} ). Notice that the multiple coefficient numbers are combined into a table using {}.
As a final example, consider the provided "hyperplane" function which has the form f = a[1] + a[2] x + a[3] y + a[4] z + ... The hyperplane fits n+1 coefficients for n coordinate dimensions plus the constant term a[1]. The hyperplane is selected using SetBasisFunc( 2 ). Here is how you would create a hyperplane as a user-provided script function:
|
-- n is the number of coefs; V is the coordinate Vector |
|
-- declare a 1-dimensional array for the basis vector |
|
-- basis vector element [1] is 1 (the constant term) |
|
-- b[2] is V[1], b[3] is V[2], etc for each coordinate V[i] |
|
|
|
|
|
-- return the basis vector |
|
-- end of the basis function |
This script example uses the n= 5, m=3 fit described above, at the end of the section Creating a Custom Basis Function. This script example is also used for the SetNumCoefs method. The function being fit is f = a[1] + a[2] x + a[3] xy + a[4] z + a[5] z^2:
|
-- create a CLsqFit object |
|
-- specify the basis function "MyBasisFunc" |
|
-- fit 5 coefficients in 3 dimensions |
|
|
-- add some data points |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- define the basis function |
|
|
-- receives n=5 and V[] with 3 elements |
|
|
|
-- the constant term a[1] |
|
-- the x term for a[2] |
|
-- the xy term for a[3] |
|
-- the z term for a[4] |
|
-- the z^2 term for a[5] |
|
-- return the basis vector |
|
|
|
|
|
-- fit the basis function |
CLsqFit class, SetBasisFunc , SetNumCoefs
Mira Pro x64 Script User's Guide, Copyright Ⓒ 2023 Mirametrics,
Inc. All Rights Reserved.