示例#1
0
    def _evaluate(self, postfix, prnlev):
        """ Evaluates a postfix in RPN format and returns a selection array """
        from sys import stderr
        buffer = ''
        stack = []

        pos = 0  # position in postfix
        while pos < len(postfix):
            p = postfix[pos]
            if p == '[': buffer = ''
            elif p == ']':  # end of the token
                ptoken = buffer
                pmask = self._selectElemMask(ptoken)
                stack.append(pmask)
            elif self._isOperand(p) or p in [':', '@']:
                buffer += p
            elif p in ['&', '|']:
                pmask1 = None
                pmask2 = None
                try:
                    pmask1 = stack.pop()
                    pmask2 = stack.pop()
                    pmask = self._binop(p, pmask1, pmask2)
                except IndexError:
                    raise MaskError('Illegal binary operation')
                stack.append(pmask)
            elif p in ['<', '>']:
                if pos < len(postfix) - 1 and postfix[pos + 1] in [':', '@']:
                    buffer += p
                else:
                    try:
                        pmask1 = stack.pop()  # distance criteria
                        pmask2 = stack.pop()
                        pmask = self._selectDistd(pmask1, pmask2)
                    except IndexError:
                        return [0 for a in self.parm.atoms]
                    stack.append(pmask)
            elif p == '!':
                try:
                    pmask1 = stack.pop()
                except IndexError:
                    raise MaskError('Illegal ! operation')
                pmask = self._neg(pmask1)
                stack.append(pmask)
            else:
                raise MaskError('Unknown symbol evaluating RPN: %s' % p)
            pos += 1
        # end while i < len(postfix)

        pmask = stack.pop()

        if stack:
            raise MaskError('There may be missing operands in the mask!')

        if prnlev > 7:
            stderr.write('%d atoms selected by %s' % (sum(pmask), self.mask))

        return pmask
示例#2
0
    def _torpn(self, infix, prnlev):
        """ Converts the infix to an RPN array """
        postfix = ''
        stack = ['_']  # use a list as a stack. Then pop() works as expected
        flag = 0
        i = 0

        while i < len(infix):
            p = infix[i]
            if p == '[':
                postfix += p
                flag = 1
            elif p == ']':
                postfix += p
                flag = 0
            elif flag:
                postfix += p
            elif p == '(':
                stack.append(p)
            elif p == ')':
                pp = stack.pop()
                while pp != '(':
                    if pp == '_':
                        raise MaskError('Unbalanced parentheses in Mask.')
                    postfix += pp
                    pp = stack.pop()
            # At this point both ()s are discarded
            elif p == '_':
                pp = stack.pop()
                while pp != '_':
                    if pp == '(':
                        raise MaskError('Unbalanced parentheses in Mask.')
                    postfix += pp
                    pp = stack.pop()
            elif self._isOperator(p):
                P1 = self._priority(p)
                P2 = self._priority(stack[len(stack) - 1])
                if P1 > P2:
                    stack.append(p)
                else:
                    while P1 <= P2:
                        pp = stack.pop()
                        postfix += pp
                        P1 = self._priority(p)
                        P2 = self._priority(stack[len(stack) - 1])
                    stack.append(p)
            else:
                raise MaskError('Unknown symbol %s' % p)
            i += 1
        # end while i < len(infix):
        return postfix
