Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
0102320
Add magic_enum dependency
daljit46 Jan 14, 2026
73e1189
First draft of affine registration on GPU
daljit46 Jan 14, 2026
022149f
Symlink shader code + hack to find registration shaders
daljit46 Jan 14, 2026
9384096
Use exec path for finding shaders
daljit46 Jan 14, 2026
0f76ded
Export boolean link-time constants
daljit46 Jan 14, 2026
8866f5f
Update registration uniforms for new GPU API
daljit46 Jan 19, 2026
cff396b
Add write_object_to_buffer helper
daljit46 Jan 19, 2026
983f1f1
Add test for write_object_to_buffer
daljit46 Jan 19, 2026
b412e03
Enforce 4-byte size for GPU object uploads
daljit46 Jan 19, 2026
3bb7533
Rename Metric class -> GlobalMetric
daljit46 Jan 29, 2026
0a6b4e2
Rename RegistrationConfig -> GlobalRegistrationConfig
daljit46 Jan 29, 2026
cfb2dca
Add missing include header in registrationtypes.h
daljit46 Jan 29, 2026
a51ba42
Add new shaders for log-demons nonlinear registration
daljit46 Jan 29, 2026
7a62d9c
Refactor VolumeSamplingField and add VectorVolumeSamplingField
daljit46 Jan 29, 2026
4b9bb15
Start refactoring for addition of non-linear registration
daljit46 Jan 30, 2026
813e5ab
Add shader for composing a displacement field
daljit46 Feb 24, 2026
1e624c0
Update comment with definition of Lie Bracket
daljit46 Feb 24, 2026
cdf3920
First sketch of log-demons nonlinear registration
daljit46 Feb 24, 2026
e6d5795
Add new function to download textures as an MR::Image
daljit46 Feb 25, 2026
2ecdb02
Produce warp for non linear registration
daljit46 Feb 25, 2026
60ba4f4
Add new function for Gaussian blur of 3D textures
daljit46 Feb 25, 2026
7acc56c
Apply gaussian blur smoothing to velocity update field
daljit46 Feb 25, 2026
eaddc3a
Apply gaussian smoothing to velocity field itself
daljit46 Feb 25, 2026
471053d
Add missing includes in imageoperations.cpp
daljit46 Feb 25, 2026
5b80047
Update blur shader for gaussian smoothing MERGE PREV
daljit46 Feb 25, 2026
aa0a2fd
Tweak CLI options for non linear case
daljit46 Feb 26, 2026
5893c29
Add ssd cost evaluation + logging
daljit46 Feb 26, 2026
caf1d20
Add missing includes
daljit46 Feb 26, 2026
4f5568f
Update comment
daljit46 Feb 26, 2026
802bed0
Add comment for instructions for MacOS profiling
daljit46 Feb 26, 2026
2d17f32
Operate in scanner space coordinate math
daljit46 Feb 26, 2026
08bcca4
Update velocity upsampling shader to per-axis coordinate scaling
daljit46 Feb 26, 2026
7582d0b
Pack MR::Image into contiguous buffer before texture upload
daljit46 Feb 28, 2026
e92e0c3
Add multi-resolution support
daljit46 Feb 28, 2026
8469824
Add convergence check for non-linear registration
daljit46 Feb 28, 2026
f572514
Include out of bounds voxels in ssd computation
daljit46 Feb 28, 2026
59d8eb7
Add ITK implementation paper in comment
daljit46 Feb 28, 2026
767434a
Dynamic number of steps for exponentiation
daljit46 Feb 28, 2026
3fb664b
Tweak parameters for better convergence
daljit46 Feb 28, 2026
0ac4113
Add original log demons paper to comment
daljit46 Mar 1, 2026
f6cbab0
First draft of symmetric version of demons
daljit46 Mar 1, 2026
d1632a9
Small refactoring to simplify the code
daljit46 Mar 1, 2026
4edf2dc
Add missing shader for computing velocity norm to history
daljit46 Mar 1, 2026
4625f6a
First draft on LNCC for nonlinear registration
daljit46 Mar 2, 2026
540cc91
Add support for read-write for RGBAF32 textures in shaders
daljit46 Mar 2, 2026
e7e2941
Add inertial demons
daljit46 Mar 2, 2026
25398a7
Enable ncc support
daljit46 Mar 2, 2026
87db353
Add function for GPU texture->texture copy
daljit46 Mar 3, 2026
580bc00
Add functions for bounds checking in texture utils
daljit46 Mar 3, 2026
64c9436
Fix symmetry in the registration procedure
daljit46 Mar 4, 2026
76e4c78
Make nl_warp option consistent with mrregister
daljit46 Mar 3, 2026
f9c8ceb
Set default ncc radius to 2
daljit46 Mar 3, 2026
bd2fc8b
Minor tweaks to logging
daljit46 Mar 3, 2026
0521a0b
Log GPU adapter information
daljit46 Mar 4, 2026
d8d1860
Log window radius for lncc
daljit46 Mar 4, 2026
a2786a8
Decrease inertia weight for demons
daljit46 Mar 4, 2026
f7547ed
Tweak Gaussian smoothing + automatic cutoff radius based on sigma
daljit46 Mar 8, 2026
2701e89
Start refactor LNCC update nonlinear shader
daljit46 Mar 8, 2026
3c4b356
Use z-streaming for lncc update and cost
daljit46 Mar 9, 2026
0c1af6a
Disable smoothing when sigmas are zero
daljit46 Mar 10, 2026
0b50d70
Tweak registration constants
daljit46 Mar 10, 2026
cc8bb60
Disable inertia at finest levels
daljit46 Mar 11, 2026
330f7b0
Set NCC as default metric type
daljit46 Mar 11, 2026
c7f1d21
Move non linear SSD pipeline in separate file
daljit46 Mar 11, 2026
37aed27
Add new function to cleanup unused resources by GPU
daljit46 Mar 11, 2026
ef92a1e
Add new function for GPU to block until all queue ops finish
daljit46 Mar 11, 2026
598e061
Free GPU resources at start of each level
daljit46 Mar 11, 2026
80b1d13
Remove negated velocity texture
daljit46 Mar 12, 2026
56fdc39
Remove forward displacement cache via backward first loop
daljit46 Mar 12, 2026
637a511
Alias smoothed update field to backward update buffer
daljit46 Mar 12, 2026
74ccb7d
Implement chunked Z-streaming for LNCC
daljit46 Mar 13, 2026
b7d88fb
Use single workgroup reduction using custom op
daljit46 Mar 13, 2026
29877cc
Use 2 inner axes for RGB extract copy in GPU texture download
daljit46 Mar 16, 2026
48b1163
Add support for masks in non linear registration
daljit46 Mar 18, 2026
f6ec914
Remove inertia code path
daljit46 Mar 18, 2026
1d5b514
Add/update copyright headers for Slang shaders
daljit46 Mar 18, 2026
d52e5e3
Apply clang-format + update copyright cpp files
daljit46 Mar 18, 2026
d10f3cc
Add initial affine transform for nonlinear registration
daljit46 Mar 19, 2026
74c01dc
Expose Gaussian regularisation to CLI
daljit46 Mar 20, 2026
8fd0e24
Don't converge with worsening objective
daljit46 Mar 20, 2026
767ec8f
Fix row/column multiplication bug
daljit46 Mar 20, 2026
1c8570b
Don't allow flat local windows to contribute for lncc
daljit46 Mar 20, 2026
94780a3
Remove unused struct in texture_utils.slang
daljit46 Mar 30, 2026
e226a72
Replace closed-form LNCC update with gradient-based force
daljit46 Mar 30, 2026
d316dea
Remove unused file
daljit46 Mar 30, 2026
0c9baf5
Update synopsis and references
daljit46 Mar 30, 2026
4818130
Remove midway outputs
daljit46 Mar 30, 2026
4e3c6ec
Rename mrreggpu -> mrgpureg
daljit46 Mar 31, 2026
982ce63
Implement moment based initialisation
daljit46 Mar 31, 2026
bb67efb
Add some comments in reduction_utils.slang
daljit46 Mar 31, 2026
7722bc6
Remove memory reduction request
daljit46 Mar 31, 2026
075b263
Merge remote-tracking branch 'origin/dev' into non_linear_reg
daljit46 Mar 31, 2026
185e62f
Make rotation search default init strategy
daljit46 Mar 31, 2026
95f2d6f
Fix angle search initialisation
daljit46 Mar 31, 2026
9dfbb1e
Allow to rigid/affine model
daljit46 Mar 31, 2026
73dfab4
Fix bug that was causing NaNs in CoM computation
daljit46 Mar 31, 2026
4c05ec7
Add multistage support
daljit46 Mar 31, 2026
36f746a
Minor CLI tweaks
daljit46 Mar 31, 2026
8c78512
Remove duplicate lines
daljit46 Mar 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
626 changes: 626 additions & 0 deletions cpp/cmd/mrgpureg.cpp

