Program Listing for File RunPlanVector.h

Return to documentation for file (include/flamegpu/simulation/RunPlanVector.h)

#ifndef INCLUDE_FLAMEGPU_SIMULATION_RUNPLANVECTOR_H_
#define INCLUDE_FLAMEGPU_SIMULATION_RUNPLANVECTOR_H_

#include <random>
#include <vector>
#include <unordered_map>
#include <string>
#include <memory>
#include <limits>

#include "flamegpu/simulation/RunPlan.h"
#include "flamegpu/detail/StaticAssert.h"
#include "flamegpu/detail/type_decode.h"
#include "flamegpu/model/EnvironmentData.h"


namespace flamegpu {

class ModelDescription;
class EnvironmentDescription;

class RunPlanVector : private std::vector<RunPlan>  {
    friend class RunPlan;
    friend class detail::AbstractSimRunner;
    friend unsigned int CUDAEnsemble::simulate(const RunPlanVector& plans);

 public:
    explicit RunPlanVector(const ModelDescription &model, unsigned int initial_length);
    void setRandomSimulationSeed(uint64_t initial_seed, unsigned int step = 0);
    void setSteps(unsigned int steps);
    void setOutputSubdirectory(const std::string &subdir);
    template<typename T>
    void setProperty(const std::string &name, const T value);
    template<typename T, flamegpu::size_type N>
    void setProperty(const std::string &name, const std::array<T, N> &value);
    template<typename T>
    void setProperty(const std::string &name, const flamegpu::size_type index, const T value);
#ifdef SWIG
    template<typename T>
    void setPropertyArray(const std::string &name, const std::vector<T> &value);
#endif
    template<typename T>
    void setPropertyLerpRange(const std::string &name, T min, T max);
    template<typename T>
    void setPropertyLerpRange(const std::string &name, flamegpu::size_type index, T min, T max);
    template<typename T>
    void setPropertyStep(const std::string& name, T init, T step);
    template<typename T>
    void setPropertyStep(const std::string& name, flamegpu::size_type index, T init, T step);
    void setRandomPropertySeed(uint64_t seed);
    uint64_t getRandomPropertySeed();
    template<typename T>
    void setPropertyUniformRandom(const std::string &name, const T min, const T max);
    template<typename T>
    void setPropertyUniformRandom(const std::string &name, const flamegpu::size_type index, const T min, const T max);
    template<typename T>
    void setPropertyNormalRandom(const std::string &name, const T mean, const T stddev);
    template<typename T>
    void setPropertyNormalRandom(const std::string &name, flamegpu::size_type index, T mean, T stddev);
    template<typename T>
    void setPropertyLogNormalRandom(const std::string &name, T mean, T stddev);
    template<typename T>
    void setPropertyLogNormalRandom(const std::string &name, flamegpu::size_type index, T mean, T stddev);
    template<typename T, typename rand_dist>
    void setPropertyRandom(const std::string &name, rand_dist &distribution);
    template<typename T, typename rand_dist>
    void setPropertyRandom(const std::string &name, flamegpu::size_type index, rand_dist &distribution);

#ifndef SWIG
    using std::vector<RunPlan>::begin;
    using std::vector<RunPlan>::end;
    using std::vector<RunPlan>::size;
    using std::vector<RunPlan>::operator[];
    using std::vector<RunPlan>::insert;
    using std::vector<RunPlan>::at;
#else
    // Can't get SWIG %import to use std::vector<RunPlan> so manually implement the required items
    size_t size() const { return std::vector<RunPlan>::size(); }
    RunPlan& operator[] (const size_t _Pos) { return std::vector<RunPlan>::operator[](_Pos); }
#endif

    RunPlanVector operator+(const RunPlan& rhs) const;
    RunPlanVector operator+(const RunPlanVector& rhs) const;
    RunPlanVector& operator+=(const RunPlan& rhs);
    RunPlanVector& operator+=(const RunPlanVector& rhs);
    RunPlanVector& operator*=(unsigned int rhs);
    RunPlanVector operator*(unsigned int rhs) const;
    bool operator==(const RunPlanVector& rhs) const;
    bool operator!=(const RunPlanVector &rhs) const;

