Skip to content

Commit 9d97e76

Browse files
committed
Merge branch 'dev' of https://github.com/topology-tool-kit/ttk into mtnn_refactor
2 parents 4edaef6 + 7b36150 commit 9d97e76

File tree

13 files changed

+236
-93
lines changed

13 files changed

+236
-93
lines changed

core/base/dimensionReduction/DimensionReduction.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,14 @@ int DimensionReduction::execute(
8181
ae_CUDA, ae_Deterministic, ae_Seed, NumberOfComponents, ae_Epochs,
8282
ae_LearningRate, ae_Optimizer, ae_Method, ae_Model, ae_Architecture,
8383
ae_Activation, ae_BatchSize, ae_BatchNormalization, ae_RegCoefficient,
84-
IsInputImages);
84+
IsInputImages, ae_PreOptimize, ae_PreOptimizeEpochs);
8585
tcdr.setDebugLevel(debugLevel_);
8686
tcdr.setThreadNumber(threadNumber_);
8787

8888
outputEmbedding.resize(NumberOfComponents);
8989
for(int d = 0; d < NumberOfComponents; d++)
9090
outputEmbedding[d].resize(nRows);
9191

92-
if(ae_PreOptimize) {
93-
DimensionReduction initDR;
94-
initDR.setDebugLevel(debugLevel_);
95-
initDR.setInputMethod(ae_PreOptimizeMethod);
96-
std::vector<std::vector<double>> latentInitialization;
97-
initDR.execute(latentInitialization, inputMatrix, nRows, nColumns);
98-
tcdr.setLatentInitialization(latentInitialization);
99-
}
100-
10192
tcdr.execute(outputEmbedding, inputMatrix, nRows);
10293

