using System; using CeresSharp; using CeresSharp.Native; using CeresSharp.Native.SafeHandles; namespace CeresSharp.Advanced; /// /// Covariance estimation for parameter blocks. /// public sealed class Covariance : IDisposable { private readonly CovarianceHandle _handle; private bool _disposed; /// /// Creates a new Covariance instance with default options. /// public Covariance() { _handle = CovarianceHandle.Create(); } /// /// Creates a new Covariance instance with the specified options. /// public Covariance(CovarianceOptions options) { if (options == null) throw new ArgumentNullException(nameof(options)); _handle = CovarianceHandle.CreateWithOptions(options.Handle.DangerousGetHandle()); } /// /// Gets the native handle. /// internal CovarianceHandle Handle => _handle; /// /// Computes the covariance for the specified parameter blocks. /// /// The problem. /// The covariance options. /// Array of parameter block arrays. /// Thrown if the operation fails. 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(); } } } } /// /// Gets the covariance block between two parameter blocks. /// /// First parameter block. /// Second parameter block. /// Output covariance block matrix (row-major). /// Thrown if the operation fails. 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); } } } } /// /// Gets the covariance matrix for multiple parameter blocks. /// /// Array of parameter block arrays. /// Output covariance matrix (row-major). /// Thrown if the operation fails. 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; } } }