Files
kappacommit 25bbf32a65 feat(model): Add ER SDE / DPM++ 2M Scheduler Support For Anima (#9125)
* refactor(anima): reshape ANIMA_SCHEDULER_MAP to (class, kwargs) tuples

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(anima): address Task 1 review feedback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(anima): add dpmpp_2m and dpmpp_2m_sde schedulers

* refactor(anima): unify ANIMA_SHIFT in schedulers.py and add Literal-coverage test

* fix(anima): seed generator into scheduler.step for SDE reproducibility

* feat(anima): add pure ancestral-Euler step helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(anima): fix _anima_euler_ancestral_step docstring formula to match code

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(anima): address Task 4 review feedback

* feat(anima): add euler_a (rectified-flow ancestral Euler) scheduler

* fix(anima): sample euler_a noise in float32 to avoid bfloat16 quantization

* chore(anima): bump anima_denoise to v1.3.0 and regen schema

* fix(frontend): revert Windows path-separator drift in schema regen

* fix(anima): gate step_generator construction to schedulers that need it

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(anima): apply ruff lint and format fixes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(frontend): expose new Anima schedulers in dropdown and metadata recall

* chore(frontend): apply prettier wrap to setAnimaScheduler PayloadAction

* fix(anima): correct euler_a math — variance-preserving noise mix, not biased Euler

* revert(anima): remove euler_a scheduler — quality not worth the complexity

* chore(anima): apply ruff format (trim trailing blank lines)

* feat(rectified-flow): add order-1 ER-SDE stepper for rectified flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(rectified-flow): document lambda_next>0 precondition; tighten terminal-step test

Address code review feedback on order-1 ER-SDE stepper:
- Add docstring preconditions to integral helpers noting the logarithmic
  singularity at lam=0 (callers must guard sigma_next>0).
- Tighten terminal-step test from atol=1e-5 to torch.equal — the algebra
  is exact when sigma_next=0, not approximate.

* feat(rectified-flow): add 2nd-order Taylor extension to ER-SDE stepper

* test(rectified-flow): tighten Task 2 state-mutation test with value assertion

Address code review feedback on the 2nd-order Taylor extension tests:
- Assert state.old_d_x0 equals the analytically-computed d_x0 = 0.2v / (1.5 - 4.0)
  rather than just checking it's non-None.
- Document that x_t is intentionally re-used across calls (state threading
  test, not trajectory correctness).
- Document the order-2 correction coefficient magnitude that justifies the
  atol=1e-3 threshold in the engagement test.

* feat(rectified-flow): add 3rd-order Taylor extension to ER-SDE stepper

* docs(rectified-flow): document have_two_back invariant + order-3 test margin

Address code review feedback on Task 3:
- Comment why have_two_back checks both old_d_x0 and sigma_prev_prev
  (the sigma~=1 boundary path can break the joint invariant).
- Document the analytically verified ~0.0004 per-element correction
  magnitude that justifies the atol=1e-3 threshold in the order-3 test.

* feat(anima): register er_sde scheduler choice

* docs(anima): document custom-code-path scheduler convention; tighten test

Address code review feedback on Task 4:
- Add an in-file comment above ANIMA_SCHEDULER_MAP explaining the
  convention: schedulers with custom code paths (er_sde) live in the
  Literal+labels only, not the map.
- Hoist `import typing` to module-level in test_anima_schedulers.py
  (was inline-imported in two test functions).
- Pin the er_sde label value (== "ER-SDE"), not just key existence.

* feat(anima): wire er_sde scheduler into denoise loop

* docs(anima): document float32 noise dtype and sigma_next/sigma_prev naming

Address code review feedback on Task 5:
- Comment explaining why fresh_noise is float32 (matches er_sde_rf_step's
  dtype contract with x_t.to(float32)).
- Bridging comment at the inpaint extension call clarifying that
  sigma_next here means the same thing as sigma_prev in the Euler branch
  and the AnimaInpaintExtension API.

* chore(anima): bump anima_denoise to v1.4.0 and regen schema

* feat(frontend): expose er_sde scheduler in dropdown and metadata recall

Address code review on Task 6 — er_sde was registered in the OpenAPI
schema but missing from the frontend's own Zod enums and Redux
PayloadAction types, so:
- The combobox dropdown didn't list it.
- setAnimaScheduler('er_sde') would fail TypeScript at the call site.
- Metadata recall for er_sde-generated images would silently no-op
  (the scheduler value couldn't pass zParameterScheduler validation).

Changes:
- Add er_sde to zAnimaSchedulerField (the per-Anima Zod enum).
- Add er_sde to the animaScheduler state-shape Zod enum.
- Widen setAnimaScheduler's PayloadAction union.
- Add ER-SDE option to the ParamAnimaScheduler combobox.
- Make the metadata Scheduler handler accept ParameterAnimaScheduler too,
  with a fallback parse and a narrowing guard before the SD/SDXL dispatch.

* fix(rectified-flow): guard 2nd-order branch against sigma_prev_curr=1.0

When step 0 goes through the sigma_curr=1 closed-form limit branch it
writes state.sigma_prev_curr=1.0. On step 1, have_one_back was True and
the 2nd-order path called _lambda(1.0) = 1.0/(1.0-1.0), crashing with
ZeroDivisionError in every real denoise run.

Fix: extend the have_one_back guard to require that sigma_prev_curr is
more than _SIGMA_ONE_TOLERANCE below 1.0. The finite-difference derivative
across the limit step is not meaningful, so skipping the 2nd-order term on
that transition is correct. Order-3 is already gated behind old_d_x0 being
set, which this path never sets, so no additional guard is needed there.

Adds a regression test that runs the full sigma=1.0->0.9->0.7->0.0
sequence and asserts no ZeroDivisionError and all-finite output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(scheduler): add ERSDEScheduler available to SD/SDXL

ER-SDE solver (Cui et al., arXiv:2309.06169) usable across SD/SDXL
(VP-SDE) and rectified-flow models. Anima migration follows in
subsequent commits.

- ERSDEScheduler(SchedulerMixin, ConfigMixin) with prediction_type
  (epsilon | v_prediction | flow_prediction), use_flow_sigmas,
  solver_order (1/2/3 with auto-warmup), and stochastic toggle
- set_timesteps(sigmas=) for pre-shifted Anima/FLUX/Z-Image schedules
- Closed-form limit at sigma=1 in flow mode
- Unit tests + VP smoke + 5/5 Anima parity vs er_sde_rf_step
  (worst delta 5.137e-07)
- Frontend wiring: zSchedulerField, SCHEDULER_OPTIONS, OpenAPI regen
- parsing.tsx cleanup: removes the AnyScheduler widening since
  er_sde is now a first-class general scheduler

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(anima): wire ERSDEScheduler into ANIMA_SCHEDULER_MAP

Adds er_sde to the standard scheduler dispatch map with rectified-flow
kwargs (flow_prediction, use_flow_sigmas=True, flow_shift=3.0,
solver_order=3, stochastic=True). Anima still routes through the
legacy elif is_er_sde: branch — that's removed in a follow-up commit.
This is the additive prerequisite that lets the cutover happen without
a window where Anima can't use ER-SDE.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(anima): add ER-SDE dispatch integration tests

Verifies the ANIMA_SCHEDULER_MAP['er_sde'] entry instantiates correctly,
accepts pre-shifted sigmas via set_timesteps(sigmas=...), and resets
multistep state. Catches wiring regressions that the algorithm-level
parity test cannot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(anima): remove elif is_er_sde branch, dispatch through ANIMA_SCHEDULER_MAP

Anima ER-SDE now flows through the same standard scheduler path as
dpmpp_2m_sde — pre-shifted sigmas via scheduler.set_timesteps(sigmas=...),
inpaint extension via inpaint_extension.merge_intermediate_latents_with_init_latents,
step_callback per-step. The custom code path was the only thing keeping
ER-SDE off the universal pipeline.

Bumps invocation version to 1.5.0.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(er_sde): mark module as internal reference and parity oracle

ERSDEScheduler is now the production code path. er_sde_rf_step is kept
as the comparison oracle for the scheduler's parity test, and as a
self-contained mathematical reference for the rectified-flow algebra.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(rectified-flow): remove er_sde.py reference helper

ERSDEScheduler is the production code path. The pure-function helper
was retained as a parity oracle but YAGNI — keeping ~200 lines of code
purely as a regression net for hypothetical future drift isn't worth
the maintenance signal it generates.

Removes:
- invokeai/backend/rectified_flow/er_sde.py
- tests/backend/rectified_flow/test_er_sde.py
- tests/backend/rectified_flow/test_er_sde_scheduler_anima_parity.py

ERSDEScheduler's own tests (test_er_sde_scheduler.py) remain — they
exercise both VP-SDE and rectified-flow paths directly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(schema): restore forward slashes in @default cache dir paths

Windows-side typegen run flipped these to backslashes. Restore the
canonical forward-slash form to match upstream.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* style: apply ruff lint and format to ER-SDE files

Sort imports + format per project ruff config (line-length 120).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(state): use zParameterAnimaScheduler in state shape

The animaScheduler field inlined its enum (originally to add er_sde
when the shared schema didn't have it yet). Now that zAnimaSchedulerField
already includes er_sde, reference the shared zParameterAnimaScheduler
to match the pattern used by scheduler/fluxScheduler/zImageScheduler.
Drops the redundant .default('euler') — initial value comes from
getInitialParamsState.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* style(types): sort imports per simple-import-sort

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(anima): honour clipped sigma schedule for DPM++ img2img/inpaint

DPMSolverMultistepScheduler doesn't accept sigmas= in diffusers 0.35.1,
so the fallback previously called set_timesteps(num_inference_steps=total_steps)
which regenerated a full schedule from sigma_max, ignoring denoising_start/end.

When the scheduler supports set_begin_index, call set_timesteps with the
full step count and offset into it, so the internal flow_shift applies
correctly and denoising starts at the right sigma.

Also fixes the inpaint sigma_prev lookup and the timestep loop to use the
same offset, and corrects the false parity-test reference in the ER-SDE
dispatch test docstring.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: apply ruff format to anima_denoise dispatch block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(anima): extract scheduler driver and fix Heun progress/inpaint bugs

Encapsulate per-scheduler dispatch quirks (sigmas= vs num_inference_steps=,
Heun's doubled-array index, set_begin_index path) in AnimaSchedulerDriver
and tighten two latent bugs in the Heun path of anima_denoise:

* Heun's terminal first-order step never reported a user-step completion,
  so progress capped at N-1 of N. The driver now flags it via sigma_prev==0,
  and the <= total_steps clamp that papered over the off-by-one is gone.
* The inpaint mix ran after every Heun half-step, corrupting the second-order
  corrector's input (RectifiedFlowInpaintExtension's docstring says it should
  be called after each denoising step — i.e. once per user step). Mix is now
  gated on completes_user_step, which is unconditionally True for non-Heun.

Also: Heun shift kwarg switched to ANIMA_SHIFT (its set_timesteps doesn't
accept sigmas=, so it builds its own internal schedule); narrative comments
in scheduler_driver and er_sde_scheduler trimmed; new tests covering driver
iteration counts, terminal sigma_prev, seed determinism, and the begin_index
fallback for clipped DPM++ schedules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: apply ruff lint and format to anima scheduler driver

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: re-run after flaky token-expiration test

The 1-second JWT token-expiration test in test_token_service.py is timing
sensitive — passes locally on retry. Empty commit to retrigger CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Your Name <you@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jonathan <34005131+JPPhoto@users.noreply.github.com>
2026-05-08 02:59:23 +00:00
..
2025-03-26 12:55:10 +11:00
2024-07-23 18:04:33 +03:00
2024-07-22 22:17:29 +03:00