 private:
    RunPlanVector(const std::shared_ptr<const std::unordered_map<std::string, EnvironmentData::PropData>> &environment, bool allow_0_steps);
    uint64_t randomPropertySeed;
    std::mt19937_64 rand;
    std::shared_ptr<const std::unordered_map<std::string, EnvironmentData::PropData>> environment;
    const bool allow_0_steps;
};

template<typename T>
void RunPlanVector::setProperty(const std::string &name, const T value) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (it->second.data.elements != detail::type_decode<T>::len_t) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' is an array with %u elements, array method should be used, "
            "in RunPlanVector::setProperty()\n",
            name.c_str(), it->second.data.elements);
    }
    for (auto &i : *this) {
        i.setProperty<T>(name, value);
    }
}
template<typename T, flamegpu::size_type N>
void RunPlanVector::setProperty(const std::string &name, const std::array<T, N> &value) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (it->second.data.elements != N * detail::type_decode<T>::len_t) {
        THROW exception::InvalidEnvPropertyType("Environment property array '%s' length mismatch %u != %u "
            "in RunPlanVector::setProperty()\n",
            name.c_str(), it->second.data.elements, N * detail::type_decode<T>::len_t);
    }
    for (auto &i : *this) {
        i.setProperty<T, N>(name, value);
    }
}
template<typename T>
void RunPlanVector::setProperty(const std::string &name, const flamegpu::size_type index, const T value) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setProperty()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setProperty()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    const unsigned int t_index = detail::type_decode<T>::len_t * index + detail::type_decode<T>::len_t;
    if (t_index > it->second.data.elements || t_index < index) {
        THROW exception::OutOfBoundsException("Environment property array index out of bounds "
            "in RunPlanVector::setProperty()\n");
    }
    for (auto &i : *this) {
        i.setProperty<T>(name, index, value);
    }
}
#ifdef SWIG
template<typename T>
void RunPlanVector::setPropertyArray(const std::string &name, const std::vector<T> &value) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setPropertyArray()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setPropertyArray()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    }
    if (value.size() * detail::type_decode<T>::len_t != it->second.data.elements) {
        THROW exception::InvalidEnvProperty("Environment property array length does not match the value provided, %u != %llu,"
            "in RunPlanVector::setPropertyArray()\n",
            name.c_str(), value.size() * detail::type_decode<T>::len_t, it->second.data.elements);
    }
    for (auto &i : *this) {
        i.setPropertyArray<T>(name, value);
    }
}
#endif

template<typename T>
void RunPlanVector::setPropertyLerpRange(const std::string &name, const T min, const T max) {
    // Validation
    if (this->size() < 2) {
        THROW exception::OutOfBoundsException("Unable to apply a property distribution a vector with less than 2 elements, "
            "in RunPlanVector::setPropertyLerpRange()\n");
    }
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setPropertyLerpRange()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(T))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setPropertyLerpRange()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(T)).name());
    }
    if (it->second.data.elements != 1) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' is an array with %u elements, array method should be used, "
            "in RunPlanVector::setPropertyLerpRange()\n",
            name.c_str(), it->second.data.elements);
    }
    unsigned int ct = 0;
    for (auto &i : *this) {
        const double a = static_cast<double>(ct++) / (this->size() - 1);
        double lerp = min * (1.0 - a) + max * a;
        if (std::numeric_limits<T>::is_integer)
            lerp = round(lerp);
        const T lerp_t = static_cast<T>(lerp);
        i.setProperty<T>(name, lerp_t);
    }
}
template<typename T>
void RunPlanVector::setPropertyLerpRange(const std::string &name, const flamegpu::size_type index, const T min, const T max) {
    // Validation
    if (this->size() < 2) {
        THROW exception::OutOfBoundsException("Unable to apply a property distribution a vector with less than 2 elements, "
            "in RunPlanVector::setPropertyLerpRange()\n");
    }
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setPropertyLerpRange()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(T))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setPropertyLerpRange()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(T)).name());
    }
    const unsigned int t_index = detail::type_decode<T>::len_t * index + detail::type_decode<T>::len_t;
    if (t_index > it->second.data.elements || t_index < index) {
        THROW exception::OutOfBoundsException("Environment property array index out of bounds "
            "in RunPlanVector::setPropertyLerpRange()\n");
    }
    unsigned int ct = 0;
    for (auto &i : *this) {
        const double a = static_cast<double>(ct++) / (this->size() - 1);
        double lerp = min * (1.0 - a) + max * a;
        if (std::numeric_limits<T>::is_integer)
            lerp = round(lerp);
        const T lerp_t = static_cast<T>(lerp);
        i.setProperty<T>(name, index, lerp_t);
    }
}

