LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
marshal.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_MARSHAL_H
6#define LINE_IO_MARSHAL_H
7
8/**
9 * @file
10 * @ingroup line_io
11 * Boundary marshalling for host bindings (MATLAB MEX, pybind11, the JSON CLI).
12 *
13 * Three things every binding needs and none of them should reinvent:
14 *
15 * 1. COLUMN-MAJOR INGEST AND EGRESS. MATLAB hands out column-major buffers and
16 * numpy's default is row-major; the port stores row-major. A transpose is
17 * unavoidable in one direction, so it is done once here, explicitly, rather
18 * than by every gateway. Note the honest cost: this is a copy, not a view.
19 * It is O(m n) against algorithms that are O(m n) at best and usually far
20 * worse (the convolution is O(prod(N+1) M R), the LU O(n^3)), so the copy is
21 * not the term that matters. Claiming zero copy here would be a lie that
22 * only pays off for a caller that never runs an algorithm.
23 *
24 * 2. EXACT VALUES ACROSS A DOUBLE BOUNDARY. Neither MATLAB nor numpy has a
25 * rational type. An exact result therefore crosses as the pair of decimal
26 * integer strings (numerator, denominator) plus a double approximation, so
27 * the host can rebuild it with sym(num)/sym(den) or fractions.Fraction and
28 * can see, rather than guess, that the double is a rounding of something
29 * exact.
30 *
31 * 3. ERROR IDENTITY. A host needs a stable identifier, not a prose message:
32 * mexErrMsgIdAndTxt takes an id, and a Python binding maps the id to an
33 * exception class. The mapping lives here so all bindings agree.
34 */
35
36#include <cstddef>
37#include <string>
38#include <vector>
39
40#include "line/num/number.h"
41#include "line/util/error.h"
42#include "line/util/matrix.h"
43
44namespace line {
45namespace io {
46
47/**
48 * Build a Matrix from a column-major host buffer (MATLAB's mxGetPr layout,
49 * numpy's order='F'). Converts the element type through num_traits, so a
50 * double buffer can feed an exact or high-precision instantiation.
51 */
52template <class T, class S>
53Matrix<T> from_column_major(const S* data, std::size_t rows, std::size_t cols) {
54 if (rows > 0 && cols > 0 && data == nullptr)
55 throw InputError("from_column_major: null buffer with nonzero dimensions");
56 Matrix<T> m(rows, cols);
57 for (std::size_t j = 0; j < cols; ++j)
58 for (std::size_t i = 0; i < rows; ++i)
59 m(i, j) = num_traits<T>::from_double(static_cast<double>(data[j * rows + i]));
60 return m;
61}
62
63/** Build a Matrix from a row-major host buffer (numpy's default order='C'). */
64template <class T, class S>
65Matrix<T> from_row_major(const S* data, std::size_t rows, std::size_t cols) {
66 if (rows > 0 && cols > 0 && data == nullptr)
67 throw InputError("from_row_major: null buffer with nonzero dimensions");
68 Matrix<T> m(rows, cols);
69 for (std::size_t i = 0; i < rows; ++i)
70 for (std::size_t j = 0; j < cols; ++j)
71 m(i, j) = num_traits<T>::from_double(static_cast<double>(data[i * cols + j]));
72 return m;
73}
74
75/** Write a Matrix into a column-major host buffer, as doubles. */
76template <class T>
77void to_column_major(const Matrix<T>& m, double* out) {
78 if (!m.empty() && out == nullptr) throw InputError("to_column_major: null destination");
79 for (std::size_t j = 0; j < m.cols(); ++j)
80 for (std::size_t i = 0; i < m.rows(); ++i)
81 out[j * m.rows() + i] = num_traits<T>::to_double(m(i, j));
82}
83
84/** Write a vector into a host buffer, as doubles. */
85template <class T>
86void to_host(const std::vector<T>& v, double* out) {
87 if (!v.empty() && out == nullptr) throw InputError("to_host: null destination");
88 for (std::size_t i = 0; i < v.size(); ++i) out[i] = num_traits<T>::to_double(v[i]);
89}
90
91/** Numerator of an exact value as a decimal string; empty for inexact types. */
92template <class T>
93std::string exact_numerator(const T&);
94template <class T>
95std::string exact_denominator(const T&);
96
97/**
98 * An exact scalar as it crosses to a host without a rational type: the two
99 * decimal integer strings, plus the double a host can use directly.
100 */
102 std::string numerator;
103 std::string denominator;
104 double approx = 0.0;
105 bool is_exact = false; ///< false when the source arithmetic was inexact
106};
107
108/** Marshal any T; only the exact instantiation fills numerator/denominator. */
109template <class T>
120
121template <class T>
122std::string exact_numerator(const T&) {
123 return std::string();
124}
125
126template <>
127inline std::string exact_numerator<Rational>(const Rational& v) {
129}
130
131template <class T>
132std::string exact_denominator(const T&) {
133 return std::string();
134}
135
136template <>
137inline std::string exact_denominator<Rational>(const Rational& v) {
139}
140
141/**
142 * Stable identifier for an error, for mexErrMsgIdAndTxt and for mapping to a
143 * host exception class. The prose message stays in what(); hosts must key on
144 * the identifier, which will not change with wording.
145 */
146inline const char* error_id(const Error& e) {
147 if (dynamic_cast<const InputError*>(&e) != nullptr) return "line:input";
148 if (dynamic_cast<const NumericError*>(&e) != nullptr) return "line:numeric";
149 if (dynamic_cast<const UnsupportedError*>(&e) != nullptr) return "line:unsupported";
150 return "line:error";
151}
152
153} // namespace io
154} // namespace line
155
156#endif // LINE_IO_MARSHAL_H
Base error for the multiprecision C++ port.
Definition error.h:31
Malformed or inconsistent input (dimensions, negative populations, ...).
Definition error.h:37
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
bool empty() const
Definition matrix.h:92
The algorithm cannot proceed on this instance (singular matrix, ...).
Definition error.h:43
Requested feature or arithmetic mode is not ported yet.
Definition error.h:49
The exception types the port throws.
Dense matrix and non-owning view.
void to_column_major(const Matrix< T > &m, double *out)
Write a Matrix into a column-major host buffer, as doubles.
Definition marshal.h:77
std::string exact_denominator(const T &)
Definition marshal.h:132
const char * error_id(const Error &e)
Stable identifier for an error, for mexErrMsgIdAndTxt and for mapping to a host exception class.
Definition marshal.h:146
ExactValue marshal_scalar(const T &v)
Marshal any T; only the exact instantiation fills numerator/denominator.
Definition marshal.h:110
Matrix< T > from_column_major(const S *data, std::size_t rows, std::size_t cols)
Build a Matrix from a column-major host buffer (MATLAB's mxGetPr layout, numpy's order='F').
Definition marshal.h:53
std::string exact_numerator(const T &)
Numerator of an exact value as a decimal string; empty for inexact types.
Definition marshal.h:122
Matrix< T > from_row_major(const S *data, std::size_t rows, std::size_t cols)
Build a Matrix from a row-major host buffer (numpy's default order='C').
Definition marshal.h:65
void to_host(const std::vector< T > &v, double *out)
Write a vector into a host buffer, as doubles.
Definition marshal.h:86
boost::multiprecision::number< boost::multiprecision::cpp_rational_backend, boost::multiprecision::et_off > Rational
Definition number.h:76
Number-type abstraction for the templated API port.
An exact scalar as it crosses to a host without a rational type: the two decimal integer strings,...
Definition marshal.h:101
std::string denominator
Definition marshal.h:103
std::string numerator
Definition marshal.h:102
bool is_exact
false when the source arithmetic was inexact
Definition marshal.h:105