Initial commit
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using CeresSharp.Native;
|
||||
using CeresSharp.Native.SafeHandles;
|
||||
|
||||
namespace CeresSharp.Advanced;
|
||||
|
||||
/// <summary>
|
||||
/// Context for performance optimization.
|
||||
/// Allows reuse of expensive objects across multiple solves.
|
||||
/// </summary>
|
||||
public sealed class Context : IDisposable
|
||||
{
|
||||
private readonly ContextHandle _handle;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Context instance.
|
||||
/// </summary>
|
||||
public Context()
|
||||
{
|
||||
_handle = ContextHandle.Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native handle.
|
||||
/// </summary>
|
||||
internal ContextHandle Handle => _handle;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_handle?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using CeresSharp;
|
||||
using CeresSharp.Native;
|
||||
using CeresSharp.Native.SafeHandles;
|
||||
|
||||
namespace CeresSharp.Advanced;
|
||||
|
||||
/// <summary>
|
||||
/// Covariance estimation for parameter blocks.
|
||||
/// </summary>
|
||||
public sealed class Covariance : IDisposable
|
||||
{
|
||||
private readonly CovarianceHandle _handle;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Covariance instance with default options.
|
||||
/// </summary>
|
||||
public Covariance()
|
||||
{
|
||||
_handle = CovarianceHandle.Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Covariance instance with the specified options.
|
||||
/// </summary>
|
||||
public Covariance(CovarianceOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
|
||||
_handle = CovarianceHandle.CreateWithOptions(options.Handle.DangerousGetHandle());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native handle.
|
||||
/// </summary>
|
||||
internal CovarianceHandle Handle => _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Computes the covariance for the specified parameter blocks.
|
||||
/// </summary>
|
||||
/// <param name="problem">The problem.</param>
|
||||
/// <param name="options">The covariance options.</param>
|
||||
/// <param name="parameterBlocks">Array of parameter block arrays.</param>
|
||||
/// <exception cref="Exceptions.CeresException">Thrown if the operation fails.</exception>
|
||||
public void Compute(Problem problem, CovarianceOptions options, double[][] parameterBlocks)
|
||||
{
|
||||
if (problem == null)
|
||||
throw new ArgumentNullException(nameof(problem));
|
||||
if (options == null)
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
if (parameterBlocks == null || parameterBlocks.Length == 0)
|
||||
throw new ArgumentException("Parameter blocks cannot be null or empty", nameof(parameterBlocks));
|
||||
|
||||
unsafe
|
||||
{
|
||||
var parameterBlockPtrs = new IntPtr[parameterBlocks.Length];
|
||||
var pinnedArrays = new System.Runtime.InteropServices.GCHandle[parameterBlocks.Length];
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < parameterBlocks.Length; i++)
|
||||
{
|
||||
if (parameterBlocks[i] == null)
|
||||
throw new ArgumentException($"Parameter block {i} is null", nameof(parameterBlocks));
|
||||
|
||||
var handle = System.Runtime.InteropServices.GCHandle.Alloc(parameterBlocks[i], System.Runtime.InteropServices.GCHandleType.Pinned);
|
||||
pinnedArrays[i] = handle;
|
||||
parameterBlockPtrs[i] = handle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
var errorMessage = new System.Text.StringBuilder(512);
|
||||
fixed (IntPtr* ptrs = parameterBlockPtrs)
|
||||
{
|
||||
var errorCode = CeresNative.ceres_wrapper_covariance_compute(
|
||||
_handle.DangerousGetHandle(),
|
||||
problem.Handle.DangerousGetHandle(),
|
||||
options.Handle.DangerousGetHandle(),
|
||||
(IntPtr)ptrs,
|
||||
parameterBlocks.Length,
|
||||
errorMessage,
|
||||
errorMessage.Capacity);
|
||||
|
||||
if (errorCode != Exceptions.CeresErrorCode.Success)
|
||||
{
|
||||
var message = errorMessage.Length > 0 ? errorMessage.ToString() : null;
|
||||
throw new Exceptions.CeresException(errorCode, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var handle in pinnedArrays)
|
||||
{
|
||||
if (handle.IsAllocated)
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the covariance block between two parameter blocks.
|
||||
/// </summary>
|
||||
/// <param name="parameterBlock1">First parameter block.</param>
|
||||
/// <param name="parameterBlock2">Second parameter block.</param>
|
||||
/// <param name="covarianceBlock">Output covariance block matrix (row-major).</param>
|
||||
/// <exception cref="Exceptions.CeresException">Thrown if the operation fails.</exception>
|
||||
public void GetCovarianceBlock(double[] parameterBlock1, double[] parameterBlock2, double[] covarianceBlock)
|
||||
{
|
||||
if (parameterBlock1 == null)
|
||||
throw new ArgumentNullException(nameof(parameterBlock1));
|
||||
if (parameterBlock2 == null)
|
||||
throw new ArgumentNullException(nameof(parameterBlock2));
|
||||
if (covarianceBlock == null)
|
||||
throw new ArgumentNullException(nameof(covarianceBlock));
|
||||
|
||||
unsafe
|
||||
{
|
||||
var errorMessage = new System.Text.StringBuilder(512);
|
||||
fixed (double* ptr1 = parameterBlock1)
|
||||
fixed (double* ptr2 = parameterBlock2)
|
||||
fixed (double* covPtr = covarianceBlock)
|
||||
{
|
||||
var errorCode = CeresNative.ceres_wrapper_covariance_get_covariance_block(
|
||||
_handle.DangerousGetHandle(),
|
||||
(IntPtr)ptr1,
|
||||
(IntPtr)ptr2,
|
||||
(IntPtr)covPtr,
|
||||
errorMessage,
|
||||
errorMessage.Capacity);
|
||||
|
||||
if (errorCode != Exceptions.CeresErrorCode.Success)
|
||||
{
|
||||
var message = errorMessage.Length > 0 ? errorMessage.ToString() : null;
|
||||
throw new Exceptions.CeresException(errorCode, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the covariance matrix for multiple parameter blocks.
|
||||
/// </summary>
|
||||
/// <param name="parameterBlocks">Array of parameter block arrays.</param>
|
||||
/// <param name="covarianceMatrix">Output covariance matrix (row-major).</param>
|
||||
/// <exception cref="Exceptions.CeresException">Thrown if the operation fails.</exception>
|
||||
public void GetCovarianceMatrix(double[][] parameterBlocks, double[] covarianceMatrix)
|
||||
{
|
||||
if (parameterBlocks == null || parameterBlocks.Length == 0)
|
||||
throw new ArgumentException("Parameter blocks cannot be null or empty", nameof(parameterBlocks));
|
||||
if (covarianceMatrix == null)
|
||||
throw new ArgumentNullException(nameof(covarianceMatrix));
|
||||
|
||||
unsafe
|
||||
{
|
||||
var parameterBlockPtrs = new IntPtr[parameterBlocks.Length];
|
||||
var pinnedArrays = new System.Runtime.InteropServices.GCHandle[parameterBlocks.Length];
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < parameterBlocks.Length; i++)
|
||||
{
|
||||
if (parameterBlocks[i] == null)
|
||||
throw new ArgumentException($"Parameter block {i} is null", nameof(parameterBlocks));
|
||||
|
||||
var handle = System.Runtime.InteropServices.GCHandle.Alloc(parameterBlocks[i], System.Runtime.InteropServices.GCHandleType.Pinned);
|
||||
pinnedArrays[i] = handle;
|
||||
parameterBlockPtrs[i] = handle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
var errorMessage = new System.Text.StringBuilder(512);
|
||||
fixed (IntPtr* ptrs = parameterBlockPtrs)
|
||||
fixed (double* covPtr = covarianceMatrix)
|
||||
{
|
||||
var errorCode = CeresNative.ceres_wrapper_covariance_get_covariance_matrix(
|
||||
_handle.DangerousGetHandle(),
|
||||
(IntPtr)ptrs,
|
||||
parameterBlocks.Length,
|
||||
(IntPtr)covPtr,
|
||||
errorMessage,
|
||||
errorMessage.Capacity);
|
||||
|
||||
if (errorCode != Exceptions.CeresErrorCode.Success)
|
||||
{
|
||||
var message = errorMessage.Length > 0 ? errorMessage.ToString() : null;
|
||||
throw new Exceptions.CeresException(errorCode, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var handle in pinnedArrays)
|
||||
{
|
||||
if (handle.IsAllocated)
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_handle?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using CeresSharp.Enums;
|
||||
using CeresSharp.Native;
|
||||
using CeresSharp.Native.SafeHandles;
|
||||
|
||||
namespace CeresSharp.Advanced;
|
||||
|
||||
/// <summary>
|
||||
/// Options for covariance estimation.
|
||||
/// </summary>
|
||||
public sealed class CovarianceOptions : IDisposable
|
||||
{
|
||||
private readonly CovarianceOptionsHandle _handle;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CovarianceOptions instance with default settings.
|
||||
/// </summary>
|
||||
public CovarianceOptions()
|
||||
{
|
||||
_handle = CovarianceOptionsHandle.Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native handle.
|
||||
/// </summary>
|
||||
internal CovarianceOptionsHandle Handle => _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of threads to use.
|
||||
/// </summary>
|
||||
public int NumThreads
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_covariance_options_get_num_threads(_handle.DangerousGetHandle());
|
||||
set => CeresNative.ceres_wrapper_covariance_options_set_num_threads(_handle.DangerousGetHandle(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sparse linear algebra library type.
|
||||
/// </summary>
|
||||
public SparseLinearAlgebraLibraryType SparseLinearAlgebraLibraryType
|
||||
{
|
||||
get => (SparseLinearAlgebraLibraryType)CeresNative.ceres_wrapper_covariance_options_get_sparse_linear_algebra_library_type(_handle.DangerousGetHandle());
|
||||
set => CeresNative.ceres_wrapper_covariance_options_set_sparse_linear_algebra_library_type(_handle.DangerousGetHandle(), (int)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the algorithm type.
|
||||
/// </summary>
|
||||
public CovarianceAlgorithmType AlgorithmType
|
||||
{
|
||||
get => (CovarianceAlgorithmType)CeresNative.ceres_wrapper_covariance_options_get_algorithm_type(_handle.DangerousGetHandle());
|
||||
set => CeresNative.ceres_wrapper_covariance_options_set_algorithm_type(_handle.DangerousGetHandle(), (int)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum reciprocal condition number.
|
||||
/// </summary>
|
||||
public double MinReciprocalConditionNumber
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_covariance_options_get_min_reciprocal_condition_number(_handle.DangerousGetHandle());
|
||||
set => CeresNative.ceres_wrapper_covariance_options_set_min_reciprocal_condition_number(_handle.DangerousGetHandle(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the null space rank.
|
||||
/// </summary>
|
||||
public int NullSpaceRank
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_covariance_options_get_null_space_rank(_handle.DangerousGetHandle());
|
||||
set => CeresNative.ceres_wrapper_covariance_options_set_null_space_rank(_handle.DangerousGetHandle(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to apply loss function.
|
||||
/// </summary>
|
||||
public bool ApplyLossFunction
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_covariance_options_get_apply_loss_function(_handle.DangerousGetHandle()) != 0;
|
||||
set => CeresNative.ceres_wrapper_covariance_options_set_apply_loss_function(_handle.DangerousGetHandle(), value ? 1 : 0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_handle?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace CeresSharp.Advanced;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for evaluation callback.
|
||||
/// Called before evaluating cost functions to allow shared computation.
|
||||
/// </summary>
|
||||
/// <param name="numResiduals">Number of residuals.</param>
|
||||
/// <param name="numParameterBlocks">Number of parameter blocks.</param>
|
||||
/// <param name="parameterBlockSizes">Array of sizes for each parameter block (can be null).</param>
|
||||
public delegate void EvaluationCallback(int numResiduals, int numParameterBlocks, int[]? parameterBlockSizes);
|
||||
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using CeresSharp.Native;
|
||||
using CeresSharp.Native.SafeHandles;
|
||||
using CeresSharp;
|
||||
|
||||
namespace CeresSharp.Advanced;
|
||||
|
||||
/// <summary>
|
||||
/// Gradient checker for validating cost function gradients.
|
||||
/// </summary>
|
||||
public sealed class GradientChecker : IDisposable
|
||||
{
|
||||
private readonly GradientCheckerHandle _handle;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GradientChecker instance.
|
||||
/// </summary>
|
||||
/// <param name="costFunction">The cost function to check.</param>
|
||||
/// <param name="manifolds">Array of manifolds (can be null if no manifolds).</param>
|
||||
/// <param name="options">The gradient checker options.</param>
|
||||
public GradientChecker(CostFunction costFunction, Manifold[]? manifolds, GradientCheckerOptions options)
|
||||
{
|
||||
if (costFunction == null)
|
||||
throw new ArgumentNullException(nameof(costFunction));
|
||||
if (options == null)
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
|
||||
unsafe
|
||||
{
|
||||
IntPtr manifoldsPtr = IntPtr.Zero;
|
||||
int numManifolds = 0;
|
||||
|
||||
if (manifolds != null && manifolds.Length > 0)
|
||||
{
|
||||
var manifoldHandles = new IntPtr[manifolds.Length];
|
||||
for (int i = 0; i < manifolds.Length; i++)
|
||||
{
|
||||
if (manifolds[i] == null)
|
||||
throw new ArgumentException($"Manifold {i} is null", nameof(manifolds));
|
||||
manifoldHandles[i] = manifolds[i].Handle;
|
||||
}
|
||||
|
||||
fixed (IntPtr* ptr = manifoldHandles)
|
||||
{
|
||||
manifoldsPtr = (IntPtr)ptr;
|
||||
numManifolds = manifolds.Length;
|
||||
}
|
||||
}
|
||||
|
||||
var handle = CeresNative.ceres_wrapper_create_gradient_checker(
|
||||
costFunction.Handle,
|
||||
manifoldsPtr,
|
||||
numManifolds,
|
||||
options.Handle.DangerousGetHandle());
|
||||
|
||||
_handle = GradientCheckerHandle.Create(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native handle.
|
||||
/// </summary>
|
||||
internal GradientCheckerHandle Handle => _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Probes gradients at the given parameters.
|
||||
/// </summary>
|
||||
/// <param name="parameters">Array of parameter block arrays.</param>
|
||||
/// <param name="relativePrecision">Relative precision for comparison.</param>
|
||||
/// <returns>True if gradients match, false otherwise.</returns>
|
||||
/// <exception cref="Exceptions.CeresException">Thrown if the operation fails (other than gradient mismatch).</exception>
|
||||
public bool Probe(double[][] parameters, double relativePrecision)
|
||||
{
|
||||
if (parameters == null || parameters.Length == 0)
|
||||
throw new ArgumentException("Parameters cannot be null or empty", nameof(parameters));
|
||||
|
||||
unsafe
|
||||
{
|
||||
var parameterBlockPtrs = new IntPtr[parameters.Length];
|
||||
var pinnedArrays = new GCHandle[parameters.Length];
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (parameters[i] == null)
|
||||
throw new ArgumentException($"Parameter block {i} is null", nameof(parameters));
|
||||
|
||||
var handle = GCHandle.Alloc(parameters[i], GCHandleType.Pinned);
|
||||
pinnedArrays[i] = handle;
|
||||
parameterBlockPtrs[i] = handle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
var sb = new StringBuilder(512);
|
||||
fixed (IntPtr* ptrs = parameterBlockPtrs)
|
||||
{
|
||||
var errorCode = CeresNative.ceres_wrapper_gradient_checker_probe(
|
||||
_handle.DangerousGetHandle(),
|
||||
(IntPtr)ptrs,
|
||||
relativePrecision,
|
||||
sb,
|
||||
sb.Capacity);
|
||||
|
||||
// InvalidParameter means gradients don't match (expected behavior)
|
||||
if (errorCode == Exceptions.CeresErrorCode.InvalidParameter)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Other error codes indicate actual errors
|
||||
if (errorCode != Exceptions.CeresErrorCode.Success)
|
||||
{
|
||||
var message = sb.Length > 0 ? sb.ToString() : null;
|
||||
throw new Exceptions.CeresException(errorCode, message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var handle in pinnedArrays)
|
||||
{
|
||||
if (handle.IsAllocated)
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Probes gradients at the given parameters and returns error message if they don't match.
|
||||
/// </summary>
|
||||
/// <param name="parameters">Array of parameter block arrays.</param>
|
||||
/// <param name="relativePrecision">Relative precision for comparison.</param>
|
||||
/// <param name="errorMessage">Output error message if gradients don't match.</param>
|
||||
/// <returns>True if gradients match, false otherwise.</returns>
|
||||
/// <exception cref="Exceptions.CeresException">Thrown if the operation fails (other than gradient mismatch).</exception>
|
||||
public bool Probe(double[][] parameters, double relativePrecision, out string? errorMessage)
|
||||
{
|
||||
if (parameters == null || parameters.Length == 0)
|
||||
throw new ArgumentException("Parameters cannot be null or empty", nameof(parameters));
|
||||
|
||||
unsafe
|
||||
{
|
||||
var parameterBlockPtrs = new IntPtr[parameters.Length];
|
||||
var pinnedArrays = new GCHandle[parameters.Length];
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (parameters[i] == null)
|
||||
throw new ArgumentException($"Parameter block {i} is null", nameof(parameters));
|
||||
|
||||
var handle = GCHandle.Alloc(parameters[i], GCHandleType.Pinned);
|
||||
pinnedArrays[i] = handle;
|
||||
parameterBlockPtrs[i] = handle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
var sb = new StringBuilder(512);
|
||||
fixed (IntPtr* ptrs = parameterBlockPtrs)
|
||||
{
|
||||
var errorCode = CeresNative.ceres_wrapper_gradient_checker_probe(
|
||||
_handle.DangerousGetHandle(),
|
||||
(IntPtr)ptrs,
|
||||
relativePrecision,
|
||||
sb,
|
||||
sb.Capacity);
|
||||
|
||||
// InvalidParameter means gradients don't match (expected behavior)
|
||||
if (errorCode == Exceptions.CeresErrorCode.InvalidParameter)
|
||||
{
|
||||
errorMessage = sb.Length > 0 ? sb.ToString() : "Gradient check failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Other error codes indicate actual errors
|
||||
if (errorCode != Exceptions.CeresErrorCode.Success)
|
||||
{
|
||||
var message = sb.Length > 0 ? sb.ToString() : null;
|
||||
throw new Exceptions.CeresException(errorCode, message);
|
||||
}
|
||||
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var handle in pinnedArrays)
|
||||
{
|
||||
if (handle.IsAllocated)
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_handle?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using CeresSharp.Native;
|
||||
using CeresSharp.Native.SafeHandles;
|
||||
|
||||
namespace CeresSharp.Advanced;
|
||||
|
||||
/// <summary>
|
||||
/// Options for gradient checking.
|
||||
/// </summary>
|
||||
public sealed class GradientCheckerOptions : IDisposable
|
||||
{
|
||||
private readonly GradientCheckerOptionsHandle _handle;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GradientCheckerOptions instance with default settings.
|
||||
/// </summary>
|
||||
public GradientCheckerOptions()
|
||||
{
|
||||
_handle = GradientCheckerOptionsHandle.Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native handle.
|
||||
/// </summary>
|
||||
internal GradientCheckerOptionsHandle Handle => _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the gradient check relative precision.
|
||||
/// </summary>
|
||||
public double GradientCheckRelativePrecision
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_gradient_checker_options_get_gradient_check_relative_precision(_handle.DangerousGetHandle());
|
||||
set => CeresNative.ceres_wrapper_gradient_checker_options_set_gradient_check_relative_precision(_handle.DangerousGetHandle(), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the numeric derivative relative step size.
|
||||
/// </summary>
|
||||
public double NumericDerivativeRelativeStepSize
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_gradient_checker_options_get_gradient_check_numeric_derivative_relative_step_size(_handle.DangerousGetHandle());
|
||||
set => CeresNative.ceres_wrapper_gradient_checker_options_set_gradient_check_numeric_derivative_relative_step_size(_handle.DangerousGetHandle(), value);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_handle?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using CeresSharp.Native;
|
||||
using CeresSharp.Native.SafeHandles;
|
||||
|
||||
namespace CeresSharp.Advanced;
|
||||
|
||||
/// <summary>
|
||||
/// Options for configuring a Ceres Problem.
|
||||
/// </summary>
|
||||
public sealed class ProblemOptions : IDisposable
|
||||
{
|
||||
private readonly ProblemOptionsHandle _handle;
|
||||
private System.Runtime.InteropServices.GCHandle? _evaluationCallbackHandle;
|
||||
private System.Runtime.InteropServices.GCHandle? _evaluationNativeCallbackHandle;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ProblemOptions instance with default settings.
|
||||
/// </summary>
|
||||
public ProblemOptions()
|
||||
{
|
||||
_handle = ProblemOptionsHandle.Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native handle.
|
||||
/// </summary>
|
||||
internal ProblemOptionsHandle Handle => _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the problem owns cost functions.
|
||||
/// </summary>
|
||||
public bool CostFunctionOwnership
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_problem_options_get_cost_function_ownership(_handle.DangerousGetHandle()) != 0;
|
||||
set => CeresNative.ceres_wrapper_problem_options_set_cost_function_ownership(_handle.DangerousGetHandle(), value ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the problem owns loss functions.
|
||||
/// </summary>
|
||||
public bool LossFunctionOwnership
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_problem_options_get_loss_function_ownership(_handle.DangerousGetHandle()) != 0;
|
||||
set => CeresNative.ceres_wrapper_problem_options_set_loss_function_ownership(_handle.DangerousGetHandle(), value ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the problem owns manifolds.
|
||||
/// </summary>
|
||||
public bool ManifoldOwnership
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_problem_options_get_manifold_ownership(_handle.DangerousGetHandle()) != 0;
|
||||
set => CeresNative.ceres_wrapper_problem_options_set_manifold_ownership(_handle.DangerousGetHandle(), value ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to enable fast removal of residual blocks.
|
||||
/// </summary>
|
||||
public bool EnableFastRemoval
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_problem_options_get_enable_fast_removal(_handle.DangerousGetHandle()) != 0;
|
||||
set => CeresNative.ceres_wrapper_problem_options_set_enable_fast_removal(_handle.DangerousGetHandle(), value ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to disable all safety checks.
|
||||
/// </summary>
|
||||
public bool DisableAllSafetyChecks
|
||||
{
|
||||
get => CeresNative.ceres_wrapper_problem_options_get_disable_all_safety_checks(_handle.DangerousGetHandle()) != 0;
|
||||
set => CeresNative.ceres_wrapper_problem_options_set_disable_all_safety_checks(_handle.DangerousGetHandle(), value ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the context for performance optimization.
|
||||
/// </summary>
|
||||
public void SetContext(Context context)
|
||||
{
|
||||
if (context == null)
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
|
||||
CeresNative.ceres_wrapper_problem_options_set_context(
|
||||
_handle.DangerousGetHandle(),
|
||||
context.Handle.DangerousGetHandle());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets an evaluation callback for shared computation.
|
||||
/// Called before evaluating cost functions to allow shared computation.
|
||||
/// </summary>
|
||||
/// <param name="callback">The evaluation callback.</param>
|
||||
public void SetEvaluationCallback(EvaluationCallback callback)
|
||||
{
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException(nameof(callback));
|
||||
|
||||
// Pin the callback delegate
|
||||
var callbackHandle = System.Runtime.InteropServices.GCHandle.Alloc(callback);
|
||||
|
||||
// Create native callback wrapper
|
||||
var nativeCallback = new CeresNative.CeresEvaluationCallback((userData, numResiduals, numParameterBlocks, parameterBlockSizesPtr) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var handle = System.Runtime.InteropServices.GCHandle.FromIntPtr(userData);
|
||||
var csCallback = (EvaluationCallback)handle.Target!;
|
||||
|
||||
// Parse parameter block sizes
|
||||
int[]? parameterBlockSizes = null;
|
||||
if (parameterBlockSizesPtr != IntPtr.Zero && numParameterBlocks > 0)
|
||||
{
|
||||
parameterBlockSizes = new int[numParameterBlocks];
|
||||
System.Runtime.InteropServices.Marshal.Copy(parameterBlockSizesPtr, parameterBlockSizes, 0, numParameterBlocks);
|
||||
}
|
||||
|
||||
// Call C# callback
|
||||
csCallback(numResiduals, numParameterBlocks, parameterBlockSizes);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors in callback
|
||||
}
|
||||
});
|
||||
|
||||
// Pin the native callback
|
||||
var nativeCallbackHandle = System.Runtime.InteropServices.GCHandle.Alloc(nativeCallback);
|
||||
|
||||
CeresNative.ceres_wrapper_problem_options_set_evaluation_callback(
|
||||
_handle.DangerousGetHandle(),
|
||||
nativeCallback,
|
||||
System.Runtime.InteropServices.GCHandle.ToIntPtr(callbackHandle));
|
||||
|
||||
// Track handles for cleanup when ProblemOptions is disposed
|
||||
_evaluationCallbackHandle = callbackHandle;
|
||||
_evaluationNativeCallbackHandle = nativeCallbackHandle;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
// Cleanup callback handles
|
||||
if (_evaluationCallbackHandle.HasValue && _evaluationCallbackHandle.Value.IsAllocated)
|
||||
_evaluationCallbackHandle.Value.Free();
|
||||
if (_evaluationNativeCallbackHandle.HasValue && _evaluationNativeCallbackHandle.Value.IsAllocated)
|
||||
_evaluationNativeCallbackHandle.Value.Free();
|
||||
|
||||
_handle?.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user