template<typename T>
void RunPlanVector::setPropertyStep(const std::string& name, T init, const T step) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setPropertyStep()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(T))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setPropertyStep()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(T)).name());
    }
    if (it->second.data.elements != 1) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' is an array with %u elements, array method should be used, "
            "in RunPlanVector::setPropertyStep()\n",
            name.c_str(), it->second.data.elements);
    }
    for (auto& i : *this) {
        i.setProperty<T>(name, init);
        init += step;
    }
}
template<typename T>
void RunPlanVector::setPropertyStep(const std::string& name, const flamegpu::size_type index, T init, const T step) {
    // Validation
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setPropertyStep()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(T))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setPropertyStep()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(T)).name());
    }
    const unsigned int t_index = detail::type_decode<T>::len_t * index + detail::type_decode<T>::len_t;
    if (t_index > it->second.data.elements || t_index < index) {
        throw exception::OutOfBoundsException("Environment property array index out of bounds "
            "in RunPlanVector::setPropertyStep()\n");
    }
    for (auto& i : *this) {
        i.setProperty<T>(name, index, init);
        init += step;
    }
}

template<typename T, typename rand_dist>
void RunPlanVector::setPropertyRandom(const std::string &name, rand_dist &distribution) {
    // Validation
    if (this->size() < 2) {
        THROW exception::OutOfBoundsException("Unable to apply a property distribution a vector with less than 2 elements, "
            "in RunPlanVector::setPropertyRandom()\n");
    }
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setPropertyRandom()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(T))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setPropertyRandom()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(T)).name());
    }
    if (it->second.data.elements != 1) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' is an array with %u elements, array method should be used, "
            "in RunPlanVector::setPropertyRandom()\n",
            name.c_str(), it->second.data.elements);
    }
    for (auto &i : *this) {
        i.setProperty<T>(name, static_cast<T>(distribution(this->rand)));
    }
}
template<typename T, typename rand_dist>
void RunPlanVector::setPropertyRandom(const std::string &name, const flamegpu::size_type index, rand_dist &distribution) {
    // Validation
    if (this->size() < 2) {
        THROW exception::OutOfBoundsException("Unable to apply a property distribution a vector with less than 2 elements, "
            "in RunPlanVector::setPropertyRandom()\n");
    }
    const auto it = environment->find(name);
    if (it == environment->end()) {
        THROW exception::InvalidEnvProperty("Environment description does not contain property '%s', "
            "in RunPlanVector::setPropertyRandom()\n",
            name.c_str());
    }
    if (it->second.data.type != std::type_index(typeid(T))) {
        THROW exception::InvalidEnvPropertyType("Environment property '%s' type mismatch '%s' != '%s', "
            "in RunPlanVector::setPropertyRandom()\n",
            name.c_str(), it->second.data.type.name(), std::type_index(typeid(T)).name());
    }
    const unsigned int t_index = detail::type_decode<T>::len_t * index + detail::type_decode<T>::len_t;
    if (t_index > it->second.data.elements || t_index < index) {
        THROW exception::OutOfBoundsException("Environment property array index out of bounds "
            "in RunPlanVector::setPropertyRandom()\n");
    }
    for (auto &i : *this) {
        i.setProperty<T>(name, index, static_cast<T>(distribution(this->rand)));
    }
}
template<typename T>
void RunPlanVector::setPropertyUniformRandom(const std::string &name, const T min, const T max) {
    static_assert(detail::StaticAssert::_Is_IntType<T>::value, "Invalid template argument for RunPlanVector::setPropertyUniformRandom(const std::string &name, T min, T max)");
    std::uniform_int_distribution<T> dist(min, max);
    setPropertyRandom<T>(name, dist);
}
template<typename T>
void RunPlanVector::setPropertyUniformRandom(const std::string &name, const flamegpu::size_type index, const T min, const T max) {
    static_assert(detail::StaticAssert::_Is_IntType<T>::value, "Invalid template argument for RunPlanVector::setPropertyUniformRandom(const std::string &name, flamegpu::size_type index, T min, T max)");
    std::uniform_int_distribution<T> dist(min, max);
    setPropertyRandom<T>(name, index, dist);
}
template<typename T>
void RunPlanVector::setPropertyNormalRandom(const std::string &name, const T mean, const T stddev) {
    static_assert(detail::StaticAssert::_Is_RealType<T>::value, "Invalid template argument for RunPlanVector::setPropertyNormalRandom(const std::string &name, T mean, T stddev)");
    std::normal_distribution<T> dist(mean, stddev);
    setPropertyRandom<T>(name, dist);
}
template<typename T>
void RunPlanVector::setPropertyNormalRandom(const std::string &name, const flamegpu::size_type index, const T mean, const T stddev) {
    static_assert(detail::StaticAssert::_Is_RealType<T>::value,
        "Invalid template argument for RunPlanVector::setPropertyNormalRandom(const std::string &name, flamegpu::size_type index, T mean, T stddev)");
    std::normal_distribution<T> dist(mean, stddev);
    setPropertyRandom<T>(name, index, dist);
}
template<typename T>
void RunPlanVector::setPropertyLogNormalRandom(const std::string &name, const T mean, const T stddev) {
    static_assert(detail::StaticAssert::_Is_RealType<T>::value,
    "Invalid template argument for RunPlanVector::setPropertyLogNormalRandom(const std::string &name, T mean, T stddev)");
    std::lognormal_distribution<T> dist(mean, stddev);
    setPropertyRandom<T>(name, dist);
}
template<typename T>
void RunPlanVector::setPropertyLogNormalRandom(const std::string &name, const flamegpu::size_type index, const T mean, const T stddev) {
    static_assert(detail::StaticAssert::_Is_RealType<T>::value,
    "Invalid template argument for RunPlanVector::setPropertyLogNormalRandom(const std::string &name, flamegpu::size_type index, T mean, T stddev)");
    std::lognormal_distribution<T> dist(mean, stddev);
    setPropertyRandom<T>(name, index, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const float min, const float max) {
    std::uniform_real_distribution<float> dist(min, max);
    setPropertyRandom<float>(name, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const flamegpu::size_type index, const float min, const float max) {
    std::uniform_real_distribution<float> dist(min, max);
    setPropertyRandom<float>(name, index, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const double min, const double max) {
    std::uniform_real_distribution<double> dist(min, max);
    setPropertyRandom<double>(name, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const flamegpu::size_type index, const double min, const double max) {
    std::uniform_real_distribution<double> dist(min, max);
    setPropertyRandom<double>(name, index, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const char min, const char max) {
    std::uniform_int_distribution<int16_t> dist(min, max);
    setPropertyRandom<char>(name, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const flamegpu::size_type index, const char min, const char max) {
    std::uniform_int_distribution<int16_t> dist(min, max);
    setPropertyRandom<char>(name, index, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const unsigned char min, const unsigned char max) {
    std::uniform_int_distribution<uint16_t> dist(min, max);
    setPropertyRandom<unsigned char>(name, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const flamegpu::size_type index, const unsigned char min, const unsigned char max) {
    std::uniform_int_distribution<uint16_t> dist(min, max);
    setPropertyRandom<unsigned char>(name, index, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const signed char min, const signed char max) {
    std::uniform_int_distribution<int16_t> dist(min, max);
    setPropertyRandom<signed char>(name, dist);
}
template<>
inline void RunPlanVector::setPropertyUniformRandom(const std::string &name, const flamegpu::size_type index, const signed char min, const signed char max) {
    std::uniform_int_distribution<int16_t> dist(min, max);
    setPropertyRandom<signed char>(name, index, dist);
}

}  // namespace flamegpu

#endif  // INCLUDE_FLAMEGPU_SIMULATION_RUNPLANVECTOR_H_