145 throw InputError(
"SolverLDES (native engine): the Replayer at " + where_ +
146 " carries no samples");
150 const double half = mean_ * std::sqrt(3.0 * scv_);
153 if (!(a_ >= 0.0) || !(b_ > a_))
154 throw InputError(
"SolverLDES (native engine): the Uniform at " + where_ +
155 " implies a support reaching below zero; a non-negative "
156 "uniform requires an SCV of at most 1/3");
166 const double c = std::sqrt(scv_);
167 a_ = std::pow(c, -1.086);
168 b_ = mean_ / std::tgamma(1.0 + 1.0 / a_);
169 if (!(a_ > 0.0) || !(b_ > 0.0))
170 throw InputError(
"SolverLDES (native engine): the Weibull at " + where_ +
171 " has unusable moments");
176 const double c2p1 = scv_ + 1.0;
177 a_ = std::log(mean_ / std::sqrt(c2p1));
178 b_ = std::sqrt(std::log(c2p1));
180 throw InputError(
"SolverLDES (native engine): the Lognormal at " + where_ +
181 " has a zero log-variance");
188 a_ = std::sqrt(1.0 + 1.0 / scv_) + 1.0;
189 b_ = mean_ * (a_ - 1.0) / a_;
197 if (!(a_ > 0.0) || a_ > 1.0)
198 throw InputError(
"SolverLDES (native engine): the Geometric at " + where_ +
199 " has a success probability outside (0,1]");
205 if (!(mean_ >= 0.0) || !(mean_ <= 1.0))
206 throw InputError(
"SolverLDES (native engine): the Bernoulli at " + where_ +
207 " has a mean outside [0,1]");
211 const double p = 1.0 - scv_ * mean_;
212 if (!(p > 0.0) || p > 1.0)
213 throw InputError(
"SolverLDES (native engine): the Binomial at " + where_ +
214 " has moments no (n,p) pair realises");
216 b_ = std::floor(mean_ / p + 0.5);
240 throw UnsupportedError(
"SolverLDES (native engine): the process at " + where_ +
241 " is of a family this engine does not sample");
247 double mean()
const {
return mean_; }
259 if (!has_schedule_)
return next(g);
260 return schedule_sample(g, from);
288 const double v = trace_[trace_idx_];
289 trace_idx_ = (trace_idx_ + 1) % trace_.size();
292 return (v <= 0.0) ? 1e-9 : v;
311 return std::ceil(std::log(1.0 -
uniform01(g)) / std::log(1.0 - a_));
326 if (erlang_k_ <= 0)
return next_markovian(g);
328 for (
int i = 0; i < erlang_k_; ++i)
333 return next_markovian(g);
342 int phase()
const {
return has_phase_ ?
static_cast<int>(phase_) : -1; }
344 if (has_phase_ && p >= 0 &&
static_cast<std::size_t
>(p) < map_.order()) {
345 phase_ =
static_cast<std::size_t
>(p);
351 void require_mean()
const {
352 if (!(mean_ > 0.0) || !std::isfinite(mean_))
353 throw InputError(
"SolverLDES (native engine): the process at " + where_ +
354 " has a mean that is neither finite nor positive");
356 void require_moments()
const {
358 if (!(scv_ > 0.0) || !std::isfinite(scv_))
359 throw InputError(
"SolverLDES (native engine): the process at " + where_ +
360 " has an SCV that is neither finite nor positive");
365 if (d.
D0.rows() == 0 || d.
D0.rows() != d.
D1.rows())
366 throw InputError(
"SolverLDES (native engine): the process at " + where_ +
367 " declares no (D0,D1) representation to sample");
368 const std::size_t K = d.
D0.rows();
371 for (std::size_t i = 0; i < K; ++i)
372 for (std::size_t j = 0; j < K; ++j) {
380 if (!(mean_ > 0.0)) {
383 throw InputError(
"SolverLDES (native engine): the process at " + where_ +
384 " has a non-positive mean under its (D0,D1) representation");
398 me_.reset(
new mam::MeSampler<double>(map_));
412 void recover_erlang() {
414 const std::size_t n = map_.order();
416 const double lambda = -map_.D0(0, 0);
417 if (!(lambda > 0.0))
return;
418 const double tol = 1e-12 * lambda;
419 for (std::size_t i = 0; i < n; ++i) {
420 if (std::fabs(-map_.D0(i, i) - lambda) > tol)
return;
421 for (std::size_t j = 0; j < n; ++j) {
422 if (j == i)
continue;
423 const double want = (j == i + 1) ? lambda : 0.0;
424 if (std::fabs(map_.D0(i, j) - want) > tol)
return;
427 for (std::size_t j = 0; j < n; ++j) {
428 const double want = (i + 1 == n && j == 0) ? lambda : 0.0;
429 if (std::fabs(map_.D1(i, j) - want) > tol)
return;
432 erlang_k_ =
static_cast<int>(n);
433 erlang_rate_ = lambda;
454 double next_map_java(Rng& g) {
455 const std::size_t n = map_.order();
459 const double lambda = map_.D1(0, 0);
460 return -std::log(g.aux.next_double()) / lambda;
465 const double r = g.aux.next_double();
467 for (std::size_t i = 0; i < n; ++i) {
477 std::vector<double> row(2 * n, 0.0);
480 const double rate = -map_.D0(phase_, phase_);
481 sample += -std::log(g.aux.next_double()) / rate;
482 for (std::size_t k = 0; k < n; ++k) {
483 row[k] = map_.D0(phase_, k);
484 row[n + k] = map_.D1(phase_, k);
487 std::size_t next_state = 2 * n - 1;
489 const double r = g.aux.next_double();
490 for (std::size_t j = 0; j < 2 * n; ++j) {
491 sum += row[j] / rate;
503 if (next_state >= 2 * n - 1 && go) {
509 phase_ = (next_state < n) ? next_state : next_state - n;
514 double next_markovian(Rng& g) {
515 std::vector<double> out;
520 return next_map_java(g);
528 return me_->next(g.mc);
534 std::vector<double> a_next;
539 std::vector<double> start;
540 if (has_phase_ && phase_known_) {
541 start.assign(map_.order(), 0.0);
545 if (has_phase_ && !tr.last.empty()) {
550 if (out.empty())
return 0.0;
558 void load_schedule(
const lang::Distrib<T>& d) {
559 if (!d.has_schedule())
560 throw InputError(
"SolverLDES (native engine): the time-inhomogeneous process at " +
561 where_ +
" carries no segment schedule");
562 for (
const T& b : d.sched_bp) bp_.push_back(num_traits<T>::to_double(b));
563 if (bp_.size() != d.sched_D0.size() + 1)
564 throw InputError(
"SolverLDES (native engine): the schedule at " + where_ +
565 " has a boundary vector that does not bound its segments");
566 for (std::size_t k = 0; k < d.sched_D0.size(); ++k) {
567 const std::size_t H = d.sched_D0[k].rows();
568 mam::Map<double> seg;
569 seg.D0 = Matrix<double>(H, H, 0.0);
570 seg.D1 = Matrix<double>(H, H, 0.0);
571 for (std::size_t a = 0; a < H; ++a)
572 for (std::size_t b2 = 0; b2 < H; ++b2) {
573 seg.D0(a, b2) = num_traits<T>::to_double(d.sched_D0[k](a, b2));
574 seg.D1(a, b2) = num_traits<T>::to_double(d.sched_D1[k](a, b2));
576 segs_.push_back(seg);
578 cyclic_ = d.sched_cyclic;
579 has_schedule_ =
true;
581 if (!(mean_ > 0.0)) mean_ = num_traits<T>::to_double(d.mean);
586 double period()
const {
return bp_.back() - bp_.front(); }
589 int segment_at(
double t)
const {
590 double offset = t - bp_.front();
591 const double per = period();
593 offset = std::fmod(offset, per);
594 if (offset < 0.0) offset += per;
595 }
else if (offset < 0.0 || offset >= per) {
598 const double pos = bp_.front() + offset;
599 for (std::size_t k = 0; k + 1 < bp_.size(); ++k)
600 if (pos < bp_[k + 1])
return static_cast<int>(k);
601 return static_cast<int>(segs_.size()) - 1;
615 double schedule_sample(Rng& g,
double from) {
616 const std::size_t H = segs_[0].order();
617 double elapsed = 0.0, pos = from;
618 for (
int guard = 0; guard < 1000000; ++guard) {
619 const int idx = segment_at(pos);
620 if (idx < 0)
return 0.0;
621 double offset = pos - bp_.front();
623 offset = std::fmod(offset, period());
624 if (offset < 0.0) offset += period();
626 const double to_boundary = (bp_[
static_cast<std::size_t
>(idx) + 1] - bp_.front()) - offset;
627 const mam::Map<double>& seg = segs_[
static_cast<std::size_t
>(idx)];
628 const double total = -seg.D0(phase_, phase_);
629 const bool last_segment =
630 (!cyclic_ &&
static_cast<std::size_t
>(idx) + 1 == segs_.size());
631 if (!(total > 0.0)) {
632 if (last_segment)
return 0.0;
633 elapsed += to_boundary;
637 const double holding = -std::log(
uniform01(g)) / total;
638 if (holding >= to_boundary) {
639 if (last_segment)
return 0.0;
640 elapsed += to_boundary;
651 for (std::size_t j = 0; j < 2 * H; ++j) {
652 const double w = (j < H) ? seg.D1(phase_, j)
653 : ((j - H == phase_) ? 0.0 : seg.D0(phase_, j - H));
656 chosen =
static_cast<int>(j);
661 for (std::size_t j = 2 * H; j-- > 0;) {
662 const double w = (j < H) ? seg.D1(phase_, j)
663 : ((j - H == phase_) ? 0.0 : seg.D0(phase_, j - H));
665 chosen =
static_cast<int>(j);
669 if (chosen <
static_cast<int>(H)) {
670 phase_ =
static_cast<std::size_t
>(chosen);
673 phase_ =
static_cast<std::size_t
>(chosen) - H;
680 double mean_ = 0.0, scv_ = 1.0, rate_ = 0.0;
681 double a_ = 0.0, b_ = 0.0;
683 double erlang_rate_ = 0.0;
684 std::vector<double> trace_;
685 std::size_t trace_idx_ = 0;
686 mam::Map<double> map_;
687 std::vector<double> entry_;
688 std::shared_ptr<mam::MeSampler<double>> me_;
689 bool has_phase_ =
false, phase_known_ =
false;
692 bool has_schedule_ =
false, cyclic_ =
false;
693 std::vector<double> bp_;
694 std::vector<mam::Map<double>> segs_;
695 std::size_t phase_ = 0;