MayaChemTools

   1 package Fingerprints::TopologicalAtomTorsionsFingerprints;
   2 #
   3 # $RCSfile: TopologicalAtomTorsionsFingerprints.pm,v $
   4 # $Date: 2015/02/28 20:48:54 $
   5 # $Revision: 1.26 $
   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 Molecule;
  35 use AtomTypes::AtomicInvariantsAtomTypes;
  36 use AtomTypes::DREIDINGAtomTypes;
  37 use AtomTypes::EStateAtomTypes;
  38 use AtomTypes::FunctionalClassAtomTypes;
  39 use AtomTypes::MMFF94AtomTypes;
  40 use AtomTypes::SLogPAtomTypes;
  41 use AtomTypes::SYBYLAtomTypes;
  42 use AtomTypes::TPSAAtomTypes;
  43 use AtomTypes::UFFAtomTypes;
  44 
  45 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  46 
  47 @ISA = qw(Fingerprints::Fingerprints Exporter);
  48 @EXPORT = qw();
  49 @EXPORT_OK = qw();
  50 
  51 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
  52 
  53 # Setup class variables...
  54 my($ClassName);
  55 _InitializeClass();
  56 
  57 # Overload Perl functions...
  58 use overload '""' => 'StringifyTopologicalAtomTorsionsFingerprints';
  59 
  60 # Class constructor...
  61 sub new {
  62   my($Class, %NamesAndValues) = @_;
  63 
  64   # Initialize object...
  65   my $This = $Class->SUPER::new();
  66   bless $This, ref($Class) || $Class;
  67   $This->_InitializeTopologicalAtomTorsionsFingerprints();
  68 
  69   $This->_InitializeTopologicalAtomTorsionsFingerprintsProperties(%NamesAndValues);
  70 
  71   return $This;
  72 }
  73 
  74 # Initialize object data...
  75 #
  76 sub _InitializeTopologicalAtomTorsionsFingerprints {
  77   my($This) = @_;
  78 
  79   # Type of fingerprint...
  80   $This->{Type} = 'TopologicalAtomTorsions';
  81 
  82   # Type of vector...
  83   $This->{VectorType} = 'FingerprintsVector';
  84 
  85   # Type of FingerprintsVector...
  86   $This->{FingerprintsVectorType} = 'NumericalValues';
  87 
  88   # Atom identifier type to use for atom IDs in atom torsions...
  89   #
  90   # Currently supported values are: AtomicInvariantsAtomTypes, DREIDINGAtomTypes,
  91   # EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes,
  92   # SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes
  93   #
  94   $This->{AtomIdentifierType} = '';
  95 
  96   # Atom types assigned to each heavy atom...
  97   #
  98   %{$This->{AssignedAtomTypes}} = ();
  99 
 100   # Final unique atom torsions...
 101   #
 102   @{$This->{AtomTorsionsIDs}} = ();
 103   %{$This->{AtomTorsionsCount}} = ();
 104 }
 105 
 106 # Initialize class ...
 107 sub _InitializeClass {
 108   #Class name...
 109   $ClassName = __PACKAGE__;
 110 }
 111 
 112 # Initialize object properties....
 113 sub _InitializeTopologicalAtomTorsionsFingerprintsProperties {
 114   my($This, %NamesAndValues) = @_;
 115 
 116   my($Name, $Value, $MethodName);
 117   while (($Name, $Value) = each  %NamesAndValues) {
 118     $MethodName = "Set${Name}";
 119     $This->$MethodName($Value);
 120   }
 121 
 122   # Make sure molecule object was specified...
 123   if (!exists $NamesAndValues{Molecule}) {
 124     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule...";
 125   }
 126   if (!exists $NamesAndValues{AtomIdentifierType}) {
 127     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying AtomIdentifierType...";
 128   }
 129 
 130   $This->_InitializeFingerprintsVector();
 131 
 132   return $This;
 133 }
 134 
 135 # Set atom identifier type..
 136 #
 137 sub SetAtomIdentifierType {
 138   my($This, $IdentifierType) = @_;
 139 
 140   if ($IdentifierType !~ /^(AtomicInvariantsAtomTypes|DREIDINGAtomTypes|EStateAtomTypes|FunctionalClassAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
 141     croak "Error: ${ClassName}->SetAtomIdentifierType: Specified value, $IdentifierType, for AtomIdentifierType is not vaild. Supported types in current release of MayaChemTools: AtomicInvariantsAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, and UFFAtomTypes.";
 142   }
 143 
 144   if ($This->{AtomIdentifierType}) {
 145     croak "Error: ${ClassName}->SeAtomIdentifierType: Can't change intial atom identifier type:  It's already set...";
 146   }
 147 
 148   $This->{AtomIdentifierType} = $IdentifierType;
 149 
 150   # Initialize atom identifier type information...
 151   $This->_InitializeAtomIdentifierTypeInformation();
 152 
 153   return $This;
 154 }
 155 
 156 # Generate fingerprints description...
 157 #
 158 sub GetDescription {
 159   my($This) = @_;
 160 
 161   # Is description explicity set?
 162   if (exists $This->{Description}) {
 163     return $This->{Description};
 164   }
 165 
 166   # Generate fingerprints description...
 167 
 168   return "$This->{Type}:$This->{AtomIdentifierType}";
 169 }
 170 
 171 # Generate topological atom torsions [ Ref 58, Ref 72 ] fingerprints...
 172 #
 173 # Methodology:
 174 #   . Assign atom types to all the atoms.
 175 #   . Generate and count atom torsions.
 176 #
 177 # Notes:
 178 #   . Hydrogen atoms are ignored during the fingerprint generation.
 179 #
 180 sub GenerateFingerprints {
 181   my($This) = @_;
 182 
 183   # Cache appropriate molecule data...
 184   $This->_SetupMoleculeDataCache();
 185 
 186   # Assign atom types to all heavy atoms...
 187   if (!$This->_AssignAtomTypes()) {
 188     carp "Warning: ${ClassName}->GenerateFingerprints: $This->{AtomIdentifierType} fingerprints generation didn't succeed: Couldn't assign valid $This->{AtomIdentifierType} to all atoms...";
 189     return $This;
 190   }
 191 
 192   # Count atom torsions...
 193   $This->_GenerateAndCountAtomTorsions();
 194 
 195   # Set final fingerprints...
 196   $This->_SetFinalFingerprints();
 197 
 198   # Clear cached molecule data...
 199   $This->_ClearMoleculeDataCache();
 200 
 201   return $This;
 202 }
 203 
 204 # Assign appropriate atom types to all heavy atoms...
 205 #
 206 sub _AssignAtomTypes {
 207   my($This) = @_;
 208   my($SpecifiedAtomTypes, $Atom, $AtomID, $IgnoreHydrogens);
 209 
 210   %{$This->{AssignedAtomTypes}} = ();
 211   $IgnoreHydrogens = 1;
 212 
 213   $SpecifiedAtomTypes = undef;
 214 
 215   IDENTIFIERTYPE: {
 216     if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
 217       $SpecifiedAtomTypes = new AtomTypes::AtomicInvariantsAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens, 'AtomicInvariantsToUse' => $This->{AtomicInvariantsToUse});
 218       last IDENTIFIERTYPE;
 219     }
 220 
 221     if ($This->{AtomIdentifierType} =~ /^DREIDINGAtomTypes$/i) {
 222       $SpecifiedAtomTypes = new AtomTypes::DREIDINGAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 223       last IDENTIFIERTYPE;
 224     }
 225 
 226     if ($This->{AtomIdentifierType} =~ /^EStateAtomTypes$/i) {
 227       $SpecifiedAtomTypes = new AtomTypes::EStateAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 228       last IDENTIFIERTYPE;
 229     }
 230 
 231     if ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
 232       $SpecifiedAtomTypes = new AtomTypes::FunctionalClassAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens, 'FunctionalClassesToUse' => $This->{FunctionalClassesToUse});
 233       last IDENTIFIERTYPE;
 234     }
 235 
 236     if ($This->{AtomIdentifierType} =~ /^MMFF94AtomTypes$/i) {
 237       $SpecifiedAtomTypes = new AtomTypes::MMFF94AtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 238       last IDENTIFIERTYPE;
 239     }
 240 
 241     if ($This->{AtomIdentifierType} =~ /^SLogPAtomTypes$/i) {
 242       $SpecifiedAtomTypes = new AtomTypes::SLogPAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 243       last IDENTIFIERTYPE;
 244     }
 245     if ($This->{AtomIdentifierType} =~ /^SYBYLAtomTypes$/i) {
 246       $SpecifiedAtomTypes = new AtomTypes::SYBYLAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 247       last IDENTIFIERTYPE;
 248     }
 249 
 250     if ($This->{AtomIdentifierType} =~ /^TPSAAtomTypes$/i) {
 251       $SpecifiedAtomTypes = new AtomTypes::TPSAAtomTypes('Molecule' => $This->{Molecule}, 'IgnorePhosphorus' => 0, 'IgnoreSulfur' => 0);
 252       last IDENTIFIERTYPE;
 253     }
 254 
 255     if ($This->{AtomIdentifierType} =~ /^UFFAtomTypes$/i) {
 256       $SpecifiedAtomTypes = new AtomTypes::UFFAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 257       last IDENTIFIERTYPE;
 258     }
 259 
 260     croak "Error: ${ClassName}->_AssignAtomTypes: Unknown atom indentifier type $This->{AtomIdentifierType}...";
 261   }
 262 
 263   # Assign atom types...
 264   $SpecifiedAtomTypes->AssignAtomTypes();
 265 
 266   # Make sure atom types assignment is successful...
 267   if (!$SpecifiedAtomTypes->IsAtomTypesAssignmentSuccessful()) {
 268     return undef;
 269   }
 270 
 271   # Collect assigned atom types...
 272   ATOM: for $Atom (@{$This->{Atoms}}) {
 273     if ($Atom->IsHydrogen()) {
 274       next ATOM;
 275     }
 276     $AtomID = $Atom->GetID();
 277     $This->{AssignedAtomTypes}{$AtomID} = $SpecifiedAtomTypes->GetAtomType($Atom);
 278   }
 279 
 280   return $This;
 281 }
 282 
 283 # Count atom torsions involving non-hydrogen atoms by going over the structurally
 284 # unique atom torsions...
 285 #
 286 sub _GenerateAndCountAtomTorsions {
 287   my($This) = @_;
 288   my($Atom1, $Atom2, $Atom3, $Atom4, $AtomID1, $AtomID2, $AtomID3, $AtomID4, $AtomTorsionID, @Atom1Neighbors, @Atom2Neighbors, @Atom3Neighbors);
 289 
 290   # Setup a hash to track structurally unique atom torsions by atom IDs...
 291   %{$This->{StructurallyUniqueAtomTorsions}} = ();
 292 
 293   ATOM1: for $Atom1 (@{$This->{Atoms}}) {
 294     if ($Atom1->IsHydrogen()) {
 295       next ATOM1;
 296     }
 297     $AtomID1 = $Atom1->GetID();
 298     # Go over Atom1 neighbors other than Atom1...
 299     @Atom1Neighbors = $Atom1->GetNeighbors($Atom1);
 300     ATOM2: for $Atom2 (@Atom1Neighbors) {
 301       if ($Atom2->IsHydrogen()) {
 302         next ATOM2;
 303       }
 304       $AtomID2 = $Atom2->GetID();
 305       # Go over Atom2 neighbors other than Atom1 and Atom2...
 306       @Atom2Neighbors = $Atom2->GetNeighbors($Atom1, $Atom2);
 307       ATOM3: for $Atom3 (@Atom2Neighbors) {
 308         if ($Atom3->IsHydrogen()) {
 309           next ATOM3;
 310         }
 311         $AtomID3 = $Atom3->GetID();
 312         @Atom3Neighbors = $Atom3->GetNeighbors($Atom1, $Atom2, $Atom3);
 313         # Go over Atom3 neighbors other than Atom1, Atom2 and Atom3...
 314         ATOM4: for $Atom4 (@Atom3Neighbors) {
 315           if ($Atom4->IsHydrogen()) {
 316             next ATOM4;
 317           }
 318           $AtomID4 = $Atom4->GetID();
 319 
 320           # Is it a structurally unique torsion?
 321           if (!$This->_IsStructurallyUniqueTorsion($AtomID1, $AtomID2, $AtomID3, $AtomID4)) {
 322             next ATOM4;
 323           }
 324 
 325           # Track structurally unique torsions...
 326           $AtomTorsionID = $This->_GetAtomTorsionID($AtomID1, $AtomID2, $AtomID3, $AtomID4);
 327           if (exists $This->{AtomTorsionsCount}{$AtomTorsionID}) {
 328             $This->{AtomTorsionsCount}{$AtomTorsionID} += 1;
 329           }
 330           else {
 331             $This->{AtomTorsionsCount}{$AtomTorsionID} = 1;
 332           }
 333         }
 334       }
 335     }
 336   }
 337 
 338   return $This;
 339 }
 340 
 341 # Is it a structurally unique torsions?
 342 #
 343 # Notes:
 344 #  . For a torsion to be structurally unique which hasn't already been encountered,
 345 #    all the four atoms involved in the torsion must be new atoms. And this can be
 346 #    simply implemented by tracking the torsions using atom IDs and maintaining a
 347 #    hash of already encountered torsions using lexicographically smaller torsion ID
 348 #    consisting of four atom IDs.
 349 #
 350 sub _IsStructurallyUniqueTorsion {
 351   my($This, @AtomIDs) = @_;
 352   my($TorsionID, $ReverseTorsionID);
 353 
 354   $TorsionID = join "-", @AtomIDs;
 355   $ReverseTorsionID = join "-", reverse @AtomIDs;
 356 
 357   # Use lexicographically smaller string...
 358   if ($ReverseTorsionID lt $TorsionID) {
 359     $TorsionID = $ReverseTorsionID;
 360   }
 361 
 362   if (exists $This->{StructurallyUniqueAtomTorsions}{$TorsionID}) {
 363     return 0;
 364   }
 365 
 366   # Keep track...
 367   $This->{StructurallyUniqueAtomTorsions}{$TorsionID} = 1;
 368 
 369   return 1;
 370 }
 371 
 372 # Get atom torsion ID corresponding to atom types involved in torsion...
 373 #
 374 # Notes:
 375 #  . TorsionID corresponds to assigned atom types of all the four torsion atoms
 376 #    concatenated by hyphen.
 377 #  . TorsionIDs are generated for both forward and backward sequence of atoms
 378 #    in the torsion and keeping the lexicographically smaller TorsionID to keep TorsionID
 379 #    independent of atom ordering.
 380 #
 381 sub _GetAtomTorsionID {
 382   my($This, @AtomIDs) = @_;
 383   my($AtomTorsionID, $ReverseAtomTorsionID, @AtomTypes);
 384 
 385   @AtomTypes = ();
 386   @AtomTypes = map { $This->{AssignedAtomTypes}{$_} } @AtomIDs;
 387 
 388   $AtomTorsionID = join "-", @AtomTypes;
 389   $ReverseAtomTorsionID = join "-", reverse @AtomTypes;
 390 
 391   # Use lexicographically smaller string as ID...
 392   return ($ReverseAtomTorsionID lt $AtomTorsionID) ? $ReverseAtomTorsionID : $AtomTorsionID;
 393 }
 394 
 395 # Set final fingerpritns vector...
 396 #
 397 sub _SetFinalFingerprints {
 398   my($This) = @_;
 399   my($AtomTorsionID, $Value, @Values);
 400 
 401   # Mark successful generation of fingerprints...
 402   $This->{FingerprintsGenerated} = 1;
 403 
 404   @Values = ();
 405   @{$This->{AtomTorsionsIDs}} = ();
 406 
 407   for $AtomTorsionID (sort keys %{$This->{AtomTorsionsCount}}) {
 408     $Value = $This->{AtomTorsionsCount}{$AtomTorsionID};
 409     push @{$This->{AtomTorsionsIDs}}, $AtomTorsionID;
 410     push @Values, $Value;
 411   }
 412 
 413   # Add AtomPairsIDs and values to fingerprint vector...
 414   $This->{FingerprintsVector}->AddValueIDs(\@{$This->{AtomTorsionsIDs}});
 415   $This->{FingerprintsVector}->AddValues(\@Values);
 416 
 417   return $This;
 418 }
 419 
 420 # Get atom torsions IDs corresponding to atom torsions count values in fingerprint
 421 # vector as an array or reference to an array...
 422 #
 423 # AtomTorsionsIDs list differes in molecules and is generated during finalization
 424 # of fingerprints to make sure the fingerprint vector containing count values
 425 # matches the atom torsions array.
 426 #
 427 sub GetAtomTorsionsIDs {
 428   my($This) = @_;
 429 
 430   return wantarray ? @{$This->{AtomTorsionsIDs}} : \@{$This->{AtomTorsionsIDs}};
 431 }
 432 
 433 # Cache  appropriate molecule data...
 434 #
 435 sub _SetupMoleculeDataCache {
 436   my($This) = @_;
 437 
 438   # Get all atoms including hydrogens. The hydrogen atoms are ignored during processing...
 439   @{$This->{Atoms}} = $This->GetMolecule()->GetAtoms();
 440 
 441   return $This;
 442 }
 443 
 444 # Clear cached molecule data...
 445 #
 446 sub _ClearMoleculeDataCache {
 447   my($This) = @_;
 448 
 449   @{$This->{Atoms}} = ();
 450 
 451   return $This;
 452 }
 453 
 454 # Set atomic invariants to use for atom identifiers...
 455 #
 456 sub SetAtomicInvariantsToUse {
 457   my($This, @Values) = @_;
 458   my($FirstValue, $TypeOfFirstValue, $AtomicInvariant, $SpecifiedAtomicInvariant, $AtomicInvariantValue, @SpecifiedAtomicInvariants, @AtomicInvariantsToUse);
 459 
 460   if (!@Values) {
 461     carp "Warning: ${ClassName}->SetAtomicInvariantsToUse: No values specified...";
 462     return;
 463   }
 464 
 465   $FirstValue = $Values[0];
 466   $TypeOfFirstValue = ref $FirstValue;
 467 
 468   @SpecifiedAtomicInvariants = ();
 469   @AtomicInvariantsToUse = ();
 470 
 471   if ($TypeOfFirstValue =~ /^ARRAY/) {
 472     push @SpecifiedAtomicInvariants, @{$FirstValue};
 473   }
 474   else {
 475     push @SpecifiedAtomicInvariants, @Values;
 476   }
 477 
 478   # Make sure specified AtomicInvariants are valid...
 479   for $SpecifiedAtomicInvariant (@SpecifiedAtomicInvariants) {
 480     if (!AtomTypes::AtomicInvariantsAtomTypes::IsAtomicInvariantAvailable($SpecifiedAtomicInvariant)) {
 481       croak "Error: ${ClassName}->SetAtomicInvariantsToUse: Specified atomic invariant, $SpecifiedAtomicInvariant, is not supported...\n ";
 482     }
 483     $AtomicInvariant = $SpecifiedAtomicInvariant;
 484     push @AtomicInvariantsToUse, $AtomicInvariant;
 485   }
 486 
 487   # Set atomic invariants to use...
 488   @{$This->{AtomicInvariantsToUse}} = ();
 489   push @{$This->{AtomicInvariantsToUse}}, @AtomicInvariantsToUse;
 490 
 491   return $This;
 492 }
 493 
 494 # Set functional classes to use for atom identifiers...
 495 #
 496 sub SetFunctionalClassesToUse {
 497   my($This, @Values) = @_;
 498   my($FirstValue, $TypeOfFirstValue, $FunctionalClass, $SpecifiedFunctionalClass, @SpecifiedFunctionalClasses, @FunctionalClassesToUse);
 499 
 500   if (!@Values) {
 501     carp "Warning: ${ClassName}->SetFunctionalClassesToUse: No values specified...";
 502     return;
 503   }
 504 
 505   if ($This->{AtomIdentifierType} !~ /^FunctionalClassAtomTypes$/i) {
 506     carp "Warning: ${ClassName}->SetFunctionalClassesToUse: FunctionalClassesToUse can't be set for InitialAtomIdentifierType of $This->{AtomIdentifierType}...";
 507     return;
 508   }
 509 
 510   $FirstValue = $Values[0];
 511   $TypeOfFirstValue = ref $FirstValue;
 512 
 513   @SpecifiedFunctionalClasses = ();
 514   @FunctionalClassesToUse = ();
 515 
 516   if ($TypeOfFirstValue =~ /^ARRAY/) {
 517     push @SpecifiedFunctionalClasses, @{$FirstValue};
 518   }
 519   else {
 520     push @SpecifiedFunctionalClasses, @Values;
 521   }
 522 
 523   # Make sure specified FunctionalClasses are valid...
 524   for $SpecifiedFunctionalClass (@SpecifiedFunctionalClasses) {
 525     if (!AtomTypes::FunctionalClassAtomTypes::IsFunctionalClassAvailable($SpecifiedFunctionalClass)) {
 526       croak "Error: ${ClassName}->SetFunctionalClassesToUse: Specified functional class, $SpecifiedFunctionalClass, is not supported...\n ";
 527     }
 528     push @FunctionalClassesToUse, $SpecifiedFunctionalClass;
 529   }
 530 
 531   # Set functional classes to use...
 532   @{$This->{FunctionalClassesToUse}} = ();
 533   push @{$This->{FunctionalClassesToUse}}, @FunctionalClassesToUse;
 534 
 535   return $This;
 536 }
 537 
 538 # Initialize atom indentifier type information...
 539 #
 540 # Current supported values:
 541 #
 542 # AtomicInvariantsAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, FunctionalClassAtomTypes,
 543 # MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes
 544 #
 545 sub _InitializeAtomIdentifierTypeInformation {
 546   my($This) = @_;
 547 
 548   if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
 549     $This->_InitializeAtomicInvariantsAtomTypesInformation();
 550   }
 551   elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
 552     $This->_InitializeFunctionalClassAtomTypesInformation();
 553   }
 554   elsif ($This->{AtomIdentifierType} =~ /^(DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
 555     # Nothing to do for now...
 556   }
 557   else {
 558     croak "Error: ${ClassName}->_InitializeAtomIdentifierTypeInformation: Unknown atom indentifier type $This->{AtomIdentifierType}...";
 559   }
 560 
 561   return $This;
 562 }
 563 
 564 # Initialize atomic invariants to use for generating atom IDs in atom torsions...
 565 #
 566 # Let:
 567 #   AS = Atom symbol corresponding to element symbol
 568 #
 569 #   X<n>   = Number of non-hydrogen atom neighbors or heavy atoms attached to atom
 570 #   BO<n> = Sum of bond orders to non-hydrogen atom neighbors or heavy atoms attached to atom
 571 #   LBO<n> = Largest bond order of non-hydrogen atom neighbors or heavy atoms attached to atom
 572 #   SB<n> = Number of single bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 573 #   DB<n> = Number of double bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 574 #   TB<n> = Number of triple bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 575 #   H<n>   = Number of implicit and explicit hydrogens for atom
 576 #   Ar     = Aromatic annotation indicating whether atom is aromatic
 577 #   RA     = Ring atom annotation indicating whether atom is a ring
 578 #   FC<+n/-n> = Formal charge assigned to atom
 579 #   MN<n> = Mass number indicating isotope other than most abundant isotope
 580 #   SM<n> = Spin multiplicity of atom. Possible values: 1 (singlet), 2 (doublet) or 3 (triplet)
 581 #
 582 #   AtomTypeIDx = Atomic invariants atom type for atom x
 583 #   AtomTypeIDy = Atomic invariants atom type for atom y
 584 #   AtomTypeIDz = Atomic invariants atom type for atom z
 585 #   AtomTypeIDw = Atomic invariants atom type for atom w
 586 #
 587 # Then:
 588 #
 589 #   Atom torsion AtomID generated by AtomTypes::AtomicInvariantsAtomTypes class corresponds to:
 590 #
 591 #     AS.X<n>.BO<n>.LBO<n>.<SB><n>.<DB><n>.<TB><n>.H<n>.Ar.RA.FC<+n/-n>.MN<n>.SM<n>
 592 #
 593 #  AtomTorsion ID corresponds to:
 594 #
 595 #    AtomTypeIDx-AtomTypeIDy-AtomTypeIDz-AtomTypeIDw
 596 #
 597 # Except for AS which is a required atomic invariant in atom torsions AtomIDs, all other atomic invariants are
 598 # optional. Default atomic invariants used for AtomID are: AS, X<n>, BO<n>, H<n>, FC<+n/-n>.
 599 # AtomID specification doesn't include atomic invariants with zero or undefined values.
 600 #
 601 # Examples of atom torsion AtomIDs in Aspirin using default atomic invariants:
 602 #
 603 #  C.X1.BO1.H3-C.X3.BO4-O.X2.BO2-C.X3.BO4
 604 #  C.X2.BO3.H1-C.X2.BO3.H1-C.X2.BO3.H1-C.X2.BO3.H1
 605 #  C.X3.BO4-C.X3.BO4-O.X2.BO2-C.X3.BO4
 606 #  C.X3.BO4-O.X2.BO2-C.X3.BO4-O.X1.BO2
 607 #
 608 # Examples of atom torsion AtomIDs in Aspirin using AS, X and BO atomic invariants:
 609 #
 610 #  C.X1.BO1-C.X3.BO4-O.X2.BO2-C.X3.BO4
 611 #  C.X2.BO3-C.X2.BO3-C.X2.BO3-C.X2.BO3
 612 #  C.X3.BO4-C.X3.BO4-O.X2.BO2-C.X3.BO4
 613 #  C.X3.BO4-O.X2.BO2-C.X3.BO4-O.X1.BO2
 614 #
 615 sub _InitializeAtomicInvariantsAtomTypesInformation {
 616   my($This) = @_;
 617 
 618   # Default atomic invariants to use for generating atom torsions atom IDs: AS, X, BO, H, FC
 619   #
 620   @{$This->{AtomicInvariantsToUse}} = ();
 621   @{$This->{AtomicInvariantsToUse}} = ('AS', 'X', 'BO', 'H', 'FC');
 622 
 623   return $This;
 624 }
 625 
 626 # Initialize functional class atom types, generated by AtomTypes::FunctionalClassAtomTypes
 627 # class, to use for generating atom identifiers...
 628 #
 629 # Let:
 630 #   HBD: HydrogenBondDonor
 631 #   HBA: HydrogenBondAcceptor
 632 #   PI :  PositivelyIonizable
 633 #   NI : NegativelyIonizable
 634 #   Ar : Aromatic
 635 #   Hal : Halogen
 636 #   H : Hydrophobic
 637 #   RA : RingAtom
 638 #   CA : ChainAtom
 639 #
 640 # Then:
 641 #
 642 #   Functiononal class atom type specification for an atom corresponds to:
 643 #
 644 #     Ar.CA.H.HBA.HBD.Hal.NI.PI.RA
 645 #
 646 #   Default functional classes used are: HBD, HBA, PI, NI, Ar, Hal
 647 #
 648 #   FunctionalAtomTypes are assigned using the following definitions [ Ref 60-61, Ref 65-66 ]:
 649 #
 650 #     HydrogenBondDonor: NH, NH2, OH
 651 #     HydrogenBondAcceptor: N[!H], O
 652 #     PositivelyIonizable: +, NH2
 653 #     NegativelyIonizable: -, C(=O)OH, S(=O)OH, P(=O)OH
 654 #
 655 sub _InitializeFunctionalClassAtomTypesInformation {
 656   my($This) = @_;
 657 
 658   # Default functional class atom typess to use for generating atom identifiers
 659   # are: HBD, HBA, PI, NI, Ar, Hal
 660   #
 661   @{$This->{FunctionalClassesToUse}} = ();
 662   @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'Ar', 'Hal');
 663 
 664   return $This;
 665 }
 666 
 667 # Return a string containg data for TopologicalAtomTorsionsFingerprints object...
 668 #
 669 sub StringifyTopologicalAtomTorsionsFingerprints {
 670   my($This) = @_;
 671   my($FingerprintsString);
 672 
 673   # Type of fingerprint...
 674   $FingerprintsString = "Fingerprint type: $This->{Type}; AtomIdentifierType: $This->{AtomIdentifierType}";
 675 
 676   if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
 677     my($AtomicInvariant, @AtomicInvariants, @AtomicInvariantsOrder, %AvailableAtomicInvariants);
 678 
 679     @AtomicInvariantsOrder = AtomTypes::AtomicInvariantsAtomTypes::GetAtomicInvariantsOrder();
 680     %AvailableAtomicInvariants = AtomTypes::AtomicInvariantsAtomTypes::GetAvailableAtomicInvariants();
 681 
 682     for $AtomicInvariant (@AtomicInvariantsOrder) {
 683       push @AtomicInvariants, "$AtomicInvariant: $AvailableAtomicInvariants{$AtomicInvariant}";
 684     }
 685 
 686     $FingerprintsString .= "; AtomicInvariantsToUse: <" . TextUtil::JoinWords(\@{$This->{AtomicInvariantsToUse}}, ", ", 0) . ">";
 687     $FingerprintsString .= "; AtomicInvariantsOrder: <" . TextUtil::JoinWords(\@AtomicInvariantsOrder, ", ", 0) . ">";
 688     $FingerprintsString .= "; AvailableAtomicInvariants: <" . TextUtil::JoinWords(\@AtomicInvariants, ", ", 0) . ">";
 689   }
 690   elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
 691     my($FunctionalClass, @FunctionalClasses, @FunctionalClassesOrder, %AvailableFunctionalClasses);
 692 
 693     @FunctionalClassesOrder = AtomTypes::FunctionalClassAtomTypes::GetFunctionalClassesOrder();
 694     %AvailableFunctionalClasses = AtomTypes::FunctionalClassAtomTypes::GetAvailableFunctionalClasses();
 695 
 696     for $FunctionalClass (@FunctionalClassesOrder) {
 697       push @FunctionalClasses, "$FunctionalClass: $AvailableFunctionalClasses{$FunctionalClass}";
 698     }
 699 
 700     $FingerprintsString .= "; FunctionalClassesToUse: <" . TextUtil::JoinWords(\@{$This->{FunctionalClassesToUse}}, ", ", 0) . ">";
 701     $FingerprintsString .= "; FunctionalClassesOrder: <" . TextUtil::JoinWords(\@FunctionalClassesOrder, ", ", 0) . ">";
 702     $FingerprintsString .= "; AvailableFunctionalClasses: <" . TextUtil::JoinWords(\@FunctionalClasses, ", ", 0) . ">";
 703   }
 704 
 705   # Total number of atom torsions...
 706   $FingerprintsString .= "; NumOfAtomTorsions: " . $This->{FingerprintsVector}->GetNumOfValues();
 707 
 708   # FingerprintsVector...
 709   $FingerprintsString .= "; FingerprintsVector: < $This->{FingerprintsVector} >";
 710 
 711   return $FingerprintsString;
 712 }
 713