LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
tikz_graph.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_IO_TIKZ_TIKZ_GRAPH_H
6#define LINE_IO_TIKZ_TIKZ_GRAPH_H
7
8/**
9 * @file
10 * @ingroup line_io
11 * The part of a network the TikZ exporter draws, and the text helpers the
12 * JAR's exporter uses to write it.
13 *
14 * `jline.io.tikz` reads a live `Network`: its node objects (for their class and
15 * name), each Queue's discipline and server count, and `sn.connmatrix`. This
16 * port reads the same facts off a `NetworkStruct` once, into a `TikzGraph`, so
17 * the layout, routing and rendering stages below are plain functions of it.
18 *
19 * THE CONNECTION MATRIX IS THE UNION SUPPORT OF THE ROUTING BLOCKS `P`, as in
20 * `jmt_conn_matrix` (jmt_writer.h): this port records links only through `P`,
21 * and `Network::link` has already replaced every class-switching link by the
22 * `CS_i_to_j` node the JAR inserts, so the topology is the JAR's, node for node.
23 *
24 * NUMBERS ARE FORMATTED AS JAVA'S `String.format("%.2f")` DOES, not as printf
25 * does. Java rounds HALF UP on the shortest decimal expansion of the double,
26 * printf rounds the exact binary value half to even, and the two disagree on a
27 * coordinate such as 1.675 (printf 1.67, Java 1.68). Such coordinates do occur:
28 * a forward edge is routed at the MEAN height of its obstacles.
29 */
30
31#include <cmath>
32#include <cstdio>
33#include <cstdlib>
34#include <limits>
35#include <string>
36#include <vector>
37
40#include "line/num/number.h"
41
42namespace line {
43namespace io {
44
45/** One node as the TikZ exporter sees it. */
46struct TikzNode {
47 std::string name;
49 std::string sched; ///< Java `SchedStrategy.name()` of a Queue (FCFSPRIO is HOL, as in MATLAB), empty when none
50 double servers = 1.0; ///< a Queue's server count, infinite for an unbounded pool
51};
52
53/** Nodes in model order plus `sn.connmatrix` over them. */
54struct TikzGraph {
55 std::string name;
56 std::vector<TikzNode> nodes;
57 std::vector<std::vector<bool>> conn; ///< conn[i][j]: node i is linked to node j, 0-based
58};
59
60namespace tikz_detail {
61
62/** Java's `SchedStrategy.name()`: the enumerator spelling, which is the upper-cased text of `sched_to_text`. */
63inline std::string java_sched_name(lang::SchedStrategy s) {
64 if (s == lang::SchedStrategy::NONE) return std::string();
65 std::string t = lang::sched_to_text(s);
66 for (std::size_t k = 0; k < t.size(); ++k)
67 if (t[k] >= 'a' && t[k] <= 'z') t[k] = static_cast<char>(t[k] - 'a' + 'A');
68 return t;
69}
70
71/**
72 * Java's `String.format("%." + prec + "f", v)`.
73 *
74 * The digits are the shortest decimal expansion that reads back as `v` (what
75 * `Double.toString` prints), rounded half up at `prec` decimals, which is
76 * `FormattedFloatingDecimal.applyPrecision`. A negative value keeps its sign
77 * even when it rounds to zero, as Java's does ("-0.00").
78 */
79inline std::string java_fixed(double v, int prec) {
80 if (std::isnan(v)) return "NaN";
81 const bool neg = std::signbit(v);
82 const double a = std::fabs(v);
83 if (std::isinf(a)) return neg ? "-Infinity" : "Infinity";
84 std::string digits; // significant digits, value = 0.d1d2... * 10^pt
85 int pt = 1;
86 if (a == 0.0) {
87 digits = "0";
88 } else {
89 char buf[64];
90 for (int p = 1; p <= 17; ++p) {
91 std::snprintf(buf, sizeof(buf), "%.*e", p - 1, a);
92 if (std::strtod(buf, nullptr) == a) break;
93 }
94 const std::string s(buf);
95 const std::size_t e = s.find('e');
96 for (std::size_t k = 0; k < e; ++k)
97 if (s[k] != '.') digits.push_back(s[k]);
98 pt = std::atoi(s.c_str() + e + 1) + 1;
99 }
100 if (pt <= 0) {
101 digits = std::string(static_cast<std::size_t>(1 - pt), '0') + digits;
102 pt = 1;
103 }
104 const std::size_t keep = static_cast<std::size_t>(pt + prec);
105 const bool round_up = digits.size() > keep && digits[keep] >= '5';
106 if (digits.size() < keep) digits.append(keep - digits.size(), '0');
107 digits.resize(keep);
108 if (round_up) {
109 std::size_t k = keep;
110 while (k > 0) {
111 --k;
112 if (digits[k] == '9') {
113 digits[k] = '0';
114 } else {
115 ++digits[k];
116 break;
117 }
118 if (k == 0) {
119 digits.insert(digits.begin(), '1');
120 ++pt;
121 }
122 }
123 }
124 std::string ip = digits.substr(0, static_cast<std::size_t>(pt));
125 const std::size_t nz = ip.find_first_not_of('0');
126 ip = (nz == std::string::npos) ? std::string("0") : ip.substr(nz);
127 std::string out = neg ? "-" : "";
128 out += ip;
129 if (prec > 0) out += "." + digits.substr(static_cast<std::size_t>(pt));
130 return out;
131}
132
133/** `name.replaceAll("[^a-zA-Z0-9]", "_")`: one underscore per CODE POINT, so a UTF-8 sequence is one character. */
134inline std::string sanitize_id(const std::string& name) {
135 std::string out;
136 out.reserve(name.size());
137 for (std::size_t k = 0; k < name.size(); ++k) {
138 const unsigned char c = static_cast<unsigned char>(name[k]);
139 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
140 out.push_back(static_cast<char>(c));
141 } else if ((c & 0xC0) != 0x80) {
142 out.push_back('_'); // a lead byte or any other ASCII character; continuation bytes add nothing
143 }
144 }
145 return out;
146}
147
148inline void replace_all(std::string& s, const std::string& from, const std::string& to) {
149 std::size_t at = 0;
150 while ((at = s.find(from, at)) != std::string::npos) {
151 s.replace(at, from.size(), to);
152 at += to.size();
153 }
154}
155
156/**
157 * `TikZNodeRenderer.escapeLatex`, applied in the SAME sequence of whole-string
158 * replacements, so a backslash comes out as `\textbackslash\{\}`, exactly as
159 * the JAR's later brace replacement leaves it.
160 */
161inline std::string escape_latex(std::string t) {
162 replace_all(t, "\\", "\\textbackslash{}");
163 replace_all(t, "_", "\\_");
164 replace_all(t, "&", "\\&");
165 replace_all(t, "%", "\\%");
166 replace_all(t, "$", "\\$");
167 replace_all(t, "#", "\\#");
168 replace_all(t, "{", "\\{");
169 replace_all(t, "}", "\\}");
170 replace_all(t, "~", "\\textasciitilde{}");
171 replace_all(t, "^", "\\textasciicircum{}");
172 return t;
173}
174
175} // namespace tikz_detail
176
177/**
178 * The drawable graph of a network struct.
179 *
180 * A node is drawn by the CLASS it was declared with, as MATLAB (through the JAR's
181 * `instanceof` chain) draws it: a Queue scheduled INF has nodetype Delay in the
182 * struct (`Network::add_queue`), but `queue_object` marks it, so it is drawn as
183 * a buffer labelled INF with an infinite server, not as a Delay box.
184 */
185template <class T>
187 TikzGraph g;
188 g.name = sn.name;
189 const std::size_t I = sn.nodes.size();
190 g.nodes.resize(I);
191 for (std::size_t i = 0; i < I; ++i) {
192 const qn::NodeDef& nd = sn.nodes[i];
193 TikzNode& tn = g.nodes[i];
194 tn.name = nd.name;
196 if (nd.station > 0 && nd.station <= sn.stations.size()) {
197 const qn::Station<T>& st = sn.stations[nd.station - 1];
199 tn.servers = st.nservers;
200 }
201 }
202 g.conn.assign(I, std::vector<bool>(I, false));
203 const T zero = num_traits<T>::from_int(0);
204 for (const auto& kv : sn.P) {
205 const Matrix<T>& B = kv.second;
206 for (std::size_t a = 0; a < B.rows() && a < I; ++a)
207 for (std::size_t b = 0; b < B.cols() && b < I; ++b)
208 if (!(B(a, b) == zero)) g.conn[a][b] = true;
209 }
210 // The Sink -> Source arc is the refresh's closure of the open chains (`apply_sink_closure`), which the
211 // reference builds on the routing matrix and never records as a link; it is not part of the drawing.
212 if (sn.sourceIdx > 0 && sn.sinkNode > 0 && sn.sourceIdx <= sn.station_to_node.size()) {
213 const std::size_t src = sn.station_to_node[sn.sourceIdx - 1];
214 if (sn.sinkNode <= I && src >= 1 && src <= I) g.conn[sn.sinkNode - 1][src - 1] = false;
215 }
216 return g;
217}
218
219} // namespace io
220} // namespace line
221
222#endif // LINE_IO_TIKZ_TIKZ_GRAPH_H
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
A network plus its refreshed NetworkStruct.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
std::string escape_latex(std::string t)
TikZNodeRenderer.escapeLatex, applied in the SAME sequence of whole-string replacements,...
Definition tikz_graph.h:161
std::string sanitize_id(const std::string &name)
name.replaceAll("[^a-zA-Z0-9]", "_"): one underscore per CODE POINT, so a UTF-8 sequence is one chara...
Definition tikz_graph.h:134
std::string java_fixed(double v, int prec)
Java's String.format("%." + prec + "f", v).
Definition tikz_graph.h:79
void replace_all(std::string &s, const std::string &from, const std::string &to)
Definition tikz_graph.h:148
std::string java_sched_name(lang::SchedStrategy s)
Java's SchedStrategy.name(): the enumerator spelling, which is the upper-cased text of sched_to_text.
Definition tikz_graph.h:63
TikzGraph tikz_graph(const qn::NetworkStruct< T > &sn)
The drawable graph of a network struct.
Definition tikz_graph.h:186
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Definition lang_types.h:181
const char * sched_to_text(SchedStrategy s)
Definition lang_types.h:230
NodeType
Node kinds, with the values of MATLAB NodeType.
Definition lang_types.h:326
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Nodes in model order plus sn.connmatrix over them.
Definition tikz_graph.h:54
std::vector< std::vector< bool > > conn
conn[i][j]: node i is linked to node j, 0-based
Definition tikz_graph.h:57
std::string name
Definition tikz_graph.h:55
std::vector< TikzNode > nodes
Definition tikz_graph.h:56
One node as the TikZ exporter sees it.
Definition tikz_graph.h:46
std::string sched
Java SchedStrategy.name() of a Queue (FCFSPRIO is HOL, as in MATLAB), empty when none.
Definition tikz_graph.h:49
lang::NodeType type
Definition tikz_graph.h:48
double servers
a Queue's server count, infinite for an unbounded pool
Definition tikz_graph.h:50
std::string name
Definition tikz_graph.h:47
A node of the network.
bool queue_object
Declared as a Queue although its INF discipline makes nodetype Delay (Network::add_queue).
One station of the network.
SchedStrategy sched
double nservers
may be infinite (a Delay, or an inf-scheduled task)