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 virtual std::map<std::string,std::string> sensitivity_metric_targets(
54 const lqn::LqnModel<double>&) const { return {}; }
55 virtual double rate_jacobian(const Value&) const { return 0.0; }
56 virtual double decode_jacobian(double) const { return 0.0; }
57protected:
58 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+"'");}
59 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+"'");}
60 std::string name_; std::size_t dimension_;
61};
62
64public: 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");}
65 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_))};}
66 void apply(qn::Network<double>&m,const Value&v)const override{m.set_number_of_servers(node(m.raw_struct(),station_),scalar_value(v));}
67 std::string type()const override{return "server_allocation";} private:std::string station_;int lo_,hi_;
68};
70public: 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){}
71 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_))};}
72 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));}
73 std::string type()const override{return "station_replicas";} private:std::string station_;int lo_,hi_;
74};
75class ServiceRate final:public DecisionVariable{
76public: 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){}
77 Value decode(const std::vector<double>&x)const override{return {lo_+x.at(0)*(hi_-lo_)};}
78 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)));}
79 std::string type()const override{return "service_rate";} private:std::string station_,class_;double lo_,hi_;
80};
82public: 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){}
83 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_))};}
84 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);}
85 std::string type()const override{return "job_population";} private:std::string class_;int lo_,hi_;
86};
88public: 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");}
89 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;}
90 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));}
91 std::string type()const override{return "routing";} private:std::string class_,source_;std::vector<std::string>targets_;
92};
94public: 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){}
95 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;}
96 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);}
97 std::string type()const override{return "class_priority";} private:std::vector<std::string>classes_;std::string mode_;int lo_,hi_;
98};
99
101public:
102 ClassServiceMapping(std::string jobclass, std::vector<std::string> stations,
103 std::string name = "")
104 : DecisionVariable(name.empty() ? jobclass + "_mapping" : std::move(name)),
105 class_(std::move(jobclass)), stations_(std::move(stations)) {}
106
107 Value decode(const std::vector<double>& x) const override {
108 if (stations_.empty()) return {-1.0};
109 const std::size_t index = std::min(
110 static_cast<std::size_t>(std::floor(std::clamp(x.at(0), 0.0, 1.0) * stations_.size())),
111 stations_.size() - 1);
112 return {static_cast<double>(index)};
113 }
114
115 void apply(qn::Network<double>& model, const Value& value) const override {
116 if (stations_.empty()) return;
117 auto& sn = model.raw_struct();
118 const std::size_t mapped_class = cls(sn, class_);
119 const std::size_t selected = std::min(
120 static_cast<std::size_t>(std::max(0.0, std::floor(scalar_value(value)))),
121 stations_.size() - 1);
122 std::vector<bool> blocked(sn.nodes.size() + 1, false);
123 for (std::size_t k = 0; k < stations_.size(); ++k) {
124 const std::size_t station = node(sn, stations_[k]);
125 if (k != selected) blocked[station] = true;
126 }
127
128 std::vector<std::vector<bool>> connected(
129 sn.nodes.size() + 1, std::vector<bool>(sn.nodes.size() + 1, false));
130 for (const auto& block : sn.P)
131 for (std::size_t i = 1; i <= sn.nodes.size(); ++i)
132 for (std::size_t j = 1; j <= sn.nodes.size(); ++j)
133 if (block.second(i - 1, j - 1) > 0.0) connected[i][j] = true;
134
136 for (std::size_t r = 1; r <= sn.classes.size(); ++r) {
137 for (std::size_t i = 1; i <= sn.nodes.size(); ++i) {
138 std::size_t degree = 0;
139 for (std::size_t j = 1; j <= sn.nodes.size(); ++j)
140 if (connected[i][j] && (r != mapped_class || !blocked[j])) ++degree;
141 if (degree == 0) continue;
142 const double probability = 1.0 / static_cast<double>(degree);
143 for (std::size_t j = 1; j <= sn.nodes.size(); ++j)
144 if (connected[i][j] && (r != mapped_class || !blocked[j]))
145 routing.set(r, r, i, j, probability);
146 }
147 }
148 sn.P.clear();
149 sn.Peff.clear();
150 model.link(routing);
151 }
152
153 std::string type() const override { return "class_mapping"; }
154
155private:
156 std::string class_;
157 std::vector<std::string> stations_;
158};
159
160using VariablePtr=std::shared_ptr<DecisionVariable>;
161} }
162#endif
Malformed or inconsistent input (dimensions, negative populations, ...).
Definition error.h:37
InputError(const std::string &what)
Definition error.h:39
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:96
Value decode(const std::vector< double > &x) const override
Definition variables.h:95
ClassPriority(std::vector< std::string >c, std::string mode="levels", int lo=1, int hi=10, std::string n="class_priorities")
Definition variables.h:94
std::string type() const override
Definition variables.h:97
ClassServiceMapping(std::string jobclass, std::vector< std::string > stations, std::string name="")
Definition variables.h:102
void apply(qn::Network< double > &model, const Value &value) const override
Definition variables.h:115
Value decode(const std::vector< double > &x) const override
Definition variables.h:107
std::string type() const override
Definition variables.h:153
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:56
virtual double rate_jacobian(const Value &) const
Definition variables.h:55
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:53
static std::size_t cls(const qn::NetworkStruct< double > &s, const std::string &n)
Definition variables.h:59
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:58
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
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:83
JobPopulation(std::string c, int lo, int hi, std::string n="")
Definition variables.h:82
std::string type() const override
Definition variables.h:85
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:84
Value decode(const std::vector< double > &x) const override
Definition variables.h:89
RoutingProbabilities(std::string c, std::string source, std::vector< std::string >targets, std::string n="")
Definition variables.h:88
std::string type() const override
Definition variables.h:91
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:90
ServerAllocation(std::string station, int lo, int hi, std::string n="")
Definition variables.h:64
Value decode(const std::vector< double > &x) const override
Definition variables.h:65
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:66
std::string type() const override
Definition variables.h:67
std::string type() const override
Definition variables.h:79
ServiceRate(std::string station, std::string jobclass, double lo, double hi, std::string n="")
Definition variables.h:76
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:78
Value decode(const std::vector< double > &x) const override
Definition variables.h:77
StationReplicas(std::string station, int lo, int hi, std::string n="")
Definition variables.h:70
Value decode(const std::vector< double > &x) const override
Definition variables.h:71
std::string type() const override
Definition variables.h:73
void apply(qn::Network< double > &m, const Value &v) const override
Definition variables.h:72
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:160
std::vector< double > Value
Definition results.h:32
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:814
The intermediate model, and the second stage that flattens it.
Definition lqn_reader.h:399