10394
this->printMsg("Computed AE dimension reduction", 1.0, t.getElapsedTime(),

core/base/dimensionReduction/DimensionReduction.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@
6666
///
6767
/// "Topological Autoencoders++: Fast and Accurate Cycle-Aware Dimensionality
6868
/// Reduction" \n
69-
/// Mattéo Clémot, Julie Digne, Julien Tierny, \n
70-
/// arXiv preprint, 2025.
69+
/// Mattéo Clémot, Julie Digne, Julien Tierny, \n
70+
/// IEEE Transactions on Visualization and Computer Graphics.
71+
/// Accepted, to be presented at IEEE VIS 2026.
7172

7273
#pragma once
7374

@@ -365,7 +366,7 @@ namespace ttk {
365366
bool ae_BatchNormalization{true};
366367
double ae_RegCoefficient{1e-2};
367368
bool ae_PreOptimize{false};
368-
METHOD ae_PreOptimizeMethod{METHOD::PCA};
369+
int ae_PreOptimizeEpochs{1000};
369370

370371
// testing
371372
std::string ModulePath{"default"};

core/base/ripsPersistenceDiagram/FastRipsPersistenceDiagram2.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
/// \b Related \b publication \n
1515
/// "Topological Autoencoders++: Fast and Accurate Cycle-Aware Dimensionality
1616
/// Reduction" \n
17-
/// Mattéo Clémot, Julie Digne, Julien Tierny, \n
18-
/// arXiv preprint, 2025.
17+
/// Mattéo Clémot, Julie Digne, Julien Tierny, \n
18+
/// IEEE Transactions on Visualization and Computer Graphics.
19+
/// Accepted, to be presented at IEEE VIS 2026.
1920

2021
#pragma once
2122

@@ -116,4 +117,4 @@ namespace ttk::rpd {
116117

117118
} // namespace ttk::rpd
118119

119-
#endif
120+
#endif

core/base/ripsPersistenceDiagram/PairCellsWithOracle.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,27 @@ ttk::rpd::PairCellsWithOracle::PairCellsWithOracle(
2929
}
3030
}
3131

32+
ttk::rpd::PairCellsWithOracle::PairCellsWithOracle(
33+
float *data,
34+
int n,
35+
int dim,
36+
MultidimensionalDiagram const &oracle,
37+
bool parallelSort)
38+
: n_(n), parallelSort_(parallelSort), oracle_(oracle) {
39+
// inherited from Debug: prefix will be printed at the beginning of every msg
40+
this->setDebugMsgPrefix("PairCellsWithOracle");
41+
42+
for(int i = 1; i < n_; ++i) {
43+
for(int j = 0; j < i; ++j) {
44+
double s = 0.;
45+
for(int d = 0; d < dim; ++d)
46+
s += (data[dim * i + d] - data[dim * j + d])
47+
* (data[dim * i + d] - data[dim * j + d]);
48+
compressedDM_.push_back(sqrt(s));
49+
}
50+
}
51+
}
52+
3253
void ttk::rpd::PairCellsWithOracle::callOracle(const PointCloud &points,
3354
MultidimensionalDiagram &oracle,
3455
double threshold,

core/base/ripsPersistenceDiagram/PairCellsWithOracle.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ namespace ttk::rpd {
2626
MultidimensionalDiagram const &oracle,
2727
bool distanceMatrix = false,
2828
bool parallelSort = false);
29+
PairCellsWithOracle(float *data,
30+
int n,
31+
int dim,
32+
MultidimensionalDiagram const &oracle,
33+
bool parallelSort = false);
2934

3035
void run();
3136

core/base/topologicalDimensionReduction/DimensionReductionModel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "DimensionReductionModel.h"
1+
#include <DimensionReductionModel.h>
22
#include <regex>
33

44
#ifdef TTK_ENABLE_TORCH

core/base/topologicalDimensionReduction/TopologicalDimensionReduction.cpp

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ ttk::TopologicalDimensionReduction::TopologicalDimensionReduction(
1919
int batchSize,
2020
bool batchNormalization,
2121
double regCoefficient,
22-
bool inputIsImages)
22+
bool inputIsImages,
23+
bool preOptimize,
24+
int preOptimizeEpochs)
2325
: NumberOfComponents(numberOfComponents), Epochs(epochs),
2426
LearningRate(learningRate), Optimizer(optimizer), Method(method),
2527
ModelType(modelType), InputIsImages(inputIsImages),
2628
Architecture(architecture), Activation(activation), BatchSize(batchSize),
27-
BatchNormalization(batchNormalization), RegCoefficient(regCoefficient) {
29+
BatchNormalization(batchNormalization), RegCoefficient(regCoefficient),
30+
PreOptimize(preOptimize), PreOptimizeEpochs(preOptimizeEpochs) {
2831
// inherited from Debug: prefix will be printed at the beginning of every msg
2932
this->setDebugMsgPrefix("TopologicalDimensionReduction");
3033

@@ -88,15 +91,18 @@ int ttk::TopologicalDimensionReduction::execute(
8891
printMsg("Initialization", 0., tm.getElapsedTime());
8992

9093
const int inputSize = n;
91-
const int inputDimension = inputMatrix.size() / n;
94+
const int inputRawDimension = inputMatrix.size() / n;
95+
const int inputDimension
96+
= inputRawDimension - PreOptimize * NumberOfComponents;
9297
if(!InputIsImages)
9398
this->printMsg("input dimension: " + std::to_string(inputDimension), 0.0,
9499
tm.getElapsedTime());
95100
else
96-
this->printMsg("input dimension: " + std::to_string(inputDimension) + " = "
97-
+ std::to_string((int)sqrt(inputDimension)) + " x "
98-
+ std::to_string((int)sqrt(inputDimension)) + " images",
99-
.0, tm.getElapsedTime());
101+
this->printMsg(
102+
"input dimension: " + std::to_string(inputDimension) + " = "
103+
+ std::to_string(static_cast<int>(sqrt(inputDimension))) + " x "
104+
+ std::to_string(static_cast<int>(sqrt(inputDimension))) + " images",
105+
.0, tm.getElapsedTime());
100106
this->printMsg("output dimension: " + std::to_string(NumberOfComponents), 0.0,
101107
tm.getElapsedTime());
102108
this->printMsg(
@@ -110,20 +116,22 @@ int ttk::TopologicalDimensionReduction::execute(
110116
return 1;
111117
initializeOptimizer();
112118

113-
const torch::Tensor input
119+
const torch::Tensor rawInput
114120
= torch::from_blob(const_cast<double *>(inputMatrix.data()),
115-
{inputSize, inputDimension}, torch::kFloat64)
121+
{inputSize, inputRawDimension}, torch::kFloat64)
116122
.to(torch::kFloat32)
117123
.to(device);
124+
const torch::Tensor input
125+
= rawInput.index({Slice(), Slice(None, inputDimension)});
118126

119127
rpd::PointCloud points(inputSize, std::vector<double>(inputDimension));
120128
for(int i = 0; i < inputSize; ++i) {
121129
for(int j = 0; j < inputDimension; ++j)
122-
points[i][j] = inputMatrix[inputDimension * i + j];
130+
points[i][j] = inputMatrix[inputRawDimension * i + j];
123131
}
124132

125-
if(latentInitialization_.numel()) {
126-
preOptimize(input, latentInitialization_);
133+
if(PreOptimize) {
134+
preOptimize(input, rawInput.index({Slice(), Slice(inputDimension, None)}));
127135
initializeOptimizer();
128136
}
129137

@@ -148,18 +156,6 @@ int ttk::TopologicalDimensionReduction::execute(
148156
return 0;
149157
}
150158

151-
void ttk::TopologicalDimensionReduction::setLatentInitialization(
152-
std::vector<std::vector<double>> const &latentInitialization) {
153-
std::vector<torch::Tensor> tensors;
154-
for(auto const &column : latentInitialization)
155-
tensors.push_back(torch::from_blob(const_cast<double *>(column.data()),
156-
{static_cast<int>(column.size())},
157-
torch::kFloat64)
158-
.to(torch::kFloat32)
159-
.to(device));
160-
latentInitialization_ = torch::stack(tensors).transpose(0, 1);
161-
}
162-
163159
void ttk::TopologicalDimensionReduction::optimizeSimple(
164160
const torch::Tensor &input) const {
165161
int epoch = 0;
@@ -180,7 +176,7 @@ void ttk::TopologicalDimensionReduction::optimizeSimple(
180176
loss.backward();
181177

182178
// IO
183-
printLoss(epoch, loss.item<double>());
179+
printLoss(epoch, Epochs, loss.item<double>());
184180

185181
return loss;
186182
};
@@ -207,7 +203,7 @@ void ttk::TopologicalDimensionReduction::optimize(
207203
loss.backward();
208204

209205
// IO
210-
printLoss(epoch, loss.item<double>());
206+
printLoss(epoch, Epochs, loss.item<double>());
211207

212208
return loss;
213209
};
@@ -232,21 +228,24 @@ void ttk::TopologicalDimensionReduction::preOptimize(
232228
loss.backward();
233229

234230
// IO
235-
printLoss(epoch, loss.item<double>());
231+
printLoss(epoch, PreOptimizeEpochs, loss.item<double>());
236232

237233
return loss;
238234
};
239235

240-
for(; epoch < Epochs; ++epoch)
236+
for(; epoch < PreOptimizeEpochs; ++epoch)
241237
torchOptimizer->step(closure);
242238
}
243239

244240
void ttk::TopologicalDimensionReduction::printLoss(int epoch,
241+
int maxEpoch,
245242
double loss) const {
246-
if(epoch % std::max(1, Epochs / 10) == 0)
243+
if(epoch % std::max(1, maxEpoch / 10) == 0)
247244
printMsg(
248-
"Loss at epoch " + std::to_string(epoch) + " : " + std::to_string(loss),
249-
double(epoch) / Epochs, -1, -1, debug::LineMode::REPLACE);
245+
"Loss at epoch " + std::to_string(epoch) + ": " + std::to_string(loss),
246+
static_cast<double>(epoch) / maxEpoch, -1, -1, debug::LineMode::REPLACE);
247+
else if(epoch == maxEpoch - 1)
248+
printMsg("Final loss value: " + std::to_string(loss), 1.);
250249
}
251250

252251
#endif

core/base/topologicalDimensionReduction/TopologicalDimensionReduction.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
/// "Topological Autoencoders++: Fast and Accurate Cycle-Aware Dimensionality
3333
/// Reduction" \n
3434
/// Mattéo Clémot, Julie Digne, Julien Tierny, \n
35-
/// arXiv preprint, 2025.
35+
/// IEEE Transactions on Visualization and Computer Graphics.
36+
/// Accepted, to be presented at IEEE VIS 2026.
3637
///
3738
/// \sa DimensionReduction.cpp %for a usage example.
3839

@@ -90,7 +91,9 @@ namespace ttk {
9091
int batchSize,
9192
bool batchNormalization,
9293
double regCoefficient,
93-
bool inputIsImages);
94+
bool inputIsImages,
95+
bool preOptimize,
96+
int preOptimizeEpochs);
9497

9598
/**
9699
* @brief Computes the projection with an AutoEncoder
@@ -107,9 +110,6 @@ namespace ttk {
107110
const std::vector<double> &inputMatrix,
108111
size_t n);
109112

110-
void setLatentInitialization(
111-
std::vector<std::vector<double>> const &latentInitialization);
112-
113113
protected:
114114
const int NumberOfComponents;
115115
const int Epochs;
@@ -123,13 +123,14 @@ namespace ttk {
123123
const int BatchSize;
124124
const bool BatchNormalization;
125125
const double RegCoefficient;
126+
const bool PreOptimize;
127+
const int PreOptimizeEpochs;
126128

127129
private:
128130
torch::DeviceType device{torch::kCPU};
129131
std::unique_ptr<DimensionReductionModel> model{nullptr};
130132
std::unique_ptr<torch::optim::Optimizer> torchOptimizer{nullptr};
131133
std::unique_ptr<TopologicalLoss> topologicalLossContainer{nullptr};
132-
torch::Tensor latentInitialization_{};
133134

134135
int initializeModel(int inputSize, int inputDimension);
135136
void initializeOptimizer();
@@ -140,7 +141,7 @@ namespace ttk {
140141
void optimize(const torch::Tensor &input) const;
141142
void optimizeSimple(const torch::Tensor &input) const;
142143

143-
inline void printLoss(int epoch, double loss) const;
144+
inline void printLoss(int epoch, int maxEpoch, double loss) const;
144145

145146
#endif
146147

core/base/topologicalDimensionReduction/TopologicalLoss.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ torch::Tensor ttk::TopologicalLoss::computeLoss(const torch::Tensor &latent) {
3232
}
3333

3434
void ttk::TopologicalLoss::precomputeInputPersistence() {
35-
if(regul_ == REGUL::TOPOAE) {
35+
if(regul_ == REGUL::TOPOAE || regul_ == REGUL::W_DIM1) {
3636
rpd::EdgeSets3 inputCritical;
3737
ripser::ripser(points_, inputCritical, rpd::inf, 0, false);
3838
inputCriticalPairIndices = {pairsToTorch(inputCritical[0])};
39-
} else if(regul_ == REGUL::TOPOAE_DIM1) {
39+
}
40+
if(regul_ == REGUL::TOPOAE_DIM1) {
4041
rpd::EdgeSets3 inputCritical;
4142
ripser::ripser(points_, inputCritical, rpd::inf, 1, false);
4243
for(int i = 0; i <= 2; ++i)
@@ -45,12 +46,6 @@ void ttk::TopologicalLoss::precomputeInputPersistence() {
4546
ripser::ripser(points_, inputPD, rpd::inf, 1, false);
4647
auction = std::make_unique<
4748
PersistenceDiagramWarmRestartAuction<rpd::PersistencePair>>(inputPD[1]);
48-
#ifdef TTK_W1REG_WITH_TOPOAE0
49-
// we add topoAE0 loss
50-
rpd::EdgeSets3 inputCritical;
51-
ripser::ripser(points_, inputCritical, rpd::inf, 0, false);
52-
inputCriticalPairIndices[0] = pairsToTorch(inputCritical[0]);
53-
#endif
5449
} else if(regul_ == REGUL::CASCADE || regul_ == REGUL::ASYMMETRIC_CASCADE) {
5550
// first compute the PD with Ripser
5651
rpd::PairCellsWithOracle::callOracle(points_, inputPD);
@@ -116,14 +111,21 @@ void ttk::TopologicalLoss::computeLatentCascades(
116111
latent_.cpu().data_ptr<float>(), latent_.size(0))
117112
.computeRips0And1Persistence(latentCriticalAndCascades, false, false);
118113
else {
119-
rpd::PairCells pc(
120-
latent_.cpu().data_ptr<float>(), latent_.size(0), latent_.size(1));
114+
rpd::MultidimensionalDiagram latentPD;
115+
ripser::ripser(latent_.cpu().data_ptr<float>(), latent_.size(0),
116+
latent_.size(1), latentPD, rpd::inf, 1, false);
117+
rpd::PairCellsWithOracle pc(latent_.cpu().data_ptr<float>(),
118+
latent_.size(0), latent_.size(1), latentPD,
119+
false);
121120
pc.run();
122121
pc.getCascades(latentCriticalAndCascades);
123122
}
124123
#else
125-
rpd::PairCells pc(
126-
latent_.cpu().data_ptr<float>(), latent_.size(0), latent_.size(1));
124+
rpd::MultidimensionalDiagram latentPD;
125+
ripser::ripser(latent_.cpu().data_ptr<float>(), latent_.size(0),
126+
latent_.size(1), latentPD, rpd::inf, 1, false);
127+
rpd::PairCellsWithOracle pc(latent_.cpu().data_ptr<float>(), latent_.size(0),
128+
latent_.size(1), latentPD, false);
127129
pc.run();
128130
pc.getCascades(latentCriticalAndCascades);
129131
#endif

core/base/topologicalDimensionReduction/TopologicalLoss.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
#pragma once
2222

23-
#include <PairCells.h>
2423
#include <PairCellsWithOracle.h>
2524
#include <PersistenceDiagramWarmRestartAuction.h>
2625
#include <RipsPersistenceDiagram.h>

0 commit comments

Comments
 (0)