v6.4.13

Fri Aug 28 2026

Bug Fixes

AI-Engine

  • guard zero-span bounds in direction loss create_direction_loss normalised a variable's position by (max - min).sum(). When a variable's boundary constraints are pinned, i.e. min == max on every date, that span is 0 and the term evaluates to 0/0 = NaN. The NaN propagates into the loss, then into the gradients, survives clip_grad_value_/clip_grad_norm_ and is written into every optimisation variable by opt.step(). The next forward pass then fails with "produced non finite values" on whichever node happens to come first in topological order, which makes the crash look unrelated to the bounds that caused it. A pinned variable cannot move, so it carries no direction signal at all: skip it instead of dividing by zero. When every direction variable is pinned the loss falls back to 1, matching the no-directions default. Reproduced on danske-spil optimization 12, where two media variables were pinned to min = max = 0.
  • accumulate the Maximize direction penalty The Maximize branch of create_direction_loss assigned loss = 1 - position instead of adding to the running sum, so it discarded every direction term accumulated before it. The result depended on the iteration order of the directions dict: a Maximize variable visited last wiped out all Minimize pressure, while the divisor n still counted the discarded terms. Add the penalty like the Minimize branch does. The loss is now the mean of the per-variable penalties regardless of direction or order, which is what the divisor already assumed. The two existing expectations move from 1.25 to 1.5: with b Minimize and c Maximize both sitting at position 0.5, each contributes 0.5, so the mean is 0.5 rather than the 0.25 the overwrite produced.