Package Biskit :: Package Dock :: Module ComplexList
[hide private]
[frames] | no frames]

Source Code for Module Biskit.Dock.ComplexList

  1  ## Automatically adapted for numpy.oldnumeric Mar 26, 2007 by alter_code1.py 
  2   
  3  ## 
  4  ## Biskit, a toolkit for the manipulation of macromolecular structures 
  5  ## Copyright (C) 2004-2006 Raik Gruenberg & Johan Leckner 
  6  ## 
  7  ## This program is free software; you can redistribute it and/or 
  8  ## modify it under the terms of the GNU General Public License as 
  9  ## published by the Free Software Foundation; either version 2 of the 
 10  ## License, or any later version. 
 11  ## 
 12  ## This program is distributed in the hope that it will be useful, 
 13  ## but WITHOUT ANY WARRANTY; without even the implied warranty of 
 14  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 15  ## General Public License for more details. 
 16  ## 
 17  ## You find a copy of the GNU General Public License in the file 
 18  ## license.txt along with this program; if not, write to the Free 
 19  ## Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 20  ## 
 21  ## 
 22  ## organise, sort, and filter Complexes 
 23  ## 
 24  ## last $Author: graik $ 
 25  ## last $Date: 2007/03/26 18:40:42 $ 
 26  ## $Revision: 2.13 $ 
 27   
 28  """ 
 29  List of Complex objects. 
 30  """ 
 31       
 32  import numpy.oldnumeric as N 
 33  import types 
 34  import biggles 
 35  import random 
 36   
 37  import Biskit.tools as t 
 38  from Biskit import PDBError, EHandler 
 39  from Biskit.Errors import BiskitError 
 40   
 41  from Biskit.Dock.Complex import Complex 
 42  from Biskit.Dock.ComplexModelRegistry import ComplexModelRegistry 
 43   
 44   
