ThrustAllocator: solver residue below 1e-9 is zero; zero intent after a call is exact zeros (zero-intent-warm-nonzero)

After a good call the warm start left 1e-34..1e-44 throttles on a zero-intent call. Throttles at or below ThrottleResidue return 0 on the one general path; TA-R3's warm start is kept. The test NothingAskedAfterACallFiresNothing arrived in the previous commit (22cb76e8), which therefore fails that test; this commit makes it pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-10 07:59:56 +02:00
commit 8fab3052e1

View file

@ -23,6 +23,9 @@ public sealed class ThrustAllocator
const float ErrorCostWeight = .3f;
const float ErrorCostOffset = 10f;
const float Ridge = .1f;
// The solver stops within its tolerance of the optimum, so a throttle that should be zero can come back as 1e-34 after
// a warm start. Below this a throttle is that residue, not an ask: nothing asked is nothing fired.
const float ThrottleResidue = 1e-9f;
const float ErrorMax = 2f;
const double KktRelativeTolerance = 1e-12;
const int ErrorVariables = 4;
@ -77,7 +80,7 @@ public sealed class ThrustAllocator
var status = BoundedLeastSquares.Solve(rows, vars, _a, _b, _lo, _hi, _x, _workspace, out _, KktRelativeTolerance);
if (status == BoundedLeastSquaresStatus.InvalidInput) return;
for (var i = 0; i < n; i++) throttle[i] = _x[i];
for (var i = 0; i < n; i++) throttle[i] = _x[i] > ThrottleResidue ? _x[i] : 0;
}
static float Component(float3 c, int axis) => axis == 0 ? c.x : axis == 1 ? c.y : c.z;