Source code for schrodinger.infra.mmbitset
"""
A pythonic wrapper for the mmbs library, providing access to bitsets.
The default error handler for the Bitset module is set to mm.error_handler.
If no error handler is specified for error_handler arguments, its value is
what will be used.
Copyright Schrodinger, LLC. All rights reserved.
"""
from schrodinger.infra import mm
from schrodinger.infra import structure
[docs]class Bitset:
    """
    This class represents a bitset, with each element either 1 or 0.
    A bitset is similar conceptually to a Python dictionary that contains
    keys that are integers from 1 to the length of the bitset, and the value
    for each key is an on/off boolean (1 or 0).
    Bitsets are usually used when keeping track of which atoms in the ct an
    operation needs to be performed on; in which case the length of the Bitset
    is the number of atoms in the ct.
    An instance of Bitset can be converted to a list containing the elements
    that are on (i.e. set to 1) via list(bitset) and to a set via set(bitset).
    This is an object-oriented wrapper for the underlying MMBS library. All
    state is stored in the C library. References to instances of this class
    can be used in direct mmbs library calls, as it will converted to the
    integer handle when necessary.
    """
[docs]    def __init__(self,
                 handle=None,
                 manage_handle=True,
                 error_handler=None,
                 size=None):
        """
        Initialize an object with an existing MMBS handle or a size.
        By default, the MMBS resources will be managed by the object. To
        keep these from being cleaned up on object deletion, set
        manage_handle=False.
        :param size: The size to use for a newly created bitset.
        :type size: int
        """
        if handle is None:
            if size is None:
                raise ValueError(
                    "Bitset constructor must provide an existing mmbs handle or a size specification."
                )
            self.handle = structure.MM_Bitset.createFromSize(size)
        else:
            self.handle = structure.MM_Bitset(handle)
        if not manage_handle:
            self.handle.release() 
[docs]    @classmethod
    def from_list(cls, size, on_list):
        """
        Alternative constructor; returns a Bitset object initialized from a
        Python list.
        Example usage::
            bs = Bitset.from_list(st.atom_total, selected_atoms)
        where <st> is the structure object, and <selected_atoms> is a list of
        atom indices.
        :param size: The size to use for a newly created bitset.
        :type size: int
        :param on_list: Turn on the bits that are present in this iterable.
                Each item should be an int between 1 and <size>.
        """
        bs = cls(size=size)
        for index in on_list:
            bs.set(index)
        return bs 
[docs]    def size(self):
        """
        Return the capacity (i.e. maximum index) of the Bitset.
        """
        return len(self.handle) 
[docs]    def resize(self, size):
        """
        Modify the capacity of the Bitset.
        """
        self.handle.resize(size) 
[docs]    def count(self):
        """
        Return the number of index values that are set to 'on'.
        """
        return self.handle.count() 
[docs]    def get(self, index):
        """
        Get the value of the index.
        Returns True if the index value is on, False if it is off.
        """
        return self.handle[int(index)] 
[docs]    def set(self, index):
        """
        Set the index value to 'on'.
        """
        self.handle[int(index)] = True 
[docs]    def unset(self, index):
        """
        Set the index value to 'off'.
        """
        self.handle[int(index)] = False 
[docs]    def invert(self):
        """Invert the bits in the set."""
        mm.mmbs_invert(self.handle) 
[docs]    def range(self, start, finish):
        """
        Set all bits in a range from "start" to "finish"
        """
        mm.mmbs_range(self.handle, start, finish) 
[docs]    def fill(self):
        """
        Set all bits to on.
        """
        self.handle.set() 
[docs]    def all(self):
        """Are all bits on?"""
        return bool(self.handle.all()) 
[docs]    def copy(self):
        """Create another bitset that is a duplicate of this one."""
        new_handle = mm.mmbs_duplicate(self.handle)
        return self.__class__(new_handle) 
    def __repr__(self):
        """
        Return a Python representation string for this bitset.
        """
        return "Bitset(%d)" % self.handle.getHandle()
    def __str__(self):
        """
        Return a string of this bitset.
        """
        return str(list(self))
    def __iter__(self):
        """
        Return an iterator for this bitset.
        Loops over all of the ON bits.
        """
        # don't return iter(self.handle) in case this is a temporary. If this
        # is cleaned up before the iterator, the iterator is invalid.
        yield from self.handle
[docs]    def __len__(self):
        """
        Returns the size of the bitset (Number of ON and OFF bits)
        """
        return self.size() 
    def __index__(self):
        """Get the MMBs C handle"""
        return int(self.handle)
    def __getitem__(self, index):
        return self.handle[index]
    def __setitem__(self, index, value):
        self.handle[index] = value