45 -class ComplexListError( BiskitError ):
46 pass
47
48 -class ConditionError( ComplexListError ):
49 pass
50
51 -class AmbiguousMatch( ConditionError ):
52 pass
53
54 -class ComplexNotFound( ConditionError):
55 pass
56 57
58 -class ComplexList( list ):
59 """ 60 List of Complex objects. Special support is given to access and use 61 entries in the Complexes' info dictionaries for filtering and sorting. 62 Some care is taken to avoid the adding of non-Complex objects (but 63 somehow it is, certainly, still possible). 64 65 The different rec- and lig_models of all Complexes are centrally kept in a 66 ComplexModelRegistry. Before adding a Complex, we check 67 whether its rec- or lig_model are equivalent (same fileName and unchanged) 68 to one in the registy. If so, they are replaced to 69 avoid that each Complex points to its own copy of essentially the same 70 PDBmodel. This does only work with PDBModels that have been pickled to 71 disc (see PDBModel.saveAs ) and haven't been changed since. It's a very 72 good idea to do this if you want to perform any distributed calculation 73 on the ComplexList. Saved PDBModels cause only little PVM traffic (not much 74 more than the file name is transmitted). By contrast, unsaved ones will 75 severly slow down the job distribution. 76 77 @todo: Removing items with pop(), del, remove() etc. will not remove 78 unused PDBModels from rec_models or lig_models. 79 """ 80
81 - def __init__(self, lst=[] ):
82 """ 83 @param lst: list of Complexes 84 @type lst: [Complex] 85 """ 86 ## non-redundant rec/lig_models of all complexes indexed by file name 87 self.models = ComplexModelRegistry() 88 89 self.initVersion = t.dateString() + ' ' + self.version() 90 91 if lst != []: 92 self.extend( lst )
93 94
95 - def version( self ):
96 """ 97 Version of class. 98 99 @return: class version number 100 @rtype: str 101 """ 102 return 'ComplexList $Revision: 2.13 $'
103 104
105 - def __setstate__(self, state ):
106 """ 107 called for unpickling the object. 108 """ 109 self.__dict__ = state 110 ## backwards compability 111 self.__defaults()
112 113
114 - def __defaults( self ):
115 self.models = getattr( self, 'models', ComplexModelRegistry() ) 116 if getattr( self, 'rec_models', 0) != 0: 117 EHandler.warning( 118 're-creating model registry..re-pickle this list!') 119 for c in self.toList(): 120 self.models.addComplex( c ) 121 del self.rec_models 122 del self.lig_models
123 124
125 - def checkType( self, v ):
126 """ 127 Make sure v is a Complex. 128 129 @raise ComplexListError: if not a Complex 130 """ 131 if not isinstance(v, Complex): 132 raise ComplexListError( 133 str( v ) + " not allowed. ComplexList requires "+str(Complex))
134 135
136 - def checkTypes( self, lst ):
137 """ 138 Make sure lst is a list of Complexes. 139 140 @raise ComplexListError: if list contains non-Complex item. 141 """ 142 if not isinstance( lst, list ): 143 raise ComplexListError("Wrong argument type: "+str(type(lst))) 144 145 if not isinstance( lst, ComplexList ): 146 for v in lst: 147 self.checkType( v )
148 149
150 - def __setitem__(self, i, v ):
151 """ 152 Set value v of position i. 153 >>> lst[i] = v 154 155 @param i: list index 156 @type i: int 157 @param v: value 158 @type v: any 159 """ 160 self.checkType( v ) 161 162 if i < list.__len__( self ): 163 self.models.removeComplex( self[i] ) 164 self.models.addComplex( v ) 165 166 list.__setitem__( self, i, v)
167 168
169 - def __add__( self, lst ):
170 """ 171 New ComplexList with the two lists. 172 173 @return: new instance with simply one list appended to the other 174 @rtype: ComplexList 175 """ 176 r = self.__class__( self ) 177 r.extend( lst ) 178 return r
179 180
181 - def __iadd__( self, lst ):
182 """ 183 List appendet to this ComplexList. 184 185 @return: this instance with lst appended 186 @rtype: ComplexList 187 """ 188 self.extend( lst ) 189 return self
190 191
192 - def ligModels( self ):
193 """ 194 Get all shared ligand PDBModels. Stray models (changed or unpickled) 195 are not returned. 196 197 @return: list of ligand models 198 @rtype: [PDBModel] 199 """ 200 return self.models.ligModels()
201 202
203 - def recModels( self ):
204 """ 205 Get all shared receptor PDBModels. Stray models (changed or unpickled) 206 are not returned. 207 208 @return: list of receptor models 209 @rtype: [PDBModel] 210 """ 211 return self.models.recModels()
212 213
214 - def __add_once( self, item, lst ):
215 """ 216 Add eithem to list of it is not already there. 217 """ 218 if lst == None: 219 lst = [] 220 if not item in lst: 221 lst.append( item ) 222 return lst
223 224
225 - def strayModels( self ):
226 """ 227 Look for models that are not in model registry. 228 229 @return: { int|str:[ PDBModel ] }, { int|str:[ PDBModel ] } 230 @rtype: dict 231 232 @note: mostly for DEBUGGING 233 """ 234 stray_ligs = {} 235 stray_recs = {} 236 known_recs = self.recModels() 237 known_ligs = self.ligModels() 238 for c in self: 239 if c.rec_model not in known_recs: 240 key = c.get( 'model1', None ) or c.rec_model.fileName \ 241 or 1 242 stray_recs[ key ] = self.__add_once( c.rec_model, 243 stray_recs.get( key, []) ) 244 245 if c.lig_model not in known_ligs: 246 key = c.get( 'model2', None ) or c.lig_model.fileName \ 247 or 1 248 stray_ligs[ key ] = self.__add_once( c.lig_model, 249 stray_ligs.get( key, []) ) 250 251 return stray_recs, stray_ligs
252 253
254 - def extend( self, lst ):
255 """ 256 extend( list ). Add all items to (the end of) this instance 257 """ 258 self.checkTypes( lst ) 259 260 list.extend( self, lst ) 261 262 for v in lst: 263 self.models.addComplex( v )
264 265
266 - def append( self, v ):
267 """ 268 append( Complex ). Append Complex to the end of this list. 269 """ 270 self.checkType( v ) 271 self.models.addComplex( v ) 272 list.append( self, v )
273 274
275 - def __getslice__( self, i, j ):
276 """ 277 Slice list. 278 279 @param i: start index 280 @type i: int 281 @param j: end index 282 @type j: int 283 284 @return: new instance with only the given range of items 285 @rtype: ComplexList 286 """ 287 r = self.__class__(super(ComplexList, self).__getslice__(i,j)) 288 return r
289 290
291 - def argsortRandom( self ):
292 """ 293 Indices for key in random order:: 294 argsortRandom() -> [ int ], indices in random order. 295 296 @return: indices in random order 297 @rtype: [int] 298 """ 299 pairs = [(random.random(), i) for i in range(0, len(self))] 300 pairs.sort() 301 return [ x[1] for x in pairs ]
302 303
304 - def argsort( self, sortKey ):
305 """ 306 Indices sort order for values of key:: 307 argsort( str_sortKey ) -> [ int ], indices after sorting 308 309 @param sortKey: key to use for sorting 310 @type sortKey: str 311 312 @return: indices after sorting 313 @rtype: [int] 314 """ 315 pairs = [(self[i].info.get(sortKey), i) for i in range(0, len(self))] 316 pairs.sort() 317 return [ x[1] for x in pairs ]
318 319
320 - def sortBy( self, sortKey ):
321 """ 322 Sort ComplexList by key:: 323 sortBy( str_sortKey ) -> ComplexList (or sub-class) 324 sorted by info[ str_sortKey ] 325 326 @param sortKey: key to use for sorting 327 @type sortKey: str 328 329 @return: sorted ComplexList 330 @rtype: ComplexList 331 """ 332 return self.take( self.argsort( sortKey ))
333 334
335 - def valuesOf(self, infoKey, default=None, indices=None, unique=0 ):
336 """ 337 Get all values of a certain info record of all or some Complexes. 338 339 @param infoKey: key for info dict 340 @type infoKey: str 341 @param default: default value if infoKey is not found (None) 342 @type default: any 343 @param indices: list of int OR None(=all), indices of Complexes (None) 344 @type indices: [int] OR None 345 @param unique: report each value only once (set union), (default 0) 346 @type unique: 1|0 347 348 @return: list of values 349 @rtype: [any] 350 """ 351 l = self 352 if indices != None: 353 l = N.take( N.array(l,'O'), indices ) 354 355 if not unique: 356 return [ c.info.get(infoKey, default) for c in l ] 357 358 r = [] 359 for c in l: 360 if c.info.get(infoKey, default) not in r: 361 r += [ c.info.get( infoKey ) ] 362 return r
363 364
365 - def filterRange( self, infoKey, vLow, vHigh ):
366 """ 367 Get indices of Complexes where vLow <= c.info[ infoKey ] <= vHigh. 368 369 Use:: 370 filterRange( str_infoKey, vLow, vHigh ) 371 372 @param infoKey: key for info dict 373 @type infoKey: str 374 @param vLow: upper value limit 375 @type vLow: float 376 @param vHigh: lower value limit 377 @type vHigh: float 378 379 @return: array of int 380 @rtype: [int] 381 """ 382 vLst = self.valuesOf( infoKey ) 383 384 maskL = N.greater_equal( vLst, vLow ) 385 maskH = N.less_equal( vLst, vHigh ) 386 387 return N.nonzero( maskL * maskH )
388 389
390 - def filterEqual( self, infoKey, lst ):
391 """ 392 Get indices of Complexes where c.info[ infoKey ] in lst. 393 394 Use:: 395 filterEqual( infoKey, lst ) 396 397 @param infoKey: key for info dict 398 @type infoKey: str 399 @param lst: list of values to look for 400 @type lst: [any] 401 402 @return: array of int 403 @rtype: [int] 404 """ 405 mask = [ c.info.get( infoKey ) in lst for c in self ] 406 return N.nonzero( mask )
407 408
409 - def filterFunct( self, f ):
410 """ 411 Get indices of Complexes where f( c ) == 1. 412 413 Use:: 414 filterFunct( f ) 415 416 @param f: filterFunct 417 @type f: function 418 419 @return: array of int 420 @rtype: [int] 421 """ 422 mask = [ f( c ) for c in self ] 423 return N.nonzero( mask )
424 425
426 - def filter( self, infoKey, cond ):
427 """ 428 Complexes matching condition. 429 430 @param infoKey: key of Complex.info dict 431 (not used if cond is function ) 432 @type infoKey: str 433 @param cond: filter condition:: 434 - (vLow, vHigh) -> vLow <= c.info[ infoKey ] <= vHigh 435 - list -> c.info[ infoKey ] in cond 436 - function -> cond( c ) == 1 437 @type cond: any 438 439 @return: ComplexList (or sub-class) 440 @rtype: ComplexList 441 442 @raise ConditionError: if cond is neither list nor tuple nor function 443 """ 444 indices = None 445 446 if type( cond ) == tuple: 447 448 indices = self.filterRange( infoKey, cond[0], cond[1] ) 449 450 if type( cond ) == list: 451 452 indices = self.filterEqual( infoKey, cond ) 453 454 if type( cond ) == types.FunctionType: 455 456 indices = self.filterFunct( cond ) 457 458 if indices == None: 459 try: 460 indices = self.filterEqual( infoKey, [cond] ) 461 except: 462 raise ConditionError( "Can't interprete filter condition.") 463 464 return self.take(indices)
465 466
467 - def argmax( self, infoKey ):
468 """ 469 Get index of complex c with highest c.infos[infokey] value 470 471 @param infoKey: key for info dict 472 @type infoKey: str 473 474 @return: index of complex c with highest c.infos[infokey] value 475 @rtype: int 476 """ 477 vLst = self.valuesOf( infoKey ) 478 return N.argmax( vLst )
479 480
481 - def max( self, infoKey ):
482 """ 483 Get higest c.infos[infokey] value. 484 485 @param infoKey: key for info dict 486 @type infoKey: str 487 488 @return: with highest c.info['infoKey'] value 489 @rtype: Complex 490 """ 491 return self[ self.argmax(infoKey) ]
492 493
494 - def argmin( self, infoKey ):
495 """ 496 Get index of complex c with lowest c.infos[infokey] value 497 498 @param infoKey: key for info dict 499 @type infoKey: str 500 501 @return: index of complex c with lowest c.infos[infokey] value 502 @rtype: int 503 """ 504 vLst = self.valuesOf( infoKey ) 505 return N.argmin( vLst )
506
507 - def min( self, infoKey ):
508 """ 509 Get lowest c.infos[infokey] value. 510 511 @param infoKey: key for info dict 512 @type infoKey: str 513 514 @return: with lowest c.info['infoKey'] value 515 @rtype: Complex 516 """ 517 return self[ self.argmin( infoKey ) ]
518 519
520 - def getIndex( self, infoKey, value ):
521 """ 522 Get list position of Complex where c.info['infoKey'] == value 523 524 @param value: vaue to look for 525 @type value: any 526 @param infoKey: key for info dict 527 @type infoKey: str 528 529 @return: position in ComplexList where c.info['infoKey'] == value 530 @rtype: int 531 532 @raise AmbiguousMatch: ComplexNotFound, 533 if there are more or less than 1 matches 534 """ 535 l = self.filterEqual( infoKey, [ value ] ) 536 537 if len( l ) == 1: 538 return l[0] 539 540 if len( l ) > 1: 541 raise AmbiguousMatch('More than one Complexes match.') 542 543 raise ComplexNotFound("No matching Complex.")
544 545
546 - def getItem( self, infoKey, value ):
547 """ 548 Get Complex from ComplexList where c.info['infoKey'] == value 549 550 @param value: vaue to look for 551 @type value: any 552 @param infoKey: key for info dict 553 @type infoKey: str 554 555 @return: Complex where c.info['infoKey'] == value 556 @rtype: Complex 557 558 @raise AmbiguousMatch: ComplexNotFound, 559 if there are more or less than 1 matches 560 """ 561 return self[ self.getIndex( infoKey, value ) ]
562 563
564 - def take( self, indices ):
565 """ 566 Take the complexes specified by indices. 567 568 @param indices: array/list of int, list positions 569 @type indices: [int] 570 571 @return: ComplexList with all items specified. 572 @rtype: ComplexList 573 """ 574 r = self.__class__( [ self[i] for i in indices ] ) 575 576 return r
577 578
579 - def toDict( self, infoKey ):
580 """ 581 Convert list into dict indexed by a certain info-record value. 582 If several Complexes have the same value, the result will have 583 a list registered for this key. 584 585 EXAMPLE: 586 >>> clst.toDict('soln') -> {1:Complex, 3:Complex, solnN:Complex} 587 588 @param infoKey: key of info dict in Complexes 589 @type infoKey: str 590 591 @return: { info1:Complex, info2:Complex, info3:[Complex, Complex].. } 592 @rtype: dict 593 """ 594 result = {} 595 for c in self: 596 t.dictAdd( result, c.info[infoKey], c ) 597 598 return result
599 600
601 - def toList( self ):
602 """ 603 Convert ComplexList to simple python list of Complexes 604 605 @return: simple python list of Complexes 606 @rtype: [Complex] 607 """ 608 return list( self )
609 610
611 - def __maskNone( self, l1, l2 ):
612 """ 613 Take out positions from l1 and l2 that are None in either of them. 614 615 @param l1: first ComplexList 616 @type l1: ComplexList 617 @param l2: second ComplexList 618 @type l2: ComplexList 619 620 @return: (l1, l2) modified lists 621 @rtype: ComplexList, ComplexList 622 """ 623 r1, r2 = [],[] 624 625 for i in range( len(l1)): 626 if l1[i] != None and l2[i] != None: 627 r1 += [ l1[i] ] 628 r2 += [ l2[i] ] 629 630 return r1, r2
631 632
633 - def plot( self, xkey, *ykey, **arg ):
634 """ 635 Plot pairs of info values. The additional arg arguments are handed 636 over to biggles.Points().:: 637 plot( xkey, [ykey1, ykey2..],[arg1=x, arg2=y]) -> biggles.FramedPlot 638 639 @param xkey: key specifying x-values 640 @type xkey: str 641 @param ykey: key specifying y-values 642 @type ykey: str OR [str] 643 @param arg: additional biggles arguments 644 @type arg: key=value 645 646 @return: biggles plot object 647 @rtype: biggles.FramedPlot() 648 """ 649 plot = biggles.FramedPlot() 650 651 plot.xlabel = xkey 652 653 colors = t.colorSpectrum( len( ykey ) ) 654 655 if not 'size' in arg: 656 arg['size'] = 1 657 658 for i in range( len(ykey)): 659 660 x = self.valuesOf( xkey ) 661 y = self.valuesOf( ykey[i] ) 662 663 x, y = self.__maskNone( x, y ) 664 665 plot.add( biggles.Points( x, y, color=colors[i], **arg ) ) 666 667 plot.add( biggles.PlotLabel( 0.2, 0.95-i/8.0, ykey[i], 668 color=colors[i] ) ) 669 670 return plot
671 672
673 - def plotArray( self, xkey, *ykey, **arg ):
674 """ 675 Plot pairs of info values.:: 676 plot( xkey, [ykey1, ykey2..],[arg1=x, arg2=y]) -> biggles.FramedArray 677 678 @param xkey: key specifying x-values 679 @type xkey: str 680 @param ykey: key specifying y-values 681 @type ykey: str OR [str] 682 @param arg: additional biggles arguments 683 @type arg: key=value 684 685 @return: biggles plot object 686 @rtype: biggles.FramedArray 687 """ 688 plot = biggles.FramedArray( len(ykey),1 ) 689 690 plot.xlabel = xkey 691 692 colors = t.colorSpectrum( len( ykey ) ) 693 694 if not 'size' in arg: 695 arg['size'] = 1 696 697 for i in range( len(ykey)): 698 699 x = self.valuesOf( xkey ) 700 y = self.valuesOf( ykey[i] ) 701 702 x, y = self.__maskNone( x, y ) 703 704 plot[i,0].add( biggles.Points( x, y, color=colors[i], **arg ) ) 705 706 plot[i,0].add( biggles.PlotLabel( 0.2, 0.95, ykey[i], 707 color=colors[i] ) ) 708 709 return plot
710 711 712 ############# 713 ## TESTING 714 ############# 715 import Biskit.test as BT 716
717 -class Test(BT.BiskitTest):
718 """Test case""" 719
720 - def test_ComplexList(self):
721 """Dock.ComplexList test""" 722 self.cl = t.Load( t.testRoot() + "/dock/hex/complexes.cl" ) 723 724 ## number of clusters among the 100 best (lowest rmsd) solutions 725 self.cl_sorted = self.cl.sortBy( 'rms' ) 726 self.hex_clst = self.cl_sorted.valuesOf( 'hex_clst', 727 indices=range(100), 728 unique=1 ) 729 730 if self.local: 731 self.p = self.cl.plot( 'rms', 'hex_eshape', 'hex_etotal' ) 732 self.p.show() 733 734 self.assertEqual( len( self.hex_clst ), 36)
735 736 if __name__ == '__main__': 737 738 BT.localTest() 739