Developer documentation
Version 3.0.3-105-gd3941f44
math.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __math_math_h__
18#define __math_math_h__
19
20#include <cmath>
21#include <cstdlib>
22
23#include "app.h"
24#include "exception.h"
25#include "mrtrix.h"
26#include "types.h"
27#include "file/key_value.h"
28#include "file/ofstream.h"
29#include "file/path.h"
30
31namespace MR
32{
33 namespace Math
34 {
35
39 constexpr double e = 2.71828182845904523536;
40 constexpr double pi = 3.14159265358979323846;
41 constexpr double pi_2 = pi / 2.0;
42 constexpr double pi_4 = pi / 4.0;
43 constexpr double sqrt2 = 1.41421356237309504880;
44 constexpr double sqrt1_2 = 1.0 / sqrt2;
45
53 template <typename T> inline constexpr T pow2 (const T& v) { return v*v; }
54 template <typename T> inline constexpr T pow3 (const T& v) { return pow2 (v) *v; }
55 template <typename T> inline constexpr T pow4 (const T& v) { return pow2 (pow2 (v)); }
56 template <typename T> inline constexpr T pow5 (const T& v) { return pow4 (v) *v; }
57 template <typename T> inline constexpr T pow6 (const T& v) { return pow2 (pow3 (v)); }
58 template <typename T> inline constexpr T pow7 (const T& v) { return pow6 (v) *v; }
59 template <typename T> inline constexpr T pow8 (const T& v) { return pow2 (pow4 (v)); }
60 template <typename T> inline constexpr T pow9 (const T& v) { return pow8 (v) *v; }
61 template <typename T> inline constexpr T pow10 (const T& v) { return pow8 (v) *pow2 (v); }
62
63
64 template <typename I, typename T> inline constexpr I round (const T x) throw ()
65 {
66 return static_cast<I> (std::round (x));
67 }
69
75 template <typename I, typename T> inline constexpr I floor (const T x) throw ()
76 {
77 return static_cast<I> (std::floor (x));
78 }
80
86 template <typename I, typename T> inline constexpr I ceil (const T x) throw ()
87 {
88 return static_cast<I> (std::ceil (x));
89 }
90
92 }
93
97 template<typename Derived>
98 inline bool is_finite(const Eigen::MatrixBase<Derived>& x)
99 {
100 return ( (x - x).array() == (x - x).array()).all();
101 }
102
104 template<typename Derived>
105 inline bool is_nan(const Eigen::MatrixBase<Derived>& x)
106 {
107 return ((x.array() == x.array())).all();
108 }
112 template <class Cont>
114 typedef char yes[1], no[2];
115 template<typename C> static yes& test(typename Cont::Scalar);
116 template<typename C> static no& test(...);
117 public:
118 static const bool value = sizeof(test<Cont>(0)) == sizeof(yes);
119 };
120
122 template <class Cont, typename ReturnType = int>
124 public:
125 using type = typename Cont::value_type;
126 };
127 template <class Cont>
128 class container_value_type <Cont, typename std::enable_if<is_eigen_type<Cont>::value, int>::type> { NOMEMALIGN
129 public:
130 using type = typename Cont::Scalar;
131 };
132
133
135 template <class MatrixType>
136 void save_matrix (const MatrixType& M, const std::string& filename, const KeyValues& keyvals = KeyValues(), const bool add_to_command_history = true)
137 {
138 DEBUG ("saving " + str(M.rows()) + "x" + str(M.cols()) + " matrix to file \"" + filename + "\"...");
139 File::OFStream out (filename);
140 File::KeyValue::write (out, keyvals, "# ", add_to_command_history);
141 Eigen::IOFormat fmt (Eigen::FullPrecision, Eigen::DontAlignCols, std::string (1, Path::delimiter (filename)), "\n", "", "", "", "");
142 out << M.format (fmt);
143 out << "\n";
144 }
145
147 template <class ValueType = default_type>
148 vector<vector<ValueType>> load_matrix_2D_vector (const std::string& filename, vector<std::string>* comments = nullptr)
149 {
150 std::ifstream stream (filename, std::ios_base::in | std::ios_base::binary);
151 if (!stream)
152 throw Exception ("Unable to open numerical data text file \"" + filename + "\": " + strerror(errno));
154 std::string sbuf, cbuf;
155 size_t hash;
156
157 while (getline (stream, sbuf)) {
158 hash = sbuf.find_first_of ('#');
159 if (comments and hash != std::string::npos) {
160 cbuf = strip (sbuf.substr (hash));
161 if (sbuf.size() > 1)
162 comments->push_back (cbuf.substr(1));
163 }
164
165 sbuf = strip (sbuf.substr (0, hash));
166 if (sbuf.empty())
167 continue;
168
169 const auto elements = MR::split (sbuf, " ,;\t", true);
170 V.push_back (vector<ValueType>());
171 try {
172 for (const auto& entry : elements)
173 V.back().push_back (to<ValueType> (entry));
174 } catch (Exception& e) {
175 e.push_back ("Cannot load row " + str(V.size()) + " of file \"" + filename + "\" as delimited numerical matrix data:");
176 e.push_back (sbuf);
177 throw e;
178 }
179
180 if (V.size() > 1)
181 if (V.back().size() != V[0].size())
182 throw Exception ("uneven rows in matrix file \"" + filename + "\" " +
183 "(first row: " + str(V[0].size()) + " columns; row " + str(V.size()) + ": " + str(V.back().size()) + " columns)");
184 }
185 if (stream.bad())
186 throw Exception (strerror (errno));
187
188 if (!V.size())
189 throw Exception ("no data in matrix file \"" + filename + "\"");
190
191 return V;
192 }
193
195 template <class ValueType = default_type>
196 Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic> load_matrix (const std::string& filename)
197 {
198 DEBUG ("loading matrix file \"" + filename + "\"...");
199 const vector<vector<ValueType>> V = load_matrix_2D_vector<ValueType> (filename);
200
201 Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic> M (V.size(), V[0].size());
202 for (ssize_t i = 0; i < M.rows(); i++)
203 for (ssize_t j = 0; j < M.cols(); j++)
204 M(i,j) = V[i][j];
205
206 DEBUG ("found " + str(M.rows()) + "x" + str(M.cols()) + " matrix in file \"" + filename + "\"");
207 return M;
208 }
209
211 template <class ValueType = default_type>
212 Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic> parse_matrix (const std::string& spec)
213 {
214 Eigen::Matrix<ValueType, Eigen::Dynamic, Eigen::Dynamic> M;
215 const auto lines = split_lines (spec);
216 for (size_t row = 0; row < lines.size(); ++row) {
217 const auto values = parse_floats (lines[row]);
218 if (M.cols() == 0)
219 M.resize (lines.size(), values.size());
220 else if (M.cols() != ssize_t (values.size()))
221 throw Exception ("error converting string to matrix - uneven number of entries per row");
222 for (size_t col = 0; col < values.size(); ++col)
223 M(row, col) = values[col];
224 }
225 return M;
226 }
227
229 template <class VectorType>
230 inline transform_type load_transform (const std::string& filename, VectorType& centre)
231 {
232 DEBUG ("loading transform file \"" + filename + "\"...");
233
234 vector<std::string> comments;
235 const vector<vector<default_type>> V = load_matrix_2D_vector<> (filename, &comments);
236
237 if (V.empty())
238 throw Exception ("transform in file " + filename + " is empty");
239
240 if (V[0].size() != 4)
241 throw Exception ("transform in file " + filename + " is invalid: does not contain 4 columns.");
242
243 if (V.size() != 3 && V.size() != 4)
244 throw Exception ("transform in file " + filename + " is invalid: must contain either 3 or 4 rows.");
245
247 for (ssize_t i = 0; i < 3; i++)
248 for (ssize_t j = 0; j < 4; j++)
249 M(i,j) = V[i][j];
250
251 if (centre.size() == 3) {
252 const std::string key = " centre: ";
253 const std::string key_legacy = "centre ";
254 centre[0] = NaN;
255 centre[1] = NaN;
256 centre[2] = NaN;
257 vector<std::string> elements;
258 for (auto & line : comments) {
259 if (strncmp(line.c_str(), key.c_str(), key.size()) == 0)
260 elements = split (strip (line.substr (key.size())), " ,;\t", true);
261 else if (strncmp(line.c_str(), key_legacy.c_str(), key_legacy.size()) == 0)
262 elements = split (strip (line.substr (key_legacy.size())), " ,;\t", true);
263 if (elements.size()) {
264 if (elements.size() != 3)
265 throw Exception ("could not parse centre in transformation file " + filename + ": " + strip (line.substr (key.size())));
266 try {
267 centre[0] = to<default_type> (elements[0]);
268 centre[1] = to<default_type> (elements[1]);
269 centre[2] = to<default_type> (elements[2]);
270 } catch (...) {
271 throw Exception ("File \"" + filename + "\" contains non-numerical data in centre: " + strip (line.substr (key.size())));
272 }
273 break;
274 }
275 }
276 }
277
278 return M;
279 }
280
281 inline transform_type load_transform (const std::string& filename)
282 {
283 Eigen::VectorXd c;
284 return load_transform (filename, c);
285 }
286
288 inline void save_transform (const transform_type& M, const std::string& filename, const KeyValues& keyvals = KeyValues(), const bool add_to_command_history = true)
289 {
290 DEBUG ("saving transform to file \"" + filename + "\"...");
291 File::OFStream out (filename);
292 File::KeyValue::write (out, keyvals, "# ", add_to_command_history);
293 const char d (Path::delimiter (filename));
294 Eigen::IOFormat fmt (Eigen::FullPrecision, Eigen::DontAlignCols, std::string (1, d), "\n", "", "", "", "");
295 out << M.matrix().format (fmt);
296 out << "\n0" << d << "0" << d << "0" << d << "1\n";
297 }
298
299 template <class Derived>
300 inline void save_transform (const transform_type& M, const Eigen::MatrixBase<Derived>& centre, const std::string& filename, const KeyValues& keyvals = KeyValues(), const bool add_to_command_history = true)
301 {
302 if (centre.rows() != 3 or centre.cols() != 1)
303 throw Exception ("save_transform() requires 3x1 vector as centre");
304 KeyValues local_keyvals = keyvals;
305 Eigen::IOFormat centrefmt(Eigen::FullPrecision, Eigen::DontAlignCols, " ", "\n", "", "", "", "\n");
306 std::ostringstream os;
307 os<<centre.transpose().format(centrefmt);
308 local_keyvals.insert(std::pair<std::string, std::string>("centre", os.str()));
309 save_transform(M, filename, local_keyvals, add_to_command_history);
310 }
311
313 template <class VectorType>
314 void save_vector (const VectorType& V, const std::string& filename, const KeyValues& keyvals = KeyValues(), const bool add_to_command_history = true)
315 {
316 DEBUG ("saving vector of size " + str(V.size()) + " to file \"" + filename + "\"...");
317 File::OFStream out (filename);
318 File::KeyValue::write (out, keyvals, "# ", add_to_command_history);
319 const char d (Path::delimiter (filename));
320 for (decltype(V.size()) i = 0; i < V.size() - 1; i++)
321 out << str(V[i], 10) << d;
322 out << str(V[V.size() - 1], 10) << "\n";
323 }
324
326 template <class ValueType = default_type>
327 Eigen::Matrix<ValueType, Eigen::Dynamic, 1> load_vector (const std::string& filename)
328 {
329 auto vec = load_matrix<ValueType> (filename);
330 if (vec.cols() == 1)
331 return vec.col(0);
332 if (vec.rows() > 1)
333 throw Exception ("file \"" + filename + "\" contains matrix, not vector");
334 return vec.row(0);
335 }
336
337
338}
339
340#endif
open output files for writing, checking for pre-existing file if necessary
Definition: ofstream.h:39
Get the underlying scalar value type for both std:: containers and Eigen.
Definition: math.h:123
typename Cont::value_type type
Definition: math.h:125
convenience functions for SFINAE on std:: / Eigen containers
Definition: math.h:113
static const bool value
Definition: math.h:118
#define DEBUG(msg)
Definition: exception.h:75
constexpr T pow6(const T &v)
Definition: math.h:57
constexpr T pow5(const T &v)
Definition: math.h:56
constexpr T pow8(const T &v)
Definition: math.h:59
bool is_nan(const Eigen::MatrixBase< Derived > &x)
check if all elements of an Eigen MatrixBase object are a number
Definition: math.h:105
constexpr T pow7(const T &v)
Definition: math.h:58
constexpr T pow2(const T &v)
Definition: math.h:53
constexpr I ceil(const T x)
template function with cast to different type
Definition: math.h:86
constexpr T pow3(const T &v)
Definition: math.h:54
constexpr T pow10(const T &v)
Definition: math.h:61
constexpr I floor(const T x)
template function with cast to different type
Definition: math.h:75
constexpr T pow4(const T &v)
Definition: math.h:55
constexpr T pow9(const T &v)
Definition: math.h:60
bool is_finite(const Eigen::MatrixBase< Derived > &x)
check if all elements of an Eigen MatrixBase object are finite
Definition: math.h:98
constexpr I round(const T x)
Definition: math.h:64
constexpr double sqrt2
Definition: math.h:43
constexpr double e
Definition: math.h:39
constexpr double sqrt1_2
Definition: math.h:44
constexpr double pi_2
Definition: math.h:41
constexpr double pi_4
Definition: math.h:42
constexpr double pi
Definition: math.h:40
#define NOMEMALIGN
Definition: memory.h:22
void write(File::OFStream &out, const KeyValues &keyvals, const std::string &prefix, const bool add_to_command_history=true)
MR::default_type value_type
Definition: typedefs.h:33
char delimiter(const std::string &filename)
Definition: path.h:214
Definition: base.h:24
std::map< std::string, std::string > KeyValues
used in various places for storing key-value pairs
Definition: types.h:238
transform_type load_transform(const std::string &filename, VectorType &centre)
read matrix data from filename into an Eigen::Tranform class
Definition: math.h:230
vector< vector< ValueType > > load_matrix_2D_vector(const std::string &filename, vector< std::string > *comments=nullptr)
read matrix data into a 2D vector filename
Definition: math.h:148
Eigen::Matrix< ValueType, Eigen::Dynamic, Eigen::Dynamic > load_matrix(const std::string &filename)
read matrix data into an Eigen::Matrix filename
Definition: math.h:196
vector< default_type > parse_floats(const std::string &spec)
std::istream & getline(std::istream &stream, std::string &string)
read a line from the stream
Definition: mrtrix.h:47
void save_transform(const transform_type &M, const std::string &filename, const KeyValues &keyvals=KeyValues(), const bool add_to_command_history=true)
write the transform M to file
Definition: math.h:288
std::string strip(const std::string &string, const std::string &ws={" \0\t\r\n", 5}, bool left=true, bool right=true)
Definition: mrtrix.h:131
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
Eigen::Transform< default_type, 3, Eigen::AffineCompact > transform_type
the type for the affine transform of an image:
Definition: types.h:234
Eigen::Matrix< ValueType, Eigen::Dynamic, 1 > load_vector(const std::string &filename)
read the vector data from filename
Definition: math.h:327
vector< std::string > split_lines(const std::string &string, bool ignore_empty_fields=true, size_t num=std::numeric_limits< size_t >::max())
Definition: mrtrix.h:179
vector< std::string > split(const std::string &string, const char *delimiters=" \t\n", bool ignore_empty_fields=false, size_t num=std::numeric_limits< size_t >::max())
constexpr default_type NaN
Definition: types.h:230
Eigen::Matrix< ValueType, Eigen::Dynamic, Eigen::Dynamic > parse_matrix(const std::string &spec)
read matrix data from a text string spec
Definition: math.h:212
void save_matrix(const MatrixType &M, const std::string &filename, const KeyValues &keyvals=KeyValues(), const bool add_to_command_history=true)
write the matrix M to file
Definition: math.h:136
void save_vector(const VectorType &V, const std::string &filename, const KeyValues &keyvals=KeyValues(), const bool add_to_command_history=true)
write the vector V to file
Definition: math.h:314
Definition: types.h:303