Files
2026-10-22 09:52:15 +02:00

646 lines
40 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# More Math
Adds math nodes for numbers and types which do not need it. Inspired by the `was_extras` node.
There is workflow folder containing examples. It will expand.
The most important thing is that last part of input must be an expression. Node uses it to output result. It can be constant, function call, variable or mixture of these. No loops, branching using if. Branches, loops, ... can be before the final expression.
## Quickstart
1. Install [ComfyUI](https://docs.comfy.org/get_started).
1. Clone this repository into `ComfyUI/custom_nodes`.
1. open command prompt/terminal/bash in your comfy folder
1. activate environment `./venv/Scripts/activate`
1. go to more_math folder `cd ./custom_nodes/more_math/`
1. install requirements `pip install -r requirements.txt`
1. Restart ComfyUI.
You can also get the node from comfy manager under the name of More math.
## Features
- Functions and variables in math expressions.
- Conversion between `INT` and `FLOAT`, `INT` and `BOOLEAN`, and `AUDIO` and `IMAGE`
- image/audio conversion encodes frequency content into RGB channels.
- Node support for `FLOAT`, `STRING`, `CONDITIONING`, `LATENT`, `IMAGE`, `MASK`, `NOISE`, `AUDIO`, `VIDEO`, `MODEL`, `CLIP`, `VAE`, `SIGMAS`, and `GUIDER`.
- Vector math with list literals `[v1, v2, ...]` and mixed operations between lists, scalars, and tensors.
- Custom functions `funcname(variable, variable, ...)->expression;`
- can be used later in expressions and in other custom functions,
- shadowing built-in functions is not supported,
- recursion is allowed, so be careful.
- Custom variables `varname=expression;`
- can be used later in the same expression chain,
- compound assignments are supported: `+=`, `-=`, `*=`, `/=`, `%=`.
- Indexed assignment `a[i, j, ...] = expression;`
- the first write to a tensor copies it, so node inputs are never changed in place,
- supports multidimensional tensors and nested lists,
- scalar values and 1-element tensors/lists fill the selected slice,
- leading singleton dimensions are squeezed when needed to match target rank.
- python-like slicing
- `a[start:end]` or `a[start:end:step]` for 1D slices,
- `a[start1:end1, start2:end2, ...]` for N-D slices (step can also be specified),
- negative indices count from the end of the dimension.
- json-like dictionary literals `{key1: value1, key2: value2, ...}` for structured data. Keys can be strings or numbers or variables (variable will be evaluated to its value), values can be any expression. Indexing using [].
- Control flow:
- `if/else`
- `while`
- `for`
- blocks `{ ... }`
- `return`
- `break`
- `continue`
- Stack support shared across evaluations through stack connections.
- useful for `GuiderMath` state between steps,
- with no stack connected, every run starts with an empty stack.
- Comments:
- line comments `# ...`
- block comments `/* ... */`
- When a tensor is expected, lists can often be used instead and will be promoted automatically.
### Control Flow Statements
- **Conditions** (`if`, `while`, ternary) are true when the value, or any of its elements, is non-zero. On a tensor the ternary picks per element instead.
- **If/Else**: `if (condition) statement [else statement]`
- **While Loops**: `while (condition) statement`
- **Blocks**: `{ statement1; statement2; ... }`
- New variables defined in blocks are isolated and don't leak to outer scope
- Modifications to existing variables persist to outer scope
- **Return Statements**: `return [expression];`
- Returns from the current function; at top level it ends the script with that value
- **For Loops**: `for (variable in expression) statement`
- Iterates over list elements
- Iterates over tensor elements along dimension `0`
- Scalars are treated as one-item iterables
- **Break/Continue**: `break;`, `continue;`
- Control loop execution (works in `while` and `for` loops)
- Only affect loops in the same function; using them outside a loop is an error
- Scripts can be cancelled with ComfyUI's interrupt, even inside endless loops or recursion.
## Operators
- Math: `+`, `-`, `*`, `/`, `%`, `^`, `|x|` (abs / norm-style magnitude)
- Assignment: `=`, `+=`, `-=`, `*=`, `/=`, `%=`
- also works on indexed targets, for example `a[i] += 1;` or `a[i, j] = expression;`.
- Boolean: `<`, `<=`, `>`, `>=`, `==`, `!=`
(`false = 0.0`, `true = 1.0`)
- Ternary: `condition ? a : b`
- Constants: `pi`/`PI`, `e`/`E` (case-insensitive).
- `none` / `None` / `null` / `NULL`: empty/no-value literal.
- Bitwise shifts: `<<`, `>>`
- Indexing: `x[i]` or `x[i, j, ...]`
- works on tensors, nested lists, and strings,
- supports scalar, tensor, and list indices.
- Lists: `[v1, v2, ...]`
- vector math is supported,
- lists can broadcast across tensor operations in many cases,
- lists can be used for color triples, coordinates, kernel sizes, and permutations.
- **Length Mismatch Handling**: All math nodes (except Model, Clip, Vae which default to broadcast) include a `length_mismatch` option to handle inputs with different batch sizes, sample counts, or list lengths. The target length is determined by the **maximum length** among all provided inputs (`a`, `b`, `c`, `d`).
- `do nothing`: dones **no validation** on input
- `tile`: Repeats shorter inputs to match the maximum length.
- `error` (Default): Raises a `ValueError` if any input lengths differ.
- `pad`: Shorter inputs are padded with zeros to match the maximum length.
## Functions
### 1) Core Math
#### 1.1 Elementary & Numeric
- `abs(x)` or `|x|`: absolute value; tensor/list inputs are handled element-wise.
- `sqrt(x)`: square root.
- `ln(x)`: natural logarithm.
- `log(x)`: base-10 logarithm.
- `log2(x)`: base-2 logarithm.
- `exp(x)`: exponential `e^x`.
- `log1p(x)`: numerically stable `ln(1 + x)`.
- `expm1(x)`: numerically stable `exp(x) - 1`.
- `logaddexp(x, y)`: numerically stable `ln(exp(x) + exp(y))`, element-wise.
- `sinc(x)`: normalized sinc, `sin(πx)/(πx)`; returns `1` at zero.
- `cbrt(x)`: real cube root, including for negative values.
- `reciprocal(x)`: element-wise `1 / x`.
- `isnan(x)`: per-element NaN test.
- `isinf(x)`: per-element infinity test.
- `isfinite(x)`: per-element finite-value test.
- `isclose(a, b, [rtol], [atol])`: elementwise approximate comparison.
- `allclose(a, b, [rtol], [atol])`: returns `1.0` when all values are approximately equal, otherwise `0.0`.
- `pow(x, y)`: power `x^y`.
- `floor(x)`: round down.
- `ceil(x)`: round up.
- `round(x)`: round to nearest integer.
- `fract(x)`: fractional part (`x - floor(x)`).
- `sign(x)`: sign (`-1`, `0`, `1`).
- `gamma(x)`: gamma function.
- `clamp(x, min, max)`: clamp to interval.
- `step(x, edge)`: returns `1` when `x >= edge`, otherwise `0`.
- `dist(x1, y1, x2, y2)` / `distance`: Euclidean distance between 2D points.
- `hypot(x, y)`: element-wise Euclidean distance from the origin, computed robustly as `sqrt(x*x + y*y)`.
#### 1.2 Trigonometric
- `sin(x)`: sine in radians.
- `cos(x)`: cosine in radians.
- `tan(x)`: tangent in radians.
- `asin(x)`: inverse sine.
- `acos(x)`: inverse cosine.
- `atan(x)`: inverse tangent.
- `atan2(y, x)`: quadrant-aware inverse tangent.
- `degrees(x)`: convert radians to degrees.
- `radians(x)`: convert degrees to radians.
#### 1.3 Hyperbolic
- `sinh(x)`: hyperbolic sine.
- `cosh(x)`: hyperbolic cosine.
- `tanh(x)`: hyperbolic tangent.
- `asinh(x)`: inverse hyperbolic sine.
- `acosh(x)`: inverse hyperbolic cosine.
- `atanh(x)`: inverse hyperbolic tangent.
#### 1.4 Activation / ML
- `relu(x)`: `max(0, x)`.
- `silu(x)`: Sigmoid Linear Unit, equivalent to `x * sigm(x)`; applied element-wise.
- `gelu(x)`: Gaussian Error Linear Unit.
- `leaky_relu(x, [negative_slope])`: Leaky ReLU; `negative_slope` defaults to `0.01`.
- `softplus(x)`: smooth ReLU-like function.
- `softsign(x)`: bounded activation `x / (1 + abs(x))`.
- `sigm(x)`: sigmoid.
- `softmax(x, [dim])`: softmax normalization.
- `softmin(x, [dim])`: softmin normalization.
- `erf(x)`: error function.
- `erfc(x)`: complementary error function, `1 - erf(x)`, evaluated directly for better tail accuracy.
- `erfinv(x)`: inverse error function.
- `group_norm(x, groups, [weight], [bias], [eps])`: PyTorch GroupNorm for channel-first tensors shaped `[N, C, ...]`; `groups` must divide `C`, affine parameters are optional vectors of length `C`, and `eps` defaults to `1e-5`.
#### 1.5 Logical
- `logical_not(x)`: element-wise logical NOT; returns `1.0` where `x` is zero and `0.0` otherwise.
- `logical_and(x, y)`: element-wise logical AND; returns `1.0` where both values are non-zero and `0.0` otherwise.
- `logical_or(x, y)`: element-wise logical OR; returns `1.0` where either value is non-zero and `0.0` otherwise.
#### 1.6 Interpolation & Remap
- `lerp(a, b, t)`: linear interpolation.
- `slerp(a, b, t, [dim])`: spherical interpolation of vector directions with linearly interpolated magnitudes. Without `dim`, all elements form one vector. Set `dim` to select an axis of vector components; all other axes identify independent vectors, and `t` may be scalar or a tensor broadcast over those axes.
- With a list `t`, `lerp`, `slerp` and the `*_ease` functions return one result per time (as does `interp` with a list `x`); list inputs from an earlier call are paired with their time, so nested curves work. Join the results into a batch with `concat(..., 0)`, e.g. `concat(slerp(slerp(V0, V1, w), slerp(V2, V3, w), w), 0)`.
- `smoothstep(x, e0, e1)`: 3rd-order smooth transition.
- `smootherstep(x, e0, e1)`: 5th-order smoother transition.
- `cubic_ease(a, b, t)` / `cubic`: cubic easing interpolation.
- `sine_ease(a, b, t)` / `sine`: sine easing interpolation.
- `elastic_ease(a, b, t)` / `elastic`: elastic easing interpolation.
- `remap(v, i_min, i_max, o_min, o_max)`: map value from one interval to another.
- `interp(x, xs, ys, [mode])`: keyframe interpolation through the points `(xs, ys)`, holding the first/last value outside the keys. Modes: `"linear"` (default), `"smooth"` (eases in and out of every key), `"step"` (holds a value until the next key). `x` can be a tensor; when `x` is a number the values can be tensors, e.g. keyframe images. Example: `interp(frame, [0, 24, 48], [0.2, 1, 0.5])`.
---
### 2) Tensor, Stats & Data Ops
#### 2.1 Element-wise / Tensor Math
- `tmin(x, y)`: element-wise minimum.
- `tmax(x, y)`: element-wise maximum.
- `tnorm(x, [dim])`: L2 normalization along the last dimension by default, or along `dim`; list input returns a nested list and tensor input returns a tensor.
- `snorm(x, [dim])`: L2/Frobenius norm of a scalar, list, or tensor. With `dim`, reduces along one or more dimensions.
- `norm(x, [p], [dims])`: p-norm globally or along one or more dimensions; `p` defaults to `2`.
- `normalize(x, [p], [dim], [eps])`: normalize each vector along `dim` (default `-1`); defaults to L2 and clamps the divisor to `eps` (`1e-12`). List input returns a nested list; tensor input returns a tensor.
- `cossim(a, b)` / `cosine_similarity`: cosine similarity.
- `cov(x, y)`: sample covariance of the flattened inputs.
- `corr(x, y)` / `correlation`: Pearson correlation.
- `entropy(x)`: Shannon entropy of `softmax(x)` (`x` is treated as logits).
- `flip(x, dims)`: flip selected dimensions; `dims` can be a single dimension, list, or tensor of dimensions, so multiple axes can be flipped at once.
- `swap(tensor, dim, i1, i2)`: swap two indices along dimension `dim`.
#### 2.2 Reductions & Statistical Aggregates
- `sum(x, [dims])`: sum; optional `dims` can be a single dimension or a list of dimensions.
- `mean(x, [dims])`: mean globally or along dimensions.
- `std(x, [dims])`: sample standard deviation globally or along dimensions; returns `0` when a reduction contains fewer than two values.
- `var(x, [dims])`: sample variance globally or along dimensions; returns `0` when a reduction contains fewer than two values.
- `prod(x, [dims])`: product globally or along dimensions.
- `logsumexp(x, [dims])`: numerically stable `log(sum(exp(x)))`, globally or along dimensions.
- `median(x, [dim])`: median globally or along one dimension; without `dim`, reduces all elements.
- `mode(x, [dim])`: mode globally or along one dimension; without `dim`, reduces all elements.
- `quartile(x, k, [dim])` / `quartil`: quartile (`k` in `0..4`), globally or along one dimension.
- `percentile(x, p, [dim])` / `prcnt`: percentile (`p` in `0..100`), globally or along one dimension.
- `quantile(x, q, [dim])`: quantile (`q` in `0..1`), globally or along one dimension.
- `moment(x, a, k)`: k-th moment around center `a`.
- `any(x)`: `1.0` if any element is non-zero.
- `all(x)`: `1.0` if all elements are non-zero.
- `count(x)` / `length(x)` / `cnt(x)`: number of elements or length.
- `cummax(x, [dim])`: cumulative maximum; `dim` defaults to `0`.
- `cummin(x, [dim])`: cumulative minimum; `dim` defaults to `0`.
- `cumsum(x, [dim])`: cumulative sum; `dim` defaults to `0`.
- `cumprod(x, [dim])`: cumulative product; `dim` defaults to `0`.
- `smin(x, ...[, dims])`: scalar minimum across inputs; optionally reduce the first tensor/list input across `dims`.
- `smax(x, ...[, dims])`: scalar maximum across inputs; optionally reduce the first tensor/list input across `dims`.
- `weighted_mean(x, weights, [dims])`: computes `sum(x * weights) / sum(weights)` globally or along one or more dimensions; raises an error if a reduction's weights sum to zero.
#### 2.3 Sorting / Selection / Indices
- `sort(x, [descending], [dim])`: tensors are sorted along the specified dimension (`dim`); if `dim` is not specified, it defaults to the last dimension (`dim=-1`); lists use Python sorting.
- `argsort(x, [descending], [dim])`: returns indices along `dim` (defaults to last dimension); `descending` defaults to `false`.
- `argmin(x, [as_position], [dim])`: global minimum index, or indices along `dim`; `as_position` returns global coordinates and cannot be combined with `dim`.
- `argmax(x, [as_position], [dim])`: global maximum index, or indices along `dim`; `as_position` returns global coordinates and cannot be combined with `dim`.
- `topk(x, k, [dim])`: tensors keep the same shape and all non-top-k values are zeroed; defaults to the last dimension. Lists return the sorted top-k slice.
- `botk(x, k, [dim])`: same as `topk`, but for smallest values.
- `topk_ind(x, k, [dim])` / `topk_indices`: top-k indices along `dim`; without `dim`, uses global indices from `flatten()`.
- `botk_ind(x, k, [dim])` / `botk_indices`: bottom-k indices along `dim`; without `dim`, uses global indices from `flatten()`.
- `unique(x)`: sorted unique values for tensors and lists.
- `where(cond, a, b)`: tensor broadcasting for tensor inputs; recursive truthy selection for lists/scalars.
- `histogram(x, bins, min, max)` / `hist`: histogram counts from `flatten()` in the given interval
- `gather(tensor, dim, indices)`: gather values along `dim` using `indices`
- `scatter(tensor, dim, indices, src)`: scatter values from `src` along `dim` using `indices`
- `scatter_add(tensor, dim, indices, src)`: scatter-add values from `src` along `dim` using `indices`
- `nonzero(x)`: returns coordinate indices for non-zero elements.
- `one_hot(indices, classes)`: converts integer class indices into float32 one-hot vectors; indices must be in `0..classes-1`.
#### 2.4 Shape / Structure / Layout
- `shape(x)`: returns shape as a Python list.
- `flatten(x)`: flattens tensor or nested list.
- `reshape(tensor, shape)` / `rshp`: reshape with element-count validation.
- `squeeze(x, [dim])`: removes singleton dimensions; optional `dim` follows PyTorch semantics.
- `unsqueeze(x, dim)`: inserts a singleton dimension at `dim`.
- `transpose(x, dim0, dim1)` / `swapaxes`: swaps two dimensions.
- `permute(tensor, dims)` / `perm`: reorder dimensions.
- `crop(tensor, position, size)`: extract a region from tensor or string.
- `pad(tensor, padding)`: pad tensor.
- `overlay(base, overlay, offset, [opacity], [mode])`: overlay one value/tensor onto another. For tensors, `mode` applies a blend mode from `blend()` to the pasted region.
- `append(a, b)`: append or concatenate items, lists, and tensors.
- `repeat(x, count, [dims])`: repeat values; tensor `count` can be a scalar or a per-dimension list when `dims` is provided.
- `batch_shuffle(tensor, indices)` / `shuffle` / `select`: reorder along batch dimension.
- `concatenate(..., dim)` / `concat` / `cat`: concatenate tensors or lists; a single list of tensors, `concat(list, dim)`, concatenates its elements.
- `stack(tensors, [dim])` / `stack(x, ..., dim)`: stack a non-empty list of tensors, or separate tensors followed by `dim`, along a new dimension.
- `roll(tensor, shifts, [dim])`: circular shift.
- `tensor(shape, [value, [type]])`: create a filled tensor; `type` a tensor to copy its dtype to self when being created.
- `zeros(shape, [dtype_template])` / `ones(shape, [dtype_template])`: create zero- or one-filled tensors; an optional tensor supplies the output dtype.
- `eye(n, [dtype_template])`: create an `n`-by-`n` identity matrix; an optional tensor supplies the output dtype.
- `interpolate_linear(tensor, scale)`: linear interpolation-based resizing. Scale can be a single float or a list. When it is a list, it is interpreted as the target output size.
- `interpolate_area(tensor, scale)`: area interpolation-based resizing.
- `interpolate_nearest(tensor, scale)` / `interpolate_nearest_exact`: nearest neighbor interpolation-based resizing.
#### 2.5 Linear Algebra
- `dot(a, b)`: dot product after flattening.
- `outer(a, b)`: outer product of flattened inputs.
- `matmul(a, b)`: matrix multiplication.
- `cross(a, b)`: cross product; last dimension must be `3`.
- `trace(x)`: sum of the main diagonal of each square matrix.
- `det(x)`: determinant of each square matrix. Singular square matrices return `0`; non-square inputs or inputs with fewer than two dimensions raise an error because their determinant is undefined.
- `solve(A, b)`: solve the square linear system `A*x = b`; prefer this to computing `inv(A) @ b`.
- `matrix_rank(x, [tol])`: numerical rank of each matrix; optional `tol` is the absolute singular-value tolerance.
- `inv(x, [pseudo])`: matrix inverse; `pseudo` defaults to false. A singular or non-square matrix raises an error in ordinary mode. Set `pseudo` to a truthy value (for example, `1`) to compute the Moore–Penrose pseudoinverse, which also supports singular and rectangular matrices.
- `pinv(x)`: inverse permutation (`result[x[i]] = i`); this is not the matrix pseudoinverse, use `inv(x, 1)` for that.
- `singular_value_decomposition(x,[full_matrices])`/`svd`: singular value decomposition.
- `diagonal_matrix(x,shape,[offset],[dim1],[dim2])` / `diag`: create a diagonal matrix from a vector. The diagonal can be offset from the main diagonal using the `offset` parameter. The `dim1` and `dim2` parameters specify which dimensions to use for the diagonal.
#### 2.6 Mapping / Sampling
- `map(tensor, c1, ...)`: coordinate remapping via `grid_sample`.
- supports up to `3` coordinate inputs,
- uses sampling coordinates derived from the provided coordinate tensors or functions,
- intended for spatial remapping / resampling.
- `get_value(tensor, position)`: read value at an N-D position using flat offset math. Superseded by tensor[position] indexing.
#### 2.7 Losses / Image Metrics
- `mse(a, b, [reduction])`: mean squared error; reduction is `mean` (default), `sum`, or `none` (`none` returns element-wise values).
- `mae(a, b, [reduction])`: mean absolute error; reduction is `mean` (default), `sum`, or `none` (`none` returns element-wise values).
- `huber_loss(prediction, target, [delta], [reduction])`: Huber regression loss; `delta` defaults to `1` and reduction defaults to `mean` (`sum` and `none` are also supported).
- `cross_entropy(logits, target, [dim], [reduction])`: classification cross-entropy; `target` may contain class indices or probabilities matching `logits`. Class dimension defaults to `1` for multi-dimensional logits and `0` for a class vector; reduction defaults to `mean`.
- `psnr(a, b, [data_range])`: peak signal-to-noise ratio in decibels; `data_range` defaults to `1` and must be positive.
---
### 3) Imaging & Spatial Processing
#### 3.1 Filters & Convolution
- `blur(x, sigma, [auto_channels])` / `gaussian`: Gaussian blur using separable convolution. If `auto_channels` is truthy, tries orienting the input such that channel is in the direction of the filter; otherwise it expects the color dimension to be last.
- `edge(x, [kernel_size])`: Sobel-style edge detection.
- `ezconvolution(tensor, ...)` / `ezconv`: convolution with auto layout handling.
- `convolution(tensor, ...)` / `conv`: direct convolution. Expects [batch, channel, spatial...] layout.
#### 3.2 Mask Morphology
- `dilate(x, [kernel_size])`: dilation.
- `erode(x, [kernel_size])`: erosion.
- `morph_open(x, [kernel_size])`: opening = erosion followed by dilation.
- `morph_close(x, [kernel_size])`: closing = dilation followed by erosion.
- `distance_transform(mask, [threshold])`: exact Euclidean distance in pixels from each pixel inside the mask (above `threshold`, default `0.5`) to the nearest pixel outside it; outside pixels are `0`. Use `distance_transform(1 - mask)` for the distance outside the mask, e.g. for feathering.
#### 3.3 Blending
- `blend(base, top, mode, [opacity])`: blends `top` onto `base` like layers in an image editor; shapes broadcast. Modes: `normal`, `multiply`, `screen`, `overlay`, `soft_light`, `hard_light`, `darken`, `lighten`, `difference`, `exclusion`, `add`, `subtract`, `color_dodge`, `color_burn`. Use `overlay(..., mode)` to place a smaller layer at an offset.
---
### 4) Optical Flow
- `rife(img1, img2, [tiling_size, iterations, multi_scale])`: compute optical flow using a RAFT-based model.
- uses the first checkpoint in `models/optical_flow`; if there is none, torchvision's RAFT-large weights are downloaded once and saved there.
- `tiling_size` chunks the image into overlapping `tiling_size`x`tiling_size` tiles to conserve memory; defaults to `0` (auto-enables `1024`x`1024` tiling above 2MPx); a value between `0` and `1` is taken as a fraction of the resolution instead.
- `iterations` defaults to `12`.
- `multi_scale` (default `false`) also runs a low-resolution pass of the base image to help catch large movements.
- `motion_mask(flow)`: motion/occlusion mask from flow.
- `flow_to_image(flow)`: visualize flow as RGB.
- `flow_apply(image, flow)`: warp image by flow.
- `flow_mag(flow)` / `flow_magnitude`: flow vector magnitude (or anything else, uses first 2 positions of last dimension).
- `flow_ang(flow)` / `flow_angle`: flow vector angle in radians (`atan2(dy, dx)`). (or anything else, uses first 2 positions of last dimension)
---
### 5) Frequency Domain (FFT)
- `fft(x)`: fast Fourier transform across all dimensions.
- `ifft(x, [shape])`: inverse FFT.
- `rfft(x, [dim])`: real-input FFT along `dim` (defaults to the last dimension).
- `irfft(x, [n], [dim])`: inverse real FFT; `n` and `dim` are optional.
- `angle(x)`: phase angle of complex values.
---
### 6) Random & Noise
#### 6.1 Random Distributions
- All random generators are seeded and deterministic for a given seed.
- All use the current node shape by default (based on shape of input to the node), but an optional trailing `shape` argument can be provided to specify a different output shape.
- All use a `rand<dist>(...)` / `random_<distribution>(...)` naming convention, but the argument list before `[shape]` differs per distribution - see each entry below.
- `noise(seed, [shape])` / `randn` / `random_normal`: normal distribution. Same generator as in `Random Noise` node.
- `rand(seed, [shape])` / `randu` / `random_uniform`: uniform distribution.
- `rande(seed, lambda, [shape])` / `random_exponential`: exponential distribution.
- `randc(seed, median, sigma, [shape])` / `random_cauchy`: Cauchy distribution.
- `randln(seed, mean, std, [shape])` / `random_log_normal`: log-normal distribution.
- `randb(seed, probability, [shape])` / `random_bernoulli`: Bernoulli distribution.
- `randp(seed, lambda, [shape])` / `random_poisson`: Poisson distribution.
- `randg(seed, shape_param, scale, [shape])` / `random_gamma`: gamma distribution.
- `randbeta(seed, alpha, beta, [shape])` / `random_beta`: beta distribution.
- `randl(seed, loc, scale, [shape])` / `random_laplace`: Laplace distribution.
- `randgumbel(seed, loc, scale, [shape])` / `random_gumbel`: Gumbel distribution.
- `randw(seed, scale, concentration, [shape])` / `random_weibull`: Weibull distribution.
- `randchi2(seed, df, [shape])` / `random_chi2`: chi-squared distribution.
- `randt(seed, df, [shape])` / `random_studentt`: Student’s t distribution.
- `pink_noise(seed, [shape])` / `pink`: deterministic pink noise with an approximately `1/f` power spectrum.
- `brown_noise(seed, [shape])` / `brown`: deterministic brown noise with an approximately `1/f^2` power spectrum.
- Pink and brown noise are normalized to zero mean and unit variance independently along the last dimension; shorter dimensions fall back to seeded white noise.
#### 6.2 Procedural Noise
All procedural noises return values in `0..1`.
- `perlin(seed, scale, [octaves, [offset, [shape]]])` / `perlin_noise`:
- smooth gradient noise,
- supports arbitrary dimensional grids.
- `voronoi(seed, scale, [jitter], [offset], [shape])` / `cellular` / `worley` / `voronoi_noise` / `cellular_noise`:
- cellular / Voronoi noise,
- `jitter` is clamped to `0..1`.
- `plasma(seed, scale, [octaves, [offset, [shape]]])` / `turbulence` / `plasma_noise`:
- high-frequency turbulence-style noise.
- `ridged(seed, scale, [octaves, [offset, [shape]]])` / `ridged_noise`:
- ridged multifractal noise with sharp peaks and mountain-like structures.
- `domain_warp(seed, scale, warp_scale, warp_strength, [octaves, [warp_octaves, [offset, [shape]]]])` / `domain_warp_noise`:
- domain-warped noise for breaking up regular patterns and adding complex organic detail.
#### 6.3 Noise Schedules
Same schedules as ComfyUI's scheduler nodes, as a tensor of sigmas for the `SIGMAS` node.
- `sigm_karras(steps, sigma_min, sigma_max, [rho])`: Karras schedule, `rho` defaults to `7`; ends with a final `0`.
- `sigm_exponential(steps, sigma_min, sigma_max)`: evenly spaced in log sigma; ends with a final `0`.
- `sigm_polyexponential(steps, sigma_min, sigma_max, [rho])`: polynomial in log sigma, `rho` defaults to `1`; ends with a final `0`.
- `sigm_laplace(steps, sigma_min, sigma_max, [mu], [beta])`: Laplace schedule, `mu` defaults to `0` and `beta` to `0.5`; like ComfyUI's Laplace scheduler it has no final `0`.
---
### 7) Bitwise
#### 7.1 Shift Operators
- `a << b`: Bitwise left shift.
- `a >> b`: Bitwise right shift.
#### 7.2 Bitwise Functions
- `band(a, b)` / `bitwise_and`: bitwise AND.
- `bor(a, b)` / `bitwise_or`: bitwise OR.
- `bxor(a, b)` / `bitwise_xor`: bitwise XOR.
- `bnot(a)` / `bitwise_not`: bitwise NOT.
- `bitcount(a)` / `popcount` / `popcnt`: number of set bits.
---
### 8) Strings
- `upper(str)`: convert to uppercase.
- `lower(str)`: convert to lowercase.
- `trim(str)`: trim surrounding whitespace.
- `split(str, [delimiter])`: split string.
- `join(list, [separator])`: join list into string.
- `substring(str, start, [length])` / `substr`: substring extraction.
- `find(str, search)`: first match position.
- `replace(str, search, replacement)`: replace occurrences.
- `startswith(str, prefix)`: returns `1.0` if the string starts with `prefix`, otherwise `0.0`.
- `endswith(str, suffix)`: returns `1.0` if the string ends with `suffix`, otherwise `0.0`.
- `regex_search(str, pattern)`: position of the first match of the regular expression `pattern`, or `-1`.
- `regex_replace(str, pattern, replacement)`: replace every regular-expression match; `replacement` can refer to groups as `\1`.
- `to_json(value)` / `json_encode(value)`: encode dictionaries, lists, and tensors as JSON. Tensors become nested arrays.
- `from_json(text)` / `json_decode(text)`: decode JSON objects to dictionaries and arrays to lists. Tensor dtype and device are not preserved.
- `format(value, [spec])`: format numbers as text; `spec` is a number of decimals (`format(x, 2)`) or a Python format spec (`format(x, ".3e")`). Defaults to the shortest form, so `0.3` stays `"0.3"`. Lists and tensors are formatted per element.
---
### 9) Color
- `rgb_to_hsv(...)`: convert RGB to HSV.
- accepts packed tensor/list input or separate `r, g, b`,
- optional trailing boolean argument enables hue in degrees - the 2nd argument in packed mode (`rgb, [degrees]`), the 4th in separate mode (`r, g, b, [degrees]`).
- `hsv_to_rgb(...)`: convert HSV to RGB.
- accepts packed tensor/list input or separate `h, s, v`,
- optional trailing boolean argument treats hue as degrees - the 2nd argument in packed mode (`hsv, [degrees]`), the 4th in separate mode (`h, s, v, [degrees]`).
- `rgb_to_oklab(...)`: convert RGB to OKLab.
- accepts packed tensor/list input or separate `r, g, b`.
- `oklab_to_rgb(...)`: convert OKLab to RGB.
- accepts packed tensor/list input or separate `L, a, b`.
- `rgb_to_cielab(...)`: convert RGB to CIELAB.
- accepts packed tensor/list input or separate `r, g, b`.
- `cielab_to_rgb(...)`: convert CIELAB to RGB.
- accepts packed tensor/list input or separate `L, a, b`.
- `luminance(rgb)` / `luminance(r, g, b)`: Rec.709 luminance `0.2126 R + 0.7152 G + 0.0722 B`; the color dimension is removed.
- `grayscale(image)`: replaces R, G, B with their luminance, keeping the shape and any alpha channel.
- `int_to_rgb(value)`: convert packed integer color to RGB triplet.
- `rgb_to_int(...)`: convert RGB triplet to packed integer color.
---
### 10) Utility & Conversion
- `print(x)`: print value and return it.
- `print_shape(x)` / `pshp`: print shape and return value.
- `range(start, end, step)`: numeric sequence as a list.
- `linspace(start, end, count)`: evenly spaced sequence.
- `logspace(start, end, count, base)`: logarithmically spaced sequence.
- `nan_to_num(x, nan, posinf, neginf)` / `nvl`: replace `NaN` and infinities.
- `timestamp()` / `now`: current Unix timestamp.
- `int(x)`: convert to int32 or nested int values.
- `float(x)`: convert to float or nested float values.
- `as_nested(x)` / `as_nested_tensor`: convert list to a nested tensor. Usefull for video+audio models. Values do not need to be in the same or compatible shape.
___
### Generators
- `text_image(text, font, size, [max_width], [weight], [rotation_angle], [line_spacing], [italic], [underline])` - renders text to an 2D tensor
- `font` is a font file path or an installed font name; installed fonts are scanned once, so restart ComfyUI after installing new ones,
- if the requested weight or italic face is missing, bold and italic are synthesized from the regular face.
- `coordinates(shape, dim, [dtype])` / `coords` - generates a tensor whose values are the coordinates of each element along the specified dimension. dtype is copied from tensor at that position. Default is float32.
---
### 11) State / Stack
- `stack_push(id, value)`: push into stack slot.
- `stack_pop(id)`: pop from stack slot.
- `stack_get(id)`: read top value without removing it.
- `stack_clear(id)`: clear stack slot.
- `stack_has(id)`: check whether slot exists and is non-empty.
### 12) Dictionary
- `add_key(dict, key, value)` - add a key-value pair to a dictionary.
- `remove_key(dict, key)` / `rm_key` - remove a key-value pair from a dictionary.
- `keys(dict)` - returns a list of dictionary keys in insertion order.
## Variables
- **Common variables (except FLOAT)**:
- - `D{N}` - position in n-th dimension of tensor, for example `D0`, `D1`, `D2`
- - `S{N}` - size of n-th dimension of tensor, for example `S0`, `S1`, `S2`
- - `V{N}` - value input, for example `V0`, `V1`, `V2`
- - `V` - list of value inputs
- - `F{N}` - float input, for example `F0`, `F1`, `F2`
- - `F` - list of float inputs
- - `Fcnt` or `F_count`: number of float inputs
- - `Vcnt` or `V_count`: number of value inputs
- `depth`: Current recursion depth (0 at top level)
- **common inputs** (legacy):
- `a`, `b`, `c`, `d`
- **Extra floats** (legacy):
- `w`, `x`, `y`, `z`
- **INSIDE IFFT**
- `F` or `frequency_count` – frequency count (freq domain, iFFT only)
- `K` or `frequency` - isotropic frequency (Euclidean norm of indices, iFFT only)
- `Kx`, `Ky`, `K_dimN` - frequency index for specific dimension
- `Fx`, `Fy`, `F_dimN` - frequency count for specific dimension
- **IMAGE and LATENT**:
- `C` or `channel` - channel of image
- `X` - X position in image, origin in top-left
- `Y` - Y position in image, origin in top-left
- `W` or `width` - image width
- `H` or `height` - image height
- `B` or `batch` - batch index
- `T` or `batch_count` - number of batches
- `N` or `channel_count` - channel count
- **LATENT**
- `V{N}_{M}` - In case of nested tensors, takes the M-th tensor from N-th input. For example, `V0_1` is the second tensor from the first input.
- **MODEL, VAE, CLIP**:
- `V{N}_d` - difference against the original input (for example applied lora)
- `K` or `key` - name of current layer. For example, `proj_out.bias`, `diffusion_model.blocks.5.attn.out_proj.weight` or `txtfusion.layerwise_blocks.0.prenorm.scale`.
- `L` or `layer` - index of current layer
- `LC` or `layer_count` - number of layers
- **IMAGE KERNEL**: in `conv`/`convolution` or `ezconv`/`ezconvolution` function kernel input
- `kX`, `kY`, `kZ` (also lowercase `kx`, `ky`, `kz`) - position in kernel, centered at `0.0`; `kZ`/`kz` is only set for 3D kernels
- `kW`, `kernel_width` - kernel width
- `kH`, `kernel_height` - kernel height
- `kD`, `kernel_depth` - kernel depth
- **AUDIO**:
- `B` or `batch` - batch index
- `N` or `channel_count` - channel count
- `C` or `channel` - audio channel
- `S` or `sample` - current sample
- `T` or `sample_count` - audio length in samples
- `R` or `sample_rate` - sample rate
- **VIDEO**
- refer to `IMAGE and LATENT` for the visual part
- `batch` means `frame`
- `batch_count` means `frame_count`
- refer to `AUDIO` for sound part
- **NOISE**
- refer to `IMAGE and LATENT` for most variables
- `I` or `input_latent` - latent used as input to generate noise
- **GUIDER**
- refer to `IMAGE and LATENT`
- `sigma` - current sigma value
- `seed` - seed used for noise generation
- `steps` - total number of sampling steps
- `current_step` - current step index, `0..steps`
- `sample` - tensor input to guider or output from sampling
## SelectiveGuiderMathNode (hooking)
Adds support for hooking into specific layers/blocks of the model during guided diffusion.
Current implementation details:
- `hook_target` supports runtime filtering in node UI.
- `layer_x` is used as direct index match (`idx == layer_x`) for current hook context.
- For guiders with `original_conds`, base conditions are restored before reattaching hooks to avoid hook accumulation across reruns/interrupted runs.
- Active hook paths currently include:
- Attention override (`attn1` / `attn2` / `double_block_attn` / `single_block_attn` / `attn_unknown`)
- DiT block replace (`dit.double_block`, `dit.single_block`)
- UNet block patches (`input_block_patch`, `middle_patch`, `output_block_patch`)
- Timestep embedding start via `emb_patch` (`block_name="time_emb"`, `layer_x=0`)
- Whole-model edges via diffusion-model wrapper (`model_begin`, `model_end`)
### Side behavior (positive / negative)
When guider has `original_conds`, hooks are attached separately for:
- `positive`
- `negative`
Runtime variables expose side information:
- `cond_side`: `"positive" | "negative" | "mixed" | "unknown"`
- `cond_index`: `0 | 1 | -1`
- `is_positive`: `1.0` or `0.0`
- `is_negative`: `1.0` or `0.0`
### Variable reference (`SelectiveGuiderMathNode`)
The following variables are available in `Expression`.
| Variable | Type | Description |
|---|---|---|
| `inp` | tensor | Current tensor received by the active hook. |
| `sample` | tensor | Alias of `inp`. |
| `F0..Fn` | float/tensor | Individual float inputs from the node. |
| `F` | list/tensor | Collection of all float inputs. |
| `D0..Dn` | tensor | Per-dimension index tensors from `generate_dim_variables`. |
| `S0..Sn` | float | Per-dimension sizes from `generate_dim_variables`. |
| `hook_kind` | string | Active hook identifier: `attn1`, `attn2`, `double_block_attn`, `single_block_attn`, `attn_unknown`, `dit_block`, `unet_block`, `model_begin`, `model_end`, or `unknown`. |
| `hook_domain` | string | High-level domain: `attention`, `diffusion`, or `unknown`. |
| `attn_kind` | string | Attention kind: `attn1`, `attn2`, `double_block_attn`, `single_block_attn`, `attn_unknown`, or `none` outside attention hooks. |
| `transformer_index` | float | Attention sub-block index inside a UNet block (`-1` if unavailable). |
| `is_attn1` | float (0/1) | `1` when current hook is `attn1`, else `0`. |
| `is_attn2` | float (0/1) | `1` when current hook is `attn2`, else `0`. |
| `is_attn1_hook` | float (0/1) | `1` when `attn_kind=="attn1"`, else `0`. |
| `is_attn2_hook` | float (0/1) | `1` when `attn_kind=="attn2"`, else `0`. |
| `is_dit` | float (0/1) | `1` when current hook is DiT block hook, else `0`. |
| `is_unet_block` | float (0/1) | `1` when current hook is UNet block hook, else `0`. |
| `is_time_emb` | float (0/1) | `1` when current hook is timestep embedding entry (`block_name=="time_emb"`), else `0`. |
| `block_name` | string | Block/stage name (`input`, `middle`, `output`, `time_emb`, `model`, DiT block type, etc.). |
| `layer_id` | float | Numeric block/layer id used by current hook context. |
| `layer` | float | Alias of `layer_id`. |
| `i` | float | Alias of `layer_id`. |
| `layer_key` | string | Composite identifier for debug/filtering (for example `output.6.attn2.0`, `unet.time_emb.0`, `model.begin`). |
| `total_blocks` | float | Total blocks in stream if available, otherwise `-1`. |
| `has_qkv` | float (0/1) | `1` in attention hooks where `q/k/v` are valid; `0` in diffusion/block hooks. |
| `q` | tensor | Query tensor in attention hooks; fallback placeholder otherwise. |
| `k` | tensor | Key tensor in attention hooks; fallback placeholder otherwise. |
| `v` | tensor | Value tensor in attention hooks; fallback placeholder otherwise. |
| `heads` | float | Number of attention heads (attention hooks only, else `0`). |
| `dim_head` | float | Per-head channel size (`q.shape[-1] / heads`) when available. |
| `activations_shape` | list | Raw shape from transformer context. Empty list if unavailable. |
| `activation_b` | float | Batch dimension from `activations_shape[0]` (or `-1`). |
| `activation_c` | float | Channel dimension from `activations_shape[1]` (or `-1`). |
| `activation_h` | float | Height dimension from `activations_shape[2]` (or `-1`). |
| `activation_w` | float | Width dimension from `activations_shape[3]` (or `-1`). |
| `attn_mode` | string | Legacy compatibility field (default `unknown`). |
| `attention_relation` | string | Inferred semantic relation: `self`, `cross`, or `unknown`. |
| `is_self_attention` | float (0/1) | `1` when the active attention is self-attention. |
| `is_cross_attention` | float (0/1) | `1` when the active attention is cross-attention. |
| `has_context` | float (0/1) | `1` when attention context appears to be present. |
| `query_tokens` | float | Query sequence length. |
| `context_tokens` | float | Context sequence length. |
| `value_tokens` | float | Value sequence length. |
| `activation_rank` | float | Rank of `activations_shape`. |
| `activation_t` | float | Temporal dimension for video-like activations (`-1` if unavailable). |
#### Practical notes
- On SD1.x, repeated hits on the same `layer_id` are normal in attention because one UNet block can contain multiple transformer sub-blocks.
- Use `transformer_index` to target exactly one sub-block.
- For timestep-begin hooking use `layer_x=0` and filter by `block_name=="time_emb"` (or `layer_key=="unet.time_emb.0"`).
- For model-edge hooks filter by `hook_kind=="model_begin"` or `hook_kind=="model_end"`.
- For model-agnostic expressions, prefer guard variables: `has_qkv`, `is_dit`, `is_unet_block`, `is_attn1`, `is_attn2`.
- `attn2` is only a hook label, not guaranteed to mean real cross-attention.
- Use `is_cross_attention` only as an inferred relation from runtime metadata.
- Use `attention_relation` for semantic relation (`self`/`cross`) and `attn_kind` for hook-path classification.
- Selective guider math:
- `hook_target`: `all`, `dit_block`, `unet_block`, `attn1`, `attn2`,
`double_block_attn`, `single_block_attn`, `model_begin`, `model_end`