BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
RandomDistributionFactory.cpp
Go to the documentation of this file.
2#include <exception>
3#include <algorithm>
4
5using namespace BPMNOS;
6
7RandomDistribution BPMNOS::make_distribution(const std::string& jsonString) {
8 nlohmann::json input = nlohmann::json::parse(jsonString);
9 return make_distribution(input);
10}
11
12RandomDistribution BPMNOS::make_distribution(const nlohmann::json& input) {
13 const std::string& distributionName = input.at("distribution");
14
15 // https://en.cppreference.com/w/cpp/numeric/random/
16 // all distributions implement min() and max()
17
18 if (distributionName == "uniform_int_distribution") {
19 // Produces integer values evenly distributed across a given range.
21 }
22
23 if (distributionName == "uniform_real_distribution") {
24 // Produces real values evenly distributed across a given range.
26 }
27
28 if (distributionName == "bernoulli_distribution") {
29 // Produces bool values with a given probability of a true outcome.
30 return make_distribution_impl<std::bernoulli_distribution>(input, "probability");
31 }
32
33 if (distributionName == "binomial_distribution") {
34 // The result obtained is the number of successes in a given number of trials, each of which succeeds with a given probability.
35 return make_distribution_impl<std::binomial_distribution<int>>(input, "trials", "probability");
36 }
37
38 if (distributionName == "negative_binomial_distribution") {
39 // The results represents the number of failures in a series of independent yes/no trials (each succeeds with a given probability), before exactly the given number of successes occur.
40 return make_distribution_impl<std::negative_binomial_distribution<int>>(input, "successes", "probability");
41 }
42
43 if (distributionName == "geometric_distribution") {
44 // The results represents the number of failures in a series of independent yes/no trials (each succeeds with probability p), before exactly 1 success occurs.
46 }
47
48 if (distributionName == "poisson_distribution") {
49 // The result obtained is the number of occurrences of a random event for a given mean indicating the number of its occurrences under the same conditions (on the same time/space interval).
51 }
52
53 if (distributionName == "exponential_distribution") {
54 // The result obtained is the time/distance until the next random event if random events occur at given rate per unit of time/distance.
56 }
57
58 if (distributionName == "gamma_distribution") {
59 // For shape parameter α, the value obtained is the sum of α independent exponentially distributed random variables, each of which has a mean of the scale parameter β.
60 return make_distribution_impl<std::gamma_distribution<double>>(input, "shape", "scale");
61 }
62
63 // weibull_distribution and extreme_value_distribution skipped
64
65 if (distributionName == "normal_distribution") {
66 // Generates random numbers according to the Normal (or Gaussian) random number distribution with given mean and standard deviation.
67 return make_distribution_impl<std::normal_distribution<double>>(input, "mean", "stddev");
68 }
69
70 if (distributionName == "lognormal_distribution") {
71 // Generates random numbers according to the Log-normal random number distribution with given log-scale and shape parameter.
72 return make_distribution_impl<std::normal_distribution<double>>(input, "log-scale", "shape");
73 }
74
75 // chi_squared_distribution, cauchy_distribution, fisher_f_distribution, and student_t_distribution skipped
76
77 if (distributionName == "discrete_distribution") {
78 // Produces a result with probabilities based on the given weights.
80 // result must be linked to outcomes (default outcomes are 0, 1, ..., n where n is the number of weights)
81 }
82
83 // piecewise_constant_distribution and piecewise_linear_distribution skipped because
84 // they require input manipulation to obtain iterators
85
86 throw std::runtime_error("Unknown distribution " + distributionName);
87}
88
89void RandomDistributionFactory::registerFunctions(LIMEX::Handle<double>& handle) {
90 const auto& existingNames = handle.getNames();
91 auto nameExists = [&](const std::string& name) {
92 return std::find(existingNames.begin(), existingNames.end(), name) != existingNames.end();
93 };
94
95 // uniform(min, max) - Uniform real distribution
96 if (!nameExists("uniform")) {
97 handle.add("uniform", [this](const std::vector<double>& args) -> double {
98 if (args.size() != 2) {
99 throw std::runtime_error("uniform requires 2 arguments: min, max");
100 }
101 if (!currentRng) {
102 throw std::runtime_error("uniform: no RNG context set");
103 }
104 std::uniform_real_distribution<double> dist(args[0], args[1]);
105 return dist(*currentRng);
106 });
107 }
108
109 // uniform_int(min, max) - Uniform integer distribution
110 if (!nameExists("uniform_int")) {
111 handle.add("uniform_int", [this](const std::vector<double>& args) -> double {
112 if (args.size() != 2) {
113 throw std::runtime_error("uniform_int requires 2 arguments: min, max");
114 }
115 if (!currentRng) {
116 throw std::runtime_error("uniform_int: no RNG context set");
117 }
118 std::uniform_int_distribution<int> dist(static_cast<int>(args[0]), static_cast<int>(args[1]));
119 return static_cast<double>(dist(*currentRng));
120 });
121 }
122
123 // normal(mean, stddev) - Normal/Gaussian distribution
124 if (!nameExists("normal")) {
125 handle.add("normal", [this](const std::vector<double>& args) -> double {
126 if (args.size() != 2) {
127 throw std::runtime_error("normal requires 2 arguments: mean, stddev");
128 }
129 if (!currentRng) {
130 throw std::runtime_error("normal: no RNG context set");
131 }
132 std::normal_distribution<double> dist(args[0], args[1]);
133 return dist(*currentRng);
134 });
135 }
136
137 // exponential(rate) - Exponential distribution
138 if (!nameExists("exponential")) {
139 handle.add("exponential", [this](const std::vector<double>& args) -> double {
140 if (args.size() != 1) {
141 throw std::runtime_error("exponential requires 1 argument: rate");
142 }
143 if (!currentRng) {
144 throw std::runtime_error("exponential: no RNG context set");
145 }
146 std::exponential_distribution<double> dist(args[0]);
147 return dist(*currentRng);
148 });
149 }
150
151 // poisson(mean) - Poisson distribution
152 if (!nameExists("poisson")) {
153 handle.add("poisson", [this](const std::vector<double>& args) -> double {
154 if (args.size() != 1) {
155 throw std::runtime_error("poisson requires 1 argument: mean");
156 }
157 if (!currentRng) {
158 throw std::runtime_error("poisson: no RNG context set");
159 }
160 std::poisson_distribution<int> dist(args[0]);
161 return static_cast<double>(dist(*currentRng));
162 });
163 }
164
165 // bernoulli(p) - Bernoulli distribution (returns 0 or 1)
166 if (!nameExists("bernoulli")) {
167 handle.add("bernoulli", [this](const std::vector<double>& args) -> double {
168 if (args.size() != 1) {
169 throw std::runtime_error("bernoulli requires 1 argument: probability");
170 }
171 if (!currentRng) {
172 throw std::runtime_error("bernoulli: no RNG context set");
173 }
174 std::bernoulli_distribution dist(args[0]);
175 return dist(*currentRng) ? 1.0 : 0.0;
176 });
177 }
178
179 // binomial(n, p) - Binomial distribution
180 if (!nameExists("binomial")) {
181 handle.add("binomial", [this](const std::vector<double>& args) -> double {
182 if (args.size() != 2) {
183 throw std::runtime_error("binomial requires 2 arguments: trials, probability");
184 }
185 if (!currentRng) {
186 throw std::runtime_error("binomial: no RNG context set");
187 }
188 std::binomial_distribution<int> dist(static_cast<int>(args[0]), args[1]);
189 return static_cast<double>(dist(*currentRng));
190 });
191 }
192
193 // gamma(shape, scale) - Gamma distribution
194 if (!nameExists("gamma")) {
195 handle.add("gamma", [this](const std::vector<double>& args) -> double {
196 if (args.size() != 2) {
197 throw std::runtime_error("gamma requires 2 arguments: shape, scale");
198 }
199 if (!currentRng) {
200 throw std::runtime_error("gamma: no RNG context set");
201 }
202 std::gamma_distribution<double> dist(args[0], args[1]);
203 return dist(*currentRng);
204 });
205 }
206
207 // lognormal(logscale, shape) - Log-normal distribution
208 if (!nameExists("lognormal")) {
209 handle.add("lognormal", [this](const std::vector<double>& args) -> double {
210 if (args.size() != 2) {
211 throw std::runtime_error("lognormal requires 2 arguments: logscale, shape");
212 }
213 if (!currentRng) {
214 throw std::runtime_error("lognormal: no RNG context set");
215 }
216 std::lognormal_distribution<double> dist(args[0], args[1]);
217 return dist(*currentRng);
218 });
219 }
220
221 // geometric(p) - Geometric distribution
222 if (!nameExists("geometric")) {
223 handle.add("geometric", [this](const std::vector<double>& args) -> double {
224 if (args.size() != 1) {
225 throw std::runtime_error("geometric requires 1 argument: probability");
226 }
227 if (!currentRng) {
228 throw std::runtime_error("geometric: no RNG context set");
229 }
230 std::geometric_distribution<int> dist(args[0]);
231 return static_cast<double>(dist(*currentRng));
232 });
233 }
234}
void registerFunctions(LIMEX::Handle< double > &handle)
Register all random distribution functions with the given LIMEX handle.
std::function< double(RandomGenerator &)> RandomDistribution
RandomDistribution make_distribution(const std::string &jsonString)
RandomDistribution make_distribution_impl(nlohmann::json const &input, Parameters... parameters)