Module func: Functional TT-format including Chebyshev interpolation

Package teneva, module func: functional TT-format with interpolation.

This module contains the functions for construction of the functional TT-representation, including Chebyshev interpolation in the TT-format, as well as calculating the values of the function using the constructed interpolation coefficients.




teneva.func.func_basis(X, m=10, kind='cheb')[source]

Compute the basis functions in the given points.

The function computes values of the first m basis functions (Chebyshev polynomials in the current version) in the given points X.

Parameters:
  • X (np.ndarray) – spatial points of interest (it is 2D array of the shape [samples, d], where d is a number of dimensions). All points should be from the interval [-1, 1] for “cheb” kind and from the interval [0, pi] for “sin” kind.

  • m (int) – maximum order of the basis function (>= 1). The first m functions (of the order 0, 1, …, m-1) will be computed.

  • kind (str) – kind of the basis (“cheb”).

Returns:

values of the basis functions of the order 0, 1, …, m-1 in X points (it is 3D array of the shape [m, samples, d]).

Return type:

np.ndarray

Examples:

X = np.array([           # Two 4-dim points
    [0., 0., 0., 0.],
    [1., 1., 1., 1.],
])

m = 3                    # Maximum order of polynomial

# Compute Chebyshev polynomials:
T = teneva.func_basis(X, m)

print(T.shape)
print(T)

# >>> ----------------------------------------
# >>> Output:

# (3, 2, 4)
# [[[ 1.  1.  1.  1.]
#   [ 1.  1.  1.  1.]]
#
#  [[ 0.  0.  0.  0.]
#   [ 1.  1.  1.  1.]]
#
#  [[-1. -1. -1. -1.]
#   [ 1.  1.  1.  1.]]]
#


teneva.func.func_diff_matrix(a, b, n, m=1, kind='cheb')[source]

Construct the differential matrix (Chebyshev or Sin) of any order.

The function returns the matrix D (if m=1), which, for the known vector y of values of a one-dimensional function on the related grid (“Chebyshev” grid if kind is “cheb” or uniform grid if kind is “sin”), gives its first derivative, i.e., y’ = D y. If the argument m is greater than 1, then the function returns a list of matrices corresponding to the first derivative, the second derivative, and so on. Note that the derivative error can be significant near the boundary of the region.

Parameters:
  • a (float) – grid lower bound.

  • b (float) – grid upper bound.

  • n (int) – grid size.

  • m (int) – the maximum order of derivative.

  • kind (str) – kind of the basis (“cheb” or “sin”).

Returns:

the differential matrices of order 1, 2, …, m if m > 1, or only one matrix corresponding to the first derivative if m = 1.

Return type:

list of np.ndarray or np.ndarray

Examples:

Let build an analytic function for demonstration:

a = -2.   # Grid lower bound
b = +3.   # Grid upper bound
n = 1000  # Grid size

# Function and its first derivative:
f     = lambda x: np.sin(x**3) + np.exp(-x**2)
f_der = lambda x: 3. * x**2 * np.cos(x**3) - 2. * x * np.exp(-x**2)

# Chebyshev grid and function values on the grid:
i = np.arange(n)
x = teneva.ind_to_poi(i, a, b, n, kind='cheb')
y = f(x)

We can compute the derivative for “y” by Chebyshev differential matrix:

D = teneva.func_diff_matrix(a, b, n)
z = D @ y

Let check the result:

z_real = f_der(x)

e_nrm = np.linalg.norm(z - z_real) / np.linalg.norm(z_real)
e_max = np.max(np.abs((z - z_real) / z_real))

print(f'Error nrm : {e_nrm:-7.1e}')
print(f'Error max : {e_max:-7.1e}')

# >>> ----------------------------------------
# >>> Output:

# Error nrm : 7.5e-13
# Error max : 1.4e-09
#

We can also calculate higher order derivatives:

D1, D2, D3 = teneva.func_diff_matrix(a, b, n, m=3)
z = [D1 @ y, D2 @ y, D3 @ y]

Let check the result:

z1_real = 3. * x**2 * np.cos(x**3) - 2. * x * np.exp(-x**2)

z2_real = 6. * x * np.cos(x**3) - 9. * x**4 * np.sin(x**3)
z2_real += - 2. * np.exp(-x**2) + 4. * x**2 * np.exp(-x**2)