Large diffs are not rendered by default.

31 changes: 30 additions & 1 deletion cpp/core/gpu/gpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,14 +870,43 @@ Image<float> ComputeContext::download_texture_as_image(const Texture &texture,
if (texture_channels == 4U && alpha_mode == DownloadTextureAlphaMode::IgnoreAlpha) {
const std::vector<uint32_t> rgb_channels = {0U, 1U, 2U};
Adapter::Extract1D source_rgb(source_image, texture_dims, rgb_channels);
threaded_copy(source_rgb, image);
// Use 2 inner axes so tiny channel loops don't dominate with threading overhead.
threaded_copy(source_rgb, image, 0, source_rgb.ndim(), 2);
} else {
threaded_copy(source_image, image);
}

return image;
}

void ComputeContext::wait_for_all_queue_operations() const {
bool callback_invoked = false;
wgpu::QueueWorkDoneStatus queue_status = wgpu::QueueWorkDoneStatus::Error;
std::string queue_message;
const wgpu::Future queue_done_future = m_device.GetQueue().OnSubmittedWorkDone(
wgpu::CallbackMode::WaitAnyOnly,
[&callback_invoked, &queue_status, &queue_message](wgpu::QueueWorkDoneStatus status, const char *message) {
callback_invoked = true;
queue_status = status;
if (message != nullptr) {
queue_message = message;
}
});
const wgpu::WaitStatus wait_status = m_instance.WaitAny(queue_done_future, std::numeric_limits<uint64_t>::max());
if (wait_status != wgpu::WaitStatus::Success) {
throw MR::Exception("Failed to wait for submitted GPU queue work: wgpu::Instance::WaitAny failed");
}
if (!callback_invoked) {
throw MR::Exception("Failed to wait for submitted GPU queue work: callback was not invoked");
}
if (queue_status != wgpu::QueueWorkDoneStatus::Success) {
const std::string suffix = queue_message.empty() ? std::string() : (": " + queue_message);
throw MR::Exception("Failed while waiting for submitted GPU queue work" + suffix);
}
}

