26#ifndef LINE_SOLVERS_LDES_SAMPLEPATH_H
27#define LINE_SOLVERS_LDES_SAMPLEPATH_H
60inline double ulp(
double v) {
62 if (std::isnan(v))
return v;
63 if (std::isinf(v))
return std::numeric_limits<double>::infinity();
64 if (v == std::numeric_limits<double>::max())
return v - std::nextafter(v, 0.0);
65 return std::nextafter(v, std::numeric_limits<double>::infinity()) - v;
77 case ProcessType::MAP:
78 case ProcessType::MMPP2:
80 case ProcessType::APH:
81 case ProcessType::HYPEREXP:
82 case ProcessType::COXIAN:
83 case ProcessType::COX2:
85 case ProcessType::ERLANG:
87 case ProcessType::BMAP:
100 const ProcessType pt = s.
type();
101 if (disabled || pt == ProcessType::DISABLED)
return 1;
103 if (pt == ProcessType::REPLAYER)
return 1;
104 if (pt == ProcessType::ERLANG && s.
erlang_k() > 0)
return std::max(1, s.
erlang_k());
105 const bool w = pt == ProcessType::PH || pt == ProcessType::APH || pt == ProcessType::HYPEREXP ||
106 pt == ProcessType::COXIAN || pt == ProcessType::COX2 || pt == ProcessType::MAP ||
107 pt == ProcessType::MMPP2 ||
118 static std::shared_ptr<Script>
single(
double total) {
119 std::shared_ptr<Script> s = std::make_shared<Script>();
120 s->cum.assign(1, total);
127 int s = std::max(0, from);
128 const int n =
static_cast<int>(
cum.size());
129 while (s < n - 1 &&
cum[
static_cast<std::size_t
>(s)] <= a) ++s;
153 return std::numeric_limits<double>::infinity();
161 std::vector<std::vector<Job>>
jobs;
165 std::vector<std::vector<ScriptP>>
src;
179 Recorder(std::size_t limit,
const std::vector<std::size_t>& stateful_nodes, std::size_t nclasses,
180 const std::vector<std::string>&
refusal,
const std::vector<bool>&
refused)
181 : limit_(limit), nodes_(stateful_nodes), R_(nclasses), refusal_(
refusal),
182 refused_(
refused), state_rows_(stateful_nodes.
size()), aggr_rows_(stateful_nodes.
size()) {}
184 bool is_full()
const {
return t_.size() >= limit_; }
185 std::size_t
size()
const {
return t_.size(); }
187 void add_row(
double time,
const std::vector<std::vector<double>>&
state,
188 const std::vector<std::vector<double>>&
aggr,
const std::vector<Ev>& evs) {
189 const double step =
static_cast<double>(t_.size());
191 for (std::size_t i = 0; i < nodes_.size(); ++i) {
192 state_rows_[i].push_back(refused_[i] ? std::vector<double>() :
state[i]);
193 aggr_rows_[i].push_back(
aggr[i]);
195 for (
const Ev& e : evs) {
196 std::vector<double> row(7);
199 for (
int c = 0; c < 5; ++c) row[static_cast<std::size_t>(c) + 2] = e.v[c];
200 events_.push_back(row);
207 const std::vector<double>&
times()
const {
return t_; }
208 const std::vector<std::vector<double>>&
events()
const {
return events_; }
211 bool refused(std::size_t isf)
const {
return refused_[isf]; }
212 const std::string&
refusal(std::size_t isf)
const {
return refusal_[isf]; }
215 std::vector<std::vector<double>>
state(std::size_t isf)
const {
216 const std::vector<std::vector<double>>& rows = state_rows_[isf];
218 for (
const std::vector<double>& r : rows) w = std::max(w, r.size());
219 std::vector<std::vector<double>> out(rows.size(), std::vector<double>(w, 0.0));
220 for (std::size_t k = 0; k < rows.size(); ++k)
221 std::copy(rows[k].begin(), rows[k].end(), out[k].begin() +
static_cast<std::ptrdiff_t
>(w - rows[k].
size()));
224 std::vector<std::vector<double>>
aggr(std::size_t isf)
const {
225 std::vector<std::vector<double>> out(aggr_rows_[isf].
size(), std::vector<double>(R_, 0.0));
226 for (std::size_t k = 0; k < out.size(); ++k)
227 for (std::size_t r = 0; r < R_ && r < aggr_rows_[isf][k].size(); ++r)
228 out[k][r] = aggr_rows_[isf][k][r];
233 std::size_t limit_ = 0;
234 std::vector<std::size_t> nodes_;
236 std::vector<std::string> refusal_;
237 std::vector<bool> refused_;
238 std::vector<double> t_;
239 std::vector<std::vector<double>> events_;
240 std::vector<std::vector<std::vector<double>>> state_rows_, aggr_rows_;
241 long long unbound_ = 0;
252 std::size_t
M = 0,
R = 0;
256 std::vector<bool> detail;
281 std::map<std::uint64_t, long long>
ids;
292 std::random_device rd;
293 rng.
set_seed((
static_cast<long long>(rd()) << 32) ^
static_cast<long long>(rd()));
310 if (std::fabs(tot - value) <= 1e-9 * std::fabs(value) + 8.0 *
ulp(value)) {
312 ScriptP s = std::make_shared<Script>();
313 s->cum.assign(n, 0.0);
316 for (std::size_t i = 0; i < n; ++i) {
321 s->cum[n - 1] = value;
327 const std::size_t k =
static_cast<std::size_t
>(erlang_k);
328 std::vector<double> u(k - 1);
330 std::sort(u.begin(), u.end());
331 ScriptP s = std::make_shared<Script>();
332 s->cum.assign(k, 0.0);
334 for (std::size_t i = 0; i + 1 < k; ++i) {
335 s->cum[i] = u[i] * value;
336 s->ph[i] =
static_cast<int>(i);
338 s->cum[k - 1] = value;
339 s->ph[k - 1] =
static_cast<int>(k - 1);
352 void capture_arrival(std::size_t st, std::size_t k,
double value,
int erlang_k,
double now) {
357 void drop(std::size_t q, std::size_t k) {
drops.push_back(std::make_pair(q, k)); }
360 ScriptP bind(std::size_t q, std::size_t k,
double expected,
double now) {
362 double best_err = std::numeric_limits<double>::infinity();
363 for (std::size_t i = 0; i <
pending.size(); ++i) {
366 const double err = std::fabs(s.
cum.back() - expected);
367 if (err < best_err) {
369 best =
static_cast<long>(i);
372 const double tol = 1e-9 * std::fabs(expected) + 8.0 *
ulp(std::fabs(now) + std::fabs(expected));
373 if (best >= 0 && best_err <= tol) {
376 const std::size_t n = s->cum.size();
377 s->cum[n - 1] = std::max(expected, n > 1 ? s->cum[n - 2] : 0.0);
385 ScriptP script_for(std::uint64_t key, std::size_t q, std::size_t k,
double expected,
double now) {
386 std::map<std::uint64_t, ScriptP>::const_iterator it =
scripts.find(key);
387 if (it !=
scripts.end())
return it->second;
395 s.
src.assign(
M, std::vector<ScriptP>(
R));
396 s.
src_start.assign(
M, std::vector<double>(
R, 0.0));
397 s.
src_seg.assign(
M, std::vector<int>(
R, 0));
398 for (std::size_t i = 0; i <
M; ++i) {
400 for (std::size_t k = 0; k <
R; ++k) {
405 const int from = (prev && prev->
src[i][k] == sc) ? prev->
src_seg[i][k] : 0;
417 template <
class PhaseFn>
418 void encode(
const Snap& s, std::vector<std::vector<double>>& state,
419 std::vector<std::vector<double>>& aggr, PhaseFn sampler_phase)
const {
421 state.assign(nsf, std::vector<double>());
422 aggr.assign(nsf, std::vector<double>());
423 const double inf = std::numeric_limits<double>::infinity();
424 for (std::size_t isf = 0; isf < nsf; ++isf) {
426 std::vector<double>
ag(
R, 0.0);
427 std::vector<double> row;
428 if (ist >= 0 &&
is_source[
static_cast<std::size_t
>(ist)]) {
429 const std::size_t i =
static_cast<std::size_t
>(ist);
431 for (std::size_t k = 0; k <
R; ++k) {
432 w +=
static_cast<std::size_t
>(
phases[i][k]);
438 for (std::size_t k = 0; k <
R; ++k) {
441 const int p = std::min(sc->ph[
static_cast<std::size_t
>(s.
src_seg[i][k])],
phases[i][k] - 1);
442 row[off +
static_cast<std::size_t
>(p)] = 1.0;
444 off +=
static_cast<std::size_t
>(
phases[i][k]);
446 }
else if (ist >= 0 &&
is_service[
static_cast<std::size_t
>(ist)]) {
447 const std::size_t i =
static_cast<std::size_t
>(ist);
448 for (std::size_t k = 0; k <
R; ++k)
ag[k] = s.
counts[i][k];
450 std::size_t nsrv = 0;
451 std::vector<std::size_t> base(
R, 0);
452 for (std::size_t k = 0; k <
R; ++k) {
454 nsrv +=
static_cast<std::size_t
>(
phases[i][k]);
456 std::vector<double> srv(nsrv, 0.0);
457 std::vector<double> buf;
458 std::vector<double> map_var(
R, 0.0);
459 std::vector<bool> map_busy(
R,
false);
460 for (
const Job& j : s.
jobs[i]) {
462 const int p = std::min(j.phase(),
phases[i][j.cls] - 1);
463 srv[base[j.cls] +
static_cast<std::size_t
>(p)] += 1.0;
464 map_var[j.cls] = p + 1;
465 map_busy[j.cls] =
true;
467 buf.push_back(
static_cast<double>(j.cls + 1));
470 const std::size_t nbuf =
fcfs_buffer[i] ? std::max<std::size_t>(1, buf.size()) : 0;
471 std::size_t nvar = 0;
472 for (std::size_t k = 0; k < R; ++k) nvar += static_cast<std::size_t>(
map_vars[i][k]);
473 row.assign(nbuf + nsrv + nvar, 0.0);
474 for (std::size_t b = 0; b < buf.size(); ++b) row[nbuf - 1 - b] = buf[b];
475 std::copy(srv.begin(), srv.end(), row.begin() +
static_cast<std::ptrdiff_t
>(nbuf));
476 std::size_t off = nbuf + nsrv;
477 for (std::size_t k = 0; k <
R; ++k) {
479 double v = map_var[k];
481 const int ph = sampler_phase(i, k);
498 const long long id =
id_fifo.front();
500 return (
id < 0) ?
next_id++ : id;
505 std::vector<Ev> deps, arvs, drps, starts, phs;
506 auto mk = [](
double t,
double n,
double c,
double id,
double ph) {
515 for (std::size_t q = 0; q <
M; ++q) {
517 const double node =
static_cast<double>(
node_of[q]);
519 for (std::size_t k = 0; k <
R; ++k)
521 deps.push_back(mk(
EV_DEP, node,
static_cast<double>(k), -1.0, 0.0));
526 std::map<std::uint64_t, const Job*> bm;
527 for (
const Job& j : b.
jobs[q]) bm[j.key] = &j;
528 for (
const Job& j : a.
jobs[q]) {
529 if (bm.count(j.key))
continue;
531 std::map<std::uint64_t, long long>::iterator it =
ids.find(j.key);
532 if (it !=
ids.end()) {
537 deps.push_back(mk(
EV_DEP, node,
static_cast<double>(j.cls),
static_cast<double>(jid),
538 j.in_service ? j.phase() + 1.0 : 0.0));
542 for (
const std::pair<std::size_t, std::size_t>& sd :
src_deps) {
543 const long long id =
next_id++;
544 deps.push_back(mk(
EV_DEP,
static_cast<double>(
node_of[sd.first]),
static_cast<double>(sd.second),
545 static_cast<double>(
id), 0.0));
548 for (std::size_t q = 0; q <
M; ++q) {
550 const double node =
static_cast<double>(
node_of[q]);
552 for (std::size_t k = 0; k <
R; ++k)
555 arvs.push_back(mk(
EV_ARV, node,
static_cast<double>(k), -1.0, 0.0));
559 std::map<std::uint64_t, const Job*> am;
560 for (
const Job& j : a.
jobs[q]) am[j.key] = &j;
561 for (
const Job& j : b.
jobs[q]) {
562 std::map<std::uint64_t, const Job*>::const_iterator it = am.find(j.key);
563 const Job* old = (it == am.end()) ? nullptr : it->second;
565 const long long id =
take_id();
567 arvs.push_back(mk(
EV_ARV, node,
static_cast<double>(j.cls),
static_cast<double>(
id), 0.0));
569 const double id =
static_cast<double>(
ids[j.key]);
570 if (j.in_service && (!old || !old->
in_service))
571 starts.push_back(mk(
EV_START, node,
static_cast<double>(j.cls),
id, j.phase() + 1.0));
572 else if (j.in_service && old->
phase() != j.phase())
573 phs.push_back(mk(
EV_PHASE, node,
static_cast<double>(j.cls),
id, j.phase() + 1.0));
576 for (
const std::pair<std::size_t, std::size_t>& dr :
drops)
577 drps.push_back(mk(
EV_DROP,
static_cast<double>(
node_of[dr.first]),
static_cast<double>(dr.second),
578 static_cast<double>(
take_id()), 0.0));
579 for (std::size_t i = 0; i <
M; ++i) {
581 for (std::size_t k = 0; k <
R; ++k) {
584 phs.push_back(mk(
EV_PHASE,
static_cast<double>(
node_of[i]),
static_cast<double>(k), -1.0,
585 sc->ph[
static_cast<std::size_t
>(b.
src_seg[i][k])] + 1.0));
588 std::vector<Ev> evs(deps);
589 evs.insert(evs.end(), arvs.begin(), arvs.end());
590 evs.insert(evs.end(), drps.begin(), drps.end());
591 evs.insert(evs.end(), starts.begin(), starts.end());
592 evs.insert(evs.end(), phs.begin(), phs.end());
605 template <
class SnapFn,
class PhaseFn>
606 int boundary(
bool stopped,
double now,
double t_next, SnapFn snapshot, PhaseFn sampler_phase) {
608 held = snapshot(now,
nullptr);
610 for (std::size_t i = 0; i <
M; ++i)
615 const std::vector<Ev> evs =
diff(
held, cur);
617 std::vector<std::vector<double>> st,
ag;
642 double tau = std::numeric_limits<double>::infinity();
643 for (std::size_t i = 0; i <
M; ++i)
645 for (
const Job& j :
held.
jobs[i]) tau = std::min(tau, j.next_boundary());
646 for (std::size_t i = 0; i <
M; ++i) {
648 for (std::size_t k = 0; k <
R; ++k) {
651 if (sc && sg <
static_cast<int>(sc->cum.size()) - 1)
652 tau = std::min(tau,
held.
src_start[i][k] + sc->cum[
static_cast<std::size_t
>(sg)]);
655 if (!(tau < t_next))
break;
656 tau = std::max(tau, now);
657 std::vector<std::vector<double>> st,
ag;
660 for (std::size_t i = 0; i <
M; ++i) {
662 const double node =
static_cast<double>(
node_of[i]);
664 const double tb = j.next_boundary();
666 const int before = j.phase();
668 while (j.next_boundary() <= tau) ++j.seg;
669 if (j.phase() != before) {
673 e.
v[2] =
static_cast<double>(j.cls);
674 e.
v[3] =
static_cast<double>(
ids[j.key]);
675 e.
v[4] = j.phase() + 1.0;
681 for (std::size_t i = 0; i <
M; ++i) {
683 for (std::size_t k = 0; k <
R; ++k) {
687 const int before = sc->ph[
static_cast<std::size_t
>(sg)];
688 while (sg <
static_cast<int>(sc->cum.size()) - 1 &&
689 held.
src_start[i][k] + sc->cum[
static_cast<std::size_t
>(sg)] <= tau)
691 if (sc->ph[
static_cast<std::size_t
>(sg)] != before) {
694 e.
v[1] =
static_cast<double>(
node_of[i]);
695 e.
v[2] =
static_cast<double>(k);
697 e.
v[4] = sc->ph[
static_cast<std::size_t
>(sg)] + 1.0;
702 if (evs.empty())
continue;
One variate generator, holding whatever state its family needs.
lang::ProcessType type() const
int erlang_k() const
Phases of an Erlang recovered for convolution, 0 when none was.
std::size_t map_order() const
Order of the (D0,D1) representation, 0 for a family that carries none.
Transcribes SamplePathRecorder: the rows and events of the path.
std::size_t stateful_node(std::size_t isf) const
const std::vector< std::vector< double > > & events() const
std::size_t num_stateful() const
std::vector< std::vector< double > > state(std::size_t isf) const
The detailed rows of node isf, LEFT-padded with zeros to the widest row.
const std::string & refusal(std::size_t isf) const
void add_row(double time, const std::vector< std::vector< double > > &state, const std::vector< std::vector< double > > &aggr, const std::vector< Ev > &evs)
bool refused(std::size_t isf) const
Recorder(std::size_t limit, const std::vector< std::size_t > &stateful_nodes, std::size_t nclasses, const std::vector< std::string > &refusal, const std::vector< bool > &refused)
long long unbound_scripts() const
const std::vector< double > & times() const
void count_unbound_script()
std::vector< std::vector< double > > aggr(std::size_t isf) const
java.util.Random, the 48-bit LCG of the Java Language Specification.
void set_seed(long long seed)
Java's setSeed: scramble by 0x5DEECE66D and mask to 48 bits.
double next_double()
Java's nextDouble(): a 26-bit and a 27-bit draw.
The variate generators of the native LDES engine.
bool process_is_marked_schedule(ProcessType p)
True when sn.proc holds the MARKED SCHEDULE slot.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
bool process_is_marked_stationary(ProcessType p)
True when the type carries PER-MARK arrival blocks, i.e.
std::shared_ptr< Script > ScriptP
int phase_count(const Sampler &s, bool disabled, bool arrival)
Phases the export reports for a law (spPhaseCount), or -1 when the law is time-inhomogeneous and has ...
constexpr double EV_START
constexpr double EV_PHASE
constexpr double EV_ARV
Event wire codes (= MATLAB EventType values).
bool walked(const Sampler &s, bool arrival)
True for a family whose draw is a phase WALK the sampler can trace: the reference's MapSampler / map_...
constexpr long long SEED_OFFSET
Seed offset of the recorder's own generator (Erlang stage reconstruction).
double ulp(double v)
Java's Math.ulp for a double.
Conservation laws of a layered queueing network, enumerated from its structure.
The two random number generators the Java LDES engine draws from, reproduced exactly: SSJ's MRG32k3a ...
The phases one walked interval visited and how long it held each, filled by a sampler only while the ...
double total() const
Sum of the holdings, in order, as the reference adds them.
One recorded event without its step and time: {type, node, class, jobId, phase}.
One job held at a detailed station in a snapshot.
double t0
Attained amount at time t0 and its rate of growth (1 in wall time, the PS share otherwise).
double next_boundary() const
Epoch at which the current segment ends, +Inf if it is the last.
Phase script of one drawn interval: segment j ends at cum[j] and is spent in ph[j] (0-based).
int seg_at(double a, int from) const
Segment holding attained amount a, never below from.
std::vector< double > cum
static std::shared_ptr< Script > single(double total)
Jobs held everywhere at one instant, indexed by station.
std::vector< std::vector< Job > > jobs
Per station: waiting jobs head first, then the jobs in service; meaningful when detail[i].
std::vector< std::vector< int > > counts
Per station and class: job count.
std::vector< std::vector< double > > src_start
std::vector< std::vector< ScriptP > > src
Per station (sources only) and class: interarrival script in progress, start, segment.
std::vector< std::vector< int > > src_seg
The recorder's bookkeeping between two boundaries: scripts drawn and not yet bound to a job,...
std::vector< std::pair< std::size_t, std::size_t > > src_deps
std::vector< std::vector< double > > const_row
per stateful node that is no station
std::vector< std::vector< double > > map_var_init
std::vector< bool > is_source
per station
void source_departure(std::size_t st, std::size_t k)
void drop(std::size_t q, std::size_t k)
std::vector< Pending > pending
std::vector< std::pair< std::size_t, std::size_t > > drops
std::vector< std::vector< int > > map_vars
per station and class
std::deque< long long > id_fifo
std::vector< std::vector< int > > phases
per station and class
std::vector< long long > station_of_sf
per stateful node: station or -1
ScriptP script_of(double value, int erlang_k)
Script of the draw that just returned value: its walk, an Erlang reconstruction, or one segment.
std::vector< bool > fcfs_buffer
per station: the row carries a buffer
ScriptP script_for(std::uint64_t key, std::size_t q, std::size_t k, double expected, double now)
Script already bound to key, or a fresh binding (stored under key).
void encode(const Snap &s, std::vector< std::vector< double > > &state, std::vector< std::vector< double > > &aggr, PhaseFn sampler_phase) const
Encodes snapshot s as LINE State rows and per-class counts.
std::vector< std::vector< double > > src_start
std::vector< long long > node_of
per station: 0-based node index
std::vector< Ev > diff(const Snap &a, const Snap &b)
Events turning snapshot a into b, in the order DEP, ARV, DROP, START, PHASE.
ScriptP bind(std::size_t q, std::size_t k, double expected, double now)
Binds a drawn script of (q, k) to a job whose drawn total is expected.
void snap_sources(Snap &s, double now, const Snap *prev) const
Source part of a snapshot.
std::map< std::uint64_t, long long > ids
std::vector< bool > is_service
per station: a Queue or a Delay
std::vector< std::vector< ScriptP > > src_script
[station][class]
int boundary(bool stopped, double now, double t_next, SnapFn snapshot, PhaseFn sampler_phase)
Event-boundary hook (spBoundary): records what the event that just ran changed, then,...
void capture_arrival(std::size_t st, std::size_t k, double value, int erlang_k, double now)
void capture_service(std::size_t q, std::size_t k, double value, int erlang_k)
bool stopped_run
True when the recorder stopped the run (stoppingReason "sample_path").
PhaseTrace * trace_fill()
Hands the scratch trace to a recording sampler call.
std::map< std::uint64_t, ScriptP > scripts