Module cross_act: compute user-specified function of TT-tensors

Package teneva, module cross_act: compute function of TT-tensors.

This module contains the function “cross_act” which approximates the output of the given function in the TT-format with input parameters also specified in the TT-format. Modification of the cross approximation method in the TT-format (TT-cross) is used.




teneva.cross_act.cross_act(f, X_list, Y0, e=1e-06, nswp=10, r=9999, dr=5, dr2=0, seed=None, log=False)[source]

Compute the output in the TT-format for the function of TT-tensors.

This is a draft (however, in most cases, the function already works successfully. There is a problem in the rank-1 case)!

This function computes the TT-approximation for the output of the given function in the TT-format with input parameters also specified in the TT-format. Modification of the cross approximation method in the TT-format (TT-cross) is used.

Parameters:
  • f (function) – function f(X) which computes the output elements for the given set of input points X, where X is a 2D np.ndarray of the shape [samples, D], where D is a number of function inputs. The function should return 1D np.ndarray of the length equals to samples (i.e., it should be the values of the target function for all provided samples).

  • X_list (list of lists) – several (D) TT-tensors, which are the input for the target function (f). Each TT-tensor should be represented as a list of its TT-cores. The dimension (d) and mode sizes for all tensors must match.

  • Y0 (list) – TT-tensor, which is the initial approximation for algorithm. It may be, for example, random TT-tensor, which can be built by the “rand” function from teneva: Y0 = teneva.rand(n, r), where n is a size of tensor modes (e.g., n = [5, 6, 7, 8, 9] for the 5-dimensional tensor) and r is related TT-rank (e.g., r = 1). Note that the shape of this tensor should be same as for input tensors from X_list.

  • e (float) – accuracy for SVD truncation and convergence criterion for algorithm (> 0). If between iterations the relative rate of solution change is less than this value, then the operation of the algorithm will be interrupted.

  • nswp (int) – maximum number of iterations (sweeps) of the algorithm (>= 0). One sweep corresponds to a complete pass of all tensor TT-cores from right to left and then from left to right.

  • r (int) – maximum rank for SVD operation (> 0).

  • dr (int) – rank for AMEN iterations (kickrank; >= 0).

  • dr2 (int) – additional rank for AMEN iterations (kickrank2; >= 0).

  • seed (int) – random seed. It should be an integer number or a numpy Generator class instance.

  • log (bool) – if flag is set, then the information about the progress of the algorithm will be printed after each sweep.

Returns:

TT-Tensor which approximates the output of the function.

Return type:

list

Examples:

Let consider the simple operation in the TT-format “Y = X1 * X2 + X3”:

d = 10     # Dimension of the input tensors
n = [20]*d # Mode sizes of the input tensors (it may be list)

# Random TT-tensors (inputs):
X1 = teneva.rand(n, r=3)
X2 = teneva.rand(n, r=4)
X3 = teneva.rand(n, r=5)

We can compute the exact result (output):

t = tpc()
Y_real = teneva.add(teneva.mul(X1, X2), X3)
Y_real = teneva.truncate(Y_real, e=1.E-16)
t = tpc() - t

teneva.show(Y_real)
print(f'\nTime (sec) : {t:-7.3f}')

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

# TT-tensor    10D : |20|  |20|  |20|  |20|  |20|  |20|  |20|  |20|  |20|  |20|
# <rank>  =   17.0 :    \17/  \17/  \17/  \17/  \17/  \17/  \17/  \17/  \17/
#
# Time (sec) :   0.005
#

We set all parameters (note that only “f”, “X_list” and “Y0” are required):

def f(X):
    # Function should compute the output elements for the given set
    # of input points X (array "[samples, D]"; in our case, D=3).
    # The function should return 1D np.ndarray of the length "samples"
    # with values of the target function for all provided samples.
    return X[:, 0] * X[:, 1] + X[:, 2]
# The input of the function (note that the dimension
# and mode sizes for all tensors must match):
X_list = [X1, X2, X3]

# Random initial approximation for the output (note that
# the shape of this tensor should be same as for X1, X2, X3):
Y0     = teneva.rand(n, r=1)

