//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from ARM Limited
// (C) COPYRIGHT 2021-2023 ARM Limited
// ALL RIGHTS RESERVED
// The entire notice above must be reproduced on all authorised
// copies and copies may only be made to the extent permitted
// by a licensing agreement from ARM Limited.

== TOSA Pseudocode

The TOSA pseudocode provides precise descriptions of TOSA operations.
Each operator contains pseudocode describing the operator's functionality.
This section contains pseudocode functions shared across multiple operators in the specification.

=== Operator Validation Helpers

The following functions are used to define the valid conditions for TOSA operators.

The REQUIRE function defines the conditions required by the TOSA operator.
If the conditions are not met then the result of the TOSA graph is marked as unpredictable.
Once the tosa_graph_result is set to tosa_unpredictable, the whole graph is considered unpredictable.

The ERROR_IF function defines a condition that must set an error if the condition holds and the graph is not unpredictable.
Note that if a graph contains both unpredictable and error statements then result of tosa_execute_graph() is tosa_unpredictable.
This condition is captured in the ERROR_IF function.

*Implementation Notes*

* An implementation is not required to detect unpredictable behavior. If tosa_execute_graph() returns tosa_unpredictable then the tosa_test_compliance() function does not require any specific output from an implementation.
* An implementation is required to detect errors in a graph that does not have unpredictable behavior (see tosa_test_compliance).
* An acceptable implementation is to stop and report an error on the first ERROR_IF condition that occurs. This satifies tosa_test_compliance() even if the tosa_execute_graph() was tosa_unpredictable.
* If the tosa_execute_graphs() result is tosa_unpredictable or tosa_error, then there is no requirement on the implementation to execute any portion of the TOSA graph.

[source,c++]
----
void REQUIRE(condition) {
    // Unpredictable overrides any previous result
    if (!(condition)) {
        tosa_graph_result = tosa_unpredictable;
    }
}

void ERROR_IF(condition) {
    // Error encodes a predictable error state and so is not registered
    // if the graph is marked as unpredictable.
    if (tosa_graph_result != tosa_unpredictable && condition) {
        tosa_graph_result = tosa_error;
    }
}

void LEVEL_CHECK(condition) {
    // If a level is specified and the level condition fails then
    // the result is unpredictable.
    REQUIRE(condition);
}
----

=== Tensor Access Helpers

==== Tensor Utilities

[source,c++]
----
include::{pseudocode}/library/tensor_utils.tosac[lines=10..-1]
----

==== Tensor Read

tensor_read reads a single data value out of the given tensor.
The shape argument contains the shape of the tensor.
Index is the coordinates within the tensor of the value to be read.

[source,c++]
----
include::{pseudocode}/library/tensor_read.tosac[lines=10..-1]
----

==== Tensor Write

tensor_write writes a single data value into the given tensor.
The shape argument contains the shape of the tensor.
Index is the coordinates within the tensor of the value to be written.
value is the value to be written to the given coordinate.

[source,c++]
----
include::{pseudocode}/library/tensor_write.tosac[lines=10..-1]
----

==== Variable Tensor Allocate

variable_tensor_allocate allocates the mutable persistent memory block for storing variable tensors.
The shape argument contains the shape of the allocated memory block for the variable_tensor.
The uid argument is a globally unique identifier for variable tensors.

[source,c++]
----
include::{pseudocode}/library/variable_tensor_allocate.tosac[lines=10..-1]
----

==== Variable Tensor Lookup

variable_tensor_lookup checks whether a variable tensor has been allocated or not.
The uid argument is a globally unique identifier for variable tensors.

[source,c++]
----
include::{pseudocode}/library/variable_tensor_lookup.tosac[lines=10..-1]
----

==== Broadcast Helpers

The following function derives the broadcast output shape from the input shapes.

[source,c++]
----
include::{pseudocode}/library/broadcast_shape.tosac[lines=10..-1]
----

The following function maps an index in the output tensor to an index in the input tensor.

[source,c++]
----
include::{pseudocode}/library/apply_broadcast.tosac[lines=10..-1]
----

=== General Pseudocode Helpers

This section contains general pseudocode utility functions used throughout the specification.

==== Arithmetic Helpers

The following functions provide arithmetic while defining requirements such that values stay in the valid range.

[source,c++]
----
include::{pseudocode}/library/arithmetic_helpers.tosac[lines=10..-1]
----

==== Type Conversion Helpers

The following definitions indicate the type to be used when the given parameters are provided.

[source,c++]
----
include::{pseudocode}/library/type_conversion_helpers.tosac[lines=10..-1]
----

==== Numeric Accuracy Helpers

For a floating point number of type in_t a normal value is of the form (1.x * 2^e).
The fractional part 'x' has a number of fractional or mantissa bits depending on the type.
The exponent 'e' has a normal range depending on the type.
The functions below return the ranges according to type.

[source,c++]
----
include::{pseudocode}/library/numeric_accuracy_helpers.tosac[lines=10..-1]
----

The following functions check if a test value in floating-point format in_t is within an error range compared to a reference value.
The functions assume that denormal values may be flushed to zero.
For the first function, the permitted error range is specified as num_ulp which is converted to an error bound as specified by the code.
For the second function, the permitted error range is specified as an absolute error bound.

[source,c++]
----
include::{pseudocode}/library/tosa_reference_check.tosac[lines=10..-1]
----

==== Numeric Conversion Helpers

The following definitions are used in pseudocode to do numeric conversions.
Where the *float_t* type is used, it represents all of the floating-point data types supported by the given profile.
See <<Number formats>> for details on the floating-point formats.

[source,c++]
----
include::{pseudocode}/library/numeric_conversion_helpers.tosac[lines=10..-1]
----

The following definition is used to flatten a list of lists into a single list.

[source,c++]
----
include::{pseudocode}/library/flatten.tosac[lines=10..-1]
----

Generic helper functions used to keep the pseudocode concise.

[source,c++]
----
include::{pseudocode}/library/generic_helpers.tosac[lines=10..-1]
----
