LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
infer_compute_ql_at_arrival.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_API_INFER_INFER_COMPUTE_QL_AT_ARRIVAL_H
6#define LINE_API_INFER_INFER_COMPUTE_QL_AT_ARRIVAL_H
7
8/**
9 * @file
10 * @ingroup api_infer
11 * Per-class queue lengths seen by each arriving job, reconstructed from
12 * arrival and response time samples.
13 *
14 * Templated port of matlab/src/api/infer/infer_compute_ql_at_arrival.m (the
15 * shipped .mexa64 is a compiled copy of that same .m). No JAR counterpart.
16 *
17 * Arrivals and response times are matched by job id, so the two sample sets
18 * need not be in the same order nor come from the same source. Each job is
19 * turned into an arrival event at t and a departure event at t + rt; the
20 * events are replayed in time order, departures before arrivals at a tie, and
21 * the state recorded at each arrival is the queue seen by that job INCLUDING
22 * itself (the arriving job is counted first, then the state is read).
23 *
24 * ARITHMETIC: only comparisons and one addition per job, so a finite field
25 * computation, exact in the exact instantiation. Exactness matters here: with
26 * rational timestamps a tie between a departure and an arrival is decided by
27 * the documented rule rather than by rounding.
28 */
29
30#include <algorithm>
31#include <cstddef>
32#include <vector>
33
34#include "line/num/number.h"
35#include "line/util/error.h"
36#include "line/util/matrix.h"
37
38namespace line {
39namespace infer {
40
41/**
42 * @brief Per-class queue lengths seen by each arriving job, reconstructed
43 * from arrival and response time samples.
44 *
45 * @param at (n) arrival times
46 * @param at_jobid (n) job id of each arrival
47 * @param rt (m) response times, m >= n
48 * @param rt_jobid (m) job id of each response time
49 * @param cls (n) class of each arrival, 0-based in [0,R)
50 * @param R number of classes
51 * @return (n x R) queue length at each arrival, in the input order
52 */
53template <class T>
54Matrix<T> infer_compute_ql_at_arrival(const std::vector<T>& at, const std::vector<long>& at_jobid,
55 const std::vector<T>& rt, const std::vector<long>& rt_jobid,
56 const std::vector<std::size_t>& cls, std::size_t R) {
57 const std::size_t n = at.size();
58 if (at_jobid.size() != n || cls.size() != n)
59 throw InputError("infer_compute_ql_at_arrival: arrival arrays have different lengths");
60 if (rt.size() != rt_jobid.size())
61 throw InputError("infer_compute_ql_at_arrival: response-time arrays have different lengths");
62 for (std::size_t c : cls)
63 if (c >= R) throw InputError("infer_compute_ql_at_arrival: class index out of range");
64
65 // match response times to arrivals by job id
66 std::vector<T> rt_matched(n);
67 for (std::size_t i = 0; i < n; ++i) {
68 bool found = false;
69 for (std::size_t k = 0; k < rt_jobid.size(); ++k)
70 if (rt_jobid[k] == at_jobid[i]) {
71 rt_matched[i] = rt[k];
72 found = true;
73 break;
74 }
75 if (!found)
76 throw InputError(
77 "infer_compute_ql_at_arrival: not all arrival job ids found among the response "
78 "time job ids");
79 }
80
81 // stable sort of the arrivals by time, as MATLAB's sort is stable
82 std::vector<std::size_t> order(n);
83 for (std::size_t i = 0; i < n; ++i) order[i] = i;
84 std::stable_sort(order.begin(), order.end(),
85 [&](std::size_t a, std::size_t b) { return at[a] < at[b]; });
86
87 struct Event {
88 T time;
89 int type; // -1 departure, +1 arrival
90 std::size_t slot;
91 std::size_t cls;
92 };
93 std::vector<Event> events;
94 events.reserve(2 * n);
95 for (std::size_t k = 0; k < n; ++k) {
96 const std::size_t i = order[k];
97 Event a;
98 a.time = at[i];
99 a.type = 1;
100 a.slot = k;
101 a.cls = cls[i];
102 events.push_back(a);
103 }
104 for (std::size_t k = 0; k < n; ++k) {
105 const std::size_t i = order[k];
106 Event d;
107 d.time = at[i] + rt_matched[i];
108 d.type = -1;
109 d.slot = k;
110 d.cls = cls[i];
111 events.push_back(d);
112 }
113 // sort by time, departures before arrivals at a tie
114 std::stable_sort(events.begin(), events.end(), [](const Event& x, const Event& y) {
115 if (x.time < y.time) return true;
116 if (y.time < x.time) return false;
117 return x.type < y.type;
118 });
119
120 const T zero = num_traits<T>::from_int(0);
121 const T one = num_traits<T>::from_int(1);
122 std::vector<T> state(R, zero);
123 Matrix<T> ql_sorted(n, R, zero);
124 for (const Event& e : events) {
125 if (e.type == 1) {
126 state[e.cls] += one;
127 for (std::size_t c = 0; c < R; ++c) ql_sorted(e.slot, c) = state[c];
128 } else {
129 state[e.cls] -= one;
130 }
131 }
132
133 Matrix<T> ql(n, R, zero);
134 for (std::size_t k = 0; k < n; ++k)
135 for (std::size_t c = 0; c < R; ++c) ql(order[k], c) = ql_sorted(k, c);
136 return ql;
137}
138
139} // namespace infer
140} // namespace line
141
142#endif // LINE_API_INFER_INFER_COMPUTE_QL_AT_ARRIVAL_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Dense matrix and non-owning view.
Matrix< T > infer_compute_ql_at_arrival(const std::vector< T > &at, const std::vector< long > &at_jobid, const std::vector< T > &rt, const std::vector< long > &rt_jobid, const std::vector< std::size_t > &cls, std::size_t R)
Per-class queue lengths seen by each arriving job, reconstructed from arrival and response time sampl...
Number-type abstraction for the templated API port.