z3_real = 6. * np.cos(x**3) - 18. * x**3 * np.sin(x**3)
z3_real += - 36. * x**3 * np.sin(x**3) - 27. * x**6 * np.cos(x**3)
z3_real += 4. * x * np.exp(-x**2)
z3_real += 8. * x * np.exp(-x**2) - 8. * x**3 * np.exp(-x**2)

z_real = [z1_real, z2_real, z3_real]

for k in range(3):
    e_nrm = np.linalg.norm(z[k] - z_real[k]) / np.linalg.norm(z_real[k])
    e_max = np.max(np.abs((z[k] - z_real[k]) / z_real[k]))
    print(f'Der # {k+1} | Error nrm : {e_nrm:-7.1e} | Error max : {e_max:-7.1e}')

# >>> ----------------------------------------
# >>> Output:

# Der # 1 | Error nrm : 7.5e-13 | Error max : 1.4e-09
# Der # 2 | Error nrm : 4.9e-09 | Error max : 4.3e-08
# Der # 3 | Error nrm : 1.3e-05 | Error max : 1.4e-03
#

We may also use the “sin” basis (DRAFT!!!):

a = 0.       # Grid lower bound
b = np.pi    # Grid upper bound
n = 1000     # Grid size

# Function and its first derivative:
f     = lambda x: np.sin(x)
f_der = lambda x: np.cos(x)

# Uniform grid and function values on the grid:
i = np.arange(n)
x = teneva.ind_to_poi(i, a, b, n, kind='uni')
y = f(x)
D = teneva.func_diff_matrix(a, b, n, kind='sin')
z = D @ y
z_real = f_der(x)

e_nrm = np.linalg.norm(z - z_real) / np.linalg.norm(z_real)
e_max = np.max(np.abs((z - z_real) / z_real))

print(f'Error nrm : {e_nrm:-7.1e}')
print(f'Error max : {e_max:-7.1e}')

# >>> ----------------------------------------
# >>> Output:

# Error nrm : 5.3e+02
# Error max : 3.2e+05
#


teneva.func.func_get(X, A, a=None, b=None, z=0.0, funcs=None, kind='cheb', skip_out=None)[source]

Compute the functional TT-approximation in given points (approx. f(X)).

Parameters:
  • X (np.ndarray) – spatial points of interest (it is 2D array of the shape [samples, d], where d is the number of dimensions). In the case of only one point, it may be also 1D array.

  • A (list) – TT-tensor of the interpolation coefficients (it has d dimensions).

  • a (float, list, np.ndarray) – grid lower bounds for each dimension (list or np.ndarray of length d). It may be also float, then the lower bounds for each dimension will be the same.

  • b (float, list, np.ndarray) – grid upper bounds for each dimension (list or np.ndarray of length d). It may be also float, then the upper bounds for each dimension will be the same.

  • z (float) – the value for points, which are outside the spatial grid.

  • funcs (list of callables) – optional custom basis functions.

  • kind (str) – kind of the basis (“cheb”). It is not used, kept for down compatibility.

  • skip_out (bool) – if flag is set, then the values outside the spatial grid will be set to z values.

Returns:

approximated function values in X points (it is 1D array of the shape [samples]). In the case of only one input point X, it will be float value.

Return type:

np.ndarray

Examples:

# In the beginning we compute the function values on the Chebyshev
# grid using TT-cross (see cross function for more details):
from scipy.optimize import rosen
f = lambda X: rosen(X.T)        # Target function

a = [-2., -4., -3., -2.]        # Grid lower bounds
b = [+2., +3., +4., +2.]        # Grid upper bounds
n = [5, 6, 7, 8]                # Grid size
Y0 = teneva.rand(n, r=2)        # Initial approximation for TT-cross
e = 1.E-3                       # Accuracy for TT-CROSS
eps = 1.E-6                     # Accuracy for truncation

Y = teneva.cross(lambda I: f(teneva.ind_to_poi(I, a, b, n, 'cheb')),
    Y0, e=e, log=True)
Y = teneva.truncate(Y, eps)
teneva.show(Y)

# >>> ----------------------------------------
# >>> Output:

# # pre | time:      0.003 | evals: 0.00e+00 | rank:   2.0 |
# #   1 | time:      0.010 | evals: 3.12e+02 | rank:   4.0 | e: 7.9e+04 |
# #   2 | time:      0.015 | evals: 1.09e+03 | rank:   6.0 | e: 0.0e+00 | stop: e |
# TT-tensor     4D : |5| |6| |7| |8|
# <rank>  =    3.0 :   \3/ \3/ \3/
#
# Then we should compute the TT-tensor for Chebyshev interpolation
# coefficients (see func_int function for more details):
A = teneva.func_int(Y)

