361 lines
12 KiB
C#
361 lines
12 KiB
C#
using CeresSharp;
|
|
using CeresSharp.Enums;
|
|
|
|
namespace CeresSharp.Test;
|
|
|
|
[TestFixture]
|
|
public class ProblemTests
|
|
{
|
|
[Test]
|
|
public void CreateProblem_ShouldSucceed()
|
|
{
|
|
using var problem = new Problem();
|
|
Assert.That(problem, Is.Not.Null);
|
|
Assert.That(problem.NumParameterBlocks, Is.EqualTo(0));
|
|
Assert.That(problem.NumResidualBlocks, Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void AddParameterBlock_ShouldIncreaseCount()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0, 3.0 };
|
|
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
Assert.That(problem.NumParameterBlocks, Is.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
public void AddMultipleParameterBlocks_ShouldIncreaseCount()
|
|
{
|
|
using var problem = new Problem();
|
|
var params1 = new double[] { 1.0, 2.0 };
|
|
var params2 = new double[] { 3.0, 4.0, 5.0 };
|
|
|
|
problem.AddParameterBlock(params1, params1.Length);
|
|
problem.AddParameterBlock(params2, params2.Length);
|
|
|
|
Assert.That(problem.NumParameterBlocks, Is.EqualTo(2));
|
|
}
|
|
|
|
[Test]
|
|
public void SetParameterBlockConstant_ShouldSucceed()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
problem.SetParameterBlockConstant(parameters);
|
|
|
|
// Should not throw
|
|
Assert.Pass();
|
|
}
|
|
|
|
[Test]
|
|
public void SetParameterBlockVariable_ShouldSucceed()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
problem.SetParameterBlockConstant(parameters);
|
|
|
|
problem.SetParameterBlockVariable(parameters);
|
|
|
|
// Should not throw
|
|
Assert.Pass();
|
|
}
|
|
|
|
[Test]
|
|
public void SetParameterLowerBound_ShouldSucceed()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
problem.SetParameterLowerBound(parameters, index: 0, lowerBound: 0.0);
|
|
|
|
// Should not throw
|
|
Assert.Pass();
|
|
}
|
|
|
|
[Test]
|
|
public void SetParameterUpperBound_ShouldSucceed()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
problem.SetParameterUpperBound(parameters, index: 0, upperBound: 10.0);
|
|
|
|
// Should not throw
|
|
Assert.Pass();
|
|
}
|
|
|
|
[Test]
|
|
public void AddResidualBlock_ShouldIncreaseResidualCount()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var costFunction = new AutoDiffCostFunction(
|
|
(parameters, residuals) =>
|
|
{
|
|
residuals[0] = parameters[0][0] - 1.0;
|
|
return true;
|
|
},
|
|
numResiduals: 1,
|
|
parameterBlockSizes: new[] { 1 });
|
|
|
|
var residualBlockId = problem.AddResidualBlock(
|
|
costFunction,
|
|
lossFunction: null,
|
|
parameterBlocks: new[] { parameters });
|
|
|
|
Assert.That(problem.NumResidualBlocks, Is.EqualTo(1));
|
|
Assert.That(residualBlockId, Is.Not.EqualTo(IntPtr.Zero), "Residual block ID should not be zero");
|
|
}
|
|
|
|
[Test]
|
|
public void AddResidualBlock_WithLossFunction_ShouldSucceed()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var costFunction = new AutoDiffCostFunction(
|
|
(parameters, residuals) =>
|
|
{
|
|
residuals[0] = parameters[0][0] - 1.0;
|
|
return true;
|
|
},
|
|
numResiduals: 1,
|
|
parameterBlockSizes: new[] { 1 });
|
|
|
|
using var loss = new HuberLoss(1.0);
|
|
var residualBlockId = problem.AddResidualBlock(
|
|
costFunction,
|
|
loss,
|
|
parameterBlocks: new[] { parameters });
|
|
|
|
Assert.That(problem.NumResidualBlocks, Is.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
public void RemoveResidualBlock_ShouldDecreaseCount()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var costFunction = new AutoDiffCostFunction(
|
|
(parameters, residuals) =>
|
|
{
|
|
residuals[0] = parameters[0][0] - 1.0;
|
|
return true;
|
|
},
|
|
numResiduals: 1,
|
|
parameterBlockSizes: new[] { 1 });
|
|
|
|
var residualBlockId = problem.AddResidualBlock(
|
|
costFunction,
|
|
lossFunction: null,
|
|
parameterBlocks: new[] { parameters });
|
|
|
|
Assert.That(problem.NumResidualBlocks, Is.EqualTo(1));
|
|
|
|
problem.RemoveResidualBlock(residualBlockId);
|
|
|
|
Assert.That(problem.NumResidualBlocks, Is.EqualTo(0));
|
|
}
|
|
|
|
[Test]
|
|
public void SetManifold_ShouldSucceed()
|
|
{
|
|
using var problem = new Problem();
|
|
var quaternion = new double[] { 0.0, 0.0, 0.0, 1.0 }; // qx, qy, qz, qw
|
|
problem.AddParameterBlock(quaternion, quaternion.Length);
|
|
|
|
using var manifold = new QuaternionManifold();
|
|
problem.SetManifold(quaternion, manifold);
|
|
|
|
// Should not throw
|
|
Assert.Pass();
|
|
}
|
|
|
|
[Test]
|
|
public void SetManifold_ThenRemove_ShouldSucceed()
|
|
{
|
|
using var problem = new Problem();
|
|
var quaternion = new double[] { 0.0, 0.0, 0.0, 1.0 };
|
|
problem.AddParameterBlock(quaternion, quaternion.Length);
|
|
|
|
using var manifold = new QuaternionManifold();
|
|
problem.SetManifold(quaternion, manifold);
|
|
|
|
// Note: SetManifold doesn't accept null - manifold removal is not directly supported
|
|
// The manifold will be removed when parameter block is removed or problem is disposed
|
|
|
|
// Should not throw
|
|
Assert.Pass();
|
|
}
|
|
|
|
|
|
[Test]
|
|
public void IsParameterBlockConstant_ShouldReturnFalse_WhenVariable()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var isConstant = problem.IsParameterBlockConstant(parameters);
|
|
|
|
Assert.That(isConstant, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void IsParameterBlockConstant_ShouldReturnTrue_WhenConstant()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
problem.SetParameterBlockConstant(parameters);
|
|
|
|
var isConstant = problem.IsParameterBlockConstant(parameters);
|
|
|
|
Assert.That(isConstant, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void HasParameterBlock_ShouldReturnTrue_WhenExists()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var hasBlock = problem.HasParameterBlock(parameters);
|
|
|
|
Assert.That(hasBlock, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void HasParameterBlock_ShouldReturnFalse_WhenNotExists()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
var fakeParams = new double[] { 99.0, 99.0 };
|
|
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var hasBlock = problem.HasParameterBlock(fakeParams);
|
|
|
|
Assert.That(hasBlock, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void GetParameterBlockSize_ShouldReturnCorrectSize()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0, 3.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var size = problem.GetParameterBlockSize(parameters);
|
|
|
|
Assert.That(size, Is.EqualTo(3));
|
|
}
|
|
|
|
[Test]
|
|
public void GetParameterBlockSize_ShouldReturnMinusOne_WhenNotExists()
|
|
{
|
|
using var problem = new Problem();
|
|
var fakeParams = new double[] { 99.0, 99.0 };
|
|
|
|
var size = problem.GetParameterBlockSize(fakeParams);
|
|
|
|
Assert.That(size, Is.EqualTo(-1));
|
|
}
|
|
|
|
[Test]
|
|
public void HasManifold_ShouldReturnFalse_WhenNoManifold()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0, 3.0, 4.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var hasManifold = problem.HasManifold(parameters);
|
|
|
|
Assert.That(hasManifold, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void HasManifold_ShouldReturnTrue_WhenManifoldSet()
|
|
{
|
|
using var problem = new Problem();
|
|
var quaternion = new double[] { 0.0, 0.0, 0.0, 1.0 };
|
|
problem.AddParameterBlock(quaternion, quaternion.Length);
|
|
|
|
using var manifold = new QuaternionManifold();
|
|
problem.SetManifold(quaternion, manifold);
|
|
|
|
var hasManifold = problem.HasManifold(quaternion);
|
|
|
|
Assert.That(hasManifold, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void GetManifoldHandle_ShouldReturnZero_WhenNoManifold()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0 };
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
var handle = problem.GetManifoldHandle(parameters);
|
|
|
|
Assert.That(handle, Is.EqualTo(IntPtr.Zero));
|
|
}
|
|
|
|
[Test]
|
|
public void GetManifoldHandle_ShouldReturnNonZero_WhenManifoldSet()
|
|
{
|
|
using var problem = new Problem();
|
|
var quaternion = new double[] { 0.0, 0.0, 0.0, 1.0 };
|
|
problem.AddParameterBlock(quaternion, quaternion.Length);
|
|
|
|
using var manifold = new QuaternionManifold();
|
|
problem.SetManifold(quaternion, manifold);
|
|
|
|
var handle = problem.GetManifoldHandle(quaternion);
|
|
|
|
Assert.That(handle, Is.Not.EqualTo(IntPtr.Zero));
|
|
}
|
|
|
|
[Test]
|
|
public void GetParameterBlockTangentSize_ShouldReturnTangentSize_WithManifold()
|
|
{
|
|
using var problem = new Problem();
|
|
var quaternion = new double[] { 0.0, 0.0, 0.0, 1.0 }; // 4D ambient
|
|
problem.AddParameterBlock(quaternion, quaternion.Length);
|
|
|
|
using var manifold = new QuaternionManifold(); // 3D tangent
|
|
problem.SetManifold(quaternion, manifold);
|
|
|
|
var tangentSize = problem.GetParameterBlockTangentSize(quaternion);
|
|
|
|
Assert.That(tangentSize, Is.EqualTo(3)); // Quaternion: 4D ambient → 3D tangent
|
|
}
|
|
|
|
[Test]
|
|
public void GetParameterBlockTangentSize_ShouldReturnAmbientSize_WithoutManifold()
|
|
{
|
|
using var problem = new Problem();
|
|
var parameters = new double[] { 1.0, 2.0, 3.0 }; // 3D
|
|
problem.AddParameterBlock(parameters, parameters.Length);
|
|
|
|
// No manifold set, so tangent size should equal ambient size
|
|
var tangentSize = problem.GetParameterBlockTangentSize(parameters);
|
|
|
|
Assert.That(tangentSize, Is.EqualTo(3)); // Without manifold, tangent = ambient
|
|
}
|
|
}
|