示例#3
0
 def _selectDistd(self, pmask1, pmask2):
     """ Selects atoms based on a distance criteria """
     # pmask1 is either @<number> or :<number>, and represents the distance
     # criteria. pmask2 is the selection of atoms from which the distance is
     # evaluated.
     pmask = _mask(len(self.parm.atoms))
     # Determine if we want > or <
     if pmask1[0] == '<':
         cmp = float.__lt__
     elif pmask1[0] == '>':
         cmp = float.__gt__
     else:
         raise MaskError(
             'Unknown comparison criteria for distance mask: %s' %
             pmask1[0])
     pmask1 = pmask1[1:]
     if pmask1[0] not in ':@':
         raise MaskError('Bad distance criteria for mask: %s' % pmask1)
     try:
         distance = float(pmask1[1:])
     except TypeError:
         raise MaskError('Distance must be a number: %s' % pmask1[1:])
     if not hasattr(self.parm.atoms[0], 'xx'):
         raise MaskError('Distance-based masks require loaded coordinates.')
     distance *= distance  # Faster to compare square of distance
     # First select all atoms that satisfy the distance. If we ended up
     # choosing residues, then we will go back through afterwards and select
     # entire residues when one of the atoms in that residue is selected.
     idxlist = [i for i, val in enumerate(pmask2) if val == 1]
     for i, atomi in enumerate(self.parm.atoms):
         for j in idxlist:
             atomj = self.parm.atoms[j]
             dx = atomi.xx - atomj.xx
             dy = atomi.xy - atomj.xy
             dz = atomi.xz - atomj.xz
             d2 = dx * dx + dy * dy + dz * dz
             if cmp(d2, distance):
                 pmask[i] = 1
                 break
     # Now see if we have to select all atoms in residues with any selected
     # atoms
     if pmask1[0] == ':':
         for res in self.parm.residues:
             for atom in res.atoms:
                 if pmask[atom.idx] == 1:
                     for atom in res.atoms:
                         pmask[atom.idx] = 1
                     break
     return pmask
示例#4
0
 def Or(self, other):
     if self.natom != other.natom:
         raise MaskError('_mask: or() requires another mask of equal size!')
     new_mask = _mask(self.natom)
     for i in xrange(len(self)):
         new_mask[i] = int(self[i] or other[i])
     return new_mask
示例#5
0
 def _binop(self, op, pmask1, pmask2):
     """ Does a binary operation on a pair of masks """
     if op == '&':
         return pmask1.And(pmask2)
     if op == '|':
         return pmask1.Or(pmask2)
     raise MaskError('Unknown operator [%s]' % op)
示例#6
0
 def _residue_numlist(self, instring, mask):
     """ Fills a _mask based on residue numbers """
     buffer = ''
     pos = 0
     at1 = at2 = dash = 0
     while pos < len(instring):
         p = instring[pos]
         if p.isdigit():
             buffer += p
         if p == ',' or pos == len(instring) - 1:
             if dash == 0:
                 at1 = int(buffer)
                 self._resnum_select(at1, at1, mask)
             else:
                 at2 = int(buffer)
                 self._resnum_select(at1, at2, mask)
                 dash = 0
             buffer = ''
         elif p == '-':
             at1 = int(buffer)
             dash = 1
             buffer = ''
             if not (p.isdigit() or p in [',', '-']):
                 raise MaskError('Unknown symbol in residue number '
                                 'parsing [%s]' % p)
         pos += 1
示例#7
0
 def And(self, other):
     if self.natom != other.natom:
         raise MaskError(
             "_mask: and() requires another mask of equal size!")
     new_mask = _mask(self.natom)
     for i in xrange(len(self)):
         new_mask[i] = int(self[i] and other[i])
     return new_mask
示例#8
0
    def _priority(self, op):
        if op in ['>', '<']: return 6
        if op in ['!']: return 5
        if op in ['&']: return 4
        if op in ['|']: return 3
        if op in ['(']: return 2
        if op in ['_']: return 1

        raise MaskError('Unknown operator [%s] in Mask ==%s==' %
                        (op, self.mask))
示例#9
0
 def _residue_namelist(self, instring, mask):
     """ Fills a _mask based on residue names """
     buffer = ''
     pos = 0
     while pos < len(instring):
         p = instring[pos]
         if p.isalnum() or p in ['*', '?', '+', "'", '-']:
             buffer += p
         if p == ',' or pos == len(instring) - 1:
             if '-' in buffer and buffer[0].isdigit():
                 self._residue_numlist(buffer, mask)
             else:
                 self._resname_select(buffer, mask)
             buffer = ''
         if not (p.isalnum() or p in ",?*'+-"):
             raise MaskError('Unknown symbol in residue name '
                             'parsing [%s]' % p)
         pos += 1
