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