diff options
Diffstat (limited to 'reference_model')
-rw-r--r-- | reference_model/CMakeLists.txt | 2 | ||||
-rw-r--r-- | reference_model/src/generate/generate_dot_product.cc | 5 | ||||
-rw-r--r-- | reference_model/src/generate/generate_dot_product.h | 2 | ||||
-rw-r--r-- | reference_model/src/generate/generate_entry.cc | 5 | ||||
-rw-r--r-- | reference_model/src/generate/generate_pseudo_random.cc | 103 | ||||
-rw-r--r-- | reference_model/src/generate/generate_pseudo_random.h | 34 | ||||
-rw-r--r-- | reference_model/src/generate/generate_utils.cc | 11 | ||||
-rw-r--r-- | reference_model/src/generate/generate_utils.h | 10 | ||||
-rw-r--r-- | reference_model/src/verify/verify_exact.cc | 19 | ||||
-rw-r--r-- | reference_model/test/generate_tests.cpp | 79 | ||||
-rw-r--r-- | reference_model/test/verify_tests.cpp | 19 |
11 files changed, 273 insertions, 16 deletions
diff --git a/reference_model/CMakeLists.txt b/reference_model/CMakeLists.txt index 5be6f8f..24467c8 100644 --- a/reference_model/CMakeLists.txt +++ b/reference_model/CMakeLists.txt @@ -73,6 +73,7 @@ set(CXX_SOURCE src/tensor.cc src/generate/generate_dot_product_states.cc src/generate/generate_dot_product.cc + src/generate/generate_pseudo_random.cc src/generate/generate_entry.cc src/generate/generate_utils.cc src/verify/verify_dot_product.cc @@ -167,6 +168,7 @@ target_include_directories(tosa_reference_verify_lib add_library(tosa_reference_generate_lib SHARED src/generate/generate_dot_product_states.cc src/generate/generate_dot_product.cc + src/generate/generate_pseudo_random.cc src/generate/generate_entry.cc src/generate/generate_utils.cc src/generate/generate_config.cc diff --git a/reference_model/src/generate/generate_dot_product.cc b/reference_model/src/generate/generate_dot_product.cc index 1d2325f..cbfac4b 100644 --- a/reference_model/src/generate/generate_dot_product.cc +++ b/reference_model/src/generate/generate_dot_product.cc @@ -56,6 +56,11 @@ bool generateMatMul(const TosaReference::GenerateConfig& cfg, void* data, size_t size) { + if (cfg.dataType != DType::DType_FP32) + { + WARNING("[Generator][DP][MatMul] Only supports FP32."); + return false; + } if (cfg.shape.size() != 3) { WARNING("[Generator][DP][MatMul] Tensor shape expected 3 dimensions."); diff --git a/reference_model/src/generate/generate_dot_product.h b/reference_model/src/generate/generate_dot_product.h index 236f577..cd9d4ba 100644 --- a/reference_model/src/generate/generate_dot_product.h +++ b/reference_model/src/generate/generate_dot_product.h @@ -37,7 +37,7 @@ std::unique_ptr<IDotProductGenerator> pickDotProductGenerator(const GenerateConf /// /// \param cfg Generator related meta-data /// \param data Buffer to generate the data to -/// \param size Size of the buffet +/// \param size Size of the buffer /// /// \return True on successful generation bool generateDotProduct(const GenerateConfig& cfg, void* data, size_t size); diff --git a/reference_model/src/generate/generate_entry.cc b/reference_model/src/generate/generate_entry.cc index e7a0044..741cd79 100644 --- a/reference_model/src/generate/generate_entry.cc +++ b/reference_model/src/generate/generate_entry.cc @@ -15,6 +15,7 @@ #include "generate.h" #include "generate_dot_product.h" +#include "generate_pseudo_random.h" #include "generate_utils.h" #include "func_debug.h" @@ -31,6 +32,10 @@ bool generate(const GenerateConfig& cfg, void* data, size_t size) return generateDotProduct(cfg, data, size); break; } + case GeneratorType::PseudoRandom: { + return generatePseudoRandom(cfg, data, size); + break; + } default: { WARNING("[Generator] Unsupported generation mode."); break; diff --git a/reference_model/src/generate/generate_pseudo_random.cc b/reference_model/src/generate/generate_pseudo_random.cc new file mode 100644 index 0000000..858a4b2 --- /dev/null +++ b/reference_model/src/generate/generate_pseudo_random.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2023, ARM Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "generate.h" +#include "generate_utils.h" + +#include <array> +#include <iterator> +#include <limits> +#include <numeric> +#include <random> +#include <string> +#include <type_traits> +#include <vector> + +namespace +{ + +// Random generator +template <typename FP> +class PseudoRandomGeneratorFloat +{ +public: + PseudoRandomGeneratorFloat(uint64_t seed) + : _gen(seed) + { + // Uniform real distribution generates real values in the range [a, b] + // and requires that b - a <= std::numeric_limits<FP>::max() so here + // we choose some arbitrary values that satisfy that condition. + constexpr auto min = std::numeric_limits<FP>::lowest() / 2; + constexpr auto max = std::numeric_limits<FP>::max() / 2; + static_assert(max <= std::numeric_limits<FP>::max() + min); + _unidis = std::uniform_real_distribution<FP>(min, max); + + // Piecewise Constant distribution + const std::array<double, 7> intervals{ min, min + 1000, -1000.0, 0.0, 1000.0, max - 1000, max }; + const std::array<double, 7> weights{ 1.0, 0.1, 1.0, 2.0, 1.0, 0.1, 1.0 }; + _pwcdis = std::piecewise_constant_distribution<FP>(intervals.begin(), intervals.end(), weights.begin()); + } + + FP getRandomUniformFloat() + { + return _unidis(_gen); + } + + FP getRandomPWCFloat() + { + return _pwcdis(_gen); + } + +private: + std::mt19937 _gen; + std::uniform_real_distribution<FP> _unidis; + std::piecewise_constant_distribution<FP> _pwcdis; +}; + +bool generateFP32(const TosaReference::GenerateConfig& cfg, void* data, size_t size) +{ + const TosaReference::PseudoRandomInfo& prinfo = cfg.pseudoRandomInfo; + PseudoRandomGeneratorFloat<float> generator(prinfo.rngSeed); + + float* a = reinterpret_cast<float*>(data); + const auto T = TosaReference::numElementsFromShape(cfg.shape); + for (auto t = 0; t < T; ++t) + { + a[t] = generator.getRandomPWCFloat(); + } + return true; +} + +} // namespace + +namespace TosaReference +{ +bool generatePseudoRandom(const GenerateConfig& cfg, void* data, size_t size) +{ + // Check we support the operator + if (cfg.opType == Op::Op_UNKNOWN) + { + WARNING("[Generator][PR] Unknown operator."); + return false; + } + + switch (cfg.dataType) + { + case DType::DType_FP32: + return generateFP32(cfg, data, size); + default: + WARNING("[Generator][PR] Unsupported type."); + return false; + } +} +} // namespace TosaReference diff --git a/reference_model/src/generate/generate_pseudo_random.h b/reference_model/src/generate/generate_pseudo_random.h new file mode 100644 index 0000000..6796d20 --- /dev/null +++ b/reference_model/src/generate/generate_pseudo_random.h @@ -0,0 +1,34 @@ +// Copyright (c) 2023, ARM Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GENERATE_PSEUDO_RANDOM_H_ +#define GENERATE_PSEUDO_RANDOM_H_ + +#include "generate_utils.h" + +namespace TosaReference +{ + +/// \brief Perform pseudo random based generation +/// +/// \param cfg Generator related meta-data +/// \param data Buffer to generate the data to +/// \param size Size of the buffer +/// +/// \return True on successful generation +bool generatePseudoRandom(const GenerateConfig& cfg, void* data, size_t size); + +}; // namespace TosaReference + +#endif // GENERATE_PSEUDO_RANDOM_H_ diff --git a/reference_model/src/generate/generate_utils.cc b/reference_model/src/generate/generate_utils.cc index da16632..bcbf9d7 100644 --- a/reference_model/src/generate/generate_utils.cc +++ b/reference_model/src/generate/generate_utils.cc @@ -39,6 +39,8 @@ NLOHMANN_JSON_SERIALIZE_ENUM(Op, { { Op::Op_UNKNOWN, "UNKNOWN" }, { Op::Op_MATMUL, "MATMUL" }, + { Op::Op_MAX_POOL2D, "MAX_POOL2D" }, + { Op::Op_PAD, "PAD" }, }) } // namespace tosa @@ -78,6 +80,11 @@ void from_json(const nlohmann::json& j, DotProductInfo& dotProductInfo) } } +void from_json(const nlohmann::json& j, PseudoRandomInfo& pseudoRandomInfo) +{ + j.at("rng_seed").get_to(pseudoRandomInfo.rngSeed); +} + void from_json(const nlohmann::json& j, GenerateConfig& cfg) { j.at("data_type").get_to(cfg.dataType); @@ -90,6 +97,10 @@ void from_json(const nlohmann::json& j, GenerateConfig& cfg) { j.at("dot_product_info").get_to(cfg.dotProductInfo); } + if (j.contains("pseudo_random_info")) + { + j.at("pseudo_random_info").get_to(cfg.pseudoRandomInfo); + } } std::optional<GenerateConfig> parseGenerateConfig(const char* json, const char* tensorName) diff --git a/reference_model/src/generate/generate_utils.h b/reference_model/src/generate/generate_utils.h index e8e67bb..0239e98 100644 --- a/reference_model/src/generate/generate_utils.h +++ b/reference_model/src/generate/generate_utils.h @@ -55,6 +55,15 @@ struct DotProductInfo std::array<int32_t, 2> kernel; }; +/// \brief Pseudo random generator meta-data +struct PseudoRandomInfo +{ + PseudoRandomInfo() = default; + + int64_t rngSeed; + // TODO: Add range support +}; + /// \brief Generator configuration struct GenerateConfig { @@ -65,6 +74,7 @@ struct GenerateConfig int32_t inputPos; tosa::Op opType; DotProductInfo dotProductInfo; + PseudoRandomInfo pseudoRandomInfo; }; /// \brief Parse the generator config when given in JSON form diff --git a/reference_model/src/verify/verify_exact.cc b/reference_model/src/verify/verify_exact.cc index 4d6c72f..36b4ec9 100644 --- a/reference_model/src/verify/verify_exact.cc +++ b/reference_model/src/verify/verify_exact.cc @@ -16,6 +16,14 @@ #include "verifiers.h" #include <cmath> +namespace +{ +bool exact_fp32(const double& referenceValue, const float& implementationValue) +{ + return std::isnan(referenceValue) ? std::isnan(implementationValue) : (referenceValue == implementationValue); +} +} // namespace + namespace TosaReference { @@ -33,15 +41,14 @@ bool verifyExact(const CTensor* referenceTensor, const CTensor* implementationTe switch (implementationTensor->data_type) { case tosa_datatype_fp32_t: { - const auto* refData = reinterpret_cast<const float*>(referenceTensor->data); + TOSA_REF_REQUIRE(referenceTensor->data_type == tosa_datatype_fp64_t, "[E] Reference tensor is not fp64"); + const auto* refData = reinterpret_cast<const double*>(referenceTensor->data); TOSA_REF_REQUIRE(refData != nullptr, "[E] Missing data for reference"); const auto* impData = reinterpret_cast<const float*>(implementationTensor->data); TOSA_REF_REQUIRE(impData != nullptr, "[E] Missing data for implementation"); - return std::equal(refData, std::next(refData, elementCount), impData, std::next(impData, elementCount), - [](const auto& referenceValue, const auto& implementationValue) { - return std::isnan(referenceValue) ? std::isnan(implementationValue) - : (referenceValue == implementationValue); - }); + auto result = std::equal(refData, std::next(refData, elementCount), impData, + std::next(impData, elementCount), exact_fp32); + return result; } default: WARNING("[Verifier][E] Data-type not supported."); diff --git a/reference_model/test/generate_tests.cpp b/reference_model/test/generate_tests.cpp index 503ecfe..c24a369 100644 --- a/reference_model/test/generate_tests.cpp +++ b/reference_model/test/generate_tests.cpp @@ -56,6 +56,24 @@ void check_output(const std::vector<T>& results, const std::vector<uint32_t>& ex } } +template <typename T> +void check_output(const std::vector<T>& results, const std::vector<T>& expected) +{ + for (size_t idx = 0; idx < expected.size(); ++idx) + { + check_value(true, *(uint32_t*)&results[idx], *(uint32_t*)&expected[idx], idx); + } +} + +template <typename T> +void check_not_output(const std::vector<T>& results, const std::vector<T>& expected) +{ + for (size_t idx = 0; idx < expected.size(); ++idx) + { + check_value(false, *(uint32_t*)&results[idx], *(uint32_t*)&expected[idx], idx); + } +} + } // namespace TEST_SUITE_BEGIN("generate"); @@ -268,4 +286,65 @@ TEST_CASE("positive - FP32 matmul dot product (first 3 values)") matmul_test_FP32(tosaName, tosaElements, templateJsonCfg, "5", 1, expected); } } +TEST_CASE("positive - pseudo random") +{ + std::string templateJsonCfg = R"({ + "tensors" : { + "input0" : { + "generator": "PSEUDO_RANDOM", + "data_type": "FP32", + "input_type": "VARIABLE", + "shape" : [ 12, 3 ], + "input_pos": 0, + "op" : "PAD", + "pseudo_random_info": { + "rng_seed": _SEED0_ + } + }, + "input1" : { + "generator": "PSEUDO_RANDOM", + "data_type": "FP32", + "input_type": "VARIABLE", + "shape" : [ 1, 3 ], + "input_pos": 1, + "op" : "PAD", + "pseudo_random_info": { + "rng_seed": _SEED1_ + } + } + + } + })"; + + const std::string tosaNameP0 = "input0"; + const size_t tosaElementsP0 = 12 * 3; + const std::string tosaNameP1 = "input1"; + const size_t tosaElementsP1 = 1 * 3; + + SUBCASE("pad - same rng") + { + std::string jsonCfg = templateJsonCfg; + update_json_template(jsonCfg, "_SEED0_", "0"); + update_json_template(jsonCfg, "_SEED1_", "0"); + + std::vector<float> bufferP0(tosaElementsP0); + std::vector<float> bufferP1(tosaElementsP1); + REQUIRE(tgd_generate_data(jsonCfg.c_str(), tosaNameP0.c_str(), (void*)bufferP0.data(), tosaElementsP0 * 4)); + REQUIRE(tgd_generate_data(jsonCfg.c_str(), tosaNameP1.c_str(), (void*)bufferP1.data(), tosaElementsP1 * 4)); + check_output<float>(bufferP0, bufferP1); + } + + SUBCASE("pad - different rng") + { + std::string jsonCfg = templateJsonCfg; + update_json_template(jsonCfg, "_SEED0_", "0"); + update_json_template(jsonCfg, "_SEED1_", "1000"); + + std::vector<float> bufferP0(tosaElementsP0); + std::vector<float> bufferP1(tosaElementsP1); + REQUIRE(tgd_generate_data(jsonCfg.c_str(), tosaNameP0.c_str(), (void*)bufferP0.data(), tosaElementsP0 * 4)); + REQUIRE(tgd_generate_data(jsonCfg.c_str(), tosaNameP1.c_str(), (void*)bufferP1.data(), tosaElementsP1 * 4)); + check_not_output<float>(bufferP0, bufferP1); + } +} TEST_SUITE_END(); // generate diff --git a/reference_model/test/verify_tests.cpp b/reference_model/test/verify_tests.cpp index 3aa477f..369a8cd 100644 --- a/reference_model/test/verify_tests.cpp +++ b/reference_model/test/verify_tests.cpp @@ -75,7 +75,7 @@ template <typename FP> std::enable_if_t<std::is_floating_point_v<FP>, std::add_lvalue_reference_t<std::uniform_real_distribution<FP>>> getUniformRealDist() { - // Uniform real distribution generates real values in the range [a, b) + // Uniform real distribution generates real values in the range [a, b] // and requires that b - a <= std::numeric_limits<FP>::max() so here // we choose some arbitrary values that satisfy that condition. constexpr auto min = std::numeric_limits<FP>::lowest() / 2; @@ -261,13 +261,14 @@ TEST_CASE("positive - exact") const auto elementCount = std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<>()); // Generate some random floats using the full range of fp32. - auto data = generateRandomTensorData<float>(elementCount); + auto data_fp32 = generateRandomTensorData<float>(elementCount); + std::vector<double> data_fp64(data_fp32.begin(), data_fp32.end()); SUBCASE("same") { const auto referenceTensor = - TosaTensor("out1", tosa_datatype_fp64_t, shape, reinterpret_cast<uint8_t*>(data.data())); + TosaTensor("out1", tosa_datatype_fp64_t, shape, reinterpret_cast<uint8_t*>(data_fp64.data())); const auto implementationTensor = - TosaTensor("out1", tosa_datatype_fp32_t, shape, reinterpret_cast<uint8_t*>(data.data())); + TosaTensor("out1", tosa_datatype_fp32_t, shape, reinterpret_cast<uint8_t*>(data_fp32.data())); REQUIRE(tvf_verify_data(referenceTensor.cTensor(), nullptr, implementationTensor.cTensor(), jsonCfg.c_str())); } @@ -275,16 +276,16 @@ TEST_CASE("positive - exact") { // Generate some mismatched tensors by setting every other value to an incrementing counter. // In theory this could be the same, but the probability is tiny. - auto otherData = std::vector<float>(elementCount); - std::generate(std::begin(otherData), std::end(otherData), [&, i = 0]() mutable { + auto otherData_fp32 = std::vector<float>(elementCount); + std::generate(std::begin(otherData_fp32), std::end(otherData_fp32), [&, i = 0]() mutable { auto oldIndex = i++; - return oldIndex % 2 ? data[oldIndex] : static_cast<float>(oldIndex); + return oldIndex % 2 ? data_fp32[oldIndex] : static_cast<float>(oldIndex); }); const auto referenceTensor = - TosaTensor("out1", tosa_datatype_fp64_t, shape, reinterpret_cast<uint8_t*>(data.data())); + TosaTensor("out1", tosa_datatype_fp64_t, shape, reinterpret_cast<uint8_t*>(data_fp64.data())); const auto implementationTensor = - TosaTensor("out1", tosa_datatype_fp32_t, shape, reinterpret_cast<uint8_t*>(otherData.data())); + TosaTensor("out1", tosa_datatype_fp32_t, shape, reinterpret_cast<uint8_t*>(otherData_fp32.data())); REQUIRE_FALSE( tvf_verify_data(referenceTensor.cTensor(), nullptr, implementationTensor.cTensor(), jsonCfg.c_str())); } |