示例#10
0
 def _atom_namelist(self, instring, mask, key='name'):
     """ Fills a _mask based on atom names/types """
     buffer = ''
     pos = 0
     while pos < len(instring):
         p = instring[pos]
         if p.isalnum() or p in "\\*?+'-":
             buffer += p
         if p == ',' or pos == len(instring) - 1:
             if '-' in buffer and buffer[0].isdigit():
                 self._atom_numlist(buffer, mask)
             else:
                 self._atname_select(buffer, mask, key)
             buffer = ''
         if not (p.isalnum() or p in "\\,?*'+-"):
             raise MaskError('Unrecognized symbol in atom name '
                             'parsing [%s]' % p)
         pos += 1
示例#11
0
    def _tokenize(self, prnlev):
        """ Tokenizes the mask string into individual selections:
            1. remove spaces
            2. isolate 'operands' into brackets [...]
            3. split expressions of the type :1-10@CA,CB into 2 parts;
               the 2 parts are joned with & operator and (for the sake
               of preserving precedence of other operators) enclosed by
               (...); i.e. :1-10@CA,CB is split into (:1-10 & @CA,CB)
            4. do basic error checking
        """
        buffer = ''  # keeping track of a single operand
        infix = ''  # value that is returned at the end
        # flag == 0: means new operand or operand was completed & ended with ]
        # flag == 1: means operand with ":" read
        # flag == 2: means operand with "@" read
        # flag == 3: means '<' or '>' read, waiting for numbers
        flag = 0
        i = 0
        while i < len(self.mask):
            p = self.mask[i]
            # skip whitespace
            if p.isspace():
                i += 1
                continue
            # If p is an operator, is the last character, or is a ()...
            elif (self._isOperator(p) or i == len(self.mask) - 1
                  or p in ['(', ')']):
                # Deal with the last character being a wildcard that we have to
                # convert
                if p == '=' and i == len(self.mask) - 1:  # wildcard
                    if flag > 0:
                        p = '*'
                    else:
                        raise MaskError("AmberMask: '=' not in name "
                                        "list syntax")
                # If this is the end of an operand, terminate the buffer, flush
                # it to infix, and reset flag to 0 and empty the buffer
                if flag > 0:
                    if i == len(self.mask) - 1 and p != ')': buffer += p
                    buffer += '])'
                    flag = 0
                    infix += buffer
                    buffer = ''
                if i != len(self.mask) - 1 or p == ')': infix += p
                # else if p is >,<
                if p in ['<', '>']:
                    buffer = '([%s' % p
                    i += 1
                    p = self.mask[i]
                    buffer += p
                    flag = 3
                    try:
                        self.parm.coords[0]
                    except AttributeError:
                        raise MaskError('<,> operators require coordinates')
                    if not p in [':', '@']:
                        raise MaskError('Bad syntax [%s]' % self.mask)
            elif self._isOperand(p):
                if flag == 0:
                    buffer = '(['
                    flag = 1
                    if p != '*':
                        raise MaskError('Bad syntax [%s]' % self.mask)
                if p == '=':  # wildcard
                    if flag > 0:
                        p = '*'
                    else:
                        raise MaskError("'=' not in name list syntax")
                buffer += p
            elif p == ':':
                if flag == 0:
                    buffer = '([:'
                    flag = 1
                else:
                    buffer += '])|([:'
                    flag = 1
            elif p == '@':
                if flag == 0:
                    buffer = '([@'
                    flag = 2
                elif flag == 1:
                    buffer += ']&[@'
                    flag = 2
                elif flag == 2:
                    buffer += '])|([@'
                    flag = 2
            else:
                raise MaskError('Unknown symbol (%s) expression' % p)
            i += 1
        # end while i < len(self.mask):
        # Check that each operand has at least 4 characters: [:1] and [@C], etc.
        i = 0
        n = 1  # number of characters in current operand
        flag = 0
        while i < len(infix):
            p = infix[i]
            if p == '[':
                n += 1
                flag = 1
            elif p == ']':
                if n < 4 and infix[i - 1] != '*':
                    raise MaskError('empty token in infix')
                n = 1
            else:
                if flag == 1:
                    n += 1
            i += 1

        return infix + '_'  # terminating _ for next step
