tensor_utils

Broadcasting, reshaping and interpolation helpers for batched tensors.

Algan’s animation state is uniformly shaped [time, mob, ...], and almost every operation has to line up operands that disagree about how many batch dimensions they carry. This module is the vocabulary for doing that: broadcast* for aligning operands, unsqueeze_* / add_dummy_dims_* for inserting axes on either side, squish / unsquish for flattening and restoring axis pairs, and cast_to_tensor for accepting the Python lists, floats and tuples a user naturally writes.

interpolate is the blend used when materializing an animated attribute between two recorded states.

These are internal helpers and deliberately excluded from from algan import *, whose names are chosen not to collide with a user’s own.

Functions

add_dummy_dims_left(x, y)[source]
add_dummy_dims_right(x, y)[source]
broadcast(x, y, ignored_dims)[source]
Parameters:

ignored_dims (list[int])

broadcast_all(xs, ignored_dims=())[source]
broadcast_both(x, y, ignored_dims)[source]
Parameters:

ignored_dims (list[int])

broadcast_both_left(x, y, ignored_dims)[source]
Parameters:

ignored_dims (list[int])

broadcast_cross_product(x, y, dim=-1)[source]
broadcast_gather(src, dim, ind, keepdim=True, out=None, **kwargs)[source]
Parameters:

dim (int)

broadcast_interleave(x, counts, inds, dim=-2)[source]
broadcast_scatter(input_tensor, dim, ind, src, **kwargs)[source]
cast_to_direction(name, x)[source]

Cast a user-supplied 3-D vector argument to a [1, N, 3] tensor.

Like cast_to_tensor(), but rejects anything whose last dimension is not 3. A bare scalar casts to shape [1, 1, 1], which broadcasts across all three axes rather than failing, so without this mob.move(1) moves diagonally to (1, 1, 1) instead of telling the user it wanted a vector.

Parameters:
  • name – The user-facing parameter name, used in the error message.

  • x – The value to cast.

Raises:

AlganConfigurationError – If x is not a 3-D vector.

cast_to_tensor(x)[source]

Converts scalars or lists of scalars into tensors, and combines lists of tensors into a single tensor. All other input types are returned unchanged. Returned tensors are always of shape [1,N,D] where D is dimension and N is the number of tensors combined.

cast_to_tensor_single(x)[source]
concat_dicts(kwargs)[source]

Concatenates a list of dicts sharing the same keys, the resulting dictionary has the same keys and concatenated values.

dot_product(x, y, dim=-1, keepdim=True, out=None)[source]
dot_product_in_place(x, y, dim=-1)[source]
expand_as_left(x, y, offset=0)[source]
Parameters:

offset (int)

expand_as_right(x, y, offset=0)[source]
Parameters:

offset (int)

implements(torch_function)[source]

Register a torch function override for ScalarTensor

interpolate(x, y, a)[source]
make_grid(height, width=None, min_coord=-1, max_coord=1, min_coord2=None, max_coord2=None)[source]
mean(xs)[source]
mid_point(x, dim=-1, keepdim=True)[source]
offset(x)[source]
pack_tensor(x, packing)[source]
packed_reorder(x, counts, ids)[source]
pad_dim_left(x, num_dims)[source]
pad_dim_right(x, num_dims)[source]
pad_dims(xs, unsqueeze_dim=-2)[source]
reduce_max_score(x, scores, dim=-1)[source]
reject_non_finite(name, value, given=None)[source]

Raise if value holds a NaN or an infinity.

A NaN reaching an animatable attribute is not caught anywhere downstream: it propagates through the basis, the projection and the rasterizer, and the symptom is a black frame (or a mob that silently disappears) with nothing in it pointing back at the line that wrote it. Checked once, where the value is authored, rather than per replayed frame.

Parameters:
  • name – The user-facing parameter or attribute name, used in the message.

  • value – The already-cast tensor to check. Non-tensors pass through.

  • given – What the user actually wrote, when it reads better than the tensor.

Return type:

The value, unchanged, so this can wrap a cast.

Raises:

AlganConfigurationError – If any element is NaN or infinite.

reject_outside_unit_interval(name, value, given=None)[source]

Raise if value falls outside the closed unit interval [0, 1].

For the attributes documented as fractions – opacity above all – where a value outside the interval has no meaning the renderer could honour. It is not clamped silently: opacity=1.5 and opacity=-0.2 are authoring mistakes, and the frame they produce looks like a bug in Algan rather than a bug in the script.

Only for values that are set. A value that is scaled stays unchecked on purpose, on the same reasoning that leaves scale(0) and a negative scale legal: a multiply is a transform, and the interval is a property of what the attribute means, not of every arithmetic step towards it.

Non-finite values are rejected first, by reject_non_finite(), so a NaN reports as a NaN rather than as an out-of-range value.

Parameters:
  • name – The user-facing parameter or attribute name, used in the message.

  • value – The value to check. A tensor is checked element-wise; a Python number is checked directly, so the plain set_opacity(1.5) spelling is caught as well as the tensor one. Anything else passes through.

  • given – What the user actually wrote, when it reads better than the tensor.

Return type:

The value, unchanged, so this can wrap a cast.

Raises:

AlganConfigurationError – If any element is NaN, infinite, below 0 or above 1.

robust_concat(xs)[source]

Concatenates multiple tensors together while broadcasting as necessary to ensure shapes match.

shuffle(x)[source]
squish(x, start=0, end=1)[source]

Flatten dimensions start through end (inclusive) of x into one.

E.g. squish(x, 1, 2) reshapes [A, B, C, D] to [A, B*C, D]. The inverse of unsquish().

Parameters:
  • start (int)

  • end (int)

texture_u8_provenance(texture)[source]

Whether a [..., 5] color texture is exactly 8-bit with zero glow.

True iff every channel value is exactly k / 255 for an integer k in [0, 255] (the values get_image() produces from an 8-bit file) AND the glow channel (index 3) is identically zero. Proved by the round trip itself – round(x * 255) / 255 == x elementwise in f32 – so a map this accepts quantizes to u8 and back bit-identically, which is the admission rule for texture_u8_storage’s packed color-map layout.

Checked ONCE at authoring (the color_texture setter, a mesh constructor) rather than at the merge: the merge runs on the prefetch worker, where an elementwise probe’s device sync waits out the whole queued chunk. NaNs fail the round trip, so they fail the check.

unpack_tensor(x, packing)[source]
unsqueeze_dims(x, y, insert_dim=0)[source]
unsqueeze_left(x, y, offset=0)[source]
Parameters:

offset (int)

unsqueeze_pack_tensors(xs, packing)[source]
unsqueeze_right(x, y, offset=0)[source]
Parameters:

offset (int)

unsqueeze_until_dim(x, dim, insert_dim=0)[source]
unsquish(x, dim=0, factor=None)[source]

Split dimension dim of x into two dimensions.

factor sets the size of the second new dimension (dim becomes [size/factor, factor]); a negative factor sets the size of the first instead ([-factor, size/-factor]); None splits into a square ([sqrt(size), sqrt(size)]). The inverse of squish().

Parameters:
  • dim (int)

  • factor (int | None)