{"record":{"id":"5e1cea203bab83fc","repo":"invoke-ai/InvokeAI","slug":"pid-student-schedule-for-num-steps-num-steps-is","errorCode":null,"errorMessage":"PiD student schedule for num_steps={num_steps} is not strictly decreasing (got {t.tolist()}); the schedule has only 4 transitions, so num_steps must be between 1 and 4.","messagePattern":"PiD student schedule for num_steps=(.+?) is not strictly decreasing \\(got (.+?)\\); the schedule has only 4 transitions, so num_steps must be between 1 and 4\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/pid/decode.py","lineNumber":320,"sourceCode":"    \"\"\"Distill-student sigma schedule.\n\n    When *num_steps* differs from the trained 4 steps, linearly sub-sample\n    the canonical 5-point list (mirrors `PidDistillModel._get_t_list`).\n    \"\"\"\n    full = torch.tensor(_STUDENT_T_LIST, device=device, dtype=torch.float32)\n    if num_steps is None or num_steps == 4:\n        t = full\n    else:\n        idx = torch.linspace(0, len(full) - 1, num_steps + 1).round().long()\n        t = full[idx]\n    assert abs(t[-1].item()) < 1e-6, \"t_list must end at 0\"\n    # The student schedule has only 4 transitions (a 5-point list). Sub-sampling to more\n    # than 4 steps rounds distinct linspace indices onto the same point, yielding duplicate\n    # timesteps that _student_sample_loop would waste a full network forward on (and which\n    # degrade rather than refine the output). Callers cap num_steps at 4; raise (not assert, so the\n    # guard survives `python -O`) if an invalid step count ever produces a non-strictly-decreasing schedule.\n    if t.numel() >= 2 and not bool((t[1:] < t[:-1]).all()):\n        raise ValueError(\n            f\"PiD student schedule for num_steps={num_steps} is not strictly decreasing (got {t.tolist()}); \"\n            \"the schedule has only 4 transitions, so num_steps must be between 1 and 4.\"\n        )\n    return t\n\n\ndef _velocity_to_x0(x_t: Tensor, net_output: Tensor, t: Tensor, *, pid_memory_optimization: bool = False) -> Tensor:\n    \"\"\"Convert the network's velocity prediction back to x0 at time *t*.\n\n    The optimized branch is a genuine precision reduction, not just a cheaper spelling, so it is worth\n    being explicit about what it buys. Measured on an RTX 4090 (B=1, 3xHxW, ``x_t`` fp32 / ``net_output``\n    bf16), transient peak for this call alone:\n\n    ======  ==========  ==============  ==============\n    size    fp64 (dflt)  fused fp64      fused fp32\n    ======  ==========  ==============  ==============\n    1024px  72 MiB      72 MiB          24 MiB\n    2048px  288 MiB     288 MiB         96 MiB","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/pid/decode.py#L302-L338","documentation":"The PiD student sampler uses a fixed 5-point schedule (4 transitions), and _get_t_list sub-samples it with linspace for a requested num_steps. If num_steps > 4, distinct linspace indices collapse onto the same timestep, producing duplicates; duplicates are not strictly decreasing and would waste a network forward while degrading output. The function therefore raises ValueError (not assert, so the check survives python -O) when the derived schedule is not strictly decreasing.","triggerScenarios":"Calling decode (which calls _get_t_list) with num_steps greater than 4, or any num_steps that yields a non-strictly-decreasing schedule.","commonSituations":"Exposing a generic 'steps' UI/config field to users who set 8/16/20 as with ordinary diffusion samplers, or piping a shared sampler config across models with different step caps.","solutions":["Set num_steps to a value between 1 and 4 for the PiD decoder","Clamp steps = min(steps, 4) in the calling pipeline/UI before decode","Use a different sampler/model if more steps are required"],"exampleFix":"// before\ndecode(image, num_steps=user_steps)  # user_steps may be 20\n// after\ndecode(image, num_steps=max(1, min(user_steps, 4)))","handlingStrategy":"validation","validationCode":"if not 1 <= num_steps <= 4:\n    raise ValueError(f\"PiD decoder supports 1-4 steps, got {num_steps}\")","typeGuard":null,"tryCatchPattern":"try:\n    out = decode(x, num_steps=n)\nexcept ValueError as e:\n    if \"strictly decreasing\" in str(e):\n        out = decode(x, num_steps=min(n, 4))\n    else:\n        raise","preventionTips":["Clamp steps to 4 in UI/config","Document the step cap","Test rejection under python -O"],"tags":["sampling","value-error","num-steps","schedule"],"backgroundTag":"invalid-step-count","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}