Source code for sparsepermutation
"""
Simple implementation of a sparse permutation datastructure which is more efficient than
saving permutation lists of length ``n`` each.
.. moduleauthor:: Fabian Ball <fabian.ball@kit.edu>
"""
__all__ = ['SparsePermutation']
class SparsePermutationArray(dict):
"""
Simple implementation of a dict which returns 'identity' if a key is missing.
This is used to represent a permutation in a sparse way (cycle notation).
However, no checks are performed in terms of validity of the cycles.
For internal use only, use ``SparsePermutation`` to create a sparse permutation
"""
def __missing__(self, key):
return key
def __setitem__(self, key, value):
if key == value:
if key in self:
self.__delattr__(key)
else:
super(SparsePermutationArray, self).__setitem__(key, value)
def __str__(self):
# Produce a nice cycle representation
keys = set(self.keys())
ret = ''
while keys:
k = min(keys)
keys.remove(k)
cycle = [k]
img_k = self[k]
while img_k != k:
cycle.append(img_k)
keys.remove(img_k)
img_k = self[img_k]
ret += '({})'.format(' '.join(map(str, cycle)))
return ret
[docs]class SparsePermutation(object):
"""
A minimal implementation of a sparse permutation that partly mimics the Permutation
implementation of ``sympy.combinatorics.Permutation``.
Usage:
>>> from sparsepermutation import SparsePermutation
>>> p = [0, 1, 3, 2] # Permutation (2 3)
>>> sp = SparsePermutation(p) # Sparsify
>>> q = list(sp) # Convert back to list
>>> assert p == q
"""
def __init__(self, permutation_array):
"""
Create a sparse permutation given a permutation list of length ``n``.
:param permutation_array: Array-like permutation representation
:type permutation_array: list | tuple
"""
self._perm = SparsePermutationArray()
self._n = len(permutation_array) # Save the length of the permutation
self._support = 0
for k, v in enumerate(permutation_array):
if k != v:
self._perm[k] = v
self._support += 1 # Increase the support count of the permutation
def __call__(self, *args):
"""
Convenient application of this permutation.
"""
if len(args) == 1:
arg = args[0]
if isinstance(arg, int): # Return the image
return self[arg]
else: # Assume an iterable that is indexable, e.g. a list
try:
t = type(arg)
return t([arg[self[i]] for i in range(len(arg))])
except TypeError:
raise TypeError('The argument must be iterable and indexable')
elif len(args) > 1: # Apply permutation on every argument
return tuple(self(arg) for arg in args)
else:
raise TypeError('No argument given')
def __getitem__(self, item):
if item >= self._n:
raise KeyError(item)
else:
return self._perm[item]
def __iter__(self):
for i in range(self._n):
yield self._perm[i]
def __hash__(self):
return hash(tuple(self))
def __str__(self):
return str(self._perm)
#############
# sympy.combinatorics.Permutation mimics
#############
[docs] def length(self):
"""
:return: The number of affected elements
:rtype: int
"""
return self._support
[docs] def atoms(self):
"""
:return: The set of elements
:rtype: set
"""
return set(range(self._n))
[docs] def support(self):
"""
:return: The list of affected elements
:rtype: list
"""
return list(self._perm)
@property
def size(self):
"""
:return: The number of elements the permutation acts on
:rtype: int
"""
return self._n