LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
ldes_region.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_SOLVERS_LDES_LDES_REGION_H
6#define LINE_SOLVERS_LDES_LDES_REGION_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * Finite capacity regions for the native LDES engine.
12 *
13 * A region caps the jobs held ACROSS a set of stations, which no per-station
14 * capacity can express: three stations each able to hold 5 jobs but at most 6
15 * between them is a region, not three caps. Four constraints may apply at once
16 * and an arrival must satisfy all of them:
17 *
18 * - the GLOBAL job cap of the region;
19 * - the PER-CLASS job cap;
20 * - the MEMORY budget, a weighted count where each class carries its own
21 * footprint (JMT's classSize, so a region can hold many small jobs or few
22 * large ones);
23 * - the LINEAR constraints A n <= b, which subsume the other three and are
24 * what a mix constraint like "2 n_1 + n_2 <= 10" needs.
25 *
26 * A REJECTED ARRIVAL EITHER DROPS OR WAITS, per class, and the two are
27 * different populations. A DROPped job is gone. A WAITQ job is parked OUTSIDE
28 * every station's own buffer -- it has not entered the region, so it is not in
29 * the region's queue length, but it IS still in the system and is charged to
30 * the station it was trying to enter. Conflating the two makes a WAITQ region
31 * lose jobs, which on a closed model changes the population.
32 *
33 * MEMBERSHIP IS INDEPENDENT OF THE CAPS. `members` says which stations the
34 * region spans; a member may have every cap unbounded and still constrain
35 * nothing while participating in the linear constraints.
36 */
37
38#include <cmath>
39#include <cstddef>
40#include <limits>
41#include <string>
42#include <vector>
43
45
46namespace line {
47namespace ldes {
48namespace engine {
49
50/** One finite capacity region, resolved to what the event loop needs. */
51struct Region {
52 std::string name;
53 std::vector<bool> members; ///< per station
54 double global_cap = -1.0; ///< -1 = unbounded
55 std::vector<double> class_cap; ///< per class, -1 = unbounded
56 double max_mem = -1.0; ///< -1 = unbounded
57 std::vector<double> class_size; ///< memory footprint per class
58 std::vector<double> class_weight; ///< occupancy weight per class
59 std::vector<lang::DropStrategy> rule; ///< per class: DROP or WAITQ
60 std::vector<std::vector<double>> lincon_A;
61 std::vector<double> lincon_b;
62
63 // ---- live state --------------------------------------------------------
64 std::vector<double> jobs; ///< per class, inside the region
65 double mem = 0.0; ///< weighted memory in use
66 std::vector<double> blocked; ///< per class, parked outside by WAITQ
67 std::vector<double> dropped; ///< per class, lost
68
69 // ---- time-weighted statistics -----------------------------------------
70 std::vector<double> tot_jobs, tot_weight, tot_mem;
71 double last_update = 0.0;
72 std::vector<double> completed, resp_sum, resp_cnt;
73 std::vector<double> entry_time; ///< unused placeholder for per-job entry
74
75 /**
76 * True when one more job of `r` would violate ANY of the region's caps.
77 *
78 * The memory rule is JMT's: admit iff used + incoming <= max, with an
79 * epsilon because a footprint may be fractional and the running total
80 * accumulates rounding.
81 */
82 bool would_exceed(std::size_t r) const {
83 double total = 0.0;
84 for (double n : jobs) total += n;
85 if (global_cap >= 0.0 && total + 1.0 > global_cap) return true;
86 if (r < class_cap.size() && class_cap[r] >= 0.0 && jobs[r] + 1.0 > class_cap[r])
87 return true;
88 if (max_mem >= 0.0) {
89 const double incoming = (r < class_size.size()) ? class_size[r] : 1.0;
90 if (mem + incoming > max_mem + 1e-9) return true;
91 }
92 for (std::size_t c = 0; c < lincon_A.size(); ++c) {
93 double lhs = (r < lincon_A[c].size()) ? lincon_A[c][r] : 0.0;
94 for (std::size_t k = 0; k < jobs.size() && k < lincon_A[c].size(); ++k)
95 lhs += lincon_A[c][k] * jobs[k];
96 if (lhs > lincon_b[c]) return true;
97 }
98 return false;
99 }
100
101 void enter(std::size_t r) {
102 jobs[r] += 1.0;
103 mem += (r < class_size.size()) ? class_size[r] : 1.0;
104 }
105 void leave(std::size_t r) {
106 jobs[r] -= 1.0;
107 mem -= (r < class_size.size()) ? class_size[r] : 1.0;
108 }
109
110 /**
111 * Advance the region's time integrals to `now`. Call BEFORE any change to
112 * `jobs`, exactly as the station integrals require.
113 *
114 * The JOB count and the WEIGHTED occupancy are separate series because a
115 * region may cap either: reporting only the raw count hides a region that
116 * is memory-bound rather than job-bound.
117 */
118 void update(double now) {
119 const double dt = now - last_update;
120 last_update = now;
121 if (!(dt > 0.0)) return;
122 for (std::size_t r = 0; r < jobs.size(); ++r) {
123 tot_jobs[r] += jobs[r] * dt;
124 const double w = (r < class_weight.size()) ? class_weight[r] : 1.0;
125 tot_weight[r] += jobs[r] * w * dt;
126 const double sz = (r < class_size.size()) ? class_size[r] : 1.0;
127 tot_mem[r] += jobs[r] * sz * dt;
128 }
129 }
130
131 bool drops(std::size_t r) const {
132 return r >= rule.size() || rule[r] != lang::DropStrategy::WAITQ;
133 }
134};
135
136} // namespace engine
137} // namespace ldes
138} // namespace line
139
140#endif // LINE_SOLVERS_LDES_LDES_REGION_H
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
One finite capacity region, resolved to what the event loop needs.
Definition ldes_region.h:51
std::vector< double > entry_time
unused placeholder for per-job entry
Definition ldes_region.h:73
double global_cap
-1 = unbounded
Definition ldes_region.h:54
std::vector< double > tot_mem
Definition ldes_region.h:70
std::vector< double > tot_weight
Definition ldes_region.h:70
double mem
weighted memory in use
Definition ldes_region.h:65
std::vector< double > class_size
memory footprint per class
Definition ldes_region.h:57
std::vector< double > class_cap
per class, -1 = unbounded
Definition ldes_region.h:55
std::vector< double > lincon_b
Definition ldes_region.h:61
std::vector< bool > members
per station
Definition ldes_region.h:53
void enter(std::size_t r)
void update(double now)
Advance the region's time integrals to now.
std::vector< double > blocked
per class, parked outside by WAITQ
Definition ldes_region.h:66
bool drops(std::size_t r) const
std::vector< double > tot_jobs
Definition ldes_region.h:70
std::vector< lang::DropStrategy > rule
per class: DROP or WAITQ
Definition ldes_region.h:59
void leave(std::size_t r)
std::vector< double > class_weight
occupancy weight per class
Definition ldes_region.h:58
std::vector< double > completed
Definition ldes_region.h:72
std::vector< double > resp_cnt
Definition ldes_region.h:72
std::vector< double > resp_sum
Definition ldes_region.h:72
std::vector< double > jobs
per class, inside the region
Definition ldes_region.h:64
std::vector< std::vector< double > > lincon_A
Definition ldes_region.h:60
bool would_exceed(std::size_t r) const
True when one more job of r would violate ANY of the region's caps.
Definition ldes_region.h:82
double max_mem
-1 = unbounded
Definition ldes_region.h:56
std::vector< double > dropped
per class, lost
Definition ldes_region.h:67