teneva.show(A) # Show the result

# >>> ----------------------------------------
# >>> Output:

# TT-tensor     4D : |5| |6| |7| |8|
# <rank>  =    3.0 :   \3/ \3/ \3/
#
# Finally we compute the approximation in selected points inside
# the bounds (the values for points outside the bounds will be set as "z"):
X = np.array([
    [0., 0., 0., 0.],
    [0., 2., 3., 2.],
    [1., 1., 1., 1.],
    [1., 1., 1., 99999999],
])

Z = teneva.func_get(X, A, a, b, z=-1.)

print(Z)    # Print the result
print(f(X)) # We can check the result by comparing it to the true values

# >>> ----------------------------------------
# >>> Output:

# [ 3.00000000e+00  5.40600000e+03  7.73070497e-12 -1.00000000e+00]
# [3.0000000e+00 5.4060000e+03 0.0000000e+00 9.9999996e+17]
#

We may also compute the value for only one point:

x = np.array([0., 2., 3., 2.])
z = teneva.func_get(x, A, a, b, z=-1.)

print(z)    # Print the result
print(f(x)) # We can check the result by comparing it to the true value

# >>> ----------------------------------------
# >>> Output:

# 5405.999999999997
# 5406.0
#


teneva.func.func_gets(A, m=None, kind='cheb')[source]

Compute the functional approximation (TT-tensor) all over the new grid.

Parameters:
  • A (list) – TT-tensor of the interpolation coefficients (it has d dimensions).

  • m (int, float, list, np.ndarray) – tensor size for each dimension of the new grid (list or np.ndarray of length d). It may be also int/float, then the size for each dimension will be the same. If it is not set, then original grid size (from the interpolation) will be used.

  • kind (str) – kind of the basis (“cheb” or “sin”).

Returns:

TT-tensor of the approximated function values on the full new grid. This relates to the d-dimensional array of the shape m.

Return type:

list

Note

Sometimes additional rounding of the result is relevant. Use for this Z = teneva.truncate(Z, e) (e.g., e = 1.E-8) after the function call.

Examples:

# In the beginning we compute the function values on the Chebyshev
# grid using TT-cross (see cross function for more details):
from scipy.optimize import rosen
f = lambda X: rosen(X.T)        # Target function

a = [-2., -4., -3., -2.]        # Grid lower bounds
b = [+2., +3., +4., +2.]        # Grid upper bounds
n = [5, 6, 7, 8]                # Grid size
Y0 = teneva.rand(n, r=2)        # Initial approximation for TT-cross
e = 1.E-3                       # Accuracy for TT-CROSS
eps = 1.E-6                     # Accuracy for truncation


Y = teneva.cross(lambda I: f(teneva.ind_to_poi(I, a, b, n, 'cheb')),
    Y0, e=e, log=True)
Y = teneva.truncate(Y, eps)
teneva.show(Y)

# >>> ----------------------------------------
# >>> Output:

# # pre | time:      0.005 | evals: 0.00e+00 | rank:   2.0 |
# #   1 | time:      0.016 | evals: 3.12e+02 | rank:   4.0 | e: 6.7e+04 |
# #   2 | time:      0.028 | evals: 1.09e+03 | rank:   6.0 | e: 4.3e-09 | stop: e |
# TT-tensor     4D : |5| |6| |7| |8|
# <rank>  =    3.0 :   \3/ \3/ \3/
#
# Then we should compute the TT-tensor for Chebyshev interpolation
# coefficients (see func_int function for more details):
A = teneva.func_int(Y)

teneva.show(A) # Show the result

# >>> ----------------------------------------
# >>> Output:

# TT-tensor     4D : |5| |6| |7| |8|
# <rank>  =    3.0 :   \3/ \3/ \3/
#
m = [7, 8, 9, 10] # New size of the grid

# Compute tensor on finer grid:
Z = teneva.func_gets(A, m)

teneva.show(Z)

# >>> ----------------------------------------
# >>> Output:

# TT-tensor     4D : |7| |8| |9| |10|
# <rank>  =    3.0 :   \3/ \3/ \3/
#
# We can compute interpolation coefficients on the new grid:
B = teneva.func_int(Z)

teneva.show(B)

# >>> ----------------------------------------
# >>> Output:

