sparse
Tutorial 01: Sparse module basic usage.#
Contributors:
Mauricio Aristizabal
University of Texas at San Antonio
Universidad EAFIT, Colombia.
Date of creation: Apr 20 2016
Last modification: Jun 19 2024
Introduction#
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.
Importing the library#
Use the following commands to import the library into the current context.
[1]:
import pyoti.sparse as oti # Import the library.
Basics#
Sparse OTI scalar creation#
In order to create an OTI number (scalar), the easiest way to do so is as follows
[2]:
a = 10 + oti.e(1) + 5.2* oti.e([3,4]) + 3*oti.e([[2,3],4])
print(a)
10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])
[3]:
a = 10 + oti.e(1) + 5.2* oti.e([3,4]) + 3*oti.e([2,2,2,4])
print(a)
10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])
The OTI number a
has been created. This number represents the following:
The ‘direction array’ given as a parameter to the function oti.e(dirArray)
can be defined as follows:
oti.e(i)
:\(\epsilon_i\) (unsigned integer)oti.e([i,j,k,l])
: \(\epsilon_i\epsilon_j\epsilon_k\epsilon_l\) (plain list of integers, with \(n\) elements)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\))
The representation of the number and the conversion to string are equivalent:
[4]:
a
[4]:
10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])
Using string conversion or the print command.
[5]:
print(a)
10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])
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’
[6]:
print(a.order)
4
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.
[7]:
print(a.short_repr())
sotinum(10.0, nnz: 4, order: 4)
pyoti
also provides a longer representation accessible using the method long_repr()
, which provides a more detailed description of the sotinum object:
[8]:
print(a.long_repr())
sotinum(10.0, nnz: 4, alloc: 4, actual order: 4, truncation order: 4, flag: 1
- Order 1-> nnz: 1 size: 1
- Order 2-> nnz: 1 size: 1
- Order 3-> nnz: 0 size: 0
- Order 4-> nnz: 1 size: 1
)
where,
nnz
: Number of non-zeroes of the OTI numberalloc
: Number of coefficients allocated in the memory of the number.actual order
: Actual order of the OTI number. This value is typically lower than the truncation order, and rises according to the arithmetic operations.truncation order
: Maximum order for the imaginary directions propagated with this OTI number.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.Order O ->
: Line that shows the number of nonzero elements and allocation size in number of coefficients for the imaginary directions of orderO
.
Display options#
Each OTI number is displayed by default with a maximum of 4 imaginary directions and with format “g”.
The current version of the library supports changing the representation using the set_printoptions
function as follows:
oti.set_printoptions(float_format="g",terms_print=4)
float_format:
sets the format to print the coefficients in the OTI number.terms_print:
sets the number of terms to print from an OTI number.
TIP 1: Using
terms_print=-1
sets the OTI library to print all non-zero imaginary coefficients in the OTI numbers.TIP 2: Reverting to the default configuration, simply call the function with no arguments
set_printoptions()
.
[9]:
oti.set_printoptions(terms_print=-1)
help(oti.set_printoptions)
Help on built-in function set_printoptions in module pyoti.sparse:
set_printoptions(...)
PURPOSE: Set the print options for OTI numbers.
INPUTS:
- float_format: (Default "g") Define the print format for t
- terms_print: (Default 4) Number of terms to print.
In order to reset the values to defaults, just call set_printoptions() with no arguments.
Usign OTI numbers.#
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
:
Operation |
Symbol |
---|---|
Addition |
|
Subtraction |
|
Multiplication |
|
Division |
|
Power |
|
Arithmetic operations#
Addition#
Add two OTIs using the +
operator. For example, adding the numbers
[10]:
a = 10.0 + oti.e([1]) + 3.0*oti.e([[2,3],4]) + 5.2*oti.e([3,4])
b = 10.0 + 2.5*oti.e([1]) - 5.2*oti.e([3,4])
sum_result = a+b
print('The result is:', sum_result)
print("With truncation order:", sum_result.order)
The result is: 20 + 3.5 * e([1]) + 0 * e([3,4]) + 3 * e([[2,3],4])
With truncation order: 4
Subtraction#
To subtract two OTI numbers, use -
operator:
[11]:
sub_result = a-b
print("The result of a-b:",sub_result)
print("Order of a-b: ",sub_result.order)
The result of a-b: - 1.5 * e([1]) + 10.4 * e([3,4]) + 3 * e([[2,3],4])
Order of a-b: 4
Notice that the result has order 4, despite adding/subtracting two numbers with different orders (\(O(\) a
\() = 4\), \(O(\) b
\() = 2\)).
Addition and subtraction preserve the maximum truncation order from either of the operands.
Multiplication#
In order to Multiply two OTI numbers in the library, just use the *
operator:
[12]:
mult_result = a*b
print("The result of a*b:",mult_result)
print("Order of a*b: ",mult_result.order)
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]])
Order of a*b: 4
Notice that the order of the result is 4. Therefore, for the multiplication, the maximum truncation order is preserved.
Division#
Division between two OTIs is performed as follows:
[13]:
div_result = a/b
print("The result of a/b:",div_result)
print("Order of a/b: ",div_result.order)
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]])
Order of a/b: 4
Elementary functions#
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:
Trigonometric functions
Function |
Symbol |
---|---|
Sine |
|
Cosine |
|
Tangent |
|
Arcsine |
|
Arccosine |
|
Arctangent |
|
Hyperbolic functions
Function |
Symbol |
---|---|
Hyperbolic sine |
|
Hyperbolic cosine |
|
Hyperbolic tangent |
|
Inverse hyperbolic sine |
|
Inverse hyperbolic cosine |
|
Inverse hyperbolic tangent |
|
Logarithmic/Exponential functions
Function |
Symbol |
---|---|
Exponential |
|
Natural logarithm |
|
Logarithm base 10 |
|
Logarithm base b |
|
Power functions
Function |
Symbol |
---|---|
Power |
|
Square root |
|
Cubic root |
|
Other functions
Function |
Symbol |
---|---|
Error function |
|
[14]:
x = 3 + 2*oti.e([1]) - 4.3 * oti.e([2,3])
f = oti.sin(x)
print(f)
0.14112 - 1.97998 * e([1]) - 0.28224 * e([[1,2]]) + 4.25697 * e([2,3])
Get and set coefficients of an OTI number.#
To get an imaginary coefficient, use the method ‘get_im(dirArray)
’ and place the direction array of the direction you want
[15]:
a = 10.0 + oti.e([1]) + 3.0*oti.e([[2,3],4]) + 5.2*oti.e([3,4])
print(a)
10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])
To get the coefficient of the direction \(\epsilon_1\) use:
[16]:
print(a.get_im(1))
1.0
or
[17]:
print(a.get_im([1]))
1.0
To get the coefficient of \(\epsilon_2^3\epsilon_4\) use:
[18]:
print(a.get_im([[2,3],4]))
3.0
or
[19]:
print(a.get_im([2,2,2,4]))
3.0
Coefficients that are not explicitly shown in the number are zero. For example, the coefficient along direction \(\epsilon_1\epsilon_2^2\) is:
[20]:
print(a.get_im([1,[2,2]]))
0.0
There are two ways to get the real coefficient: Get the real
[21]:
print(a.get_im(0))
10.0
[22]:
a.real
[22]:
10.0
Set coefficients of an OTI number#
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.
[23]:
a = 10.0 + oti.e([1]) + 3.0*oti.e([[2,3],4]) + 5.2*oti.e([3,4])
print(a)
10 + 1 * e([1]) + 5.2 * e([3,4]) + 3 * e([[2,3],4])
[24]:
a.set_im(7.3,[3,4])
print(a)
10 + 1 * e([1]) + 7.3 * e([3,4]) + 3 * e([[2,3],4])
[25]:
a.set_im(4.2,[1,[3,2]])
print(a)
10 + 1 * e([1]) + 7.3 * e([3,4]) + 4.2 * e([1,[3,2]]) + 3 * e([[2,3],4])
Alternative way to get and set components.#
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.
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:
For order 2
dirArray |
Index |
order |
---|---|---|
|
0 |
2 |
|
1 |
2 |
|
2 |
2 |
|
3 |
2 |
|
4 |
2 |
|
5 |
2 |
For order 3.
dirArray |
Index |
order |
---|---|---|
|
0 |
3 |
|
1 |
3 |
|
2 |
3 |
|
3 |
3 |
|
4 |
3 |
|
5 |
3 |
|
6 |
3 |
|
7 |
3 |
|
8 |
3 |
|
9 |
3 |
Index-order pairs are defined as a list of two elements, first the index (0-based) and then the order: [idx,order]
.
[26]:
a[ [ 0, 1 ] ] # gets e([1]) coefficient
[26]:
1.0
[27]:
a[ [ 4, 3 ] ] = 55 # Sets e([1,1,3]) coefficient
a
[27]:
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])