5#ifndef LINE_LANG_QN_ENVIRONMENT_H
6#define LINE_LANG_QN_ENVIRONMENT_H
100 low.reserve(name.size());
101 for (std::size_t i = 0; i < name.size(); ++i)
102 low.push_back(
static_cast<char>(std::tolower(
static_cast<unsigned char>(name[i]))));
106 throw InputError(
"Environment: unknown reset policy '" + name +
107 "'. The serializable policies are 'keep' (carry the queue lengths across "
108 "the switch) and 'clear' (empty the queues); a custom policy is a function "
109 "and is installed through add_transition, not by name");
200 const std::string& node_name,
203 std::size_t node = 0;
204 for (std::size_t i = 0; i <
sn.nodes.size(); ++i)
205 if (
sn.nodes[i].name == node_name) {
210 throw InputError(
"Environment: node '" + node_name +
211 "' is not in the base model, so it has no service to degrade");
212 const std::size_t ist =
sn.nodes[node - 1].station;
214 throw InputError(
"Environment: node '" + node_name +
215 "' is not a station and carries no service distribution, so it cannot "
216 "break down into a degraded service");
222 for (std::size_t r = 1; r <=
sn.classes.size(); ++r)
sn.set_service(ist, r, d);
232 if (
nstages == 0)
throw InputError(
"Environment: a random environment needs a stage");
235 std::size_t
nstages()
const {
return stages_.size(); }
236 const std::string&
name()
const {
return name_; }
239 void set_stage(std::size_t e,
const std::string& nm,
const std::string& type,
242 stages_[e].name = nm;
243 stages_[e].type = type;
244 stages_[e].model = model;
245 stages_[e].has_model =
true;
246 stages_[e].has_lqn =
false;
263 void set_lqn_stage(std::size_t e,
const std::string& nm,
const std::string& type,
266 stages_[e].name = nm;
267 stages_[e].type = type;
268 stages_[e].lqn_model = model;
269 stages_[e].has_lqn =
true;
270 stages_[e].has_model =
false;
277 return stages_[e].has_lqn;
282 for (std::size_t e = 0; e < stages_.size(); ++e)
283 if (stages_[e].has_lqn)
return true;
298 for (std::size_t e = 0; e < stages_.size(); ++e)
299 if (stages_[e].has_lqn)
301 who +
": stage " + std::to_string(e + 1) +
" ('" + stages_[e].
name +
302 "') holds a LayeredNetwork, and " + why +
303 ". The mean-field coupling (method 'default' / 'meanfield') is the one that "
304 "solves a layered stage, through SolverLN");
312 arcs_[e][h].enabled =
true;
313 arcs_[e][h].dist = d;
314 arcs_[e][h].reset = reset;
334 if (!arcs_[e][h].enabled)
335 throw InputError(
"Environment: no transition " + stages_[e].
name +
" -> " +
337 " is declared, so its rate cannot be made state dependent");
338 arcs_[e][h].reset_rates = f;
352 if (!arcs_[e][h].enabled)
353 throw InputError(
"Environment: no transition " + stages_[e].
name +
" -> " +
354 stages_[h].
name +
" is declared, so its distribution cannot be set");
355 arcs_[e][h].dist = d;
360 for (std::size_t e = 0; e < stages_.size(); ++e)
361 if (stages_[e].has_model && stages_[e].
name == nm)
return e;
362 return stages_.size();
376 if (!arcs_[e][h].enabled)
377 throw InputError(
"Environment: no transition " + stages_[e].
name +
" -> " +
379 " is declared, so it cannot be given a reset policy");
380 arcs_[e][h].reset = reset;
386 const std::vector<NodeFailure<T>>&
node_failures()
const {
return node_failures_; }
390 for (std::size_t i = 0; i < node_failures_.size(); ++i)
391 if (node_failures_[i].node == nm)
return i;
392 return node_failures_.size();
412 const std::string& reset_policy =
"keep") {
416 throw InputError(
"Environment: the UP and DOWN stages of node '" + node_name +
417 "' must be different stages");
418 if (!stages_[up].has_model)
set_stage(up,
"UP",
"operational", base);
428 record_node_failure(nf);
433 const std::string& reset_policy =
"keep") {
436 if (down == stages_.size())
438 "' is not defined; declare the breakdown of node '" + node_name +
439 "' before its repair");
440 if (up == stages_.size())
441 throw InputError(
"Environment: no UP stage is defined, so node '" + node_name +
442 "' has nothing to be repaired into");
446 if (idx == node_failures_.size())
447 throw InputError(
"Environment: no breakdown is recorded for node '" + node_name +
448 "', so its repair would describe a failure that was never declared");
449 node_failures_[idx].repair = repair;
450 node_failures_[idx].has_repair =
true;
451 node_failures_[idx].repair_reset = reset_policy;
459 const std::string& breakdown_reset =
"keep",
460 const std::string& repair_reset =
"keep") {
461 add_node_breakdown(up, down, base, node_name, breakdown, down_service, breakdown_reset);
477 const std::string& breakdown_reset,
478 const std::string& repair_reset) {
481 if (up == stages_.size())
482 throw InputError(
"Environment: cannot register a node failure on '" + node_name +
483 "': no UP stage is defined in this environment");
484 if (down == stages_.size())
485 throw InputError(
"Environment: cannot register a node failure on '" + node_name +
487 "' stage is defined in this environment");
501 record_node_failure(nf);
512 const std::size_t E = stages_.size();
513 for (std::size_t e = 0; e < E; ++e)
514 if (!stages_[e].declared())
515 throw InputError(
"Environment: stage " + std::to_string(e + 1) +
" has no model");
519 for (std::size_t e = 0; e < E; ++e)
520 for (std::size_t h = 0; h < E; ++h)
proc[e][h] = arc_mmap(e, h, E);
524 std::vector<double> lambda(E, 0.0);
525 for (std::size_t e = 0; e < E; ++e) {
529 for (std::size_t h = 0; h < E; ++h) {
530 if (h == e)
continue;
531 ht = superpose_collapsed(ht,
proc[e][h]);
536 for (
double v : cl) tot += v;
538 for (std::size_t h = 0; h < E; ++h) Pemb(e, h) = cl[h] / tot;
540 lambda[e] = (m > 0.0) ? 1.0 / m : 0.0;
543 bool all_positive =
true;
544 for (
double v : lambda)
545 if (!(v > 0.0)) all_positive =
false;
548 "Environment: a stage has no finite holding time, so the environment is "
549 "absorbing; the reference leaves that case unimplemented (Environment.init has "
550 "no branch for it)");
555 for (std::size_t e = 0; e < E; ++e)
556 for (std::size_t h = 0; h < E; ++h)
557 A(e, h) = -lambda[e] * ((e == h ? 1.0 : 0.0) - Pemb(e, h));
561 for (std::size_t e = 0; e < E; ++e) {
562 for (std::size_t h = 0; h < E; ++h)
566 for (std::size_t h = 0; h < E; ++h) s +=
prob_orig(h, e);
568 for (std::size_t h = 0; h < E; ++h)
prob_orig(h, e) /= s;
601 "Environment: reliability metrics are read from probEnv; call init() first");
602 const std::size_t E = stages_.size();
606 "Environment: no UP stage, so there is nothing for a breakdown to leave; "
607 "reliability metrics apply to an environment built with add_node_breakdown");
608 std::vector<std::size_t> down;
609 for (std::size_t e = 0; e < E; ++e)
610 if (stages_[e].
name.compare(0, 5,
"DOWN_") == 0) down.push_back(e);
613 "Environment: no DOWN_<node> stage, so no node breaks down; reliability metrics "
614 "apply to an environment built with add_node_breakdown");
616 double lambda_total = 0.0;
617 for (std::size_t h : down)
618 if (arcs_[up][h].enabled) lambda_total += arc_rate(up, h);
619 if (!(lambda_total > 0.0))
621 "Environment: no UP -> DOWN transition, so the system never fails and its mean "
622 "time to failure is not a number");
624 std::vector<double> mu, p;
625 for (std::size_t e : down)
626 if (arcs_[e][up].enabled) {
627 mu.push_back(arc_rate(e, up));
632 "Environment: no DOWN -> UP transition, so the system is never repaired and its "
633 "mean time to repair is not a number");
636 r.
mttf = 1.0 / lambda_total;
638 for (
double v : p) ptot += v;
640 for (std::size_t i = 0; i < mu.size(); ++i)
641 r.
mttr += (ptot > 0.0 ? p[i] / ptot : 1.0 /
static_cast<double>(mu.size())) / mu[i];
643 double pup =
prob_env[up], pdown = 0.0;
644 for (std::size_t e : down) pdown +=
prob_env[e];
645 r.
availability = (pup + pdown > 0.0) ? pup / (pup + pdown) : 0.0;
650 std::vector<std::vector<mam::Mmap<double>>>
proc;
658 void check(std::size_t e)
const {
659 if (e >= stages_.size())
throw InputError(
"Environment: stage index out of range");
663 double arc_rate(std::size_t e, std::size_t h)
const {
666 throw InputError(
"Environment: the transition " + stages_[e].
name +
" -> " +
668 " has no positive mean, so it carries no rate to report");
673 void record_node_failure(
const NodeFailure<T>& nf) {
675 if (idx < node_failures_.size())
676 node_failures_[idx] = nf;
678 node_failures_.push_back(nf);
685 mam::Mmap<double> arc_mmap(std::size_t e, std::size_t h, std::size_t E)
const {
687 if (!arcs_[e][h].enabled) {
688 m.D0 = Matrix<double>(1, 1, 0.0);
689 m.D1 = Matrix<double>(1, 1, 0.0);
690 m.Dc.assign(E, Matrix<double>(1, 1, 0.0));
693 const lang::Distrib<T>& d = arcs_[e][h].dist;
694 const std::size_t n = d.
D0.rows();
696 throw InputError(
"Environment: the transition distribution has no representation");
697 m.D0 = Matrix<double>(n, n, 0.0);
698 m.D1 = Matrix<double>(n, n, 0.0);
699 for (std::size_t a = 0; a < n; ++a)
700 for (std::size_t b = 0; b < n; ++b) {
701 m.D0(a, b) = num_traits<T>::to_double(d.D0(a, b));
702 m.D1(a, b) = num_traits<T>::to_double(d.D1(a, b));
704 m.Dc.assign(E, Matrix<double>(n, n, 0.0));
718 static mam::Mmap<double> superpose_collapsed(
const mam::Mmap<double>& a,
719 const mam::Mmap<double>& b) {
723 s.Dc.reserve(a.Dc.size());
724 for (std::size_t c = 0; c < a.Dc.size(); ++c)
725 s.Dc.push_back(collapse(
mam::krons(a.Dc[c], b.Dc[c])));
730 static Matrix<double> collapse(
const Matrix<double>& X) {
731 Matrix<double> Y(X.rows(), X.cols(), 0.0);
732 for (std::size_t i = 0; i < X.rows(); ++i) {
734 for (std::size_t j = 0; j < X.cols(); ++j) s += X(i, j);
741 std::vector<EnvStage<T>> stages_;
742 std::vector<std::vector<EnvArc<T>>> arcs_;
743 std::vector<NodeFailure<T>> node_failures_;
UnsupportedError(const std::string &what)
const std::string & name() const
void set_env_rate_reset(std::size_t e, std::size_t h, const ResetEnvRates< T > &f)
resetEnvRatesFun{e,h}: make the e -> h transition depend on the state the stage is left in.
void register_node_failure(const std::string &node_name, const lang::Distrib< T > &breakdown, const lang::Distrib< T > &down_service, bool has_repair, const lang::Distrib< T > &repair, const std::string &breakdown_reset, const std::string &repair_reset)
Port of registerNodeFailure: attach a breakdown descriptor, and its reset policies,...
const std::vector< NodeFailure< T > > & node_failures() const
nodeFailures, the declarative record of the breakdowns declared here.
void init()
Port of Environment.init().
std::vector< double > rate
1/E[holding time]
Environment(const std::string &nm, std::size_t nstages)
void add_node_breakdown(std::size_t up, std::size_t down, const qn::NetworkStruct< T > &base, const std::string &node_name, const lang::Distrib< T > &breakdown, const lang::Distrib< T > &down_service, const std::string &reset_policy="keep")
Port of addNodeBreakdown, on a FIXED stage count.
const EnvStage< T > & stage(std::size_t e) const
Matrix< double > prob_orig
probOrig(h, e)
std::vector< std::vector< mam::Mmap< double > > > proc
proc[e][h]
void add_node_failure_repair(std::size_t up, std::size_t down, const qn::NetworkStruct< T > &base, const std::string &node_name, const lang::Distrib< T > &breakdown, const lang::Distrib< T > &repair, const lang::Distrib< T > &down_service, const std::string &breakdown_reset="keep", const std::string &repair_reset="keep")
addNodeFailureRepair: the two calls above, in order.
void set_transition_dist(std::size_t e, std::size_t h, const lang::Distrib< T > &d)
Replace the distribution of an arc that is already declared.
void set_lqn_stage(std::size_t e, const std::string &nm, const std::string &type, const lqn::LqnStruct< T > &model)
addStage with a LayeredNetwork: the stage holds a LAYERED model.
std::size_t find_stage(const std::string &nm) const
The index of the stage called nm, or nstages() when there is none.
Reliability reliability() const
std::size_t nstages() const
static std::string down_stage_name(const std::string &nm)
The name addNodeBreakdown gives the stage in which nm is down.
std::vector< mam::Mmap< double > > hold_time
holdTime[e]
void set_reset(std::size_t e, std::size_t h, const ResetMarginal &reset)
Install a reset policy on an arc that is already declared.
std::size_t find_node_failure(const std::string &nm) const
findNodeFailure: the descriptor for nm, or node_failures().size().
void reject_lqn_stages(const std::string &who, const std::string &why) const
Refuse an environment carrying a LayeredNetwork stage, by name.
void set_stage(std::size_t e, const std::string &nm, const std::string &type, const qn::NetworkStruct< T > &model)
addStage: name the stage and give it its network.
bool has_lqn_stages() const
True when ANY stage holds a LayeredNetwork.
Matrix< double > pemb
the embedded jump chain
const EnvArc< T > & arc(std::size_t e, std::size_t h) const
bool is_lqn(std::size_t e) const
True when stage e holds a LayeredNetwork rather than a flat network.
void add_node_repair(const std::string &node_name, const lang::Distrib< T > &repair, const std::string &reset_policy="keep")
Port of addNodeRepair: the DOWN_<node> -> UP arc and its policy.
void add_transition(std::size_t e, std::size_t h, const lang::Distrib< T > &d, const ResetMarginal &reset=ResetMarginal())
addTransition: enable e -> h with a distribution and a reset policy.
std::vector< double > prob_env
probEnv
A network plus its refreshed NetworkStruct.
Courtois decomposition of a nearly completely decomposable (NCD) CTMC.
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
LayeredNetworkStruct, the flattened description of a layered queueing network.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
Marked MAP (MMAP) algebra: per-class rates, class probabilities, superposition, normalization and sca...
std::function< Matrix< double >(const Matrix< double > &)> ResetMarginal
The reset policy of a transition, resetFun in the reference.
qn::NetworkStruct< T > env_degraded_model(const qn::NetworkStruct< T > &base, const std::string &node_name, const lang::Distrib< T > &down_service)
The DOWN stage network of addNodeBreakdown: the base network with ONE node's service replaced by its ...
std::function< lang::Distrib< T >(const lang::Distrib< T > &, const Matrix< double > &, const Matrix< double > &, const Matrix< double > &)> ResetEnvRates
resetEnvRatesFun in the reference: the state-dependent environment rate.
ResetMarginal env_reset_policy(const std::string &name)
The two NAMED reset policies of Environment.resolveResetPolicy, which are the only ones the JSON inte...
void prior_refresh_moments(Distrib< T > &d)
Write the mixture moments onto a Prior, the counterpart of dist_refresh_moments for the Markovian fam...
void dist_refresh_moments(Distrib< T > &d)
Fill in the first two moments of a distribution given by its matrices.
Mmap< T > mmap_normalize(const Mmap< T > &in)
Clamp negative off-diagonal and per-class entries to zero and rebuild D1 and the diagonal of D0 from ...
Matrix< T > krons(const Matrix< T > &A, const Matrix< T > &B)
Kronecker sum, MATLAB's krons: kron(A, I_nb) + kron(I_na, B).
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
std::vector< T > mmap_count_lambda(const Mmap< T > &m)
Per-class arrival rates, lambda_c = theta D1^(c) e.
ReducibleResult< T > ctmc_solve_reducible(const Matrix< T > &Q, const std::vector< T > &pi0, double zeroColTol=1e-12)
Limiting distribution of a CTMC whose generator may be reducible.
A queueing network and its refreshed NetworkStruct.
Prior: parameter uncertainty as a weighted set of alternative models.
One arc of the environment process.
lang::Distrib< T > dist
the e -> h transition time
ResetMarginal reset
empty means the identity
ResetEnvRates< T > reset_rates
empty means the rate does not depend on the state
One stage: a name, a category, and the model in force while it lasts.
std::string type
the stage category, informational only
lqn::LqnStruct< T > lqn_model
The layered model, when this stage holds a LayeredNetwork.
bool declared() const
True when the stage carries a model of either kind.
qn::NetworkStruct< T > model
getReliabilityTable: MTTF, MTTR, MTBF and availability of an environment built out of node breakdowns...
double mttr
mean time to repair, DOWN -> UP
double availability
stationary probability of being UP
double mttf
mean time to failure, UP -> any DOWN
Environment.nodeFailures{k}: the declarative record of one node breakdown.
std::string repair_reset
empty when there is no repair
std::string breakdown_reset
std::string node
the node that breaks down
lang::Distrib< T > down_service
the node's service while it is down
bool has_repair
false for a breakdown with no repair arc
lang::Distrib< T > breakdown
time to failure, the UP -> DOWN transition
lang::Distrib< T > repair
time to repair, the DOWN -> UP transition
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
An MMAP: the underlying MAP plus the per-class arrival matrices.