{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tensors\n", "``` text\n", "Copyright 2022 National Technology & Engineering Solutions of Sandia,\n", "LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the\n", "U.S. Government retains certain rights in this software.\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tensors are extensions of multidimensial arrays with additional operations defined on them. Here we explain the basics for creating and working with dense tensors." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more details, see the {class}`pyttb.tensor` class documentation." ] }, { "cell_type": "code", "execution_count": 195, "metadata": {}, "outputs": [], "source": [ "import pyttb as ttb\n", "import numpy as np\n", "import sys\n", "from pyttb.matlab.matlab_support import matlab_print" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a tensor from an array" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The {class}`pyttb.tensor` command creates a (multidimensional) array as a tensor object. By default, it creates a deep copy of the input object. It also reorders that copy to be F-ordered if it isn't already. For a tensor of size $m \\times n \\times p$, the shape is `(m,n,p)`." ] }, { "cell_type": "code", "execution_count": 196, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (4, 3, 2) with order F\n", "data[:, :, 0] =\n", "[[1. 1. 1.]\n", " [1. 1. 1.]\n", " [1. 1. 1.]\n", " [1. 1. 1.]]\n", "data[:, :, 1] =\n", "[[1. 1. 1.]\n", " [1. 1. 1.]\n", " [1. 1. 1.]\n", " [1. 1. 1.]]" ] }, "execution_count": 196, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = np.ones((4, 3, 2)) # Create numpy 4 x 3 x 2 array of ones.\n", "X = ttb.tensor(M) # Convert to 4 x 3 x 2 tensor object.\n", "X" ] }, { "cell_type": "code", "execution_count": 197, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (4, 6) with order F\n", "data[:, :] =\n", "[[1. 1. 1. 1. 1. 1.]\n", " [1. 1. 1. 1. 1. 1.]\n", " [1. 1. 1. 1. 1. 1.]\n", " [1. 1. 1. 1. 1. 1.]]" ] }, "execution_count": 197, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X = ttb.tensor(M, (4, 6)) # Reshape to 4 x 6 tensor.\n", "X" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is an option to only do a shallow copy the input data, but it must be F-ordered. This can be useful for larger data. (A vector is both C- and F-ordered, which is useful for functions that don't support alternative orderings.)" ] }, { "cell_type": "code", "execution_count": 198, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 3, 2) with order F\n", "data[:, :, 0] =\n", "[[0.5488135 0.60276338 0.4236548 ]\n", " [0.71518937 0.54488318 0.64589411]]\n", "data[:, :, 1] =\n", "[[0.43758721 0.96366276 0.79172504]\n", " [0.891773 0.38344152 0.52889492]]" ] }, "execution_count": 198, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(0)\n", "v = np.random.rand(12) # length-12 vector of uniform random numbers.\n", "X = ttb.tensor(v, (2, 3, 2), copy=False) # Converted to 2 x 3 x 2 tensor.\n", "X" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A Note on Display of Tensors\n", "The display of a tensor is by _frontal slice_ where the first two indices range and the remainder stay fixed. This is different than how Python normal displays multidimensional arrays where the last two indices range and the remainder stay fixed." ] }, { "cell_type": "code", "execution_count": 279, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[[0.5488135 , 0.71518937],\n", " [0.60276338, 0.54488318],\n", " [0.4236548 , 0.64589411]],\n", "\n", " [[0.43758721, 0.891773 ],\n", " [0.96366276, 0.38344152],\n", " [0.79172504, 0.52889492]],\n", "\n", " [[0.56804456, 0.92559664],\n", " [0.07103606, 0.0871293 ],\n", " [0.0202184 , 0.83261985]]])" ] }, "execution_count": 279, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Display of the above tensor object in the usual Python way.\n", "X.data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Printing similar to MATLAB \n", "It is possible to print similar to MATLAB using {func}`matlab_print` which has the optional arguments `name` and `format` to further customize the outputs. You will need \n", "\n", "``` python\n", "from pyttb.matlab.matlab_support import matlab_print\n", "```\n", "\n", "in your code for this to work as shown here." ] }, { "cell_type": "code", "execution_count": 199, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 2 x 3 x 2\n", "\tX(:,:, 0) =\n", "\t\t0.5488 0.6028 0.4237\n", "\t\t0.7152 0.5449 0.6459\n", "\n", "\tX(:,:, 1) =\n", "\t\t0.4376 0.9637 0.7917\n", "\t\t0.8918 0.3834 0.5289\n", "\n" ] } ], "source": [ "matlab_print(X,name='X',format='7.4f') # Print tensor X in MATLAB format." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Optionally, you can specify a different shape for the tensor, so long as the input array has the right number of elements. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a tensor with elements generated by a function\n", "\n", "Using {meth}`pyttb.tensor.from_function` takes another function that is used to generate entries of the tensor. The returned array should be in Fortran order to avoid unnecessary copies and rearrangement. Since the data will be reshape in any case, returning a vector is recommended. Alternatively, ensure the function returns in F-order for those methods that support it." ] }, { "cell_type": "code", "execution_count": 200, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 2 x 3 x 2\n", "\tX(:,:, 0) =\n", "\t\t1.6243 -0.5282 0.8654\n", "\t\t-0.6118 -1.0730 -2.3015\n", "\n", "\tX(:,:, 1) =\n", "\t\t1.7448 0.3190 1.4621\n", "\t\t-0.7612 -0.2494 -2.0601\n", "\n" ] } ], "source": [ "# Ensure reproducibility of random numbers.\n", "np.random.seed(1) \n", "# Function to generate normally distributed random numbers.\n", "randn = lambda s: np.random.randn(np.prod(s))\n", "# Create 2 x 3 x 2 tensor of normally distributed random numbers.\n", "X = ttb.tensor.from_function(randn, (2, 3, 2)) \n", "# Print tensor X in MATLAB format.\n", "matlab_print(X,name='X',format='7.4f') " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We show how to use {meth}`pyttb.tensor.from_function` in the next example to create a tensor of all ones, but it's even easier to use {meth}`pyttb.tenones` described below." ] }, { "cell_type": "code", "execution_count": 201, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 3 x 4 x 2\n", "\tX(:,:, 0) =\n", "\t\t 1 1 1 1\n", "\t\t 1 1 1 1\n", "\t\t 1 1 1 1\n", "\n", "\tX(:,:, 1) =\n", "\t\t 1 1 1 1\n", "\t\t 1 1 1 1\n", "\t\t 1 1 1 1\n", "\n" ] } ], "source": [ "# Function to generate tensor of ones. Uses explicit Fortran order.\n", "ones = lambda s: np.ones(s,order='F') \n", "# Create 3 x 4 x 2 tensor of ones.\n", "X = ttb.tensor.from_function(ones, (3, 4, 2))\n", "# Print tensor X in MATLAB format.\n", "matlab_print(X,name='X',format='2.0f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create a tensor of all ones.\n", "Using {func}`pyttb.tenones` to create a tensor of all ones." ] }, { "cell_type": "code", "execution_count": 202, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 3 x 4 x 2\n", "\tX(:,:, 0) =\n", "\t\t 1 1 1 1\n", "\t\t 1 1 1 1\n", "\t\t 1 1 1 1\n", "\n", "\tX(:,:, 1) =\n", "\t\t 1 1 1 1\n", "\t\t 1 1 1 1\n", "\t\t 1 1 1 1\n", "\n" ] } ], "source": [ "\n", "X = ttb.tenones((3, 4, 2)) \n", "matlab_print(X,name='X',format='2.0f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create a tensor of all zeros\n", "Use {func}`pyttb.tenzeros` to create a tensor of all zeros.\n" ] }, { "cell_type": "code", "execution_count": 203, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 1 x 4 x 2\n", "\tX(:,:, 0) =\n", "\t\t 0 0 0 0\n", "\n", "\tX(:,:, 1) =\n", "\t\t 0 0 0 0\n", "\n" ] } ], "source": [ "X = ttb.tenzeros((1, 4, 2)) # Creates a 1 x 4 x 2 tensor of zeroes.\n", "matlab_print(X,name='X',format='2.0f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create a random tensor\n", "Use {func}`pyttb.tenrand` to create a tensor with uniform random values from [0,1]." ] }, { "cell_type": "code", "execution_count": 204, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 5 x 4 x 2\n", "\tX(:,:, 0) =\n", "\t\t0.4360 0.5497 0.4204 0.2046\n", "\t\t0.2997 0.6211 0.1346 0.1844\n", "\t\t0.8540 0.8466 0.5052 0.4281\n", "\t\t0.1272 0.2260 0.2203 0.4678\n", "\t\t0.6404 0.5052 0.7936 0.1623\n", "\n", "\tX(:,:, 1) =\n", "\t\t0.0259 0.4353 0.3303 0.6193\n", "\t\t0.2668 0.5291 0.5136 0.7853\n", "\t\t0.4942 0.0796 0.0653 0.0965\n", "\t\t0.5967 0.1069 0.3498 0.2017\n", "\t\t0.4831 0.3869 0.5800 0.7008\n", "\n" ] } ], "source": [ "# Creates a 5 x 4 x 2 tensor of uniform [0,1] random numbers\n", "np.random.seed(2) # Reproducible random numbers\n", "X = ttb.tenrand((5, 4, 2))\n", "matlab_print(X,name='X',format='7.4f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a one-dimensional tensor\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To specify a 1-way tensor of size $m$, the shape should be of the form `(m,)`." ] }, { "cell_type": "code", "execution_count": 205, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 5\n", "\tX(:) =\n", "\t\t0.5508\n", "\t\t0.7081\n", "\t\t0.2909\n", "\t\t0.5108\n", "\t\t0.8929\n" ] } ], "source": [ "np.random.seed(3)\n", "X = ttb.tenrand((5,)) # Creates a 1-way tensor.\n", "matlab_print(X,name='X',format='7.4f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Specifying trailing singleton dimensions in a tensor\n", "Likewise, trailing singleton dimensions must be explicitly specified." ] }, { "cell_type": "code", "execution_count": 206, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Y is a tensor of shape 3 x 4\n", "\tY(:,:) =\n", "\t\t0.9670 0.5472 0.9727 0.7148\n", "\t\t0.6977 0.2161 0.9763 0.0062\n", "\t\t0.2530 0.4348 0.7794 0.1977\n" ] } ], "source": [ "np.random.seed(4)\n", "Y = ttb.tenrand((3, 4)) # Creates a 2-way tensor of size 4 x 3.\n", "matlab_print(Y,name='Y',format='7.4f')" ] }, { "cell_type": "code", "execution_count": 207, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Y is a tensor of shape 3 x 4 x 1\n", "\tY(:,:, 0) =\n", "\t\t0.9670 0.5472 0.9727 0.7148\n", "\t\t0.6977 0.2161 0.9763 0.0062\n", "\t\t0.2530 0.4348 0.7794 0.1977\n", "\n" ] } ], "source": [ "np.random.seed(4)\n", "Y = ttb.tenrand((3, 4, 1)) # Creates a 3-way tensor of size 3 x 4 x 1.\n", "matlab_print(Y,name='Y',format='7.4f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The constituent parts of a tensor\n", "A tensor has two parts: `data` (a multidimensional array) and `shape` (a tuple of integers)." ] }, { "cell_type": "code", "execution_count": 208, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 2 x 4 x 3\n", "\tX(:,:, 0) =\n", "\t\t0.2220 0.9186 0.7659 0.1877\n", "\t\t0.4413 0.2741 0.6288 0.2658\n", "\n", "\tX(:,:, 1) =\n", "\t\t0.8707 0.4884 0.5184 0.0807\n", "\t\t0.1583 0.4142 0.5798 0.2847\n", "\n", "\tX(:,:, 2) =\n", "\t\t0.2067 0.6117 0.2968 0.7384\n", "\t\t0.8799 0.2961 0.5999 0.2536\n", "\n" ] } ], "source": [ "np.random.seed(5)\n", "X = ttb.tenrand((2, 4, 3)) # Create tensor of size 2 x 4 x 3 with random numbers.\n", "matlab_print(X,name='X',format='7.4f')" ] }, { "cell_type": "code", "execution_count": 280, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[[0.5488135 , 0.71518937],\n", " [0.60276338, 0.54488318],\n", " [0.4236548 , 0.64589411]],\n", "\n", " [[0.43758721, 0.891773 ],\n", " [0.96366276, 0.38344152],\n", " [0.79172504, 0.52889492]],\n", "\n", " [[0.56804456, 0.92559664],\n", " [0.07103606, 0.0871293 ],\n", " [0.0202184 , 0.83261985]]])" ] }, "execution_count": 280, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The array (note that its display order is different from the tensor).\n", "X.data # The array." ] }, { "cell_type": "code", "execution_count": 210, "metadata": {}, "outputs": [ { "data": { "text/plain": [ " C_CONTIGUOUS : False\n", " F_CONTIGUOUS : True\n", " OWNDATA : False\n", " WRITEABLE : True\n", " ALIGNED : True\n", " WRITEBACKIFCOPY : False" ] }, "execution_count": 210, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Note that it's is stored in Fortran format (F_CONTIGUOUS = True).\n", "X.data.flags" ] }, { "cell_type": "code", "execution_count": 211, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2, 4, 3)" ] }, "execution_count": 211, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The shape\n", "X.shape" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a tensor from its constituent parts\n", "_This is an efficient way to create a tensor copy, but it illustrates the role of the parts. A more efficient way is to use `Y = X` (shallow copy) or `Y = X.copy()` (deep copy)._" ] }, { "cell_type": "code", "execution_count": 212, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 2 x 4 x 3\n", "\tX(:,:, 0) =\n", "\t\t0.5488 0.5449 0.4376 0.3834\n", "\t\t0.5680 0.0871 0.7782 0.7992\n", "\n", "\tX(:,:, 1) =\n", "\t\t0.7152 0.4237 0.8918 0.7917\n", "\t\t0.9256 0.0202 0.8700 0.4615\n", "\n", "\tX(:,:, 2) =\n", "\t\t0.6028 0.6459 0.9637 0.5289\n", "\t\t0.0710 0.8326 0.9786 0.7805\n", "\n", "Y is a tensor of shape 2 x 4 x 3\n", "\tY(:,:, 0) =\n", "\t\t0.5488 0.5449 0.4376 0.3834\n", "\t\t0.5680 0.0871 0.7782 0.7992\n", "\n", "\tY(:,:, 1) =\n", "\t\t0.7152 0.4237 0.8918 0.7917\n", "\t\t0.9256 0.0202 0.8700 0.4615\n", "\n", "\tY(:,:, 2) =\n", "\t\t0.6028 0.6459 0.9637 0.5289\n", "\t\t0.0710 0.8326 0.9786 0.7805\n", "\n" ] } ], "source": [ "np.random.seed(0)\n", "X = ttb.tenrand((2, 4, 3)) # Create data.\n", "matlab_print(X,name='X',format='7.4f') # Print tensor X in MATLAB format.\n", "Y = ttb.tensor(X.data, X.shape) # Creates a (deep) copy of X from its parts.\n", "matlab_print(Y,name='Y',format='7.4f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating an empty tensor\n", "An empty constructor exists." ] }, { "cell_type": "code", "execution_count": 213, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape \n", "\tX(:) =\n", "\n" ] } ], "source": [ "X = ttb.tensor() # Creates an empty tensor\n", "matlab_print(X,name='X',format='7.4f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Removing singleton dimensions from a tensor\n", "Use {meth}`pyttb.tensor.squeeze` to remove single dimensions from a tensor." ] }, { "cell_type": "code", "execution_count": 214, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Y is a tensor of shape 4 x 3 x 1\n", "\tY(:,:, 0) =\n", "\t\t0.5488 0.7152 0.6028\n", "\t\t0.5449 0.4237 0.6459\n", "\t\t0.4376 0.8918 0.9637\n", "\t\t0.3834 0.7917 0.5289\n", "\n" ] } ], "source": [ "np.random.seed(0)\n", "Y = ttb.tenrand((4, 3, 1)) # Create the data.\n", "matlab_print(Y,name='Y',format='7.4f') # Print tensor Y in MATLAB format." ] }, { "cell_type": "code", "execution_count": 215, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Z is a tensor of shape 4 x 3\n", "\tZ(:,:) =\n", "\t\t0.5488 0.7152 0.6028\n", "\t\t0.5449 0.4237 0.6459\n", "\t\t0.4376 0.8918 0.9637\n", "\t\t0.3834 0.7917 0.5289\n" ] } ], "source": [ "Z = Y.squeeze() # Squeeze out the singleton dimension.\n", "matlab_print(Z,name='Z',format='7.4f') # Print tensor Z in MATLAB format. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Convert a tensor to a (multidimensional) array\n", "Use {meth}`pyttb.tensor.double` to convert a tensor to a numpy array; this is identical to extracting the `data` member." ] }, { "cell_type": "code", "execution_count": 216, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[[0.5488135 , 0.71518937, 0.60276338, 0.54488318],\n", " [0.4236548 , 0.64589411, 0.43758721, 0.891773 ],\n", " [0.96366276, 0.38344152, 0.79172504, 0.52889492],\n", " [0.56804456, 0.92559664, 0.07103606, 0.0871293 ],\n", " [0.0202184 , 0.83261985, 0.77815675, 0.87001215]],\n", "\n", " [[0.97861834, 0.79915856, 0.46147936, 0.78052918],\n", " [0.11827443, 0.63992102, 0.14335329, 0.94466892],\n", " [0.52184832, 0.41466194, 0.26455561, 0.77423369],\n", " [0.45615033, 0.56843395, 0.0187898 , 0.6176355 ],\n", " [0.61209572, 0.616934 , 0.94374808, 0.6818203 ]]])" ] }, "execution_count": 216, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(0)\n", "X = ttb.tenrand((2, 5, 4)) # Create the data.\n", "X.double() # Converts X to an array of doubles." ] }, { "cell_type": "code", "execution_count": 217, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[[0.5488135 , 0.71518937, 0.60276338, 0.54488318],\n", " [0.4236548 , 0.64589411, 0.43758721, 0.891773 ],\n", " [0.96366276, 0.38344152, 0.79172504, 0.52889492],\n", " [0.56804456, 0.92559664, 0.07103606, 0.0871293 ],\n", " [0.0202184 , 0.83261985, 0.77815675, 0.87001215]],\n", "\n", " [[0.97861834, 0.79915856, 0.46147936, 0.78052918],\n", " [0.11827443, 0.63992102, 0.14335329, 0.94466892],\n", " [0.52184832, 0.41466194, 0.26455561, 0.77423369],\n", " [0.45615033, 0.56843395, 0.0187898 , 0.6176355 ],\n", " [0.61209572, 0.616934 , 0.94374808, 0.6818203 ]]])" ] }, "execution_count": 217, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.data # Same thing." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use `ndims` and `shape` to get the shape of a tensor" ] }, { "cell_type": "code", "execution_count": 282, "metadata": {}, "outputs": [], "source": [ "X = ttb.tenrand((4,3,2)) # Create a 4 x 3 x 2 tensor of random numbers." ] }, { "cell_type": "code", "execution_count": 283, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 283, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.ndims # Number of dimensions (or ways)." ] }, { "cell_type": "code", "execution_count": 284, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4, 3, 2)" ] }, "execution_count": 284, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.shape # Tuple with the sizes of all dimensions." ] }, { "cell_type": "code", "execution_count": 285, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 285, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.shape[2] # Size of a single dimension." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Subscripted reference for a tensor" ] }, { "cell_type": "code", "execution_count": 287, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.5488135039273248" ] }, "execution_count": 287, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(0)\n", "X = ttb.tenrand((3, 4, 2, 1)) # Create a 3x4x2x1 random tensor.\n", "X[0, 0, 0, 0] # Extract a single element." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to extract a subtensor that contains a single element. Observe that singleton dimensions are **not** dropped unless they are specifically specified, e.g., as above." ] }, { "cell_type": "code", "execution_count": 288, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (1,) with order F\n", "data[:] =\n", "[0.5488135]" ] }, "execution_count": 288, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[0, 0, 0, :] # Produces a tensor of order 1 and shape 1." ] }, { "cell_type": "code", "execution_count": 289, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (3, 1) with order F\n", "data[:, :] =\n", "[[0.5488135 ]\n", " [0.96366276]\n", " [0.0202184 ]]" ] }, "execution_count": 289, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[:, 0, 0, :] # Produces a tensor of shape 3x1." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Moreover, the subtensor is automatically renumbered/resized in the same way that numpy works for arrays except that singleton dimensions are handled explicitly." ] }, { "cell_type": "code", "execution_count": 292, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 1) with order F\n", "data[:, :, 0] =\n", "[[0.60276338 0.43758721]\n", " [0.79172504 0.07103606]]" ] }, "execution_count": 292, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[0:2, [1, 3], 0, :] # Produces a tensor of shape 2x2x1." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's also possible to extract a list of elements by passing in an array of subscripts or a column array of linear indices." ] }, { "cell_type": "code", "execution_count": 293, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.5488135 , 0.78052918])" ] }, "execution_count": 293, "metadata": {}, "output_type": "execute_result" } ], "source": [ "subs = np.array([[0, 0, 0, 0], [2, 3, 1, 0]])\n", "X[subs] # Extract 2 values by subscript." ] }, { "cell_type": "code", "execution_count": 226, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.5488135 , 0.78052918])" ] }, "execution_count": 226, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inds = np.array([0, 23])\n", "X[inds] # Same thing with linear indices." ] }, { "cell_type": "code", "execution_count": 227, "metadata": {}, "outputs": [], "source": [ "np.random.seed(0)\n", "X = ttb.tenrand((10,)) # Create a random tensor." ] }, { "cell_type": "code", "execution_count": 228, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.5488135 , 0.71518937, 0.60276338, 0.54488318, 0.4236548 ])" ] }, "execution_count": 228, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X[0:5] # Extract a subtensor." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Subscripted assignment for a tensor\n", "We can assign a single element, an entire subtensor, or a list of values for a tensor." ] }, { "cell_type": "code", "execution_count": 331, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 3 x 4 x 2\n", "\tX(:,:, 0) =\n", "\t\t0.0000 0.6028 0.4237 0.4376\n", "\t\t0.9637 0.7917 0.5680 0.0710\n", "\t\t0.0202 0.7782 0.9786 0.4615\n", "\n", "\tX(:,:, 1) =\n", "\t\t0.7152 0.5449 0.6459 0.8918\n", "\t\t0.3834 0.5289 0.9256 0.0871\n", "\t\t0.8326 0.8700 0.7992 0.7805\n", "\n" ] } ], "source": [ "np.random.seed(0)\n", "X = ttb.tenrand((3,4,2)) # Create some data.\n", "X[0, 0, 0] = 0 # Replaces the [0,0,0] element.\n", "matlab_print(X,name='X',format='7.4f') # Print tensor X in MATLAB format." ] }, { "cell_type": "code", "execution_count": 332, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 3 x 4 x 2\n", "\tX(:,:, 0) =\n", "\t\t1.0000 1.0000 0.4237 0.4376\n", "\t\t1.0000 1.0000 0.5680 0.0710\n", "\t\t0.0202 0.7782 0.9786 0.4615\n", "\n", "\tX(:,:, 1) =\n", "\t\t0.7152 0.5449 0.6459 0.8918\n", "\t\t0.3834 0.5289 0.9256 0.0871\n", "\t\t0.8326 0.8700 0.7992 0.7805\n", "\n" ] } ], "source": [ "X[0:2, 0:2,0] = np.ones((2, 2)) # Replaces a subtensor.\n", "matlab_print(X,name='X',format='7.4f') # Print tensor X in MATLAB format." ] }, { "cell_type": "code", "execution_count": 333, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 3 x 4 x 2\n", "\tX(:,:, 0) =\n", "\t\t5.0000 1.0000 0.4237 0.4376\n", "\t\t1.0000 1.0000 0.5680 0.0710\n", "\t\t0.0202 0.7782 0.9786 0.4615\n", "\n", "\tX(:,:, 1) =\n", "\t\t7.0000 0.5449 0.6459 0.8918\n", "\t\t0.3834 0.5289 0.9256 0.0871\n", "\t\t0.8326 0.8700 0.7992 0.7805\n", "\n" ] } ], "source": [ "subs = np.array([[0, 0, 0], [0,0,1]])\n", "X[subs] = [5, 7] # Replaces the (0,0,0) and (1,0,0) elements.\n", "matlab_print(X,name='X',format='7.4f') # Print tensor X in MATLAB format." ] }, { "cell_type": "code", "execution_count": 339, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 4 x 3 x 2\n", "\tX(:,:, 0) =\n", "\t\t 5.0000 0.6028 0.4237\n", "\t\t 0.4376 0.9637 0.7917\n", "\t\t 0.5680 0.0710 0.0202\n", "\t\t 0.7782 0.9786 0.4615\n", "\n", "\tX(:,:, 1) =\n", "\t\t 7.0000 0.5449 0.6459\n", "\t\t 0.8918 0.3834 0.5289\n", "\t\t 0.9256 0.0871 0.8326\n", "\t\t 0.8700 0.7992 0.7805\n", "\n" ] } ], "source": [ "X[[0, 12]] = [5, 7] # Same as above using linear indices.\n", "matlab_print(X,name='X',format='7.4f') # Print tensor X in MATLAB format." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to **grow** the tensor automatically by assigning elements outside the original range of the tensor." ] }, { "cell_type": "code", "execution_count": 340, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 4 x 3 x 3\n", "\tX(:,:, 0) =\n", "\t\t 5.0000 0.6028 0.4237\n", "\t\t 0.4376 0.9637 0.7917\n", "\t\t 0.5680 0.0710 0.0202\n", "\t\t 0.7782 0.9786 0.4615\n", "\n", "\tX(:,:, 1) =\n", "\t\t 7.0000 0.5449 0.6459\n", "\t\t 0.8918 0.3834 0.5289\n", "\t\t 0.9256 0.0871 0.8326\n", "\t\t 0.8700 0.7992 0.7805\n", "\n", "\tX(:,:, 2) =\n", "\t\t 1.0000 0.0000 0.0000\n", "\t\t 0.0000 0.0000 0.0000\n", "\t\t 0.0000 0.0000 0.0000\n", "\t\t 0.0000 0.0000 0.0000\n", "\n" ] } ], "source": [ "X[0,0,2] = 1 # Grows the shape of the tensor.\n", "matlab_print(X,name='X',format='7.4f') # Print tensor X in MATLAB format." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using negative indexing for the last array index" ] }, { "cell_type": "code", "execution_count": 341, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 4 x 3 x 2\n", "\tX(:,:, 0) =\n", "\t\t 0.5488 0.6028 0.4237\n", "\t\t 0.4376 0.9637 0.7917\n", "\t\t 0.5680 0.0710 0.0202\n", "\t\t 0.7782 0.9786 0.4615\n", "\n", "\tX(:,:, 1) =\n", "\t\t 0.7152 0.5449 0.6459\n", "\t\t 0.8918 0.3834 0.5289\n", "\t\t 0.9256 0.0871 0.8326\n", "\t\t 0.8700 0.7992 0.7805\n", "\n" ] } ], "source": [ "np.random.seed(0)\n", "X = ttb.tenrand((4,3,2)) # Create some data.\n", "matlab_print(X,name='X',format='7.4f') # Print tensor X in MATLAB format." ] }, { "cell_type": "code", "execution_count": 344, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Last value in array is 0.7805'" ] }, "execution_count": 344, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f\"Last value in array is {X[-1]:.4f}\" # Same as X(3,2,1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Extracting subscripts of nonzero elements of a tensor\n", "Use {meth}`pyttb.tensor.find` to get nonzero elements and values from a tensor." ] }, { "cell_type": "code", "execution_count": 385, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 2 x 2 x 2\n", "\tX(:,:, 0) =\n", "\t\t 2 2\n", "\t\t 1 2\n", "\n", "\tX(:,:, 1) =\n", "\t\t 0 0\n", "\t\t 1 0\n", "\n" ] } ], "source": [ "# Create a tensor that's about 33% zeros.\n", "np.random.seed(5)\n", "randint = lambda s: np.random.randint(0, 3, np.prod(s))\n", "X = ttb.tensor.from_function(randint, (2, 2, 2)) # Create a tensor.\n", "matlab_print(X,name='X',format='2.0f') # Print tensor X in MATLAB format." ] }, { "cell_type": "code", "execution_count": 386, "metadata": {}, "outputs": [], "source": [ "S, V = X.find() # Find all the nonzero subscripts and values." ] }, { "cell_type": "code", "execution_count": 387, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 0, 0],\n", " [1, 0, 0],\n", " [0, 1, 0],\n", " [1, 1, 0],\n", " [1, 0, 1]])" ] }, "execution_count": 387, "metadata": {}, "output_type": "execute_result" } ], "source": [ "S # Nonzero subscripts" ] }, { "cell_type": "code", "execution_count": 388, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[2],\n", " [1],\n", " [2],\n", " [2],\n", " [1]], dtype=int32)" ] }, "execution_count": 388, "metadata": {}, "output_type": "execute_result" } ], "source": [ "V # Values" ] }, { "cell_type": "code", "execution_count": 390, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(array([[0, 0, 0],\n", " [0, 1, 0],\n", " [1, 1, 0]]),\n", " array([[ True],\n", " [ True],\n", " [ True]]))" ] }, "execution_count": 390, "metadata": {}, "output_type": "execute_result" } ], "source": [ "larger_entries = X >= 2 # Find entries >= 2.\n", "larger_subs, larger_vals = larger_entries.find() # Find subscripts of values >= 2.\n", "larger_subs, larger_vals" ] }, { "cell_type": "code", "execution_count": 379, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([2., 2., 2.])" ] }, "execution_count": 379, "metadata": {}, "output_type": "execute_result" } ], "source": [ "V = X[larger_subs]\n", "V" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Computing the Frobenius norm of a tensor\n", "The method {meth}`pyttb.tensor.norm` computes the Frobenius norm of a tensor. This corresponds to the Euclidean norm of the vectorized tensor." ] }, { "cell_type": "code", "execution_count": 391, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.631397990238147" ] }, "execution_count": 391, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(0)\n", "X = ttb.tenrand((2,3,3))\n", "X.norm()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Reshaping a tensor\n", "The method {meth}`pyttb.tensor.reshape` reshapes a tensor into a given shape array. The total number of elements in the tensor cannot change.\n", "_Currently, this methods creates a **copy** of the tensor, and this needs to be fixed._" ] }, { "cell_type": "code", "execution_count": 395, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape 3 x 2 x 3\n", "\tX(:,:, 0) =\n", "\t\t 5 3\n", "\t\t 0 7\n", "\t\t 3 9\n", "\n", "\tX(:,:, 1) =\n", "\t\t 3 4\n", "\t\t 5 7\n", "\t\t 2 6\n", "\n", "\tX(:,:, 2) =\n", "\t\t 8 6\n", "\t\t 8 7\n", "\t\t 1 7\n", "\n", "Y is a tensor of shape 3 x 3 x 2\n", "\tY(:,:, 0) =\n", "\t\t 5 3 3\n", "\t\t 0 7 5\n", "\t\t 3 9 2\n", "\n", "\tY(:,:, 1) =\n", "\t\t 4 8 6\n", "\t\t 7 8 7\n", "\t\t 6 1 7\n", "\n" ] } ], "source": [ "np.random.seed(0)\n", "randint = lambda s: np.random.randint(0, 10, np.prod(s))\n", "X = ttb.tensor.from_function(randint, (3, 2, 3))\n", "matlab_print(X,name='X',format='2.0f')\n", "Y = X.reshape((3,3,2))\n", "matlab_print(Y,name='Y',format='2.0f')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic operations (plus, minus, and, or, etc.) on a tensor\n", "tensors support plus, minus, times, divide, power, equals, and not-equals operators. tensors can use their operators with another tensor or a scalar (with the exception of equalities which only takes tensors). All mathematical operators are elementwise operations." ] }, { "cell_type": "code", "execution_count": 245, "metadata": {}, "outputs": [], "source": [ "np.random.seed(0)\n", "A = ttb.tensor(np.floor(3 * np.random.rand(2, 2, 3))) # Generate some data.\n", "B = ttb.tensor(np.floor(3 * np.random.rand(2, 2, 3)))" ] }, { "cell_type": "code", "execution_count": 246, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. 0.]\n", " [1. 1.]]\n", "data[:, :, 1] =\n", "[[1. 0.]\n", " [1. 1.]]\n", "data[:, :, 2] =\n", "[[0. 1.]\n", " [1. 1.]]" ] }, "execution_count": 246, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A.logical_and(B) # Calls and." ] }, { "cell_type": "code", "execution_count": 247, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. 1.]\n", " [1. 1.]]\n", "data[:, :, 1] =\n", "[[1. 1.]\n", " [1. 1.]]\n", "data[:, :, 2] =\n", "[[1. 1.]\n", " [1. 1.]]" ] }, "execution_count": 247, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A.logical_or(B)" ] }, { "cell_type": "code", "execution_count": 248, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[0. 1.]\n", " [0. 0.]]\n", "data[:, :, 1] =\n", "[[0. 1.]\n", " [0. 0.]]\n", "data[:, :, 2] =\n", "[[1. 0.]\n", " [0. 0.]]" ] }, "execution_count": 248, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A.logical_xor(B)" ] }, { "cell_type": "code", "execution_count": 249, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[ True False]\n", " [False False]]\n", "data[:, :, 1] =\n", "[[ True False]\n", " [ True False]]\n", "data[:, :, 2] =\n", "[[False False]\n", " [ True False]]" ] }, "execution_count": 249, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A == B # Calls eq." ] }, { "cell_type": "code", "execution_count": 250, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[False True]\n", " [ True True]]\n", "data[:, :, 1] =\n", "[[False True]\n", " [False True]]\n", "data[:, :, 2] =\n", "[[ True True]\n", " [False True]]" ] }, "execution_count": 250, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A != B # Calls neq." ] }, { "cell_type": "code", "execution_count": 251, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[False True]\n", " [False False]]\n", "data[:, :, 1] =\n", "[[False True]\n", " [False True]]\n", "data[:, :, 2] =\n", "[[ True False]\n", " [False False]]" ] }, "execution_count": 251, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A > B # Calls gt." ] }, { "cell_type": "code", "execution_count": 252, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[ True True]\n", " [False False]]\n", "data[:, :, 1] =\n", "[[ True True]\n", " [ True True]]\n", "data[:, :, 2] =\n", "[[ True False]\n", " [ True False]]" ] }, "execution_count": 252, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A >= B # Calls ge." ] }, { "cell_type": "code", "execution_count": 253, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[False False]\n", " [ True True]]\n", "data[:, :, 1] =\n", "[[False False]\n", " [False False]]\n", "data[:, :, 2] =\n", "[[False True]\n", " [False True]]" ] }, "execution_count": 253, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A < B # Calls lt." ] }, { "cell_type": "code", "execution_count": 254, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[ True False]\n", " [ True True]]\n", "data[:, :, 1] =\n", "[[ True False]\n", " [ True False]]\n", "data[:, :, 2] =\n", "[[False True]\n", " [ True True]]" ] }, "execution_count": 254, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A <= B # Calls le." ] }, { "cell_type": "code", "execution_count": 255, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[0. 0.]\n", " [0. 0.]]\n", "data[:, :, 1] =\n", "[[0. 0.]\n", " [0. 0.]]\n", "data[:, :, 2] =\n", "[[0. 0.]\n", " [0. 0.]]" ] }, "execution_count": 255, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A.logical_not() # Calls not." ] }, { "cell_type": "code", "execution_count": 256, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. 1.]\n", " [1. 1.]]\n", "data[:, :, 1] =\n", "[[2. 1.]\n", " [2. 2.]]\n", "data[:, :, 2] =\n", "[[1. 1.]\n", " [2. 1.]]" ] }, "execution_count": 256, "metadata": {}, "output_type": "execute_result" } ], "source": [ "+A # Calls uplus." ] }, { "cell_type": "code", "execution_count": 257, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[-1. -1.]\n", " [-1. -1.]]\n", "data[:, :, 1] =\n", "[[-2. -1.]\n", " [-2. -2.]]\n", "data[:, :, 2] =\n", "[[-1. -1.]\n", " [-2. -1.]]" ] }, "execution_count": 257, "metadata": {}, "output_type": "execute_result" } ], "source": [ "-A # Calls uminus." ] }, { "cell_type": "code", "execution_count": 258, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[2. 1.]\n", " [3. 3.]]\n", "data[:, :, 1] =\n", "[[4. 1.]\n", " [4. 3.]]\n", "data[:, :, 2] =\n", "[[1. 3.]\n", " [4. 3.]]" ] }, "execution_count": 258, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A + B # Calls plus." ] }, { "cell_type": "code", "execution_count": 259, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[ 0. 1.]\n", " [-1. -1.]]\n", "data[:, :, 1] =\n", "[[0. 1.]\n", " [0. 1.]]\n", "data[:, :, 2] =\n", "[[ 1. -1.]\n", " [ 0. -1.]]" ] }, "execution_count": 259, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A - B # Calls minus." ] }, { "cell_type": "code", "execution_count": 260, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. 0.]\n", " [2. 2.]]\n", "data[:, :, 1] =\n", "[[4. 0.]\n", " [4. 2.]]\n", "data[:, :, 2] =\n", "[[0. 2.]\n", " [4. 2.]]" ] }, "execution_count": 260, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A * B # Calls times." ] }, { "cell_type": "code", "execution_count": 261, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[5. 5.]\n", " [5. 5.]]\n", "data[:, :, 1] =\n", "[[10. 5.]\n", " [10. 10.]]\n", "data[:, :, 2] =\n", "[[ 5. 5.]\n", " [10. 5.]]" ] }, "execution_count": 261, "metadata": {}, "output_type": "execute_result" } ], "source": [ "5 * A # Calls mtimes." ] }, { "cell_type": "code", "execution_count": 262, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. 1.]\n", " [1. 1.]]\n", "data[:, :, 1] =\n", "[[4. 1.]\n", " [4. 2.]]\n", "data[:, :, 2] =\n", "[[1. 1.]\n", " [4. 1.]]" ] }, "execution_count": 262, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A**B # Calls power." ] }, { "cell_type": "code", "execution_count": 263, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. 1.]\n", " [1. 1.]]\n", "data[:, :, 1] =\n", "[[4. 1.]\n", " [4. 4.]]\n", "data[:, :, 2] =\n", "[[1. 1.]\n", " [4. 1.]]" ] }, "execution_count": 263, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A**2 # Calls power." ] }, { "cell_type": "code", "execution_count": 264, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. inf]\n", " [0.5 0.5]]\n", "data[:, :, 1] =\n", "[[ 1. inf]\n", " [ 1. 2.]]\n", "data[:, :, 2] =\n", "[[inf 0.5]\n", " [1. 0.5]]" ] }, "execution_count": 264, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A / B # Calls ldivide." ] }, { "cell_type": "code", "execution_count": 265, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[2. 2.]\n", " [2. 2.]]\n", "data[:, :, 1] =\n", "[[1. 2.]\n", " [1. 1.]]\n", "data[:, :, 2] =\n", "[[2. 2.]\n", " [1. 2.]]" ] }, "execution_count": 265, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 / A # Calls rdivide." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using `tenfun` for elementwise operations on one or more tensors\n", "The method `tenfun` applies a specified function to a number of tensors. This can be used for any function that is not predefined for tensors." ] }, { "cell_type": "code", "execution_count": 266, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[2. 2.]\n", " [2. 2.]]\n", "data[:, :, 1] =\n", "[[3. 2.]\n", " [3. 3.]]\n", "data[:, :, 2] =\n", "[[2. 2.]\n", " [3. 2.]]" ] }, "execution_count": 266, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(0)\n", "A = ttb.tensor(np.floor(3 * np.random.rand(2, 2, 3), order=\"F\")) # Generate some data.\n", "A.tenfun(lambda x: x + 1) # Increment every element of A by one." ] }, { "cell_type": "code", "execution_count": 267, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. 1.]\n", " [2. 2.]]\n", "data[:, :, 1] =\n", "[[2. 1.]\n", " [2. 2.]]\n", "data[:, :, 2] =\n", "[[1. 2.]\n", " [2. 2.]]" ] }, "execution_count": 267, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Wrap np.maximum in a function with a function signature that Python's inspect.signature can handle.\n", "def max_elements(a, b):\n", " return np.maximum(a, b)\n", "\n", "\n", "A.tenfun(max_elements, B) # Max of A and B, elementwise." ] }, { "cell_type": "code", "execution_count": 268, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (2, 2, 3) with order F\n", "data[:, :, 0] =\n", "[[1. 1.]\n", " [1. 1.]]\n", "data[:, :, 1] =\n", "[[2. 1.]\n", " [2. 2.]]\n", "data[:, :, 2] =\n", "[[1. 2.]\n", " [2. 1.]]" ] }, "execution_count": 268, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.random.seed(0)\n", "C = ttb.tensor(\n", " np.floor(5 * np.random.rand(2, 2, 3), order=\"F\")\n", ") # Create another tensor.\n", "\n", "\n", "def elementwise_mean(X):\n", " # finding mean for the columns\n", " return np.floor(np.mean(X, axis=0), order=\"F\")\n", "\n", "\n", "A.tenfun(elementwise_mean, B, C) # Elementwise means for A, B, and C." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use `permute` to reorder the modes of a tensor" ] }, { "cell_type": "code", "execution_count": 269, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X is a tensor of shape (2, 3, 4) with order F\n", "data[:, :, 0] =\n", "[[1 3 5]\n", " [2 4 6]]\n", "data[:, :, 1] =\n", "[[ 7 9 11]\n", " [ 8 10 12]]\n", "data[:, :, 2] =\n", "[[13 15 17]\n", " [14 16 18]]\n", "data[:, :, 3] =\n", "[[19 21 23]\n", " [20 22 24]]\n" ] } ], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(2, 3, 4))\n", "print(f\"X is a {X}\")" ] }, { "cell_type": "code", "execution_count": 270, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (4, 3, 2) with order F\n", "data[:, :, 0] =\n", "[[ 1 3 5]\n", " [ 7 9 11]\n", " [13 15 17]\n", " [19 21 23]]\n", "data[:, :, 1] =\n", "[[ 2 4 6]\n", " [ 8 10 12]\n", " [14 16 18]\n", " [20 22 24]]" ] }, "execution_count": 270, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X.permute(np.array((2, 1, 0))) # Reverse the modes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Permuting a 1-dimensional tensor works correctly." ] }, { "cell_type": "code", "execution_count": 271, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (4,) with order F\n", "data[:] =\n", "[1 2 3 4]" ] }, "execution_count": 271, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X = ttb.tensor(np.arange(1, 5), (4,))\n", "X.permute(\n", " np.array(\n", " 1,\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Symmetrizing and checking for symmetry in a tensor\n", "A tensor can be symmetrized in a collection of modes with the command `symmetrize`. The new, symmetric tensor is formed by averaging over all elements in the tensor which are required to be equal." ] }, { "cell_type": "code", "execution_count": 272, "metadata": {}, "outputs": [], "source": [ "np.random.rand(0)\n", "X = ttb.tensor(np.arange(1, 5), (4,)) # Create some data\n", "W = ttb.tensor(np.random.rand(4, 4, 4))\n", "Y = X.symmetrize()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An optional argument `grps` can also be passed to `symmetrize` which specifies an array of modes with respect to which the tensor should be symmetrized." ] }, { "cell_type": "code", "execution_count": 273, "metadata": {}, "outputs": [], "source": [ "np.random.seed(0)\n", "X = ttb.tensor(np.random.rand(3, 3, 2))\n", "Z = X.symmetrize(np.array((0, 1)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Additionally, one can check for symmetry in tensors with the `issymmetric` function. Similar to `symmetrize`, a collection of modes can be passed as a second argument." ] }, { "cell_type": "code", "execution_count": 274, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 274, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Y.issymmetric()" ] }, { "cell_type": "code", "execution_count": 275, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 275, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Z.issymmetric(np.array((1, 2)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Displaying a tensor" ] }, { "cell_type": "code", "execution_count": 276, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tensor of shape (3, 3, 2) with order F\n", "data[:, :, 0] =\n", "[[0.5488135 0.60276338 0.4236548 ]\n", " [0.43758721 0.96366276 0.79172504]\n", " [0.56804456 0.07103606 0.0202184 ]]\n", "data[:, :, 1] =\n", "[[0.71518937 0.54488318 0.64589411]\n", " [0.891773 0.38344152 0.52889492]\n", " [0.92559664 0.0871293 0.83261985]]\n" ] } ], "source": [ "print(X)" ] }, { "cell_type": "code", "execution_count": 277, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tensor of shape (3, 3, 2) with order F\n", "data[:, :, 0] =\n", "[[0.5488135 0.60276338 0.4236548 ]\n", " [0.43758721 0.96366276 0.79172504]\n", " [0.56804456 0.07103606 0.0202184 ]]\n", "data[:, :, 1] =\n", "[[0.71518937 0.54488318 0.64589411]\n", " [0.891773 0.38344152 0.52889492]\n", " [0.92559664 0.0871293 0.83261985]]" ] }, "execution_count": 277, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X # In the python interface" ] } ], "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.11.11" } }, "nbformat": 4, "nbformat_minor": 1 }