CogniPilot

MODELICA LIBRARY · ENGINEERING REFERENCE

Tests/JacobianRuleTests/So3.mo

Download original .moGitHub source

Documentation: Tests.JacobianRuleTests.So3

within Tests.JacobianRuleTests;
model So3 "SO(3) rules against central differences over a dense magnitude sweep"
  constant Real step = 1.0e-6
    "Central-difference step: the O(h^2) truncation and the O(eps/h) rounding
     of a binary64 central difference cross near 1e-6 for quantities of order one";
  constant Integer trials = 6 "Randomized points per magnitude";
  constant Real magnitudes[32] = {
    1.0e-6, 3.0e-6, 1.0e-5, 3.0e-5, 1.0e-4, 3.0e-4, 1.0e-3, 3.0e-3,
    0.01, 0.03, 0.05, 0.07, 0.08, 0.085, 0.09, 0.092,
    0.094, 0.096, 0.098, 0.0999, 0.1, 0.1001, 0.102, 0.105,
    0.11, 0.2, 0.3, 0.5, 1.0, 1.5, 2.0, 3.0}
    "Dense from 1e-6 to 3 rad. 1e-4 rad is the radius where exp_map and the
     rule-local Jacobians switch between series and closed form, and 0.09 to
     0.11 brackets the radius the SE(3) and SE_2(3) building blocks switch at";
  constant Real tolerance[7] = {
    1.0e-9, 2.0e-9, 1.5e-9, 1.5e-9, 1.0e-9, 5.0e-9, 2.5e-9}
    "One budget per rule: exp_map, log_map, product left, product right,
     inverse, rotate rotation, rotate vector. Each is about five times what
     that rule measures over the whole sweep, which is its own difference
     floor: eps times the magnitude of the quantity being differenced, divided
     by the step, so the rules that carry a 5 m target vector sit higher than
     the rules that carry only rotations";
  constant Real familyTolerance = 1.0e-14
    "The three identities are exact: the first measures 4.4e-16 and the other
     two measure zero, so this budget is rounding, not accuracy";
  Real sweep[7] "Worst over the whole magnitude list, one entry per rule";
  Real family[3] "J_r J_r^-1 - I, J_l - J_r^T, J_l^-1 - (J_r^-1)^T";
  Real series[7] "0.05 rad";
  Real belowBranch[7] "0.095 rad";
  Real atBranch[7] "0.0999 rad, the last magnitude below 0.1";
  Real large[7] "2.0 rad";
equation
  sweep = Tests.RuleChecks.so3RuleResidualsSweep(magnitudes, step, trials);
  family = Tests.RuleChecks.so3JacobianFamilyResiduals(magnitudes, trials);
  series = Tests.RuleChecks.so3RuleResiduals(0.05, step, trials);
  belowBranch = Tests.RuleChecks.so3RuleResiduals(0.095, step, trials);
  atBranch = Tests.RuleChecks.so3RuleResiduals(0.0999, step, trials);
  large = Tests.RuleChecks.so3RuleResiduals(2.0, step, trials);

  assert(sweep[1] < tolerance[1],
    "exp_map_jacobian disagrees with a central difference of exp_map");
  assert(sweep[2] < tolerance[2],
    "log_map_jacobian disagrees with a central difference of log_map");
  assert(sweep[3] < tolerance[3],
    "product_left_factor_jacobian disagrees with a central difference of product");
  assert(sweep[4] < tolerance[4],
    "product_right_factor_jacobian disagrees with a central difference of product");
  assert(sweep[5] < tolerance[5],
    "inverse_jacobian disagrees with a central difference of inverse");
  assert(sweep[6] < tolerance[6],
    "rotate_rotation_jacobian disagrees with a central difference of rotate");
  assert(sweep[7] < tolerance[7],
    "rotate_vector_jacobian disagrees with a central difference of rotate");

  assert(family[1] < familyTolerance,
    "right_jacobian_exact and right_jacobian_inv_exact are not inverse");
  assert(family[2] < familyTolerance,
    "left_jacobian_exact is not the transpose of right_jacobian_exact");
  assert(family[3] < familyTolerance,
    "left_jacobian_inv_exact is not the transpose of right_jacobian_inv_exact");

  // The named rows are inside the sweep and are recorded so a failure can be
  // placed. 0.095 and 0.0999 rad are where a rule that carried the SE(3)
  // building blocks' 0.1 rad series radius reports its own truncation, of
  // order |v|^5/720, as a disagreement with exp_map: 1.0e-8 and 1.3e-8, both
  // over the 1e-8 budget this model used to share between all seven rules.
  assert(Tests.Assertions.maxAbsVector(belowBranch) < 5.0e-9,
    "SO(3) rules disagree with central differences at 0.095 rad");
  assert(Tests.Assertions.maxAbsVector(atBranch) < 5.0e-9,
    "SO(3) rules disagree with central differences at 0.0999 rad");

  annotation(Documentation(info="<html>
    <p>Each vector entry is one rule: exp_map, log_map, product in its left
    factor, product in its right factor, inverse, rotate in its rotation slot,
    and rotate in its vector slot. Budgets are per rule rather than per model:
    one shared budget hides a rule sitting a hundred times worse than its
    neighbours, which is how the two-term series in <code>right_jacobian</code>
    reached 1.3e-8 at 0.0999 rad while its six neighbours measured 1e-11 to
    8e-10 and no assertion noticed.</p>
    <p>The sweep is dense and includes 0.1 rad itself. The central differences
    here call only <code>exp_map</code>, <code>log_map</code>,
    <code>product</code>, <code>inverse</code> and <code>rotate</code>, none of
    which branches at 0.1 rad, so no magnitude in this model straddles a branch
    of its own oracle. The rules and their oracle now branch at the same
    1e-4 rad, where the two sides differ by about 1e-33 rather than the 1.4e-7
    that a 0.1 rad radius produces.</p>
    <p><code>family</code> is not a difference. It is three exact identities of
    the rule-local Jacobian family, measured directly, so it stays sharp where a
    finite difference bottoms out at 1e-10.</p>
  </html>"),
    experiment(StartTime = 0.0, StopTime = 0.002, Tolerance = 1.0e-8, Interval = 0.001));
end So3;