void ComputeContext::reduce_memory_usage() const { dawn::native::ReduceMemoryUsage(m_device.Get()); }

Kernel ComputeContext::new_kernel(const KernelSpec &kernel_spec) const {
struct BindingEntries {
std::vector<wgpu::BindGroupEntry> bind_group_entries;
Expand Down
17 changes: 17 additions & 0 deletions cpp/core/gpu/gpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ struct ComputeContext {
new_buffer_from_host_object(const Object &object, BufferType buffer_type = BufferType::StorageBuffer) const {
static_assert(std::is_trivially_copyable_v<Object>, "Object must be trivially copyable");
static_assert(std::is_standard_layout_v<Object>, "Object must be standard layout");
static_assert(sizeof(Object) % 4 == 0, "Object size must be a multiple of 4 bytes");
return {buffer_type, inner_new_buffer_from_host_memory(&object, sizeof(object), buffer_type)};
}

Expand All @@ -241,6 +242,16 @@ struct ComputeContext {
return Buffer<T>{bufferType, std::move(buffer)};
}

// Writes a POD-like object into a byte buffer (e.g. uniform buffers).
template <typename Object>
void write_object_to_buffer(const Buffer<std::byte> &buffer, const Object &object, uint64_t offset_bytes = 0) const {
static_assert(std::is_trivially_copyable_v<Object>, "Object must be trivially copyable");
static_assert(std::is_standard_layout_v<Object>, "Object must be standard layout");
static_assert(sizeof(Object) % 4 == 0, "Object size must be a multiple of 4 bytes");
const auto bytes = tcb::as_bytes(tcb::span<const Object>(&object, 1));
write_to_buffer<std::byte>(buffer, bytes, offset_bytes);
}

// This function blocks until the download is complete.
template <typename T = float> [[nodiscard]] std::vector<T> download_buffer_as_vector(const Buffer<T> &buffer) const {
std::vector<T> result(buffer.wgpu_handle.GetSize() / sizeof(T));
Expand Down Expand Up @@ -329,6 +340,12 @@ struct ComputeContext {

[[nodiscard]] Sampler new_linear_sampler() const;

// Block until all queued GPU work has completed.
void wait_for_all_queue_operations() const;

// Ask Dawn to free unused GPU memory (e.g. staging/cached resources).
void reduce_memory_usage() const;

private:
wgpu::Buffer inner_new_empty_buffer(size_t byteSize, BufferType bufferType = BufferType::StorageBuffer) const;
wgpu::Buffer inner_new_buffer_from_host_memory(const void *srcMemory,
Expand Down
83 changes: 83 additions & 0 deletions cpp/core/gpu/registration/adabelief.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* Copyright (c) 2008-2026 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/

#include "gpu/registration/adabelief.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <numeric>
#include <tcb/span.hpp>
#include <vector>

AdaBelief::AdaBelief(const std::vector<Parameter> &parameters, float beta1, float beta2, float epsilon)
: m_parameters(parameters),
m_beta1(beta1),
m_beta2(beta2),
m_epsilon(epsilon),
m_timeStep(0),
m_firstMoments(parameters.size(), 0.0F),
m_secondMoments(parameters.size(), 0.0F),
m_mask(parameters.size(), 0),
m_updates(parameters.size(), 0.0F) {}

std::vector<float> AdaBelief::parameterValues() const {
std::vector<float> values(m_parameters.size());
std::transform(
m_parameters.begin(), m_parameters.end(), values.begin(), [](const Parameter &param) { return param.value; });
return values;
}

void AdaBelief::setParameterValues(tcb::span<const float> values) {
for (size_t i = 0; i < m_parameters.size() && i < values.size(); ++i) {
m_parameters[i].value = values[i];
}
}

void AdaBelief::reset() {
m_timeStep = 0;
m_firstMoments.assign(m_parameters.size(), 0.0F);
m_secondMoments.assign(m_parameters.size(), 0.0F);
m_mask.assign(m_parameters.size(), 0);
m_updates.assign(m_parameters.size(), 0.0F);
}

void AdaBelief::step(const std::vector<float> &gradients) {
assert(gradients.size() == m_parameters.size());
++m_timeStep;

// First pass: compute moment estimates, the update direction, and binary masks
for (size_t i = 0; i < m_parameters.size(); ++i) {
m_firstMoments[i] = m_beta1 * m_firstMoments[i] + (1.0F - m_beta1) * gradients[i];
const float diff = gradients[i] - m_firstMoments[i];
m_secondMoments[i] = m_beta2 * m_secondMoments[i] + (1.0F - m_beta2) * diff * diff;
const float firstMomentCorrected = m_firstMoments[i] / (1.0F - std::pow(m_beta1, m_timeStep));
const float secondMomentCorrected = m_secondMoments[i] / (1.0F - std::pow(m_beta2, m_timeStep));
m_updates[i] = firstMomentCorrected / (std::sqrt(secondMomentCorrected) + m_epsilon);
// Create mask: update only if update and gradient are aligned (i.e. product > 0)
m_mask[i] = (m_updates[i] * gradients[i] > 0.0F) ? 1 : 0;
}

// Compute the average mask value across all parameters
const float maskSum = std::accumulate(m_mask.begin(), m_mask.end(), 0);
const float maskMean = maskSum / static_cast<float>(m_mask.size());

// Second pass: apply cautious update to each parameter
for (size_t i = 0; i < m_parameters.size(); ++i) {
// If the mask is 0, no update is applied; otherwise, scale the update to compensate for the overall sparsity.
m_parameters[i].value -= m_parameters[i].learning_rate * (m_updates[i] * m_mask[i] / (maskMean + m_epsilon));
}
}
56 changes: 56 additions & 0 deletions cpp/core/gpu/registration/adabelief.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* Copyright (c) 2008-2026 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/

#pragma once

#include <cstdint>
#include <tcb/span.hpp>
#include <vector>

// AdaBelief is an improved version of Adam that takes into account the curvature of the loss function.
// See here https://arxiv.org/abs/2010.07468
// This version is further enhanced by the idea in this paper https://arxiv.org/abs/2411.16085
// which consists in performing an element‐wise mask to the update such that only the components
// where the proposed update direction and the current gradient are aligned
// (i.e., have the same sign) are applied. This ensures that every step reliably reduces the loss
// and avoids potential overshooting or oscillations.
class AdaBelief {
public:
struct Parameter {
float value;
float learning_rate;
};

AdaBelief(const std::vector<Parameter> &parameters, float beta1 = 0.7F, float beta2 = 0.9999F, float epsilon = 1e-6F);

void step(const std::vector<float> &gradients);
std::vector<float> parameterValues() const;
void setParameterValues(tcb::span<const float> values);

// Resets the optimizer internal state (moments and timestep) but keeps the parameters unchanged
void reset();

private:
std::vector<Parameter> m_parameters;
float m_beta1;
float m_beta2;
float m_epsilon;
int m_timeStep;
std::vector<float> m_firstMoments; // Exponential moving average of gradients (m_t)
std::vector<float> m_secondMoments; // Exponential moving average of squared deviations ((g_t - m_t)^2)
std::vector<uint32_t> m_mask;
std::vector<float> m_updates;
};
53 changes: 53 additions & 0 deletions cpp/core/gpu/registration/calculatorinterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* Copyright (c) 2008-2026 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/

#pragma once

#include "gpu/registration/ncccalculator.h"
#include "gpu/registration/nmicalculator.h"
#include "gpu/registration/registrationtypes.h"
#include "gpu/registration/ssdcalculator.h"
#include "match_variant.h"

#include <cassert>
#include <variant>

namespace MR::GPU {

class Calculator final : public std::variant<NMICalculator, SSDCalculator, NCCCalculator> {
public:
// Expose std::variant constructors publicly
using std::variant<NMICalculator, SSDCalculator, NCCCalculator>::variant;

struct Config {
Texture fixed_texture;
Texture moving_texture;
MR::Transform fixed_transform;
MR::Transform moving_transform;
float downscale_factor;
GlobalMetric metric;
};

void update(const GlobalTransform &transformation) {
MR::match_v(*this, [&](auto &&arg) { arg.update(transformation); });
}

IterationResult get_result() const {
return MR::match_v(*this, [&](auto &&arg) { return arg.get_result(); });
}
};

} // namespace MR::GPU
25 changes: 25 additions & 0 deletions cpp/core/gpu/registration/calculatoroutput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Copyright (c) 2008-2026 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/

#pragma once

#include <cstdint>

namespace MR::GPU {

enum class CalculatorOutput : uint8_t { Cost, CostAndGradients };

}
76 changes: 76 additions & 0 deletions cpp/core/gpu/registration/convergencechecker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* Copyright (c) 2008-2026 the MRtrix3 contributors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Covered Software is provided under this License on an "as is"
* basis, without warranty of any kind, either expressed, implied, or
* statutory, including, without limitation, warranties that the
* Covered Software is free of defects, merchantable, fit for a
* particular purpose or non-infringing.
* See the Mozilla Public License v. 2.0 for more details.
*
* For more details, see http://www.mrtrix.org/.
*/

#include "gpu/registration/convergencechecker.h"
#include "exception.h"
#include <cassert>
#include <cmath>
#include <cstddef>
#include <stdexcept>
#include <string>
#include <tcb/span.hpp>
#include <vector>

namespace MR {

ConvergenceChecker::ConvergenceChecker(const Config &checkerConfiguration) : m_configuration(checkerConfiguration) {
assert(!m_configuration.param_thresholds.empty());
assert(m_configuration.patienceLimit > 0U);
}

bool ConvergenceChecker::has_converged(tcb::span<const float> current_params, float current_cost) {
if (m_configuration.param_thresholds.size() != current_params.size()) {
throw std::invalid_argument("ConvergenceChecker::has_converged: parameter threshold configuration mismatch.");
}

if (!m_initialized) {
DEBUG("ConvergenceChecker: Initializing with first parameters and cost.");
m_minimum_cost = current_cost;
m_best_params.assign(current_params.begin(), current_params.end());
m_initialized = true;
return false;
}

const bool has_better_cost = current_cost < m_minimum_cost;

const auto significant_param_improvement = [&]() {
for (size_t idx = 0; idx < current_params.size(); ++idx) {
const float param_diff = std::fabs(m_best_params[idx] - current_params[idx]);
if (param_diff >= m_configuration.param_thresholds[idx]) {
return true;
}
}
return false;
}();

if (has_better_cost) {
m_minimum_cost = current_cost;
m_best_params.assign(current_params.begin(), current_params.end());
// For the patience counter, only significant parameter improvements count
m_patience_counter = significant_param_improvement ? 0U : m_patience_counter + 1U;
DEBUG("ConvergenceChecker: Better cost found. Resetting patience counter to " + std::to_string(m_patience_counter) +
".");
} else {
DEBUG("ConvergenceChecker: No better cost found. Incrementing patience counter.");
++m_patience_counter;
}

return m_patience_counter >= m_configuration.patienceLimit;
}

void ConvergenceChecker::reset_patience() { m_patience_counter = 0; }

} // namespace MR
Loading
Loading