AIfES 2  2.0.0
aimath_q7_avr_pgm.h File Reference

Math functions for Q7 data type using AVR pgmspace.h library for constant data storage. More...

Go to the source code of this file.

Functions

void aimath_q7_avr_pgm_linear32_1 (const aitensor_t *a, const aitensor_t *b, const aitensor_t *c, aitensor_t *result)
 Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row. More...
 
void aimath_q7_avr_pgm_linear32_2 (const aitensor_t *a, const aitensor_t *b, const aitensor_t *c, aitensor_t *result)
 Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row. More...
 
void aimath_q7_avr_pgm_linear32_bt_1 (const aitensor_t *a, const aitensor_t *b, const aitensor_t *c, aitensor_t *result)
 Performs a matrix multiplication of Q7 matrices a and b (transposed) and adds a Q31 vector c to each row. More...
 
void aimath_q7_avr_pgm_linear32_bt_2 (const aitensor_t *a, const aitensor_t *b, const aitensor_t *c, aitensor_t *result)
 Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row. More...
 
void aimath_q7_avr_pgm_relu (const aitensor_t *x, aitensor_t *result)
 Calculates the rectifier (ReLU) value of each element in a Q7 tensor. More...
 
void aimath_q7_avr_pgm_leaky_relu (const aitensor_t *x, const void *alpha, aitensor_t *result)
 Calculates the leaky rectifier (leaky ReLU) value of each element in a Q7 tensor. More...
 
void aimath_q7_avr_pgm_elu (const aitensor_t *x, const void *alpha, aitensor_t *result)
 Calculates the exponential rectifier (ELU) value of each element in a Q7 tensor. More...
 
void aimath_q7_avr_pgm_sigmoid (const aitensor_t *x, aitensor_t *result)
 Calculates the sigmoid of each element in a Q7 tensor. More...
 
void aimath_q7_avr_pgm_tanh (const aitensor_t *x, aitensor_t *result)
 Calculates the tanh of each element in a Q7 tensor. More...
 
void aimath_q7_avr_pgm_softsign (const aitensor_t *x, aitensor_t *result)
 Calculates the softsign value of each element in a Q7 tensor. More...
 
void aimath_q7_avr_pgm_softmax (const aitensor_t *x, aitensor_t *result)
 Calculates the softmax value of each batch element (row) of a Q7 tensor. More...
 

Detailed Description

Math functions for Q7 data type using AVR pgmspace.h library for constant data storage.

Version
2.2.0

These functions modify the default implementation of the Q7 math functions to work with parameters, stored in the program memory of AVR controllers.

The library avr/pgmspace.h is required.

Function Documentation

◆ aimath_q7_avr_pgm_elu()

void aimath_q7_avr_pgm_elu ( const aitensor_t x,
const void *  alpha,
aitensor_t result 
)

Calculates the exponential rectifier (ELU) value of each element in a Q7 tensor.

The quantization parameters x must be defined constant in PROGMEM.

This function wraps the function aimath_f32_default_elu() internally.

\[ result_{i} = \begin{cases} \alpha \cdot (e^{x_i} - 1) & \text{if } x_i < 0 \\ x_i & \text{if } x_i \geq 0 \end{cases} \]

The ELU is calculated with a piecewise linear approximation to avoid using exponential functions.

\[ result_{i} = \begin{cases} x_i & \text{if } 0 \leq x_i\\ \alpha \cdot 0.625 \cdot x_i & \text{if } -1 \leq x < 0\\ \alpha \cdot (0.25 \cdot x_i - 0.375) & \text{if } -2 \leq x < -1\\ \alpha \cdot (0.09375 \cdot x_i - 0.6875) & \text{if } -3 \leq x < -2\\ \alpha \cdot (0.03125 \cdot x_i - 0.875) & \text{if } -4 \leq x < -3\\ - \alpha & \text{if } x < -4 \end{cases} \]

The quantization parameters of the result tensor are set to {shift = x.shift, zero_point = x.zero_point} by the function because the output values are in the interval (max(-alpha, min(x)), max(x)].

Example:

uint16_t x_shape[2] = {2, 3};
const aimath_q7_params_t x_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t x_data[2*3] = { 2, -4, 6,
-8, 10, -12};
aitensor_t x = AITENSOR_2D_Q7(x_shape, &x_params, x_data);
aiscalar_q7_t alpha = AISCALAR_Q7(1.0f, 0, 0);
uint16_t result_shape[2] = {2, 3};
aimath_q7_params_t result_params; // {shift, zero point}
int8_t result_data[2*3];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
aimath_q7_avr_pgm_elu(&x, &alpha, &result);
print_aitensor(&result);
void print_aitensor(const aitensor_t *tensor)
Printing a tensor to console.
void aimath_q7_avr_pgm_elu(const aitensor_t *x, const void *alpha, aitensor_t *result)
Calculates the exponential rectifier (ELU) value of each element in a Q7 tensor.
Parameters used for the quantized Q7 values, used as property of a tensor.
Definition: aimath_q7.h:148
Single quantized Q7 value/scalar.
Definition: aimath_q7.h:155
A tensor in AIfES.
Definition: aifes_math.h:89
Parameters
*xQ7 tensor to calculate the ELU from (N-D tensor) (quantization parameters const in PROGMEM)
*alphaScalar \( \alpha \) (type aiscalar_q7_t)
*resultResulting Q7 tensor (N-D tensor)

◆ aimath_q7_avr_pgm_leaky_relu()

void aimath_q7_avr_pgm_leaky_relu ( const aitensor_t x,
const void *  alpha,
aitensor_t result 
)

Calculates the leaky rectifier (leaky ReLU) value of each element in a Q7 tensor.

The quantization parameters x must be defined constant in PROGMEM.

This function wraps the function aimath_f32_default_leaky_relu() internally.

\[ result_{i} = \begin{cases} \alpha \cdot x_i & \text{if } x_i < 0 \\ x_i & \text{if } x_i \geq 0 \end{cases} \]

The quantization parameters of the result tensor are set to {shift = x.shift, zero_point = x.zero_point} by the function because the output values are in the interval (alpha * min(x), max(x)].

Example:

uint16_t x_shape[2] = {2, 3};
const aimath_q7_params_t x_params PROGMEM = {6, 0}; // {shift, zero point}
int8_t x_data[2*3] = { 2, -4, 6,
-8, 10, -12};
aitensor_t x = AITENSOR_2D_Q7(x_shape, &x_params, x_data);
aiscalar_q7_t alpha = AISCALAR_Q7(0.01f, 10, 0);
uint16_t result_shape[2] = {2, 3};
aimath_q7_params_t result_params; // {shift, zero point}
int8_t result_data[2*3];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
aimath_q7_avr_pgm_leaky_relu(&x, &alpha, &result);
print_aitensor(&result);
void aimath_q7_avr_pgm_leaky_relu(const aitensor_t *x, const void *alpha, aitensor_t *result)
Calculates the leaky rectifier (leaky ReLU) value of each element in a Q7 tensor.
Parameters
*xQ7 tensor to calculate the leaky ReLU from (N-D tensor) (quantization parameters const in PROGMEM)
*alphaScalar \( \alpha \) (type aiscalar_q7_t) for the leakage
*resultResulting Q7 tensor (N-D tensor)

◆ aimath_q7_avr_pgm_linear32_1()

void aimath_q7_avr_pgm_linear32_1 ( const aitensor_t a,
const aitensor_t b,
const aitensor_t c,
aitensor_t result 
)

Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row.

The data of b and c and the quantization parameters of a, b, c and result must be defined constant in PROGMEM.

Same functionality as aimath_f32_default_linear32().

The addition of the horizontal vector c is performed via broadcast, i.e. element wise in each column Mathematically this broadcast is equal to multiplying c with an vertical vector (with the same number of elements as c) and adding the result to a * b.

** The quantization parameters of the vector c have to be {zero_point = 0, shift = a.shift + b.shift}! **

\[ result = a \cdot b + \left( \begin{array}{c} 1 \\ 1 \\ \vdots \\ 1 \\ \end{array}\right) \cdot c \]

Example:

uint16_t a_shape[2] = {3, 3};
const aimath_q7_params_t a_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t a_data[3*3] = { 2, 4, 6,
8, 10, 12,
14, 16, 18};
aitensor_t a = AITENSOR_2D_Q7(a_shape, &a_params, a_data);
uint16_t b_shape[2] = {3, 2};
const aimath_q7_params_t b_params PROGMEM = {2, 0}; // {shift, zero point}
const int8_t b_data[3*2] PROGMEM = {4, 0,
0, 4,
0, 0};
aitensor_t b = AITENSOR_2D_Q7(b_shape, &b_params, b_data);
uint16_t c_shape[2] = {1, 2};
const aimath_q31_params_t c_params PROGMEM = {3, 0}; // {shift, zero point}
const int32_t c_data[1*2] PROGMEM = {16, 40};
aitensor_t c = AITENSOR_2D_Q31(c_shape, &c_params, c_data);
uint16_t result_shape[2] = {3, 2};
const aimath_q7_params_t result_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t result_data[3*2];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
aimath_q7_avr_pgm_linear32_1(&a, &b, &c, &result);
void aimath_q7_avr_pgm_linear32_1(const aitensor_t *a, const aitensor_t *b, const aitensor_t *c, aitensor_t *result)
Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row.
Parameters used for the quantized Q31 values, used as property of a tensor.
Definition: aimath_q31.h:149
Parameters
*aQ7 matrix a (2D tensor of shape [N x K]) (quantization parameters const in PROGMEM)
*bQ7 matrix b (2D tensor of shape [K x M]) (data and quantization parameters const in PROGMEM)
*cQ31 vector c (2D tensor of shape [1 x M]) (data and quantization parameters const in PROGMEM)
*resultResulting Q7 matrix (2D tensor of shape [N x M]) (quantization parameters const in PROGMEM)

◆ aimath_q7_avr_pgm_linear32_2()

void aimath_q7_avr_pgm_linear32_2 ( const aitensor_t a,
const aitensor_t b,
const aitensor_t c,
aitensor_t result 
)

Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row.

The data of b and c and the quantization parameters of b, c and result (not of a!) must be defined constant in PROGMEM.

Same functionality as aimath_f32_default_linear32().

The addition of the horizontal vector c is performed via broadcast, i.e. element wise in each column Mathematically this broadcast is equal to multiplying c with an vertical vector (with the same number of elements as c) and adding the result to a * b.

** The quantization parameters of the vector c have to be {zero_point = 0, shift = a.shift + b.shift}! **

\[ result = a \cdot b + \left( \begin{array}{c} 1 \\ 1 \\ \vdots \\ 1 \\ \end{array}\right) \cdot c \]

Example:

uint16_t a_shape[2] = {3, 3};
aimath_q7_params_t a_params = {1, 0}; // {shift, zero point}
int8_t a_data[3*3] = { 2, 4, 6,
8, 10, 12,
14, 16, 18};
aitensor_t a = AITENSOR_2D_Q7(a_shape, &a_params, a_data);
uint16_t b_shape[2] = {3, 2};
const aimath_q7_params_t b_params PROGMEM = {2, 0}; // {shift, zero point}
const int8_t b_data[3*2] PROGMEM = {4, 0,
0, 4,
0, 0};
aitensor_t b = AITENSOR_2D_Q7(b_shape, &b_params, b_data);
uint16_t c_shape[2] = {1, 2};
const aimath_q31_params_t c_params PROGMEM = {3, 0}; // {shift, zero point}
const int32_t c_data[1*2] PROGMEM = {16, 40};
aitensor_t c = AITENSOR_2D_Q31(c_shape, &c_params, c_data);
uint16_t result_shape[2] = {3, 2};
const aimath_q7_params_t result_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t result_data[3*2];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
aimath_q7_avr_pgm_linear32_2(&a, &b, &c, &result);
void aimath_q7_avr_pgm_linear32_2(const aitensor_t *a, const aitensor_t *b, const aitensor_t *c, aitensor_t *result)
Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row.
Parameters
*aQ7 matrix a (2D tensor of shape [N x K])
*bQ7 matrix b (2D tensor of shape [K x M]) (data and quantization parameters const in PROGMEM)
*cQ31 vector c (2D tensor of shape [1 x M]) (data and quantization parameters const in PROGMEM)
*resultResulting Q7 matrix (2D tensor of shape [N x M]) (quantization parameters const in PROGMEM)

◆ aimath_q7_avr_pgm_linear32_bt_1()

void aimath_q7_avr_pgm_linear32_bt_1 ( const aitensor_t a,
const aitensor_t b,
const aitensor_t c,
aitensor_t result 
)

Performs a matrix multiplication of Q7 matrices a and b (transposed) and adds a Q31 vector c to each row.

The data of b and c and the quantization parameters of a, b, c and result must be defined constant in PROGMEM.

The b matrix has to be transposed.

Same functionality as aimath_f32_default_linear32_bt().

The addition of the horizontal vector c is performed via broadcast, i.e. element wise in each column Mathematically this broadcast is equal to multiplying c with an vertical vector (with the same number of elements as c) and adding the result to \( a * b^T \).

The quantization parameters of the vector c have to be {zero_point = 0, shift = a.shift + b.shift}!

