diff --git a/dev-requirements.txt b/dev-requirements.txt index 31e4b98..7c3e0e6 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -18,3 +18,8 @@ pytest-cov>=5.0.0 # Allows codecov to generate coverage reports coverage[toml]>=6.4 codecov>=2.1.12 + +# Allows documentation +sphinx>=7.3.7 +sphinxawesome_theme>=5.2.0 +sphinx_collapse>=0.1.3 diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 0000000..e56b184 --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,8 @@ +a { + color: blue; /* Set the color of links to blue */ + text-decoration: underline; /* Underline links */ +} + +a:hover { + color: darkblue; /* Change link color to dark blue on hover */ +} diff --git a/docs/afjit.py b/docs/afjit.py new file mode 100644 index 0000000..97f3163 --- /dev/null +++ b/docs/afjit.py @@ -0,0 +1,24 @@ +# [jit-snippet] + +# As JIT is automatically enabled in ArrayFire, this version of the function +# forces each expression to be evaluated. If the eval() function calls are +# removed, then the execution of this code would be equivalent to the +# following function. + +def pi_no_jit(x, y, temp, samples): + temp = x * x + temp.eval() + temp += y * y + temp.eval() + temp = sqrt(temp) + temp.eval() + temp = temp < 1 + temp.eval() + return 4.0 * sum(temp) / samples + +def pi_jit(x, y, temp, samples): + temp = sqrt(x * x + y * y) < 1 + temp.eval() + return 4.0 * sum(temp) / samples + +# [jit-endsnippet] \ No newline at end of file diff --git a/docs/arrayandmatrixmanipulation.py b/docs/arrayandmatrixmanipulation.py new file mode 100644 index 0000000..55d5f77 --- /dev/null +++ b/docs/arrayandmatrixmanipulation.py @@ -0,0 +1,142 @@ +# [manipulation1-snippet] + +import arrayfire as af + +# Creates a 3x3 array filled with random numbers between [0, 1) +a = af.randu((3, 3)) + +# Flattens the array 'a' into a 1-dimensional column vector +flat_a = af.flat(a) + +# Display the original array 'a' +print(a) + +# [manipulation1-endsnippet] + + +# [manipulation2-snippet] + + +import arrayfire as af + +# Generate a 5x2 array of uniformly distributed random numbers between [0, 1) +a = af.randu((5, 2)) + +# Print the original array 'a' +print("Original array 'a' [5 2 1 1]") +print(a) + +# Flip the array 'a' along both axes (rows and columns) +flip_a = af.flip(a) + +# Print the flipped array 'flip_a' +print("\nFlipped array 'flip_a' [5 2 1 1]") +print(flip_a) + +# [manipulation2-endsnippet] + + +# [manipulation3-snippet] + +import arrayfire as af + +# Generate a 1-dimensional array 'a' of size 5 filled with uniformly distributed random numbers between [0, 1) +a = af.randu((5,)) + +# Print the original array 'a' +print("Original array 'a' [5 1 1 1]") +print(a) + +# Join the array 'a' with itself along axis 0 +a_join = af.join(0, a, a) + +# Print the joined array 'a_join' +print("\nJoined array 'a_join' [10 1 1 1]") +print(a_join) +# [manipulation3-endsnippet] + + +# [manipulation4-snippet] + +import arrayfire as af + +a = af.randu((8,)) + +print(a) + +moddims_a = af.moddims(a,(2,4)) + +print(moddims_a) + +moddims_b = af.moddims(a,(len(a),)) +print(moddims_b) + +# [manipulation4-endsnippet] + + +# [manipulation5-snippet] + + +import arrayfire as af + +a = af.randu((2,2,3,1)) + +print(a) + +a_reorder = af.reorder(a,()) +# [manipulation5-endsnippet] + +# [manipulation6-snippet] + +import arrayfire as af + +a = af.randu((3,5)) +print(a) + +a_shift = af.shift(a,(0,2)) +print(a_shift) + +a_shift1 = af.shift(a,(-1,2)) +print(a_shift1) + +# [manipulation6-endsnippet] + + +# [manipulation7-snippet] + +import arrayfire as af + +a = af.randu((3,)) #[3,1,1,1] + +print (a) + +a_tile = af.tile(a,(2,)) +print(a_tile) + +a_tile1 = af.tile(a,(2,2)) +print(a_tile1) + +a_tile2 = af.tile(a,(1,2,3)) +print(a_tile2) +# [manipulation7-endsnippet] + + +# [manipulation8-snippet] + +import arrayfire as af + +a = af.randu((3,3)) +print(a) #[3 3 1 1] + +''' 0.3949 0.8465 0.3709 + 0.3561 0.9399 0.2751 + 0.6097 0.6802 0.2720''' + + +a_transpose = af.transpose(a) +print(a_transpose) #[3 3 1 1] + +''' 0.3949 0.3561 0.6097 + 0.8465 0.9399 0.6802 + 0.3709 0.2751 0.2720''' +# [manipulation8-endsnippet] diff --git a/docs/arrayandmatrixmanipulation.rst b/docs/arrayandmatrixmanipulation.rst new file mode 100644 index 0000000..ef691e4 --- /dev/null +++ b/docs/arrayandmatrixmanipulation.rst @@ -0,0 +1,144 @@ +Array and Matrix Manipulation +============================= +ArrayFire provides several different methods for manipulating arrays and matrices. The functionality includes: + +* moddims() - change the dimensions of an array without changing the data +* array() - create a (shallow) copy of an array with different dimensions. +* flat() - flatten an array to one dimension +* flip() - flip an array along a dimension +* join() - join up to 4 arrays +* reorder() - changes the dimension order within the array +* shift() - shifts data along a dimension +* tile() - repeats an array along a dimension +* transpose() - performs a matrix transpose +* T() - transpose a matrix or vector (shorthand notation) +* H() - Hermitian Transpose (conjugate-transpose) a matrix + +Below we provide several examples of these functions and their use. + +flat() +====== +The **flat()** function flattens an array to one dimension: + +.. literalinclude:: arrayandmatrixmanipulation.py + :language: python + :start-after: [manipulation1-snippet] + :end-before: [manipulation1-endsnippet] + +The **flat** function can be called from Python as follows: + +.. admonition:: Function + + af.flat(array) - Python function for flattening an array + +flip() +====== +The **flip()** function flips the contents of an array along a chosen dimension. In the example below, we show the 5x2 array flipped along the zeroth (i.e. within a column) and first (e.g. across rows) axes: + + +.. literalinclude:: arrayandmatrixmanipulation.py + :language: python + :start-after: [manipulation2-snippet] + :end-before: [manipulation2-endsnippet] + +The **flip** function can be called from Python as follows: + +.. admonition:: Function + + af.flip(array) - Python function for flipping an array + + +join() +====== + +The **join()** function joins arrays along a specific dimension. The C++ interface can join up to four arrays whereas the C interface supports up to 10 arrays. Here is an example of how to use join an array to itself: + +.. literalinclude:: arrayandmatrixmanipulation.py + :language: python + :start-after: [manipulation3-snippet] + :end-before: [manipulation3-endsnippet] + + +The **join** function can be called from Python as follows: + +.. admonition:: Function + + af.join(0, array, array1) - Python function for joining arrays along a specified axis + +moddims() +========= + +The **moddims()** function changes the dimensions of an array without changing its data or order. Note that this function modifies only the metadata associated with the array. It does not modify the content of the array. Here is an example of moddims() converting an 8x1 array into a 2x4 and then back to a 8x1: + +.. literalinclude:: arrayandmatrixmanipulation.py + :language: python + :start-after: [manipulation4-snippet] + :end-before: [manipulation4-endsnippet] + +The moddims function has a single form in the Python API: + +.. admonition:: Function + + af.moddims(array, (3,2)) - Python function for modifying dimensions of an array + + +reorder() +========= +The **reorder()** function modifies the order of data within an array by exchanging data according to the change in dimensionality. The linear ordering of data within the array is preserved. + +.. literalinclude:: arrayandmatrixmanipulation.py + :language: python + :start-after: [manipulation5-snippet] + :end-before: [manipulation5-endsnippet] + +shift() +======= +The **shift()** function shifts data in a circular buffer fashion along a chosen dimension. Consider the following example: + + +.. literalinclude:: arrayandmatrixmanipulation.py + :language: python + :start-after: [manipulation6-snippet] + :end-before: [manipulation6-endsnippet] + +The shift function can be called from Python as follows: +.. admonition:: Function + + af.shift(array, (3,2)) - Python function for shifting arrays along specified dimension + +tile() +====== +The **tile()** function repeats an array along the specified dimension. For example below we show how to tile an array along the zeroth and first dimensions of an array: + +.. literalinclude:: arrayandmatrixmanipulation.py + :language: python + :start-after: [manipulation7-snippet] + :end-before: [manipulation7-endsnippet] + +.. admonition:: Function + + af.tile(array, (3,2)) - Python function that tiles arrays along specified dimensions + + +transpose() +=========== +The **transpose()** function performs a standard matrix transpose. The input array must have the dimensions of a 2D-matrix. + +.. literalinclude:: arrayandmatrixmanipulation.py + :language: python + :start-after: [manipulation8-snippet] + :end-before: [manipulation8-endsnippet] + + +The python interface for transpose is as follows: + +.. admonition:: Function + + af.transpose(array) - Python function to transpose matrix in place + + + +array() +======= +**array()** can be used to create a (shallow) copy of a matrix with different dimensions. The total number of elements must remain the same. This function is a wrapper over the moddims() function discussed earlier. + diff --git a/docs/arrayfirejitcodegeneration.rst b/docs/arrayfirejitcodegeneration.rst new file mode 100644 index 0000000..d536368 --- /dev/null +++ b/docs/arrayfirejitcodegeneration.rst @@ -0,0 +1,45 @@ +ArrayFire JIT Code Generation +============================= +The ArrayFire library offers JIT (Just In Time) compiling for elementwise arithmetic operations. This includes trigonometric functions, comparisons, and element-wise operations. + +At runtime, ArrayFire aggregates these function calls using an Abstract Syntax Tree (AST) data structure such that whenever a JIT-supported function is called, it is added into the AST for a given variable instance. The AST of the variable is computed if one of the following conditions is met: + +* an explication evaluation is required by the programmer using the eval function, or +* the variable is required to compute a different variable that is not JIT-supported. + +When the above occurs, and the variable needs to be evaluated, the functions and variables in the AST data structure are used to create a single kernel. This is done by creating a customized kernel on-the-fly that is made up of all the functions in the AST. The customized function is then executed. + +This JIT compilation technique has multiple benefits: + +* A reduced number of kernel calls – a kernel call can be a significant overhead for small data sets. +* Better cache performance – there are many instances in which the memory required by a single element in the array can be reused multiple times, or the temporary value of a computation can be stored in the cache and reused by future computations. +* Temporary memory allocation and write-back can be reduced – when multiple expressions are evaluated and stored into temporary arrays, these arrays need to be allocated and the results written back to main memory. +* Avoid computing elements that are not used – there are cases in which the AST is created for a variable; however, the expression is not used later in the computation. Thus, its evaluation can be avoided. +* Better performance – all the above can help reduce the total execution time. + +.. literalinclude:: afjit.py + :language: python + :start-after: [jit-snippet] + :end-before: [jit-endsnippet] + + +The above code computes the value of π using a Monte-Carlo simulation where points are randomly generated within the unit square. Each point is tested to see if it is within the unit circle. The ratio of points within the circle and square approximate the value π. The accuracy of π improves as the number of samples is increased, which motivates using additional samples. + +There are two implementations above: + +1. an implementation that does not benefit from the JIT (pi_no_jit), and +2. an implementation that takes advantage of the JIT feature (pi_jit). + +Specifically, as JIT is an integral feature of the ArrayFire library, it cannot simply be turned on and off. The only way for a programmer to sidestep the JIT operations is to manually force the evaluation of expressions. This is done in the non-JIT-supported implementation. + +Timing these two implementations results in the following performance benchmark: + +**Add Picture** + +The above figure depicts the execution time (abscissa) as a function of the number of samples (ordinate) for the two implementations discussed above. + +When the number of samples is small, the execution time of pi_no_jit is dominated by the launch of multiple kernels and the execution time pi_jit is dominated by on-the-fly compilation of the JIT code required to launch a single kernel. Even with this JIT compilation time, pi_jit outperforms pi_no_jit by 1.4-2.0X for smaller sample sizes. + +When the number of samples is large, both the kernel launch overhead and the JIT code creation are no longer the limiting factors – the kernel’s computational load dominates the execution time. Here, the pi_jit outperforms pi_no_jit by 2.0-2.7X. + +The number of applications that benefit from the JIT code generation is significant. The actual performance benefits are also application-dependent. \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..256a621 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,46 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +import sys +import os +sys.path.insert(0, os.path.abspath('../..')) + + + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'ArrayFire' +copyright = '2024, ArrayFire' +author = 'ArrayFire' +release = '' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.duration', + 'sphinx.ext.doctest', + 'sphinx.ext.autodoc', + 'sphinx.ext.mathjax', + 'sphinx_collapse', +] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinxawesome_theme' +html_static_path = ['_static'] +html_permalinks = False + +# -- Suppress specific warnings -------------------------------------------- + +suppress_warnings = [ + 'ref.include_missing', +] diff --git a/docs/configuringarrayfireenvironment.rst b/docs/configuringarrayfireenvironment.rst new file mode 100644 index 0000000..00754e6 --- /dev/null +++ b/docs/configuringarrayfireenvironment.rst @@ -0,0 +1,9 @@ +Configuring ArrayFire Environment +================================= +This page lists environment and runtime configurations that will help enhance your experience with ArrayFire. + +Environment Variables +===================== +The following are useful environment variable that can be used with ArrayFire. + + diff --git a/docs/debuggingarrayfirecode.rst b/docs/debuggingarrayfirecode.rst new file mode 100644 index 0000000..cb36d03 --- /dev/null +++ b/docs/debuggingarrayfirecode.rst @@ -0,0 +1,9 @@ +Debugging ArrayFire Issues +========================== +Python +~~~~~~ +* :literal:`arrayfire.device.print_mem_info("message")`: Print table of memory used by ArrayFire on the active GPU + +Further Reading +=============== +See the `ArrayFire README `_ for support information. \ No newline at end of file diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..b78d09a --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,7 @@ +Examples +======== + + +TO DO +----- +ADD LIST OF EXAMPLES \ No newline at end of file diff --git a/docs/functions.rst b/docs/functions.rst new file mode 100644 index 0000000..8fa82ee --- /dev/null +++ b/docs/functions.rst @@ -0,0 +1,122 @@ +Functions +========= + +Documentation grouped by category: + + +.. collapse:: Arrayfire Classes + + .. list-table:: + + * - Class 1 + - Description of Class 1 + + +.. collapse:: Arrayfire Functions + + .. list-table:: + + * - :doc:`af.array() ` + - Creates an array from a list or other data structure. + * - :doc:`af.zeros() ` + - Creates an array filled with zeros. + * - :doc:`af.ones() ` + - Creates an array filled with ones. + * - :doc:`af.randu() ` + - Creates an array with random uniform values. + * - :doc:`af.randn() ` + - Creates an array with random normal values. + * - :doc:`af.transpose() ` + - Transposes an array. + * - :doc:`af.reshape() ` + - Reshapes an array. + * - :doc:`af.reorder() ` + - Reorders dimensions of an array. + * - :doc:`af.slice() ` + - Extracts a slice of an array. + * - :doc:`af.join() ` + - Joins arrays along a specified dimension. + * - :doc:`af.add() ` + - Performs element-wise addition. + * - :doc:`af.subtract() ` + - Performs element-wise subtraction. + * - :doc:`af.multiply() ` + - Performs element-wise multiplication. + * - :doc:`af.divide() ` + - Performs element-wise division. + * - :doc:`af.dot() ` + - Computes the dot product of two arrays. + * - :doc:`af.exp() ` + - Computes the exponential of each element. + * - :doc:`af.log() ` + - Computes the natural logarithm of each element. + * - :doc:`af.sqrt() ` + - Computes the square root of each element. + * - :doc:`af.matmul() ` + - Matrix multiplication. + * - :doc:`af.inv() ` + - Computes the inverse of a matrix. + * - :doc:`af.det() ` + - Computes the determinant of a matrix. + * - :doc:`af.eig() ` + - Computes eigenvalues and eigenvectors. + * - :doc:`af.fft() ` + - Computes the Fast Fourier Transform. + * - :doc:`af.ifft() ` + - Computes the Inverse Fast Fourier Transform. + * - :doc:`af.mean() ` + - Computes the mean of elements. + * - :doc:`af.stdev() ` + - Computes the standard deviation. + * - :doc:`af.median() ` + - Computes the median value. + * - :doc:`af.sum() ` + - Computes the sum of elements. + * - :doc:`af.prod() ` + - Computes the product of elements. + * - :doc:`af.device() ` + - Returns the device identifier. + * - :doc:`af.get_device() ` + - Matrix multiplication. + * - :doc:`af.set_device() ` + - Gets the current device. + * - :doc:`af.get() ` + - Copies data from the GPU to the CPU. + * - :doc:`af.min() ` + - Finds the minimum value. + * - :doc:`af.max() ` + - Finds the maximum value. + + +.. collapse:: Arrayfire Functions by Category + + .. list-table:: + + * - **Array Creation** + - Functions in this category are used to initialize arrays with specific values or patterns. + * - **Array Manipulation** + - These functions help modify the structure or arrangement of arrays. + * - **Mathematical Operations** + - Functions for performing fundamental arithmetic and mathematical operations on arrays. + * - **Linear Algebra** + - Functions for performing linear algebra operations, essential in many scientific and engineering tasks. + * - **Fourier Transforms** + - Functions for performing Fourier analysis, essential for signal processing and frequency analysis. + * - **Statistics** + - Functions for computing statistical metrics and analyzing data distributions. + * - **Data Reduction** + - Functions for aggregating and reducing data to summarize or condense information. + * - **Utilities** + - General-purpose functions for managing arrays and devices. + * - **Special Functions** + - Functions for creating and applying specific types of filters, commonly used in signal processing and analysis. + + +.. collapse:: Graphics + + .. list-table:: + + * - **Rendering Function** + - Rendering Function to draw images, plots etc + * - **Window Function** + - Window Creation, modification and destruction of functions diff --git a/docs/functions/add.rst b/docs/functions/add.rst new file mode 100644 index 0000000..05d70fa --- /dev/null +++ b/docs/functions/add.rst @@ -0,0 +1,30 @@ +add +=== +The 'af.add()' function in ArrayFire is used to perform element-wise addition between arrays. This function enables you to add corresponding elements of two arrays or an array and a scalar, resulting in a new array where each element is the sum of the respective elements from the input arrays or the scalar. + + +Function +-------- +:literal:`af.add()` + - Python interface used to perform element-wise addition. + +Detailed Description +-------------------- +The 'af.add()' function computes the sum of two arrays or an array and a scalar, element by element. When adding two arrays, both must have the same dimensions, or be broadcastable to a common shape. For array-scalar addition, the scalar value is added to each element of the array. This function is particularly useful for operations requiring element-wise arithmetic. + +Function Documentation +---------------------- +.. sidebar:: af.add() + + Syntax: + af.add(array1, array2) + af.add(array, scalar) + + Parameters: + 'array1': The first ArrayFire array in the addition operation. + 'array2': The second ArrayFire array with the same dimensions as 'array1', or compatible for broadcasting. + 'array': An ArrayFire array to which the scalar is added. + 'scalar': A scalar value to be added to each element of 'array'. + + Returns: + An ArrayFire array containing the element-wise sum of the inputs. diff --git a/docs/functions/array.rst b/docs/functions/array.rst new file mode 100644 index 0000000..9605817 --- /dev/null +++ b/docs/functions/array.rst @@ -0,0 +1,30 @@ +array +===== +The 'af.array()' function is part of the ArrayFire library, which provides a powerful framework for performing high-performance numerical computations. This function is designed to create an ArrayFire array from various data structures, such as Python lists or other iterable collections. + +Function +-------- +:literal:`af.array()` + - Python interface to form an array. + + +Detailed Description +-------------------- +The 'af.array()' function allows you to convert a data structure, such as a Python list or tuple, into an ArrayFire array. This conversion is essential for leveraging ArrayFire's optimized computational capabilities. By creating an array from a list or another iterable, you can perform efficient mathematical operations, matrix manipulations, and other numerical computations using ArrayFire's APIs. + +The function supports multiple data types and can handle multi-dimensional arrays. The ability to create an ArrayFire array from native Python structures makes it easier to integrate ArrayFire into Python-based workflows. + +Function Documentation +---------------------- +.. sidebar:: af.array() + + Syntax: + af.array(data) + + Parameters: + 'data': A list, tuple, or another iterable containing the elements to be converted into an ArrayFire array. The data should be in a format that ArrayFire can interpret, such as nested lists for multi-dimensional arrays. + + Returns: + An ArrayFire array containing the elements from the input data structure. + + diff --git a/docs/functions/det.rst b/docs/functions/det.rst new file mode 100644 index 0000000..f2acfda --- /dev/null +++ b/docs/functions/det.rst @@ -0,0 +1,25 @@ +det +=== +The 'af.det()' function in ArrayFire computes the determinant of a square matrix. The determinant is a scalar value that provides important properties of the matrix, such as whether it is invertible. It is a fundamental operation in linear algebra with applications in areas such as system of equations, matrix inversion, and eigenvalue problems. + +Function +-------- +:literal:`af.det()` + - Python interface to find determinant of a square matrix. + +Detailed Description +-------------------- +The 'af.det()' function calculates the determinant of a square matrix. The matrix must be square, meaning the number of rows must be equal to the number of columns. The determinant of a matrix is a scalar that can provide information about the matrix, such as its invertibility. If the determinant is zero, the matrix is singular (non-invertible); if it is non-zero, the matrix is invertible. + +Function Documentation +---------------------- +.. sidebar:: af.det() + + Syntax: + af.det(matrix) + + Parameters: + 'matrix': A 2D ArrayFire array (matrix) whose determinant is to be calculated. The matrix must be square, i.e., the number of rows must be equal to the number of columns. + + Returns: + A scalar value representing the determinant of the input matrix. The type of the result will be the same as the input matrix data type. diff --git a/docs/functions/device.rst b/docs/functions/device.rst new file mode 100644 index 0000000..318547b --- /dev/null +++ b/docs/functions/device.rst @@ -0,0 +1,43 @@ +device +====== +The 'af.device()' function in the ArrayFire library is used to get or set the current device or device properties. This function provides an interface to interact with the GPU or CPU device being used for computations. It allows you to select a particular device from available options or query the properties of the current device. + +Function +-------- +:literal:`af.device()` + - Python interface to get or set device properties. + +Detailed Description +-------------------- +The 'af.device()' function is useful for managing and querying the device that ArrayFire is using for computations. This function can perform various tasks related to device management, such as: + +- Getting the Current Device: Retrieve the index of the currently selected device. +- Setting the Current Device: Select a specific device for computations. +- Querying Device Properties: Obtain information about the current device. + +This functionality is important for applications that need to explicitly choose between multiple GPUs or check device capabilities. + +Function Documentation +---------------------- +.. sidebar:: af.device() + + Syntax: + af.device(option=None, value=None) + + Parameters: + 'option' (optional): Specifies the type of information or action. Can be one of the following: + + - "device": Get the current device index. + - "set": Set the current device index. + - "count": Get the number of available devices. + - "name": Get the name of the current device. + - "info": Get detailed information about the current device. + + 'value (optional)': The index of the device to set. Used only when the option is "set". If option is "set", this parameter should be an integer representing the device index. + + Returns: + Depending on the option, the function returns various types of information: + - If 'option' is "device": The index of the current device. + - If 'option' is "count": The number of available devices. + - If 'option' is "name": The name of the current device. + - If 'option' is "info": Detailed information about the current device. diff --git a/docs/functions/divide.rst b/docs/functions/divide.rst new file mode 100644 index 0000000..74d7880 --- /dev/null +++ b/docs/functions/divide.rst @@ -0,0 +1,36 @@ +divide +====== +The 'af.divide()' function in the ArrayFire library performs element-wise division of two arrays. This function allows you to divide one array by another, or to divide each element of an array by a scalar value. It supports operations on arrays of the same shape or on a scalar and an array. + +Function +-------- +:literal:`af.divide` + - Python interface for dividing one array by another. + +Detailed Description +-------------------- +The af.divide() function is used to perform element-wise division of arrays or between an array and a scalar. This operation divides each element of the first array by the corresponding element of the second array, or divides each element of the array by the scalar value. + +**Element-wise Division:** When dividing two arrays, the function performs the division for each corresponding element of the arrays. + +**Scalar Division:** When dividing an array by a scalar, the function divides each element of the array by the scalar value. + +The function handles broadcasting automatically when the dimensions of the arrays are compatible. + +Function Documentation +---------------------- +.. sidebar:: af.divide + + Syntax: + af.divide(array1, array2) + af.divide(array, scalar) + + + Parameters: + - array1: The first ArrayFire array (numerator) in the division operation. + - array2: The second ArrayFire array (denominator) in the division operation. Must have the same shape as array1 or be a scalar. + - array: The ArrayFire array to be divided by a scalar. + - scalar: The scalar value used to divide each element of array. + + Returns: + An ArrayFire array containing the results of the element-wise division. The resulting array will have the same shape as the input arrays. diff --git a/docs/functions/dot.rst b/docs/functions/dot.rst new file mode 100644 index 0000000..c776872 --- /dev/null +++ b/docs/functions/dot.rst @@ -0,0 +1,31 @@ +dot +=== +The 'af.dot()' function in ArrayFire computes the dot product of two arrays. This function is used for calculating the scalar product of two vectors or the matrix multiplication when applied to matrices. It is a fundamental operation in linear algebra with applications in various fields such as machine learning, physics, and numerical computing. + +Function +-------- +:literal:`af.dot()` + - Python interface to compute dot product of two arrays. + +Detailed Description +-------------------- +The af.dot() function computes the dot product, which can refer to different operations depending on the type of input: + +**Dot Product of Two Vectors:** For 1D arrays (vectors), the function calculates the scalar product. This is the sum of the products of corresponding elements of the vectors. + +**Matrix Multiplication:** For 2D arrays (matrices), it performs matrix multiplication, which is also referred to as the dot product in this context. + +Function Documentation +---------------------- +.. sidebar:: af.dot() + + Syntax: + af.dot(array1, array2) + + Parameters: + 'array1': The first ArrayFire array (vector or matrix) in the dot product operation. + 'array2': The second ArrayFire array (vector or matrix) in the dot product operation. Must be compatible with 'array1' for dot product or matrix multiplication. + + Returns: + - For vectors: A scalar value representing the dot product of the vectors. + - For matrices: An ArrayFire array representing the result of matrix multiplication. \ No newline at end of file diff --git a/docs/functions/eig.rst b/docs/functions/eig.rst new file mode 100644 index 0000000..250547a --- /dev/null +++ b/docs/functions/eig.rst @@ -0,0 +1,37 @@ +eig +=== +The 'af.eig()' function in ArrayFire computes the eigenvalues and eigenvectors of a square matrix. This function is essential in linear algebra for analyzing matrices and is used in various applications including machine learning, physics, and numerical analysis. + +Function +-------- +:literal:`af.eig()` + - Python interface for computing the eigenvalues and eigenvectors of a square matrix. + +Detailed Description +-------------------- +The 'af.eig()' function computes the eigenvalues and eigenvectors of a square matrix. For a given matrix +A, eigenvalues and eigenvectors are defined such that: + +𝐴𝑣 = 𝜆𝑣 + + +where: +- λ represents an eigenvalue. +- v represents the corresponding eigenvector. +The function returns two arrays: +- An array containing the eigenvalues of the matrix. +- An array containing the eigenvectors of the matrix. Each column in this array corresponds to an eigenvector. + +Function Documentation +---------------------- +.. sidebar:: af.eig() + + Syntax: + eigenvalues, eigenvectors = af.eig(matrix) + + Parameters: + 'matrix': A 2D ArrayFire array (matrix) for which eigenvalues and eigenvectors are to be computed. The matrix must be square (i.e., the number of rows must be equal to the number of columns). + + Returns: + - eigenvalues: An ArrayFire array containing the eigenvalues of the input matrix. + - eigenvectors: An ArrayFire array containing the eigenvectors of the input matrix. Each column of this array is an eigenvector corresponding to the eigenvalues. diff --git a/docs/functions/exp.rst b/docs/functions/exp.rst new file mode 100644 index 0000000..5d06ec0 --- /dev/null +++ b/docs/functions/exp.rst @@ -0,0 +1,32 @@ +exp +=== +The 'af.exp()' function in ArrayFire computes the element-wise exponential of an array. This function calculates the exponential of each element in the input array, using the base of the natural logarithm (e ≈ 2.71828). It is commonly used in scientific computing, machine learning, and numerical analysis where exponential transformations are required. + +Function +-------- +:literal:`af.exp()` + - Python interface used to compute the exponential of each element in an array. + +Detailed Description +-------------------- +The af.exp() function performs the exponential operation on each element of the input array. If +𝑥 is an element in the array, then exp⁡(𝑥) computes 𝑒𝑥, where 𝑒 is the base of the natural logarithm. + +This function supports: + +- **Element-wise Operations:** Applying the exponential function to each element of the array independently. +- **Broadcasting:** Automatically handling arrays of different shapes for operations where applicable. + +Function Documentation +---------------------- +.. sidebar:: af.exp() + + Syntax: + af.exp(array) + + Parameters: + 'array': An ArrayFire array (1D, 2D, or higher-dimensional) for which the exponential is to be computed. + + Returns: + An ArrayFire array with the same shape as the input array, where each element is the exponential of the corresponding element in the input array. + diff --git a/docs/functions/fft.rst b/docs/functions/fft.rst new file mode 100644 index 0000000..e648438 --- /dev/null +++ b/docs/functions/fft.rst @@ -0,0 +1,41 @@ +fft +=== +The 'af.fft()' function in ArrayFire computes the Fast Fourier Transform (FFT) of an array. The FFT is an efficient algorithm to compute the discrete Fourier transform (DFT) and its inverse. The function is used to transform signals from the time domain to the frequency domain, which is essential in signal processing, image analysis, and many other fields. + +Function +-------- +:literal:`af.fft()` + - Python interface for transforming signals from the time domain to the frequency domain. + +Detailed Description +-------------------- +The 'af.fft()' function performs a discrete Fourier transform of the input array. The FFT is commonly used for: + +- Signal Processing: Analyzing the frequency components of a signal. +- Image Processing: Transforming images to the frequency domain for various processing tasks. +- Numerical Analysis: Solving differential equations and other problems where Fourier methods are applicable. + +The function supports: + +- 1D FFT: Transforming 1D arrays (vectors). +- 2D FFT: Transforming 2D arrays (matrices). +- 3D FFT: Transforming 3D arrays (volumes). +- Multi-dimensional FFTs: Transforming arrays of higher dimensions. + +The FFT can be computed along specified dimensions or all dimensions of the input array. + +Function Documentation +---------------------- +.. sidebar:: af.fft() + + Syntax: + af.fft(array, dim=None, is_inverse=False) + + Parameters: + - array: The ArrayFire array (1D, 2D, or higher-dimensional) to transform. + - dim (optional): The dimension along which to compute the FFT. If not specified, the FFT is computed along all dimensions. + - is_inverse (optional): A boolean flag indicating whether to compute the inverse FFT. If True, the function computes the inverse FFT; otherwise, it computes the forward FFT. + + Returns: + An ArrayFire array of the same shape as the input array containing the FFT of the input array. + diff --git a/docs/functions/get.rst b/docs/functions/get.rst new file mode 100644 index 0000000..f7d8956 --- /dev/null +++ b/docs/functions/get.rst @@ -0,0 +1,31 @@ +get +=== +The af.get() function in ArrayFire is used to retrieve data from an ArrayFire array and copy it into a standard Python data structure, such as a NumPy array or a Python list. This function is useful for accessing and manipulating the data stored in ArrayFire arrays within the Python environment. + +Function +-------- +:literal:`af.get()` + - Python interface used to retrieve data from an ArrayFire array and copy it into a standard Python data structure. + +Detailed Description +-------------------- +The 'af.get()' function is used to copy the data from an ArrayFire array into a standard Python data structure. This function is typically used when you need to work with the data in a format that is compatible with other Python libraries or for further analysis and manipulation in Python. + +Key Points: + +- Conversion to NumPy Arrays: You can convert ArrayFire arrays to NumPy arrays for use with libraries that do not support ArrayFire arrays directly. +- Conversion to Python Lists: You can convert ArrayFire arrays to Python lists for ease of use in general Python code. + +Function Documentation +---------------------- +.. sidebar:: af.get() + + Syntax: + af.get(array) + + + Parameters: + 'array': The ArrayFire array from which to retrieve data. + + Returns: + A standard Python data structure (e.g., NumPy array or Python list) containing the data from the ArrayFire array. diff --git a/docs/functions/get_device.rst b/docs/functions/get_device.rst new file mode 100644 index 0000000..953b0c2 --- /dev/null +++ b/docs/functions/get_device.rst @@ -0,0 +1,27 @@ +get_device +========== +The 'af.get_device()' function in ArrayFire is used to retrieve the device object associated with the ArrayFire context. This function provides information about the current active device, such as its ID, name, and properties. It is useful for managing and querying the status of devices in a multi-device environment. + +Function +-------- +:literal:`af.get_device()` + - Python interface used to retrieve the device object associated with the ArrayFire context. + +Detailed Description +-------------------- +The 'af.get_device()' function provides access to the device object in ArrayFire. This device object contains various details about the GPU or other computational device that ArrayFire is using for computations. This function is particularly useful in environments where multiple devices are available and you need to query or manage device-specific information. + +Function Documentation +---------------------- +.. sidebar:: af.get_device() + + Syntax: + device = af.get_device() + + + Parameters: + This function does not take any parameters. + + Returns: + An ArrayFire device object representing the current active device in the ArrayFire context. + diff --git a/docs/functions/ifft.rst b/docs/functions/ifft.rst new file mode 100644 index 0000000..991576c --- /dev/null +++ b/docs/functions/ifft.rst @@ -0,0 +1,27 @@ +ifft +==== +The 'af.ifft()' function in ArrayFire computes the inverse Fast Fourier Transform (IFFT) of an array. The IFFT is used to transform data from the frequency domain back to the time domain, which is crucial in many signal processing, image analysis, and scientific computing applications. + +Function +-------- +:literal:`af.ifft()` + - Python interface used to compute the inverse Fast Fourier Transform of an array. + +Detailed Description +-------------------- +The 'af.ifft()' function performs an inverse discrete Fourier transform of the input array. It is essentially the reverse operation of the Fast Fourier Transform (FFT), converting frequency domain data back into the time domain. + +Function Documentation +---------------------- +.. sidebar:: af.ifft() + + Syntax: + af.ifft(array, dim=None) + + Parameters: + 'array': The ArrayFire array (1D, 2D, or higher-dimensional) to which the inverse FFT is to be applied. + 'dim (optional)'': The dimension along which to compute the inverse FFT. If not specified, the IFFT is computed along all dimensions. + + Returns: + An ArrayFire array of the same shape as the input array containing the result of the inverse FFT. + diff --git a/docs/functions/index.rst b/docs/functions/index.rst new file mode 100644 index 0000000..03a66e4 --- /dev/null +++ b/docs/functions/index.rst @@ -0,0 +1,39 @@ +.. toctree:: + :maxdepth: 2 + :caption: Functions: + + transpose + array + ones + zeros + sum + subtract + sttdev + randu + randn + det + device + divide + dot + eig + fft + get + get_device + inv + join + matmul + max + mean + median + min + multiply + prod + reorder + set_device + slice + sqrt + add + exp + log + ifft + reshape diff --git a/docs/functions/inv.rst b/docs/functions/inv.rst new file mode 100644 index 0000000..37969b1 --- /dev/null +++ b/docs/functions/inv.rst @@ -0,0 +1,30 @@ +inv +=== +The 'af.inv()' function in ArrayFire computes the inverse of a square matrix. Matrix inversion is a fundamental operation in linear algebra with applications in solving systems of linear equations, optimization problems, and various computational tasks. + +Function +-------- +:literal:`af.inv()` + - Python interface used to find inverse of a square matrix. + +Detailed Description +-------------------- +The af.inv() function calculates the inverse of a given square matrix. For a matrix +𝐴, the inverse is denoted as 𝐴−1, and it satisfies the following property: + +A⋅A−1=I +where I is the identity matrix. The matrix A must be square (i.e., the number of rows must be equal to the number of columns) for its inverse to exist. + +Function Documentation +---------------------- +.. sidebar:: af.inv() + + Syntax: + af.inv(matrix) + + Parameters: + 'matrix:'' A 2D ArrayFire array representing the square matrix for which the inverse is to be computed. The matrix must be square. + + Returns: + An ArrayFire array of the same shape as the input matrix, containing the inverse of the input matrix. + diff --git a/docs/functions/join.rst b/docs/functions/join.rst new file mode 100644 index 0000000..18906f7 --- /dev/null +++ b/docs/functions/join.rst @@ -0,0 +1,27 @@ +join +==== +The 'af.join()' function in ArrayFire is used to concatenate multiple arrays along a specified dimension. This operation is useful for combining data from different arrays into a single array, which can be particularly useful in data processing, machine learning, and numerical analysis. + + + +Function +-------- +:literal:`af.join` + - Python interface used to concatenate multiple arrays along a specified dimension. + +Detailed Description +-------------------- +The 'af.join()' function allows you to concatenate multiple arrays along a specified dimension. The arrays being concatenated must have compatible shapes along all dimensions except for the dimension along which concatenation is performed. + +Function Documentation +---------------------- +.. sidebar:: af.join() + + Syntax: + af.join(dim, \*arrays) + + Parameters: + 'dim': The dimension along which the arrays will be concatenated. This is an integer indicating the axis along which the join operation will take place. + '\*arrays': The arrays to be concatenated. These can be 1D, 2D, or higher-dimensional arrays, and they must be compatible in all dimensions except the specified dimension. + Returns: + An ArrayFire array that is the result of concatenating the input arrays along the specified dimension. diff --git a/docs/functions/log.rst b/docs/functions/log.rst new file mode 100644 index 0000000..3477898 --- /dev/null +++ b/docs/functions/log.rst @@ -0,0 +1,26 @@ +log +=== +The 'af.log()' function in ArrayFire computes the natural logarithm (base 𝑒) of each element in the input array. This function is commonly used in mathematical and scientific computing for transformations, normalization, and statistical analysis. + +Function +-------- +:literal:`af.log()` + - Python interface used to compute the natural logarithm of each base element in the input array. + +Detailed Description +-------------------- +The af.log() function applies the natural logarithm operation element-wise to the input array. The natural logarithm is the logarithm to the base +𝑒, where 𝑒 is approximately equal to 2.71828. The function operates on each element of the array independently. + +Function Documentation +---------------------- +.. sidebar:: af.log() + + Syntax: + af.log(array) + + Parameters: + 'array': An ArrayFire array (1D, 2D, or higher-dimensional) containing the values for which the natural logarithm is to be computed. + + Returns: + An ArrayFire array of the same shape as the input array, with each element replaced by its natural logarithm. diff --git a/docs/functions/matmul.rst b/docs/functions/matmul.rst new file mode 100644 index 0000000..e36f41b --- /dev/null +++ b/docs/functions/matmul.rst @@ -0,0 +1,27 @@ +matmul +====== +The 'af.matmul()' function in ArrayFire performs matrix multiplication between two arrays. Matrix multiplication is a fundamental operation in linear algebra and is widely used in various scientific and engineering computations. + +Function +-------- +:literal:`af.matmul()` + - Python interface used to perform matrix multiplication between two arrays. + +Detailed Description +-------------------- +The af.matmul() function computes the matrix product of two input arrays. Matrix multiplication is defined for two matrices 𝐴 and 𝐵 where the number of columns in 𝐴 is equal to the number of rows in 𝐵. The result is a matrix where each element is computed as the dot product of rows of +𝐴 with columns of 𝐵. + +Function Documentation +---------------------- +.. sidebar:: af.matmul() + + Syntax: + af.matmul(A, B) + + Parameters: + 'A': The first input array (matrix) to be multiplied. + 'B': The second input array (matrix) to be multiplied. + + Returns: + An ArrayFire array representing the result of the matrix multiplication. diff --git a/docs/functions/max.rst b/docs/functions/max.rst new file mode 100644 index 0000000..091719a --- /dev/null +++ b/docs/functions/max.rst @@ -0,0 +1,31 @@ +max +=== +The 'af.max()' function in ArrayFire is used to find the maximum value within an array or along a specific dimension. It can also return the indices of the maximum values if requested. + + + +Function +-------- +:literal:`af.max()` + - Python interface used to find the maximum value within an array or specific dimension. + +Detailed Description +-------------------- +The af.max() function performs one of the following operations: + +- Finds the maximum value in the entire array. +- Finds the maximum value along a specified dimension. +- Returns both the maximum values and their indices if requested. + +Function Documentation +---------------------- +.. sidebar:: af.max() + + Syntax: + af.max(array) + + Parameters: + 'array': The ArrayFire array from which to find the maximum values. + + Returns: + An ArrayFire array containing the maximum element from the input data structure. diff --git a/docs/functions/mean.rst b/docs/functions/mean.rst new file mode 100644 index 0000000..a900ee0 --- /dev/null +++ b/docs/functions/mean.rst @@ -0,0 +1,27 @@ +mean +==== +The 'af.mean()' function in ArrayFire computes the mean (average) value of elements in an array or along a specified dimension. It is a fundamental statistical operation used in various data analysis and processing tasks. + +Function +-------- +:literal:`af.mean()` + - Python interface used to compute the average value of elements in an array or specified dimension. + +Detailed Description +-------------------- +The 'af.mean()' function calculates the average value of elements in the input array. The mean is computed by summing all elements and dividing by the number of elements. The function can compute the mean over the entire array or along a specified dimension. + +Function Documentation +---------------------- +.. sidebar:: af.mean() + + Syntax: + af.mean(array) + + + Parameters: + 'array': The ArrayFire array for which the mean is to be computed. + + Returns: + An ArrayFire array containing the mean values. If 'axis' is specified, the result will have the specified dimension reduced. + diff --git a/docs/functions/median.rst b/docs/functions/median.rst new file mode 100644 index 0000000..4ee1417 --- /dev/null +++ b/docs/functions/median.rst @@ -0,0 +1,27 @@ +median +====== +The af.median() function in ArrayFire computes the median value of elements in an array or along a specified dimension. The median is a measure of central tendency that provides a robust statistic by identifying the middle value in a sorted list of numbers. + +Function +-------- +:literal:`af.median()` + - Python interface used to compute the median value of elements in an array or specified dimension. + +Detailed Description +-------------------- +The 'af.median()' function finds the median value of elements in the input array. The median is defined as the middle value in a sorted sequence of numbers. If the number of elements is even, it is the average of the two middle numbers. The function can compute the median over the entire array or along a specified dimension. + +Function Documentation +---------------------- +.. sidebar:: af.median() + + Syntax: + af.median(array) + + + Parameters: + 'array': The ArrayFire array for which the median is to be computed. + + Returns: + An ArrayFire array containing the median values. If 'axis' is specified, the result will have the specified dimension reduced. + diff --git a/docs/functions/min.rst b/docs/functions/min.rst new file mode 100644 index 0000000..6ee6556 --- /dev/null +++ b/docs/functions/min.rst @@ -0,0 +1,25 @@ +min +=== +The af.min() function in ArrayFire is used to compute the minimum value within an array or along a specific dimension. This operation helps in identifying the smallest value in a dataset or reducing multi-dimensional arrays to a lower dimension based on the minimum values. + +Function +-------- +:literal:`af.min()` + - Python interface used to compute the minimum value within an array or specified dimension. + +Detailed Description +-------------------- +The 'af.min()' function finds the minimum value of elements in the input array. The minimum value is the smallest element in the array. The function can compute the minimum over the entire array or along a specified dimension. + +Function Documentation +---------------------- +.. sidebar:: af.min() + + Syntax: + af.min(array) + + Parameters: + 'array': The ArrayFire array from which to find the minimum values. + + Returns: + An ArrayFire array containing the minimum values from the input data structure. diff --git a/docs/functions/multiply.rst b/docs/functions/multiply.rst new file mode 100644 index 0000000..623b01c --- /dev/null +++ b/docs/functions/multiply.rst @@ -0,0 +1,32 @@ +multiply +======== +The 'af.multiply()' function in ArrayFire performs element-wise multiplication between two arrays. This means each element of the first array is multiplied by the corresponding element of the second array. It supports both scalar and array inputs for flexible use. + +Function +-------- +:literal:`af.multiply()` + - Python interface used to perform element-wise multiplication between two arrays. + +Detailed Description +-------------------- +The 'af.multiply()' function computes the product of two input arrays element-wise. If the inputs are arrays, they must have the same dimensions or be broadcastable to compatible dimensions. If one of the inputs is a scalar, it multiplies each element of the other array by that scalar. + +Function Documentation +---------------------- +.. sidebar:: af.multiply() + + Syntax: + af.multiply(array1, array2) + af.multiply(array, scalar) + af.multiply(scalar, array) + + + Parameters: + 'array1': The first input array. + 'array2': The second input array. Must have the same dimensions as array1 or be broadcastable to the dimensions of array1. + 'array': The input array if using scalar multiplication. + 'scalar': The scalar value to multiply with the array. + + Returns: + An ArrayFire array containing the result of the element-wise multiplication. + diff --git a/docs/functions/ones.rst b/docs/functions/ones.rst new file mode 100644 index 0000000..fbf78af --- /dev/null +++ b/docs/functions/ones.rst @@ -0,0 +1,26 @@ +ones +==== +The 'af.ones()' function in the ArrayFire library is used to create arrays where every element is initialized to one. This function is useful for various numerical and scientific computing tasks where you need an array filled with ones, such as initializing weights in machine learning algorithms or setting up identity matrices. + +Function +-------- +:literal:`af.ones()` + - Pyhton interface to create an array filled with ones. + +Detailed Description +-------------------- +The 'af.ones()' function creates an ArrayFire array in which all the elements are set to one. This function is versatile and can create arrays of different shapes and data types. By default, the function creates arrays with single precision floating-point numbers (float), but you can specify different data types as needed. + +The dimensions of the array are specified as arguments to the function, allowing you to create both one-dimensional and multi-dimensional arrays. + +Function Documentation +---------------------- +.. sidebar:: af.ones() + + Syntax: + af.ones(dim0, dim1) + + Parameters: + 'dim0, dim1': Integers representing the dimensions of the array. You can provide multiple dimensions to create multi-dimensional arrays. For instance, 'dim0' represents the number of rows, and 'dim1' represents the number of columns for a 2D array. + Returns: + An ArrayFire array with the specified dimensions and data type, where all elements are initialized to one. diff --git a/docs/functions/prod.rst b/docs/functions/prod.rst new file mode 100644 index 0000000..6922383 --- /dev/null +++ b/docs/functions/prod.rst @@ -0,0 +1,27 @@ +prod +==== +The 'af.prod()' function in ArrayFire computes the product of elements in an array or along a specified dimension. It aggregates elements by multiplying them together, which is useful in various mathematical and data processing applications. + +Function +-------- +:literal:`af.prod()` + - Python interface used to compute the product of elements in an array or along a specified dimension + +Detailed Description +-------------------- +The 'af.prod()' function calculates the product of all elements in the input array or along a specified dimension. If no dimension is specified, it computes the product of all elements in the array. This function can also handle multi-dimensional arrays, where you can specify the dimension along which to compute the product. + +Function Documentation +---------------------- +.. sidebar:: af.prod() + + Syntax: + af.prod(array) + + + Parameters: + 'array': The ArrayFire array for which the product is to be computed. + + Returns: + An ArrayFire array containing the product of elements. If axis is specified, the result will have the specified dimension reduced. + diff --git a/docs/functions/randn.rst b/docs/functions/randn.rst new file mode 100644 index 0000000..43ff957 --- /dev/null +++ b/docs/functions/randn.rst @@ -0,0 +1,26 @@ +randn +===== +The 'af.randn()' function in the ArrayFire library is used to generate arrays filled with random numbers drawn from a normal (Gaussian) distribution with mean 0 and standard deviation 1. This is useful in statistical simulations, machine learning, and other applications where normally distributed random values are required. + +Function +-------- +:literal:`af.python()` + - Python interface to form an array filled with numbers. + +Detailed Description +-------------------- +The 'af.randn()' function creates an ArrayFire array where each element is a random number sampled from a standard normal distribution, also known as a Gaussian distribution. The distribution has a mean of 0 and a standard deviation of 1. This function allows you to specify the dimensions of the array and optionally the data type. By default, the function generates numbers in single precision floating-point format (float), but other data types can be specified if needed. + +Function Documentation +---------------------- +.. sidebar:: af.randn() + + Syntax: + af.randn(dim0, dim1) + + + Parameters: + 'dim0, dim1': Integers representing the dimensions of the array. You can specify multiple dimensions to create multi-dimensional arrays. For example, dim0 represents the number of rows, and dim1 represents the number of columns for a 2D array. + + Returns: + An ArrayFire array with the specified dimensions and data type, where all elements are initialized with random values sampled from a normal distribution with mean 0 and standard deviation 1. diff --git a/docs/functions/randu.rst b/docs/functions/randu.rst new file mode 100644 index 0000000..16de0df --- /dev/null +++ b/docs/functions/randu.rst @@ -0,0 +1,29 @@ +randu +===== +The af.randu() function in ArrayFire is used to generate arrays with uniformly distributed random numbers. The numbers are drawn from a uniform distribution in the interval +[0,1). This function is useful for initializing random data for simulations, stochastic processes, or for testing algorithms that require random inputs. + +Function +-------- +:literal:`af.randu()` + - Python interface for making a rray with random units. + +Detailed Description +-------------------- +The 'af.randu()' function creates an ArrayFire array filled with random numbers that follow a uniform distribution between 0 and 1 (excluding 1). This function allows you to specify the dimensions of the array and optionally the data type. By default, the generated numbers are single-precision floating-point values (float), but other data types can be specified if needed. + +Random number generation is a fundamental aspect of many computational tasks, and 'af.randu()' provides an efficient way to create random datasets with uniform distribution. + +Function Documentation +---------------------- +.. sidebar:: af.randu() + + Syntax: + af.randu(dim0, dim1) + + Parameters: + 'dim0, dim1': Integers representing the dimensions of the array. You can provide multiple dimensions to create multi-dimensional arrays. For example, dim0 represents the number of rows, and dim1 represents the number of columns for a 2D array. + + Returns: + An ArrayFire array with the specified dimensions and data type, where all elements are initialized with random values uniformly distributed between 0 and 1. + diff --git a/docs/functions/reorder.rst b/docs/functions/reorder.rst new file mode 100644 index 0000000..d51f9a8 --- /dev/null +++ b/docs/functions/reorder.rst @@ -0,0 +1,27 @@ +reorder +======= +The 'af.reorder()' function in ArrayFire is used to rearrange the dimensions of an array. This is useful for changing the order of dimensions to align with the requirements of different operations or to facilitate certain types of computations. + +Function +-------- +:literal:`af.reorder()` + - Python interface used to rearrange the dimensions of an array. + +Detailed Description +-------------------- +The 'af.reorder()' function changes the order of dimensions of the input array. It is often used in conjunction with other functions that require specific data layouts. Reordering dimensions can help optimize performance or compatibility with other libraries and tools. + +Function Documentation +---------------------- +.. sidebar:: af.reorder() + + Syntax: + af.reorder(array, \*order) + + Parameters: + 'array': The ArrayFire array whose dimensions are to be reordered. + '\*order': The new order of dimensions specified as a sequence of integers. Each integer represents the index of the dimension in the new order. + + Returns: + An ArrayFire array with the dimensions reordered according to the specified order. + diff --git a/docs/functions/reshape.rst b/docs/functions/reshape.rst new file mode 100644 index 0000000..8f9ec78 --- /dev/null +++ b/docs/functions/reshape.rst @@ -0,0 +1,25 @@ +reshape +======= +The 'af.reshape()' function in ArrayFire is used to change the shape of an array. This function allows you to rearrange the dimensions of an array while keeping the underlying data intact. It is particularly useful for preparing data for operations that require specific shapes or for transforming data between different formats. + +Function +-------- +:literal:`af.reshape()` + - Python interface used to change the shape of an array. + +Detailed Description +-------------------- +The 'af.reshape()' function changes the shape of an existing array to a new shape specified by the user. The total number of elements in the original array must match the total number of elements in the reshaped array. The function does not alter the data but reinterprets it under a new shape. + +Function Documentation +---------------------- +.. sidebar:: af.reshape() + + Syntax: + af.reshape(array,\*new_shape) + + Parameters: + 'array': The ArrayFire array to be reshaped. + '\*new_shape': The new shape for the array, specified as a sequence of integers. The product of these dimensions must equal the total number of elements in the original array. + Returns: + An ArrayFire array with the specified new shape. \ No newline at end of file diff --git a/docs/functions/set_device.rst b/docs/functions/set_device.rst new file mode 100644 index 0000000..10007ae --- /dev/null +++ b/docs/functions/set_device.rst @@ -0,0 +1,25 @@ +set_device +========== +The 'af.set_device()' function in ArrayFire is used to specify which device (GPU or CPU) will be used for subsequent ArrayFire operations. This is particularly useful when working with multiple devices or GPUs to ensure that computations are directed to the desired hardware. + +Function +-------- +:literal:`af.set_device()` + - Python interface used to specify which device will be used for subsequent arrayfire operations. + +Detailed Description +-------------------- +The 'af.set_device()' function sets the device ID for ArrayFire operations. By default, ArrayFire uses the first available device (usually device ID 0). When you have multiple GPUs or devices, you can use this function to select which device ArrayFire should use for subsequent operations. + +Function Documentation +---------------------- +.. sidebar:: af.set_device() + + Syntax: + af.set_device(device_id) + + Parameters: + 'device_id': An integer representing the ID of the device to be set. This ID corresponds to the device index in the ArrayFire device list. + + Returns: + None diff --git a/docs/functions/slice.rst b/docs/functions/slice.rst new file mode 100644 index 0000000..3832e45 --- /dev/null +++ b/docs/functions/slice.rst @@ -0,0 +1,27 @@ +slice +===== +The 'af.slice()' function in ArrayFire is used to extract a subarray from a larger array by specifying the start and end indices along each dimension. This function is useful for accessing a portion of an array, which is essential for tasks such as data manipulation, cropping, and partitioning. + +Function +-------- +:literal:`af.slice()` + - Python interface used to extract a subarray from a larger array by specifying the start and end indices along each dimension. + +Detailed Description +-------------------- +The 'af.slice()' function allows you to extract a contiguous subarray from the original array. You specify the starting and ending indices for each dimension to define the subarray. This function provides a way to access specific regions of multi-dimensional arrays efficiently. + +Function Documentation +---------------------- +.. sidebar:: af.slice() + + Syntax: + af.slice(array, \*start_indices, \*end_indices) + + Parameters: + 'array': The ArrayFire array from which the subarray will be extracted. + '\*start_indices': A sequence of integers specifying the starting indices for each dimension. + '\*end_indices': A sequence of integers specifying the ending indices for each dimension. + + Returns: + An ArrayFire array containing the extracted subarray. diff --git a/docs/functions/sqrt.rst b/docs/functions/sqrt.rst new file mode 100644 index 0000000..4fb397f --- /dev/null +++ b/docs/functions/sqrt.rst @@ -0,0 +1,25 @@ +sqrt +==== +The 'af.sqrt()' function in ArrayFire is used to compute the square root of each element in an array. This is a common mathematical operation used in various computational tasks, including normalization, scaling, and feature extraction. + +Function +-------- +:literal:`af.sqrt()` + - Python interface used to compute the square root of each element in an array. + +Detailed Description +-------------------- +The 'af.sqrt()' function calculates the square root of every element in the input array. The result is an array of the same shape, where each element is the square root of the corresponding element in the input array. + +Function Documentation +---------------------- +.. sidebar:: af.sqrt() + + Syntax: + af.sqrt(array) + + Parameters: + 'array': The ArrayFire array for which the square root will be computed. + + Returns: + An ArrayFire array where each element is the square root of the corresponding element in the input array. diff --git a/docs/functions/sttdev.rst b/docs/functions/sttdev.rst new file mode 100644 index 0000000..11fb80e --- /dev/null +++ b/docs/functions/sttdev.rst @@ -0,0 +1,26 @@ +stdev +===== +The 'af.stdev()' function in ArrayFire computes the standard deviation of elements in an array. The standard deviation is a measure of the amount of variation or dispersion in a set of values. This function can compute the standard deviation across all elements or along a specified dimension of the array. + +Function +-------- +:literal:`af.stdev()` + - Python interface used to compute the standard deviation of elements in an array. + +Detailed Description +-------------------- +The 'af.stdev()' function calculates the standard deviation of the values in an array. It can operate over all elements of the array or along a specified dimension. This function helps in understanding the spread or variability of data points. + +Function Documentation +---------------------- +.. sidebar:: af.stdev() + + Syntax: + af.stdev(array) + + + Parameters: + 'array': The ArrayFire array for which the standard deviation will be computed. + + Returns: + An ArrayFire array containing the computed standard deviation. diff --git a/docs/functions/subtract.rst b/docs/functions/subtract.rst new file mode 100644 index 0000000..b92b0ea --- /dev/null +++ b/docs/functions/subtract.rst @@ -0,0 +1,29 @@ +subtract +======== +The 'af.subtract()' function in ArrayFire is used to perform element-wise subtraction between two arrays or between an array and a scalar. This operation is fundamental in many computational tasks, including data manipulation, feature scaling, and mathematical operations. + +Function +-------- +:literal:`af.subtract()` + - Python interface used to perform element-wise subtraction between two arrays or between an array and a scalar. + +Detailed Description +-------------------- +The 'af.subtract()' function subtracts the elements of one array from the corresponding elements of another array, or subtracts a scalar value from each element of an array. The function performs this operation element-wise, meaning that each element of the first operand is subtracted by the corresponding element of the second operand. + +Function Documentation +---------------------- +.. sidebar:: af.subtract() + + Syntax: + af.subtract(array1, array2) + af.subtract(array, scalar) + + Parameters: + 'array1': The ArrayFire array from which elements will be subtracted. + 'array2': The ArrayFire array to subtract from array1. This should have the same shape as array1 or be broadcast-compatible. + 'scalar': A scalar value to subtract from each element of array. + + Returns: + An ArrayFire array containing the result of the subtraction. + diff --git a/docs/functions/sum.rst b/docs/functions/sum.rst new file mode 100644 index 0000000..13407e5 --- /dev/null +++ b/docs/functions/sum.rst @@ -0,0 +1,26 @@ +sum +=== +The 'af.sum()' function in ArrayFire computes the sum of the elements in an array. This function can perform the summation over all elements or along a specified dimension. It is useful for aggregating data, calculating totals, and summarizing results in numerical computations. + +Function +-------- +:literal:`af.sum()` + - Python interface used to compute the sum of the elements in an array. + +Detailed Description +-------------------- +The 'af.sum()' function calculates the sum of array elements. You can choose to compute the sum across all elements of the array or along a specific dimension. This function supports both reducing the array to a single value and computing sums along specified axes. + +Function Documentation +---------------------- +.. sidebar:: af.sum() + + Syntax: + af.sum(array) + + Parameters: + 'array': The ArrayFire array for which the sum will be computed. + 'dim': An optional integer specifying the dimension along which to compute the sum. By default, it is set to 0, which means the function will sum along the first dimension. + + Returns: + An ArrayFire array containing the sum of the elements. diff --git a/docs/functions/transpose.rst b/docs/functions/transpose.rst new file mode 100644 index 0000000..5f41466 --- /dev/null +++ b/docs/functions/transpose.rst @@ -0,0 +1,27 @@ +transpose +========= +The 'af.transpose()' function in ArrayFire is used to compute the transpose of a matrix or higher-dimensional array. Transposing a matrix involves swapping its rows and columns. This function is fundamental in linear algebra operations and many computational tasks. + +Function +-------- +:literal:`af.transpose()` + - Python interface used to compute the transpose of a matrix or higher-dimensional array. + +Detailed Description +-------------------- +The 'af.transpose()' function computes the transpose of a given array. For 2D matrices, this means converting rows into columns and columns into rows. For higher-dimensional arrays, the function permutes dimensions according to the specified order. + +Function Documentation +---------------------- +.. sidebar:: af.transpose() + + Syntax: + af.transpose(array, \*permutation) + + Parameters: + 'array': The ArrayFire array to be transposed. + '\*permutation': An optional sequence of integers specifying the new order of dimensions. If not provided, the function defaults to transposing the last two dimensions. + + Returns: + An ArrayFire array that is the transpose of the input array, with dimensions permuted according to the provided permutation. + diff --git a/docs/functions/zeros.rst b/docs/functions/zeros.rst new file mode 100644 index 0000000..90ead5f --- /dev/null +++ b/docs/functions/zeros.rst @@ -0,0 +1,28 @@ +zeros +===== +The 'af.zeros()' function in the ArrayFire library is used to create arrays filled with zeros. This is a common operation when initializing arrays that will be updated or manipulated later in numerical computations. The function allows you to specify the dimensions and data type of the array, making it flexible for various use cases. + +Function +-------- +:literal:`af.zeros()` + - Python interface to create array filled with zeros. + +Detailed Description +-------------------- +The 'af.zeros()' function creates an ArrayFire array where every element is initialized to zero. It is particularly useful when you need to allocate space for an array but want to ensure that all values start from zero. This is often used in numerical methods, data processing, and initialization of variables in scientific computing. + +You can specify the dimensions of the array as well as its data type. By default, the function creates arrays with a single precision floating-point type (float), but you can specify other data types if needed. + +Function Documentation +---------------------- +.. sidebar:: af.zero() + + Syntax: + af.zeros(dim0, dim1) + + Parameters: + 'dim0, dim1': Integers representing the dimensions of the array. You can specify multiple dimensions to create multi-dimensional arrays. For example, dim0 is the number of rows, and dim1 is the number of columns for a 2D array. + Returns: + An ArrayFire array with the specified dimensions and data type, where all elements are initialized to zero. + + diff --git a/docs/gettingstarted.py b/docs/gettingstarted.py new file mode 100644 index 0000000..7d6a092 --- /dev/null +++ b/docs/gettingstarted.py @@ -0,0 +1,275 @@ + +# [gettingstarted1-snippet] +# Arrays may be created using the array constructor and dimensioned +# as 1D, 2D, 3D; however, the values in these arrays will be undefined +import arrayfire as af + +array = af.constant(0,(100,)) +array_2d = af.constant(0, (10, 100)) +array_3d = af.constant(0, (10, 10, 10)) +# [gettingstarted1-endsnippet] + + + +# [gettingstarted2-snippet] +import arrayfire as af + +# Generate an array of size three filled with zeros. +# If no data type is specified, ArrayFire defaults to f32. +# The constant function generates the data on the device. +zeroes = af.constant(0,(3,)) + +# Generate a 1x4 array of uniformly distributed [0,1] random numbers +# The randu function generates the data on the device. +rand1 = af.randu((1,4)) + +# Generate a 2x2 array (or matrix, if you prefer) of random numbers +# sampled from a normal distribution. +# The randn function generates data on the device. +rand2 = af.randu((2,2)) + +# Generate a 3x3 identity matrix. The data is generated on the device. +iden = af.identity((3,3)) + +# Lastly, create a 2x1 array (column vector) of uniformly distributed +# 32-bit complex numbers (c32 data type): +randcplx = af.randu((2,1)) +# [gettingstarted2-endsnippet] + + +# [gettingstarted3-snippet] +import arrayfire as af +# Create a six-element array on the host +hA = ([0, 1, 2, 3, 4, 5]) + +# Which can be copied into an ArrayFire Array using the pointer copy +# constructor. Here we copy the data into a 2x3 matrix: +A = af.moddims(af.Array(hA),(2,3)) + + +# ArrayFire provides a convenince function for printing array +# objects in case you wish to see how the data is stored: +print(A) + +#todo how to create complex numbers +# [gettingstarted3-endsnippet] + + + +# [gettingstarted4-snippet] + +import arrayfire as af +import pycuda.driver as cuda +import numpy as np + +# Create an array on the host +host_ptr = af.Array([0, 1, 2, 3, 4, 5]) + + +# Create an ArrayFire array 'a' from host_ptr (2x3 matrix) +A = af.moddims(host_ptr,(2,3)) + +# Allocate CUDA device memory and copy data from host to device +device_ptr = cuda.mem_alloc(host_ptr.nbytes) +cuda.memcpy_htod(device_ptr, host_ptr) + +# Create an ArrayFire array 'b' from CUDA-allocated device memory (2x3 matrix) +b = af.Array(device_ptr, dims=(2, 3), is_device=True) + +# Note: ArrayFire takes ownership of `device_ptr`, so no need to free it manually + +# Clean up CUDA resources (not necessary due to Python's automatic memory management) +# cuda.mem_free(device_ptr) +# [gettingstarted4-endsnippet] + + + +# [gettingstarted5-snippet] + +import arrayfire as af + +# Generate two arrays +a= af.randu((2,2)) # Create a 2x2 array with random numbers between [0, 1] +b = af.constant(1,(2,1)) # Create a 2x1 array filled with constant value 1 + +# Print arrays 'a' and 'b' to the console +print("Array 'a':", a) + +print("Array 'b':",b) + +# Print the results of an expression involving arrays +result = a.col(0) + b + 0.4 # Perform operation: first column of 'a' + 'b' + 0.4 +print("Result of expression (a.col(0) + b + 0.4):") +print(result) +# [gettingstarted5-endsnippet] + + +# [gettingstarted6-snippet] + +import arrayfire as af + +# Create a 4x5x2 array of uniformly distributed random numbers +a = af.randu((4,5,2)) + +# Determine the number of dimensions using the `numdims()` function +print("numdims(a):", a.numdims()) # Print the number of dimensions (should be 3) + +# Print the size of the individual dimensions using the `dims()` function +print("dims =", a.dims()) # Print dimensions as a tuple (4, 5, 2) + +# Alternatively, access dimensions using a dim4 object +dims = a.dims() +print("dims =", dims[0], dims[1]) # Print dimensions separately (4, 5) +# [gettingstarted6-endsnippet] + + +# [gettingstarted7-snippet] + +import arrayfire as af + +# Create an example ArrayFire array 'a' +a = af.randu((4, 5)) # Example array of dtype float32 + +# Get the type stored in the array +print("underlying type:", a.type()) + +# Check if the array contains complex or real values +print("is complex?", a.iscomplex(), " is real?", a.isreal()) + +# Check if the array is a vector, column vector, or row vector +print("is vector?", a.isvector(), " column?", a.iscolumn(), " row?", a.isrow()) + +# Check if the array is empty, and determine its total elements and memory usage +print("empty?", a.isempty(), " total elements:", a.elements(), " bytes:", a.bytes()) +# [gettingstarted7-endsnippet] + + +# [gettingstarted8-snippet] + +import arrayfire as af + +# Generate a 3x3 array of uniformly distributed random numbers +R = af.randu((3, 3)) +print(af.constant(1,( 3, 3)) + af.join(af.sin(R))) # will be c32 + +# Rescale complex values to unit circle +a = af.randn(5) +print(a / af.abs(a)) + +# Calculate L2 norm of vectors +X = af.randn((3, 4)) +print(af.sqrt(af.sum(af.pow(X, 2)))) # norm of every column vector +print(af.sqrt(af.sum(af.pow(X, 2), 0))) # same as above +print(af.sqrt(af.sum(af.pow(X, 2), 1))) # norm of every row vector + +# [gettingstarted8-endsnippet] + + +# [gettingstarted9-snippet] + +import arrayfire as af +import math + +# Generate a 5x5 array of uniformly distributed random numbers +A = af.randu((5, 5)) + +# Set elements in A greater than 0.5 to NaN +A[af.where(A > 0.5)] = af.NaN + +# Generate arrays x and y with 10 million random numbers each +x = af.randu(int(10e6)) +y = af.randu(int(10e6)) + +# Estimate Pi using Monte Carlo method +pi_est = 4 * af.sum(af.hypot(x, y) < 1) / 10e6 + +# Print the estimation error compared to math.pi +print("estimation error:", abs(math.pi - pi_est)) +# [gettingstarted9-endsnippet] + + + +# [gettingstarted10-snippet] + +import arrayfire as af + +# Create an array consisting of 3 random numbers of type f32 (float) +a = af.randu(3) + +# Copy array data from device to host +host_a = a.host_ptr() # Get host pointer +print("host_a[2] =", host_a[2]) # Access host data as a normal array +a.unlock() # Unlock array to allow garbage collection if necessary + +# Access device memory for CUDA kernel +d_cuda = a.device_ptr() # Get device pointer (no need to free) +value = af.sum(d_cuda[2]) # Access device memory data +print("d_cuda[2] =", value) + +# For OpenCL, accessing memory is similar but with a different syntax +# Note: ArrayFire handles these details internally, no explicit OpenCL handling in Python + +# No need to free pointers in ArrayFire Python interface as memory management is automatic +# [gettingstarted10-endsnippet] + + +# [gettingstarted11-snippet] + +import arrayfire as af + +# Create an array consisting of 3 random numbers +a = af.randu(3) + +# Get the scalar value of the array +val = a.scalar() + +# Print the scalar value +print(f"scalar value: {val}") +# [gettingstarted11-endsnippet] + + +# [gettingstarted12-snippet] + +import arrayfire as af + +# Define host arrays +h_A = ([1, 1, 0, 0, 4, 0, 0, 2, 0]) +h_B = ([1, 0, 1, 0, 1, 0, 1, 1, 1]) + +# Create ArrayFire arrays A and B from host arrays +A = af.Array(h_A, dims=(3, 3)) +B = af.Array(h_B, dims=(3, 3)) + +# Print arrays A and B +print(A) +print(B) + +# Perform bitwise operations +A_and_B = A & B +A_or_B = A | B +A_xor_B = A ^ B + +# Print results of bitwise operations +print(A_and_B) +print(A_or_B) +print(A_xor_B) +# [gettingstarted12-endsnippet] + + + +# [gettingstarted13-snippet] + +import arrayfire as af + +def main(): + # Generate random values + a = af.randu(10000, dtype=af.Dtype.f32) + + # Sum all the values + result = af.sum(a) + print(f"sum: {result}\n") + + +# [gettingstarted13-endsnippet] + + diff --git a/docs/gettingstarted.rst b/docs/gettingstarted.rst new file mode 100644 index 0000000..69a2f70 --- /dev/null +++ b/docs/gettingstarted.rst @@ -0,0 +1,210 @@ +Getting Started +======================== + +Introduction +============ + +ArrayFire is a high performance software library for parallel computing with an easy-to-use API. ArrayFire abstracts away much of the details of programming parallel architectures by providing a high-level container object, the array, that represents data stored on a CPU, GPU, FPGA, or other type of accelerator. This abstraction permits developers to write massively parallel applications in a high-level language where they need not be concerned about low-level optimizations that are frequently required to achieve high throughput on most parallel architectures. + +Supported data types +==================== + +ArrayFire provides one generic container object, the array on which functions and mathematical operations are performed. The :literal:`array` can represent one of many different basic data types: + +* f32 real single-precision (:literal:`float`) +* c32 complex single-precision (:literal:`cfloat`) +* f64 real double-precision (:literal:`double`) +* c64 complex double-precision (:literal:`cdouble`) +* f16 real half-precision (:literal:`half_float::half`) +* b8 8-bit boolean values (:literal:`bool`) +* s32 32-bit signed integer (:literal:`int`) +* u32 32-bit unsigned integer (:literal:`unsigned`) +* u8 8-bit unsigned values (:literal:`unsigned char`) +* s64 64-bit signed integer (:literal:`intl`) +* u64 64-bit unsigned integer (:literal:`uintl`) +* s16 16-bit signed integer (:literal:`short`) +* u16 16-bit unsigned integer (:literal:`unsigned short`) + +Most of these data types are supported on all modern GPUs; however, some older devices may lack support for double precision arrays. In this case, a runtime error will be generated when the array is constructed. + +If not specified otherwise, :literal:`array`s are created as single precision floating point numbers (:literal:`f32`). + +Creating and populating an ArrayFire array +========================================== + +ArrayFire arrays represent memory stored on the device. As such, creation and population of an array will consume memory on the device which cannot freed until the :literal:`array` object goes out of scope. As device memory allocation can be expensive, ArrayFire also includes a memory manager which will re-use device memory whenever possible. + +Arrays can be created using one of the array constructors. Below we show how to create 1D, 2D, and 3D arrays with uninitialized values: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted1-snippet] + :end-before: [gettingstarted1-endsnippet] + + + +However, uninitialized memory is likely not useful in your application. ArrayFire provides several convenient functions for creating arrays that contain pre-populated values including constants, uniform random numbers, uniform normally distributed numbers, and the identity matrix: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted2-snippet] + :end-before: [gettingstarted2-endsnippet] + + +A complete list of ArrayFire functions that automatically generate data on the device may be found on the functions to create arrays page. As stated above, the default data type for arrays is f32 (a 32-bit floating point number) unless specified otherwise. + +ArrayFire arrays may also be populated from data found on the host. For example: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted3-snippet] + :end-before: [gettingstarted3-endsnippet] + +ArrayFire also supports array initialization from memory already on the GPU. For example, with CUDA one can populate an :literal:`array` directly using a call to :literal:`cudaMemcpy`: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted4-snippet] + :end-before: [gettingstarted4-endsnippet] + +Similar functionality exists for OpenCL too. If you wish to intermingle ArrayFire with CUDA or OpenCL code, we suggest you consult the CUDA interoperability or OpenCL interoperability pages for detailed instructions. + +ArrayFire array contents, dimensions, and properties +==================================================== + +ArrayFire provides several functions to determine various aspects of arrays. This includes functions to print the contents, query the dimensions, and determine various other aspects of arrays. + +The print function can be used to print arrays that have already been generated or any expression involving arrays: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted5-snippet] + :end-before: [gettingstarted5-endsnippet] + +The dimensions of an array may be determined using either a dim4 object or by accessing the dimensions directly using the dims() and numdims() functions: + + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted6-snippet] + :end-before: [gettingstarted6-endsnippet] + + + +In addition to dimensions, arrays also carry several properties including methods to determine the underlying type and size (in bytes). You can even determine whether the array is empty, real/complex, a row/column, or a scalar or a vector: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted7-snippet] + :end-before: [gettingstarted7-endsnippet] + +For further information on these capabilities, we suggest you consult the full documentation on the array. + + +Writing mathematical expressions in ArrayFire +============================================= + +ArrayFire leverages an advanced Just-In-Time (JIT) compilation engine that optimizes array operations by minimizing the number of CUDA/OpenCL kernels used. In Python, ArrayFire functions operate similarly to a vector library. This means that typical element-wise operations, such as :literal:`c[i] = a[i] + b[i]` in C, can be expressed more succinctly as :literal:`c = a + b`, eliminating the need for explicit indexing. + +When multiple array operations are involved, ArrayFire's JIT engine consolidates them through "kernel fusion". This technique not only reduces the frequency of kernel invocations but also optimizes memory usage by eliminating redundant global memory operations. The JIT functionality extends seamlessly across Python function boundaries, continuing until a non-JIT function is encountered or a synchronization operation is explicitly invoked in the code. + +ArrayFire provides a broad spectrum of functions tailored for element-wise operations. It supports standard arithmetic operators (+, -, *, /) as well as a variety of transcendental functions (sin, cos, log, sqrt, etc.). These capabilities empower users to perform complex computations efficiently and effectively. + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted8-snippet] + :end-before: [gettingstarted8-endsnippet] + +To see the complete list of functions please consult the documentation on mathematical, linear algebra, signal processing, and statistics. + +Mathematical constants +====================== + +In Python, ArrayFire provides several platform-independent constants such as Pi, NaN, and Inf. If ArrayFire lacks a specific constant you require, you can create it using the `af.constant` array constructor. + +These constants are universally applicable across all ArrayFire functions. Below, we illustrate their usage in element selection and a mathematical expression: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted9-snippet] + :end-before: [gettingstarted9-endsnippet] + +Please note that our constants may, at times, conflict with macro definitions in standard header files. When this occurs, please refer to our constants using the :literal:`af::` namespace. + +Indexing +======== +Like all functions in ArrayFire, indexing is also executed in parallel on the OpenCL/CUDA devices. Because of this, indexing becomes part of a JIT operation and is accomplished using parentheses instead of square brackets (i.e. as :literal:`A(0)` instead of :literal:`A[0]`). To index :literal:`af::` arrays you may use one or a combination of the following functions: + +* integer scalars +* seq() representing a linear sequence +* end representing the last element of a dimension +* span representing the entire description +* row(i) or col(i) specifying a single row/column +* rows(first,last) or cols(first,last) specifying a span of rows or columns + +Please see the indexing page for several examples of how to use these functions. + +Getting access to ArrayFire array memory on the host and device +=============================================================== + +Memory in :literal:`af::arrays` may be accessed using the host() and device() functions. The :literal:`host` function copies the data from the device and makes it available in a C-style array on the host. As such, it is up to the developer to manage any memory returned by :literal:`host`. The :literal:`device` function returns a pointer/reference to device memory for interoperability with external CUDA/OpenCL kernels. As this memory belongs to ArrayFire, the programmer should not attempt to free/deallocate the pointer. For example, here is how we can interact with both OpenCL and CUDA: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted10-snippet] + :end-before: [gettingstarted10-endsnippet] + + +ArrayFire also provides several helper functions for creating :literal:`af::arrays` from OpenCL :literal:`cl_mem` references and :literal:`cl::Buffer` objects. See the :literal:`include/af/opencl.h` file for further information. + +Lastly, if you want only the first value from an :literal:`af::array` you can use get it using the scalar() function: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted11-snippet] + :end-before: [gettingstarted11-endsnippet] + + +Bitwise operators +================= +In addition to supporting standard mathematical functions, arrays that contain integer data types also support bitwise operators including and, or, and shift: + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted12-snippet] + :end-before: [gettingstarted12-endsnippet] + + +Using the ArrayFire API in Python +================================= + +The ArrayFire library in Python is seamlessly integrated into the Python environment. To start using the library, simply :literal:`import arrayfire` and begin coding! + +# todo @stf talk about arrayapi implementation + +Sample using Python API +~~~~~~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: gettingstarted.py + :language: python + :start-after: [gettingstarted13-snippet] + :end-before: [gettingstarted13-endsnippet] + + + +What to read next? +================== + +Now that you have a general introduction to ArrayFire, where do you go from here? In particular you might find these documents useful + +* Building an ArrayFire program on Linux +* Building an ArrayFire program on Windows +* Timing ArrayFire code + +Where to go for help? +===================== + +* Google Groups: https://groups.google.com/forum/#!forum/arrayfire-users +* ArrayFire Services: `Consulting `_ | `Support `_ | `Training `_ +* ArrayFire Blogs: http://arrayfire.com/blog/ +* Email: technical@arrayfire.com \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..ff4004b --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,41 @@ +.. TestProject2 documentation master file, created by + sphinx-quickstart on Mon Jun 24 10:53:47 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to ArrayFire's documentation! +===================================== + +.. toctree:: + :maxdepth: 4 + :caption: Contents: + + overview + tutorial + functions + releasenotes + examples + arrayandmatrixmanipulation + arrayfirejitcodegeneration + configuringarrayfireenvironment + debuggingarrayfirecode + gettingstarted + indexing + installation + introductiontovectorization + linux + functions/index + + +Indices and tables +================== + * :ref:`genindex` + * :ref:`modindex` + * :ref:`search` + + + + + + + diff --git a/docs/indexing.py b/docs/indexing.py new file mode 100644 index 0000000..e5d2364 --- /dev/null +++ b/docs/indexing.py @@ -0,0 +1,129 @@ +# [indexing1-snippet] + +import arrayfire as af + +data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] + +A = af.Array(data) +A = af.moddims(A,(4,4)) + +# [indexing1-endsnippet] + + +# [indexing2-snippet] + +A[0,0] #Returns an array pointing to the first element + + +A[2,3] #WARN: avoid doing this. Demo only +# [indexing2-endsnippet] + + +# [indexing3-snippet] + +ref0 = A[2,-1] # 14 second row last column +ref1 = A[2,-2] # 10 Second row, second to last(third) column +# [indexing3-endsnippet] + + +# [indexing4-snippet] + +#Returns an array pointing to the third column +A[:,2] +# [indexing4-endsnippet] + + + +# [indexing5-snippet] + +#Returns an array pointing to the second row +A[1, :] +# [indexing5-endsnippet] + + + +# [indexing6-snippet] + + #Returns an array pointing to the first two columns +A[:, 0:2] + +# [indexing6-endsnippet] + + +# [indexing7-snippet] + +reference = A[:, 1] +reference2 = A[0:3, 1] +reference3 = A[0:2, :] +# [indexing7-endsnippet] + + +# [indexing8-snippet] + +copy = A[2, :] +copy2 = A[1:3:2, :] + +hidx = [0, 1, 2] +idx = af.Array(hidx) +copy3 = A[idx, :] + +# [indexing8-endsnippet] + + +# [indexing9-snippet] + +inputA = af.constant(3,(10,10)) +inputB = af.constant(2,(10,10)) +data = af.constant(1,(10,10)) + +#Points to the second column of data. Does not allocate memory +ref = data[:,1] + +# This call does NOT update data. Memory allocated in matmul +ref = af.matmul(inputA, inputB) +# reference does not point to the same memory as the data array + +# [indexing9-endsnippet] + + +# [indexing10-snippet] + +reference = A[:, 2] + +A[:, 2] = 3.14 +# [indexing10-endsnippet] + + +# [indexing11-snippet] + +ref = A[:, 2] + +A[:, 2] = 3.14 +# [indexing11-endsnippet] + + +# [indexing12-snippet] + +hidx = [4, 3, 4, 0] +hvals = [9.0, 8.0, 7.0, 6.0] + +idx = af.Array(hidx) +vals = af.Array(hvals) +# [indexing12-endsnippet] + + +# [indexing13-snippet] + +A = af.Array[1,2,3,4,5,6,7,8,9] +A = af.moddims(A,(3,3)) +# 1.0000 4.0000 7.0000 +# 2.0000 5.0000 8.0000 +# 3.0000 6.0000 9.0000 + +print(A[0,0]) # first element +# 1.0000 + +print(A[0,1]) # first row, second column +# 4.0000 +# [indexing13-endsnippet] + diff --git a/docs/indexing.rst b/docs/indexing.rst new file mode 100644 index 0000000..07991c2 --- /dev/null +++ b/docs/indexing.rst @@ -0,0 +1,208 @@ +Indexing +======== +Indexing in ArrayFire is a powerful but easy to abuse feature of the af::array class. This feature allows you to reference or copy subsections of a larger array and perform operations on only a subset of elements. + +Indexing in ArrayFire can be performed using the parenthesis operator or one of the member functions of the af::array class. These functions allow you to reference one or a range of elements from the original array. + +Here we will demonstrate some of the ways you can use indexing in ArrayFire and discuss ways to minimize the memory and performance impact of these operations. + +Lets start by creating a new 4x4 matrix of floating point numbers: + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing1-snippet] + :end-before: [indexing1-endsnippet] + +ArrayFire is column-major so the resulting A array will look like this: + +.. math:: + + \begin{bmatrix} + 0 & 4 & 8 & 12 \\ + 1 & 5 & 9 & 13 \\ + 2 & 6 & 10 & 14 \\ + 3 & 7 & 11 & 15 + \end{bmatrix} + +In Python, for a two-dimensional array like a matrix, you can access its first element by providing the indices 0, 0 within the indexing operator of the af.array object. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing2-snippet] + :end-before: [indexing2-endsnippet] + +.. math:: + + + A[0,0] = [0] + + A[2,3] = [14] + +.. note:: + :class: warning + + Normally you want to avoid accessing individual elements of the array like this for performance reasons. + + This is a warning note regarding accessing individual elements of arrays. + + + + +Indexing with negative values will access from the end of the array. For example, the value negative one and negative two(-2) will return the last and second to last element of the array, respectively. ArrayFire provides the end alias for this which also allows you to index the last element of the array. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing3-snippet] + :end-before: [indexing3-endsnippet] + + +Indexing slices and subarrays* +============================== +You can access regions of the array via the af::seq and af::span objects. The span objects allows you to select the entire set of elements across a particular dimension/axis of an array. For example, we can select the third column of the array by passing span as the first argument and 2 as the second argument to the parenthesis operator. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing4-snippet] + :end-before: [indexing4-endsnippet] + +.. math:: + + A[:, 2]=\begin{bmatrix} + 8 \\ + 9 \\ + 10 \\ + 11 \\ + \end{bmatrix} + +You can read that as saying that you want all values across the first dimension, but only from index 2 of the second dimension. + +You can access the second row by passing [1, :] to the array + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing5-snippet] + :end-before: [indexing5-endsnippet] + +.. math:: + + A[1, :]=\begin{bmatrix} + 1,5,9,13\\ + \end{bmatrix} + +You can use Python's slicing notation to define a range when indexing in **arrayfire**. For example, if you want to get the first two columns of an array, you can access the array by specifying **':'** for the rows (to select all rows), and **0:2** for the columns (to select columns from index 0 to 1). + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing6-snippet] + :end-before: [indexing6-endsnippet] + +.. math:: + + A[:, 0:2]= \begin{bmatrix} + 0 & 4\\ + 1 & 5\\ + 2 & 6\\ + 3 & 7\\ + \end{bmatrix} + + +Indexing using af.Array ? (Does the python wrapper support cartesian products) TODO STF +======================================================================================= + + +In Python with arrayfire, you can also index arrays using other **af.array** objects. arrayfire performs a Cartesian product of the input arrays. + +References and copies +===================== +All indexing operations in ArrayFire return **af.array** objects, which are instances of the array_proxy class. These objects can either be newly created arrays or references to the original array, depending on the type of indexing operation applied to them + +* When an array is indexed using another **af.array** , a new array is created instead of referencing the original data. +* If an array was indexed using a scalar, **sequential '0:2'** or **span ':'**, then the resulting array will reference the original data IF the first dimension is continuous. The following lines will not allocate additional memory. + +.. note:: + :class: warning + + The new arrays wither references or newly allocated arrays, are independent of the original data. Meaning that any changes to the original array will not propagate to the references. Likewise, any changes to the reference arrays will not modify the original data. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing7-snippet] + :end-before: [indexing7-endsnippet] + +The following code snippet shows some examples of indexing that will allocate new memory. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing8-snippet] + :end-before: [indexing8-endsnippet] + +Even though the copy3 array references continuous memory in the original array, using an **af.array** for indexing in ArrayFire results in the creation of a new array + +Assignment +========== +In Python with ArrayFire, assigning an **af.array** replaces the array on the left-hand side of :literal:`=` with the result from the right-hand side. This can lead to changes in type and shape compared to the original array. Notably, assignments do not update arrays previously referenced through indexing operations. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing9-snippet] + :end-before: [indexing9-endsnippet] + + +The :literal:`ref` array is created by indexing into the data array. The initialized :literal:`ref` array points to the data array and does not allocate memory when it is created. After the matmul call, the :literal:`ref` array will not be pointing to the data array. The matmul call will not update the values of the data array. + +You can update the contents of an **af.Array** by assigning with the operator parenthesis. For example, if you wanted to change the third column of the :literal:`A` array you can do that by assigning to :literal:`A[:, 2]`. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing10-snippet] + :end-before: [indexing10-endsnippet] + +.. math:: + + ref= \begin{bmatrix} + 8\\ + 9\\ + 10\\ + 11\\ + \end{bmatrix} A = \begin{bmatrix} + 0 & 4 & 3.14 & 12\\ + 1 & 5 & 3.14 & 13\\ + 2 & 6 & 3.14 & 14\\ + 3 & 7 & 3.14 & 15\\ + \end{bmatrix} + +This will update only the array being modified. If there are arrays that are referring to this array because of an indexing operation, those values will remain unchanged. + +Allocation will only be performed if there are other arrays referencing the data at the point of assignment. In the previous example, an allocation will be performed when assigning to the :literal:`A` array because the :literal:`ref` array is pointing to the original data. Here is another example demonstrating when an allocation will occur: + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing11-snippet] + :end-before: [indexing11-endsnippet] + + +In this example, no allocation will take place because when the :literal:`ref` object is created, it is pointing to :literal:`A`'s data. Once it goes out of scope, no data points to A, therefore when the assignment takes place, the data is modified in place instead of being copied to a new address. + +You can also assign to arrays using another af::arrays as an indexing array. This works in a similar way to the other types of assignment but care must be taken to assure that the indexes are unique. Non-unique indexes will result in a race condition which will cause non-deterministic values. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing12-snippet] + :end-before: [indexing12-endsnippet] + + + +**TODO STF** + +Member Functions TODO STF +========================= + +Additional examples +=================== +See Assignment & Indexing operation on arrays for the full listing. + +.. literalinclude:: indexing.py + :language: python + :start-after: [indexing13-snippet] + :end-before: [indexing13-endsnippet] + \ No newline at end of file diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..edd9cc5 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,135 @@ +ArrayFire Installer +=================== + +Installing ArrayFire couldn't be easier. Navigate to https://arrayfire.com/download and download the appropriate installer for the target architecture and operating system. Although ArrayFire can be `built from source `_, the installers conveniently package necessary dependencies. + +Install the latest device drivers before using ArrayFire. Drivers and runtimes should be downloaded and installed from each device vendor's website. + +Install Instructions +==================== + +* :ref:`Windows ` +* :ref:`Linux ` +* :ref:`macOs ` + +.. _Windows: + +Windows +======= +Once the ArrayFire has been downloaded, run the installer. + +The installer offers the option to automatically add ArrayFire to the path for all users. If the installer did not do this, simply append :literal:`%AF_PATH%/lib` to the PATH variable so that the loader can find ArrayFire DLLs. + +For more information on using ArrayFire on Windows, visit the following page. + + +.. _Linux: + +Linux +===== + +There are two ways to install ArrayFire on Linux. + +Package Manager +Using the ArrayFire Linux Installer +As of today, approach (1) is only supported for Ubuntu 18.04 and 20.04. Please go through the GitHub wiki `page `_ for detailed instructions. + +For approach (2), once the ArrayFire installer is downloaded, execute the installer from the terminal as shown below. Set the :literal:`--prefix` argument to the target install directory; we recommend :literal:`/opt`. + +.. code-block:: text + + ./ArrayFire_*_Linux_x86_64.sh --include-subdir --prefix=/opt + +Given sudo permissions, the ArrayFire libraries can be added to the path via :literal:`ldconfig` like so: + +.. code-block:: text + + echo /opt/arrayfire/lib64 > /etc/ld.so.conf.d/arrayfire.conf + sudo ldconfig + +Otherwise, the :literal:`LD_LIBRARY_PATH` environment variable can be set so that the shared library loader can find the ArrayFire libraries. + +For more information on using ArrayFire on Linux, visit the following page*. + +Graphics support +~~~~~~~~~~~~~~~~ + +ArrayFire enables high-performance visualizations via the `Forge `_ library. On Linux, there are a few dependencies to install to enable graphics support: + +* FreeImage +* Fontconfig +* GLU (OpenGL Utility Library) + +To install these dependencies on common Linux distributions: + +**Debian, Ubuntu (14.04 and above), and other Debian derivatives** + +.. code-block:: text + + apt install build-essential libfreeimage3 libfontconfig1 libglu1-mesa + + +**Fedora, Redhat, CentOS** + +.. code-block:: text + + yum install freeimage fontconfig mesa-libGLU + + +.. _macOS: + +macOS +===== + +Once the ArrayFire installer has been downloaded, execute the installer by either double-clicking on the ArrayFire :literal:`pkg` file or running the following command: + +.. code-block:: text + + sudo installer -pkg Arrayfire-*_OSX.pkg -target / + +For more information on using ArrayFire on macOS, visit the following page*. + + +NVIDIA Tegra devices +~~~~~~~~~~~~~~~~~~~~ + +ArrayFire is capable of running TX2 devices. + +Before installing ArrayFire, make sure the latest version of JetPack (v2.3 and above) or L4T (v24.2 and above) is installed. + +Tegra prerequisites +~~~~~~~~~~~~~~~~~~~ + +The following dependencies are required for Tegra devices: + +.. code-block:: text + + sudo apt install libopenblas-dev liblapacke-dev + +Testing installation +==================== + +After ArrayFire is finished installing, we recommend building and running a few of the provided examples to verify things are working as expected. + +On Windows, open the CMakeLists.txt file from CMake-GUI. Once the project is configured and generated, build and run the examples from Visual Studio. + +On Linux, run the following commands: + +.. code-block:: text + + cp -r /opt/arrayfire/share/ArrayFire/examples /tmp/examples + cd /tmp/examples + mkdir build + cd build + cmake .. + make + ./helloworld/helloworld_{cpu,cuda,oneapi,opencl} + +Getting help +~~~~~~~~~~~~ + +* Google Groups: https://groups.google.com/forum/#!forum/arrayfire-users +* ArrayFire Services: `Consulting `_ | `Training `_ +* ArrayFire Blogs: http://arrayfire.com/blog/ +* Email: support@arrayfire.com + diff --git a/docs/introductiontovectorization.py b/docs/introductiontovectorization.py new file mode 100644 index 0000000..f44c63a --- /dev/null +++ b/docs/introductiontovectorization.py @@ -0,0 +1,195 @@ + +# [vectorization1-snippet] +import arrayfire as af + +# Create an ArrayFire array 'a' using af.range() +a = af.range(10) # Creates an array [0, 1, 2, ..., 9] + +# Loop through the elements of 'a' and increment each element by 1 +for i in range(a.dims()[0]): + a[i] = a[i] + 1 # Increment each element by 1 + +# Print the modified array 'a' +print("Modified array 'a':") +print(a) +# [vectorization1-endsnippet] + + +# [vectorization2-snippet] + +import arrayfire as af + +#[0, 9] +a = af.range(10) + +# [1, 10] +a = a+ 1 +# [vectorization2-endsnippet] + + +# [vectorization3-snippet] + +import arrayfire as af + +# Define the filter coefficients as a list +g_coef = [1, 2, 1, + 2, 4, 2, + 1, 2, 1] + +# Convert the coefficients list to an ArrayFire array and scale it +filter = (1.0 / 16.0) * af.Array(3, 3, g_coef) + +# Generate a random signal array of dimensions WIDTH x HEIGHT x NUM +WIDTH = 100 +HEIGHT = 100 +NUM = 3 +signal = af.randu(WIDTH, HEIGHT, NUM) + +# Perform 2D convolution of signal with filter +conv = af.convolve2(signal, filter) + +# Print the result if needed +print("Convolution result:") +print(conv) +# [vectorization3-endsnippet] + + +# [vectorization4-snippet] + +import arrayfire as af + +# Define dimensions +WIDTH = 256 +HEIGHT = 256 +NUM_IMAGES = 100 + +# Generate an array of 100 WIDTH x HEIGHT images of random numbers +imgs = af.randu((NUM_IMAGES, (WIDTH, HEIGHT))) + +# Rotate all of the images in a single command (rotate by 45 degrees) +rot_imgs = af.rotate(imgs, 45) + +# Print the shape of rot_imgs to verify the result +print("Shape of rotated images:", rot_imgs.shape()) + +# Optionally, print the rotated images +# af.af_print(rot_imgs) + +# Optionally, display or further process `rot_imgs` as needed + +# [vectorization4-endsnippet] + + +# [vectorization5-snippet] + +import arrayfire as af + +# Create an ArrayFire array 'a' using af.range() +a = af.range(10) # Creates an array [0, 1, 2, ..., 9] + +# Perform element-wise addition using vectorized operations +a = a + 1 # Increment each element by 1 + +# Print the modified array 'a' +print("Modified array 'a':") +print(a) +# [vectorization5-endsnippet] + + +#NOT WORKING +# [vectorization6-snippet] + +import arrayfire as af + +# Example data dimensions +N = 10 +SPAN = af.span + +# Example arrays A and B +A = af.randu(100, N) # Example array A of size 100xN filled with random numbers +B = af.constant(0, 100, N) # Example array B initialized with zeros + +# Parallel execution using gfor loop +af.gfor(seq_i, N): +B[SPAN, seq_i] = af.accum(A[SPAN, seq_i]) + +# Print array B after parallel accumulations +print("Array B after parallel accumulations:") +af.af_print(B) +# [vectorization6-endsnippet] + + +# [vectorization7-snippet] + +import arrayfire as af + +# Calculate accumulative sum along columns of A +B = af.accum(A) + + +# [vectorization7-endsnippet] + +#NOT WORKING +# [vectorization8-snippet] + +import arrayfire as af + +# Define constants +p = 4 +n = 1000 + +# Generate constants array and variable terms array +consts = af.randu(p) +var_terms = af.randn(p, n) + +# Initialize combination array +combination = af.constant(0, p, n) + +# Perform element-wise multiplication using gfor loop +af.gfor(seq_i, n): +combination[:, seq_i] = consts * var_terms[:, seq_i] + +# Print the combination array after computation +print("Combination array:") +af.af_print(combination) +# [vectorization8-endsnippet] + + + +# [vectorization9-snippet] + +import arrayfire as af + +# Create the filter and weight vectors +filter = af.randu((1, 5)) +weights = af.randu((5, 5)) + +# Apply the filter using a for-loop equivalent +filtered_weights = af.constant(0, (5, 5)) +for i in range(weights.shape[1]): + filtered_weights[:, i] = af.matmul(filter, weights[:, i]) + +# Print the filtered weights array +print("Filtered weights:") +af.af_print(filtered_weights) +# [vectorization9-endsnippet] + + +# [vectorization10-snippet] + +import arrayfire as af + +# Create the filter and weight vectors +filter = af.randu((1, 5)) # Shape: 1x5 +weights = af.randu((5, 5)) # Shape: 5x5 + +# Transpose the filter to align dimensions for broadcasting +filter_transposed = af.transpose(filter) # Shape: 5x1 + +# Element-wise multiplication with broadcasting +filtered_weights = filter_transposed * weights + +# Print the filtered weights array +print("Filtered weights:") +print(filtered_weights) +# [vectorization10-endsnippet] diff --git a/docs/introductiontovectorization.rst b/docs/introductiontovectorization.rst new file mode 100644 index 0000000..dcf8455 --- /dev/null +++ b/docs/introductiontovectorization.rst @@ -0,0 +1,126 @@ +Introduction to Vectorization +============================= +Programmers and Data Scientists want to take advantage of fast and parallel computational devices. Writing vectorized code is necessary to get the best performance out of the current generation parallel hardware and scientific computing software. However, writing vectorized code may not be immediately intuitive. ArrayFire provides many ways to vectorize a given code segment. In this tutorial, we present several methods to vectorize code using ArrayFire and discuss the benefits and drawbacks associated with each method. + +Generic/Default Vectorization +============================= +By its very nature, ArrayFire is a vectorized library. Most functions operate on arrays as a whole – on all elements in parallel. Wherever possible, existing vectorized functions should be used opposed to manually indexing into arrays. For example consider the following code: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization1-snippet] + :end-before: [vectorization1-endsnippet] + +Although completely valid, the code is very inefficient as it results in a kernel kernels that operate on one datum. Instead, the developer should have used ArrayFire's overload of the + operator: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization2-snippet] + :end-before: [vectorization2-endsnippet] + +This code will result in a single kernel that operates on all 10 elements of :literal:`a` in parallel. + +Most ArrayFire functions are vectorized. A small subset of these include: + ++---------------------------------------+--------------------------------------------+ +| Operator Category | Functions | ++=======================================+============================================+ +| Arithmetic Operations | +, -, \*, /, %, >, < | ++---------------------------------------+--------------------------------------------+ +| Logical Operations | &&, ||(or), <, >, ==, != etc. | ++---------------------------------------+--------------------------------------------+ +| Numeric functions | abs(), floor(), round(), min(), max(), etc.| ++---------------------------------------+--------------------------------------------+ +| Complex Operations | real(), imag(), conj(), etc. | ++---------------------------------------+--------------------------------------------+ +| Exponential and logarithmic functions | exp(), log(), expm1(), log1p(), etc. | ++---------------------------------------+--------------------------------------------+ +| Logical Operations | sin(), cos(), tan(), etc. | ++---------------------------------------+--------------------------------------------+ +| Hyperbolic Functions | sinh(), cosh(), tanh(), etc. | ++---------------------------------------+--------------------------------------------+ + +In addition to element-wise operations, many other functions are also vectorized in ArrayFire. + +Notice that even that perform some form of aggregation (e.g. :literal:`sum()` or :literal:`min()`), signal processing (like :literal:`convolve()`), and even image processing functions (i.e. :literal:`rotate()`) all support vectorization on different columns or images. For example, if we have :literal:`NUM` images of size :literal:`WIDTH` by :literal:`HEIGHT`, one could convolve each image in a vector fashion as follows: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization3-snippet] + :end-before: [vectorization3-endsnippet] + + +Similarly, one can rotate 100 images by 45 degrees in a single call using code like the following: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization4-snippet] + :end-before: [vectorization4-endsnippet] + +Although most functions in ArrayFire do support vectorization, some do not. Most notably, all linear algebra functions. Even though they are not vectorized linear algebra operations still execute in parallel on your hardware. + +Using the built in vectorized operations should be the first and preferred method of vectorizing any code written with ArrayFire. + +GFOR: Parallel for-loops +======================== +Another novel method of vectorization present in ArrayFire is the GFOR loop replacement construct. GFOR allows launching all iterations of a loop in parallel on the GPU or device, as long as the iterations are independent. While the standard for-loop performs each iteration sequentially, ArrayFire's gfor-loop performs each iteration at the same time (in parallel). ArrayFire does this by tiling out the values of all loop iterations and then performing computation on those tiles in one pass. You can think of gfor as performing auto-vectorization of your code, e.g. you write a gfor-loop that increments every element of a vector but behind the scenes ArrayFire rewrites it to operate on the entire vector in parallel. + +The original for-loop example at the beginning of this document could be rewritten using GFOR as follows: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization5-snippet] + :end-before: [vectorization5-endsnippet] + +In this case, each instance of the gfor loop is independent, thus ArrayFire will automatically tile out the :literal:`a` array in device memory and execute the increment kernels in parallel. + +To see another example, you could run an accum() on every slice of a matrix in a for-loop, or you could "vectorize" and simply do it all in one gfor-loop operation: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization6-snippet] + :end-before: [vectorization6-endsnippet] + +However, returning to our previous vectorization technique, accum() is already vectorized and the operation could be completely replaced with merely: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization7-snippet] + :end-before: [vectorization7-endsnippet] + +It is best to vectorize computation as much as possible to avoid the overhead in both for-loops and gfor-loops. However, the gfor-loop construct is most effective in the narrow case of broadcast-style operations. Consider the case when we have a vector of constants that we wish to apply to a collection of variables, such as expressing the values of a linear combination for multiple vectors. The broadcast of one set of constants to many vectors works well with gfor-loops: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization8-snippet] + :end-before: [vectorization8-endsnippet] + +Using GFOR requires following several rules and multiple guidelines for optimal performance. The details of this vectorization method can be found in the GFOR documentation. + + +Batching +======== +The batchFunc() function allows the broad application of existing ArrayFire functions to multiple sets of data. Effectively, batchFunc() allows ArrayFire functions to execute in "batch processing" mode. In this mode, functions will find a dimension which contains "batches" of data to be processed and will parallelize the procedure. + +Consider the following example. Here we create a filter which we would like to apply to each of the weight vectors. The naive solution would be using a for-loop as we have seen previously: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization9-snippet] + :end-before: [vectorization9-endsnippet] + +However, as we have discussed above, this solution will be very inefficient. One may be tempted to implement a vectorized solution as follows: + +.. literalinclude:: introductiontovectorization.py + :language: python + :start-after: [vectorization10-snippet] + :end-before: [vectorization10-endsnippet] + + +However, the dimensions of :literal:`filter` and :literal:`weights` do not match, thus ArrayFire will generate a runtime error. + +Advanced Vectorization +====================== +We have seen the different methods ArrayFire provides to vectorize our code. Tying them all together is a slightly more involved process that needs to consider data dimensionality and layout, memory usage, nesting order, etc. An excellent example and discussion of these factors can be found on our blog: + +http://arrayfire.com/how-to-write-vectorized-code/ \ No newline at end of file diff --git a/docs/linux.rst b/docs/linux.rst new file mode 100644 index 0000000..b9f86d6 --- /dev/null +++ b/docs/linux.rst @@ -0,0 +1,123 @@ +Using ArrayFire on Linux +======================== + +Once you have :ref:`installed ` ArrayFire on your system, the next thing to do is set up your build system. On Linux, you can create ArrayFire projects using almost any editor, compiler, or build system. The only requirements are that you include the ArrayFire header directories and link with the ArrayFire library you intend to use i.e. CUDA, OpenCL, oneAPI, CPU, or Unified backends. + +.. _bigpicture: + +The big picture +=============== + +On Linux, we recommend installing ArrayFire to :literal:`/opt/arrayfire` directory. The installer will populate files in the following sub-directories: + +.. code-block:: text + + include/arrayfire.h - Primary ArrayFire include file + include/af/*.h - Additional include files + lib/libaf* - CPU, CUDA, oneAPI, and OpenCL libraries (.a, .so) + lib/libforge* - Visualization library + lib/libcu* - CUDA backend dependencies + lib/libOpenCL.so - OpenCL ICD Loader library + share/ArrayFire/cmake/* - CMake config (find) scripts + share/ArrayFire/examples/* - All ArrayFire examples + +Because ArrayFire follows standard installation practices, you can use basically any build system to create and compile projects that use ArrayFire. Among the many possible build systems on Linux we suggest using ArrayFire with either CMake or Makefiles with CMake being our preferred build system. + +Prerequisite software +===================== + +To build ArrayFire projects you will need a compiler + +**Fedora, Centos and Redhat** + +Install EPEL repo (not required for Fedora) + +.. code-block:: text + + yum install epel-release + yum update + +Install build dependencies + +.. code-block:: text + + yum install gcc gcc-c++ cmake3 make + +**Debian and its derivatives** + +Install common dependencies + +.. code-block:: text + + apt install build-essential cmake cmake-curses-gui + +CMake +We recommend that the CMake build system be used to create ArrayFire projects. As `discussed above `, ArrayFire ships with a series of CMake scripts to make finding and using our library easy. + +First create a file called :literal:`CMakeLists.txt` in your project directory: + +.. code-block:: text + + cd your-project-directory + touch CMakeLists.txt + +and populate it with the following code: + +.. code-block:: text + + find_package(ArrayFire) + add_executable( [list your source files here]) + + # To use Unified backend, do the following. + # Unified backend lets you choose the backend at runtime + target_link_libraries( ArrayFire::af) + +where :literal:`my_executable` is the name of the executable you wish to create. See the `CMake documentation `_ for more information on how to use CMake. To link with a specific backend directly, replace the :literal:`ArrayFire::af` with the following for their respective backends. + +* :literal:`ArrayFire::afcpu` for CPU backend. +* :literal:`ArrayFire::afcuda` for CUDA backend. +* :literal:`ArrayFire::afoneapi` for oneAPI backend. +* :literal:`ArrayFire::afopencl` for OpenCL backend. + +Next we need to instruct CMake to create build instructions and then compile. We suggest using CMake's out-of-source build functionality to keep your build and source files cleanly separated. To do this open the CMake GUI. + +.. code-block:: text + + cd your-project-directory + mkdir build + cd build + cmake .. + make + +NOTE: If you have installed ArrayFire to a non-standard location, CMake can still help you out. When you execute CMake specify the path to ArrayFire installation root as :literal:`ArrayFire_DIR` variable. + +For example, if ArrayFire were installed locally to :literal:`/home/user/ArrayFire` then you would modify the :literal:`cmake` command above to contain the following definition: + +.. code-block:: text + + cmake -DArrayFire_DIR=/home/user/ArrayFire .. + +You can also specify this information in the :literal:`ccmake` command-line interface. + + +Makefiles +========= + +Building ArrayFire projects with Makefiles is fairly similar to CMake except you must specify all paths and libraries manually. + +As with any :literal:`make` project, you need to specify the include path to the directory containing :literal:`arrayfire.h` file. This should be :literal`-I /opt/arrayfire/include` if you followed our installation instructions. + +Similarly, you will need to specify the path to the ArrayFire library using the :literal:`-L` option (e.g. :literal:`-L/opt/arrayfire/lib`) followed by the specific ArrayFire library you wish to use using the :literal:`-l` option (for example :literal:`-lafcpu`, :literal:`-lafopencl`, :literal:`-lafoneapi`, :literal:`-lafcuda`, or :literal:`-laf` for the CPU, OpenCL, oneAPI, and CUDA, and unified backends, respectively. + +Here is a minimal example Makefile which uses ArrayFire's CPU backend: + +.. code-block:: text + + LIBS=-lafcpu + LIB_PATHS=-L/opt/arrayfire/lib + INCLUDES=-I/opt/arrayfire/include + CC=g++ $(COMPILER_OPTIONS) + COMPILER_OPTIONS=-std=c++11 -g + + all: main.cpp Makefile + $(CC) main.cpp -o test $(INCLUDES) $(LIBS) $(LIB_PATHS) \ No newline at end of file diff --git a/docs/overview.py b/docs/overview.py new file mode 100644 index 0000000..18ee70a --- /dev/null +++ b/docs/overview.py @@ -0,0 +1,16 @@ +import arrayfire as af + +# [pi-example-simple-snippet] +# Monte Carlo estimation of pi +def calc_pi_device(samples) -> float: + # Simple, array based API + # Generate uniformly distributed random numers + x = af.randu(samples) + y = af.randu(samples) + # Supports Just In Time Compilation + # The following line generates a single kernel + within_unit_circle = (x * x + y * y) < 1 + # Intuitive function names + return 4 * af.count(within_unit_circle) / samples +# [pi-example-simple-endsnippet] + diff --git a/docs/overview.rst b/docs/overview.rst new file mode 100644 index 0000000..39385fb --- /dev/null +++ b/docs/overview.rst @@ -0,0 +1,121 @@ +Overview +======== + + + +About Array Fire +---------------- + +`ArrayFire `_ is a high performance library for parallel computing with an easy-to-use API. It enables users to write scientific computing code that is portable across CUDA, OpenCL and CPU devices. This project provides Python bindings for the ArrayFire library. + +Installing ArrayFire +-------------------- + +Install ArrayFire using either a binary installer for Windows, OSX, or Linux or download it from source: + * `Download and install Binaries `_ + * `Build from source `_ + +Easy to use +----------- + +The array object is beautifully simple. + +Array-based notation effectively expresses computational algorithms in readable math-resembling notation. Expertise in parallel programming is not required to use ArrayFire. + +A few lines of ArrayFire code accomplishes what can take 100s of complicated lines in CUDA, oneAPI, or OpenCL kernels. + +ArrayFire is extensive! +----------------------- +Support for multiple domains +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ArrayFire contains hundreds of functions across various domains including: + +Vector Algorithms +Image Processing +Computer Vision +Signal Processing +Linear Algebra +Statistics +and more. +Each function is hand-tuned by ArrayFire developers with all possible low-level optimizations. + +Support for various data types and sizes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ArrayFire operates on common data shapes and sizes, including vectors, matrices, volumes, and + +It supports common data types, including single and double precision floating point values, complex numbers, booleans, and 32-bit signed and unsigned integers. + +Extending ArrayFire +~~~~~~~~~~~~~~~~~~~ + +ArrayFire can be used as a stand-alone application or integrated with existing CUDA, oneAPI, or OpenCL code. + +Code once, run anywhere! +------------------------ + +With support for x86, ARM, CUDA, oneAPI, and OpenCL devices, ArrayFire supports for a comprehensive list of devices. + +Each ArrayFire installation comes with: + + * a CUDA backend (named 'libafcuda') for `NVIDIA GPUs `_ + * a oneAPI backend (named 'libafoneapi') for `oneAPI devices `_ + * an OpenCL backend (named 'libafopencl') for `OpenCL devices `_, + * a CPU backend (named 'libafcpu') to fall back to when CUDA, oneAPI, or OpenCL devices are unavailable. + +ArrayFire is highly efficient +----------------------------- +Vectorized and Batched Operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ArrayFire supports batched operations on N-dimensional arrays. Batch operations in ArrayFire are run in parallel ensuring an optimal usage of CUDA, oneAPI, or OpenCL devices. + +Best performance with ArrayFire is achieved using vectorization techniques. + +ArrayFire can also execute loop iterations in parallel with the gfor function. + +Just in Time compilation +~~~~~~~~~~~~~~~~~~~~~~~~ +ArrayFire performs run-time analysis of code to increase arithmetic intensity and memory throughput, while avoiding unnecessary temporary allocations. It has an awesome internal JIT compiler to make important optimizations. + +Read more about how ArrayFire JIT. can improve the performance in your application. + + +Simple Example +-------------- + +Here is an example of ArrayFire code that performs a Monte Carlo estimation of PI. + +.. literalinclude:: overview.py + :language: python + :start-after: [pi-example-simple-snippet] + :end-before: [pi-example-simple-endsnippet] + + + + +Product Support +--------------- +Free Community Options +~~~~~~~~~~~~~~~~~~~~~~ + * `ArrayFire Mailing List `_ (recommended) + * `StackOverFlow `_ + +Premium Support +~~~~~~~~~~~~~~~ + * Phone Support - available for purchase(request a quote) + +Contact Us +~~~~~~~~~~ + * If you need to contact us, visit our `contact us page `_. + +Email +~~~~~ + * Engineering: technical@arrayfire.com + * Sales: sales@arrayfire.com + +Citations and Acknowledgements +------------------------------ + +If you redistribute ArrayFire, please follow the terms established in `the license `_. If you wish to cite ArrayFire in an academic publication, please use the following reference: \ No newline at end of file diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst new file mode 100644 index 0000000..1dbe412 --- /dev/null +++ b/docs/releasenotes.rst @@ -0,0 +1,9 @@ +Release Notes +============= + + + +TO DO +----- +Add Release Notes + diff --git a/docs/tutorial.rst b/docs/tutorial.rst new file mode 100644 index 0000000..3ffe64c --- /dev/null +++ b/docs/tutorial.rst @@ -0,0 +1,20 @@ +Tutorial +======== + +* :doc:`Installation ` +* :doc:`Using on Linux ` +* Using on Windows +* Using on OSX +* :doc:`Getting Started ` +* :doc:`Introduction to Vectorization ` +* :doc:`Array and Matrix Manipulation ` +* CUDA Interoperability +* OpenCL Interoperability +* Unified Backend +* Forge Visualization +* :doc:`Indexing ` +* Timing ArrayFire +* :doc:`Configuring ArrayFire Environment ` +* :doc:`Debugging ArrayFire Code ` +* :doc:`ArrayFire JIT Code Generation ` +* GFOR Usage \ No newline at end of file diff --git a/tests/test_documentation/__init__.py b/tests/test_documentation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_documentation/test_documentation.py b/tests/test_documentation/test_documentation.py new file mode 100644 index 0000000..ddbb0de --- /dev/null +++ b/tests/test_documentation/test_documentation.py @@ -0,0 +1,402 @@ +import pytest +import arrayfire as af +import math + + + + +def test_array_shapes(): + # [gettingstarted1-snippet] + # Arrays may be created using the array constructor and dimensioned + # as 1D, 2D, 3D; however, the values in these arrays will be undefined + import arrayfire as af + + array = af.constant(0, (100,)) + array_2d = af.constant(0, (10, 100)) + array_3d = af.constant(0, (10, 10, 10)) + # [gettingstarted1-endsnippet] + assert array.shape == (100,) # Check shape of 1D array + assert array_2d.shape == (10, 100) # Check shape of 2D array + assert array_3d.shape == (10, 10, 10) # Check shape of 3D array + + + # [pi-example-simple-snippet] + # Monte Carlo estimation of pi +def calc_pi_device(samples) -> float: + # Simple, array based API + # Generate uniformly distributed random numers + x = af.randu(samples) + y = af.randu(samples) + # Supports Just In Time Compilation + # The following line generates a single kernel + within_unit_circle = (x * x + y * y) < 1 + # Intuitive function names + return 4 * af.count(within_unit_circle) / samples + # [pi-example-simple-endsnippet] + +def test_calc_pi_device(): + samples = 100000 + x = af.randu(samples) + y = af.randu(samples) + within_unit_circle = (x * x + y * y) < 1 + result = 4 * af.count(within_unit_circle) / samples + assert isinstance(result, float) + error = abs(result - math.pi) + tolerance = 0.01 + assert error < tolerance, f"Error ({error}) exceeds tolerance ({tolerance})" + + +# [gettingstarted2-snippet] +import arrayfire as af + +# Generate an array of size three filled with zeros. +# If no data type is specified, ArrayFire defaults to f32. +# The constant function generates the data on the device. +zeroes = af.constant(0,(3,)) + +# Generate a 1x4 array of uniformly distributed [0,1] random numbers +# The randu function generates the data on the device. +rand1 = af.randu((1,4)) + +# Generate a 2x2 array (or matrix, if you prefer) of random numbers +# sampled from a normal distribution. +# The randn function generates data on the device. +rand2 = af.randu((2,2)) + +# Generate a 3x3 identity matrix. The data is generated on the device. +iden = af.identity((3,3)) + +# Lastly, create a 2x1 array (column vector) of uniformly distributed +# 32-bit complex numbers (c32 data type): +randcplx = af.randu((2,1)) +# [gettingstarted2-endsnippet] + +import pytest +import arrayfire as af + +def test_arrayfire_operations(): + # Generate an array of size three filled with zeros + zeroes = af.constant(0, (3,)) + assert zeroes.shape == (3,) # Check shape + + # Generate a 1x4 array of uniformly distributed [0,1] random numbers + rand1 = af.randu((1, 4)) + assert rand1.shape == (1, 4) # Check shape + + # Generate a 2x2 array of random numbers sampled from a normal distribution + rand2 = af.randn((2, 2)) + assert rand2.shape == (2, 2) # Check shape + + # Generate a 3x3 identity matrix + iden = af.identity((3,3)) + assert iden.shape == (3, 3) # Check shape + + # Generate a 2x1 array (column vector) of uniformly distributed 32-bit complex numbers + randcplx = af.randu((2, 1)) + assert randcplx.shape == (2, ) # Check shape + +# [gettingstarted3-snippet] +import arrayfire as af +# Create a six-element array on the host +hA = ([0, 1, 2, 3, 4, 5]) + +# Which can be copied into an ArrayFire Array using the pointer copy +# constructor. Here we copy the data into a 2x3 matrix: +A = af.moddims(af.Array(hA),(2,3)) + + +# ArrayFire provides a convenince function for printing array +# objects in case you wish to see how the data is stored: +print(A) + +#todo how to create complex numbers +# [gettingstarted3-endsnippet] + + +def test_arrayfire_conversion(): + # Create a six-element array on the host + hA = ([0, 1, 2, 3, 4, 5]) + + # Copy data from host array to an ArrayFire array and reshape to 2x3 matrix + A = af.moddims(af.Array(hA),(2,3)) + + # Assert that the shape of A is (2, 3) + assert A.shape == (2, 3) + + # Assert that the elements in A match hA + for i in range(2): + for j in range(3): + assert A[i, j] == hA[i * 3 + j] + +# [gettingstarted11-snippet] + +import arrayfire as af + +# Create an array consisting of 3 random numbers +a = af.randu(3) + +# Get the scalar value of the array +val = a.scalar() + +# Print the scalar value +print(f"scalar value: {val}") +# [gettingstarted11-endsnippet] + + +import pytest +import arrayfire as af + +def test_arrayfire_scalar_value(): + # Create an array consisting of 3 random numbers + a = af.randu(3) + + # Get the scalar value of the array + val = a.scalar() + + # Assert that the scalar value is a float + assert isinstance(val, float) + + # Assert that the scalar value is between 0 and 1 (inclusive) + assert 0 <= val <= 1 + +def test_vectorization(): + + # [vectorization2-snippet] + + import arrayfire as af + + #[0, 9] + a = af.range(10) + + # [1, 10] + a = a+ 1 + # [vectorization2-endsnippet] + # Assertion: Verify the elements of the array 'a' + expected_result = af.Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + assert a == expected_result + +def test_apply_filter(): + # [vectorization9-snippet] + + import arrayfire as af + + # Create the filter and weight vectors + filter = af.randu((1, 5)) + weights = af.randu((5, 5)) + + # Apply the filter using a for-loop equivalent + filtered_weights = af.constant(0, (5, 5)) + for i in range(weights.shape[1]): + filtered_weights[:, i] = af.matmul(filter, weights[:, i]) + + # Print the filtered weights array + print("Filtered weights:") + print(filtered_weights) + # [vectorization9-endsnippet] + assert filtered_weights.shape == (5, 5) + + + + +def test_filtered_weights(): + # [vectorization10-snippet] + + import arrayfire as af + + # Create the filter and weight vectors + filter = af.randu((1, 5)) # Shape: 1x5 + weights = af.randu((5, 5)) # Shape: 5x5 + + # Transpose the filter to align dimensions for broadcasting + filter_transposed = af.transpose(filter) # Shape: 5x1 + + # Element-wise multiplication with broadcasting + filtered_weights = filter_transposed * weights + + expected_shape = (5, 5) # Expected shape of filtered_weights + + # Assertions + assert filtered_weights.shape == expected_shape + + # Print the filtered weights array + print("Filtered weights:") + print(filtered_weights) + # [vectorization10-endsnippet] + +def test_flatten_array(): + # [manipulation1-snippet] + + import arrayfire as af + + # Creates a 3x3 array filled with random numbers between [0, 1) + a = af.randu((3, 3)) + + # Flattens the array 'a' into a 1-dimensional column vector + flat_a = af.flat(a) + + # Display the original array 'a' + print(a) + + # [manipulation1-endsnippet] + assert flat_a.shape == (9,) # Check if it's a 1-dimensional array + assert len(flat_a) == 9 # Check if it has 9 elements (3x3 array) + + +def test_flip_array(): + + # [manipulation2-snippet] + + import arrayfire as af + + # Generate a 5x2 array of uniformly distributed random numbers between [0, 1) + a = af.randu((5, 2)) + + # Print the original array 'a' + print("Original array 'a' [5 2 1 1]") + print(a) + + # Flip the array 'a' along both axes (rows and columns) + flip_a = af.flip(a) + + # Print the flipped array 'flip_a' + print("\nFlipped array 'flip_a' [5 2 1 1]") + print(flip_a) + + # [manipulation2-endsnippet] + assert flip_a.shape == a.shape + assert af.flip(flip_a) == a + assert af.min(a) >= 0 + assert af.max(a) < 1 + assert af.min(flip_a) >= 0 + assert af.max(flip_a) < 1 + +def test_join_array(): + # [manipulation3-snippet] + + import arrayfire as af + + # Generate a 1-dimensional array 'a' of size 5 filled with uniformly distributed random numbers between [0, 1) + a = af.randu((5,)) + + # Print the original array 'a' + print("Original array 'a' [5 1 1 1]") + print(a) + + # Join the array 'a' with itself along axis 0 + a_join = af.join(0, a, a) + + # Print the joined array 'a_join' + print("\nJoined array 'a_join' [10 1 1 1]") + print(a_join) + # [manipulation3-endsnippet] + assert a_join.shape == (10,) + + + +def test_moddims_operations(): + # [manipulation4-snippet] + + import arrayfire as af + + a = af.randu((8,)) + + print(a) + + moddims_a = af.moddims(a,(2,4)) + + print(moddims_a) + + moddims_b = af.moddims(a,(len(a),)) + print(moddims_b) + + # [manipulation4-endsnippet] + assert moddims_a.shape == (2, 4) + assert moddims_b.shape == (8,) + assert a == af.moddims(moddims_a, (8,)) + assert a == moddims_b + + + +def test_arrayfire_shift(): + # [manipulation6-snippet] + + import arrayfire as af + + a = af.randu((3,5)) + print(a) + + a_shift = af.shift(a,(0,2)) + print(a_shift) + + a_shift1 = af.shift(a,(-1,2)) + print(a_shift1) + + # [manipulation6-endsnippet] + + # Check if arrays are equal by comparing element-wise + assert a != a_shift + assert a != a_shift1 + assert a_shift.shape == (3,5) + assert a_shift1.shape == (3,5) + + +def transpose_arrayifre(): + # [manipulation8-snippet] + + import arrayfire as af + + a = af.randu((3,3)) + print(a) #[3 3 1 1] + + ''' 0.3949 0.8465 0.3709 + 0.3561 0.9399 0.2751 + 0.6097 0.6802 0.2720''' + + + a_transpose = af.transpose(a) + print(a_transpose) #[3 3 1 1] + + ''' 0.3949 0.3561 0.6097 + 0.8465 0.9399 0.6802 + 0.3709 0.2751 0.2720''' + # [manipulation8-endsnippet] + # Convert arrays to Python lists for comparison + a_list = a.to_array().tolist() + a_transpose_list = a_transpose.to_array().tolist() + + # Compute the expected transpose manually + expected_a_transpose = list(zip(*a_list)) + + # Check if the transpose operation is correct + assert a_transpose_list == expected_a_transpose + + + + +def test_moddims(): + # [indexing1-snippet] + + import arrayfire as af + + data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] + + A = af.Array(data) + A = af.moddims(A,(4,4)) + +# [indexing1-endsnippet] + expected_result = [ + [0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15] + ] + + dims = A.shape + A_list = [[A[i, j] for j in range(dims[1])] for i in range(dims[0])] + + # Check if the reshaped array matches the expected result + assert A_list == expected_result + + + +