code: def mutation_combinations(mutations):
      '''A generator which returns all non-empty combinations of ChainMutation objects, respecting residue position i.e. if two residues
         have the same chain and residue ID then we do not return a combination with both residues.
         Note: You need to use ChainMutation objects here as their equality considers the chain id.
      '''
      mutations = sorted(mutations)
      combntn = itertools.chain.from_iterable(itertools.combinations(mutations, x) for x in range(len(mutations) + 1))
      for c in combntn:
          if len(c) > 0: # filter out the empty combination
              positions = ['%s%s' % (m.Chain, m.ResidueID.strip()) for m in c]
              if len(positions) == len(set(positions)): # filter out combinations where
                  yield f mutation_combinations(mutations):
      '''A generator which returns all non-empty combinations of ChainMutation objects, respecting residue position i.e. if two residues
         have the same chain and residue ID then we do not return a combination with both residues.
         Note: You need to use ChainMutation objects here as their equality considers the chain id.
      '''
      mutations = sorted(mutations)
      combntn = itertools.chain.from_iterable(itertools.combinations(mutations, x) for x in range(len(mutations) + 1))
      for c in combntn:
          if len(c) > 0: # filter out the empty combination
              positions = ['%s%s' % (m.Chain, m.ResidueID.strip()) for m in c]
              if len(positions) == len(set(positions)): # filter out combinations where
                  yield c
docs: A generator which returns all non-empty combinations of ChainMutation objects, respecting residue position i.e. if two residues
         have the same chain and residue ID then we do not return a combination with both residues.
         Note: You need to use ChainMutation objects here as their equality considers the chain id.