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 for(
int j = 0; j < vtu->GetNumberOfCells() - 1; ++j) {
272 const auto persArray = vtu->GetCellData()->GetArray(
"Persistence");
273 const auto pers = persArray->GetTuple1(j);
274 pointPers->SetTuple1(2 * j + 0, pers);
275 pointPers->SetTuple1(2 * j + 1, pers);
278 const auto cid = inv_clustering[i];
279 const auto &matchings{matchingsPerCluster[cid][i]};
280 double minSadCost{}, sadSadCost{}, sadMaxCost{};
281 const auto &diag{diags[i]};
283 for(
size_t j = 0; j < matchings.size(); ++j) {
284 const auto &m{matchings[j]};
285 const auto bidderId{std::get<0>(m)};
289 this->
printWrn(
"Out-of-bounds access averted");
294 const auto &p1{diag[bidderId]};
296 minSadCost += std::get<2>(m);
299 sadSadCost += std::get<2>(m);
301 sadMaxCost += std::get<2>(m);
306 addCostsAsFieldData(vtu, minSadCost, sadSadCost, sadMaxCost);
313 const std::array<double, 3> trans{0, 0, i == 0 ? -spacing : spacing};
315 output->SetBlock(i, vtu);
317 const auto c = inv_clustering[i];
318 const auto angle = 2.0 *
M_PI *
static_cast<double>(diagIdInClust[i])
319 /
static_cast<double>(clustSize[c]);
321 const std::array<double, 3> trans{
322 3.0 * (spacing + 0.2) * max_persistence * c
323 + spacing * max_persistence * std::cos(angle) + 0.2,
324 spacing * max_persistence * std::sin(angle), 0};
326 output->SetBlock(i, vtu);
330 output->SetBlock(i, vtu);
336 vtkMultiBlockDataSet *output,
337 const std::vector<DiagramType> &final_centroids,
338 const std::vector<std::vector<std::vector<ttk::MatchingType>>>
339 &matchingsPerCluster,
340 vtkUnstructuredGrid *
const someInputDiag,
342 const double spacing,
343 const double max_persistence)
const {
345 if(final_centroids.size() != matchingsPerCluster.size()) {
346 this->
printWrn(
"Inconsistent matchings vector size");
351 const auto dim{
static_cast<int>(someInputDiag->GetCellData()
356 for(
size_t i = 0; i < final_centroids.size(); ++i) {
357 vtkNew<vtkUnstructuredGrid> vtu{};
358 DiagramToVTU(vtu, final_centroids[i], da, *
this, dim,
false);
361 vtkNew<vtkIntArray> cid{};
362 cid->SetName(
"ClusterId");
363 cid->SetNumberOfTuples(1);
364 cid->SetTuple1(0, i);
365 vtu->GetFieldData()->AddArray(cid);
368 vtkNew<vtkIntArray> cidPD{};
369 cidPD->SetName(
"ClusterID");
370 cidPD->SetNumberOfComponents(1);
371 cidPD->SetNumberOfTuples(vtu->GetNumberOfPoints());
373 vtu->GetPointData()->AddArray(cidPD);
376 vtkNew<vtkDoubleArray> pointPers{};
377 pointPers->SetName(
"Persistence");
378 pointPers->SetNumberOfTuples(vtu->GetNumberOfPoints());
379 vtu->GetPointData()->AddArray(pointPers);
382 for(
int j = 0; j < vtu->GetNumberOfCells() - 1; ++j) {
383 const auto persArray = vtu->GetCellData()->GetArray(
"Persistence");
384 const auto pers = persArray->GetTuple1(j);
385 pointPers->SetTuple1(2 * j + 0, pers);
386 pointPers->SetTuple1(2 * j + 1, pers);
389 double minSadCost{}, sadSadCost{}, sadMaxCost{};
391 for(
const auto &matchingsPerDiag : matchingsPerCluster[i]) {
392 for(
const auto &m : matchingsPerDiag) {
393 const auto goodId{std::get<1>(m)};
394 const auto &p0{final_centroids[i][goodId]};
396 minSadCost += std::get<2>(m);
399 sadSadCost += std::get<2>(m);
401 sadMaxCost += std::get<2>(m);
406 addCostsAsFieldData(vtu, minSadCost, sadSadCost, sadMaxCost);
410 const std::array<double, 3> trans{
411 3.0 * (spacing + 0.2) * max_persistence * i, 0, 0};
413 output->SetBlock(i, vtu);
417 output->SetBlock(i, vtu);
423 vtkMultiBlockDataSet *output,
424 const size_t nClusters,
425 const std::vector<DiagramType> &diags,
426 const std::vector<std::vector<std::vector<ttk::MatchingType>>>
427 &matchingsPerCluster,
428 const std::vector<DiagramType> ¢roids,
429 const std::vector<int> &inv_clustering,
431 const double spacing,
432 const double max_persistence)
const {
435 std::vector<int> diagIdInClust{};
437 std::vector<int> clustSize{};
441 clustSize.resize(nClusters, 0);
442 diagIdInClust.resize(diags.size());
443 for(
size_t i = 0; i < inv_clustering.size(); ++i) {
444 auto &diagsInClust = clustSize[inv_clustering[i]];
445 diagIdInClust[i] = diagsInClust;
452 std::vector<int> matchings_count(centroids[0].size());
453 std::vector<int> count_to_good{};
455 for(
size_t i = 0; i < diags.size(); ++i) {
456 const auto cid = inv_clustering[i];
457 const auto &diag{diags[i]};
458 const auto &matchings{matchingsPerCluster[cid][i]};
460 vtkNew<vtkUnstructuredGrid> matchingsGrid{};
462 const auto nCells{matchings.size()};
463 const auto nPoints{2 * matchings.size()};
465 vtkNew<vtkPoints> points{};
466 points->SetNumberOfPoints(nPoints);
467 matchingsGrid->SetPoints(points);
470 vtkNew<vtkIntArray> diagIdVerts{};
471 diagIdVerts->SetName(
"DiagramID");
472 diagIdVerts->SetNumberOfTuples(nPoints);
473 matchingsGrid->GetPointData()->AddArray(diagIdVerts);
475 vtkNew<vtkIntArray> pointId{};
476 pointId->SetName(
"PointID");
477 pointId->SetNumberOfTuples(nPoints);
478 matchingsGrid->GetPointData()->AddArray(pointId);
481 vtkNew<vtkIntArray> diagIdCells{};
482 diagIdCells->SetName(
"DiagramID");
483 diagIdCells->SetNumberOfTuples(nCells);
484 matchingsGrid->GetCellData()->AddArray(diagIdCells);
486 vtkNew<vtkIntArray> clusterId{};
487 clusterId->SetName(
"ClusterID");
488 clusterId->SetNumberOfTuples(nCells);
489 clusterId->Fill(cid);
490 matchingsGrid->GetCellData()->AddArray(clusterId);
492 vtkNew<vtkDoubleArray> matchCost{};
493 matchCost->SetName(
"Cost");
494 matchCost->SetNumberOfTuples(nCells);
495 matchingsGrid->GetCellData()->AddArray(matchCost);
497 vtkNew<vtkIntArray> pairType{};
498 pairType->SetName(
"PairType");
499 pairType->SetNumberOfTuples(nCells);
500 matchingsGrid->GetCellData()->AddArray(pairType);
502 vtkNew<vtkIntArray> isDiagonal{};
503 isDiagonal->SetName(
"IsDiagonal");
504 isDiagonal->SetNumberOfTuples(nCells);
505 matchingsGrid->GetCellData()->AddArray(isDiagonal);
507 for(
size_t j = 0; j < matchings.size(); ++j) {
508 const auto &m{matchings[j]};
509 const auto bidderId{std::get<0>(m)};
510 const auto goodId{std::get<1>(m)};
515 this->
printWrn(
"Out-of-bounds access averted");
520 matchings_count[goodId] += 1;
521 count_to_good.push_back(goodId);
524 const auto &p0{centroids[cid][goodId]};
525 std::array<double, 3> coords1{};
526 std::array<double, 3> coords0{p0.birth.sfValue, p0.death.sfValue, 0};
529 const auto &p{diag[bidderId]};
530 coords1[0] = p.birth.sfValue;
531 coords1[1] = p.death.sfValue;
533 isDiagonal->SetTuple1(j, 0);
535 const double diagonal_projection
536 = (p0.birth.sfValue + p0.death.sfValue) / 2.0;
537 coords1[0] = diagonal_projection;
538 coords1[1] = diagonal_projection;
540 isDiagonal->SetTuple1(j, 1);
544 const auto angle = 2.0 *
M_PI *
static_cast<double>(diagIdInClust[i])
545 /
static_cast<double>(clustSize[cid]);
547 = 3.0 * (std::abs(spacing) + 0.2) * max_persistence * cid;
549 coords1[0] += shift + spacing * max_persistence * std::cos(angle);
550 coords1[1] += spacing * max_persistence * std::sin(angle);
553 coords1[2] = (diags.size() == 2 && i == 0) ? -spacing : spacing;
556 points->SetPoint(2 * j + 0, coords0.data());
557 points->SetPoint(2 * j + 1, coords1.data());
558 std::array<vtkIdType, 2> ids{
559 2 *
static_cast<vtkIdType
>(j) + 0,
560 2 *
static_cast<vtkIdType
>(j) + 1,
562 matchingsGrid->InsertNextCell(VTK_LINE, 2, ids.data());
564 diagIdCells->SetTuple1(j, i);
565 matchCost->SetTuple1(j, std::get<2>(m));
566 diagIdVerts->SetTuple1(2 * j + 0, i);
567 diagIdVerts->SetTuple1(2 * j + 1, i);
568 pointId->SetTuple1(2 * j + 0, goodId);
569 pointId->SetTuple1(2 * j + 1, bidderId);
572 const auto &p1{diag[bidderId]};
573 pairType->SetTuple1(j, p1.isFinite ? p1.dim : -1);
575 pairType->SetTuple1(j, p0.dim);
583 output->SetBlock(i, matchingsGrid);
587 if(nClusters == 1 && diags.size() == 2) {
589 for(
size_t i = 0; i < diags.size(); ++i) {
590 vtkNew<vtkIntArray> matchNumber{};
591 matchNumber->SetName(
"MatchNumber");
593 = vtkUnstructuredGrid::SafeDownCast(output->GetBlock(i));
594 const auto nCells = matchings->GetNumberOfCells();
595 matchNumber->SetNumberOfTuples(nCells);
596 for(
int j = 0; j < nCells; j++) {
597 const auto goodId = count_to_good[j + (i == 0 ? 0 : nPrevCells)];
598 matchNumber->SetTuple1(j, matchings_count[goodId]);
600 matchings->GetCellData()->AddArray(matchNumber);