7#include <vtkInformation.h>
9#include <vtkDataArray.h>
10#include <vtkDataSet.h>
11#include <vtkFloatArray.h>
12#include <vtkObjectFactory.h>
13#include <vtkPointData.h>
14#include <vtkSmartPointer.h>
16#include <vtkUnsignedIntArray.h>
38 this->SetNumberOfInputPorts(3);
39 this->SetNumberOfOutputPorts(4);
50 vtkInformation *info) {
52 info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(),
"vtkMultiBlockDataSet");
53 }
else if(port == 1) {
54 info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(),
"vtkMultiBlockDataSet");
55 info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
56 }
else if(port == 2) {
57 info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(),
"vtkTable");
58 info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
80 vtkInformation *info) {
81 if(port == 0 or port == 1 or port == 2 or port == 3) {
82 info->Set(vtkDataObject::DATA_TYPE_NAME(),
"vtkMultiBlockDataSet");
103 vtkInformationVector **inputVector,
104 vtkInformationVector *outputVector) {
105#ifndef TTK_ENABLE_TORCH
108 printErr(
"This filter requires Torch.");
114 auto blocks = vtkMultiBlockDataSet::GetData(inputVector[0], 0);
115 auto blocks2 = vtkMultiBlockDataSet::GetData(inputVector[1], 0);
116 auto table = vtkTable::GetData(inputVector[2], 0);
121 std::vector<vtkSmartPointer<vtkMultiBlockDataSet>> inputTrees, inputTrees2;
127 vtkAbstractArray *clusterAsgn;
129 clusterAsgn = this->GetInputArrayToProcess(0, inputVector);
133 clusterAsgn_[i] = clusterAsgn->GetVariantValue(i).ToInt();
138 "You must provide a table column in info input to use clustering loss");
142 std::stringstream ss;
150 if((treesNodes.size() != 0 and inputTrees[0]->GetBlock(0) != treesNodes[0])
151 or (treesNodes2.size() != inputTrees2.size()))
152 resetDataVisualization();
162 printMsg(
"Computation with normalized Wasserstein.");
164 printMsg(
"Computation without normalized Wasserstein.");
166 return run(outputVector, inputTrees, inputTrees2);
170#ifdef TTK_ENABLE_TORCH
172 vtkInformationVector *outputVector,
175 runCompute(outputVector, inputTrees, inputTrees2);
176 runOutput(outputVector, inputTrees, inputTrees2);
181 vtkInformationVector *
ttkNotUsed(outputVector),
187 std::vector<ttk::ftm::MergeTree<float>> intermediateMTrees,
192 inputTrees, intermediateMTrees, treesNodes, treesArcs, treesSegmentation,
199 auto &inputTrees2ToUse
201 ttk::ftm::constructTrees<float>(inputTrees2ToUse, intermediateMTrees2,
202 treesNodes2, treesArcs2, treesSegmentation2,
207 const int numInputs = intermediateMTrees.size();
208 const int numInputs2 = intermediateMTrees2.size();
209 setDataVisualization(numInputs, numInputs2);
214 execute(intermediateMTrees, intermediateMTrees2);
216 ttk::ftm::mergeTreesTemplateToDouble<float>(
217 intermediateMTrees, intermediateDTrees);
224 vtkInformationVector *outputVector,
232 auto output_origins = vtkMultiBlockDataSet::GetData(outputVector, 0);
233 auto output_vectors = vtkMultiBlockDataSet::GetData(outputVector, 1);
234 auto output_coef = vtkMultiBlockDataSet::GetData(outputVector, 2);
235 auto output_data = vtkMultiBlockDataSet::GetData(outputVector, 3);
241 std::vector<std::vector<ttk::ftm::idNode>> originsMatchingVectorT(
242 originsMatchingSize),
243 invOriginsMatchingVectorT = originsMatchingVectorT;
244 for(
unsigned int l = 0; l < originsMatchingVectorT.size(); ++l) {
245 auto &tree1 = (l == 0 ? origins_[0] : originsPrime_[l - 1]);
246 auto &tree2 = (l == 0 ? originsPrime_[0] : originsPrime_[l]);
248 originsMatchingVectorT[l]);
250 invOriginsMatchingVectorT[l]);
252 std::vector<std::vector<ttk::ftm::idNode>> originsMatchingVector;
253 std::vector<std::vector<double>> originsPersPercent, originsPersDiff;
254 std::vector<double> originPersPercent, originPersDiff;
255 std::vector<int> originPersistenceOrder;
256 ttk::wae::computeTrackingInformation(
257 origins_, originsPrime_, originsMatchingVectorT, invOriginsMatchingVectorT,
259 originsPersDiff, originPersPercent, originPersDiff, originPersistenceOrder);
261 std::vector<std::vector<std::vector<ttk::ftm::idNode>>>
263 for(
unsigned int l = 0; l < invDataMatchingVectorT.size(); ++l) {
265 for(
unsigned int i = 0; i < invDataMatchingVectorT[l].size(); ++i)
268 invDataMatchingVectorT[l][i]);
270 std::vector<std::vector<ttk::ftm::idNode>> invReconstMatchingVectorT(
272 for(
unsigned int i = 0; i < invReconstMatchingVectorT.size(); ++i) {
273 auto l = recs_[i].size() - 1;
276 invReconstMatchingVectorT[i]);
282 output_data->SetNumberOfBlocks(1);
285 data->SetNumberOfBlocks(1);
288 dataSeg->SetNumberOfBlocks(recs_.size());
289 bool outputSegmentation = !treesSegmentation.empty() and treesSegmentation[0];
290 for(
unsigned int l = 0; l < 1; ++l) {
293 out_layer_i->SetNumberOfBlocks(recs_.size());
294 std::vector<ttk::ftm::MergeTree<float> *> trees(recs_.size());
295 for(
unsigned int i = 0; i < recs_.size(); ++i)
296 trees[i] = &(recs_[i][l].mTree);
299 std::vector<std::vector<std::tuple<std::string, std::vector<int>>>>
300 customIntArrays(recs_.size());
301 std::vector<std::vector<std::tuple<std::string, std::vector<double>>>>
302 customDoubleArrays(recs_.size());
303 unsigned int lShift = 0;
304 ttk::wae::computeCustomArrays(
306 invReconstMatchingVectorT, originsMatchingVector, originsMatchingVectorT,
307 originsPersPercent, originsPersDiff, originPersistenceOrder, l, lShift,
308 customIntArrays, customDoubleArrays);
311 ttk::wae::makeManyOutput(trees, treesNodes,
treesNodeCorr_, out_layer_i,
312 customIntArrays, customDoubleArrays,
315 if(outputSegmentation and l == 0) {
316 ttk::wae::makeManyOutput(
321 data->SetBlock(l, out_layer_i);
322 std::stringstream ss;
323 ss << (l == 0 ?
"Input" :
"Layer") << l;
324 data->GetMetaData(l)->Set(vtkCompositeDataSet::NAME(), ss.str());
326 output_data->SetBlock(0, data);
327 unsigned int num = 0;
328 output_data->GetMetaData(num)->Set(
329 vtkCompositeDataSet::NAME(),
"layersTrees");
330 if(outputSegmentation)
331 output_data->SetBlock(1, dataSeg);
332 vtkNew<vtkFloatArray> lossArray{};
333 lossArray->SetName(
"Loss");
335 output_data->GetFieldData()->AddArray(lossArray);
340 output_origins->SetNumberOfBlocks(2);
348 std::vector<ttk::ftm::MergeTree<float> *> trees(
noLayers_);
349 std::vector<std::vector<std::tuple<std::string, std::vector<int>>>>
351 std::vector<std::vector<std::tuple<std::string, std::vector<double>>>>
353 for(
unsigned int l = 0; l <
noLayers_; ++l) {
354 trees[l] = &(origins_[l].mTree);
356 std::string name2{
"OriginPersPercent"};
357 customDoubleArrays[l].emplace_back(
358 std::make_tuple(name2, originPersPercent));
359 std::string name3{
"OriginPersDiff"};
360 customDoubleArrays[l].emplace_back(
361 std::make_tuple(name3, originPersDiff));
362 std::string nameOrder{
"OriginPersOrder"};
363 customIntArrays[l].emplace_back(
364 std::make_tuple(nameOrder, originPersistenceOrder));
367 ttk::wae::makeManyOutput(trees, origins, customIntArrays, customDoubleArrays,
371 customIntArrays.clear();
373 customDoubleArrays.clear();
375 for(
unsigned int l = 0; l <
noLayers_; ++l) {
376 trees[l] = &(originsPrime_[l].mTree);
377 if(l < originsMatchingVector.size()) {
378 std::vector<int> customArrayMatching,
379 originPersOrder(trees[l]->tree.getNumberOfNodes(), -1);
380 for(
unsigned int i = 0; i < originsMatchingVector[l].size(); ++i) {
381 customArrayMatching.emplace_back(originsMatchingVector[l][i]);
382 if(originsMatchingVector[l][i] < originPersistenceOrder.size())
384 = originPersistenceOrder[originsMatchingVector[l][i]];
386 std::string name{
"OriginTrueNodeId"};
387 customIntArrays[l].emplace_back(
388 std::make_tuple(name, customArrayMatching));
389 std::string nameOrder{
"OriginPersOrder"};
390 customIntArrays[l].emplace_back(
391 std::make_tuple(nameOrder, originPersOrder));
392 std::string name2{
"OriginPersPercent"};
393 customDoubleArrays[l].emplace_back(
394 std::make_tuple(name2, originsPersPercent[l]));
395 std::string name3{
"OriginPersDiff"};
396 customDoubleArrays[l].emplace_back(
397 std::make_tuple(name3, originsPersDiff[l]));
400 ttk::wae::makeManyOutput(trees, originsP, customIntArrays, customDoubleArrays,
403 output_origins->SetBlock(0, origins);
404 output_origins->SetBlock(1, originsP);
406 for(
unsigned int l = 0; l <
noLayers_; ++l) {
409 std::stringstream ss;
410 ss << (l == 0 ?
"InputOrigin" :
"LayerOrigin") << l;
411 auto originsMetaData = origins->GetMetaData(l);
413 originsMetaData->Set(vtkCompositeDataSet::NAME(), ss.str());
415 ss << (l == 0 ?
"InputOriginPrime" :
"LayerOriginPrime") << l;
416 auto originsPMetaData = originsP->GetMetaData(l);
418 originsPMetaData->Set(vtkCompositeDataSet::NAME(), ss.str());
421 output_origins->GetMetaData(num)->Set(
422 vtkCompositeDataSet::NAME(),
"layersOrigins");
424 output_origins->GetMetaData(num)->Set(
425 vtkCompositeDataSet::NAME(),
"layersOriginsPrime");
430 output_coef->SetNumberOfBlocks(allAlphas_[0].size());
431 for(
unsigned int l = 0; l < allAlphas_[0].size(); ++l) {
433 vtkNew<vtkIntArray> treeIDArray{};
434 treeIDArray->SetName(
"TreeID");
435 treeIDArray->SetNumberOfTuples(inputTrees.size());
436 for(
unsigned int i = 0; i < inputTrees.size(); ++i)
437 treeIDArray->SetTuple1(i, i);
438 coef_table->AddColumn(treeIDArray);
439 auto noVec = allAlphas_[0][l].sizes()[0];
440 for(
unsigned int v = 0; v < noVec; ++v) {
442 vtkNew<vtkFloatArray> tArray{};
444 tArray->SetName(name.c_str());
445 tArray->SetNumberOfTuples(allAlphas_.size());
447 vtkNew<vtkFloatArray> actArray{};
448 std::string actName =
"Act" + name;
449 actArray->SetName(actName.c_str());
450 actArray->SetNumberOfTuples(allAlphas_.size());
452 vtkNew<vtkFloatArray> tArrayNorm{};
454 tArrayNorm->SetName(nameNorm.c_str());
455 tArrayNorm->SetNumberOfTuples(allAlphas_.size());
457 vtkNew<vtkFloatArray> actArrayNorm{};
458 std::string actNameNorm =
"Act" + nameNorm;
459 actArrayNorm->SetName(actNameNorm.c_str());
460 actArrayNorm->SetNumberOfTuples(allAlphas_.size());
462 for(
unsigned int i = 0; i < allAlphas_.size(); ++i) {
463 tArray->SetTuple1(i, allAlphas_[i][l][v].item<float>());
464 actArray->SetTuple1(i, allActAlphas_[i][l][v].item<float>());
465 tArrayNorm->SetTuple1(i, allScaledAlphas_[i][l][v].item<float>());
466 actArrayNorm->SetTuple1(i, allActScaledAlphas_[i][l][v].item<float>());
468 coef_table->AddColumn(tArray);
469 coef_table->AddColumn(actArray);
470 coef_table->AddColumn(tArrayNorm);
471 coef_table->AddColumn(actArrayNorm);
474 vtkNew<vtkIntArray> clusterArray{};
475 clusterArray->SetName(
"ClusterAssignment");
476 clusterArray->SetNumberOfTuples(inputTrees.size());
479 coef_table->AddColumn(clusterArray);
482 vtkNew<vtkIntArray> treesNoNodesArray{};
483 treesNoNodesArray->SetNumberOfTuples(recs_.size());
484 treesNoNodesArray->SetName(
"treeNoNodes");
485 for(
unsigned int i = 0; i < recs_.size(); ++i)
486 treesNoNodesArray->SetTuple1(
487 i, recs_[i][0].mTree.tree.getNumberOfNodes());
488 coef_table->AddColumn(treesNoNodesArray);
490 output_coef->SetBlock(l, coef_table);
491 std::stringstream ss;
493 output_coef->GetMetaData(l)->Set(vtkCompositeDataSet::NAME(), ss.str());
498 for(
unsigned int b = 0; b < inputTrees[0]->GetNumberOfBlocks(); ++b) {
499 vtkNew<vtkFieldData> fd{};
500 fd->CopyStructure(inputTrees[0]->GetBlock(b)->GetFieldData());
501 fd->SetNumberOfTuples(inputTrees.size());
502 for(
size_t i = 0; i < inputTrees.size(); ++i) {
503 fd->SetTuple(i, 0, inputTrees[i]->GetBlock(b)->GetFieldData());
507 for(
int i = 0; i < fd->GetNumberOfArrays(); ++i) {
508 auto array = fd->GetAbstractArray(i);
509 array->SetName(array->GetName());
510 vtkTable::SafeDownCast(output_coef->GetBlock(0))->AddColumn(array);
515 std::vector<std::string> paramNames;
517 for(
auto paramName : paramNames) {
518 vtkNew<vtkDoubleArray> array{};
519 array->SetName(paramName.c_str());
521 output_coef->GetFieldData()->AddArray(array);
523 vtkNew<vtkDoubleArray> arrayActivate{};
524 arrayActivate->SetName(
"activate");
525 arrayActivate->InsertNextTuple1(
activate_);
526 output_coef->GetFieldData()->AddArray(arrayActivate);
527 vtkNew<vtkDoubleArray> arrayActivateFunction{};
528 arrayActivateFunction->SetName(
"activationFunction");
530 output_coef->GetFieldData()->AddArray(arrayActivateFunction);
535 std::vector<std::vector<std::vector<ttk::ftm::idNode>>> dataMatchingVectorT(
537 for(
unsigned int l = 0; l < dataMatchingVectorT.size(); ++l) {
539 for(
unsigned int i = 0; i < dataMatchingVectorT[l].size(); ++i) {
540 auto &origin = (l == 0 ? origins_[0] : originsPrime_[l - 1]);
542 dataMatchingVectorT[l][i]);
545 output_vectors->SetNumberOfBlocks(2);
548 vectors->SetNumberOfBlocks(vSTensor_.size());
551 vectorsPrime->SetNumberOfBlocks(vSTensor_.size());
552 for(
unsigned int l = 0; l < vSTensor_.size(); ++l) {
556 for(
unsigned int v = 0; v < vSTensor_[l].sizes()[1]; ++v) {
558 vtkNew<vtkFloatArray> vectorArray{};
561 vectorArray->SetName(name.c_str());
562 vectorArray->SetNumberOfTuples(vSTensor_[l].sizes()[0]);
563 for(
unsigned int i = 0; i < vSTensor_[l].sizes()[0]; ++i)
564 vectorArray->SetTuple1(i, vSTensor_[l][i][v].item<
float>());
565 vectorsTable->AddColumn(vectorArray);
567 vtkNew<vtkFloatArray> vectorPrimeArray{};
570 vectorPrimeArray->SetName(name2.c_str());
571 vectorPrimeArray->SetNumberOfTuples(vSPrimeTensor_[l].sizes()[0]);
572 for(
unsigned int i = 0; i < vSPrimeTensor_[l].sizes()[0]; ++i)
573 vectorPrimeArray->SetTuple1(i, vSPrimeTensor_[l][i][v].item<
float>());
574 vectorsPrimeTable->AddColumn(vectorPrimeArray);
577 vtkNew<vtkUnsignedIntArray> revNodeCorrArray{};
578 revNodeCorrArray->SetName(
"revNodeCorr");
579 revNodeCorrArray->SetNumberOfTuples(vSTensor_[l].sizes()[0]);
580 std::vector<unsigned int> revNodeCorr;
581 getReverseTorchNodeCorr(origins_[l], revNodeCorr);
582 for(
unsigned int i = 0; i < vSTensor_[l].sizes()[0]; ++i)
583 revNodeCorrArray->SetTuple1(i, revNodeCorr[i]);
584 vectorsTable->AddColumn(revNodeCorrArray);
586 vtkNew<vtkUnsignedIntArray> revNodeCorrPrimeArray{};
587 revNodeCorrPrimeArray->SetNumberOfTuples(vSPrimeTensor_[l].sizes()[0]);
588 revNodeCorrPrimeArray->SetName(
"revNodeCorr");
589 std::vector<unsigned int> revNodeCorrPrime;
590 getReverseTorchNodeCorr(originsPrime_[l], revNodeCorrPrime);
591 for(
unsigned int i = 0; i < vSPrimeTensor_[l].sizes()[0]; ++i)
592 revNodeCorrPrimeArray->SetTuple1(i, revNodeCorrPrime[i]);
593 vectorsPrimeTable->AddColumn(revNodeCorrPrimeArray);
595 auto addOriginMatchingArray
597 std::vector<ttk::ftm::idNode> &originMatchingVector) {
598 vtkNew<vtkIntArray> matchingArray{};
599 matchingArray->SetNumberOfTuples(originMatchingVector.size());
600 matchingArray->SetName(
"nextOriginMatching");
601 for(
unsigned int i = 0; i < originMatchingVector.size(); ++i)
602 matchingArray->SetTuple1(i, (
int)originMatchingVector[i]);
603 vectorsTableT->AddColumn(matchingArray);
606 addOriginMatchingArray(vectorsTable, originsMatchingVectorT[l]);
607 if(l < originsMatchingVectorT.size() - 1)
608 addOriginMatchingArray(vectorsPrimeTable, originsMatchingVectorT[l + 1]);
610 auto addDataMatchingArray
612 std::vector<std::vector<ttk::ftm::idNode>> &dataMatchingVector) {
613 for(
unsigned int i = 0; i < dataMatchingVector.size(); ++i) {
614 vtkNew<vtkIntArray> matchingArray{};
615 matchingArray->SetNumberOfTuples(dataMatchingVector[i].size());
616 std::stringstream ss;
619 matchingArray->SetName(ss.str().c_str());
620 for(
unsigned int j = 0; j < dataMatchingVector[i].size(); ++j)
621 matchingArray->SetTuple1(j, (
int)dataMatchingVector[i][j]);
622 vectorsTableT->AddColumn(matchingArray);
626 addDataMatchingArray(vectorsTable, dataMatchingVectorT[l]);
627 if(l < dataMatchingVectorT.size() - 1)
628 addDataMatchingArray(vectorsPrimeTable, dataMatchingVectorT[l + 1]);
630 if(l == vSTensor_.size() - 1) {
631 for(
unsigned int i = 0; i < invReconstMatchingVectorT.size(); ++i) {
632 vtkNew<vtkIntArray> matchingArray{};
633 matchingArray->SetNumberOfTuples(invReconstMatchingVectorT[i].size());
634 std::stringstream ss;
635 ss <<
"reconstMatching"
637 matchingArray->SetName(ss.str().c_str());
638 for(
unsigned int j = 0; j < invReconstMatchingVectorT[i].size(); ++j)
639 matchingArray->SetTuple1(j, (
int)invReconstMatchingVectorT[i][j]);
640 vectorsPrimeTable->AddColumn(matchingArray);
645 vectors->SetBlock(l, vectorsTable);
646 std::stringstream ss;
647 ss <<
"Vectors" << l;
648 vectors->GetMetaData(l)->Set(vtkCompositeDataSet::NAME(), ss.str());
649 vectorsPrime->SetBlock(l, vectorsPrimeTable);
651 ss <<
"VectorsPrime" << l;
652 vectorsPrime->GetMetaData(l)->Set(vtkCompositeDataSet::NAME(), ss.str());
654 output_vectors->SetBlock(0, vectors);
655 output_vectors->SetBlock(1, vectorsPrime);
657 output_vectors->GetMetaData(num)->Set(vtkCompositeDataSet::NAME(),
"Vectors");
659 output_vectors->GetMetaData(num)->Set(
660 vtkCompositeDataSet::NAME(),
"VectorsPrime");
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
TTK VTK-filter that wraps the ttk::MergeTreeAutoencoder module.
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
ttkMergeTreeAutoencoder()
int FillOutputPortInformation(int port, vtkInformation *info) override
int run(vtkInformationVector *outputVector, std::vector< vtkSmartPointer< vtkMultiBlockDataSet > > &inputTrees, std::vector< vtkSmartPointer< vtkMultiBlockDataSet > > &inputTrees2)
int FillInputPortInformation(int port, vtkInformation *info) override
int runCompute(vtkInformationVector *outputVector, std::vector< vtkSmartPointer< vtkMultiBlockDataSet > > &inputTrees, std::vector< vtkSmartPointer< vtkMultiBlockDataSet > > &inputTrees2)
int runOutput(vtkInformationVector *outputVector, std::vector< vtkSmartPointer< vtkMultiBlockDataSet > > &inputTrees, std::vector< vtkSmartPointer< vtkMultiBlockDataSet > > &inputTrees2)
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
void execute(std::vector< ftm::MergeTree< float > > &trees, std::vector< ftm::MergeTree< float > > &trees2)
unsigned int activationFunction_
std::vector< unsigned int > clusterAsgn_
std::vector< std::vector< std::tuple< ftm::idNode, ftm::idNode, double > > > originsMatchings_
std::vector< std::vector< double > > persCorrelationMatrix_
std::vector< std::vector< std::tuple< ftm::idNode, ftm::idNode, double > > > reconstMatchings_
double clusteringLossWeight_
std::vector< std::vector< std::vector< std::tuple< ftm::idNode, ftm::idNode, double > > > > dataMatchings_
void getInverseMatchingVector(ftm::MergeTree< dataType > &barycenter, ftm::MergeTree< dataType > &tree, std::vector< std::tuple< ftm::idNode, ftm::idNode, double > > &matchings, std::vector< ftm::idNode > &matchingVector)
void getMatchingVector(ftm::MergeTree< dataType > &barycenter, ftm::MergeTree< dataType > &tree, std::vector< std::tuple< ftm::idNode, ftm::idNode, double > > &matchings, std::vector< ftm::idNode > &matchingVector)
double mixtureCoefficient_
void getParamNames(std::vector< std::string > ¶mNames)
bool normalizedWasserstein_
std::vector< std::vector< int > > treesNodeCorr_
bool branchDecomposition_
bool isPersistenceDiagram_
double getParamValueFromName(std::string ¶mName)
std::string getTableCoefficientNormName(int noAxes, int axeNum)
std::string getTableVectorName(int noAxes, int axeNum, int vId, int vComp, bool isSecondInput)
std::string getTableTreeName(int noTrees, int treeNum)
std::string getTableCoefficientName(int noAxes, int axeNum)
void loadBlocks(std::vector< vtkSmartPointer< vtkMultiBlockDataSet > > &inputTrees, vtkMultiBlockDataSet *blocks)
vtkStandardNewMacro(ttkMergeTreeAutoencoder)
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)