ComfyUI Node

TF Sweep Edit

The difference between a batch button and an experiment

By KorayUlusan·Created 3 days ago·Updated about 21 hours ago· 1
TF Sweep Edit
  • levels
  • target_tokens
  • source_tokens
  • source_levels
  • regions
  • pipeline
  • report
  • sheet
  • levels
axis
values1,2,3,4
level2
seed592
strength1.00
source_mode
baselinetrue
decodetrue
arm_limit12
output_arm0
sheet_layout
size384

The question this node exists to answer is the one every edit session eventually asks: was that change my edit, or just the seed? A single before/after pair can't tell you - re-sampling from an edited canvas is still sampling. TF Sweep Edit runs one edit once per value of a single axis - a seed list, a set of levels, or a strength ramp - with everything else pinned, and tabulates the results. That's what makes it an experiment rather than a loop: no two arms differ in more than one way.

The design that earns the word "experiment" is the per-arm baseline. Every arm gets the same trajectory resumed at the same level with the same seed and no edit, and each arm is measured against its own baseline. Without that, a seed sweep mostly measures the seed - "the image changed a lot" becomes indistinguishable from "the seed changed a lot." baseline (on by default) is what buys you the honest number; turning it off folds the resume seed into every figure, which the tooltip admits is only defensible for a strength sweep. Cost scales honestly: two re-samples and one decode per arm, so four arms is a few seconds and forty is a coffee break.

Inputs break into the edit itself and the sweep axis. axis is the one thing that varies - seed, level (l*), or strength - and values supplies it as comma/space-separated numbers with a-b inclusive ranges (1,2,3,4 is the default; duplicates are dropped). Everything else is pinned to its widget below: level, seed, strength, source_mode, target_tokens, source_tokens. arm_limit (default 12) refuses to start if values expands past it - a genuine guard against a mistyped 0-1000 pinning your GPU for an hour, not a real ceiling. decode (on) renders each arm's final level for the contact sheet; off uses free PCA tiles instead and the table is unaffected. output_arm names which arm leaves on the levels output so you can save or compare one result.

Wire a TF Region Map into regions and the node sweeps a shape edit instead - target_tokens are handed to the region named by source_tokens. One constraint follows from geometry: a region map describes a single level, so only seed and strength are available as axes and level is refused with that reason. Sweeping l* is the one axis that can't hold everything fixed - a selection snapped to one level's regions isn't a whole region at another - so the node keeps the token set fixed, which is the only sane meaning of "the same edit at every level," and says so in the report rather than silently doing something else.

The number to actually read is the spread across arms: mean pairwise cosine distance at the final level. Near zero means the edit decides the outcome, so a single-seed result was trustworthy. Large means the seed does, and it wasn't. The report output is the whole table, and the sheet output is the contact sheet with the no-edit baseline first - both exist to feed the save nodes, because a sweep's numbers outlive the tab.

Install

Pack-standard: Manager → search Trajectory Forcing, or git clone https://github.com/korayulusan/ComfyUI-TrajectoryForcing into custom_nodes/. JAX inside ComfyUI needs its own Python 3.11 env on CUDA 12; requirements.txt is empty on purpose, install.py adds the JAX stack or declines with a reason (normal), and README's env/setup.sh is the fallback. Health check:

cd ComfyUI/custom_nodes/ComfyUI-TrajectoryForcing
python -m tf_nodes.doctor

A sweep is only as meaningful as its inputs are pinned - and the pack's own example workflow 05 is exactly this graph, ready to load and run if you want to see the spread before you trust a single-seed conclusion.

CategoryTrajectoryForcing/edit

Inputs (18)

NameTypeDefaultDescription
levelsTF_LEVELSThe unedited trajectory. Every arm starts from this one, so the arms differ only in the swept value.
target_tokensTF_TOKENSTokens that receive the new feature.
source_tokensTF_TOKENSTokens the feature is taken from.
axisCOMBOThe one thing that varies between arms. Everything else is pinned to its widget below.
valuesSTRING1,2,3,4Values for the axis: numbers separated by commas or spaces, with 'a-b' for an inclusive range. Duplicates are dropped.
levelINT20–3l*, the level being edited. Pinned for every arm unless 'level (l*)' is the axis, in which case 'values' supplies it.
seedINT5920–18446744073709550000Sampling seed. Re-sampling with the same seed and class reproduces a trajectory exactly.
strengthFLOAT1.000–1Pinned for every arm unless 'strength' is the axis. 1.0 replaces outright, which is the paper's edit.
source_modeCOMBOAs TF Feature Edit. The source feature is read from the arm's own level, so a level sweep re-reads it at each one.
baselineBOOLEANtrueResume each arm a second time without the edit and measure against that. Off, arms are measured against the input trajectory instead, which folds the resume seed into every number -- cheaper, and only honest for a strength sweep.
decodeBOOLEANtrueDecode each arm's final level to RGB for the contact sheet. Off, the sheet uses PCA tiles instead, which costs nothing; the table is unaffected either way.
arm_limitINT121–64Refuse to start if 'values' expands past this. A guard against a mistyped range holding the GPU, not a real ceiling.
output_armINT00–63Which arm leaves on the 'levels' output, counting from 0, for feeding TF Save Levels or TF Compare Levels.
sheet_layoutCOMBO'contact sheet' stitches the baseline and every arm into one image -- a row up to six, a near-square grid beyond -- so they can be seen at once. 'separate frames' returns a batch instead, which ComfyUI shows one at a time but which SaveImage can write as one file each.
sizeINT384128–1024
source_levelsoptTF_LEVELSTake the source feature from a different trajectory. Unwired means the same one. Feature edits only.
regionsoptTF_REGIONSWire a TF Region Map to sweep a *shape* edit instead: 'target_tokens' are handed over to the region named by 'source_tokens', taking that whole region's mean feature. The map belongs to one level, so only the seed and strength axes are available.
pipelineoptTF_PIPELINEUsually unnecessary: the trajectory carries the pipeline that made it. Wire it for a trajectory from TF Load Levels.

Outputs (3)

NameTypeDescription
reportSTRING
sheetIMAGEContact sheet: the no-edit baseline first, then one frame per arm.
levelsTF_LEVELSThe arm named by 'output_arm', already resumed.