\[ result = a \cdot b^T + \left( \begin{array}{c} 1 \\ 1 \\ \vdots \\ 1 \\ \end{array}\right) \cdot c \]

Example:

uint16_t a_shape[2] = {3, 3};
const aimath_q7_params_t a_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t a_data[3*3] = { 2, 4, 6,
8, 10, 12,
14, 16, 18};
aitensor_t a = AITENSOR_2D_Q7(a_shape, &a_params, a_data);
uint16_t b_shape[2] = {2, 3};
const aimath_q7_params_t b_params PROGMEM = {2, 0}; // {shift, zero point}
const int8_t b_data[2*3] PROGMEM = {4, 0, 0,
0, 4, 0};
aitensor_t b = AITENSOR_2D_Q7(b_shape, &b_params, b_data);
uint16_t c_shape[2] = {1, 2};
const aimath_q31_params_t c_params PROGMEM = {3, 0}; // {shift, zero point}
const int32_t c_data[1*2] PROGMEM = {16, 40};
aitensor_t c = AITENSOR_2D_Q31(c_shape, &c_params, c_data);
uint16_t result_shape[2] = {3, 2};
const aimath_q7_params_t result_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t result_data[3*2];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
aimath_q7_avr_pgm_linear32_bt_1(&a, &b, &c, &result);
void aimath_q7_avr_pgm_linear32_bt_1(const aitensor_t *a, const aitensor_t *b, const aitensor_t *c, aitensor_t *result)
Performs a matrix multiplication of Q7 matrices a and b (transposed) and adds a Q31 vector c to eac...
Parameters
*aQ7 matrix a (2D tensor of shape [N x K]) (quantization parameters const in PROGMEM)
*bQ7 matrix b (2D tensor of shape [M x K]) (data and quantization parameters const in PROGMEM)
*cQ31 vector c (2D tensor of shape [1 x M]) (data and quantization parameters const in PROGMEM)
*resultResulting Q7 matrix (2D tensor of shape [N x M]) (quantization parameters const in PROGMEM)

◆ aimath_q7_avr_pgm_linear32_bt_2()

void aimath_q7_avr_pgm_linear32_bt_2 ( const aitensor_t a,
const aitensor_t b,
const aitensor_t c,
aitensor_t result 
)

Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row.

The data of b and c and the quantization parameters of b, c and result (not of a!) must be defined constant in PROGMEM.

The b matrix has to be transposed.

Same functionality as aimath_f32_default_linear32().

The addition of the horizontal vector c is performed via broadcast, i.e. element wise in each column Mathematically this broadcast is equal to multiplying c with an vertical vector (with the same number of elements as c) and adding the result to \( a * b^T \).

** The quantization parameters of the vector c have to be {zero_point = 0, shift = a.shift + b.shift}! **

\[ result = a \cdot b^T + \left( \begin{array}{c} 1 \\ 1 \\ \vdots \\ 1 \\ \end{array}\right) \cdot c \]

Example:

uint16_t a_shape[2] = {3, 3};
aimath_q7_params_t a_params = {1, 0}; // {shift, zero point}
int8_t a_data[3*3] = { 2, 4, 6,
8, 10, 12,
14, 16, 18};
aitensor_t a = AITENSOR_2D_Q7(a_shape, &a_params, a_data);
uint16_t b_shape[2] = {2, 3};
const aimath_q7_params_t b_params PROGMEM = {2, 0}; // {shift, zero point}
const int8_t b_data[2*3] PROGMEM = {4, 0, 0,
0, 4, 0};
aitensor_t b = AITENSOR_2D_Q7(b_shape, &b_params, b_data);
uint16_t c_shape[2] = {1, 2};
const aimath_q31_params_t c_params PROGMEM = {3, 0}; // {shift, zero point}
const int32_t c_data[1*2] PROGMEM = {16, 40};
aitensor_t c = AITENSOR_2D_Q31(c_shape, &c_params, c_data);
uint16_t result_shape[2] = {3, 2};
const aimath_q7_params_t result_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t result_data[3*2];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
aimath_q7_avr_pgm_linear32_bt_2(&a, &b, &c, &result);
void aimath_q7_avr_pgm_linear32_bt_2(const aitensor_t *a, const aitensor_t *b, const aitensor_t *c, aitensor_t *result)
Performs a matrix multiplication of Q7 matrices a and b and adds a Q31 vector c to each row.
Parameters
*aQ7 matrix a (2D tensor of shape [N x K])
*bQ7 matrix b (2D tensor of shape [M x K]) (data and quantization parameters const in PROGMEM)
*cQ31 vector c (2D tensor of shape [1 x M]) (data and quantization parameters const in PROGMEM)
*resultResulting Q7 matrix (2D tensor of shape [N x M]) (quantization parameters const in PROGMEM)

