LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sn_field_view.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_SN_SN_FIELD_VIEW_H
6#define LINE_API_SN_SN_FIELD_VIEW_H
7
8/**
9 * @file
10 * @ingroup api_sn
11 * The FIELD VIEW the NetworkStruct contract is checked on: for each value, its
12 * kind (Matrix, vector, map, scalar, string, function, opaque), whether it is
13 * empty, and its extents, so a contract written in MATLAB's dimensions (M
14 * stations, K classes, ...) can be checked against a struct that is object-
15 * shaped. sn_contract.h (generated) builds the view of a whole struct; this
16 * header holds the hand-written `sn_field_view` overloads it calls and the
17 * aggregation `sn_field_rows` that turns a member of every Station, JobClass
18 * or NodeDef record into one per-record field.
19 *
20 * Extents: a Matrix has rows x cols; a container has its size, and records the
21 * 1-D extent of each element (a container's size, a vector Matrix's length),
22 * kEmptyExtent for an empty element and kNoExtent for one with no 1-D extent
23 * (a scalar, a 2-D Matrix).
24 */
25
26#include <cstddef>
27#include <functional>
28#include <limits>
29#include <map>
30#include <set>
31#include <string>
32#include <type_traits>
33#include <utility>
34#include <vector>
35
37#include "line/util/matrix.h"
38
39namespace line {
40namespace api {
41
42constexpr long kNoExtent = -1;
43constexpr long kEmptyExtent = -2;
44
45/** The view of one field. */
47 std::string kind = "opaque";
48 bool empty = false;
49 int ndim = 0; ///< 0 scalar-like, 1 container, 2 Matrix
50 long rows = 0; ///< Matrix rows, container size
51 long cols = 0; ///< Matrix cols
52 std::vector<long> elems; ///< per-element 1-D extent of a container
53};
54
55/** The view of a whole struct: its dimensions and one view per contract field, by MATLAB name. */
57 std::map<char, double> dims;
58 std::map<std::string, SnFieldView> fields;
59};
60
61/** The 1-D extent of a field view: a container's size, a vector Matrix's length. */
62inline long sn_view_extent(const SnFieldView& v) {
63 if (v.empty) {
64 return kEmptyExtent;
65 }
66 if (v.ndim == 1) {
67 return v.rows;
68 }
69 if (v.ndim == 2 && (v.rows == 1 || v.cols == 1)) {
70 return v.rows * v.cols;
71 }
72 return kNoExtent;
73}
74
75namespace detail {
76template <class X, class = void>
77struct is_container : std::false_type {};
78template <class X>
79struct is_container<X, std::void_t<typename X::value_type, decltype(std::declval<const X&>().begin()),
80 decltype(std::declval<const X&>().size())>> : std::true_type {};
81template <class X>
82struct is_map : std::false_type {};
83template <class K, class V, class C, class A>
84struct is_map<std::map<K, V, C, A>> : std::true_type {};
85template <class X>
86struct is_function : std::false_type {};
87template <class S>
88struct is_function<std::function<S>> : std::true_type {};
89template <class X>
90struct is_matrix : std::false_type {};
91template <class U>
92struct is_matrix<Matrix<U>> : std::true_type {};
93} // namespace detail
94
95template <class X>
96SnFieldView sn_field_view(const X& x);
97
98namespace detail {
99template <class X>
100long element_extent(const X& x) {
101 if constexpr (is_matrix<X>::value || is_map<X>::value || is_function<X>::value ||
102 std::is_same<X, std::string>::value ||
103 (is_container<X>::value && !std::is_same<X, std::string>::value)) {
104 return sn_view_extent(sn_field_view(x));
105 } else {
106 (void)x;
107 return kNoExtent;
108 }
109}
110} // namespace detail
111
112/** The view of one value; the kind follows its C++ type. */
113template <class X>
115 SnFieldView v;
116 if constexpr (detail::is_matrix<X>::value) {
117 v.kind = "Matrix";
118 v.ndim = 2;
119 v.rows = static_cast<long>(x.rows());
120 v.cols = static_cast<long>(x.cols());
121 v.empty = x.rows() == 0 || x.cols() == 0;
122 } else if constexpr (std::is_same<X, std::string>::value) {
123 v.kind = "string";
124 v.empty = x.empty();
125 } else if constexpr (detail::is_function<X>::value) {
126 v.kind = "function";
127 v.empty = !static_cast<bool>(x);
128 } else if constexpr (detail::is_map<X>::value) {
129 v.kind = "map";
130 v.ndim = 1;
131 v.rows = static_cast<long>(x.size());
132 v.empty = x.empty();
133 for (const auto& kv : x) {
134 v.elems.push_back(detail::element_extent(kv.second));
135 }
136 } else if constexpr (detail::is_container<X>::value) {
137 v.kind = "vector";
138 v.ndim = 1;
139 v.rows = static_cast<long>(x.size());
140 v.empty = x.size() == 0;
141 for (const auto& e : x) {
142 using E = typename std::decay<decltype(e)>::type;
143 v.elems.push_back(detail::element_extent(static_cast<const E&>(e)));
144 }
145 } else if constexpr (std::is_arithmetic<X>::value || std::is_enum<X>::value) {
146 v.kind = "scalar";
147 } else {
148 v.kind = "opaque";
149 }
150 return v;
151}
152
153/** One field made of a member of every record (Station, JobClass, NodeDef), in record order. */
154template <class R, class F>
155SnFieldView sn_field_rows(const std::vector<R>& records, F member) {
156 SnFieldView v;
157 v.ndim = 1;
158 v.rows = static_cast<long>(records.size());
159 v.empty = true;
160 bool first = true;
161 for (const auto& r : records) {
162 SnFieldView e = member(r);
163 if (first) {
164 v.kind = e.kind;
165 first = false;
166 } else if (e.kind != v.kind) {
167 v.kind = "mixed";
168 }
169 v.empty = v.empty && e.empty;
170 v.elems.push_back(sn_view_extent(e));
171 }
172 if (first) {
173 v.kind = "vector";
174 }
175 // scalar records carry no per-element extent, and a record of scalars is never empty
176 if (v.kind == "scalar") {
177 v.empty = records.empty();
178 }
179 return v;
180}
181
182/** The dimensions the contract's shapes are written in. */
183template <class T>
184std::map<char, double> sn_contract_dims(const qn::NetworkStruct<T>& sn) {
185 return {{'M', static_cast<double>(sn.nstations)}, {'K', static_cast<double>(sn.nclasses)},
186 {'N', static_cast<double>(sn.nof_nodes())}, {'S', static_cast<double>(sn.nof_stateful())},
187 {'C', static_cast<double>(sn.nchains)}, {'R', static_cast<double>(sn.regions.size())}};
188}
189
190} // namespace api
191} // namespace line
192
193#endif // LINE_API_SN_SN_FIELD_VIEW_H
A network plus its refreshed NetworkStruct.
Dense matrix and non-owning view.
SnFieldView sn_field_view(const X &x)
The view of one value; the kind follows its C++ type.
SnFieldView sn_field_rows(const std::vector< R > &records, F member)
One field made of a member of every record (Station, JobClass, NodeDef), in record order.
constexpr long kEmptyExtent
long sn_view_extent(const SnFieldView &v)
The 1-D extent of a field view: a container's size, a vector Matrix's length.
std::map< char, double > sn_contract_dims(const qn::NetworkStruct< T > &sn)
The dimensions the contract's shapes are written in.
constexpr long kNoExtent
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
A queueing network and its refreshed NetworkStruct.
The view of one field.
long cols
Matrix cols.
int ndim
0 scalar-like, 1 container, 2 Matrix
std::vector< long > elems
per-element 1-D extent of a container
long rows
Matrix rows, container size.
The view of a whole struct: its dimensions and one view per contract field, by MATLAB name.
std::map< char, double > dims
std::map< std::string, SnFieldView > fields