TTK
Loading...
Searching...
No Matches
PersistenceDiagram.h
Go to the documentation of this file.
1
132
133#pragma once
134
135// base code includes
136#include <ApproximateTopology.h>
138#include <FTMTreePP.h>
141#include <ProgressiveTopology.h>
142#include <Triangulation.h>
143
144namespace ttk {
145
150 class PersistenceDiagram : virtual public Debug {
151
152 public:
153 enum class BACKEND {
154 FTM = 0,
159 };
160
162
163 inline void setBackend(const BACKEND be) {
164 this->BackEnd = be;
165 }
166
167 inline void setComputeMinSad(const bool data) {
168 this->dms_.setComputeMinSad(data);
169 }
170 inline void setComputeSadSad(const bool data) {
171 this->dms_.setComputeSadSad(data);
172 }
173 inline void setComputeSadMax(const bool data) {
174 this->dms_.setComputeSadMax(data);
175 }
176
181 template <typename scalarType, typename triangulationType>
182 void
183 augmentPersistenceDiagram(std::vector<PersistencePair> &persistencePairs,
184 const scalarType *const scalars,
185 const triangulationType *triangulation);
186
188 ftm::TreeType treeType,
189 const SimplexId vertexId) const;
190
191 void sortPersistenceDiagram(std::vector<PersistencePair> &diagram,
192 const SimplexId *const offsets) const;
193
194 template <typename scalarType>
196 ftm::FTMTreePP &tree,
197 const std::vector<
198 std::tuple<ttk::SimplexId, ttk::SimplexId, scalarType, bool>> &pairs,
199 std::vector<PersistencePair> &diagram) const;
200
209 template <typename scalarType, class triangulationType>
210 int execute(std::vector<PersistencePair> &CTDiagram,
211 const scalarType *inputScalars,
212 const size_t scalarsMTime,
213 const SimplexId *inputOffsets,
214 const triangulationType *triangulation);
215
216 template <typename scalarType, class triangulationType>
217 int executeFTM(std::vector<PersistencePair> &CTDiagram,
218 const scalarType *inputScalars,
219 const SimplexId *inputOffsets,
220 const triangulationType *triangulation);
221
222 template <class triangulationType>
223 int executeProgressiveTopology(std::vector<PersistencePair> &CTDiagram,
224 const SimplexId *inputOffsets,
225 const triangulationType *triangulation);
226 template <typename scalarType, class triangulationType>
227 int executeApproximateTopology(std::vector<PersistencePair> &CTDiagram,
228 const scalarType *inputScalars,
229 const triangulationType *triangulation);
230
231 template <class triangulationType>
232 int executePersistentSimplex(std::vector<PersistencePair> &CTDiagram,
233 const SimplexId *inputOffsets,
234 const triangulationType *triangulation);
235
236 template <typename scalarType, class triangulationType>
237 int executeDiscreteMorseSandwich(std::vector<PersistencePair> &CTDiagram,
238 const scalarType *inputScalars,
239 const size_t scalarsMTime,
240 const SimplexId *inputOffsets,
241 const triangulationType *triangulation);
242
243 template <class triangulationType>
244 void checkProgressivityRequirement(const triangulationType *triangulation);
245
246 template <class triangulationType>
247 void checkManifold(const triangulationType *const triangulation);
248
249 inline void
251 if(triangulation) {
252 triangulation->preconditionBoundaryVertices();
253 if(this->BackEnd == BACKEND::FTM
259 }
263 dms_.preconditionTriangulation(triangulation);
264 triangulation->preconditionManifold();
265 }
268 psp_.preconditionTriangulation(triangulation);
269 }
270 }
271 }
272
273 inline void setOutputMonotonyOffsets(void *data) {
275 }
276 inline void setOutputOffsets(void *data) {
277 outputOffsets_ = data;
278 }
279 inline void setOutputScalars(void *data) {
280 outputScalars_ = data;
281 }
282 inline void setDeltaApproximate(double data) {
283 approxT_.setDelta(data);
284 }
285
286 protected:
287 bool IgnoreBoundary{false};
292
293 // int BackEnd{0};
295 // progressivity
298
301 bool IsResumable{false};
302 double TimeLimit{};
303
304 // Approximation
308 double Epsilon;
309 };
310} // namespace ttk
311
312template <typename scalarType>
314 ftm::FTMTreePP &tree,
315 const std::vector<
316 std::tuple<ttk::SimplexId, ttk::SimplexId, scalarType, bool>> &pairs,
317 std::vector<PersistencePair> &diagram) const {
318
319 const ttk::SimplexId numberOfPairs = pairs.size();
320 diagram.resize(numberOfPairs);
321 for(ttk::SimplexId i = 0; i < numberOfPairs; ++i) {
322 const ttk::SimplexId v0 = std::get<0>(pairs[i]);
323 const ttk::SimplexId v1 = std::get<1>(pairs[i]);
324 const bool type = std::get<3>(pairs[i]);
325
326 if(type == true) {
327 diagram[i] = PersistencePair{
329 v0, getNodeType(tree.getJoinTree(), ftm::TreeType::Join, v0), {}, {}},
331 v1, getNodeType(tree.getJoinTree(), ftm::TreeType::Join, v1), {}, {}},
332 0, true};
333 } else {
334 diagram[i] = PersistencePair{
336 v1,
338 {},
339 {}},
341 v0,
343 {},
344 {}},
345 2, true};
346 }
347 }
348
349 diagram.back().isFinite = false; // global extrema pair is infinite
350
351 return 0;
352}
353
354template <typename scalarType, typename triangulationType>
356 std::vector<PersistencePair> &persistencePairs,
357 const scalarType *const scalars,
358 const triangulationType *triangulation) {
359
360#ifdef TTK_ENABLE_OPENMP
361#pragma omp parallel for num_threads(threadNumber_)
362#endif // TTK_ENABLE_OPENMP
363 for(std::size_t i = 0; i < persistencePairs.size(); ++i) {
364 auto &pair{persistencePairs[i]};
365 triangulation->getVertexPoint(pair.birth.id, pair.birth.coords[0],
366 pair.birth.coords[1], pair.birth.coords[2]);
367 pair.birth.sfValue = scalars[pair.birth.id];
368 triangulation->getVertexPoint(pair.death.id, pair.death.coords[0],
369 pair.death.coords[1], pair.death.coords[2]);
370 pair.death.sfValue = scalars[pair.death.id];
371 }
372}
373
374template <typename scalarType, class triangulationType>
375int ttk::PersistenceDiagram::execute(std::vector<PersistencePair> &CTDiagram,
376 const scalarType *inputScalars,
377 const size_t scalarsMTime,
378 const SimplexId *inputOffsets,
379 const triangulationType *triangulation) {
380
382
383 checkProgressivityRequirement(triangulation);
384 checkManifold(triangulation);
385
386 Timer tm{};
387
388 switch(BackEnd) {
389 case BACKEND::PERSISTENT_SIMPLEX:
390 executePersistentSimplex(CTDiagram, inputOffsets, triangulation);
391 break;
392 case BACKEND::DISCRETE_MORSE_SANDWICH:
393 executeDiscreteMorseSandwich(
394 CTDiagram, inputScalars, scalarsMTime, inputOffsets, triangulation);
395 break;
396 case BACKEND::PROGRESSIVE_TOPOLOGY:
397 executeProgressiveTopology(CTDiagram, inputOffsets, triangulation);
398 break;
399 case BACKEND::APPROXIMATE_TOPOLOGY:
400 executeApproximateTopology(CTDiagram, inputScalars, triangulation);
401 break;
402 case BACKEND::FTM:
403 executeFTM(CTDiagram, inputScalars, inputOffsets, triangulation);
404 break;
405 default:
406 printErr("No method was selected");
407 }
408
409 this->printMsg("Complete", 1.0, tm.getElapsedTime(), this->threadNumber_);
410
411 // augment persistence pairs with meta-data
412 augmentPersistenceDiagram(CTDiagram, inputScalars, triangulation);
413
414 // finally sort the diagram
415 sortPersistenceDiagram(CTDiagram, inputOffsets);
416
418
419 return 0;
420}
421
422template <class triangulationType>
424 std::vector<PersistencePair> &CTDiagram,
425 const SimplexId *inputOffsets,
426 const triangulationType *triangulation) {
427
428 Timer tm{};
429 const auto dim = triangulation->getDimensionality();
430
431 std::vector<ttk::PersistentSimplexPairs::PersistencePair> pairs{};
432
433 psp_.setDebugLevel(this->debugLevel_);
434 psp_.setThreadNumber(this->threadNumber_);
435 psp_.computePersistencePairs(pairs, inputOffsets, *triangulation);
436 dms_.setInputOffsets(inputOffsets);
437
438 // convert PersistentSimplex pairs (with critical cells id) to PL
439 // pairs (with vertices id)
440
441#ifdef TTK_ENABLE_OPENMP
442#pragma omp parallel for num_threads(threadNumber_)
443#endif // TTK_ENABLE_OPENMP
444 for(size_t i = 0; i < pairs.size(); ++i) {
445 auto &pair{pairs[i]};
446 if(pair.type > 0) {
447 pair.birth = dms_.getCellGreaterVertex(
448 Cell{pair.type, pair.birth}, *triangulation);
449 }
450 if(pair.death != -1) {
451 pair.death = dms_.getCellGreaterVertex(
452 Cell{pair.type + 1, pair.death}, *triangulation);
453 }
454 }
455
456 CTDiagram.reserve(pairs.size() + 1);
457
458 // find the global maximum
459 const auto nVerts = triangulation->getNumberOfVertices();
460 const SimplexId globmax = std::distance(
461 inputOffsets, std::max_element(inputOffsets, inputOffsets + nVerts));
462
463 // convert pairs to the relevant format
464 for(const auto &p : pairs) {
465 const auto isFinite = (p.death >= 0);
466 const auto death = isFinite ? p.death : globmax;
467 if(p.type == 0) {
468 const auto deathType = (isFinite && dim > 1)
471 CTDiagram.emplace_back(PersistencePair{
473 CriticalVertex{death, deathType, {}, {}}, p.type, isFinite});
474 } else if(p.type == 1) {
475 const auto birthType
477 const auto deathType = (isFinite && dim == 3)
480 CTDiagram.emplace_back(PersistencePair{
481 CriticalVertex{p.birth, birthType, {}, {}},
482 CriticalVertex{death, deathType, {}, {}}, p.type, isFinite});
483 } else if(p.type == 2) {
484 CTDiagram.emplace_back(PersistencePair{
485 CriticalVertex{p.birth, CriticalType::Saddle2, {}, {}},
486 CriticalVertex{death, CriticalType::Local_maximum, {}, {}}, p.type,
487 isFinite});
488 }
489 }
490
491 return 0;
492}
493
494template <typename scalarType, class triangulationType>
496 std::vector<PersistencePair> &CTDiagram,
497 const scalarType *inputScalars,
498 const size_t scalarsMTime,
499 const SimplexId *inputOffsets,
500 const triangulationType *triangulation) {
501
502 Timer tm{};
503 const auto dim = triangulation->getDimensionality();
504
505 dms_.buildGradient(inputScalars, scalarsMTime, inputOffsets, *triangulation);
506 std::vector<DiscreteMorseSandwich::PersistencePair> dms_pairs{};
507 dms_.computePersistencePairs(
508 dms_pairs, inputOffsets, *triangulation, this->IgnoreBoundary);
509 CTDiagram.resize(dms_pairs.size());
510
511 // transform DiscreteMorseSandwich pairs (critical cells id) to PL
512 // pairs (vertices id)
513#ifdef TTK_ENABLE_OPENMP
514#pragma omp parallel for num_threads(threadNumber_)
515#endif // TTK_ENABLE_OPENMP
516 for(size_t i = 0; i < dms_pairs.size(); ++i) {
517 auto &pair{dms_pairs[i]};
518 if(pair.type > 0) {
519 pair.birth = dms_.getCellGreaterVertex(
520 Cell{pair.type, pair.birth}, *triangulation);
521 }
522 if(pair.death != -1) {
523 pair.death = dms_.getCellGreaterVertex(
524 Cell{pair.type + 1, pair.death}, *triangulation);
525 }
526 }
527
528 // find the global maximum
529 const auto nVerts = triangulation->getNumberOfVertices();
530 const SimplexId globmax = std::distance(
531 inputOffsets, std::max_element(inputOffsets, inputOffsets + nVerts));
532
533 // convert pairs to the relevant format
534#ifdef TTK_ENABLE_OPENMP
535#pragma omp parallel for num_threads(threadNumber_)
536#endif // TTK_ENABLE_OPENMP
537 for(size_t i = 0; i < dms_pairs.size(); ++i) {
538 const auto &p{dms_pairs[i]};
539 const auto isFinite = (p.death >= 0);
540 const auto death = isFinite ? p.death : globmax;
541
542 if(p.type == 0) {
543 const auto dtype = (isFinite && dim > 1) ? CriticalType::Saddle1
545 CTDiagram[i] = PersistencePair{
547 CriticalVertex{death, dtype, {}, {}}, p.type, isFinite};
548 } else if(p.type == 1) {
549 const auto btype
551 const auto dtype = (isFinite && dim == 3) ? CriticalType::Saddle2
553 CTDiagram[i] = PersistencePair{CriticalVertex{p.birth, btype, {}, {}},
554 CriticalVertex{death, dtype, {}, {}},
555 p.type, isFinite};
556 } else if(p.type == 2) {
557 const auto btype = (isFinite || dim == 3) ? CriticalType::Saddle2
559 CTDiagram[i] = PersistencePair{
560 CriticalVertex{p.birth, btype, {}, {}},
561 CriticalVertex{death, CriticalType::Local_maximum, {}, {}}, p.type,
562 isFinite};
563 }
564 }
565
566 return 0;
567}
568
569template <typename scalarType, class triangulationType>
571 std::vector<PersistencePair> &CTDiagram,
572 const scalarType *inputScalars,
573 const triangulationType *triangulation) {
574
575 approxT_.setDebugLevel(debugLevel_);
576 approxT_.setThreadNumber(threadNumber_);
577 approxT_.setupTriangulation(const_cast<ttk::ImplicitTriangulation *>(
578 (const ImplicitTriangulation *)triangulation));
579 approxT_.setStartingResolutionLevel(StartingResolutionLevel);
580 approxT_.setStoppingResolutionLevel(StoppingResolutionLevel);
581 approxT_.setPreallocateMemory(true);
582 approxT_.setEpsilon(Epsilon);
583
584 std::vector<ApproximateTopology::PersistencePair> resultDiagram{};
585
586 approxT_.computeApproximatePD(
587 resultDiagram, inputScalars, (scalarType *)outputScalars_,
588 (SimplexId *)outputOffsets_, (int *)outputMonotonyOffsets_);
589
590 // create the final diagram
591 for(const auto &p : resultDiagram) {
592 if(p.pairType == 0) {
593 CTDiagram.emplace_back(PersistencePair{
595 CriticalVertex{p.death, CriticalType::Saddle1, {}, {}}, p.pairType,
596 true});
597 } else if(p.pairType == 2) {
598 CTDiagram.emplace_back(PersistencePair{
599 CriticalVertex{p.birth, CriticalType::Saddle2, {}, {}},
601 p.pairType, true});
602 } else if(p.pairType == -1) {
603 CTDiagram.emplace_back(PersistencePair{
606 p.pairType, false});
607 }
608 }
609
610 return 0;
611}
612
613template <class triangulationType>
615 std::vector<PersistencePair> &CTDiagram,
616 const SimplexId *inputOffsets,
617 const triangulationType *triangulation) {
618
619 progT_.setDebugLevel(debugLevel_);
620 progT_.setThreadNumber(threadNumber_);
621 progT_.setupTriangulation(const_cast<ttk::ImplicitTriangulation *>(
622 (const ImplicitTriangulation *)triangulation));
623 progT_.setStartingResolutionLevel(StartingResolutionLevel);
624 progT_.setStoppingResolutionLevel(StoppingResolutionLevel);
625 progT_.setTimeLimit(TimeLimit);
626 progT_.setIsResumable(IsResumable);
627 progT_.setPreallocateMemory(true);
628
629 std::vector<ProgressiveTopology::PersistencePair> resultDiagram{};
630
631 progT_.computeProgressivePD(resultDiagram, inputOffsets);
632
633 // create the final diagram
634 for(const auto &p : resultDiagram) {
635 if(p.pairType == 0) {
636 CTDiagram.emplace_back(PersistencePair{
638 CriticalVertex{p.death, CriticalType::Saddle1, {}, {}}, p.pairType,
639 true});
640 } else if(p.pairType == 2) {
641 CTDiagram.emplace_back(PersistencePair{
642 CriticalVertex{p.birth, CriticalType::Saddle2, {}, {}},
644 p.pairType, true});
645 } else if(p.pairType == -1) {
646 CTDiagram.emplace_back(PersistencePair{
648 CriticalVertex{p.death, CriticalType::Local_maximum, {}, {}}, 0,
649 false});
650 }
651 }
652
653 return 0;
654}
655
656template <typename scalarType, class triangulationType>
658 std::vector<PersistencePair> &CTDiagram,
659 const scalarType *inputScalars,
660 const SimplexId *inputOffsets,
661 const triangulationType *triangulation) {
662
663 contourTree_.setVertexScalars(inputScalars);
664 contourTree_.setTreeType(ftm::TreeType::Join_Split);
665 contourTree_.setVertexSoSoffsets(inputOffsets);
666 contourTree_.setSegmentation(false);
667 contourTree_.build<scalarType>(triangulation);
668
669 // get persistence pairs
670 std::vector<std::tuple<ttk::SimplexId, ttk::SimplexId, scalarType>> JTPairs;
671 std::vector<std::tuple<ttk::SimplexId, ttk::SimplexId, scalarType>> STPairs;
672 contourTree_.computePersistencePairs<scalarType>(JTPairs, true);
673 contourTree_.computePersistencePairs<scalarType>(STPairs, false);
674
675 // merge pairs
676 const auto JTSize = JTPairs.size();
677 const auto STSize = STPairs.size();
678 std::vector<std::tuple<ttk::SimplexId, ttk::SimplexId, scalarType, bool>>
679 CTPairs(JTSize + STSize);
680 for(size_t i = 0; i < JTSize; ++i) {
681 const auto &x = JTPairs[i];
682 CTPairs[i]
683 = std::make_tuple(std::get<0>(x), std::get<1>(x), std::get<2>(x), true);
684 }
685 for(size_t i = 0; i < STSize; ++i) {
686 const auto &x = STPairs[i];
687 CTPairs[JTSize + i]
688 = std::make_tuple(std::get<0>(x), std::get<1>(x), std::get<2>(x), false);
689 }
690
691 // remove the last pair which is present two times (global extrema pair)
692 if(!CTPairs.empty()) {
693 auto cmp =
694 [](
695 const std::tuple<ttk::SimplexId, ttk::SimplexId, scalarType, bool> &a,
696 const std::tuple<ttk::SimplexId, ttk::SimplexId, scalarType, bool> &b) {
697 return std::get<2>(a) < std::get<2>(b);
698 };
699
700 std::sort(CTPairs.begin(), CTPairs.end(), cmp);
701 CTPairs.erase(CTPairs.end() - 1);
702 }
703
704 // get persistence diagrams
705 computeCTPersistenceDiagram<scalarType>(contourTree_, CTPairs, CTDiagram);
706
707 return 0;
708}
709
710template <class triangulationType>
712 const triangulationType *ttkNotUsed(triangulation)) {
713
714 if((BackEnd == BACKEND::PROGRESSIVE_TOPOLOGY
715 || BackEnd == BACKEND::APPROXIMATE_TOPOLOGY)
716 && !std::is_same<ttk::ImplicitWithPreconditions, triangulationType>::value
717 && !std::is_same<ttk::ImplicitNoPreconditions, triangulationType>::value) {
718
719 printWrn("Explicit, Compact or Periodic triangulation detected.");
720 printWrn("Defaulting to the FTM backend.");
721
722 BackEnd = BACKEND::FTM;
723 }
724}
725
726template <class triangulationType>
728 const triangulationType *const triangulation) {
729
730 if(this->BackEnd != BACKEND::DISCRETE_MORSE_SANDWICH) {
731 return;
732 }
733
734 if(!triangulation->isManifold()) {
735 this->printWrn("Non-manifold data-set detected.");
736 this->printWrn("Defaulting to the Persistence Simplex backend.");
737
738 this->BackEnd = BACKEND::PERSISTENT_SIMPLEX;
739 }
740}
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition: BaseClass.h:47
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
TTK processing package for progressive Topological Data Analysis.
int threadNumber_
Definition: BaseClass.h:95
virtual int setThreadNumber(const int threadNumber)
Definition: BaseClass.h:80
Minimalist debugging class.
Definition: Debug.h:88
int debugLevel_
Definition: Debug.h:379
virtual int setDebugLevel(const int &debugLevel)
Definition: Debug.cpp:147
TTK DiscreteMorseSandwich processing package.
void setComputeSadMax(const bool data)
void setComputeMinSad(const bool data)
void preconditionTriangulation(AbstractTriangulation *const data)
void setComputeSadSad(const bool data)
ImplicitTriangulation is a class that provides time and memory efficient traversal methods on triangu...
TTK processing package for the computation of persistence diagrams.
int execute(std::vector< PersistencePair > &CTDiagram, const scalarType *inputScalars, const size_t scalarsMTime, const SimplexId *inputOffsets, const triangulationType *triangulation)
int executePersistentSimplex(std::vector< PersistencePair > &CTDiagram, const SimplexId *inputOffsets, const triangulationType *triangulation)
int executeDiscreteMorseSandwich(std::vector< PersistencePair > &CTDiagram, const scalarType *inputScalars, const size_t scalarsMTime, const SimplexId *inputOffsets, const triangulationType *triangulation)
void setComputeSadSad(const bool data)
void setOutputMonotonyOffsets(void *data)
void sortPersistenceDiagram(std::vector< PersistencePair > &diagram, const SimplexId *const offsets) const
void preconditionTriangulation(AbstractTriangulation *triangulation)
ttk::ProgressiveTopology progT_
void setDeltaApproximate(double data)
void setBackend(const BACKEND be)
ttk::CriticalType getNodeType(ftm::FTMTree_MT *tree, ftm::TreeType treeType, const SimplexId vertexId) const
void checkProgressivityRequirement(const triangulationType *triangulation)
void setOutputScalars(void *data)
int executeFTM(std::vector< PersistencePair > &CTDiagram, const scalarType *inputScalars, const SimplexId *inputOffsets, const triangulationType *triangulation)
dcg::DiscreteGradient dcg_
int executeProgressiveTopology(std::vector< PersistencePair > &CTDiagram, const SimplexId *inputOffsets, const triangulationType *triangulation)
ttk::ApproximateTopology approxT_
DiscreteMorseSandwich dms_
void setComputeMinSad(const bool data)
void setOutputOffsets(void *data)
void augmentPersistenceDiagram(std::vector< PersistencePair > &persistencePairs, const scalarType *const scalars, const triangulationType *triangulation)
Complete a ttk::DiagramType instance with scalar field values (useful for persistence) and 3D coordin...
void setComputeSadMax(const bool data)
int executeApproximateTopology(std::vector< PersistencePair > &CTDiagram, const scalarType *inputScalars, const triangulationType *triangulation)
PersistentSimplexPairs psp_
void checkManifold(const triangulationType *const triangulation)
int computeCTPersistenceDiagram(ftm::FTMTreePP &tree, const std::vector< std::tuple< ttk::SimplexId, ttk::SimplexId, scalarType, bool > > &pairs, std::vector< PersistencePair > &diagram) const
Textbook algorithm to find persistence pairs.
void preconditionTriangulation(AbstractTriangulation *const data)
Preprocess all the required connectivity requests on the triangulation.
TTK processing package for progressive Topological Data Analysis.
TTK discreteGradient processing package.
int setThreadNumber(const int n) override
Definition: FTMTree_CT.h:86
FTMTree_MT * getSplitTree()
Definition: FTMTree_CT.h:51
int setDebugLevel(const int &d) override
Definition: FTMTree_CT.h:79
FTMTree_MT * getJoinTree()
Definition: FTMTree_CT.h:47
void preconditionTriangulation(AbstractTriangulation *tri, const bool preproc=true)
Definition: FTMTree_CT.h:72
The Topology ToolKit.
CriticalType
default value for critical index
Definition: DataTypes.h:80
int SimplexId
Identifier type for simplices of any dimension.
Definition: DataTypes.h:22
printMsg(debug::output::GREEN+"                           "+debug::output::ENDCOLOR+debug::output::GREEN+"▒"+debug::output::ENDCOLOR+debug::output::GREEN+"▒▒▒▒▒▒▒▒▒▒▒▒▒░"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)