5#define VALUE_TO_STRING(x) #x
6#define VALUE(x) VALUE_TO_STRING(x)
8#ifdef TTK_ENABLE_SCIKIT_LEARN
9#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
11#include <numpy/arrayobject.h>
20#ifdef TTK_ENABLE_SCIKIT_LEARN
21 auto finalize_callback = []() { Py_Finalize(); };
23 if(!Py_IsInitialized()) {
25 atexit(finalize_callback);
28 const char *version = Py_GetVersion();
29 if(version[0] >=
'3') {
30 this->
printMsg(
"Initializing Python " + std::to_string(version[0])
31 + std::to_string(version[1]) + std::to_string(version[2]));
33 this->
printErr(
"Python 3 + is required :" + std::string{version}
42#ifdef TTK_ENABLE_SCIKIT_LEARN
45 this->
printErr(
"Warning: scikit-learn support disabled: Python/Numpy may "
46 "not be installed properly");
47 this->
printErr(
"Module features disabled.");
53 std::vector<std::vector<double>> &outputEmbedding,
54 const std::vector<double> &inputMatrix,
56 const int nColumns)
const {
58#ifdef TTK_ENABLE_SCIKIT_LEARN
59#ifndef TTK_ENABLE_KAMIKAZE
76 vector<PyObject *> gc;
84 PyObject *pNumberOfComponents;
85 PyObject *pNumberOfNeighbors;
87 PyObject *pIsDeterministic;
95 PyObject *pTSNEParams;
100 PyArrayObject *npEmbedding;
104 if(PyArray_API ==
nullptr) {
105#ifndef __clang_analyzer__
109 if(PyArray_API ==
nullptr) {
114 const int numberOfDimensions = 2;
115 npy_intp dimensions[2]{nRows, nColumns};
117 std::vector<std::string> methodToString{
118 "SE",
"LLE",
"MDS",
"t-SNE",
"IsoMap",
"PCA"};
120 pArray = PyArray_SimpleNewFromData(numberOfDimensions, dimensions, NPY_DOUBLE,
121 const_cast<double *
>(inputMatrix.data()));
122#ifndef TTK_ENABLE_KAMIKAZE
124 this->
printErr(
"Python: failed to convert the array.");
125 goto collect_garbage;
128 gc.push_back(pArray);
130 npArr =
reinterpret_cast<PyArrayObject *
>(pArray);
132 pSys = PyImport_ImportModule(
"sys");
133#ifndef TTK_ENABLE_KAMIKAZE
135 this->
printErr(
"Python: failed to load the sys module.");
136 goto collect_garbage;
141 pPath = PyObject_GetAttrString(pSys,
"path");
142#ifndef TTK_ENABLE_KAMIKAZE
144 this->
printErr(
"Python: failed to get the path variable.");
145 goto collect_garbage;
151 modulePath =
VALUE(TTK_SCRIPTS_PATH);
155 this->
printMsg(
"Loading Python script from: " + modulePath);
156 PyList_Append(pPath, PyUnicode_FromString(modulePath.data()));
159 pNumberOfComponents = PyLong_FromLong(numberOfComponents);
160#ifndef TTK_ENABLE_KAMIKAZE
161 if(!pNumberOfComponents) {
162 this->
printErr(
"Python: cannot convert pNumberOfComponents.");
163 goto collect_garbage;
166 gc.push_back(pNumberOfComponents);
168 pNumberOfNeighbors = PyLong_FromLong(numberOfNeighbors);
169#ifndef TTK_ENABLE_KAMIKAZE
170 if(!pNumberOfNeighbors) {
171 this->
printErr(
"Python: cannot convert pNumberOfNeighbors.");
172 goto collect_garbage;
175 gc.push_back(pNumberOfNeighbors);
177 pMethod = PyLong_FromLong(
static_cast<long>(this->
Method));
178#ifndef TTK_ENABLE_KAMIKAZE
180 this->
printErr(
"Python: cannot convert pMethod.");
181 goto collect_garbage;
184 gc.push_back(pMethod);
188 "MDS is known to be instable when used with multiple threads");
191#ifndef TTK_ENABLE_KAMIKAZE
193 this->
printErr(
"Python: cannot convert pJobs.");
194 goto collect_garbage;
198 pIsDeterministic = PyLong_FromLong(
static_cast<long>(this->
IsDeterministic));
199#ifndef TTK_ENABLE_KAMIKAZE
200 if(!pIsDeterministic) {
201 this->
printErr(
"Python: cannot convert pIsDeterministic.");
202 goto collect_garbage;
207 pName = PyUnicode_FromString(
ModuleName.data());
208#ifndef TTK_ENABLE_KAMIKAZE
210 this->
printErr(
"Python: moduleName parsing failed.");
211 goto collect_garbage;
216 pModule = PyImport_Import(pName);
217#ifndef TTK_ENABLE_KAMIKAZE
219 this->
printErr(
"Python: module import failed.");
220 goto collect_garbage;
223 gc.push_back(pModule);
226 pFunc = PyObject_GetAttrString(pModule,
FunctionName.data());
227#ifndef TTK_ENABLE_KAMIKAZE
229 this->
printErr(
"Python: functionName parsing failed.");
230 goto collect_garbage;
233 if(!PyCallable_Check(pFunc)) {
234 this->
printErr(
"Python: function call failed.");
235 goto collect_garbage;
239 pSEParams = PyList_New(0);
240 PyList_Append(pSEParams, PyUnicode_FromString(
se_Affinity.data()));
241 PyList_Append(pSEParams, PyFloat_FromDouble(
se_Gamma));
242 PyList_Append(pSEParams, PyUnicode_FromString(
se_EigenSolver.data()));
244 pLLEParams = PyList_New(0);
246 PyList_Append(pLLEParams, PyUnicode_FromString(
lle_EigenSolver.data()));
247 PyList_Append(pLLEParams, PyFloat_FromDouble(
lle_Tolerance));
249 PyList_Append(pLLEParams, PyUnicode_FromString(
lle_Method.data()));
255 pMDSParams = PyList_New(0);
256 PyList_Append(pMDSParams, PyBool_FromLong(
mds_Metric));
257 PyList_Append(pMDSParams, PyLong_FromLong(
mds_Init));
259 PyList_Append(pMDSParams, PyLong_FromLong(
mds_Verbose));
260 PyList_Append(pMDSParams, PyFloat_FromDouble(
mds_Epsilon));
263 pTSNEParams = PyList_New(0);
270 PyList_Append(pTSNEParams, PyUnicode_FromString(
tsne_Metric.data()));
271 PyList_Append(pTSNEParams, PyUnicode_FromString(
tsne_Init.data()));
272 PyList_Append(pTSNEParams, PyLong_FromLong(
tsne_Verbose));
273 PyList_Append(pTSNEParams, PyUnicode_FromString(
tsne_Method.data()));
274 PyList_Append(pTSNEParams, PyFloat_FromDouble(
tsne_Angle));
276 pISOParams = PyList_New(0);
277 PyList_Append(pISOParams, PyUnicode_FromString(
iso_EigenSolver.data()));
278 PyList_Append(pISOParams, PyFloat_FromDouble(
iso_Tolerance));
280 PyList_Append(pISOParams, PyUnicode_FromString(
iso_PathMethod.data()));
283 PyList_Append(pISOParams, PyUnicode_FromString(
iso_Metric.data()));
285 pPCAParams = PyList_New(0);
286 PyList_Append(pPCAParams, PyBool_FromLong(
pca_Copy));
287 PyList_Append(pPCAParams, PyBool_FromLong(
pca_Whiten));
288 PyList_Append(pPCAParams, PyUnicode_FromString(
pca_SVDSolver.data()));
289 PyList_Append(pPCAParams, PyFloat_FromDouble(
pca_Tolerance));
292 pParams = PyList_New(0);
293 gc.push_back(pParams);
295 PyList_Append(pParams, pSEParams);
296 PyList_Append(pParams, pLLEParams);
297 PyList_Append(pParams, pMDSParams);
298 PyList_Append(pParams, pTSNEParams);
299 PyList_Append(pParams, pISOParams);
300 PyList_Append(pParams, pPCAParams);
302 pReturn = PyObject_CallFunctionObjArgs(
303 pFunc, npArr, pMethod, pNumberOfComponents, pNumberOfNeighbors, pJobs,
304 pIsDeterministic, pParams, NULL);
305#ifndef TTK_ENABLE_KAMIKAZE
307 this->
printErr(
"Python: function returned invalid object.");
308 goto collect_garbage;
311 gc.push_back(pReturn);
313 pNRows = PyList_GetItem(pReturn, 0);
314#ifndef TTK_ENABLE_KAMIKAZE
316 this->
printErr(
"Python: function returned invalid number of rows");
317 goto collect_garbage;
321 pNColumns = PyList_GetItem(pReturn, 1);
322#ifndef TTK_ENABLE_KAMIKAZE
324 this->
printErr(
"Python: function returned invalid number of columns.");
325 goto collect_garbage;
329 pEmbedding = PyList_GetItem(pReturn, 2);
330#ifndef TTK_ENABLE_KAMIKAZE
332 this->
printErr(
"Python: function returned invalid embedding data.");
333 goto collect_garbage;
337 if(PyLong_AsLong(pNRows) == nRows
338 and PyLong_AsLong(pNColumns) == numberOfComponents) {
339 npEmbedding =
reinterpret_cast<PyArrayObject *
>(pEmbedding);
341 outputEmbedding.resize(numberOfComponents);
342 for(
int i = 0; i < numberOfComponents; ++i) {
343 outputEmbedding[i].resize(nRows);
344 if(PyArray_TYPE(npEmbedding) == NPY_FLOAT) {
345 float *c_out =
reinterpret_cast<float *
>(PyArray_DATA(npEmbedding));
346 for(
int j = 0; j < nRows; ++j)
347 outputEmbedding[i][j] = c_out[i * nRows + j];
348 }
else if(PyArray_TYPE(npEmbedding) == NPY_DOUBLE) {
349 double *c_out =
reinterpret_cast<double *
>(PyArray_DATA(npEmbedding));
350 for(
int j = 0; j < nRows; ++j)
351 outputEmbedding[i][j] = c_out[i * nRows + j];
360 this->
printMsg(
"Computed " + methodToString[
static_cast<int>(this->
Method)],
366#ifndef TTK_ENABLE_KAMIKAZE
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
void setDebugMsgPrefix(const std::string &prefix)
int printMsg(const std::string &msg, const debug::Priority &priority=debug::Priority::INFO, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cout) const
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
float lle_HessianTolerance
std::string iso_NeighborsAlgorithm
std::string se_EigenSolver
std::string pca_MaxIteration
std::string iso_EigenSolver
std::string pca_SVDSolver
std::string iso_PathMethod
std::string mds_Dissimilarity
std::string lle_NeighborsAlgorithm
std::string lle_EigenSolver
float lle_ModifiedTolerance
bool isPythonFound() const
int execute(std::vector< std::vector< double > > &outputEmbedding, const std::vector< double > &inputMatrix, const int nRows, const int nColumns) const
int tsne_MaxIterationProgress
float tsne_GradientThreshold