neuralforecast icon indicating copy to clipboard operation
neuralforecast copied to clipboard

Incorrect handling of first quantile knot in `isqf_domain_map` of ISQF

Open JingangQu opened this issue 4 months ago • 0 comments
trafficstars

What happened + What you expected to happen

Description

The current implementation of isqf_domain_map function applies F.softplus() to all quantile knots, including the first one. This is incorrect because the first quantile knot represents the starting position of the distribution and should be allowed to take negative values.

Current Implementation (Incorrect)

# Line 1739 in neuralforecast/losses/pytorch.py
quantile_knots = F.softplus(input[..., start_index : start_index + num_qk]) + tol
qk_y = torch.cumsum(quantile_knots, dim=-1)

Expected Behavior

The first quantile knot should remain unconstrained (can be negative), while subsequent knots should have positive increments to ensure monotonicity. This matches the reference implementation in isqf.py.

Reference Implementation (Correct)

quantile_knots = torch.cat(
    [
        input[..., start_index : start_index + 1],  # First knot unconstrained
        F.softplus(quantile_knots[..., start_index + 1 : start_index + num_qk]) + tol,  # Subsequent increments positive
    ],
    dim=-1,
)
qk_y = torch.cumsum(quantile_knots, dim=-1)

Versions / Dependencies

Latest version of neuralforecast

Reproduction script

Wrong code:

# Line 1739 in neuralforecast/losses/pytorch.py
quantile_knots = F.softplus(input[..., start_index : start_index + num_qk]) + tol
qk_y = torch.cumsum(quantile_knots, dim=-1)

Correct code:

quantile_knots = torch.cat(
    [
        input[..., start_index : start_index + 1],  # First knot unconstrained
        F.softplus(quantile_knots[..., start_index + 1 : start_index + num_qk]) + tol,  # Subsequent increments positive
    ],
    dim=-1,
)
qk_y = torch.cumsum(quantile_knots, dim=-1)

Issue Severity

None

JingangQu avatar Jun 25 '25 21:06 JingangQu