Univariate Theory of Functional Connections

Univariate Theory of Functional Connections#

This tutorial is based on the theory from and notation found in The Multivariate Theory of Functional Connections: Theory, Proofs, and Application in Partial Differential Equations: even though this journal article focuses on the multivariate TFC, its opening sections discuss the univariate TFC in detail. This journal article is open access and therefore free to download, and the interested user is encouraged to refer to it where necessary to solidify their understanding of TFC.

Creating the univariate class#

This tutorial is designed to familiarize the user with the univariate TFC class. To begin, let’s import it.

[1]:
from tfc import utfc

The univariate class has one required keyword argument, but typically two are specified. The required keyword argument is \(x_f\), which is the right-hand side of the domain. The non-required but typically specified keyword argument is \(x_0\), which is the left-hand side of the domain. For now, suppose we are working on the domain \(x\in[0,1]\).

The univariate class has 3 required positional arguments, they are:

  1. \(N\) - the number of points used when creating the x and z vectors; the discretization points for the problem domain and free function domain respectively.

  2. \(n_C\) - are the basis functions to be removed.

  3. \(m\) - the degree of the basis function expansion. In general, this is one larger than the total number of basis functions.

The first and third of these required arguments are simple, and are specified as integers. The second can be a bit more complicated.

To use least-squares optimization option for TFC, one must remove the terms in the basis function expansion that are linearly dependent to the support functions used to derive the constrained expression. If the basis functions used are polynomials (the typical choice) and the support functions used are monomials (the typical choice), then on typically needs to remove the first \(n_C\) basis functions, where \(n_C\) is the number of constraints. For this typical case, an integer is all that is needed for this third required argument.

Suppose that we wanted to simply remove the first two basis functions.

[2]:
myTFC = utfc(100,2,5,x0=0,xf=1)
print("Number of basis functions: "+str(myTFC.basisClass.m-myTFC.basisClass.numC))
Number of basis functions: 4

The total number of basis functions is equal to the degree specified plus one, i.e., 5+1=6, minus the number of constraints, two, for a total of four. Internally, the univariate TFC class creates an array that stores the basis function terms that are removed: when specifying an integer, this list is merely the first \(n_C\) terms.

[3]:
print(myTFC.nC)
[0 1]

Naturally, one could have specified \(n_C\) using a list (or array) in the first place. Suppose in this case that we want to remove the constant and \(x^2\) term, i.e., the basis function terms 0 and 2.

[4]:
myTFC = utfc(100,[0,2],5,x0=0,xf=1)
print("Number of basis functions: "+str(myTFC.basisClass.m-myTFC.basisClass.numC))
print("Basis functions removed: "+str(myTFC.nC))
Number of basis functions: 4
Basis functions removed: [0 2]

If one wanted to keep all basis functions, this could accomplished by setting nC = -1.

The univariate TFC class also contains the following optional keyword arguments:

  • x0 - This optional keyword argument is used to specify the beginning of the differential equation domain. The default is 0.

  • basis - This optional string keyword argument specifies the functions that will be used. The default is Chebyshev orthogonal polynomials. It can be specified as:

    • CP - Chebyshev orthogonal polynomials

    • LeP - Legendre orthogonal polynomials

    • FS - Fourier basis

    • ELMReLU - ELM that uses the rectified linear unit as the nonlinear activation function

    • ELMTanh - ELM that uses the hyberbolic tangent as the nonlinear activation function.

    • ELMSigmoid - ELM that uses the sigmoid as the nonlinear activation function

    • ELMSinh - ELM that uses swish as the nonlinear activation function

    • ELMSin - ELM that uses sin as the nonlinear activation function

For example, suppose we wanted to create a univariate TFC class that uses the first 4 terms of the Fourier basis on the domain \(x\in[3,7]\). This could be done using:

[5]:
myTFC = utfc(10,2,3,x0=3,xf=7)

Using the basis functions#

The main purpose of the univariate TFC class is to provide a way to easily create and take derivatives of the linear combinations of functions that form the free function. These are easily accessed using via the H method.

[6]:
x = myTFC.x
H = myTFC.H
print(H(x))
[[ 1.         -1.        ]
 [ 0.76604444 -0.5       ]
 [ 0.17364818  0.5       ]
 [-0.5         1.        ]
 [-0.93969262  0.5       ]
 [-0.93969262 -0.5       ]
 [-0.5        -1.        ]
 [ 0.17364818 -0.5       ]
 [ 0.76604444  0.5       ]
 [ 1.          1.        ]]

The H method also contains an optional keyword argument boolean argument called full that can be used to neglect the the removal of the terms specified by \(n_C\).

[7]:
print(H(x,full=True))
[[ 1.         -1.          1.         -1.        ]
 [ 1.         -0.93969262  0.76604444 -0.5       ]
 [ 1.         -0.76604444  0.17364818  0.5       ]
 [ 1.         -0.5        -0.5         1.        ]
 [ 1.         -0.17364818 -0.93969262  0.5       ]
 [ 1.          0.17364818 -0.93969262 -0.5       ]
 [ 1.          0.5        -0.5        -1.        ]
 [ 1.          0.76604444  0.17364818 -0.5       ]
 [ 1.          0.93969262  0.76604444  0.5       ]
 [ 1.          1.          1.          1.        ]]

In addition to H, the TFC class also provides access to dH, d2H, d4H, and d8H which can be used to access the first, second, fourth, and eighth derivative of H directly. The function H and the derivative variants are all encoded as JAX primitives. Therefore, one can take derivatives of them using the JAX transformations or even run them via the JIT compiler. Of course, since these are non-scalar functions, one must use an elementwise gradient rather than the standard grad transformation available from JAX. The TFC package comes equippped with an elemenwise gradient function.

Note

The JAX primitives of the TFC basis functions are only encoded for derivatives up to order eight. If for some reason you need higher order derivatives, please either switch to the Python basis function backend (see basis function backends tutorial for details) or file an issue on the TFC GitHub and we will add them.

[8]:
import jax.numpy as np
from jax import jit
from tfc.utils import egrad
dH = myTFC.dH
dHjax = jit(egrad(H))
assert(np.all(dH(x)==dHjax(x)))

The user is encouraged to consult the JAX GitHub and the JAX documentation for more information on the JAX package.