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