◆ aimath_q7_avr_pgm_relu()

void aimath_q7_avr_pgm_relu ( const aitensor_t x,
aitensor_t result 
)

Calculates the rectifier (ReLU) value of each element in a Q7 tensor.

The quantization parameters x must be defined constant in PROGMEM.

This function wraps the function aimath_f32_default_relu() internally.

\[ result_{i} = max(0, x_{i}) \]

The quantization parameters of the result tensor are set to {shift = x.shift, zero_point = x.zero_point} by the function because the output values are in the interval [0, max(x)].

Example:

uint16_t x_shape[2] = {2, 3};
const aimath_q7_params_t x_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t x_data[2*3] = { 2, -4, 6,
-8, 10, -12};
aitensor_t x = AITENSOR_2D_Q7(x_shape, &x_params, x_data);
uint16_t result_shape[2] = {2, 3};
aimath_q7_params_t result_params;
int8_t result_data[2*3];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
print_aitensor(&result);
void aimath_q7_avr_pgm_relu(const aitensor_t *x, aitensor_t *result)
Calculates the rectifier (ReLU) value of each element in a Q7 tensor.
Parameters
*xQ7 tensor to calculate the ReLU from (N-D tensor) (quantization parameters const in PROGMEM)
*resultResulting Q7 tensor (N-D tensor)

◆ aimath_q7_avr_pgm_sigmoid()

void aimath_q7_avr_pgm_sigmoid ( const aitensor_t x,
aitensor_t result 
)

Calculates the sigmoid of each element in a Q7 tensor.

The quantization parameters x must be defined constant in PROGMEM.

This function wraps the function aimath_f32_default_sigmoid() internally.

\[ result_{i} = \sigma(x_{i}) = \frac{1}{1 + e^{-x_{i}}} \]

The sigmoid is calculated with a piecewise linear approximation (PLAN) to avoid using exponential functions.

\[ result_{i} = \sigma_{PLAN}(x_i) = \begin{cases} 1 & \text{if } 5 \leq x_i\\ 0.03125 \cdot |x_i| + 0.84375 & \text{if } 2.375 \leq x_i < 5\\ 0.0125 \cdot |x_i| + 0.625 & \text{if } 1 \leq x_i < 2.375\\ 0.25 \cdot |x_i| + 0.5 & \text{if } 0 \leq x_i < 1\\ 1 - \sigma_{PLAN}(- x_i) & \text{if } x_i < 0\\ \end{cases} \]

The quantization parameters of the result tensor are set to {shift = 8, zero_point = -2^7} by the function because the output values are in the interval (0, 1).

Example:

uint16_t x_shape[2] = {2, 3};
const aimath_q7_params_t x_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t x_data[2*3] = { 2, -4, 6,
-8, 10, -12};
aitensor_t x = AITENSOR_2D_Q7(x_shape, &x_params, x_data);
uint16_t result_shape[2] = {2, 3};
aimath_q7_params_t result_params;
int8_t result_data[2*3];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
print_aitensor(&result);
void aimath_q7_avr_pgm_sigmoid(const aitensor_t *x, aitensor_t *result)
Calculates the sigmoid of each element in a Q7 tensor.
See also
Sigmoid PLAN: https://www.researchgate.net/figure/Comparative-representation-of-the-sigmoid-function-and-PLAN-approximation_fig7_228618304
Parameters
*xQ7 tensor to calculate the sigmoid from (N-D tensor) (quantization parameters const in PROGMEM)
*resultResulting Q7 tensor (N-D tensor)

◆ aimath_q7_avr_pgm_softmax()

void aimath_q7_avr_pgm_softmax ( const aitensor_t x,
aitensor_t result 
)

Calculates the softmax value of each batch element (row) of a Q7 tensor.

The quantization parameters x must be defined constant in PROGMEM.

This function wraps the function aimath_f32_default_softmax() internally.

\[ result_{i} = \frac{e^{x_i}}{\sum_{j=1}^{K} e^{x_j}} \]

The quantization parameters of the result tensor are set to {shift = 8, zero_point = -128} by the function because the output values are in the interval (0, 1).

Example:

