v2.0.0
Loading...
Searching...
No Matches
python_test_helper.cpp
Go to the documentation of this file.
1//=============================================================================================================
34
35//=============================================================================================================
36// INCLUDES
37//=============================================================================================================
38
39#include "python_test_helper.h"
40
41#include <QCoreApplication>
42#include <QDir>
43#include <QFile>
44#include <QProcessEnvironment>
45#include <QTextStream>
46#include <QRegularExpression>
47
48//=============================================================================================================
49// USED NAMESPACES
50//=============================================================================================================
51
52using namespace UTILSLIB;
53using namespace Eigen;
54
55//=============================================================================================================
56// DEFINE MEMBER METHODS
57//=============================================================================================================
58
62
63//=============================================================================================================
64
66{
67 return isPythonAvailable() && hasPackage("mne");
68}
69
70//=============================================================================================================
71
73{
74 return m_runner.isPythonAvailable();
75}
76
77//=============================================================================================================
78
79bool PythonTestHelper::hasPackage(const QString& packageName) const
80{
81 return m_runner.isPackageAvailable(packageName);
82}
83
84//=============================================================================================================
85
86PythonRunnerResult PythonTestHelper::eval(const QString& code, int timeoutMs) const
87{
88 Q_UNUSED(timeoutMs);
89 return m_runner.runCode(code);
90}
91
92//=============================================================================================================
93
94double PythonTestHelper::evalDouble(const QString& code, bool* ok, int timeoutMs) const
95{
96 PythonRunnerResult result = eval(code, timeoutMs);
97 if (!result.success) {
98 if (ok) *ok = false;
99 return 0.0;
100 }
101
102 bool parseOk = false;
103 double value = result.stdOut.trimmed().toDouble(&parseOk);
104 if (ok) *ok = parseOk;
105 return parseOk ? value : 0.0;
106}
107
108//=============================================================================================================
109
110VectorXd PythonTestHelper::evalVector(const QString& code, bool* ok, int timeoutMs) const
111{
112 PythonRunnerResult result = eval(code, timeoutMs);
113 if (!result.success) {
114 if (ok) *ok = false;
115 return VectorXd();
116 }
117
118 // Parse stdout: one value per line, or space-separated on one line
119 QString output = result.stdOut.trimmed();
120 if (output.isEmpty()) {
121 if (ok) *ok = false;
122 return VectorXd();
123 }
124
125 QStringList lines = output.split('\n', Qt::SkipEmptyParts);
126
127 // If single line, split by whitespace
128 QList<double> values;
129 for (const QString& line : lines) {
130 QStringList tokens = line.trimmed().split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
131 for (const QString& token : tokens) {
132 bool parseOk = false;
133 double val = token.toDouble(&parseOk);
134 if (!parseOk) {
135 if (ok) *ok = false;
136 return VectorXd();
137 }
138 values.append(val);
139 }
140 }
141
142 VectorXd vec(values.size());
143 for (int i = 0; i < values.size(); ++i) {
144 vec(i) = values[i];
145 }
146
147 if (ok) *ok = true;
148 return vec;
149}
150
151//=============================================================================================================
152
153MatrixXd PythonTestHelper::evalMatrix(const QString& code, bool* ok, int timeoutMs) const
154{
155 PythonRunnerResult result = eval(code, timeoutMs);
156 if (!result.success) {
157 if (ok) *ok = false;
158 return MatrixXd();
159 }
160
161 QString output = result.stdOut.trimmed();
162 if (output.isEmpty()) {
163 if (ok) *ok = false;
164 return MatrixXd();
165 }
166
167 QStringList lines = output.split('\n', Qt::SkipEmptyParts);
168 int nRows = lines.size();
169 int nCols = -1;
170
171 QList<QList<double>> rowData;
172 for (const QString& line : lines) {
173 QStringList tokens = line.trimmed().split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
174 if (nCols < 0) {
175 nCols = tokens.size();
176 } else if (tokens.size() != nCols) {
177 if (ok) *ok = false;
178 return MatrixXd();
179 }
180
181 QList<double> row;
182 for (const QString& token : tokens) {
183 bool parseOk = false;
184 double val = token.toDouble(&parseOk);
185 if (!parseOk) {
186 if (ok) *ok = false;
187 return MatrixXd();
188 }
189 row.append(val);
190 }
191 rowData.append(row);
192 }
193
194 if (nCols <= 0) {
195 if (ok) *ok = false;
196 return MatrixXd();
197 }
198
199 MatrixXd mat(nRows, nCols);
200 for (int r = 0; r < nRows; ++r) {
201 for (int c = 0; c < nCols; ++c) {
202 mat(r, c) = rowData[r][c];
203 }
204 }
205
206 if (ok) *ok = true;
207 return mat;
208}
209
210//=============================================================================================================
211
213 const QStringList& args,
214 int timeoutMs) const
215{
216 Q_UNUSED(timeoutMs);
217 return m_runner.run(scriptPath, args);
218}
219
220//=============================================================================================================
221
223{
224 return QCoreApplication::applicationDirPath() + "/../resources/data/mne-cpp-test-data/";
225}
226
227//=============================================================================================================
228
230{
231 const QString val = QProcessEnvironment::systemEnvironment().value("MNE_REQUIRE_PYTHON").toLower();
232 return val == "true" || val == "1";
233}
234
235//=============================================================================================================
236
237bool PythonTestHelper::writeMatrix(const QString& filePath, const MatrixXd& mat)
238{
239 QFile file(filePath);
240 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
241 return false;
242 }
243
244 QTextStream out(&file);
245 out.setRealNumberNotation(QTextStream::ScientificNotation);
246 out.setRealNumberPrecision(17);
247
248 for (int r = 0; r < mat.rows(); ++r) {
249 for (int c = 0; c < mat.cols(); ++c) {
250 if (c > 0) out << ' ';
251 out << mat(r, c);
252 }
253 out << '\n';
254 }
255
256 file.close();
257 return true;
258}
259
260//=============================================================================================================
261
262MatrixXd PythonTestHelper::readMatrix(const QString& filePath, bool* ok)
263{
264 QFile file(filePath);
265 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
266 if (ok) *ok = false;
267 return MatrixXd();
268 }
269
270 QTextStream in(&file);
271 QList<QList<double>> rowData;
272 int nCols = -1;
273
274 while (!in.atEnd()) {
275 QString line = in.readLine().trimmed();
276 if (line.isEmpty()) continue;
277
278 QStringList tokens = line.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts);
279 if (nCols < 0) {
280 nCols = tokens.size();
281 } else if (tokens.size() != nCols) {
282 if (ok) *ok = false;
283 return MatrixXd();
284 }
285
286 QList<double> row;
287 for (const QString& token : tokens) {
288 bool parseOk = false;
289 double val = token.toDouble(&parseOk);
290 if (!parseOk) {
291 if (ok) *ok = false;
292 return MatrixXd();
293 }
294 row.append(val);
295 }
296 rowData.append(row);
297 }
298
299 file.close();
300
301 if (rowData.isEmpty() || nCols <= 0) {
302 if (ok) *ok = false;
303 return MatrixXd();
304 }
305
306 MatrixXd mat(rowData.size(), nCols);
307 for (int r = 0; r < rowData.size(); ++r) {
308 for (int c = 0; c < nCols; ++c) {
309 mat(r, c) = rowData[r][c];
310 }
311 }
312
313 if (ok) *ok = true;
314 return mat;
315}
316
317//=============================================================================================================
318
319MatrixXd PythonTestHelper::evalMatrixViaFile(const QString& code,
320 const QString& outputFilePath,
321 bool* ok,
322 int timeoutMs) const
323{
324 PythonRunnerResult result = eval(code, timeoutMs);
325 if (!result.success) {
326 if (ok) *ok = false;
327 return MatrixXd();
328 }
329
330 return readMatrix(outputFilePath, ok);
331}
PythonTestHelper class declaration — test-frame convenience for MNE-Python cross-validation.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Script execution result container.
Eigen::VectorXd evalVector(const QString &code, bool *ok=nullptr, int timeoutMs=60000) const
static bool writeMatrix(const QString &filePath, const Eigen::MatrixXd &mat)
double evalDouble(const QString &code, bool *ok=nullptr, int timeoutMs=30000) const
PythonRunnerResult runScript(const QString &scriptPath, const QStringList &args={}, int timeoutMs=120000) const
Eigen::MatrixXd evalMatrixViaFile(const QString &code, const QString &outputFilePath, bool *ok=nullptr, int timeoutMs=120000) const
bool hasPackage(const QString &packageName) const
PythonRunnerResult eval(const QString &code, int timeoutMs=30000) const
static Eigen::MatrixXd readMatrix(const QString &filePath, bool *ok=nullptr)
Eigen::MatrixXd evalMatrix(const QString &code, bool *ok=nullptr, int timeoutMs=60000) const