{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Converting a `tensor` to a 2D numpy array and vice versa\n", "```\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", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We show how to convert a `tensor` to a 2D numpy array stored with extra information so that it can be converted back to a `tensor`. Converting to a 2D numpy array requires an ordered mapping of the `tensor` indices to the rows and the columns of the 2D numpy array." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pyttb as ttb\n", "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a `tenmat` (`tensor` as 2D numpy array) object" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "X" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Dims [0,1] map to rows, [2,3] to columns.\n", "A = X.to_tenmat(np.array([0, 1]), np.array([2, 3]))\n", "A" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "B = X.to_tenmat(np.array([1, 0]), np.array([2, 3])) # Order matters!\n", "B" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "C = X.to_tenmat(np.array([0, 1]), np.array([3, 2]))\n", "C" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a `tenmat` by specifying the dimensions mapped to the rows\n", "If just the row indices are specified, then the columns are arranged in increasing order." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1])) # np.array([1]) passed to the `rdims` parameter\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a `tenmat` by specifying the dimensions mapped to the columns\n", "Likewise, just the columns can be specified if the `cdims` argument is given. The columns are arranged in increasing order." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "# Same as A = ttb.tenmat.from_tensor_type(X, np.array([0,3]), np.array([1,2]))\n", "A = X.to_tenmat(cdims=np.array([1, 2]))\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Vectorize via `tenmat`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(cdims=np.arange(0, 4)) # Map all the dimensions to the columns\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Alternative ordering for the columns for mode-$n$ matricization\n", "Mode-$n$ matricization means that only mode $n$ is mapped to the rows. Different column orderings are available." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([2])) # By default, columns are ordered as [0, 1, 3].\n", "A" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), np.array([2, 0, 3])) # Explicit specification.\n", "A" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"fc\") # Forward cyclic, [2,3,0].\n", "A" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Constituent parts of a `tenmat`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "A.data # The 2D numpy array itself." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A.tshape # Shape of the original tensor." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A.rindices # Dimensions that were mapped to the rows." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A.cindices # Dimensions that were mapped to the columns." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a `tenmat` from its constituent parts" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "B = ttb.tenmat(A.data, A.rindices, A.cindices, A.tshape)\n", "B # Recreates A." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating an empty `tenmat`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "B = ttb.tenmat() # Empty tenmat.\n", "B" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use `double` to convert a `tenmat` to a 2D numpy array" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "A.double() # Converts A to a standard 2D numpy array." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use `to_tensor` to convert a `tenmat` to a `tensor`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "Y = A.to_tensor()\n", "Y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use `shape` and `tshape` for the dimensions of a tenmat" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "A.shape # 2D numpy array shape." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A.tshape # Corresponding tensor shape." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Subscripted reference for a `tenmat`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "A[1, 0] # Returns the (1,0) element of the 2D numpy array" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Subscripted assignment for a `tenmat`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "A[0:2, 0:2] = np.ones((2, 2))\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using negative indexing for the last array index" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A[-1][-1] # Same as A[1, 11]." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic operations for `tenmat`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "A.norm() # Norm of the 2D numpy array." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A.ctranspose() # Also swaps mapped dimensions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "+A # Calls uplus." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "-A # Calls uminus." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A + A # Calls plus" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A - A # Calls minus." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multiplying two `tenmat`s\n", "It is possible to compute the product of two `tenmat`s and have a result that can be converted into a `tensor`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "X = ttb.tensor(np.arange(1, 25), shape=(3, 2, 2, 2)) # Create a tensor.\n", "A = X.to_tenmat(np.array([1]), cdims_cyclic=\"bc\") # Backward cyclic, [0,3,2].\n", "B = A * A.ctranspose() # Tenmat that is the product of two tenmats.\n", "B" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "B.to_tensor() # Corresponding tensor." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Displaying a `tenmat`\n", "Shows the original tensor dimensions, the modes mapped to rows, the modes mapped to columns, and the 2D numpy array." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A" ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 1 }