uint16_t x_shape[2] = {2, 3};
const aimath_q7_params_t x_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t x_data[2*3] = { 2, -4, 6,
-8, 10, -12};
aitensor_t x = AITENSOR_2D_Q7(x_shape, &x_params, x_data);
uint16_t result_shape[2] = {2, 3};
aimath_q7_params_t result_params;
int8_t result_data[2*3];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
print_aitensor(&result);
void aimath_q7_avr_pgm_softmax(const aitensor_t *x, aitensor_t *result)
Calculates the softmax value of each batch element (row) of a Q7 tensor.
Parameters
*xQ7 tensor to calculate the softmax from (N-D tensor) (quantization parameters const in PROGMEM)
*resultResulting Q7 tensor (N-D tensor)

◆ aimath_q7_avr_pgm_softsign()

void aimath_q7_avr_pgm_softsign ( const aitensor_t x,
aitensor_t result 
)

Calculates the softsign value of each element in a Q7 tensor.

The quantization parameters x must be defined constant in PROGMEM.

This function wraps the function aimath_f32_default_softsign() internally.

\[ result_{i} = \frac {x_i} {1 + |x_i|} \]

The quantization parameters of the result tensor are set to {shift = 7, zero_point = 0} by the function because the output values are in the interval (-1, 1).

Example:

uint16_t x_shape[2] = {2, 3};
const aimath_q7_params_t x_params PROGMEM = {1, 0}; // {shift, zero point}
int8_t x_data[2*3] = { 2, -4, 6,
-8, 10, -12};
aitensor_t x = AITENSOR_2D_Q7(x_shape, &x_params, x_data);
uint16_t result_shape[2] = {2, 3};
aimath_q7_params_t result_params;
int8_t result_data[2*3];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
print_aitensor(&result);
void aimath_q7_avr_pgm_softsign(const aitensor_t *x, aitensor_t *result)
Calculates the softsign value of each element in a Q7 tensor.
Parameters
*xQ7 tensor to calculate the softsign from (N-D tensor) (quantization parameters const in PROGMEM)
*resultResulting Q7 tensor (N-D tensor)

◆ aimath_q7_avr_pgm_tanh()

void aimath_q7_avr_pgm_tanh ( const aitensor_t x,
aitensor_t result 
)

Calculates the tanh of each element in a Q7 tensor.

The quantization parameters x must be defined constant in PROGMEM.

This function wraps the function aimath_f32_default_tanh() internally.

\[ result_{i} = \tanh(x_{i}) = \frac{e^{x_i} - e^{-x_i}}{e^{x_i} + e^{-x_i}} \]

The tanh is calculated with a piecewise linear approximation (PLA) to avoid using exponential functions.

\[ result_{i} = \tanh_{PLA}(x_i) = 2 \cdot \sigma(2x_i) - 1 = \begin{cases} 1 & \text{if } 5 \leq x_i\\ 0.0625 \cdot |x_i| + 0.6875 & \text{if } 2.375 \leq x_i < 5\\ 0.25 \cdot |x_i| + 0.25 & \text{if } 1 \leq x_i < 2.375\\ 0.5 \cdot |x_i| & \text{if } 0 \leq x_i < 1\\ - \tanh_{PLA}(- x_i) & \text{if } x_i < 0\\ \end{cases} \]

The quantization parameters of the result tensor are set to {shift = 7, zero_point = 0} by the function because the output values are in the interval (-1, 1).

Example:

uint16_t x_shape[2] = {2, 3};
const aimath_q7_params_t x_params PROGMEM= {1, 0}; // {shift, zero point}
int8_t x_data[2*3] = { 2, -4, 6,
-8, 10, -12};
aitensor_t x = AITENSOR_2D_Q7(x_shape, &x_params, x_data);
uint16_t result_shape[2] = {2, 3};
aimath_q7_params_t result_params;
int8_t result_data[2*3];
aitensor_t result = AITENSOR_2D_Q7(result_shape, &result_params, result_data);
print_aitensor(&result);
void aimath_q7_avr_pgm_tanh(const aitensor_t *x, aitensor_t *result)
Calculates the tanh of each element in a Q7 tensor.
See also
Sigmoid PLAN: https://www.researchgate.net/figure/Comparative-representation-of-the-sigmoid-function-and-PLAN-approximation_fig7_228618304
Parameters
*xQ7 tensor to calculate the tanh from (N-D tensor) (quantization parameters const in PROGMEM)
*resultResulting Q7 tensor (N-D tensor)