{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Cross-approximation\n", "\n", "Often, we would like to build a $N$-dimensional tensor from a **black-box function** $f: \\Omega \\subset \\mathbb{R}^N \\to \\mathbb{R}$, where $\\Omega$ is a tensor product grid. That is, we are free to sample *whatever entries we want* within our domain $\\Omega$, but we cannot afford to sample the entire domain (as it contains an **exponentially large number of points**). One way to build such a tensor is using **cross-approximation** ([I. Oseledets, E. Tyrtyshnikov: \"TT-cross Approximation for Multidimensional Arrays\"](http://www.mat.uniroma2.it/~tvmsscho/papers/Tyrtyshnikov5.pdf)) from well-chosen fibers in the domain.\n", "\n", "We support two major use cases of cross-approximation in the TT format.\n", "\n", "## Approximating a Function over a Domain\n", "\n", "This is the more basic setting. We just need to specify:\n", "\n", "- Our function of interest\n", "- The tensor product domain $\\Omega = u_1 \\otimes \\dots \\otimes u_N$" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cross-approximation over a 5D domain containing 3.35544e+07 grid points:\n", "iter: 0 | eps: 9.221e-01 | total time: 0.0110 | largest rank: 1\n", "iter: 1 | eps: 4.867e-03 | total time: 0.0350 | largest rank: 4\n", "iter: 2 | eps: 4.295e-06 | total time: 0.0609 | largest rank: 7\n", "iter: 3 | eps: 8.606e-09 | total time: 0.1027 | largest rank: 10 <- converged: eps < 1e-06\n", "Did 33984 function evaluations, which took 0.001594s (2.133e+07 evals/s)\n", "\n", "5D TT tensor:\n", "\n", " 32 32 32 32 32\n", " | | | | |\n", " (0) (1) (2) (3) (4)\n", " / \\ / \\ / \\ / \\ / \\\n", "1 10 10 10 10 1\n", "\n" ] } ], "source": [ "import tntorch as tn\n", "import torch\n", "torch.set_default_dtype(torch.float64)\n", "\n", "def function(x, y, z, t, w): # Input arguments are vectors\n", " return 1 / (x + y + z + t + w) # Hilbert tensor\n", "\n", "domain = [torch.arange(1, 33) for n in range(5)]\n", "t = tn.cross(function=function, domain=domain)\n", "\n", "print(t)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sometimes it's more convenient to work with functions that accept matrices (instead of a list of vectors) as input. We can do this with the `function_arg='matrix'` flag:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cross-approximation over a 5D domain containing 3.35544e+07 grid points:\n", "iter: 0 | eps: 9.355e-01 | total time: 0.0138 | largest rank: 1\n", "iter: 1 | eps: 4.148e-03 | total time: 0.0341 | largest rank: 4\n", "iter: 2 | eps: 5.244e-06 | total time: 0.0610 | largest rank: 7\n", "iter: 3 | eps: 7.581e-09 | total time: 0.0961 | largest rank: 10 <- converged: eps < 1e-06\n", "Did 33984 function evaluations, which took 0.00437s (7.777e+06 evals/s)\n", "\n" ] } ], "source": [ "def function(Xs): # Matrix (one row per sample, one column per input variable) and return a vector with one result per sample\n", " return 1/torch.sum(Xs, dim=1)\n", "\n", "t = tn.cross(function=function, domain=domain, function_arg='matrix')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Element-wise Operations on Tensors\n", "\n", "Here we have one (or several) $N$-dimensional tensors that we want to transform element-wise. For instance, we may want to square each element of our tensor:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cross-approximation over a 5D domain containing 3.35544e+07 grid points:\n", "iter: 0 | eps: 9.539e-01 | total time: 0.0062 | largest rank: 1\n", "iter: 1 | eps: 2.066e-02 | total time: 0.0174 | largest rank: 4\n", "iter: 2 | eps: 5.644e-05 | total time: 0.0338 | largest rank: 7\n", "iter: 3 | eps: 6.255e-08 | total time: 0.0627 | largest rank: 10 <- converged: eps < 1e-06\n", "Did 33984 function evaluations, which took 0.0005157s (6.59e+07 evals/s)\n", "\n" ] } ], "source": [ "t2 = tn.cross(function=lambda x: x**2, tensors=t)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just for practice, let's do this now in a slightly different way by passing two tensors:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cross-approximation over a 5D domain containing 3.35544e+07 grid points:\n", "iter: 0 | eps: 9.757e-01 | total time: 0.0081 | largest rank: 1\n", "iter: 1 | eps: 2.939e-02 | total time: 0.0228 | largest rank: 4\n", "iter: 2 | eps: 1.086e-04 | total time: 0.0440 | largest rank: 7\n", "iter: 3 | eps: 8.331e-08 | total time: 0.0675 | largest rank: 10 <- converged: eps < 1e-06\n", "Did 33984 function evaluations, which took 0.0005171s (6.572e+07 evals/s)\n", "\n" ] } ], "source": [ "t2 = tn.cross(function=lambda x, y: x*y, tensors=[t, t])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's check the accuracy of our cross-approximated squaring operation, compared to the groundtruth `t*t`:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor(8.6986e-08)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tn.relative_error(t*t, t2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See [this notebook](arithmetics.ipynb) for more examples on element-wise tensor operations." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 2 }