1 package Fingerprints::TopologicalPharmacophoreAtomTripletsFingerprints; 2 # 3 # $RCSfile: TopologicalPharmacophoreAtomTripletsFingerprints.pm,v $ 4 # $Date: 2015/02/28 20:48:54 $ 5 # $Revision: 1.30 $ 6 # 7 # Author: Manish Sud <msud@san.rr.com> 8 # 9 # Copyright (C) 2015 Manish Sud. All rights reserved. 10 # 11 # This file is part of MayaChemTools. 12 # 13 # MayaChemTools is free software; you can redistribute it and/or modify it under 14 # the terms of the GNU Lesser General Public License as published by the Free 15 # Software Foundation; either version 3 of the License, or (at your option) any 16 # later version. 17 # 18 # MayaChemTools is distributed in the hope that it will be useful, but without 19 # any warranty; without even the implied warranty of merchantability of fitness 20 # for a particular purpose. See the GNU Lesser General Public License for more 21 # details. 22 # 23 # You should have received a copy of the GNU Lesser General Public License 24 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or 25 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330, 26 # Boston, MA, 02111-1307, USA. 27 # 28 29 use strict; 30 use Carp; 31 use Exporter; 32 use Fingerprints::Fingerprints; 33 use TextUtil (); 34 use MathUtil (); 35 use Molecule; 36 use AtomTypes::FunctionalClassAtomTypes; 37 38 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 39 40 @ISA = qw(Fingerprints::Fingerprints Exporter); 41 @EXPORT = qw(); 42 @EXPORT_OK = qw(); 43 44 %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]); 45 46 # Setup class variables... 47 my($ClassName); 48 _InitializeClass(); 49 50 # Overload Perl functions... 51 use overload '""' => 'StringifyTopologicalPharmacophoreAtomTripletsFingerprints'; 52 53 # Class constructor... 54 sub new { 55 my($Class, %NamesAndValues) = @_; 56 57 # Initialize object... 58 my $This = $Class->SUPER::new(); 59 bless $This, ref($Class) || $Class; 60 $This->_InitializeTopologicalPharmacophoreAtomTripletsFingerprints(); 61 62 $This->_InitializeTopologicalPharmacophoreAtomTripletsFingerprintsProperties(%NamesAndValues); 63 64 return $This; 65 } 66 67 # Initialize object data... 68 # 69 sub _InitializeTopologicalPharmacophoreAtomTripletsFingerprints { 70 my($This) = @_; 71 72 # Type of fingerprint... 73 $This->{Type} = 'TopologicalPharmacophoreAtomTriplets'; 74 75 # Type of vector... 76 $This->{VectorType} = 'FingerprintsVector'; 77 78 # AtomTripletsSetSizeToUse... 79 # 80 # ArbitrarySize - Corrresponds to atom triplets with non-zero count 81 # FixedSize - Corresponds to all atom triplets with zero and non-zero count 82 # 83 # Possible values: ArbitrarySize or FixedSize. Default: ArbitrarySize 84 # 85 $This->{AtomTripletsSetSizeToUse} = ''; 86 87 # 88 # OrderedNumericalValues - For ArbitrarySize value of AtomTripletsSetSizeToUse 89 # NumericalValues - For FixedSize value of AtomTripletsSetSizeToUse 90 # 91 # Possible values: OrderedNumericalValues or NumericalValues. Default: NumericalValues 92 # 93 $This->{FingerprintsVectorType} = ''; 94 95 # Minimum and maximum bond distance between pharmacophore atom pairs corresponding to 96 # atom triplets and distance bin size used for binning distances. 97 # 98 # In order to distribute distance bins of equal size, the last bin is allowed to go past the 99 # maximum distance specified by upto distance bin size. 100 # 101 # The default MinDistance and MaxDistance values of 1 and 10 with DistanceBinSize of 102 # 2 [ Ref 70 ] generates the following 5 distance bins: [1, 2] [3, 4] [5, 6] [7, 8] [9 10] 103 # 104 $This->{MinDistance} = 1; 105 $This->{MaxDistance} = 10; 106 107 # Distance bin size used for binning distances... 108 # 109 $This->{DistanceBinSize} = 2; 110 111 # Determines whether to apply triangle inequality to distances triplets during basis set generation... 112 # 113 $This->{UseTriangleInequality} = 1; 114 115 # Initialize pharmacophore atom types information... 116 $This->_InitializeToplogicalPharmacophoreAtomTypesInformation(); 117 118 # Pharmacophore types assigned to each heavy atom... 119 # 120 %{$This->{AssignedAtomTypes}} = (); 121 122 # All pharmacophore atom triplets between minimum and maximum distance... 123 # 124 %{$This->{AtomTriplets}} = (); 125 @{$This->{AtomTriplets}{IDs}} = (); 126 %{$This->{AtomTriplets}{Count}} = (); 127 } 128 129 # Initialize class ... 130 sub _InitializeClass { 131 #Class name... 132 $ClassName = __PACKAGE__; 133 } 134 135 # Initialize object properties.... 136 sub _InitializeTopologicalPharmacophoreAtomTripletsFingerprintsProperties { 137 my($This, %NamesAndValues) = @_; 138 139 my($Name, $Value, $MethodName); 140 while (($Name, $Value) = each %NamesAndValues) { 141 $MethodName = "Set${Name}"; 142 $This->$MethodName($Value); 143 } 144 145 # Make sure molecule object was specified... 146 if (!exists $NamesAndValues{Molecule}) { 147 croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule..."; 148 } 149 $This->_InitializeTopologicalPharmacophoreAtomTripletsFingerprintsVector(); 150 151 return $This; 152 } 153 154 # Initialize fingerprints vector... 155 # 156 sub _InitializeTopologicalPharmacophoreAtomTripletsFingerprintsVector { 157 my($This) = @_; 158 159 if (!$This->{AtomTripletsSetSizeToUse}) { 160 $This->{AtomTripletsSetSizeToUse} = 'ArbitrarySize'; 161 } 162 163 # Vector type and type of values... 164 $This->{VectorType} = 'FingerprintsVector'; 165 166 if ($This->{AtomTripletsSetSizeToUse} =~ /^FixedSize$/i) { 167 $This->{FingerprintsVectorType} = 'OrderedNumericalValues'; 168 } 169 else { 170 $This->{FingerprintsVectorType} = 'NumericalValues'; 171 } 172 173 $This->_InitializeFingerprintsVector(); 174 } 175 176 # Set atom parits set size to use... 177 # 178 sub SetAtomTripletsSetSizeToUse { 179 my($This, $Value) = @_; 180 181 if ($This->{AtomTripletsSetSizeToUse}) { 182 croak "Error: ${ClassName}->SetAtomTripletsSetSizeToUse: Can't change size: It's already set..."; 183 } 184 185 if ($Value !~ /^(ArbitrarySize|FixedSize)$/i) { 186 croak "Error: ${ClassName}->SetAtomTripletsSetSizeToUse: Unknown AtomTripletsSetSizeToUse value: $Value; Supported values: ArbitrarySize or FixedSize"; 187 } 188 189 $This->{AtomTripletsSetSizeToUse} = $Value; 190 191 return $This; 192 } 193 194 # Initialize topological atom types, generated by AtomTypes::FunctionalClassAtomTypes 195 # class, to use for atom triplets fingerprint generation... 196 # 197 # Let: 198 # HBD: HydrogenBondDonor 199 # HBA: HydrogenBondAcceptor 200 # PI : PositivelyIonizable 201 # NI : NegativelyIonizable 202 # Ar : Aromatic 203 # Hal : Halogen 204 # H : Hydrophobic 205 # RA : RingAtom 206 # CA : ChainAtom 207 # 208 # Then: 209 # 210 # Functiononal class atom type specification for an atom corresponds to: 211 # 212 # Ar.CA.H.HBA.HBD.Hal.NI.PI.RA 213 # 214 # Default pharmacophore atom types [ Ref 71 ] to use for atom triplets fingerprint generation 215 # are: HBD, HBA, PI, NI, H, Ar 216 # 217 # FunctionalAtomTypes are assigned using the following definitions [ Ref 60-61, Ref 65-66 ]: 218 # 219 # HydrogenBondDonor: NH, NH2, OH 220 # HydrogenBondAcceptor: N[!H], O 221 # PositivelyIonizable: +, NH2 222 # NegativelyIonizable: -, C(=O)OH, S(=O)OH, P(=O)OH 223 # 224 sub _InitializeToplogicalPharmacophoreAtomTypesInformation { 225 my($This) = @_; 226 227 # Default pharmacophore atom types to use for atom triplets fingerprint generation 228 # are: HBD, HBA, PI, NI, H, Ar 229 # 230 @{$This->{AtomTypesToUse}} = (); 231 @{$This->{AtomTypesToUse}} = sort ('HBD', 'HBA', 'PI', 'NI', 'H', 'Ar'); 232 233 return $This; 234 } 235 236 # Set atom types to use for atom triplets... 237 # 238 sub SetAtomTypesToUse { 239 my($This, @Values) = @_; 240 my($FirstValue, $TypeOfFirstValue, $AtomType, $SpecifiedAtomType, @SpecifiedAtomTypes, @AtomTypesToUse); 241 242 if (!@Values) { 243 carp "Warning: ${ClassName}->SetAtomTypesToUse: No values specified..."; 244 return; 245 } 246 247 $FirstValue = $Values[0]; 248 $TypeOfFirstValue = ref $FirstValue; 249 250 @SpecifiedAtomTypes = (); 251 @AtomTypesToUse = (); 252 253 if ($TypeOfFirstValue =~ /^ARRAY/) { 254 push @SpecifiedAtomTypes, @{$FirstValue}; 255 } 256 else { 257 push @SpecifiedAtomTypes, @Values; 258 } 259 260 # Make sure specified AtomTypes are valid... 261 for $SpecifiedAtomType (@SpecifiedAtomTypes) { 262 if (!AtomTypes::FunctionalClassAtomTypes::IsFunctionalClassAvailable($SpecifiedAtomType)) { 263 croak "Error: ${ClassName}->SetAtomTypesToUse: Specified atom type, $SpecifiedAtomType, is not supported...\n "; 264 } 265 $AtomType = $SpecifiedAtomType; 266 push @AtomTypesToUse, $AtomType; 267 } 268 269 # Set atom types to use... 270 @{$This->{AtomTypesToUse}} = (); 271 push @{$This->{AtomTypesToUse}}, sort @AtomTypesToUse; 272 273 return $This; 274 } 275 276 # Set minimum distance for pharmacophore atom pairs in atom triplets... 277 # 278 sub SetMinDistance { 279 my($This, $Value) = @_; 280 281 if (!TextUtil::IsPositiveInteger($Value)) { 282 croak "Error: ${ClassName}->SetMinDistance: MinDistance value, $Value, is not valid: It must be a positive integer..."; 283 } 284 $This->{MinDistance} = $Value; 285 286 return $This; 287 } 288 289 # Set maximum distance for pharmacophore atom pairs in atom triplets... 290 # 291 sub SetMaxDistance { 292 my($This, $Value) = @_; 293 294 if (!TextUtil::IsPositiveInteger($Value)) { 295 croak "Error: ${ClassName}->SetMaxDistance: MaxDistance value, $Value, is not valid: It must be a positive integer..."; 296 } 297 $This->{MaxDistance} = $Value; 298 299 return $This; 300 } 301 302 # Set distance bin size for binning pharmacophore atom pair distances in atom triplets... 303 # 304 sub SetDistanceBinSize { 305 my($This, $Value) = @_; 306 307 if (!TextUtil::IsPositiveInteger($Value)) { 308 croak "Error: ${ClassName}->SetDistanceBinSize: DistanceBinSize value, $Value, is not valid: It must be a positive integer..."; 309 } 310 $This->{DistanceBinSize} = $Value; 311 312 return $This; 313 } 314 315 # Generate fingerprints description... 316 # 317 sub GetDescription { 318 my($This) = @_; 319 320 # Is description explicity set? 321 if (exists $This->{Description}) { 322 return $This->{Description}; 323 } 324 325 # Generate fingerprints description... 326 327 return "$This->{Type}:$This->{AtomTripletsSetSizeToUse}:MinDistance$This->{MinDistance}:MaxDistance$This->{MaxDistance}"; 328 } 329 330 # Generate topological pharmacophore atom triplets [ Ref 66, Ref 68-71 ] fingerprints... 331 # 332 # Let: 333 # 334 # P = Any of the supported pharmacophore atom types 335 # 336 # Px = Pharmacophore atom x 337 # Py = Pharmacophore atom y 338 # Pz = Pharmacophore atom z 339 # 340 # Dxy = Distance or lower bound of binned distance between Px and Py 341 # Dxz = Distance or lower bound of binned distance between Px and Pz 342 # Dyz = Distance or lower bound of binned distance between Py and Pz 343 # 344 # Then: 345 # PxDyz-PyDxz-PzDxy = Pharmacophore atom triplet ID for atoms Px, Py and Pz 346 # 347 # For example: H1-H1-H1, H2-HBA-H2 and so on 348 # 349 # Methodology: 350 # . Generate a distance matrix. 351 # . Using specified minimum, maximum and distance bin size, generate a binned distance 352 # matrix from distance matrix. The lower distance bound on the distance bin is used 353 # in the binned distance matrix and atom triplet IDs. 354 # . Assign pharmacophore atom types to all the atoms. 355 # . Initialize pharmacophore atom triplets basis set for all unique triplets constituting 356 # atom pairs binned distances between minimum and maximum distance. 357 # . Optionally, trinagle inequality is also implied which means: 358 # . Distance or binned distance between any two pairs in a triplet must be less than the 359 # sum of distances or binned distances between other two pairs and greater than the 360 # difference of distances between other pairs. 361 # . Using binned distance matrix and pharmacophore atom types, count occurance of 362 # unique atom triplets. 363 # 364 # Notes: 365 # . Hydrogen atoms are ignored during the fingerprint generation. 366 # 367 sub GenerateFingerprints { 368 my($This) = @_; 369 370 if ($This->{MinDistance} > $This->{MaxDistance}) { 371 croak "Error: ${ClassName}->GenerateTopologicalPharmacophoreAtomTripletsFingerprints: No fingerpritns generated: MinDistance, $This->{MinDistance}, must be <= MaxDistance, $This->{MaxDistance}..."; 372 } 373 374 # Cache appropriate molecule data... 375 $This->_SetupMoleculeDataCache(); 376 377 # Generate distance matrix... 378 if (!$This->_SetupDistanceMatrix()) { 379 carp "Warning: ${ClassName}->GenerateFingerprints: Fingerprints generation didn't succeed: Couldn't generate distance matrix..."; 380 return $This; 381 } 382 383 # Generate binned distance matrix... 384 $This->_GenerateBinnedDistanceMatrix(); 385 386 # Assign pharmacohore atom types to all heavy atoms... 387 $This->_AssignPharmacophoreAtomTypes(); 388 389 # Initialize values of all possible pharmacohore atom triplets... 390 $This->_InitializePharmacophoreAtomTriplets(); 391 392 # Count atom triplets... 393 $This->_CountPharmacohoreAtomTriplets(); 394 395 # Set final fingerprints... 396 $This->_SetFinalFingerprints(); 397 398 # Clear cached molecule data... 399 $This->_ClearMoleculeDataCache(); 400 401 return $This; 402 } 403 404 # Setup distance matrix... 405 # 406 sub _SetupDistanceMatrix { 407 my($This) = @_; 408 409 $This->{DistanceMatrix} = $This->GetMolecule()->GetDistanceMatrix(); 410 411 if (!$This->{DistanceMatrix}) { 412 return undef; 413 } 414 415 return $This; 416 } 417 418 # Generate binned distance matrix for distances with in the specified distance ranges... 419 # 420 sub _GenerateBinnedDistanceMatrix { 421 my($This) = @_; 422 my($DistanceMatrix, $BinnedDistanceMatrix, $NumOfRows, $NumOfCols, $RowIndex, $ColIndex, $SkipIndexCheck); 423 424 $DistanceMatrix = $This->{DistanceMatrix}; 425 ($NumOfRows, $NumOfCols) = $DistanceMatrix->GetSize(); 426 427 # Initialize binned distance matrix... 428 $BinnedDistanceMatrix = new Matrix($NumOfRows, $NumOfCols); 429 430 # Setup distance to binned distance map... 431 my($BinnedDistance, $Distance, %DistanceToBinnedDistance); 432 %DistanceToBinnedDistance = (); 433 for ($BinnedDistance = $This->{MinDistance}; $BinnedDistance <= $This->{MaxDistance}; $BinnedDistance += $This->{DistanceBinSize}) { 434 for $Distance ($BinnedDistance .. ($BinnedDistance + $This->{DistanceBinSize} - 1)) { 435 $DistanceToBinnedDistance{$Distance} = $BinnedDistance; 436 } 437 } 438 439 # Generate binned distance matrix... 440 $SkipIndexCheck = 0; 441 for $RowIndex (0 .. ($NumOfRows - 1) ) { 442 COLINDEX: for $ColIndex (($RowIndex + 1) .. ($NumOfCols - 1) ) { 443 $Distance = $DistanceMatrix->GetValue($RowIndex, $ColIndex, $SkipIndexCheck); 444 if ($Distance < $This->{MinDistance} || $Distance > $This->{MaxDistance}) { 445 next COLINDEX; 446 } 447 $BinnedDistance = $DistanceToBinnedDistance{$Distance}; 448 $BinnedDistanceMatrix->SetValue($RowIndex, $ColIndex, $BinnedDistance, $SkipIndexCheck); 449 $BinnedDistanceMatrix->SetValue($ColIndex, $RowIndex, $BinnedDistance, $SkipIndexCheck); 450 } 451 } 452 453 $This->{BinnedDistanceMatrix} = $BinnedDistanceMatrix; 454 455 return $This; 456 } 457 458 # Assign pharmacohore atom types to all heavy atoms... 459 # 460 sub _AssignPharmacophoreAtomTypes { 461 my($This) = @_; 462 my($Atom, $AtomID, $AtomType, $FunctionalClassAtomTypes); 463 464 # Assign topological pharmacophore atom types... 465 $FunctionalClassAtomTypes = new AtomTypes::FunctionalClassAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => 1, 'FunctionalClassesToUse' => $This->{AtomTypesToUse}); 466 $FunctionalClassAtomTypes->AssignAtomTypes(); 467 468 %{$This->{AssignedAtomTypes}} = (); 469 470 ATOM: for $Atom (@{$This->{Atoms}}) { 471 if ($Atom->IsHydrogen()) { 472 next ATOM; 473 } 474 $AtomID = $Atom->GetID(); 475 476 my(@AtomTypes); 477 @AtomTypes = (); 478 479 $AtomType = $FunctionalClassAtomTypes->GetAtomType($Atom); 480 if ($AtomType && $AtomType !~ /^None$/i) { 481 push @AtomTypes, split /\./, $AtomType; 482 } 483 # Assign phramacophore types list to atom... 484 $This->{AssignedAtomTypes}{$AtomID} = \@AtomTypes; 485 } 486 return $This; 487 } 488 489 # Initialize pharmacophore atom triplets basis set for all unique triplets constituting atom pairs 490 # binned distances between minimum and maximum distance and optionally applying triangle 491 # inequality. The DistanceBinSize determines the size of the distance bins. The lower distance 492 # bound, along with specified pharmacophore types, is used during generation of atom triplet 493 # IDs. 494 # 495 # 496 sub _InitializePharmacophoreAtomTriplets { 497 my($This) = @_; 498 my($AtomType1, $AtomType2, $AtomType3, $BinnedDistance12, $BinnedDistance13, $BinnedDistance23, $AtomTripletID); 499 500 # Initialize atom triplets information... 501 for ($BinnedDistance12 = $This->{MinDistance}; $BinnedDistance12 <= $This->{MaxDistance}; $BinnedDistance12 += $This->{DistanceBinSize}) { 502 for ($BinnedDistance13 = $This->{MinDistance}; $BinnedDistance13 <= $This->{MaxDistance}; $BinnedDistance13 += $This->{DistanceBinSize}) { 503 DISTANCE23: for ($BinnedDistance23 = $BinnedDistance12; $BinnedDistance23 <= $This->{MaxDistance}; $BinnedDistance23 += $This->{DistanceBinSize}) { 504 if ($This->{UseTriangleInequality} && !$This->_DoDistancesSatisfyTriangleInequality($BinnedDistance12, $BinnedDistance13, $BinnedDistance23)) { 505 next DISTANCE23; 506 } 507 for $AtomType1 (@{$This->{AtomTypesToUse}}) { 508 for $AtomType2 (@{$This->{AtomTypesToUse}}) { 509 ATOMTYPE3: for $AtomType3 (@{$This->{AtomTypesToUse}}) { 510 $AtomTripletID = $This->_GetAtomTripletID($AtomType1, $BinnedDistance23, $AtomType2, $BinnedDistance13, $AtomType3, $BinnedDistance12); 511 if (exists $This->{AtomTriplets}{Count}{$AtomTripletID}) { 512 next ATOMTYPE3; 513 } 514 # Unique atom triplets information... 515 push @{$This->{AtomTriplets}{IDs}}, $AtomTripletID; 516 $This->{AtomTriplets}{Count}{$AtomTripletID} = 0; 517 } 518 } 519 } 520 } 521 } 522 } 523 return $This; 524 } 525 526 # Check triangle inequality... 527 # 528 sub _DoDistancesSatisfyTriangleInequality { 529 my($This, $Distance1, $Distance2, $Distance3) = @_; 530 531 if ( !($Distance1 > abs($Distance2 - $Distance3) && $Distance1 < ($Distance2 + $Distance3)) ) { 532 return 0; 533 } 534 if ( !($Distance2 > abs($Distance1 - $Distance3) && $Distance2 < ($Distance1 + $Distance3)) ) { 535 return 0; 536 } 537 if ( !($Distance3 > abs($Distance1 - $Distance2) && $Distance3 < ($Distance1 + $Distance2)) ) { 538 return 0; 539 } 540 return 1; 541 } 542 543 # Count pharmacophore atom triplets... 544 # 545 sub _CountPharmacohoreAtomTriplets { 546 my($This) = @_; 547 my($NumOfAtoms, $AtomIndex1, $AtomIndex2, $AtomIndex3, $AtomID1, $AtomID2, $AtomID3, $AtomType1, $AtomType2, $AtomType3, $BinnedDistance12, $BinnedDistance13, $BinnedDistance23, $SkipIndexCheck, $BinnedDistanceMatrix, $AtomTripletID); 548 549 $NumOfAtoms = @{$This->{Atoms}}; 550 $BinnedDistanceMatrix = $This->{BinnedDistanceMatrix}; 551 $SkipIndexCheck = 0; 552 553 ATOMINDEX1: for $AtomIndex1 (0 .. ($NumOfAtoms - 1)) { 554 $AtomID1 = $This->{AtomIndexToID}{$AtomIndex1}; 555 if ( !((exists($This->{AssignedAtomTypes}{$AtomID1}) && @{$This->{AssignedAtomTypes}{$AtomID1}})) ) { 556 next ATOMINDEX1; 557 } 558 559 ATOMINDEX2: for $AtomIndex2 (($AtomIndex1 + 1) .. ($NumOfAtoms - 1)) { 560 $AtomID2 = $This->{AtomIndexToID}{$AtomIndex2}; 561 if ( !((exists($This->{AssignedAtomTypes}{$AtomID2}) && @{$This->{AssignedAtomTypes}{$AtomID2}})) ) { 562 next ATOMINDEX2; 563 } 564 $BinnedDistance12 = $BinnedDistanceMatrix->GetValue($AtomIndex1, $AtomIndex2, $SkipIndexCheck); 565 if ($BinnedDistance12 == 0) { 566 next ATOMINDEX2; 567 } 568 569 ATOMINDEX3: for $AtomIndex3 (($AtomIndex2 + 1) .. ($NumOfAtoms - 1)) { 570 $AtomID3 = $This->{AtomIndexToID}{$AtomIndex3}; 571 if ( !((exists($This->{AssignedAtomTypes}{$AtomID3}) && @{$This->{AssignedAtomTypes}{$AtomID3}})) ) { 572 next ATOMINDEX3; 573 } 574 $BinnedDistance13 = $BinnedDistanceMatrix->GetValue($AtomIndex1, $AtomIndex3, $SkipIndexCheck); 575 $BinnedDistance23 = $BinnedDistanceMatrix->GetValue($AtomIndex2, $AtomIndex3, $SkipIndexCheck); 576 if ($BinnedDistance13 == 0 || $BinnedDistance23 == 0) { 577 next ATOMINDEX3; 578 } 579 if ($This->{UseTriangleInequality} && !$This->_DoDistancesSatisfyTriangleInequality($BinnedDistance12, $BinnedDistance13, $BinnedDistance23)) { 580 next ATOMINDEX3; 581 } 582 583 # Go over possible pharmacohore triplets for the three pharmacophore atoms using the 584 # binned distances... 585 for $AtomType1 (@{$This->{AssignedAtomTypes}{$AtomID1}}) { 586 for $AtomType2 (@{$This->{AssignedAtomTypes}{$AtomID2}}) { 587 for $AtomType3 (@{$This->{AssignedAtomTypes}{$AtomID3}}) { 588 $AtomTripletID = $This->_GetAtomTripletID($AtomType1, $BinnedDistance23, $AtomType2, $BinnedDistance13, $AtomType3, $BinnedDistance12); 589 $This->{AtomTriplets}{Count}{$AtomTripletID} += 1; 590 } 591 } 592 } 593 } 594 } 595 } 596 return $This; 597 } 598 599 # Set final fingerpritns vector... 600 # 601 sub _SetFinalFingerprints { 602 my($This) = @_; 603 my($UseArbitrarySetSize, $ID, $Value, @IDs, @Values); 604 605 # Mark successful generation of fingerprints... 606 $This->{FingerprintsGenerated} = 1; 607 608 # Is it an ArbitraySize atom triplets set size? 609 $UseArbitrarySetSize = $This->{AtomTripletsSetSizeToUse} =~ /^ArbitrarySize$/i ? 1 : 0; 610 611 # Set atom triplet count values... 612 @IDs = (); @Values = (); 613 614 if ($UseArbitrarySetSize) { 615 ID: for $ID (@{$This->{AtomTriplets}{IDs}}) { 616 $Value = $This->{AtomTriplets}{Count}{$ID}; 617 if ($Value == 0) { 618 next ID; 619 } 620 push @IDs, $ID; 621 push @Values, $Value; 622 } 623 } 624 else { 625 @Values = map { $This->{AtomTriplets}{Count}{$_} } @{$This->{AtomTriplets}{IDs}}; 626 } 627 628 # Set atom triplet IDs for fingerprint vector... 629 if ($UseArbitrarySetSize) { 630 $This->{FingerprintsVector}->AddValueIDs(\@IDs); 631 } 632 else { 633 $This->{FingerprintsVector}->AddValueIDs(\@{$This->{AtomTriplets}{IDs}}); 634 } 635 636 # Set atom triplets count values for fingerprint vector... 637 $This->{FingerprintsVector}->AddValues(\@Values); 638 639 return $This; 640 } 641 642 # Return an array or reference to an array containing atom triplet IDs... 643 # 644 sub GetAtomTripletIDs { 645 my($This) = @_; 646 647 return wantarray ? @{$This->{AtomTriplets}{IDs}} : \@{$This->{AtomTriplets}{IDs}}; 648 } 649 650 # Get pharmacophore atom triplet ID corresponding to atom types and distances 651 # corresponding to atom triplet... 652 # 653 sub _GetAtomTripletID { 654 my($This, $Px, $Dyz, $Py, $Dxz, $Pz, $Dxy) = @_; 655 my($AtomTripletID, @AtomIDs); 656 657 @AtomIDs = (); 658 659 @AtomIDs = sort("${Px}${Dyz}", "${Py}${Dxz}", "${Pz}${Dxy}"); 660 $AtomTripletID = join "-", @AtomIDs; 661 662 return $AtomTripletID; 663 } 664 665 # Cache appropriate molecule data... 666 # 667 sub _SetupMoleculeDataCache { 668 my($This) = @_; 669 670 # Get all atoms including hydrogens to correctly map atom indices to atom IDs for 671 # usage of distance matrix. The hydrogen atoms are ignored during processing... 672 # 673 @{$This->{Atoms}} = $This->GetMolecule()->GetAtoms(); 674 675 # Get all atom IDs... 676 my(@AtomIDs); 677 @AtomIDs = (); 678 @AtomIDs = map { $_->GetID() } @{$This->{Atoms}}; 679 680 # Set AtomIndex to AtomID hash... 681 %{$This->{AtomIndexToID}} = (); 682 @{$This->{AtomIndexToID}}{ (0 .. $#AtomIDs) } = @AtomIDs; 683 684 return $This; 685 } 686 687 # Clear cached molecule data... 688 # 689 sub _ClearMoleculeDataCache { 690 my($This) = @_; 691 692 @{$This->{Atoms}} = (); 693 694 return $This; 695 } 696 697 698 # Return a string containg data for TopologicalPharmacophoreAtomTripletsFingerprints object... 699 # 700 sub StringifyTopologicalPharmacophoreAtomTripletsFingerprints { 701 my($This) = @_; 702 my($FingerprintsString, $UseTriangleInequality); 703 704 # Type of fingerprint... 705 $FingerprintsString = "Fingerprint type: $This->{Type}; AtomTripletsSetSizeToUse: $This->{AtomTripletsSetSizeToUse}"; 706 707 # Distances information... 708 $FingerprintsString .= "; MinDistance: $This->{MinDistance}; MaxDistance: $This->{MaxDistance}; DistanceBinSize: $This->{DistanceBinSize}; UseTriangleInequality: " . ($This->{UseTriangleInequality} ? "Yes" : "No"); 709 710 # Pharmacophore atom type labels and description... 711 my($AtomType, @AtomTypes, @AtomTypesOrder, %AvailableAtomTypes); 712 713 @AtomTypesOrder = AtomTypes::FunctionalClassAtomTypes::GetFunctionalClassesOrder(); 714 %AvailableAtomTypes = AtomTypes::FunctionalClassAtomTypes::GetAvailableFunctionalClasses(); 715 716 @AtomTypes = (); 717 for $AtomType (@AtomTypesOrder) { 718 push @AtomTypes, "$AtomType: $AvailableAtomTypes{$AtomType}"; 719 } 720 721 $FingerprintsString .= "; AtomTypesToUse: <" . TextUtil::JoinWords(\@{$This->{AtomTypesToUse}}, ", ", 0) . ">"; 722 $FingerprintsString .= "; AtomTypesOrder: <" . TextUtil::JoinWords(\@AtomTypesOrder, ", ", 0) . ">"; 723 $FingerprintsString .= "; AvailableAtomTypes: <" . TextUtil::JoinWords(\@AtomTypes, ", ", 0) . ">"; 724 725 # Total number of pharmacophore atom triplets... 726 $FingerprintsString .= "; NumOfAtomTriplets: " . $This->{FingerprintsVector}->GetNumOfValues(); 727 728 # FingerprintsVector... 729 $FingerprintsString .= "; FingerprintsVector: < $This->{FingerprintsVector} >"; 730 731 return $FingerprintsString; 732 } 733