e      = 1.E-6  # Accuracy and convergence criterion (optional)
nswp   = 10     # Maximum number of iterations (optional)
r      = 9999   # Maximum rank for SVD operation (optional)
dr     = 3      # Rank ("kickrank") for AMEN (optional)
dr2    = 1      # Additional rank for AMEN (optional)
log    = True   # If true, then logs will be presented (optional)

And now we can run the function:

t = tpc()
Y = teneva.cross_act(f, X_list, Y0, e, nswp, r, dr, dr2, seed=123, log=log)
Y = teneva.truncate(Y, e=1.E-16)
t = tpc() - t

print('\nResult:')
teneva.show(Y)

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

# == cross-act #    1 | e:  1.4e+00 | r:   7.0
# == cross-act #    2 | e:  8.3e-01 | r:  13.0
# == cross-act #    3 | e:  4.3e-01 | r:  19.0
# == cross-act #    4 | e:  1.8e-01 | r:  20.0
# == cross-act #    5 | e:  3.5e-15 | r:  20.0
#
# Result:
# TT-tensor    10D : |20|  |20|  |20|  |20|  |20|  |20|  |20|  |20|  |20|  |20|
# <rank>  =   18.4 :    \18/  \17/  \19/  \20/  \17/  \18/  \19/  \19/  \18/
#

Finally, we can check the result:

eps = teneva.accuracy(Y, Y_real)

print(f'Time (sec) : {t:-7.3f}')
print(f'Error      : {eps:-7.1e}')

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

# Time (sec) :   0.213
# Error      : 0.0e+00
#

Note that for this example, we do not have a gain in time, however, if we consider a more complex function of arguments in the TT-format, then the situation will change dramatically, since a general function cannot be calculated using simple operations in the TT-format. For example:

d = 5      # Dimension of the input tensors
n = [10]*d # Mode sizes of the input tensors (it may be list)

# Random TT-tensors (inputs):
X1 = teneva.rand(n, r=3)
X2 = teneva.rand(n, r=4)
X3 = teneva.rand(n, r=5)
def f(X):
    return np.exp(-0.1 * X[:, 0]**2) + X[:, 1] + 0.42 * np.sin(X[:, 2]**2)
t = tpc()
Y = teneva.rand(n, r=1)
Y = teneva.cross_act(f, [X1, X2, X3], Y, log=True)
Y = teneva.truncate(Y, e=1.E-10)
t = tpc() - t

print('\nResult:')
teneva.show(Y)

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

# == cross-act #    1 | e:  1.3e+00 | r:  10.8
# == cross-act #    2 | e:  3.5e-01 | r:  17.8
# == cross-act #    3 | e:  3.9e-01 | r:  23.9
# == cross-act #    4 | e:  3.9e-01 | r:  29.8
# == cross-act #    5 | e:  4.1e-01 | r:  35.7
# == cross-act #    6 | e:  3.8e-01 | r:  41.6
# == cross-act #    7 | e:  3.6e-01 | r:  47.4
# == cross-act #    8 | e:  2.9e-01 | r:  53.2
# == cross-act #    9 | e:  2.4e-01 | r:  59.1
# == cross-act #   10 | e:  1.5e-01 | r:  64.6
# == cross-act #   11 | e:  6.1e-02 | r:  65.7
#
# Result:
# TT-tensor     5D : |10|  |10|   |10|   |10|  |10|
# <rank>  =   63.0 :    \10/  \100/  \100/  \10/
#

We can check the accuracy from comparison with the full tensor:

X1_full = teneva.full(X1).reshape(-1, order='F').reshape(-1, 1)
X2_full = teneva.full(X2).reshape(-1, order='F').reshape(-1, 1)
X3_full = teneva.full(X3).reshape(-1, order='F').reshape(-1, 1)

Y_full = teneva.full(Y).reshape(-1, order='F')

Y_real = f(np.hstack((X1_full, X2_full, X3_full)))

e_nrm = np.linalg.norm(Y_full - Y_real) / np.linalg.norm(Y_real)
e_max = np.max(np.abs(Y_full - Y_real))

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

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

# Error norm : 3.0e-15
# Error max  : 3.8e-14
#