Module phiml.math.perm
Functions related to tensor permutation.
Functions
def all_permutations(dims: phiml.math._shape.Shape,
list_dim=(~permᵈ),
index_dim: phiml.math._shape.Shape | None = (indexᶜ),
convert=False) ‑> phiml.math._tensors.Tensor-
Expand source code
def all_permutations(dims: Shape, list_dim=dual('perm'), index_dim: Optional[Shape] = channel('index'), convert=False) -> Tensor: """ Returns a `Tensor` containing all possible permutation indices of `dims` along `list_dim`. Args: dims: Dims along which elements are permuted. list_dim: Single dim along which to list the permutations. index_dim: Dim listing vector components for multi-dim permutations. Can be `None` if `dims.rank == 1`. convert: Whether to convert the permutations to the default backend. If `False`, the result is backed by NumPy. Returns: Permutations as a single index `Tensor`. """ np_perms = np.asarray(list(permutations(range(dims.volume)))) perms = reshaped_tensor(np_perms, [list_dim, dims], convert=convert) if index_dim is None: assert len(dims) == 1, f"For multi-dim permutations, index_dim must be specified." return perms return unravel_index(perms, dims, index_dim)Returns a
Tensorcontaining all possible permutation indices ofdimsalonglist_dim.Args
dims- Dims along which elements are permuted.
list_dim- Single dim along which to list the permutations.
index_dim- Dim listing vector components for multi-dim permutations. Can be
Noneifdims.rank == 1. convert- Whether to convert the permutations to the default backend. If
False, the result is backed by NumPy.
Returns
Permutations as a single index
Tensor. def optimal_perm(cost_matrix: phiml.math._tensors.Tensor)-
Expand source code
def optimal_perm(cost_matrix: Tensor): """ Given a pair-wise cost matrix of two equal-size vectors, finds the optimal permutation to apply to one vector (corresponding to the dual dim of `cost_matrix`) in order to obtain the minimum total cost for a bijective map. Args: cost_matrix: Pair-wise cost matrix. Must be a square matrix with a dual and primal dim. Returns: dual_perm: Permutation that, when applied along the vector corresponding to the dual dim in `cost_matrix`, yields the minimum cost. cost: Optimal cost vector, listed along primal dim of `cost_matrix`. """ assert dual(cost_matrix) and primal(cost_matrix), f"cost_matrix must have primal and dual dims but got {cost_matrix}" perms = all_permutations(primal(cost_matrix), index_dim=None) perms = expand(perms, channel(index=dual(cost_matrix).name_list)) cost_pairs_by_perm = cost_matrix[perms] perm, cost = dmin((perms, cost_pairs_by_perm), key=psum(cost_pairs_by_perm)) return perm, costGiven a pair-wise cost matrix of two equal-size vectors, finds the optimal permutation to apply to one vector (corresponding to the dual dim of
cost_matrix) in order to obtain the minimum total cost for a bijective map.Args
cost_matrix- Pair-wise cost matrix. Must be a square matrix with a dual and primal dim.
Returns
dual_perm- Permutation that, when applied along the vector corresponding to the dual dim in
cost_matrix, yields the minimum cost. cost- Optimal cost vector, listed along primal dim of
cost_matrix.
def pick_random(value: ~TensorOrTree,
dim: str | Sequence | set | phiml.math._shape.Shape | Callable | None,
count: int | phiml.math._shape.Shape | None = 1,
weight: phiml.math._tensors.Tensor | None = None,
same_selection_dims: str | Sequence | set | phiml.math._shape.Shape | Callable | None = <function non_batch>,
selections: phiml.math._shape.Shape = ()) ‑> ~TensorOrTree-
Expand source code
def pick_random(value: TensorOrTree, dim: DimFilter, count: Union[int, Shape, None] = 1, weight: Optional[Tensor] = None, same_selection_dims: DimFilter = non_batch, selections: Shape = EMPTY_SHAPE) -> TensorOrTree: """ Pick one or multiple random entries from `value`. Args: value: Tensor or tree. When containing multiple tensors, the corresponding entries are picked on all tensors that have `dim`. You can pass `range` (the type) to retrieve the picked indices. dim: Dimension along which to pick random entries. `Shape` with one dim. count: Number of entries to pick. When specified as a `Shape`, lists picked values along `count` instead of `dim`. weight: Probability weight of each item along `dim`. Will be normalized to sum to 1. same_selection_dims: Dims along which to use the same random selection for each element. All other dims except `dim` are treated as batch. selections: Additional dims to generate more random subsets. These will be part of the output. Returns: `Tensor` or tree equal to `value`. """ v_shape = shape(value) dim = dim if isinstance(dim, Shape) else v_shape.only(dim) same_selection_dims = v_shape.only(same_selection_dims) - dim batches = selections + v_shape - dim - same_selection_dims nu_dims = v_shape.non_uniform_shape u_batches = batches - nu_dims assert nu_dims in batches, f"Cannot use same random selection across non-uniform dims but got {nu_dims} while batches are {batches}" if count is None and dim.well_defined: count = dim.size n = dim.volume if count is None else (count.volume if isinstance(count, SHAPE_TYPES) else count) b = default_backend() idx_slices = [] for nui in nu_dims.meshgrid(): u_dim = dim.after_gather(nui) nat_weight = weight.native([u_dim]) if weight is not None else None if u_dim.volume >= n: nat_idx = b.random_subsets(u_dim.volume, subset_size=n, subset_count=u_batches.volume, allow_duplicates=False, element_weights=nat_weight) elif u_dim.volume > 0: nat_idx = b.range(n) % u_dim.volume else: raise ValueError(f"Cannot pick random from empty tensor {u_dim}") idx = reshaped_tensor(nat_idx, [u_batches, count if isinstance(count, SHAPE_TYPES) else u_dim.without_sizes()], convert=False) if count == 1: idx = squeeze(count, u_dim) # idx = ravel_index() idx_slices.append(expand(idx, channel(index=u_dim.name))) idx = stack(idx_slices, nu_dims) return slice_(value, idx)Pick one or multiple random entries from
value.Args
value- Tensor or tree. When containing multiple tensors, the corresponding entries are picked on all tensors that have
dim. You can passrange(the type) to retrieve the picked indices. dim- Dimension along which to pick random entries.
Shapewith one dim. count- Number of entries to pick. When specified as a
Shape, lists picked values alongcountinstead ofdim. weight- Probability weight of each item along
dim. Will be normalized to sum to 1. same_selection_dims- Dims along which to use the same random selection for each element. All other dims except
dimare treated as batch. selections- Additional dims to generate more random subsets. These will be part of the output.
Returns
Tensoror tree equal tovalue. def random_permutation(*shape: phiml.math._shape.Shape | Any,
dims=<function non_batch>,
index_dim=(indexᶜ)) ‑> phiml.math._tensors.Tensor-
Expand source code
def random_permutation(*shape: Union[Shape, Any], dims=non_batch, index_dim=channel('index')) -> Tensor: """ Generate random permutations of the integers between 0 and the size of `shape`. When multiple dims are given, the permutation is randomized across all of them and tensor of multi-indices is returned. Batch dims result in batches of permutations. Args: *shape: `Shape` of the result tensor, including `dims` and batches. *dims: Sequence dims for an individual permutation. The total `Shape.volume` defines the maximum integer. All other dims from `shape` are treated as batch. Returns: `Tensor` """ assert dims is not batch, f"dims cannot include all batch dims because that violates the batch principle. Specify batch dims by name instead." shape = concat_shapes_(*shape) assert not shape.dual_rank, f"random_permutation does not support dual dims but got {shape}" perm_dims = shape.only(dims) batches = shape - perm_dims nu = perm_dims.non_uniform_shape batches -= nu assert nu in shape, f"Non-uniform permutation dims {perm_dims} must be included in the shape but got {shape}" b = default_backend() result = [] for idx in nu.meshgrid(): perm_dims_i = perm_dims.after_gather(idx) native = b.random_permutations(batches.volume, perm_dims_i.volume) if perm_dims_i.rank == 0: # cannot add index_dim result.append(reshaped_tensor(native, [batches, ()], convert=False)) else: native = b.unravel_index(native, perm_dims_i.sizes) result.append(reshaped_tensor(native, [batches, perm_dims_i, index_dim.with_size(perm_dims_i.name_list)], convert=False)) return stack(result, nu)Generate random permutations of the integers between 0 and the size of
shape.When multiple dims are given, the permutation is randomized across all of them and tensor of multi-indices is returned.
Batch dims result in batches of permutations.
Args
*shapeShapeof the result tensor, includingdimsand batches.*dims- Sequence dims for an individual permutation. The total
Shape.volumedefines the maximum integer. All other dims fromshapeare treated as batch.
Returns
Tensor