# TT-tensor     4D : |7| |8| |9| |10|
# <rank>  =    3.0 :   \3/ \3/ \3/
#
# Finally we compute the approximation in selected points
# inside the bounds for 2 different approximations:
X = np.array([
    [0., 0., 0., 0.],
    [0., 2., 3., 2.],
    [1., 1., 1., 1.],
    [1., 1., 1., 99999999],
])

z1 = teneva.func_get(X, A, a, b, z=-1.)
z2 = teneva.func_get(X, B, a, b, z=-1.)

# We can check the result by comparing it to the true values:
print(z1)
print(z2)
print(f(X))

# >>> ----------------------------------------
# >>> Output:

# [ 3.00000000e+00  5.40600000e+03  7.73070497e-12 -1.00000000e+00]
# [ 3.00000000e+00  5.40600000e+03  1.20365939e-11 -1.00000000e+00]
# [3.0000000e+00 5.4060000e+03 0.0000000e+00 9.9999996e+17]
#

We may also use “sin” basis:

Y = teneva.cross(lambda I: f(teneva.ind_to_poi(I, a, b, n, 'uni')),
    Y0, e=e, log=True)
Y = teneva.truncate(Y, eps)
A = teneva.func_int(Y, kind='sin')
Z = teneva.func_gets(A, m, kind='sin')
teneva.show(Z)

# >>> ----------------------------------------
# >>> Output:

# # pre | time:      0.004 | evals: 0.00e+00 | rank:   2.0 |
# #   1 | time:      0.013 | evals: 3.12e+02 | rank:   4.0 | e: 5.7e+04 |
# #   2 | time:      0.019 | evals: 1.09e+03 | rank:   6.0 | e: 0.0e+00 | stop: e |
# TT-tensor     4D : |7| |8| |9| |10|
# <rank>  =    3.0 :   \3/ \3/ \3/
#


teneva.func.func_int(Y, kind='cheb')[source]

Compute the TT-tensor for functional interpolation coefficients.

Parameters:

Y (list) – TT-tensor with function values on the corresponding grid.

Returns:

TT-tensor that collects interpolation coefficients. It has the same shape as the given tensor Y.

Return type:

list

Note

Sometimes additional rounding of the result is relevant. Use for this A = teneva.truncate(A, e) (e.g., e = 1.E-8) after the function call.

Examples:

# In the beginning we compute the function values on the Chebyshev
# grid using TT-cross (see cross function for more details):
from scipy.optimize import rosen
f = lambda X: rosen(X.T)        # Target function

a = [-2., -4., -3., -2.]        # Grid lower bounds
b = [+2., +3., +4., +2.]        # Grid upper bounds
n = [5, 6, 7, 8]                # Grid size
Y0 = teneva.rand(n, r=2)        # Initial approximation for TT-cross
e = 1.E-3                       # Accuracy for TT-CROSS
eps = 1.E-6                     # Accuracy for truncation


Y = teneva.cross(lambda I: f(teneva.ind_to_poi(I, a, b, n, 'cheb')),
    Y0, e=e, log=True)
Y = teneva.truncate(Y, eps)
teneva.show(Y)

# >>> ----------------------------------------
# >>> Output:

# # pre | time:      0.004 | evals: 0.00e+00 | rank:   2.0 |
# #   1 | time:      0.010 | evals: 3.12e+02 | rank:   4.0 | e: 1.1e+05 |
# #   2 | time:      0.016 | evals: 1.09e+03 | rank:   6.0 | e: 6.0e-09 | stop: e |
# TT-tensor     4D : |5| |6| |7| |8|
# <rank>  =    3.0 :   \3/ \3/ \3/
#
# Then we can compute the TT-tensor for Chebyshev
# interpolation coefficients:
A = teneva.func_int(Y)

teneva.show(A)

# >>> ----------------------------------------
# >>> Output:

# TT-tensor     4D : |5| |6| |7| |8|
# <rank>  =    3.0 :   \3/ \3/ \3/
#

We may also use “sin” basis:

Y = teneva.cross(lambda I: f(teneva.ind_to_poi(I, a, b, n, 'uni')),
    Y0, e=e, log=True)
Y = teneva.truncate(Y, eps)
teneva.show(Y)

# >>> ----------------------------------------
# >>> Output:

# # pre | time:      0.004 | evals: 0.00e+00 | rank:   2.0 |
# #   1 | time:      0.010 | evals: 3.12e+02 | rank:   4.0 | e: 9.5e+04 |
# #   2 | time:      0.018 | evals: 1.09e+03 | rank:   6.0 | e: 1.4e-08 | stop: e |
# TT-tensor     4D : |5| |6| |7| |8|
# <rank>  =    3.0 :   \3/ \3/ \3/
#
# Then we can compute the TT-tensor for Sin
# interpolation coefficients:
A = teneva.func_int(Y, kind='sin')

teneva.show(A)

# >>> ----------------------------------------
# >>> Output:

# TT-tensor     4D : |5| |6| |7| |8|
# <rank>  =    3.0 :   \3/ \3/ \3/
#


teneva.func.func_int_general(Y, X, basis_func, rcond=1e-06)[source]

Compute the TT-tensor for functional interpolation coefficients.

Parameters:
  • Y (list) – TT-tensor.

  • X (list, np.ndarray) – values of continuous argument for each TT-core. It should be 2-dim array or 1-dim array if the values are the same for all TT-cores.

  • basis_func (function) – function, which corresponds to the values of the basis functions in the TT-format. It should return np.ndarray of the size n x m, where n is a number of one-dimensional points and m is a number of basis functions.

Returns:

TT-tensor, which represents the interpolation coefficients in terms of the functional TT-format.

Return type:

list

Examples:

a = -2. # Lower bound for continuous grid
b = +3. # Upper bound for continuous grid
d = 4   # Dimension of the grid
n = 10  # Number of grid points

# Build grid points:
I = np.arange(n)
X = teneva.ind_to_poi(I, a, b, n)

# Random TT-tensor:
Y = teneva.rand([n]*d, r=4)
# basis_func = TODO
# A = teneva.func_int_general(Y, X, basis_func, rcond=1e-6)


teneva.func.func_sum(A, a, b, kind='cheb')[source]

Integrate a function from its functional approximation in the TT-format.

Parameters:
  • A (list) – TT-tensor of the interpolation coefficients (it has d dimensions).

  • a (float, list, np.ndarray) – grid lower bounds for each dimension (list or np.ndarray of length d). It may be also float, then the lower bounds for each dimension will be the same.

  • b (float, list, np.ndarray) – grid upper bounds for each dimension (list or np.ndarray of length d). It may be also float, then the upper bounds for each dimension will be the same.

  • kind (str) – kind of the basis (“cheb” or “sin”).

Returns:

the value of the integral.

Return type:

float

Note

This function works only for symmetric grids!

Examples:

# In the beginning we compute the function values on the Chebyshev
# grid using TT-cross (see cheb_bld function for more details):

d = 4
def f(X): # Target function
    a = 2.
    r = np.exp(-np.sum(X*X, axis=1) / a) / (np.pi * a)**(d/2)
    return r.reshape(-1)

a = [-12., -14., -13., -11.]    # Grid lower bounds
b = [+12., +14., +13., +11.]    # Grid upper bounds
n = [50, 50, 50, 50]            # Grid size
Y0 = teneva.rand(n, r=2)        # Initial approximation for TT-cross
e = 1.E-5                       # Accuracy for TT-CROSS
eps = 1.E-6                     # Accuracy for truncation

Y = teneva.cross(lambda I: f(teneva.ind_to_poi(I, a, b, n, 'cheb')),
    Y0, e=e, log=True)
Y = teneva.truncate(Y, eps)
teneva.show(Y)

# >>> ----------------------------------------
# >>> Output:

# # pre | time:      0.004 | evals: 0.00e+00 | rank:   2.0 |
# #   1 | time:      0.013 | evals: 2.40e+03 | rank:   4.0 | e: 1.0e+00 |
# #   2 | time:      0.029 | evals: 8.40e+03 | rank:   6.0 | e: 7.4e-09 | stop: e |
# TT-tensor     4D : |50| |50| |50| |50|
# <rank>  =    1.0 :    \1/  \1/  \1/
#
# Then we should compute the TT-tensor for Chebyshev interpolation
# coefficients (see func_int function for more details):
A = teneva.func_int(Y)

teneva.show(A)

# >>> ----------------------------------------
# >>> Output:

# TT-tensor     4D : |50| |50| |50| |50|
# <rank>  =    1.0 :    \1/  \1/  \1/
#
# Finally we compute the integral:
v = teneva.func_sum(A, a, b)

print(v) # Print the result (the real value is 1.)

# >>> ----------------------------------------
# >>> Output:

# 1.000000019159871
#