{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## `sparse` Tutorial 01: Sparse module basic usage.\n", "\n", "```\n", "Contributors:\n", " Mauricio Aristizabal\n", " University of Texas at San Antonio\n", " Universidad EAFIT, Colombia.\n", "\n", "Date of creation: Apr 20 2016\n", "Last modification: Jun 19 2024\n", "```\n", "\n", "### Introduction\n", "\n", "The main purpose of this document is to show the basic operation of the sparse 'Order truncated Imaginary' (OTI) module in ```pyoti``` . Multiple examples are given in order to show and understand the capabilities of the library, as well as its basic advantages.\n", "\n", "#### Importing the library\n", "Use the following commands to import the library into the current context." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [], "source": [ "import pyoti.sparse as oti # Import the library. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Basics\n", "#### Sparse OTI scalar creation\n", "\n", "In order to create an OTI number (scalar), the easiest way to do so is as follows" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])\n" ] } ], "source": [ "a = 10 + oti.e(1) + 5.2* oti.e([3,4]) + 3*oti.e([[2,3],4])\n", "print(a)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])\n" ] } ], "source": [ "a = 10 + oti.e(1) + 5.2* oti.e([3,4]) + 3*oti.e([2,2,2,4])\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The OTI number ```a``` has been created. This number represents the following:\n", "\n", "$$\n", "a = 10 + \\epsilon_1 + 5.2 \\ \\epsilon_3\\epsilon_4+3 \\ \\epsilon_2^3\\epsilon_4 \n", "$$\n", "\n", "The 'direction array' given as a parameter to the function ```oti.e(dirArray)``` can be defined as follows:\n", "\n", "- ```oti.e(i)``` :$\\epsilon_i$ (unsigned integer)\n", "\n", "- ```oti.e([i,j,k,l])``` : $\\epsilon_i\\epsilon_j\\epsilon_k\\epsilon_l$ (plain list of integers, with $n$ elements)\n", "\n", "- ```oti.e([[i,m],j,[k,n],l])``` : $\\epsilon_i^m\\epsilon_j\\epsilon_k^n\\epsilon_l$ (the items with exponents other than 1, are defined as a 2-element list representing the base and exponent pair. Usually $m$, $n$ are greater than 1, but can be any integer $m,n\\geq0$)\n", "\n", "\n", "The representation of the number and the conversion to string are equivalent:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "data": { "text/plain": [ "10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using string conversion or the print command." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])\n" ] } ], "source": [ "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The number has been created with the maximum truncation order corresponding to the maximum order of the given term. To know the order of the number, just call the atribute 'order'" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4\n" ] } ], "source": [ "print(a.order)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some users maybe interested in knowing specific information about the sotinum object. ```pyoti``` provides a ```short_repr()``` method to show basic information about the object. It shows the real coefficient, the total number of non-zero imaginary coefficients and its truncation order." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sotinum(10.0, nnz: 4, order: 4)\n" ] } ], "source": [ "print(a.short_repr())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```pyoti``` also provides a longer representation accessible using the method ```long_repr()```, which provides a more detailed description of the sotinum object:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sotinum(10.0, nnz: 4, alloc: 4, actual order: 4, truncation order: 4, flag: 1\n", " - Order 1-> nnz: 1 size: 1 \n", " - Order 2-> nnz: 1 size: 1 \n", " - Order 3-> nnz: 0 size: 0 \n", " - Order 4-> nnz: 1 size: 1 \n", ")\n" ] } ], "source": [ "print(a.long_repr())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where, \n", "\n", "* ```nnz```: Number of non-zeroes of the OTI number\n", "* ```alloc```: Number of coefficients allocated in the memory of the number.\n", "* ```actual order```: Actual order of the OTI number. This value is typically lower than the truncation order, and rises according to the arithmetic operations. \n", "* ```truncation order```: Maximum order for the imaginary directions propagated with this OTI number.\n", "* ```flag```: Flag set to the OTI number. Typically, 1 represents that the object is owner of the memory, and 0 means it is not the owner of the memory. \n", "* ```Order O ->```: Line that shows the number of nonzero elements and allocation size in number of coefficients for the imaginary directions of order ```O```. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Display options\n", "Each OTI number is displayed by default with a maximum of 4 imaginary directions and with format \"g\". \n", "\n", "The current version of the library supports changing the representation using the ```set_printoptions``` function as follows:\n", "\n", "``` python\n", "oti.set_printoptions(float_format=\"g\",terms_print=4)\n", "```\n", "- ```float_format:``` sets the format to print the coefficients in the OTI number.\n", "- ```terms_print:``` sets the number of terms to print from an OTI number.\n", "\n", "\n", "\n", "> **TIP 1:** Using ```terms_print=-1``` sets the OTI library to print **all** non-zero imaginary coefficients in the OTI numbers.\n", "\n", "> **TIP 2:** Reverting to the default configuration, simply call the function with no arguments ```set_printoptions()```.\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Help on built-in function set_printoptions in module pyoti.sparse:\n", "\n", "set_printoptions(...)\n", " PURPOSE: Set the print options for OTI numbers.\n", " \n", " INPUTS:\n", " - float_format: (Default \"g\") Define the print format for t\n", " - terms_print: (Default 4) Number of terms to print.\n", " \n", " In order to reset the values to defaults, just call set_printoptions() with no arguments.\n", "\n" ] } ], "source": [ "oti.set_printoptions(terms_print=-1)\n", "help(oti.set_printoptions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Usign OTI numbers.\n", "\n", "In ```pyoti```, OTI numbers have overloaded operators so that operations work as similar as other scalar types in Python, like ```float``` or ```int```. The followign provides a summary of arithmetic operations supported in ```pyoti```:\n", "\n", "| Operation | Symbol | \n", "|---:|:---:|\n", "| Addition | ```a+b``` |\n", "| Subtraction | ```a-b``` |\n", "| Multiplication | ```a*b``` |\n", "| Division | ```a/b``` |\n", "| Power | ```a**b``` |\n", "\n", "\n", "#### Arithmetic operations\n", "\n", "##### Addition\n", "\n", "Add two OTIs using the ```+``` operator. For example, adding the numbers \n", "\n", "$$\n", "a = 10 + \\phantom{2.5 } \\epsilon_1 + 5.2 \\ \\epsilon_3\\epsilon_4+3 \\ \\epsilon_2^3\\epsilon_4\n", "$$\n", "\n", "$$\n", "b = 10 + 2.5 \\ \\epsilon_1 -5.2 \\ \\epsilon_3\\epsilon_4 \\phantom{+3 \\ \\epsilon_2^3\\epsilon_4}\n", "$$\n", "\n", "$$\n", "a+b = 20 + 3.5 \\ \\epsilon_1 +0 \\ \\epsilon_3\\epsilon_4 + 3 \\ \\epsilon_2^3\\epsilon_4\n", "$$" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The result is: 20 + 3.5 * e([1]) + 0 * e([3,4]) + 3 * e([[2,3],4])\n", "With truncation order: 4\n" ] } ], "source": [ "a = 10.0 + oti.e([1]) + 3.0*oti.e([[2,3],4]) + 5.2*oti.e([3,4]) \n", "b = 10.0 + 2.5*oti.e([1]) - 5.2*oti.e([3,4])\n", "\n", "sum_result = a+b\n", "print('The result is:', sum_result)\n", "print(\"With truncation order:\", sum_result.order)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Subtraction\n", "\n", "To subtract two OTI numbers, use ```-``` operator:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The result of a-b: - 1.5 * e([1]) + 10.4 * e([3,4]) + 3 * e([[2,3],4])\n", "Order of a-b: 4\n" ] } ], "source": [ "sub_result = a-b\n", "\n", "print(\"The result of a-b:\",sub_result)\n", "print(\"Order of a-b: \",sub_result.order)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the result has order 4, despite adding/subtracting two numbers with different orders ($O($ ```a``` $) = 4$, $O($ ```b``` $) = 2$). \n", "\n", "**Addition and subtraction preserve the maximum truncation order from either of the operands**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Multiplication\n", "\n", "In order to Multiply two OTI numbers in the library, just use the ```*``` operator:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The result of a*b: 100 + 35 * e([1]) + 2.5 * e([[1,2]]) + 0 * e([3,4]) + 7.8 * e([1,3,4]) + 30 * e([[2,3],4]) - 27.04 * e([[3,2],[4,2]])\n", "Order of a*b: 4\n" ] } ], "source": [ "mult_result = a*b\n", "print(\"The result of a*b:\",mult_result)\n", "print(\"Order of a*b: \",mult_result.order)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the order of the result is 4. Therefore, for the multiplication, the **maximum truncation order** is preserved.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Division\n", "\n", "Division between two OTIs is performed as follows:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The result of a/b: 1 - 0.15 * e([1]) + 0.0375 * e([[1,2]]) + 1.04 * e([3,4]) + 0.00625 * e([[1,3]]) - 0.078 * e([1,3,4]) + 0.3 * e([[2,3],4]) + 0.0325 * e([[1,2],3,4]) + 0.2704 * e([[3,2],[4,2]])\n", "Order of a/b: 4\n" ] } ], "source": [ "div_result = a/b\n", "print(\"The result of a/b:\",div_result)\n", "print(\"Order of a/b: \",div_result.order)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### Elementary functions\n", "\n", "```pyoti``` supports multiple functions, such as sine, cosine, etc. All functions are accessed from the module. For instance, using the imported ```pyoti.sparse``` module as ```oti```, functions can be called using e.g. ```oti.sin()```. Supported functions are given in the following table: \n", "\n", "Trigonometric functions\n", "\n", "| Function | Symbol | \n", "|---:|:---:|\n", "| Sine | ```sin(x)``` |\n", "| Cosine | ```cos(x)``` |\n", "| Tangent | ```tan(x)``` |\n", "| Arcsine | ```asin(x)``` |\n", "| Arccosine | ```acos(x)``` |\n", "| Arctangent | ```atan(x)``` |\n", "\n", "\n", "Hyperbolic functions\n", "\n", "| Function | Symbol | \n", "|---:|:---:|\n", "| Hyperbolic sine | ```sinh(x)``` |\n", "| Hyperbolic cosine | ```cosh(x)``` |\n", "| Hyperbolic tangent | ```tanh(x)``` |\n", "| Inverse hyperbolic sine | ```asinh(x)``` |\n", "| Inverse hyperbolic cosine | ```acosh(x)``` |\n", "| Inverse hyperbolic tangent | ```atanh(x)``` |\n", "\n", "\n", "\n", "Logarithmic/Exponential functions\n", "\n", "| Function | Symbol | \n", "|---:|:---:|\n", "| Exponential | ```exp(x)``` |\n", "| Natural logarithm | ```log(x)``` |\n", "| Logarithm base 10 | ```log10(x)``` |\n", "| Logarithm base b | ```logb(x,b)``` |\n", "\n", "\n", "\n", "Power functions\n", "\n", "| Function | Symbol | \n", "|---:|:---:|\n", "| Power | ```pow(x,e)``` or ```x**e``` |\n", "| Square root | ```sqrt(x)``` |\n", "| Cubic root | ```cbrt(x)``` |\n", "\n", "\n", "Other functions\n", "\n", "| Function | Symbol | \n", "|---:|:---:|\n", "| Error function | ```erf(x)``` |\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.14112 - 1.97998 * e([1]) - 0.28224 * e([[1,2]]) + 4.25697 * e([2,3])\n" ] } ], "source": [ "x = 3 + 2*oti.e([1]) - 4.3 * oti.e([2,3])\n", "f = oti.sin(x)\n", "print(f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Get and set coefficients of an OTI number.\n", "\n", "To get an imaginary coefficient, use the method '```get_im(dirArray)```' and place the direction array of the direction you want" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])\n" ] } ], "source": [ "a = 10.0 + oti.e([1]) + 3.0*oti.e([[2,3],4]) + 5.2*oti.e([3,4]) \n", "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get the coefficient of the direction $\\epsilon_1$ use:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.0\n" ] } ], "source": [ "print(a.get_im(1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "or" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.0\n" ] } ], "source": [ "print(a.get_im([1]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get the coefficient of $\\epsilon_2^3\\epsilon_4$ use:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.0\n" ] } ], "source": [ "print(a.get_im([[2,3],4]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "or" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.0\n" ] } ], "source": [ "print(a.get_im([2,2,2,4]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Coefficients that are not explicitly shown in the number are zero. For example, the coefficient along direction $\\epsilon_1\\epsilon_2^2$ is:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.0\n" ] } ], "source": [ "print(a.get_im([1,[2,2]]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are two ways to get the real coefficient: Get the real " ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10.0\n" ] } ], "source": [ "print(a.get_im(0))" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10.0" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.real" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Set coefficients of an OTI number \n", "\n", "Similar to the methods of getting coefficients, setting the values of coefficients works in the same way. If the coefficient already exist, it will be modified, and if the coefficient does not exist, it will be created. Use the method ```set_im(newValue,dirArray)``` to do so.\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])\n" ] } ], "source": [ "a = 10.0 + oti.e([1]) + 3.0*oti.e([[2,3],4]) + 5.2*oti.e([3,4]) \n", "print(a)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 + 1 * e([1]) + 7.3 * e([3,4]) + 3 * e([[2,3],4])\n" ] } ], "source": [ "a.set_im(7.3,[3,4])\n", "print(a)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "jupyter": { "outputs_hidden": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10 + 1 * e([1]) + 7.3 * e([3,4]) + 4.2 * e([1,[3,2]]) + 3 * e([[2,3],4])\n" ] } ], "source": [ "a.set_im(4.2,[1,[3,2]])\n", "print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Alternative way to get and set components.\n", "\n", "There is an alternative way to get and set the imaginary coefficients within pyoti. This approach uses the \"index, order\" pairs of an imagianry direction, instead of the \"direction array\" components.\n", "\n", "The index-order pair is the index according to the sorted imaginary directions of an OTI number. For a given order, the index is related to the position of the direction. For first order it is trivial, as the index corresponds to the basis of the imaginary direction (minus one). for Higher order, the index is determined by the appearance of the new imaginary basis. For instance:\n", "\n", "For order 2\n", "\n", "| dirArray | Index | order |\n", "|:--------:|:-----:|:------|\n", "|`[1,1]` | 0 | 2 |\n", "|`[1,2]` | 1 | 2 |\n", "|`[2,2]` | 2 | 2 |\n", "|`[1,3]` | 3 | 2 |\n", "|`[2,3]` | 4 | 2 |\n", "|`[3,3]` | 5 | 2 |\n", "\n", "For order 3.\n", "\n", "| dirArray | Index | order |\n", "|:--------:|:-----:|:------|\n", "|`[1,1,1]` | 0 | 3 |\n", "|`[1,1,2]` | 1 | 3 |\n", "|`[1,2,2]` | 2 | 3 |\n", "|`[2,2,2]` | 3 | 3 |\n", "|`[1,1,3]` | 4 | 3 |\n", "|`[1,2,3]` | 5 | 3 |\n", "|`[2,2,3]` | 6 | 3 |\n", "|`[1,3,3]` | 7 | 3 |\n", "|`[2,3,3]` | 8 | 3 |\n", "|`[3,3,3]` | 9 | 3 |\n", "\n", "\n", "Index-order pairs are defined as a list of two elements, first the index (0-based) and then the order: `[idx,order]`." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[ [ 0, 1 ] ] # gets e([1]) coefficient" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10 + 1 * e([1]) + 7.3 * e([3,4]) + 55 * e([[1,2],3]) + 4.2 * e([1,[3,2]]) + 3 * e([[2,3],4])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[ [ 4, 3 ] ] = 55 # Sets e([1,1,3]) coefficient\n", "a" ] } ], "metadata": { "kernelspec": { "display_name": "pyoti", "language": "python", "name": "pyoti" }, "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.9.13" } }, "nbformat": 4, "nbformat_minor": 4 }