140 lines
5.1 KiB
C++
140 lines
5.1 KiB
C++
/*
|
|
* 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 <tvm/ffi/error.h>
|
|
|
|
#include <cstdint>
|
|
#include <random>
|
|
|
|
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_
|