117inline std::vector<double>
infer_mlps(
const std::vector<double>& muZ,
double nCores,
118 const std::vector<MlpsSample>& samples) {
119 using namespace mlpsdetail;
120 const std::size_t R = muZ.size();
121 if (R == 0)
throw InputError(
"infer_mlps: at least one class is required");
122 if (samples.empty())
throw InputError(
"infer_mlps: no observations");
123 for (std::size_t i = 0; i < samples.size(); ++i) {
124 if (samples[i].cls < 1 || samples[i].cls > R)
125 throw InputError(
"infer_mlps: a sample names a class outside 1..R");
126 if (samples[i].ql.size() != R)
127 throw InputError(
"infer_mlps: a sample's queue length has the wrong width");
128 if (!(samples[i].rt > 0.0))
129 throw InputError(
"infer_mlps: a response time must be positive");
134 for (std::size_t i = 0; i < samples.size(); ++i)
135 for (std::size_t r = 0; r < R; ++r) meanQL += samples[i].ql[r];
136 meanQL /=
static_cast<double>(samples.size());
137 const double Vtilde = std::min(meanQL, nCores);
138 std::vector<double> x0(R, 1e-3), lo(R, 0.0), hi(R, 0.0);
140 for (std::size_t i = 0; i < samples.size(); ++i) rtmax = std::max(rtmax, samples[i].rt);
141 for (std::size_t r = 0; r < R; ++r) {
144 for (std::size_t i = 0; i < samples.size(); ++i)
145 if (samples[i].cls == r + 1) {
146 sum += samples[i].rt;
149 if (cnt > 0 && meanQL > 0.0)
150 x0[r] = Vtilde * (
sum /
static_cast<double>(cnt)) / meanQL;
155 const std::size_t newR = R + 1;
156 std::vector<std::string> keys;
157 std::vector<MlpsPrebuilt> pre;
158 std::map<std::string, std::size_t> keyIndex;
159 auto key_of = [](std::size_t tc,
const std::vector<double>& q) {
160 std::string k = std::to_string(tc);
161 for (std::size_t i = 0; i < q.size(); ++i) k +=
"," + std::to_string(q[i]);
165 for (std::size_t i = 0; i < samples.size(); ++i) {
166 const std::string k = key_of(samples[i].cls, samples[i].ql);
167 if (keyIndex.count(k))
continue;
169 const std::size_t tc = samples[i].cls;
173 std::vector<double> N(newR, 0.0);
174 for (std::size_t r = 0; r < R; ++r) N[r] = samples[i].ql[r];
179 "infer_mlps: a sample reports its own class empty on arrival, so the tagged job "
180 "cannot be moved into the auxiliary class");
183 const std::size_t d = m.
add_delay(
"Think");
186 std::vector<std::size_t> cls(newR, 0);
187 for (std::size_t r = 0; r < newR; ++r) {
190 const double mz = (r + 1 == newR) ? muZ[tc - 1] : muZ[r];
195 for (std::size_t r = 0; r < newR; ++r) {
196 P.
set(cls[r], cls[r], d, q, 1.0);
197 P.
set(cls[r], cls[r], q, d, 1.0);
214 for (std::size_t e = 0; e < gen.
sync.size(); ++e)
215 if (gen.
sync[e].active.node == q && gen.
sync[e].active.cls == newR &&
217 pb.depSync.push_back(e);
218 if (pb.depSync.empty())
220 "infer_mlps: the augmented chain has no departure of the auxiliary class at the "
221 "queue, so the sojourn has no absorbing event");
225 const std::size_t qst = asn.
stations[0].name ==
"Queue1" ? 1 : 2;
226 const std::size_t taggedCol = (qst - 1) * newR + (newR - 1);
227 for (std::size_t s = 0; s < aggr.
rows(); ++s)
228 if (std::fabs(aggr(s, taggedCol) - 1.0) < 1e-9) pb.subset.push_back(s);
229 if (pb.subset.empty())
230 throw InputError(
"infer_mlps: no state has the tagged job at the queue");
233 for (std::size_t s = 0; s < pb.subset.size(); ++s)
234 for (std::size_t r = 0; r < newR; ++r)
235 pb.SSqueue(s, r) = aggr(pb.subset[s], (qst - 1) * newR + r);
237 keyIndex[k] = pre.size();
243 const double TOL = 1e-6;
244 auto objective = [&](
const std::vector<double>& x) {
246 std::vector<double> rates(R, 0.0);
247 for (std::size_t r = 0; r < R; ++r)
248 rates[r] = (x[r] > 0.0) ? 1.0 / x[r] : std::numeric_limits<double>::infinity();
249 for (std::size_t r = 0; r < R; ++r)
250 if (!std::isfinite(rates[r]))
return std::numeric_limits<double>::infinity();
252 std::vector<Matrix<double>> A(pre.size());
253 for (std::size_t p = 0; p < pre.size(); ++p) {
255 for (std::size_t r = 0; r < newR; ++r) {
256 const double rate = (r + 1 == newR) ? rates[pre[p].tagClass - 1] : rates[r];
265 for (std::size_t di = 0; di < pre[p].depSync.size(); ++di) {
267 for (std::size_t i = 0; i < Q.
rows(); ++i)
268 for (std::size_t j = 0; j < Q.
cols(); ++j) Q(i, j) -= F(i, j);
270 const std::size_t ns = pre[p].subset.size();
272 for (std::size_t i = 0; i < ns; ++i)
273 for (std::size_t j = 0; j < ns; ++j) S(i, j) = Q(pre[p].subset[i], pre[p].subset[j]);
278 for (std::size_t i = 0; i < samples.size(); ++i) {
279 const std::size_t p = keyIndex.find(key_of(samples[i].cls, samples[i].ql))->second;
281 const std::size_t ns = S.
rows();
283 const std::size_t idx = match_row(pre[p].SSqueue, pre[p].N);
284 std::vector<double> pie(ns, 0.0);
285 if (idx !=
static_cast<std::size_t
>(-1)) pie[idx] = 1.0;
292 for (std::size_t a = 0; a < ns; ++a) {
294 for (std::size_t b = 0; b < ns; ++b) row += S(a, b);
295 for (std::size_t b = 0; b < ns; ++b) mp.
D1(a, b) = -row * pie[b];
297 std::vector<double> at(1, samples[i].rt);
299 f -= std::log(TOL + std::max(0.0, like));
304 std::vector<Bound<double>> bounds(R);
305 for (std::size_t r = 0; r < R; ++r) {
306 bounds[r].has_lo =
true;
307 bounds[r].has_hi =
true;
308 bounds[r].lo = lo[r];
309 bounds[r].hi = hi[r];