示例#12
0
 def remove(self, *args, **kwargs):
     raise MaskError('_mask is a fixed-length array!')
示例#13
0
 def extend(self, *args, **kwargs):
     raise MaskError('_mask is a fixed-length array!')
示例#14
0
    def _selectElemMask(self, ptoken):
        """ Selects an element mask """
        # some constants
        ALL = 0
        NUMLIST = 1
        NAMELIST = 2
        TYPELIST = 3
        ELEMLIST = 4
        # define the mask object and empty buffer
        pmask = _mask(len(self.parm.atoms))
        buffer = ''
        buffer_p = 0
        # This is a residue NUMber LIST
        if ptoken.startswith(':'):
            reslist = NUMLIST
            pos = 1
            while pos < len(ptoken):
                p = ptoken[pos]
                buffer += p
                buffer_p += 1
                if p == '*' and ptoken[pos - 1] != '\\':
                    if buffer_p == 1 and (pos == len(ptoken) - 1
                                          or ptoken[pos + 1] == ','):
                        reslist = ALL
                    elif reslist == NUMLIST:
                        reslist = NAMELIST
                elif p.isalpha() or p in '?*':
                    reslist = NAMELIST
                if pos == len(ptoken) - 1:
                    buffer_p = 0
                if len(buffer) != 0 and buffer_p == 0:
                    if reslist == ALL:
                        pmask.select_all()
                    elif reslist == NUMLIST:
                        self._residue_numlist(buffer, pmask)
                    elif reslist == NAMELIST:
                        self._residue_namelist(buffer, pmask)
                    reslist = NUMLIST
                pos += 1
        elif ptoken.startswith('@'):
            atomlist = NUMLIST
            pos = 1
            while pos < len(ptoken):
                p = ptoken[pos]
                buffer += p
                buffer_p += 1
                if p == '*' and ptoken[pos - 1] != "\\":
                    if buffer_p == 0 and (pos == len(ptoken) - 1
                                          or ptoken[pos + 1] == ','):
                        atomlist = ALL
                    elif atomlist == NUMLIST:
                        atomlist = NAMELIST
                elif p.isalpha() or p in '?*':
                    if atomlist == NUMLIST:
                        atomlist = NAMELIST
                elif p == '%':
                    atomlist = TYPELIST
                elif p == '/':
                    atomlist = ELEMLIST
                if pos == len(ptoken) - 1:
                    buffer_p = 0

                if len(buffer) != 0 and buffer_p == 0:
                    if atomlist == ALL:
                        pmask.select_all()
                    elif atomlist == NUMLIST:
                        self._atom_numlist(buffer, pmask)
                    elif atomlist == NAMELIST:
                        self._atom_namelist(buffer, pmask)
                    elif atomlist == TYPELIST:
                        self._atom_typelist(buffer[1:], pmask)
                    elif atomlist == ELEMLIST:
                        self._atom_elemlist(buffer[1:], pmask)
                pos += 1
        elif ptoken.strip() == '*':
            pmask.select_all()
        elif ptoken[0] in ['<', '>']:
            return ptoken
        else:
            raise MaskError('Mask is missing : and @')
        # end if ':' in ptoken:

        return pmask