/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ /*! * \file random_engine.h * \brief Random number generator. It provides a generic interface consistent with * `std::uniform_random_bit_generator` */ #ifndef TVM_S_TIR_RANDOM_ENGINE_H_ #define TVM_S_TIR_RANDOM_ENGINE_H_ #include #include #include namespace tvm { namespace s_tir { /*! * \brief This linear congruential engine is a drop-in replacement for std::minstd_rand. It strictly * corresponds to std::minstd_rand and is designed to be platform-independent. * \note Our linear congruential engine is a complete implementation of * std::uniform_random_bit_generator so it can be used as generator for any STL random number * distribution. However, parts of std::linear_congruential_engine's member functions are not * included for simplification. For full member functions of std::minstd_rand, please check out the * following link: https://en.cppreference.com/w/cpp/numeric/random/linear_congruential_engine */ class LinearCongruentialEngine { public: using TRandState = int64_t; /*! \brief The result type. */ using result_type = uint64_t; /*! \brief The multiplier */ static constexpr TRandState multiplier = 48271; /*! \brief The increment */ static constexpr TRandState increment = 0; /*! \brief The modulus */ static constexpr TRandState modulus = 2147483647; /*! \brief The minimum possible value of random state here. */ static constexpr result_type min() { return 0; } /*! \brief The maximum possible value of random state here. */ static constexpr result_type max() { return modulus - 1; } /*! * \brief Get a device random state * \return The random state */ static TRandState DeviceRandom() { std::random_device rd; return rd() % modulus; } /*! * \brief Operator to move the random state to the next and return the new random state. According * to definition of linear congruential engine, the new random state value is computed as * new_random_state = (current_random_state * multiplier + increment) % modulus. * \return The next current random state value in the type of result_type. * \note In order for better efficiency, the implementation here has a few assumptions: * 1. The multiplication and addition won't overflow. * 2. The given random state pointer `rand_state_ptr` is not nullptr. * 3. The given random state `*(rand_state_ptr)` is in the range of [0, modulus - 1]. */ result_type operator()() { (*rand_state_ptr_) = ((*rand_state_ptr_) * multiplier + increment) % modulus; return *rand_state_ptr_; } /*! * \brief Normalize the random seed to the range of [1, modulus - 1]. * \param rand_state The random seed. * \return The normalized random seed. */ static TRandState NormalizeSeed(TRandState rand_state) { if (rand_state == -1) { rand_state = DeviceRandom(); } else { rand_state %= modulus; } if (rand_state == 0) { rand_state = 1; } if (rand_state < 0) { TVM_FFI_THROW(ValueError) << "Random seed must be non-negative"; } return rand_state; } /*! * \brief Change the start random state of RNG with the seed of a new random state value. * \param rand_state The random state given in result_type. */ void Seed(TRandState rand_state) { TVM_FFI_ICHECK(rand_state_ptr_ != nullptr); *rand_state_ptr_ = NormalizeSeed(rand_state); } /*! * \brief Fork a new seed for another RNG from current random state. * \return The forked seed. */ TRandState ForkSeed() { // In order for reproducibility, we compute the new seed using RNG's random state and a // different set of parameters. Note that both 32767 and 1999999973 are prime numbers. return ((*this)() * 32767) % 1999999973; } /*! * \brief Construct a random number generator with a random state pointer. * \param rand_state_ptr The random state pointer given in result_type*. * \note The random state is not checked for whether it's nullptr and whether it's in the range of * [0, modulus-1]. We assume the given random state is valid or the Seed function would be * called right after the constructor before any usage. */ explicit LinearCongruentialEngine(TRandState* rand_state_ptr) { rand_state_ptr_ = rand_state_ptr; } private: TRandState* rand_state_ptr_; }; } // namespace s_tir } // namespace tvm #endif // TVM_S_TIR_RANDOM_ENGINE_H_