46 std::vector<std::vector<double>> &outputEmbedding,
47 const std::vector<double> &inputMatrix,
50 int *insertionTimeForTopomap)
const {
52#ifndef TTK_ENABLE_SCIKIT_LEARN
64 std::vector<double> coordsTopomap(2 * nRows);
65 topomap.
execute<
double>(coordsTopomap.data(), insertionTimeForTopomap,
67 outputEmbedding.resize(2);
68 outputEmbedding[0].resize(nRows);
69 outputEmbedding[1].resize(nRows);
70 for(
int i = 0; i < nRows; i++) {
71 outputEmbedding[0][i] = coordsTopomap[2 * i];
72 outputEmbedding[1][i] = coordsTopomap[2 * i + 1];
80#ifdef TTK_ENABLE_SCIKIT_LEARN
81#ifndef TTK_ENABLE_KAMIKAZE
97 vector<PyObject *> gc;
105 PyObject *pNumberOfComponents;
106 PyObject *pNumberOfNeighbors;
108 PyObject *pIsDeterministic;
112 PyObject *pEmbedding;
114 PyObject *pLLEParams;
115 PyObject *pMDSParams;
116 PyObject *pTSNEParams;
117 PyObject *pISOParams;
118 PyObject *pPCAParams;
120 PyArrayObject *npArr;
121 PyArrayObject *npEmbedding;
125 if(PyArray_API ==
nullptr) {
126#ifndef __clang_analyzer__
130 if(PyArray_API ==
nullptr) {
135 const int numberOfDimensions = 2;
136 npy_intp dimensions[2]{nRows, nColumns};
138 std::vector<std::string> methodToString{
139 "SE",
"LLE",
"MDS",
"t-SNE",
"IsoMap",
"PCA"};
141 pArray = PyArray_SimpleNewFromData(numberOfDimensions, dimensions, NPY_DOUBLE,
142 const_cast<double *
>(inputMatrix.data()));
143#ifndef TTK_ENABLE_KAMIKAZE
145 this->
printErr(
"Python: failed to convert the array.");
146 goto collect_garbage;
149 gc.push_back(pArray);
151 npArr =
reinterpret_cast<PyArrayObject *
>(pArray);
153 pSys = PyImport_ImportModule(
"sys");
154#ifndef TTK_ENABLE_KAMIKAZE
156 this->
printErr(
"Python: failed to load the sys module.");
157 goto collect_garbage;
162 pPath = PyObject_GetAttrString(pSys,
"path");
163#ifndef TTK_ENABLE_KAMIKAZE
165 this->
printErr(
"Python: failed to get the path variable.");
166 goto collect_garbage;
172 modulePath =
VALUE(TTK_SCRIPTS_PATH);
176 this->
printMsg(
"Loading Python script from: " + modulePath);
177 PyList_Append(pPath, PyUnicode_FromString(modulePath.data()));
180 pNumberOfComponents = PyLong_FromLong(numberOfComponents);
181#ifndef TTK_ENABLE_KAMIKAZE
182 if(!pNumberOfComponents) {
183 this->
printErr(
"Python: cannot convert pNumberOfComponents.");
184 goto collect_garbage;
187 gc.push_back(pNumberOfComponents);
189 pNumberOfNeighbors = PyLong_FromLong(numberOfNeighbors);
190#ifndef TTK_ENABLE_KAMIKAZE
191 if(!pNumberOfNeighbors) {
192 this->
printErr(
"Python: cannot convert pNumberOfNeighbors.");
193 goto collect_garbage;
196 gc.push_back(pNumberOfNeighbors);
198 pMethod = PyLong_FromLong(
static_cast<long>(this->
Method));
199#ifndef TTK_ENABLE_KAMIKAZE
201 this->
printErr(
"Python: cannot convert pMethod.");
202 goto collect_garbage;
205 gc.push_back(pMethod);
209 "MDS is known to be instable when used with multiple threads");
212#ifndef TTK_ENABLE_KAMIKAZE
214 this->
printErr(
"Python: cannot convert pJobs.");
215 goto collect_garbage;
219 pIsDeterministic = PyLong_FromLong(
static_cast<long>(this->
IsDeterministic));
220#ifndef TTK_ENABLE_KAMIKAZE
221 if(!pIsDeterministic) {
222 this->
printErr(
"Python: cannot convert pIsDeterministic.");
223 goto collect_garbage;
228 pName = PyUnicode_FromString(
ModuleName.data());
229#ifndef TTK_ENABLE_KAMIKAZE
231 this->
printErr(
"Python: moduleName parsing failed.");
232 goto collect_garbage;
237 pModule = PyImport_Import(pName);
238#ifndef TTK_ENABLE_KAMIKAZE
240 this->
printErr(
"Python: module import failed.");
241 goto collect_garbage;
244 gc.push_back(pModule);
247 pFunc = PyObject_GetAttrString(pModule,
FunctionName.data());
248#ifndef TTK_ENABLE_KAMIKAZE
250 this->
printErr(
"Python: functionName parsing failed.");
251 goto collect_garbage;
254 if(!PyCallable_Check(pFunc)) {
255 this->
printErr(
"Python: function call failed.");
256 goto collect_garbage;
260 pSEParams = PyList_New(0);
261 PyList_Append(pSEParams, PyUnicode_FromString(
se_Affinity.data()));
262 PyList_Append(pSEParams, PyFloat_FromDouble(
se_Gamma));
263 PyList_Append(pSEParams, PyUnicode_FromString(
se_EigenSolver.data()));
265 pLLEParams = PyList_New(0);
267 PyList_Append(pLLEParams, PyUnicode_FromString(
lle_EigenSolver.data()));
268 PyList_Append(pLLEParams, PyFloat_FromDouble(
lle_Tolerance));
270 PyList_Append(pLLEParams, PyUnicode_FromString(
lle_Method.data()));
276 pMDSParams = PyList_New(0);
277 PyList_Append(pMDSParams, PyBool_FromLong(
mds_Metric));
278 PyList_Append(pMDSParams, PyLong_FromLong(
mds_Init));
280 PyList_Append(pMDSParams, PyLong_FromLong(
mds_Verbose));
281 PyList_Append(pMDSParams, PyFloat_FromDouble(
mds_Epsilon));
284 pTSNEParams = PyList_New(0);
291 PyList_Append(pTSNEParams, PyUnicode_FromString(
tsne_Metric.data()));
292 PyList_Append(pTSNEParams, PyUnicode_FromString(
tsne_Init.data()));
293 PyList_Append(pTSNEParams, PyLong_FromLong(
tsne_Verbose));
294 PyList_Append(pTSNEParams, PyUnicode_FromString(
tsne_Method.data()));
295 PyList_Append(pTSNEParams, PyFloat_FromDouble(
tsne_Angle));
297 pISOParams = PyList_New(0);
298 PyList_Append(pISOParams, PyUnicode_FromString(
iso_EigenSolver.data()));
299 PyList_Append(pISOParams, PyFloat_FromDouble(
iso_Tolerance));
301 PyList_Append(pISOParams, PyUnicode_FromString(
iso_PathMethod.data()));
304 PyList_Append(pISOParams, PyUnicode_FromString(
iso_Metric.data()));
306 pPCAParams = PyList_New(0);
307 PyList_Append(pPCAParams, PyBool_FromLong(
pca_Copy));
308 PyList_Append(pPCAParams, PyBool_FromLong(
pca_Whiten));
309 PyList_Append(pPCAParams, PyUnicode_FromString(
pca_SVDSolver.data()));
310 PyList_Append(pPCAParams, PyFloat_FromDouble(
pca_Tolerance));
313 pParams = PyList_New(0);
314 gc.push_back(pParams);
316 PyList_Append(pParams, pSEParams);
317 PyList_Append(pParams, pLLEParams);
318 PyList_Append(pParams, pMDSParams);
319 PyList_Append(pParams, pTSNEParams);
320 PyList_Append(pParams, pISOParams);
321 PyList_Append(pParams, pPCAParams);
323 pReturn = PyObject_CallFunctionObjArgs(
324 pFunc, npArr, pMethod, pNumberOfComponents, pNumberOfNeighbors, pJobs,
325 pIsDeterministic, pParams, NULL);
326#ifndef TTK_ENABLE_KAMIKAZE
328 this->
printErr(
"Python: function returned invalid object.");
329 goto collect_garbage;
332 gc.push_back(pReturn);
334 pNRows = PyList_GetItem(pReturn, 0);
335#ifndef TTK_ENABLE_KAMIKAZE
337 this->
printErr(
"Python: function returned invalid number of rows");
338 goto collect_garbage;
342 pNColumns = PyList_GetItem(pReturn, 1);
343#ifndef TTK_ENABLE_KAMIKAZE
345 this->
printErr(
"Python: function returned invalid number of columns.");
346 goto collect_garbage;
350 pEmbedding = PyList_GetItem(pReturn, 2);
351#ifndef TTK_ENABLE_KAMIKAZE
353 this->
printErr(
"Python: function returned invalid embedding data.");
354 goto collect_garbage;
358 if(PyLong_AsLong(pNRows) == nRows
359 and PyLong_AsLong(pNColumns) == numberOfComponents) {
360 npEmbedding =
reinterpret_cast<PyArrayObject *
>(pEmbedding);
362 outputEmbedding.resize(numberOfComponents);
363 for(
int i = 0; i < numberOfComponents; ++i) {
364 outputEmbedding[i].resize(nRows);
365 if(PyArray_TYPE(npEmbedding) == NPY_FLOAT) {
366 float *c_out =
reinterpret_cast<float *
>(PyArray_DATA(npEmbedding));
367 for(
int j = 0; j < nRows; ++j)
368 outputEmbedding[i][j] = c_out[i * nRows + j];
369 }
else if(PyArray_TYPE(npEmbedding) == NPY_DOUBLE) {
370 double *c_out =
reinterpret_cast<double *
>(PyArray_DATA(npEmbedding));
371 for(
int j = 0; j < nRows; ++j)
372 outputEmbedding[i][j] = c_out[i * nRows + j];
381 this->
printMsg(
"Computed " + methodToString[
static_cast<int>(this->
Method)],
387#ifndef TTK_ENABLE_KAMIKAZE