55 vtkInformationVector **inputVector,
56 vtkInformationVector *outputVector) {
58 auto blocks = vtkMultiBlockDataSet::GetData(inputVector[0], 0);
61 std::vector<vtkUnstructuredGrid *> input;
66 if(blocks !=
nullptr) {
67 numInputs = blocks->GetNumberOfBlocks();
68 input.resize(numInputs);
69 for(
int i = 0; i < numInputs; ++i) {
70 input[i] = vtkUnstructuredGrid::SafeDownCast(blocks->GetBlock(i));
71 if(this->GetMTime() < input[i]->GetMTime()) {
84 printWrn(
"Custom weight only supported for pairwise distance.");
85 printWrn(
"Non-matching weight is set to 1.");
90 auto output_clusters = vtkMultiBlockDataSet::GetData(outputVector, 0);
91 auto output_centroids = vtkMultiBlockDataSet::GetData(outputVector, 1);
92 auto output_matchings = vtkMultiBlockDataSet::GetData(outputVector, 2);
96 intermediateDiagrams_ = {};
98 final_centroids_ = {};
100 intermediateDiagrams_.resize(numInputs);
101 all_matchings_.resize(3);
104 std::vector<double> max_persistences(numInputs);
106 for(
int i = 0; i < numInputs; i++) {
107 auto &diag{this->intermediateDiagrams_[i]};
110 this->
printErr(
"Could not read Persistence Diagram");
118 diag.emplace_back(diag[0]);
122 max_persistences[i] = diag[0].persistence();
125 this->max_dimension_total_
126 = *std::max_element(max_persistences.begin(), max_persistences.end());
134 inv_clustering_ = this->
execute(
135 intermediateDiagrams_, final_centroids_, all_matchings_);
140 final_centroids_.resize(1);
141 inv_clustering_.resize(numInputs);
142 for(
int i_input = 0; i_input < numInputs; i_input++) {
143 inv_clustering_[i_input] = 0;
148 pdBarycenter.setWasserstein(wassersteinMetric);
149 pdBarycenter.setMethod(2);
150 pdBarycenter.setNumberOfInputs(numInputs);
155 pdBarycenter.setAlpha(
Alpha);
156 pdBarycenter.setLambda(
Lambda);
159 pdBarycenter.execute(
160 intermediateDiagrams_, final_centroids_[0], all_matchings_);
167 this->all_matchings_, this->inv_clustering_,
168 this->DisplayMethod, this->Spacing,
169 this->max_dimension_total_);
171 this->all_matchings_, input[0], this->DisplayMethod,
172 this->Spacing, this->max_dimension_total_);
175 this->all_matchings_, this->final_centroids_, this->inv_clustering_,
176 this->DisplayMethod, this->Spacing, this->max_dimension_total_);
213 vtkMultiBlockDataSet *output,
214 const std::vector<vtkUnstructuredGrid *> &diagsVTU,
215 const std::vector<ttk::DiagramType> &diags,
216 const std::vector<std::vector<std::vector<ttk::MatchingType>>>
217 &matchingsPerCluster,
218 const std::vector<int> &inv_clustering,
220 const double spacing,
221 const double max_persistence)
const {
224 std::vector<int> diagIdInClust{};
226 std::vector<int> clustSize{};
232 = 1 + *std::max_element(inv_clustering.begin(), inv_clustering.end());
233 clustSize.resize(nClusters, 0);
234 diagIdInClust.resize(diagsVTU.size());
235 for(
size_t i = 0; i < inv_clustering.size(); ++i) {
236 auto &diagsInClust = clustSize[inv_clustering[i]];
237 diagIdInClust[i] = diagsInClust;
242 output->SetNumberOfBlocks(diagsVTU.size());
244 for(
size_t i = 0; i < diagsVTU.size(); ++i) {
245 vtkNew<vtkUnstructuredGrid> vtu{};
246 vtu->ShallowCopy(diagsVTU[i]);
249 vtkNew<vtkIntArray> clusterId{};
250 clusterId->SetName(
"ClusterId");
251 clusterId->SetNumberOfComponents(1);
252 clusterId->SetNumberOfTuples(1);
253 clusterId->Fill(inv_clustering[i]);
254 vtu->GetFieldData()->AddArray(clusterId);
257 vtkNew<vtkIntArray> cidPD{};
258 cidPD->SetName(
"ClusterID");
259 cidPD->SetNumberOfComponents(1);
260 cidPD->SetNumberOfTuples(vtu->GetNumberOfPoints());
261 cidPD->Fill(inv_clustering[i]);
262 vtu->GetPointData()->AddArray(cidPD);
265 vtkNew<vtkDoubleArray> pointPers{};
266 pointPers->SetName(
"Persistence");
267 pointPers->SetNumberOfTuples(vtu->GetNumberOfPoints());
268 vtu->GetPointData()->AddArray(pointPers);
271 const auto persArray = vtu->GetCellData()->GetArray(
"Persistence");
272 for(
int j = 0; j < vtu->GetNumberOfCells() - 1; ++j) {
273 const auto pers = persArray->GetTuple1(j);
274 pointPers->SetTuple1(2 * j + 0, pers);
275 pointPers->SetTuple1(2 * j + 1, pers);
277 pointPers->SetTuple1(vtu->GetNumberOfPoints() - 1,
278 persArray->GetTuple1(vtu->GetNumberOfCells() - 1));
280 const auto cid = inv_clustering[i];
281 const auto &matchings{matchingsPerCluster[cid][i]};
282 double minSadCost{}, sadSadCost{}, sadMaxCost{};
283 const auto &diag{diags[i]};
285 for(
size_t j = 0; j < matchings.size(); ++j) {
286 const auto &m{matchings[j]};
287 const auto bidderId{std::get<0>(m)};
291 this->
printWrn(
"Out-of-bounds access averted");
296 const auto &p1{diag[bidderId]};
298 minSadCost += std::get<2>(m);
301 sadSadCost += std::get<2>(m);
303 sadMaxCost += std::get<2>(m);
308 addCostsAsFieldData(vtu, minSadCost, sadSadCost, sadMaxCost);
315 const std::array<double, 3> trans{0, 0, i == 0 ? -spacing : spacing};
317 output->SetBlock(i, vtu);
319 const auto c = inv_clustering[i];
320 const auto angle = 2.0 *
M_PI *
static_cast<double>(diagIdInClust[i])
321 /
static_cast<double>(clustSize[c]);
323 const std::array<double, 3> trans{
324 3.0 * (spacing + 0.2) * max_persistence * c
325 + spacing * max_persistence * std::cos(angle) + 0.2,
326 spacing * max_persistence * std::sin(angle), 0};
328 output->SetBlock(i, vtu);
332 output->SetBlock(i, vtu);
338 vtkMultiBlockDataSet *output,
339 const std::vector<DiagramType> &final_centroids,
340 const std::vector<std::vector<std::vector<ttk::MatchingType>>>
341 &matchingsPerCluster,
342 vtkUnstructuredGrid *
const someInputDiag,
344 const double spacing,
345 const double max_persistence)
const {
347 if(final_centroids.size() != matchingsPerCluster.size()) {
348 this->
printWrn(
"Inconsistent matchings vector size");
353 const auto dim{
static_cast<int>(someInputDiag->GetCellData()
358 for(
size_t i = 0; i < final_centroids.size(); ++i) {
359 vtkNew<vtkUnstructuredGrid> vtu{};
360 DiagramToVTU(vtu, final_centroids[i], da, *
this, dim,
false);
363 vtkNew<vtkIntArray> cid{};
364 cid->SetName(
"ClusterId");
365 cid->SetNumberOfTuples(1);
366 cid->SetTuple1(0, i);
367 vtu->GetFieldData()->AddArray(cid);
370 vtkNew<vtkIntArray> cidPD{};
371 cidPD->SetName(
"ClusterID");
372 cidPD->SetNumberOfComponents(1);
373 cidPD->SetNumberOfTuples(vtu->GetNumberOfPoints());
375 vtu->GetPointData()->AddArray(cidPD);
378 vtkNew<vtkDoubleArray> pointPers{};
379 pointPers->SetName(
"Persistence");
380 pointPers->SetNumberOfTuples(vtu->GetNumberOfPoints());
381 vtu->GetPointData()->AddArray(pointPers);
384 for(
int j = 0; j < vtu->GetNumberOfCells() - 1; ++j) {
385 const auto persArray = vtu->GetCellData()->GetArray(
"Persistence");
386 const auto pers = persArray->GetTuple1(j);
387 pointPers->SetTuple1(2 * j + 0, pers);
388 pointPers->SetTuple1(2 * j + 1, pers);
391 double minSadCost{}, sadSadCost{}, sadMaxCost{};
393 for(
const auto &matchingsPerDiag : matchingsPerCluster[i]) {
394 for(
const auto &m : matchingsPerDiag) {
395 const auto goodId{std::get<1>(m)};
396 const auto &p0{final_centroids[i][goodId]};
398 minSadCost += std::get<2>(m);
401 sadSadCost += std::get<2>(m);
403 sadMaxCost += std::get<2>(m);
408 addCostsAsFieldData(vtu, minSadCost, sadSadCost, sadMaxCost);
412 const std::array<double, 3> trans{
413 3.0 * (spacing + 0.2) * max_persistence * i, 0, 0};
415 output->SetBlock(i, vtu);
419 output->SetBlock(i, vtu);
425 vtkMultiBlockDataSet *output,
426 const size_t nClusters,
427 const std::vector<DiagramType> &diags,
428 const std::vector<std::vector<std::vector<ttk::MatchingType>>>
429 &matchingsPerCluster,
430 const std::vector<DiagramType> ¢roids,
431 const std::vector<int> &inv_clustering,
433 const double spacing,
434 const double max_persistence)
const {
437 std::vector<int> diagIdInClust{};
439 std::vector<int> clustSize{};
443 clustSize.resize(nClusters, 0);
444 diagIdInClust.resize(diags.size());
445 for(
size_t i = 0; i < inv_clustering.size(); ++i) {
446 auto &diagsInClust = clustSize[inv_clustering[i]];
447 diagIdInClust[i] = diagsInClust;
454 std::vector<int> matchings_count(centroids[0].size());
455 std::vector<int> count_to_good{};
457 for(
size_t i = 0; i < diags.size(); ++i) {
458 const auto cid = inv_clustering[i];
459 const auto &diag{diags[i]};
460 const auto &matchings{matchingsPerCluster[cid][i]};
462 vtkNew<vtkUnstructuredGrid> matchingsGrid{};
464 const auto nCells{matchings.size()};
465 const auto nPoints{2 * matchings.size()};
467 vtkNew<vtkPoints> points{};
468 points->SetNumberOfPoints(nPoints);
469 matchingsGrid->SetPoints(points);
472 vtkNew<vtkIntArray> diagIdVerts{};
473 diagIdVerts->SetName(
"DiagramID");
474 diagIdVerts->SetNumberOfTuples(nPoints);
475 matchingsGrid->GetPointData()->AddArray(diagIdVerts);
477 vtkNew<vtkIntArray> pointId{};
478 pointId->SetName(
"PointID");
479 pointId->SetNumberOfTuples(nPoints);
480 matchingsGrid->GetPointData()->AddArray(pointId);
483 vtkNew<vtkIntArray> diagIdCells{};
484 diagIdCells->SetName(
"DiagramID");
485 diagIdCells->SetNumberOfTuples(nCells);
486 matchingsGrid->GetCellData()->AddArray(diagIdCells);
488 vtkNew<vtkIntArray> clusterId{};
489 clusterId->SetName(
"ClusterID");
490 clusterId->SetNumberOfTuples(nCells);
491 clusterId->Fill(cid);
492 matchingsGrid->GetCellData()->AddArray(clusterId);
494 vtkNew<vtkDoubleArray> matchCost{};
495 matchCost->SetName(
"Cost");
496 matchCost->SetNumberOfTuples(nCells);
497 matchingsGrid->GetCellData()->AddArray(matchCost);
499 vtkNew<vtkIntArray> pairType{};
500 pairType->SetName(
"PairType");
501 pairType->SetNumberOfTuples(nCells);
502 matchingsGrid->GetCellData()->AddArray(pairType);
504 vtkNew<vtkIntArray> isDiagonal{};
505 isDiagonal->SetName(
"IsDiagonal");
506 isDiagonal->SetNumberOfTuples(nCells);
507 matchingsGrid->GetCellData()->AddArray(isDiagonal);
509 for(
size_t j = 0; j < matchings.size(); ++j) {
510 const auto &m{matchings[j]};
511 const auto bidderId{std::get<0>(m)};
512 const auto goodId{std::get<1>(m)};
517 this->
printWrn(
"Out-of-bounds access averted");
522 matchings_count[goodId] += 1;
523 count_to_good.push_back(goodId);
526 const auto &p0{centroids[cid][goodId]};
527 std::array<double, 3> coords1{};
528 std::array<double, 3> coords0{p0.birth.sfValue, p0.death.sfValue, 0};
531 const auto &p{diag[bidderId]};
532 coords1[0] = p.birth.sfValue;
533 coords1[1] = p.death.sfValue;
535 isDiagonal->SetTuple1(j, 0);
537 const double diagonal_projection
538 = (p0.birth.sfValue + p0.death.sfValue) / 2.0;
539 coords1[0] = diagonal_projection;
540 coords1[1] = diagonal_projection;
542 isDiagonal->SetTuple1(j, 1);
546 const auto angle = 2.0 *
M_PI *
static_cast<double>(diagIdInClust[i])
547 /
static_cast<double>(clustSize[cid]);
549 = 3.0 * (std::abs(spacing) + 0.2) * max_persistence * cid;
551 coords1[0] += shift + spacing * max_persistence * std::cos(angle);
552 coords1[1] += spacing * max_persistence * std::sin(angle);
555 coords1[2] = (diags.size() == 2 && i == 0) ? -spacing : spacing;
558 points->SetPoint(2 * j + 0, coords0.data());
559 points->SetPoint(2 * j + 1, coords1.data());
560 std::array<vtkIdType, 2> ids{
561 2 *
static_cast<vtkIdType
>(j) + 0,
562 2 *
static_cast<vtkIdType
>(j) + 1,
564 matchingsGrid->InsertNextCell(VTK_LINE, 2, ids.data());
566 diagIdCells->SetTuple1(j, i);
567 matchCost->SetTuple1(j, std::get<2>(m));
568 diagIdVerts->SetTuple1(2 * j + 0, i);
569 diagIdVerts->SetTuple1(2 * j + 1, i);
570 pointId->SetTuple1(2 * j + 0, goodId);
571 pointId->SetTuple1(2 * j + 1, bidderId);
574 const auto &p1{diag[bidderId]};
575 pairType->SetTuple1(j, p1.isFinite ? p1.dim : -1);
577 pairType->SetTuple1(j, p0.dim);
585 output->SetBlock(i, matchingsGrid);
589 if(nClusters == 1 && diags.size() == 2) {
591 for(
size_t i = 0; i < diags.size(); ++i) {
592 vtkNew<vtkIntArray> matchNumber{};
593 matchNumber->SetName(
"MatchNumber");
595 = vtkUnstructuredGrid::SafeDownCast(output->GetBlock(i));
596 const auto nCells = matchings->GetNumberOfCells();
597 matchNumber->SetNumberOfTuples(nCells);
598 for(
int j = 0; j < nCells; j++) {
599 const auto goodId = count_to_good[j + (i == 0 ? 0 : nPrevCells)];
600 matchNumber->SetTuple1(j, matchings_count[goodId]);
602 matchings->GetCellData()->AddArray(matchNumber);