LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
variables.h
Go to the documentation of this file.
1#ifndef LINE_OPT_VARIABLES_H
2#define LINE_OPT_VARIABLES_H
3
4/**
5 * @file
6 * @ingroup line_opt
7 * The decision variables of a flat queueing network.
8 *
9 * A `DecisionVariable` is a named box with a dimension and a bounded domain. The
10 * search works in a normalized [0,1] space and `decode` maps a point of that
11 * space onto the model quantity, so an integer variable, a rate and a
12 * probability simplex are all the same thing to the optimizer.
13 *
14 * `ServerAllocation` and `StationReplicas` are integer counts, `ServiceRate` and
15 * `JobPopulation` continuous, `RoutingProbabilities` a simplex whose decode
16 * renormalizes, `ClassPriority` an integer ranking, and
17 * `ClassServiceMapping` an assignment. The layered counterparts live in
18 * `layered_variables.h`.
19 */
20
21#include <algorithm>
22#include <cmath>
23#include <memory>
24#include <optional>
25#include <string>
26#include <vector>
29#include "line/opt/results.h"
30#include "line/util/error.h"
31
32namespace line { namespace opt {
33
35public:
36 explicit DecisionVariable(std::string n,std::size_t d=1):name_(std::move(n)),dimension_(d){}
37 virtual ~DecisionVariable()=default;
38 const std::string& name()const{return name_;} std::size_t dimension()const{return dimension_;}
39 virtual Value decode(const std::vector<double>& x)const=0;
40 virtual void apply(qn::Network<double>&,const Value&)const {
41 throw InputError("line-opt: variable '" + name_ + "' cannot be applied to a flat Network");
42 }
43 virtual void apply(lqn::LqnModel<double>&,const Value&)const {
44 throw InputError("line-opt: variable '" + name_ + "' cannot be applied to a LayeredNetwork");
45 }
46 virtual std::string type()const=0;
47 virtual std::vector<std::string> layers(const lqn::LqnModel<double>&) const { return {}; }
48 virtual std::optional<Value> current_value(const lqn::LqnModel<double>&) const {
49 return std::nullopt;
50 }
51 virtual bool supports_sensitivity() const { return false; }
52 virtual std::string sensitivity_key(const lqn::LqnModel<double>&) const { return {}; }
53 /** Candidate sensitivity-table rows, most specific first; the LN method decides how a layer names its classes. */
54 virtual std::vector<std::string> sensitivity_keys(const lqn::LqnModel<double>& model) const {
55 const std::string key = sensitivity_key(model);
56 return key.empty() ? std::vector<std::string>() : std::vector<std::string>{key};
57 }
58 virtual std::map<std::string,std::string> sensitivity_metric_targets(
59 const lqn::LqnModel<double>&) const { return {}; }
60 virtual double rate_jacobian(const Value&) const { return 0.0; }
61 virtual double decode_jacobian(double) const { return 0.0; }
62protected:
63 static std::size_t node(const qn::NetworkStruct<double>&s,const std::string&n){for(std::size_t i=0;i<s.nodes.size();++i)if(s.nodes[i].name==n)return i+1;throw InputError("line-opt: unknown node '"+n+"'");}
64 static std::size_t cls(const qn::NetworkStruct<double>&s,const std::string&n){for(std::size_t i=0;i<s.classes.size();++i)if(s.classes[i].name==n)return i+1;throw InputError("line-opt: unknown class '"+n+"'");}
65 std::string name_; std::size_t dimension_;
66};
67
69public: ServerAllocation(std::string station,int lo,int hi,std::string n=""):DecisionVariable(n.empty()?station+"_servers":n),station_(std::move(station)),lo_(lo),hi_(hi){if(lo>hi)throw InputError("ServerAllocation: invalid bounds");}
70 Value decode(const std::vector<double>&x)const override{return {double(std::clamp<int>(int(std::llround(lo_+x.at(0)*(hi_-lo_))),lo_,hi_))};}
71 void apply(qn::Network<double>&m,const Value&v)const override{m.set_number_of_servers(node(m.raw_struct(),station_),scalar_value(v));}
72 std::string type()const override{return "server_allocation";} private:std::string station_;int lo_,hi_;
73};
75public: StationReplicas(std::string station,int lo,int hi,std::string n=""):DecisionVariable(n.empty()?station+"_replicas":n),station_(std::move(station)),lo_(lo),hi_(hi){}
76 Value decode(const std::vector<double>&x)const override{return {double(std::clamp<int>(int(std::llround(lo_+x.at(0)*(hi_-lo_))),lo_,hi_))};}
77 void apply(qn::Network<double>&m,const Value&v)const override{auto&sn=m.raw_struct();auto nd=node(sn,station_);auto st=sn.nodes.at(nd-1).station;double base=sn.stations.at(st-1).nservers;if(!std::isfinite(base)||base<1)base=1;m.set_number_of_servers(nd,base*scalar_value(v));}
78 std::string type()const override{return "station_replicas";} private:std::string station_;int lo_,hi_;
79};
80class ServiceRate final:public DecisionVariable{
81public: ServiceRate(std::string station,std::string jobclass,double lo,double hi,std::string n=""):DecisionVariable(n.empty()?station+"_"+jobclass+"_rate":n),station_(std::move(station)),class_(std::move(jobclass)),lo_(lo),hi_(hi){}
82 Value decode(const std::vector<double>&x)const override{return {lo_+x.at(0)*(hi_-lo_)};}
83 void apply(qn::Network<double>&m,const Value&v)const override{auto&s=m.raw_struct();m.set_service(node(s,station_),cls(s,class_),lang::Distrib<double>::exp_rate(scalar_value(v)));}
84 std::string type()const override{return "service_rate";} private:std::string station_,class_;double lo_,hi_;
85};
87public: JobPopulation(std::string c,int lo,int hi,std::string n=""):DecisionVariable(n.empty()?c+"_population":n),class_(std::move(c)),lo_(lo),hi_(hi){}
88 Value decode(const std::vector<double>&x)const override{return {double(std::clamp<int>(int(std::llround(lo_+x.at(0)*(hi_-lo_))),lo_,hi_))};}
89 void apply(qn::Network<double>&m,const Value&v)const override{auto&s=m.raw_struct();s.classes.at(cls(s,class_)-1).population=scalar_value(v);}
90 std::string type()const override{return "job_population";} private:std::string class_;int lo_,hi_;
91};
93public: RoutingProbabilities(std::string c,std::string source,std::vector<std::string>targets,std::string n=""):DecisionVariable(n.empty()?c+"_routing_from_"+source:n,std::max<std::size_t>(1,targets.size()-1)),class_(std::move(c)),source_(std::move(source)),targets_(std::move(targets)){if(targets_.empty())throw InputError("RoutingProbabilities: no targets");}
94 Value decode(const std::vector<double>&x)const override{Value p(targets_.size());if(p.size()==1){p[0]=1;return p;}double rem=1;for(std::size_t i=0;i+1<p.size();++i){p[i]=rem*x.at(i);rem-=p[i];}p.back()=rem;return p;}
95 void apply(qn::Network<double>&m,const Value&v)const override{auto&s=m.raw_struct();auto c=cls(s,class_),src=node(s,source_);for(std::size_t i=0;i<targets_.size();++i)s.set_route(c,c,src,node(s,targets_[i]),v.at(i));}
96 std::string type()const override{return "routing";} private:std::string class_,source_;std::vector<std::string>targets_;
97};
99public: ClassPriority(std::vector<std::string>c,std::string mode="levels",int lo=1,int hi=10,std::string n="class_priorities"):DecisionVariable(std::move(n),mode=="levels"?c.size():std::max<std::size_t>(1,c.size()-1)),classes_(std::move(c)),mode_(std::move(mode)),lo_(lo),hi_(hi){}
100 Value decode(const std::vector<double>&x)const override{Value v;if(mode_=="levels"){for(double z:x)v.push_back(std::round(lo_+z*(hi_-lo_)));return v;}std::vector<std::pair<double,std::size_t>>k;for(std::size_t i=0;i<classes_.size();++i)k.push_back({-(i<x.size()?x[i]:0),i});std::stable_sort(k.begin(),k.end());for(auto&p:k)v.push_back(double(p.second));return v;}
101 void apply(qn::Network<double>&m,const Value&v)const override{auto&s=m.raw_struct();if(mode_=="levels")for(std::size_t i=0;i<classes_.size();++i)s.classes.at(cls(s,classes_[i])-1).prio=int(v.at(i));else for(std::size_t rank=0;rank<v.size();++rank)s.classes.at(cls(s,classes_.at(std::size_t(v[rank])))-1).prio=int(v.size()-rank);}
102 std::string type()const override{return "class_priority";} private:std::vector<std::string>classes_;std::string mode_;int lo_,hi_;
103};
104
106public:
107 ClassServiceMapping(std::string jobclass, std::vector<std::string> stations,
108 std::string name = "")
109 : DecisionVariable(name.empty() ? jobclass + "_mapping" : std::move(name)),
110 class_(std::move(jobclass)), stations_(std::move(stations)) {}
111
112 Value decode(const std::vector<double>& x) const override {
113 if (stations_.empty()) return {-1.0};
114 const std::size_t index = std::min(
115 static_cast<std::size_t>(std::floor(std::clamp(x.at(0), 0.0, 1.0) * stations_.size())),
116 stations_.size() - 1);
117 return {static_cast<double>(index)};
118 }
119
120 void apply(qn::Network<double>& model, const Value& value) const override {
121 if (stations_.empty()) return;
122 auto& sn = model.raw_struct();
123 const std::size_t mapped_class = cls(sn, class_);
124 const std::size_t selected = std::min(
125 static_cast<std::size_t>(std::max(0.0, std::floor(scalar_value(value)))),
126 stations_.size() - 1);
127 std::vector<bool> blocked(sn.nodes.size() + 1, false);
128 for (std::size_t k = 0; k < stations_.size(); ++k) {
129 const std::size_t station = node(sn, stations_[k]);
130 if (k != selected) blocked[station] = true;
131 }
132
133 std::vector<std::vector<bool>> connected(
134 sn.nodes.size() + 1, std::vector<bool>(sn.nodes.size() + 1, false));
135 for (const auto& block : sn.P)
136 for (std::size_t i = 1; i <= sn.nodes.size(); ++i)
137 for (std::size_t j = 1; j <= sn.nodes.size(); ++j)
138 if (block.second(i - 1, j - 1) > 0.0) connected[i][j] = true;
139
141 for (std::size_t r = 1; r <= sn.classes.size(); ++r) {
142 for (std::size_t i = 1; i <= sn.nodes.size(); ++i) {
143 std::size_t degree = 0;
144 for (std::size_t j = 1; j <= sn.nodes.size(); ++j)
145 if (connected[i][j] && (r != mapped_class || !blocked[j])) ++degree;
146 if (degree == 0) continue;
147 const double probability = 1.0 / static_cast<double>(degree);
148 for (std::size_t j = 1; j <= sn.nodes.size(); ++j)
149 if (connected[i][j] && (r != mapped_class || !blocked[j]))
150 routing.set(r, r, i, j, probability);
151 }
152 }
153 sn.P.clear();
154 sn.Peff.clear();
155 model.link(routing);
156 }
157
158 std::string type() const override { return "class_mapping"; }
159
160private:
161 std::string class_;
162 std::vector<std::string> stations_;
163};
164
165using VariablePtr=std::shared_ptr<DecisionVariable>;
166} }
167#endif
Malformed or inconsistent input (dimensions, negative populations, ...): LINE:input:<kind>.
Definition error.h:103
InputError(const std::string &what)
Definition error.h:105
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:101
Value decode(const std::vector< double > &x) const override
Definition variables.h:100
ClassPriority(std::vector< std::string >c, std::string mode="levels", int lo=1, int hi=10, std::string n="class_priorities")
Definition variables.h:99
std::string type() const override
Definition variables.h:102
ClassServiceMapping(std::string jobclass, std::vector< std::string > stations, std::string name="")
Definition variables.h:107
void apply(qn::Network< double > &model, const Value &value) const override
Definition variables.h:120
Value decode(const std::vector< double > &x) const override
Definition variables.h:112
std::string type() const override
Definition variables.h:158
virtual Value decode(const std::vector< double > &x) const =0
virtual void apply(qn::Network< double > &, const Value &) const
Definition variables.h:40
virtual bool supports_sensitivity() const
Definition variables.h:51
virtual double decode_jacobian(double) const
Definition variables.h:61
virtual double rate_jacobian(const Value &) const
Definition variables.h:60
virtual ~DecisionVariable()=default
DecisionVariable(std::string n, std::size_t d=1)
Definition variables.h:36
virtual std::optional< Value > current_value(const lqn::LqnModel< double > &) const
Definition variables.h:48
virtual std::map< std::string, std::string > sensitivity_metric_targets(const lqn::LqnModel< double > &) const
Definition variables.h:58
static std::size_t cls(const qn::NetworkStruct< double > &s, const std::string &n)
Definition variables.h:64
virtual void apply(lqn::LqnModel< double > &, const Value &) const
Definition variables.h:43
static std::size_t node(const qn::NetworkStruct< double > &s, const std::string &n)
Definition variables.h:63
const std::string & name() const
Definition variables.h:38
virtual std::string sensitivity_key(const lqn::LqnModel< double > &) const
Definition variables.h:52
virtual std::vector< std::string > layers(const lqn::LqnModel< double > &) const
Definition variables.h:47
virtual std::vector< std::string > sensitivity_keys(const lqn::LqnModel< double > &model) const
Candidate sensitivity-table rows, most specific first; the LN method decides how a layer names its cl...
Definition variables.h:54
std::size_t dimension() const
Definition variables.h:38
virtual std::string type() const =0
Value decode(const std::vector< double > &x) const override
Definition variables.h:88
JobPopulation(std::string c, int lo, int hi, std::string n="")
Definition variables.h:87
std::string type() const override
Definition variables.h:90
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:89
Value decode(const std::vector< double > &x) const override
Definition variables.h:94
RoutingProbabilities(std::string c, std::string source, std::vector< std::string >targets, std::string n="")
Definition variables.h:93
std::string type() const override
Definition variables.h:96
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:95
ServerAllocation(std::string station, int lo, int hi, std::string n="")
Definition variables.h:69
Value decode(const std::vector< double > &x) const override
Definition variables.h:70
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:71
std::string type() const override
Definition variables.h:72
std::string type() const override
Definition variables.h:84
ServiceRate(std::string station, std::string jobclass, double lo, double hi, std::string n="")
Definition variables.h:81
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:83
Value decode(const std::vector< double > &x) const override
Definition variables.h:82
StationReplicas(std::string station, int lo, int hi, std::string n="")
Definition variables.h:75
Value decode(const std::vector< double > &x) const override
Definition variables.h:76
std::string type() const override
Definition variables.h:78
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:77
A network plus its refreshed NetworkStruct.
std::vector< JobClass > classes
std::vector< NodeDef > nodes
every node, in creation order
A queueing network under construction.
void set_number_of_servers(std::size_t node, double n)
queue.setNumberOfServers(n).
NetworkStruct< T > & raw_struct()
The struct WITHOUT refreshing it, for a caller that is still building.
void link(const RoutingMatrix< T > &Pm)
model.link(P): install the routing.
void set_service(std::size_t node, std::size_t cls, const Distrib< T > &d)
station.setService(class, dist).
The routing matrix a model script fills in, MATLAB's P cell array.
The exception types the port throws.
.lqnx -> LqnStruct, a port of matlab/src/lang/layered/@LayeredNetwork/parseXML.m followed by ....
double scalar_value(const Value &v)
Definition results.h:35
std::shared_ptr< DecisionVariable > VariablePtr
Definition variables.h:165
std::vector< double > Value
Definition results.h:32
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
The Network constructor API: Queue, Delay, Source, Sink, Router, ClassSwitch, Cache,...
What an evaluation and a solve return.
static Distrib exp_rate(const T &r)
Definition lang_types.h:955
The intermediate model, and the second stage that flattens it.
Definition lqn_reader.h:411