21 const std::size_t M=
sn.stations.size(),R=
sn.nclasses;
22 for(
const auto& s:
sn.stations)
if(R>1&&s.sched==SS::FCFS)
23 throw UnsupportedError(
ErrorKind{
"MultiClass"},
"SSA MLMC requires a population state; multiclass FCFS needs queue order");
24 for(
const auto& s:
sn.statespace)
if(s.second.rows()!=1)
27 if(!ctmc::analyzer_detail::default_init_state(
sn,init))
throw InputError(
"Invalid MLMC initial state");
28 std::vector<std::size_t> retained,keep,routeIndex(
sn.nodes.size());
29 for(std::size_t node=0;node<
sn.nodes.size();++node)
if(
sn.nodes[node].station!=0||
sn.nodes[node].nodetype==NT::Sink) {
30 routeIndex[node]=retained.size(); retained.push_back(node);
31 for(std::size_t r=0;r<R;++r) keep.push_back(node*R+r);
35 p.
x0.assign(M*R,0); p.nu.resize(M*R);
36 std::vector<std::size_t> origins;
37 std::vector<double> weights,servers(M);
38 std::vector<SS> sched(M); std::vector<bool> source(M);
39 std::vector<std::vector<double>> mu(M,std::vector<double>(R));
40 for(std::size_t i=0;i<M;++i) {
41 std::size_t node=
sn.station_to_node[i]; source[i]=
sn.nodes[node-1].nodetype==NT::Source;
42 sched[i]=
sn.stations[i].sched; servers[i]=
sn.stations[i].nservers;
45 for(std::size_t r=0;r<R;++r) p.x0[i*R+r]=a.second[r];
47 for(std::size_t r=0;r<R;++r) {
48 double rate=
sn.rates(i,r); mu[i][r]=std::isnan(rate)?0:rate;
49 if(mu[i][r]==0)
continue;
50 if(!std::isfinite(rate)||rate<0)
throw InputError(
"MLMC requires finite exponential rates");
52 for(std::size_t dest=0;dest<retained.size()*R;++dest) {
53 double w=routing(routeIndex[node-1]*R+r,dest);
54 if(!std::isfinite(w)||w<0)
throw InputError(
"Invalid MLMC routing probability");
55 total+=w;
if(w==0)
continue;
56 std::size_t j=retained[dest/R],s=dest%R;
57 std::vector<double> change(M*R,0);
58 if(!source[i]) change[i*R+r]--;
59 if(
sn.nodes[j].nodetype!=NT::Sink) {
60 if(
sn.nodes[j].nodetype==NT::Source||
sn.nodes[j].station==0)
62 change[(
sn.nodes[j].station-1)*R+s]++;
64 for(std::size_t a=0;a<M*R;++a) p.nu[a].push_back(change[a]);
65 origins.push_back(i*R+r); weights.push_back(w);
67 if(std::abs(total-1)>1e-10)
throw InputError(
"Active station/class routing must sum to one");
70 std::vector<qn::NetworkStruct<double>::Reward> rewards;
71 for(
const auto& r:
sn.reward)
if(rewardName.empty()||r.name==rewardName) {
72 out.
names.push_back(r.name); rewards.push_back(r);
74 if(!rewardName.empty()&&rewards.empty())
throw InputError(
"Unknown reward: "+rewardName);
75 p.rates=[=](
const std::vector<double>& x) {
76 std::vector<double> d(M*R,0);
77 for(std::size_t i=0;i<M;++i) {
78 double n=0;
for(std::size_t r=0;r<R;++r) n+=std::max(x[i*R+r],0.0);
79 for(std::size_t r=0;r<R;++r) {
80 if(source[i]) d[i*R+r]=mu[i][r];
81 else if(sched[i]==SS::INF) d[i*R+r]=mu[i][r]*std::max(x[i*R+r],0.0);
82 else if(n>0) d[i*R+r]=mu[i][r]*std::max(x[i*R+r],0.0)*std::min(n,servers[i])/n;
85 std::vector<double> a(origins.size());
86 for(std::size_t k=0;k<a.size();++k) a[k]=d[origins[k]]*weights[k];
89 p.observe=[rewards](
const std::vector<double>& x) {
90 std::vector<double> f;
for(
const auto& r:rewards) f.push_back(r.fn(x));
return f;
std::pair< T, std::vector< T > > to_marginal_aggr(const NetworkStruct< T > &sn, std::size_t ind, const std::vector< T > &state_i)
Port of State.toMarginalAggr: the job counts of one node's state row, without the per-phase detail to...
std::vector< MlmcResult > solver_ssa_get_tran_reward(const qn::NetworkStruct< double > &sn, const SsaOptions &opt, const std::vector< double > ×, const std::string &name="", const MlmcOptions &control=MlmcOptions())
SSA's finite-horizon reward getter.