flopscope.

flopscope.numpy.symmetrize

fnp.symmetrize(data: 'np.ndarray', *, symmetry, mode: 'str' = 'reynolds-projection') -> 'SymmetricTensor'[flopscope source]

Make an array invariant under a permutation group.

Adapted from NumPy docs np.symmetrize

Areacore
Typecustom
NumPy Refnp.symmetrize
Cost
per-operation
Flopscope Context

Symmetrize onto a permutation group's invariant subspace. Cost depends on mode: mode='reynolds-projection' (default) is (|G|+1)*numel (|G| transposed adds + scaling pass; transpose/zeros free; validation uncounted); mode='canonical-copy' is numel (one write per element, copying each orbit's lexicographically-first entry over the orbit; orbit map built from generators and cached, never enumerates |G|; no validation pass, exact by construction). Reynolds upcasts to result_type(input, float64); canonical-copy preserves the input dtype.

Two modes, differing in whether the discarded entries get a vote. "reynolds-projection" (the default) averages each orbit:

R_G(T) = (1 / |G|) * sum_{g in G} g · T

"canonical-copy" instead keeps one entry per orbit -- the one at the lexicographically smallest index -- and copies it over the rest. Every other value in the orbit is discarded rather than mixed in, which is what makes it the right choice when the input is not trusted: nothing a caller hid in the redundant positions can reach the result. It is also the cheaper of the two, being one copy pass rather than |G| transposed adds, and it never enumerates the group.

Parameters

data:array_like

Input array to symmetrize.

symmetry:SymmetryGroup

Symmetry group to average over. If symmetry.axes is None, axes are interpreted as tuple(range(symmetry.degree)).

mode:{"reynolds-projection", "canonical-copy"}, optional

Which symmetrization to apply. Defaults to "reynolds-projection".

Returns

:SymmetricTensor

The projected tensor, validated and wrapped as a SymmetricTensor.

Raises

:SymmetryError

If data has incompatible dimensions for group axes or if the projected result cannot be validated as symmetric for group.

Notes

"reynolds-projection" performs exact Reynolds averaging internally, billing (|G| + 1) * numel(data) FLOPs:

Internal validation runs but is NOT billed (decision D1).

where |G| is the group order and numel = data.size.

"canonical-copy" bills numel(data) -- one write per output element, the same rate as every other materializing copy (copy, take, repeat). It needs no validation pass because its output is invariant by construction, and it never enumerates |G|: the orbit map is built from the group's generators and cached per (shape, group action).

The two modes also differ in dtype. Averaging must divide, so "reynolds-projection" accumulates in result_type(data, float64) -- a float32 input comes back float64. "canonical-copy" only moves values, so it preserves the input dtype exactly, including integer, boolean and complex types.

The canonical pattern for generating random data with symmetry is:

fnp.random.symmetric(shape, symmetry_group, distribution=...).

Examples

>>> import flopscope as flops
>>> import flopscope.numpy as fnp
>>> data = fnp.random.randn(4, 4)
>>> S = flops.symmetrize(data, symmetry=flops.SymmetryGroup.symmetric(axes=(0, 1)))
>>> S.is_symmetric((0, 1))
True