Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

Raw File Download
Permalink

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
content badge Iframe embedding
swh:1:cnt:7dbc9b3681f9d602de97dfead010d662b9135130
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Differentiable TT Cross-approximation: Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tntorch as tn\n",
    "import torch\n",
    "torch.set_default_dtype(torch.float64)  # Note: float32 is sometimes unstable\n",
    "torch.manual_seed(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example 1: Single Optimization\n",
    "\n",
    "We will optimize the problem $\\arg \\min_{\\mathcal{T}} \\|\\cos \\mathcal{T}\\|$ (element-wise cosine) using Adam on the adaptive TT-cross interpolation formula."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0000: loss=133.653\n",
      "0200: loss=41.3387\n",
      "0400: loss=4.57952\n",
      "0600: loss=1.38465\n",
      "0800: loss=0.503446\n",
      "1000: loss=0.270926\n",
      "1200: loss=0.19317\n",
      "1400: loss=0.153916\n",
      "1600: loss=0.130659\n",
      "1800: loss=0.115443\n"
     ]
    }
   ],
   "source": [
    "# Initial solution; we constrain our search to rank-5 tensors\n",
    "t = tn.rand(32, 32, 32, ranks_tt=5, requires_grad=True)\n",
    "\n",
    "function = lambda x: torch.cos(x)\n",
    "\n",
    "# Optimization\n",
    "optimizer = torch.optim.Adam(t.cores)\n",
    "for it in range(2000):\n",
    "    optimizer.zero_grad()\n",
    "    \n",
    "    if it % 400 == 0:  # Update cross indices\n",
    "        _, info = tn.cross(tensors=[t], function=function, verbose=False, return_info=True)\n",
    "\n",
    "    # Forward pass\n",
    "    t2 = tn.cross_forward(tensors=[t], function=function, info=info)\n",
    "    loss = tn.norm(t2)\n",
    "\n",
    "    if it % 200 == 0:\n",
    "        print('{:04}: loss={:g}'.format(it, loss))\n",
    "    loss.backward()\n",
    "    optimizer.step()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3D TT tensor:\n",
      "\n",
      " 32  32  32\n",
      "  |   |   |\n",
      " (0) (1) (2)\n",
      " / \\ / \\ / \\\n",
      "1   5   5   1\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[[1.5708, 1.5707, 1.5707,  ..., 1.5705, 4.7115, 1.5705],\n",
      "         [1.5707, 1.5706, 1.5706,  ..., 1.5702, 4.7118, 1.5707],\n",
      "         [1.5704, 1.5704, 1.5704,  ..., 1.5705, 4.7118, 1.5704],\n",
      "         ...,\n",
      "         [1.5710, 1.5709, 1.5707,  ..., 1.5700, 4.7114, 1.5707],\n",
      "         [1.5707, 1.5706, 1.5707,  ..., 1.5707, 4.7118, 1.5705],\n",
      "         [1.5702, 1.5702, 1.5702,  ..., 1.5706, 4.7118, 1.5703]],\n",
      "\n",
      "        [[1.5703, 1.5703, 1.5704,  ..., 1.5704, 4.7117, 1.5704],\n",
      "         [1.5703, 1.5703, 1.5703,  ..., 1.5704, 4.7116, 1.5704],\n",
      "         [1.5704, 1.5703, 1.5704,  ..., 1.5704, 4.7117, 1.5704],\n",
      "         ...,\n",
      "         [1.5703, 1.5703, 1.5703,  ..., 1.5704, 4.7116, 1.5704],\n",
      "         [1.5703, 1.5703, 1.5704,  ..., 1.5704, 4.7119, 1.5704],\n",
      "         [1.5704, 1.5704, 1.5704,  ..., 1.5704, 4.7115, 1.5704]],\n",
      "\n",
      "        [[1.5702, 1.5703, 1.5703,  ..., 1.5704, 4.7114, 1.5703],\n",
      "         [1.5705, 1.5704, 1.5704,  ..., 1.5704, 4.7116, 1.5705],\n",
      "         [1.5707, 1.5705, 1.5706,  ..., 1.5702, 4.7117, 1.5706],\n",
      "         ...,\n",
      "         [1.5700, 1.5701, 1.5702,  ..., 1.5706, 4.7117, 1.5702],\n",
      "         [1.5703, 1.5703, 1.5703,  ..., 1.5704, 4.7115, 1.5704],\n",
      "         [1.5707, 1.5706, 1.5705,  ..., 1.5703, 4.7114, 1.5705]],\n",
      "\n",
      "        ...,\n",
      "\n",
      "        [[1.5704, 1.5704, 1.5704,  ..., 1.5704, 4.7113, 1.5703],\n",
      "         [1.5706, 1.5705, 1.5705,  ..., 1.5703, 4.7116, 1.5706],\n",
      "         [1.5707, 1.5705, 1.5706,  ..., 1.5703, 4.7117, 1.5706],\n",
      "         ...,\n",
      "         [1.5703, 1.5703, 1.5703,  ..., 1.5704, 4.7116, 1.5704],\n",
      "         [1.5704, 1.5704, 1.5704,  ..., 1.5704, 4.7114, 1.5704],\n",
      "         [1.5705, 1.5705, 1.5705,  ..., 1.5703, 4.7114, 1.5704]],\n",
      "\n",
      "        [[1.5704, 1.5704, 1.5704,  ..., 1.5703, 4.7115, 1.5704],\n",
      "         [1.5704, 1.5705, 1.5704,  ..., 1.5706, 4.7112, 1.5703],\n",
      "         [1.5704, 1.5705, 1.5704,  ..., 1.5704, 4.7111, 1.5703],\n",
      "         ...,\n",
      "         [1.5702, 1.5703, 1.5704,  ..., 1.5707, 4.7115, 1.5703],\n",
      "         [1.5704, 1.5705, 1.5703,  ..., 1.5702, 4.7110, 1.5703],\n",
      "         [1.5705, 1.5706, 1.5705,  ..., 1.5704, 4.7115, 1.5705]],\n",
      "\n",
      "        [[1.5701, 1.5702, 1.5702,  ..., 1.5704, 4.7115, 1.5703],\n",
      "         [1.5704, 1.5704, 1.5704,  ..., 1.5704, 4.7116, 1.5704],\n",
      "         [1.5707, 1.5706, 1.5706,  ..., 1.5702, 4.7117, 1.5706],\n",
      "         ...,\n",
      "         [1.5699, 1.5700, 1.5701,  ..., 1.5706, 4.7118, 1.5702],\n",
      "         [1.5702, 1.5703, 1.5703,  ..., 1.5703, 4.7116, 1.5704],\n",
      "         [1.5707, 1.5707, 1.5706,  ..., 1.5702, 4.7114, 1.5706]]],\n",
      "       grad_fn=<AsStridedBackward>)\n"
     ]
    }
   ],
   "source": [
    "print(t.torch())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example 2: Joint Optimization\n",
    "\n",
    "We will now find $\\mathcal{T}_1, \\mathcal{T}_2$ such that $\\|\\mathcal{T}_1^2 + \\mathcal{T}_2^2 - 1\\|$ is minimal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0000: loss=2407.16\n",
      "0200: loss=275.716\n",
      "0400: loss=125.413\n",
      "0600: loss=71.8586\n",
      "0800: loss=46.5382\n",
      "1000: loss=30.6674\n",
      "1200: loss=19.8756\n",
      "1400: loss=13.2586\n",
      "1600: loss=9.2578\n",
      "1800: loss=6.68817\n"
     ]
    }
   ],
   "source": [
    "t1 = tn.rand(16, 16, 16, 16, ranks_tt=3, requires_grad=True)\n",
    "t2 = tn.rand(16, 16, 16, 16, ranks_tt=3, requires_grad=True)\n",
    "\n",
    "function = lambda x, y: x**2 + y**2\n",
    "\n",
    "# Optimization\n",
    "optimizer = torch.optim.Adam(t1.cores + t2.cores)\n",
    "for it in range(2000):\n",
    "    optimizer.zero_grad()\n",
    "    \n",
    "    if it % 400 == 0:  # Update cross indices\n",
    "        _, info = tn.cross(tensors=[t1, t2], function=function, verbose=False, return_info=True)\n",
    "\n",
    "    # Forward pass\n",
    "    t3 = tn.cross_forward(tensors=[t1, t2], function=function, info=info)\n",
    "    loss = tn.norm(t3-1)\n",
    "\n",
    "    if it % 200 == 0:\n",
    "        print('{:04}: loss={:g}'.format(it, loss))\n",
    "    loss.backward()\n",
    "    optimizer.step()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example 3: Fixed Grid\n",
    "\n",
    "Differentiable cross-approximation can be also used to find parameters $\\pmb{\\theta}$ such that $f(\\pmb{x}, \\pmb{\\theta})$ satisfies some property for all $\\pmb{x} \\in \\Omega$, where $\\Omega$ is a fixed (no to be optimized) grid of points.\n",
    "\n",
    "In this toy example, we will find $w_1, w_2, b$ such that the simple neural network $\\tanh(w_1 x_1 + w_2 x_2 + b)$ is as close to $0.5$ as possible for all $(x_1, x_2) \\in [-1, 1]^2$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "N = 2\n",
    "I = 64\n",
    "domain = [torch.linspace(-1, 1, I) for n in range(N)]\n",
    "\n",
    "\n",
    "# Super-simple 1-layer MLP\n",
    "class Net(torch.nn.Module):\n",
    "    \n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        self.linear = torch.nn.Linear(N, 1, bias=True)\n",
    "        \n",
    "    def forward(self, Xs):\n",
    "        Xs = self.linear(Xs)\n",
    "        return torch.tanh(Xs)\n",
    "\n",
    "    \n",
    "net = Net()\n",
    "function = net.forward"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let us visualize the network's response over the domain $\\Omega = [-1, 1]^2$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cross-approximation over a 2D domain containing 4096 grid points:\n",
      "iter: 0  | eps: 5.079e-01 | total time:   0.0063 | largest rank:   1\n",
      "iter: 1  | eps: 1.222e-05 | total time:   0.0261 | largest rank:   4\n",
      "iter: 2  | eps: 1.377e-09 | total time:   0.0378 | largest rank:   7 <- converged: eps < 1e-06\n",
      "Did 2304 function evaluations, which took 0.001031s (2.235e+06 evals/s)\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def visualize_net():\n",
    "    t = tn.cross(domain=domain, function=function, function_arg='matrix')\n",
    "    plt.figure()\n",
    "    plt.imshow(t.numpy())\n",
    "    plt.colorbar()\n",
    "    plt.show()\n",
    "    \n",
    "visualize_net()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now optimize the network:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0000: loss=33.3485\n",
      "0200: loss=18.9223\n",
      "0400: loss=7.43317\n",
      "0600: loss=1.24353\n",
      "0800: loss=0.0103478\n",
      "1000: loss=0.00480166\n",
      "1200: loss=0.00513987\n",
      "1400: loss=0.00359209\n",
      "1600: loss=0.00339219\n",
      "1800: loss=0.00377836\n"
     ]
    }
   ],
   "source": [
    "# Optimization\n",
    "optimizer = torch.optim.Adam(net.parameters())\n",
    "for it in range(2000):\n",
    "    optimizer.zero_grad()\n",
    "    \n",
    "    if it % 400 == 0:  # Update cross indices\n",
    "        _, info = tn.cross(domain=domain, function=function, function_arg='matrix', verbose=False, return_info=True)\n",
    "\n",
    "    # Forward pass\n",
    "    t2 = tn.cross_forward(domain=domain, function=function, info=info, function_arg='matrix')\n",
    "    loss = tn.norm(t2-0.5)\n",
    "\n",
    "    if it % 200 == 0:\n",
    "        print('{:04}: loss={:g}'.format(it, loss))\n",
    "    loss.backward()\n",
    "    optimizer.step()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, let's check that the resulting network satisfies the property we wanted:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cross-approximation over a 2D domain containing 4096 grid points:\n",
      "iter: 0  | eps: 1.167e-11 | total time:   0.0100 | largest rank:   1 <- converged: eps < 1e-06\n",
      "Did 192 function evaluations, which took 0.0003726s (5.152e+05 evals/s)\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "visualize_net()"
   ]
  }
 ],
 "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": 4
}

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API