165 "lossn_mci requires transcendental arithmetic (log-space importance weights)");
171 const std::size_t R = nu.size(), J = C.size();
172 if (R == 0 || J == 0)
throw InputError(
"lossn_mci: empty loss network");
174 const std::size_t S =
opt.samples;
175 if (S < 2)
throw InputError(
"lossn_mci: at least two samples are required");
179 std::vector<std::size_t> N(R, 0);
180 for (std::size_t k = 0; k < R; ++k) {
183 for (std::size_t j = 0; j < J; ++j) {
184 if (!(A(j, k) > zero))
continue;
185 const T ratio = C[j] / A(j, k);
186 if (!any || ratio < best) best = ratio;
191 N[k] = bd <= 0.0 ? 0 :
static_cast<std::size_t
>(std::floor(bd));
195 std::vector<T> gamma(
opt.gamma);
198 for (std::size_t j = 0; j < J; ++j) {
199 if (C[j] == zero)
throw InputError(
"lossn_mci: a link has zero capacity");
201 for (std::size_t k = 0; k < R; ++k) load += A(j, k) * nu[k];
202 load = T(load / C[j]);
203 if (j == 0 || load > delta) delta = load;
207 if (base < floorBase) base = floorBase;
208 gamma.assign(R, zero);
209 for (std::size_t k = 0; k < R; ++k) {
211 for (std::size_t j = 0; j < J; ++j)
212 if (A(j, k) > b) b = A(j, k);
213 gamma[k] = nu[k] * pow(base, b);
216 if (gamma.size() != R)
throw InputError(
"lossn_mci: gamma must have one entry per route");
218 for (std::size_t k = 0; k < R; ++k)
219 if (gamma[k] < gammaFloor) gamma[k] = gammaFloor;
223 std::vector<std::vector<T>> cdf(R);
224 for (std::size_t k = 0; k < R; ++k) {
225 std::vector<T> logterms(N[k] + 1);
226 const std::vector<T> lf = detail::log_factorials<T>(N[k]);
227 for (std::size_t l = 0; l <= N[k]; ++l)
230 const T lse = detail::logsumexp(logterms);
232 cdf[k].assign(N[k] + 1, zero);
234 for (std::size_t l = 0; l <= N[k]; ++l) {
235 acc += exp(T(logterms[l] - lse));
242 std::vector<std::size_t> V(S * R, 0);
243 for (std::size_t k = 0; k < R; ++k)
244 for (std::size_t s = 0; s < S; ++s) {
247 for (std::size_t l = 0; l <= N[k]; ++l)
248 if (u > cdf[k][l]) ++v;
253 std::vector<T> logratio(R);
254 for (std::size_t k = 0; k < R; ++k) {
255 if (!(nu[k] > zero))
throw InputError(
"lossn_mci: a route has non-positive offered load");
256 logratio[k] = T(log(nu[k]) - log(gamma[k]));
258 std::vector<T> log_alpha(S, zero);
259 std::vector<char> inOmega(S, 0);
260 std::vector<char> inOmegaK(S * R, 0);
261 std::vector<T> AV(J, zero);
262 for (std::size_t s = 0; s < S; ++s) {
263 for (std::size_t j = 0; j < J; ++j) {
265 for (std::size_t k = 0; k < R; ++k)
270 for (std::size_t j = 0; j < J && ok; ++j)
271 if (AV[j] > C[j]) ok =
false;
272 inOmega[s] = ok ? 1 : 0;
273 for (std::size_t k = 0; k < R; ++k) {
275 for (std::size_t j = 0; j < J && okk; ++j)
276 if (AV[j] > T(C[j] - A(j, k))) okk =
false;
277 inOmegaK[s * R + k] = okk ? 1 : 0;
280 for (std::size_t k = 0; k < R; ++k)
291 for (std::size_t s = 0; s < S; ++s)
292 if (inOmega[s]) laO.push_back(log_alpha[s]);
294 res.
lG = -std::numeric_limits<double>::infinity();
296 const T lse = detail::logsumexp(laO);
298 std::log(
static_cast<double>(S));
303 bool anyOmega =
false;
304 for (std::size_t s = 0; s < S; ++s)
306 if (!anyOmega || log_alpha[s] > M) M = log_alpha[s];
309 std::vector<T> w(S), Z(S);
311 for (std::size_t s = 0; s < S; ++s) {
312 w[s] = exp(T(log_alpha[s] - M));
313 Z[s] = inOmega[s] ? w[s] : zero;
322 res.
Loss.assign(R, zero);
323 res.
QLen.assign(R, zero);
325 throw NumericError(
"lossn_mci: no sampled state was feasible, the estimator is undefined");
328 for (std::size_t s = 0; s < S; ++s) varZ += T(Z[s] - meanZ) * T(Z[s] - meanZ);
331 for (std::size_t k = 0; k < R; ++k) {
334 for (std::size_t s = 0; s < S; ++s) {
335 Y[s] = inOmegaK[s * R + k] ? w[s] : zero;
339 const T phi = T(meanY / meanZ);
340 T varY = zero, covYZ = zero;
341 for (std::size_t s = 0; s < S; ++s) {
342 varY += T(Y[s] - meanY) * T(Y[s] - meanY);
343 covYZ += T(Y[s] - meanY) * T(Z[s] - meanZ);
346 varY = T(varY / denom);
347 covYZ = T(covYZ / denom);
350 if (sig2 < zero) sig2 = zero;
351 const T half = T(crit * sqrt(sig2));
355 res.
Loss[k] = T(one - phi);
356 res.
QLen[k] = nu[k] * phi;