5using namespace torch::indexing;
7ttk::TopologicalDimensionReduction::TopologicalDimensionReduction(
11 int numberOfComponents,
17 const std::string &architecture,
18 const std::string &activation,
20 bool batchNormalization,
21 double regCoefficient,
24 int preOptimizeEpochs)
25 : NumberOfComponents(numberOfComponents), Epochs(epochs),
26 LearningRate(learningRate), Optimizer(optimizer), Method(method),
27 ModelType(modelType), InputIsImages(inputIsImages),
28 Architecture(architecture), Activation(activation), BatchSize(batchSize),
29 BatchNormalization(batchNormalization), RegCoefficient(regCoefficient),
30 PreOptimize(preOptimize), PreOptimizeEpochs(preOptimizeEpochs) {
32 this->setDebugMsgPrefix(
"TopologicalDimensionReduction");
34 if(torch::cuda::is_available() && useCUDA && !deterministic)
35 device = torch::kCUDA;
38 at::manual_seed(seed);
39 at::globalContext().setDeterministicFillUninitializedMemory(
true);
40 at::globalContext().setDeterministicAlgorithms(
true,
true);
42 at::globalContext().setDeterministicFillUninitializedMemory(
false);
43 at::globalContext().setDeterministicAlgorithms(
false,
false);
47int ttk::TopologicalDimensionReduction::initializeModel(
int inputSize,
49 if((!InputIsImages && !AutoEncoder::isStringValid(Architecture))
51 && !ConvolutionalAutoEncoder::isStringValid(Architecture))) {
52 printErr(
"Invalid string for layers description.");
57 model = std::make_unique<AutoEncoder>(inputDimension, NumberOfComponents,
58 Architecture, Activation,
61 model = std::make_unique<ConvolutionalAutoEncoder>(
62 sqrt(inputDimension), NumberOfComponents, Architecture,
65 model = std::make_unique<AutoDecoder>(inputDimension, inputSize,
66 NumberOfComponents, Architecture,
69 model = std::make_unique<DirectOptimization>(inputSize, NumberOfComponents);
74void ttk::TopologicalDimensionReduction::initializeOptimizer() {
75 if(Optimizer == OPTIMIZER::ADAM)
76 torchOptimizer = std::make_unique<torch::optim::Adam>(
77 model->parameters(), LearningRate);
78 else if(Optimizer == OPTIMIZER::SGD)
79 torchOptimizer = std::make_unique<torch::optim::SGD>(
80 model->parameters(), LearningRate);
81 else if(Optimizer == OPTIMIZER::LBFGS)
82 torchOptimizer = std::make_unique<torch::optim::LBFGS>(
83 model->parameters(), LearningRate);
86int ttk::TopologicalDimensionReduction::execute(
87 std::vector<std::vector<double>> &outputEmbedding,
88 const std::vector<double> &inputMatrix,
91 printMsg(
"Initialization", 0., tm.getElapsedTime());
93 const int inputSize = n;
94 const int inputRawDimension = inputMatrix.size() / n;
95 const int inputDimension
96 = inputRawDimension - PreOptimize * NumberOfComponents;
98 this->
printMsg(
"input dimension: " + std::to_string(inputDimension), 0.0,
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());
106 this->
printMsg(
"output dimension: " + std::to_string(NumberOfComponents), 0.0,
107 tm.getElapsedTime());
109 "#elements: " + std::to_string(inputSize), 0.0, tm.getElapsedTime());
111#ifndef TTK_ENABLE_CGAL
112 printWrn(
"TTK not compiled with CGAL enabled: this backend could be slow.");
115 if(initializeModel(inputSize, inputDimension))
117 initializeOptimizer();
119 const torch::Tensor rawInput
120 = torch::from_blob(
const_cast<double *
>(inputMatrix.data()),
121 {inputSize, inputRawDimension}, torch::kFloat64)
124 const torch::Tensor input
125 = rawInput.index({Slice(), Slice(None, inputDimension)});
127 rpd::PointCloud points(inputSize, std::vector<double>(inputDimension));
128 for(
int i = 0; i < inputSize; ++i) {
129 for(
int j = 0; j < inputDimension; ++j)
130 points[i][j] = inputMatrix[inputRawDimension * i + j];
134 preOptimize(input, rawInput.index({Slice(), Slice(inputDimension, None)}));
135 initializeOptimizer();
138 if(Method == REGUL::NO_REGUL) {
139 printMsg(
"Starting optimization", 0., tm.getElapsedTime());
140 optimizeSimple(input);
142 printMsg(
"Computing input persistence", 0., tm.getElapsedTime());
143 topologicalLossContainer
144 = std::make_unique<TopologicalLoss>(input, points, Method);
145 printMsg(
"Starting optimization", 0., tm.getElapsedTime());
149 const torch::Tensor latent = model->encode(input).cpu();
150 for(
int i = 0; i < inputSize; ++i) {
151 for(
int j = 0; j < NumberOfComponents; ++j)
152 outputEmbedding[j][i] = latent[i][j].item<double>();
155 printMsg(
"Complete", 1., tm.getElapsedTime());
159void ttk::TopologicalDimensionReduction::optimizeSimple(
160 const torch::Tensor &input)
const {
164 TensorIndex indices = Slice();
167 = torch::randint(input.size(0), {BatchSize}, torch::kInt).to(device);
170 torchOptimizer->zero_grad();
171 const torch::Tensor prediction = model->forward(input.index(indices));
174 const torch::Tensor loss
175 = torch::mse_loss(prediction, input.index(indices));
179 printLoss(epoch, Epochs, loss.item<
double>());
184 for(; epoch < Epochs; ++epoch)
185 torchOptimizer->step(closure);
188void ttk::TopologicalDimensionReduction::optimize(
189 const torch::Tensor &input)
const {
194 torchOptimizer->zero_grad();
195 const torch::Tensor latent = model->encode(input);
196 const torch::Tensor prediction = model->decode(latent);
199 const torch::Tensor topologicalLoss
200 = RegCoefficient * topologicalLossContainer->computeLoss(latent);
201 const torch::Tensor reconstructionLoss = torch::mse_loss(prediction, input);
202 const torch::Tensor loss = reconstructionLoss + topologicalLoss;
206 printLoss(epoch, Epochs, loss.item<
double>());
211 for(; epoch < Epochs; ++epoch)
212 torchOptimizer->step(closure);
215void ttk::TopologicalDimensionReduction::preOptimize(
216 const torch::Tensor &input,
const torch::Tensor &target)
const {
221 torchOptimizer->zero_grad();
222 const torch::Tensor latent = model->encode(input);
223 const torch::Tensor prediction = model->decode(latent);
226 const torch::Tensor loss
227 = torch::mse_loss(latent, target) + torch::mse_loss(prediction, input);
231 printLoss(epoch, PreOptimizeEpochs, loss.item<
double>());
236 for(; epoch < PreOptimizeEpochs; ++epoch)
237 torchOptimizer->step(closure);
240void ttk::TopologicalDimensionReduction::printLoss(
int epoch,
243 if(epoch % std::max(1, maxEpoch / 10) == 0)
245 "Loss at epoch " + std::to_string(epoch) +
": " + std::to_string(loss),
247 else if(epoch == maxEpoch - 1)
248 printMsg(
"Final loss value: " + std::to_string(loss), 1.);
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
std::vector< std::vector< value_t > > PointCloud
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)