LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
ssa_mlmc.h
Go to the documentation of this file.
1#ifndef LINE_SOLVERS_SSA_MLMC_H
2#define LINE_SOLVERS_SSA_MLMC_H
3
4#include <algorithm>
5#include <cmath>
6#include <functional>
7#include <numeric>
8#include <limits>
9#include <random>
10#include <stdexcept>
11#include <string>
12#include <vector>
13#include <boost/math/distributions/normal.hpp>
14
15namespace line { namespace ssa {
16
17/** Generic fixed-increment jump process. Rates/rewards include signed auxiliary states. */
19 std::vector<double> x0;
20 std::vector<std::vector<double>> nu; // dimension by channels
21 std::function<std::vector<double>(const std::vector<double>&)> rates, observe;
22};
24 std::vector<double> absTol{0.1};
25 double confidenceLevel=0.95;
27 int maxPaths=1000000, maxEvents=1000000;
28 unsigned long seed=23000;
29};
30struct MlmcResult {
32 std::vector<std::vector<double>> confidenceInterval, levelMeans, levelVariances, pilotVariances;
33 std::vector<int> pathsPerLevel;
34 std::vector<bool> samplingToleranceMet;
36 double time=0, confidenceLevel=0.95;
37 std::string biasStatus, method;
38 std::vector<std::string> names;
39};
40namespace mlmc_detail {
41class Engine {
42 using Vec=std::vector<double>;
43 const MlmcProblem& p;
45 double time,z;
46 std::size_t d,k,q,nl;
47 std::vector<int> nsteps;
48 Vec steps,tol,initial;
49 std::mt19937_64 rng;
50 double work=0;
51 [[noreturn]] static void fail(const char* s) { throw std::invalid_argument(s); }
52 Vec rates(const Vec& x) {
53 Vec a=p.rates(x); double sum=0;
54 if(a.size()!=k) fail("MLMC rate dimension changed");
55 for(double v:a) { if(!std::isfinite(v)||v<0) fail("MLMC rates must be finite and nonnegative on auxiliary states"); sum+=v; }
56 if(!std::isfinite(sum)) fail("MLMC total rate overflow");
57 return a;
58 }
59 Vec observe(const Vec& x) {
60 Vec f=p.observe(x);
61 if(f.empty()) fail("Define at least one MLMC reward");
62 for(double v:f) if(!std::isfinite(v)) fail("MLMC rewards must be finite on auxiliary states");
63 return f;
64 }
65 double uniform() { return std::generate_canonical<double,53>(rng); }
66 double poisson(double mean) {
67 if(mean==0) return 0;
68 if(!std::isfinite(mean)||mean>std::numeric_limits<int>::max()/4.0)
69 fail("MLMC Poisson mean exceeds supported range; refine the grid");
70 return static_cast<double>(std::poisson_distribution<int>(mean)(rng));
71 }
72 void jump(Vec& x,std::size_t channel,double count) {
73 for(std::size_t i=0;i<d;++i) x[i]+=p.nu[i][channel]*count;
74 }
75 Vec difference(const Vec& x,const Vec& c) {
76 Vec a=observe(x),b=observe(c);
77 if(a.size()!=q||b.size()!=q) fail("MLMC reward dimension changed");
78 for(std::size_t i=0;i<q;++i) a[i]-=b[i];
79 return a;
80 }
81 Vec sample(int level) {
82 Vec x=p.x0,c=p.x0; work=0;
83 if(level==0) {
84 for(int j=0;j<nsteps[0];++j) {
85 Vec a=rates(x);
86 for(std::size_t r=0;r<k;++r) jump(x,r,poisson(a[r]*steps[0]));
87 work+=std::max<std::size_t>(k,1);
88 }
89 return observe(x);
90 }
91 if(level<=o.levels) {
92 Vec ac; double h=steps[level];
93 for(int j=0;j<nsteps[level];++j) {
94 if(j%o.refinementFactor==0) ac=rates(c);
95 Vec af=rates(x);
96 for(std::size_t r=0;r<k;++r) {
97 double shared=std::min(af[r],ac[r]),common=poisson(shared*h);
98 jump(x,r,common+poisson((af[r]-shared)*h));
99 jump(c,r,common+poisson((ac[r]-shared)*h));
100 }
101 work+=3*std::max<std::size_t>(k,1);
102 }
103 return difference(x,c);
104 }
105 double t=0,h=steps.back(); int events=0;
106 for(int j=1;j<=nsteps.back();++j) {
107 Vec ac=rates(c); double boundary=j*h;
108 while(t<boundary) {
109 Vec ax=rates(x),a(3*k); double total=0;
110 for(std::size_t r=0;r<k;++r) {
111 a[r]=std::min(ax[r],ac[r]); a[k+r]=ax[r]-a[r]; a[2*k+r]=ac[r]-a[r];
112 total+=ax[r]+ac[r]-a[r];
113 }
114 work+=3*std::max<std::size_t>(k,1);
115 if(!std::isfinite(total)) fail("MLMC split rate overflow");
116 if(total==0) { t=boundary; break; }
117 double dt=-std::log(std::max(std::numeric_limits<double>::min(),uniform()))/total;
118 if(t+dt>=boundary) { t=boundary; break; }
119 if(t+dt==t) fail("MLMC event time cannot advance");
120 double u=uniform()*total,cs=0; std::size_t channel=0;
121 for(;channel+1<a.size();++channel) { cs+=a[channel]; if(u<cs) break; }
122 if(channel<2*k) jump(x,channel%k,1);
123 if(channel<k||channel>=2*k) jump(c,channel%k,1);
124 for(double v:x) if(v<0) fail("MLMC exact process left the nonnegative domain");
125 t+=dt;
126 if(++events>o.maxEvents) fail("MLMC exact correction exceeds maxEvents");
127 }
128 }
129 return difference(x,c);
130 }
131 struct Batch { Vec mean,var; double cost; };
132 Batch batch(int level,int n) {
133 Batch b{Vec(q),Vec(q),0};
134 for(int i=1;i<=n;++i) {
135 Vec y=sample(level); b.cost+=work;
136 for(double v:y) if(!std::isfinite(v)) fail("MLMC reward difference overflow");
137 if(y.size()!=q) fail("MLMC reward dimension changed");
138 for(std::size_t r=0;r<q;++r) {
139 double delta=y[r]-b.mean[r]; b.mean[r]+=delta/i; b.var[r]+=delta*(y[r]-b.mean[r]);
140 }
141 }
142 for(double v:b.mean) if(!std::isfinite(v)) fail("MLMC moment overflow");
143 for(double& v:b.var) {
144 if(!std::isfinite(v)) fail("MLMC moment overflow");
145 v=std::max(0.0,v/(n-1));
146 }
147 b.cost=std::max(1.0,b.cost/n); return b;
148 }
149public:
150 Engine(const MlmcProblem& p_,double time_,const MlmcOptions& o_):p(p_),o(o_),time(time_),rng(o.seed) {
151 if(!std::isfinite(time)||time<0||o.levels<0||o.levels>20||o.baseSteps<1||o.refinementFactor<2
152 ||o.pilotPaths<2||o.minPaths<2||o.maxPaths<2||o.maxEvents<1||!(o.confidenceLevel>0&&o.confidenceLevel<1))
153 fail("Invalid MLMC time, hierarchy or sampling options");
154 d=p.x0.size();
155 if(d==0||p.nu.size()!=d) fail("Invalid MLMC state dimension");
156 k=p.nu[0].size();
157 for(std::size_t i=0;i<d;++i) {
158 if(!std::isfinite(p.x0[i])||p.x0[i]<0||p.x0[i]!=std::floor(p.x0[i])||p.nu[i].size()!=k)
159 fail("MLMC needs nonnegative integer initial counts");
160 for(double v:p.nu[i]) if(!std::isfinite(v)||v!=std::floor(v)) fail("MLMC needs integer increments");
161 }
162 initial=observe(p.x0); q=initial.size(); nl=o.levels+2;
163 if(o.absTol.size()!=1&&o.absTol.size()!=q) fail("absTol must be scalar or one per reward");
164 for(std::size_t r=0;r<q;++r) {
165 double v=o.absTol[o.absTol.size()==1?0:r];
166 if(!std::isfinite(v)||v<=0) fail("absTol must be positive");
167 tol.push_back(v);
168 }
169 long long n=o.baseSteps;
170 for(int l=0;l<=o.levels;++l) {
171 if(n>o.maxEvents) fail("MLMC hierarchy exceeds maxEvents");
172 nsteps.push_back(static_cast<int>(n)); steps.push_back(time/n);
173 if(time>0 && steps.back()==0) fail("MLMC time steps underflow; use a coarser grid"); n*=o.refinementFactor;
174 }
175 z=-boost::math::quantile(boost::math::normal(),(1-o.confidenceLevel)/2);
176 }
178 MlmcResult e; e.time=time; e.confidenceLevel=o.confidenceLevel; e.steps=steps;
180 e.levelMeans.assign(q,Vec(nl)); e.levelVariances=e.levelMeans; e.pilotVariances=e.levelMeans;
181 e.pathsPerLevel.assign(nl,0); e.costsPerSample.assign(nl,0);
182 e.estimate.assign(q,0); e.standardError.assign(q,0); e.confidenceInterval.assign(q,Vec(2));
183 e.samplingToleranceMet.assign(q,false);
184 if(time>0) {
185 if(static_cast<long long>(nl)*(static_cast<long long>(o.pilotPaths)+o.minPaths)>o.maxPaths)
186 fail("MLMC minimum sampling allocation exceeds maxPaths");
187 for(std::size_t l=0;l<nl;++l) { Batch b=batch(static_cast<int>(l),o.pilotPaths); e.costsPerSample[l]=b.cost;
188 for(std::size_t r=0;r<q;++r) e.pilotVariances[r][l]=b.var[r]; }
189 std::fill(e.pathsPerLevel.begin(),e.pathsPerLevel.end(),o.minPaths);
190 for(std::size_t r=0;r<q;++r) {
191 double sum=0;
192 for(std::size_t l=0;l<nl;++l) sum+=std::sqrt(e.pilotVariances[r][l]*e.costsPerSample[l]);
193 for(std::size_t l=0;l<nl;++l) {
194 double n=std::ceil(sum/std::pow(tol[r]/z,2)*std::sqrt(e.pilotVariances[r][l]/e.costsPerSample[l]));
195 if(!std::isfinite(n)||n>o.maxPaths) fail("MLMC pilot allocation exceeds maxPaths");
196 e.pathsPerLevel[l]=std::max(e.pathsPerLevel[l],static_cast<int>(n));
197 }
198 }
199 long long total=nl*o.pilotPaths; for(int n:e.pathsPerLevel) total+=n;
200 if(total>o.maxPaths) fail("MLMC pilot allocation exceeds maxPaths");
201 for(std::size_t l=0;l<nl;++l) { Batch b=batch(static_cast<int>(l),e.pathsPerLevel[l]);
202 for(std::size_t r=0;r<q;++r) {
203 e.levelMeans[r][l]=b.mean[r]; e.levelVariances[r][l]=b.var[r];
204 e.estimate[r]+=b.mean[r]; e.standardError[r]+=b.var[r]/e.pathsPerLevel[l];
205 }
206 }
207 } else e.estimate=initial;
208 for(std::size_t r=0;r<q;++r) {
209 e.standardError[r]=std::sqrt(e.standardError[r]);
210 e.confidenceInterval[r][0]=e.estimate[r]-z*e.standardError[r];
211 e.confidenceInterval[r][1]=e.estimate[r]+z*e.standardError[r];
212 e.samplingToleranceMet[r]=z*e.standardError[r]<=tol[r];
213 }
214 e.biasStatus=time==0?"zero":"zero-discretization-bias";
215 return e;
216 }
217};
218} // mlmc_detail
219
220/** Independent pilots, fixed production counts, pointwise normal intervals. */
221inline MlmcResult ssa_mlmc(const MlmcProblem& p,double time,const MlmcOptions& o=MlmcOptions()) {
222 return mlmc_detail::Engine(p,time,o).run();
223}
224}} // line::ssa
225#endif
Engine(const MlmcProblem &p_, double time_, const MlmcOptions &o_)
Definition ssa_mlmc.h:150
MlmcResult ssa_mlmc(const MlmcProblem &p, double time, const MlmcOptions &o=MlmcOptions())
Independent pilots, fixed production counts, pointwise normal intervals.
Definition ssa_mlmc.h:221
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
unsigned long seed
Definition ssa_mlmc.h:28
std::vector< double > absTol
Definition ssa_mlmc.h:24
Generic fixed-increment jump process.
Definition ssa_mlmc.h:18
std::vector< double > x0
Definition ssa_mlmc.h:19
std::function< std::vector< double >(const std::vector< double > &)> rates
Definition ssa_mlmc.h:21
std::function< std::vector< double >(const std::vector< double > &)> observe
Definition ssa_mlmc.h:21
std::vector< std::vector< double > > nu
Definition ssa_mlmc.h:20
std::vector< std::vector< double > > levelMeans
Definition ssa_mlmc.h:32
std::string method
Definition ssa_mlmc.h:37
std::vector< bool > samplingToleranceMet
Definition ssa_mlmc.h:34
std::vector< double > steps
Definition ssa_mlmc.h:31
std::vector< int > pathsPerLevel
Definition ssa_mlmc.h:33
std::vector< double > estimate
Definition ssa_mlmc.h:31
std::vector< std::vector< double > > levelVariances
Definition ssa_mlmc.h:32
std::vector< double > standardError
Definition ssa_mlmc.h:31
std::string biasStatus
Definition ssa_mlmc.h:37
std::vector< double > costsPerSample
Definition ssa_mlmc.h:31
std::vector< std::vector< double > > confidenceInterval
Definition ssa_mlmc.h:32
std::vector< std::vector< double > > pilotVariances
Definition ssa_mlmc.h:32
std::vector< std::string > names
Definition ssa_mlmc.h:38