MayaChemTools

   1 package Fingerprints::AtomNeighborhoodsFingerprints;
   2 #
   3 # $RCSfile: AtomNeighborhoodsFingerprints.pm,v $
   4 # $Date: 2015/02/28 20:48:53 $
   5 # $Revision: 1.27 $
   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 '""' => 'StringifyAtomNeighborhoodsFingerprints';
  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->_InitializeAtomNeighborhoodsFingerprints();
  68 
  69   $This->_InitializeAtomNeighborhoodsFingerprintsProperties(%NamesAndValues);
  70 
  71   return $This;
  72 }
  73 
  74 # Initialize object data...
  75 #
  76 sub _InitializeAtomNeighborhoodsFingerprints {
  77   my($This) = @_;
  78 
  79   # Type of fingerprint...
  80   $This->{Type} = 'AtomNeighborhoods';
  81 
  82   # Type of vector...
  83   $This->{VectorType} = 'FingerprintsVector';
  84 
  85   # Type of FingerprintsVector...
  86   $This->{FingerprintsVectorType} = 'AlphaNumericalValues';
  87 
  88   # Minimum and maximum atomic neighborhoods radii...
  89   $This->{MinNeighborhoodRadius} = 0;
  90   $This->{MaxNeighborhoodRadius} = 2;
  91 
  92   # Atom identifier type to use for atom IDs in atom neighborhood atoms...
  93   #
  94   # Currently supported values are: AtomicInvariantsAtomTypes, DREIDINGAtomTypes,
  95   # EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes,
  96   # SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes
  97   #
  98   $This->{AtomIdentifierType} = '';
  99 
 100   # Atom types assigned to each heavy atom...
 101   %{$This->{AssignedAtomTypes}} = ();
 102 
 103   # Atom neighorhoods with in specified atom radii..
 104   %{$This->{AtomNeighborhoods}} = ();
 105 
 106   # Atom neighborhoods atom types count at different neighborhoods...
 107   %{$This->{NeighborhoodAtomTypesCount}} = ();
 108 
 109   # Atom neighborhood identifiers using specified atom identifier types methodology...
 110   @{$This->{AtomNeighborhoodsIdentifiers}} = ();
 111 }
 112 
 113 # Initialize class ...
 114 sub _InitializeClass {
 115   #Class name...
 116   $ClassName = __PACKAGE__;
 117 }
 118 
 119 # Initialize object properties....
 120 sub _InitializeAtomNeighborhoodsFingerprintsProperties {
 121   my($This, %NamesAndValues) = @_;
 122 
 123   my($Name, $Value, $MethodName);
 124   while (($Name, $Value) = each  %NamesAndValues) {
 125     $MethodName = "Set${Name}";
 126     $This->$MethodName($Value);
 127   }
 128 
 129   # Make sure molecule object was specified...
 130   if (!exists $NamesAndValues{Molecule}) {
 131     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying molecule...";
 132   }
 133   if (exists $NamesAndValues{Size}) {
 134     croak "Error: ${ClassName}->New: Object can't be instantiated with a user specified size: It's an arbitrary length vector...";
 135   }
 136   if (!exists $NamesAndValues{AtomIdentifierType}) {
 137     croak "Error: ${ClassName}->New: Object can't be instantiated without specifying AtomIdentifierType...";
 138   }
 139 
 140   $This->_InitializeFingerprintsVector();
 141 
 142   return $This;
 143 }
 144 
 145 # Set atom identifier type..
 146 #
 147 sub SetAtomIdentifierType {
 148   my($This, $IdentifierType) = @_;
 149 
 150   if ($IdentifierType !~ /^(AtomicInvariantsAtomTypes|DREIDINGAtomTypes|EStateAtomTypes|FunctionalClassAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
 151     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.";
 152   }
 153 
 154   if ($This->{AtomIdentifierType}) {
 155     croak "Error: ${ClassName}->SetAtomIdentifierType: Can't change intial atom identifier type:  It's already set...";
 156   }
 157 
 158   $This->{AtomIdentifierType} = $IdentifierType;
 159 
 160   # Initialize atom identifier type information...
 161   $This->_InitializeAtomIdentifierTypeInformation();
 162 
 163   return $This;
 164 }
 165 
 166 # Set minimum atom neighborhood radius...
 167 #
 168 sub SetMinNeighborhoodRadius {
 169   my($This, $Value) = @_;
 170 
 171   if (!TextUtil::IsInteger($Value)) {
 172     croak "Error: ${ClassName}->SetMinNeighborhoodRadius: MinNeighborhoodRadius value, $Value, is not valid:  It must be an  integer...";
 173   }
 174 
 175   if ($Value < 0 ) {
 176     croak "Error: ${ClassName}->SetMinNeighborhoodRadius: MinNeighborhoodRadius value, $Value, is not valid:  It must be >= 0...";
 177   }
 178   $This->{MinNeighborhoodRadius} = $Value;
 179 
 180   return $This;
 181 }
 182 
 183 # Set maximum atom neighborhood radius...
 184 #
 185 sub SetMaxNeighborhoodRadius {
 186   my($This, $Value) = @_;
 187 
 188   if (!TextUtil::IsInteger($Value)) {
 189     croak "Error: ${ClassName}->SetMaxNeighborhoodRadius: MaxNeighborhoodRadius value, $Value, is not valid:  It must be an  integer...";
 190   }
 191 
 192   if ($Value < 0 ) {
 193     croak "Error: ${ClassName}->SetMaxNeighborhoodRadius: MaxNeighborhoodRadius value, $Value, is not valid:  It must be >= 0...";
 194   }
 195   $This->{MaxNeighborhoodRadius} = $Value;
 196 
 197   return $This;
 198 }
 199 
 200 # Generate fingerprints description...
 201 #
 202 sub GetDescription {
 203   my($This) = @_;
 204 
 205   # Is description explicity set?
 206   if (exists $This->{Description}) {
 207     return $This->{Description};
 208   }
 209 
 210   # Generate fingerprints description...
 211 
 212   return "$This->{Type}:$This->{AtomIdentifierType}:MinRadius$This->{MinNeighborhoodRadius}:MaxRadius$This->{MaxNeighborhoodRadius}";
 213 }
 214 
 215 # Generate atom neighborhood [ Ref 53-56, Ref 73 ] fingerprints...
 216 #
 217 # Methodology:
 218 #   . Assign atom types to all non-hydrogen atoms in the molecule
 219 #   . Get atom neighborhoods up to MaxNeighborhoodRadis
 220 #   . Count unqiue atom types at each neighborhood radii for all heavy atoms
 221 #   . Generate neighborhood identifiers for all neighborhoods around central
 222 #     heavy atom
 223 #      . Atom neighborhood identifier for a specific radii is generated using neighborhood
 224 #        radius, assigned atom type and its count as follows:
 225 #
 226 #            NR<n>-<AtomType>-ATC<n>
 227 #
 228 #      . Atom neighborhood identifier for a central atom at all specified radii is generated
 229 #        by concatenating neighborhood identifiers at each radii by colon:
 230 #
 231 #            NR<n>-<AtomType>-ATC<n>:NR<n>-<AtomType>-ATC<n>:
 232 #
 233 #   . Set final fingerprints as list of neighborhood atom indentifiers
 234 #
 235 sub GenerateFingerprints {
 236   my($This) = @_;
 237 
 238   if ($This->{MinNeighborhoodRadius} > $This->{MaxNeighborhoodRadius}) {
 239     croak "Error: ${ClassName}->GenerateFingerprints: No fingerpritns generated: MinLength, $This->{MinNeighborhoodRadius}, must be less than MaxLength, $This->{MaxNeighborhoodRadius}...";
 240   }
 241 
 242   # Cache appropriate molecule data...
 243   $This->_SetupMoleculeDataCache();
 244 
 245   # Assign atom types to all heavy atoms...
 246   if (!$This->_AssignAtomTypes()) {
 247     carp "Warning: ${ClassName}->GenerateFingerprints: $This->{AtomIdentifierType} fingerprints generation didn't succeed: Couldn't assign valid $This->{AtomIdentifierType} to all atoms...";
 248     return $This;
 249   }
 250 
 251   # Intialize atom neighborhoods information...
 252   $This->_InitializeAtomNeighborhoods();
 253 
 254   # Identify atom neighborhoods with in specified radii...
 255   $This->_GetAtomNeighborhoods();
 256 
 257   # Count atom neighborhoods atom types...
 258   $This->_CountAtomNeighborhoodsAtomTypes();
 259 
 260   # Genenerate atom neighborhood identifiers...
 261   $This->_GenerateAtomNeighborhoodIdentifiers();
 262 
 263   # Set final fingerprints...
 264   $This->_SetFinalFingerprints();
 265 
 266   # Clear cached molecule data...
 267   $This->_ClearMoleculeDataCache();
 268 
 269   return $This;
 270 }
 271 
 272 # Assign appropriate atom types to all heavy atoms...
 273 #
 274 sub _AssignAtomTypes {
 275   my($This) = @_;
 276   my($SpecifiedAtomTypes, $Atom, $AtomID, $IgnoreHydrogens);
 277 
 278   %{$This->{AssignedAtomTypes}} = ();
 279   $IgnoreHydrogens = 1;
 280 
 281   $SpecifiedAtomTypes = undef;
 282 
 283   IDENTIFIERTYPE: {
 284     if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
 285       $SpecifiedAtomTypes = new AtomTypes::AtomicInvariantsAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens, 'AtomicInvariantsToUse' => $This->{AtomicInvariantsToUse});
 286       last IDENTIFIERTYPE;
 287     }
 288 
 289     if ($This->{AtomIdentifierType} =~ /^DREIDINGAtomTypes$/i) {
 290       $SpecifiedAtomTypes = new AtomTypes::DREIDINGAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 291       last IDENTIFIERTYPE;
 292     }
 293 
 294     if ($This->{AtomIdentifierType} =~ /^EStateAtomTypes$/i) {
 295       $SpecifiedAtomTypes = new AtomTypes::EStateAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 296       last IDENTIFIERTYPE;
 297     }
 298 
 299     if ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
 300       $SpecifiedAtomTypes = new AtomTypes::FunctionalClassAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens, 'FunctionalClassesToUse' => $This->{FunctionalClassesToUse});
 301       last IDENTIFIERTYPE;
 302     }
 303 
 304     if ($This->{AtomIdentifierType} =~ /^MMFF94AtomTypes$/i) {
 305       $SpecifiedAtomTypes = new AtomTypes::MMFF94AtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 306       last IDENTIFIERTYPE;
 307     }
 308 
 309     if ($This->{AtomIdentifierType} =~ /^SLogPAtomTypes$/i) {
 310       $SpecifiedAtomTypes = new AtomTypes::SLogPAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 311       last IDENTIFIERTYPE;
 312     }
 313     if ($This->{AtomIdentifierType} =~ /^SYBYLAtomTypes$/i) {
 314       $SpecifiedAtomTypes = new AtomTypes::SYBYLAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 315       last IDENTIFIERTYPE;
 316     }
 317 
 318     if ($This->{AtomIdentifierType} =~ /^TPSAAtomTypes$/i) {
 319       $SpecifiedAtomTypes = new AtomTypes::TPSAAtomTypes('Molecule' => $This->{Molecule}, 'IgnorePhosphorus' => 0, 'IgnoreSulfur' => 0);
 320       last IDENTIFIERTYPE;
 321     }
 322 
 323     if ($This->{AtomIdentifierType} =~ /^UFFAtomTypes$/i) {
 324       $SpecifiedAtomTypes = new AtomTypes::UFFAtomTypes('Molecule' => $This->{Molecule}, 'IgnoreHydrogens' => $IgnoreHydrogens);
 325       last IDENTIFIERTYPE;
 326     }
 327 
 328     croak "Error: ${ClassName}->_AssignAtomTypes: Unknown atom indentifier type $This->{AtomIdentifierType}...";
 329   }
 330 
 331   # Assign atom types...
 332   $SpecifiedAtomTypes->AssignAtomTypes();
 333 
 334   # Make sure atom types assignment is successful...
 335   if (!$SpecifiedAtomTypes->IsAtomTypesAssignmentSuccessful()) {
 336     return undef;
 337   }
 338 
 339   # Collect assigned atom types...
 340   ATOM: for $Atom (@{$This->{Atoms}}) {
 341     if ($Atom->IsHydrogen()) {
 342       next ATOM;
 343     }
 344     $AtomID = $Atom->GetID();
 345     $This->{AssignedAtomTypes}{$AtomID} = $SpecifiedAtomTypes->GetAtomType($Atom);
 346   }
 347 
 348   return $This;
 349 }
 350 
 351 # Initialize topological atom pairs between specified distance range...
 352 #
 353 sub _InitializeAtomNeighborhoods {
 354   my($This) = @_;
 355   my($Radius);
 356 
 357   # Initialize atom neighborhood count information between specified radii...
 358   %{$This->{NeighborhoodAtomTypesCount}} = ();
 359 
 360   for $Radius ($This->{MinNeighborhoodRadius} .. $This->{MaxNeighborhoodRadius}) {
 361     %{$This->{NeighborhoodAtomTypesCount}{$Radius}} = ();
 362   }
 363 
 364   # Initialize atom neighborhoods atoms information at all specified radii...
 365   #
 366   %{$This->{AtomNeighborhoods}} = ();
 367 
 368   for $Radius (0 .. $This->{MaxNeighborhoodRadius}) {
 369     %{$This->{AtomNeighborhoods}{$Radius}} = ();
 370   }
 371 
 372   return $This;
 373 }
 374 
 375 # Collect atom neighborhoods upto maximum neighborhood radius...
 376 #
 377 # Notes:
 378 #  . Fingerprints are only generated for neighborhoods between specified minimum
 379 #    and maximum neighborhood radii.
 380 #
 381 sub _GetAtomNeighborhoods {
 382   my($This) = @_;
 383   my($Atom, $AtomID, $MaxRadius, $Radius, $Molecule);
 384 
 385   $MaxRadius = $This->{MaxNeighborhoodRadius};
 386   $Molecule = $This->GetMolecule();
 387 
 388   # Collect atom neighborhoods...
 389 
 390   ATOM: for $Atom (@{$This->{Atoms}}) {
 391     $AtomID = $Atom->GetID();
 392     $Radius = 0;
 393 
 394     if ($MaxRadius == 0) {
 395       # Atom is its own neighborhood at 0 radius...
 396       my(@AtomNeighborhoodsAtoms);
 397 
 398       @AtomNeighborhoodsAtoms = ($Atom);
 399       $This->{AtomNeighborhoods}{$Radius}{$AtomID} = \@AtomNeighborhoodsAtoms;
 400 
 401       next ATOM;
 402     }
 403 
 404     # Collect available atom neighborhoods at different neighborhood radii levels...
 405     my($AtomNeighborhoodAtomsRef);
 406 
 407     for $AtomNeighborhoodAtomsRef ($Molecule->GetAtomNeighborhoodsWithRadiusUpto($Atom, $MaxRadius)) {
 408       $This->{AtomNeighborhoods}{$Radius}{$AtomID} = $AtomNeighborhoodAtomsRef;
 409       $Radius++;
 410     }
 411   }
 412   return $This;
 413 }
 414 
 415 # Count atom neighborhoods atom types for each non-hydrogen central atoms with
 416 # neighborhoods in specified radii range...
 417 #
 418 sub _CountAtomNeighborhoodsAtomTypes {
 419   my($This) = @_;
 420   my($AtomID, $NeighborhoodAtomID, $Radius, $NeighborhoodAtom, $NeighborhoodAtomType, $AtomNeighborhoodAtomsRef);
 421 
 422   RADIUS: for $Radius (sort { $a <=> $b } keys %{$This->{AtomNeighborhoods}} ) {
 423     if ($Radius < $This->{MinNeighborhoodRadius} || $Radius > $This->{MaxNeighborhoodRadius}) {
 424       next RADIUS;
 425     }
 426     # Go over the neighborhoods of each atom at the current radius...
 427     for $AtomID (keys %{$This->{AtomNeighborhoods}{$Radius}}) {
 428       $AtomNeighborhoodAtomsRef = $This->{AtomNeighborhoods}{$Radius}{$AtomID};
 429       NEIGHBORHOODATOM: for $NeighborhoodAtom (@{$AtomNeighborhoodAtomsRef}) {
 430         if ($NeighborhoodAtom->IsHydrogen()) {
 431           next NEIGHBORHOODATOM;
 432         }
 433         $NeighborhoodAtomID = $NeighborhoodAtom->GetID();
 434         $NeighborhoodAtomType = $This->{AssignedAtomTypes}{$NeighborhoodAtomID};
 435 
 436         # Count neighbothood atom types for each atom at different radii...
 437         if (!exists $This->{NeighborhoodAtomTypesCount}{$Radius}{$AtomID}) {
 438           %{$This->{NeighborhoodAtomTypesCount}{$Radius}{$AtomID}} = ();
 439         }
 440         if (exists $This->{NeighborhoodAtomTypesCount}{$Radius}{$AtomID}{$NeighborhoodAtomType}) {
 441           $This->{NeighborhoodAtomTypesCount}{$Radius}{$AtomID}{$NeighborhoodAtomType} += 1;
 442         }
 443         else {
 444           $This->{NeighborhoodAtomTypesCount}{$Radius}{$AtomID}{$NeighborhoodAtomType} = 1;
 445         }
 446       }
 447     }
 448   }
 449   return $This;
 450 }
 451 
 452 # Generate atom neighborhood identifiers for each non-hydrogen atom using atom
 453 # neighborhood atom types and their count information...
 454 #
 455 # Let:
 456 #   NR<n> = Neighborhood radius
 457 #   AtomType = Assigned atom type
 458 #   ATC<n> = AtomType count
 459 #
 460 # Then:
 461 #
 462 #   AtomNeighborhoodAtomIdentifier for a neighborhood atom generated for
 463 #   AtomTypes::AtomicInvariantsAtomTypes class corresponds to:
 464 #
 465 #     NR<n>-<AtomType>-ATC<n>
 466 #
 467 #   AtomNeighborhoodsIdentifier for all specified atom neighbothoods of an atom generated for
 468 #   AtomTypes::AtomicInvariantsAtomTypes class corresponds to:
 469 #
 470 #     NR<n>-<AtomType>-ATC<n>;NR<n>-<AtomType>-ATC<n>;...
 471 #
 472 sub _GenerateAtomNeighborhoodIdentifiers {
 473   my($This) = @_;
 474   my($Atom, $AtomID, $Radius, $AtomType, $AtomTypeCount, $AtomNeighborhoodIdentifier, @AtomNeighborhoodIdentifiers);
 475 
 476   @{$This->{AtomNeighborhoodsIdentifiers}} = ();
 477 
 478   for $Atom (@{$This->{Atoms}}) {
 479     $AtomID = $Atom->GetID();
 480     @AtomNeighborhoodIdentifiers = ();
 481     RADIUS: for $Radius ($This->{MinNeighborhoodRadius} .. $This->{MaxNeighborhoodRadius}) {
 482       if (!exists $This->{NeighborhoodAtomTypesCount}{$Radius}{$AtomID}) {
 483         next RADIUS;
 484       }
 485       for $AtomType (sort keys %{$This->{NeighborhoodAtomTypesCount}{$Radius}{$AtomID}}) {
 486         $AtomTypeCount = $This->{NeighborhoodAtomTypesCount}{$Radius}{$AtomID}{$AtomType};
 487         push @AtomNeighborhoodIdentifiers, "NR${Radius}-${AtomType}-ATC${AtomTypeCount}";
 488       }
 489     }
 490     $AtomNeighborhoodIdentifier = join(":", @AtomNeighborhoodIdentifiers);
 491     push @{$This->{AtomNeighborhoodsIdentifiers}}, $AtomNeighborhoodIdentifier;
 492   }
 493 
 494   return $This;
 495 }
 496 
 497 # Set final fingerprits vector...
 498 #
 499 sub _SetFinalFingerprints {
 500   my($This) = @_;
 501 
 502   # Mark successful generation of fingerprints...
 503   $This->{FingerprintsGenerated} = 1;
 504 
 505   # Sort AtomNeighborhoodsIdentifiers..
 506   #
 507   @{$This->{AtomNeighborhoodsIdentifiers}} = sort @{$This->{AtomNeighborhoodsIdentifiers}};
 508 
 509   # Add sorted atom neighborhood identifiers to FingerprintsVector which is already defined
 510   # during initialization containing AlphaNumericalValues...
 511   #
 512   $This->{FingerprintsVector}->AddValues(\@{$This->{AtomNeighborhoodsIdentifiers}});
 513 
 514   return $This;
 515 }
 516 
 517 # Cache  appropriate molecule data...
 518 #
 519 sub _SetupMoleculeDataCache {
 520   my($This) = @_;
 521 
 522   # Get all non-hydrogen atoms...
 523   my($NegateAtomCheckMethod);
 524   $NegateAtomCheckMethod = 1;
 525   @{$This->{Atoms}} = $This->GetMolecule()->GetAtoms("IsHydrogen", $NegateAtomCheckMethod);
 526 
 527   return $This;
 528 }
 529 
 530 # Clear cached molecule data...
 531 #
 532 sub _ClearMoleculeDataCache {
 533   my($This) = @_;
 534 
 535   @{$This->{Atoms}} = ();
 536 
 537   return $This;
 538 }
 539 
 540 # Set atomic invariants to use for atom identifiers...
 541 #
 542 sub SetAtomicInvariantsToUse {
 543   my($This, @Values) = @_;
 544   my($FirstValue, $TypeOfFirstValue, $AtomicInvariant, $SpecifiedAtomicInvariant, $AtomicInvariantValue, @SpecifiedAtomicInvariants, @AtomicInvariantsToUse);
 545 
 546   if (!@Values) {
 547     carp "Warning: ${ClassName}->SetAtomicInvariantsToUse: No values specified...";
 548     return;
 549   }
 550 
 551   $FirstValue = $Values[0];
 552   $TypeOfFirstValue = ref $FirstValue;
 553 
 554   @SpecifiedAtomicInvariants = ();
 555   @AtomicInvariantsToUse = ();
 556 
 557   if ($TypeOfFirstValue =~ /^ARRAY/) {
 558     push @SpecifiedAtomicInvariants, @{$FirstValue};
 559   }
 560   else {
 561     push @SpecifiedAtomicInvariants, @Values;
 562   }
 563 
 564   # Make sure specified AtomicInvariants are valid...
 565   for $SpecifiedAtomicInvariant (@SpecifiedAtomicInvariants) {
 566     if (!AtomTypes::AtomicInvariantsAtomTypes::IsAtomicInvariantAvailable($SpecifiedAtomicInvariant)) {
 567       croak "Error: ${ClassName}->SetAtomicInvariantsToUse: Specified atomic invariant, $SpecifiedAtomicInvariant, is not supported...\n ";
 568     }
 569     $AtomicInvariant = $SpecifiedAtomicInvariant;
 570     push @AtomicInvariantsToUse, $AtomicInvariant;
 571   }
 572 
 573   # Set atomic invariants to use...
 574   @{$This->{AtomicInvariantsToUse}} = ();
 575   push @{$This->{AtomicInvariantsToUse}}, @AtomicInvariantsToUse;
 576 
 577   return $This;
 578 }
 579 
 580 # Set functional classes to use for atom identifiers...
 581 #
 582 sub SetFunctionalClassesToUse {
 583   my($This, @Values) = @_;
 584   my($FirstValue, $TypeOfFirstValue, $FunctionalClass, $SpecifiedFunctionalClass, @SpecifiedFunctionalClasses, @FunctionalClassesToUse);
 585 
 586   if (!@Values) {
 587     carp "Warning: ${ClassName}->SetFunctionalClassesToUse: No values specified...";
 588     return;
 589   }
 590 
 591   if ($This->{AtomIdentifierType} !~ /^FunctionalClassAtomTypes$/i) {
 592     carp "Warning: ${ClassName}->SetFunctionalClassesToUse: FunctionalClassesToUse can't be set for InitialAtomIdentifierType of $This->{AtomIdentifierType}...";
 593     return;
 594   }
 595 
 596   $FirstValue = $Values[0];
 597   $TypeOfFirstValue = ref $FirstValue;
 598 
 599   @SpecifiedFunctionalClasses = ();
 600   @FunctionalClassesToUse = ();
 601 
 602   if ($TypeOfFirstValue =~ /^ARRAY/) {
 603     push @SpecifiedFunctionalClasses, @{$FirstValue};
 604   }
 605   else {
 606     push @SpecifiedFunctionalClasses, @Values;
 607   }
 608 
 609   # Make sure specified FunctionalClasses are valid...
 610   for $SpecifiedFunctionalClass (@SpecifiedFunctionalClasses) {
 611     if (!AtomTypes::FunctionalClassAtomTypes::IsFunctionalClassAvailable($SpecifiedFunctionalClass)) {
 612       croak "Error: ${ClassName}->SetFunctionalClassesToUse: Specified functional class, $SpecifiedFunctionalClass, is not supported...\n ";
 613     }
 614     push @FunctionalClassesToUse, $SpecifiedFunctionalClass;
 615   }
 616 
 617   # Set functional classes to use...
 618   @{$This->{FunctionalClassesToUse}} = ();
 619   push @{$This->{FunctionalClassesToUse}}, @FunctionalClassesToUse;
 620 
 621   return $This;
 622 }
 623 
 624 # Initialize atom indentifier type information...
 625 #
 626 # Current supported values:
 627 #
 628 # AtomicInvariantsAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, FunctionalClassAtomTypes,
 629 # MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes
 630 #
 631 sub _InitializeAtomIdentifierTypeInformation {
 632   my($This) = @_;
 633 
 634   if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
 635     $This->_InitializeAtomicInvariantsAtomTypesInformation();
 636   }
 637   elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
 638     $This->_InitializeFunctionalClassAtomTypesInformation();
 639   }
 640   elsif ($This->{AtomIdentifierType} =~ /^(DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
 641     # Nothing to do for now...
 642   }
 643   else {
 644     croak "Error: ${ClassName}->_InitializeAtomIdentifierTypeInformation: Unknown atom indentifier type $This->{AtomIdentifierType}...";
 645   }
 646 
 647   return $This;
 648 }
 649 
 650 # Initialize atomic invariants atom types to use for generating atom identifiers...
 651 #
 652 # Let:
 653 #   AS = Atom symbol corresponding to element symbol
 654 #
 655 #   X<n>   = Number of non-hydrogen atom neighbors or heavy atoms attached to atom
 656 #   BO<n> = Sum of bond orders to non-hydrogen atom neighbors or heavy atoms attached to atom
 657 #   LBO<n> = Largest bond order of non-hydrogen atom neighbors or heavy atoms attached to atom
 658 #   SB<n> = Number of single bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 659 #   DB<n> = Number of double bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 660 #   TB<n> = Number of triple bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
 661 #   H<n>   = Number of implicit and explicit hydrogens for atom
 662 #   Ar     = Aromatic annotation indicating whether atom is aromatic
 663 #   RA     = Ring atom annotation indicating whether atom is a ring
 664 #   FC<+n/-n> = Formal charge assigned to atom
 665 #   MN<n> = Mass number indicating isotope other than most abundant isotope
 666 #   SM<n> = Spin multiplicity of atom. Possible values: 1 (singlet), 2 (doublet) or 3 (triplet)
 667 #
 668 # Then:
 669 #
 670 #   Atom type generated by AtomTypes::AtomicInvariantsAtomTypes class corresponds to:
 671 #
 672 #     AS.X<n>.BO<n>.LBO<n>.<SB><n>.<DB><n>.<TB><n>.H<n>.Ar.RA.FC<+n/-n>.MN<n>.SM<n>
 673 #
 674 # Except for AS which is a required atomic invariant in atom types, all other atomic invariants are
 675 # optional. Default atomic invariants used for AtomID are: AS, X<n>, BO<n>, H<n>, FC<+n/-n>.
 676 # AtomID specification doesn't include atomic invariants with zero or undefined values.
 677 #
 678 sub _InitializeAtomicInvariantsAtomTypesInformation {
 679   my($This) = @_;
 680 
 681   # Default atomic invariants to use for generating atom neighborhood atom IDs: AS, X, BO, H, FC
 682   #
 683   @{$This->{AtomicInvariantsToUse}} = ();
 684   @{$This->{AtomicInvariantsToUse}} = ('AS', 'X', 'BO', 'H', 'FC');
 685 
 686   return $This;
 687 }
 688 
 689 # Initialize functional class atom types, generated by AtomTypes::FunctionalClassAtomTypes
 690 # class, to use for generating atom identifiers...
 691 #
 692 # Let:
 693 #   HBD: HydrogenBondDonor
 694 #   HBA: HydrogenBondAcceptor
 695 #   PI :  PositivelyIonizable
 696 #   NI : NegativelyIonizable
 697 #   Ar : Aromatic
 698 #   Hal : Halogen
 699 #   H : Hydrophobic
 700 #   RA : RingAtom
 701 #   CA : ChainAtom
 702 #
 703 # Then:
 704 #
 705 #   Functiononal class atom type specification for an atom corresponds to:
 706 #
 707 #     Ar.CA.H.HBA.HBD.Hal.NI.PI.RA
 708 #
 709 #   Default functional classes used are: HBD, HBA, PI, NI, Ar, Hal
 710 #
 711 #   FunctionalAtomTypes are assigned using the following definitions [ Ref 60-61, Ref 65-66 ]:
 712 #
 713 #     HydrogenBondDonor: NH, NH2, OH
 714 #     HydrogenBondAcceptor: N[!H], O
 715 #     PositivelyIonizable: +, NH2
 716 #     NegativelyIonizable: -, C(=O)OH, S(=O)OH, P(=O)OH
 717 #
 718 sub _InitializeFunctionalClassAtomTypesInformation {
 719   my($This) = @_;
 720 
 721   # Default functional class atom typess to use for generating atom identifiers
 722   # are: HBD, HBA, PI, NI, Ar, Hal
 723   #
 724   @{$This->{FunctionalClassesToUse}} = ();
 725   @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'Ar', 'Hal');
 726 
 727   return $This;
 728 }
 729 
 730 # Return a string containg data for AtomNeighborhoodsFingerprints object...
 731 #
 732 sub StringifyAtomNeighborhoodsFingerprints {
 733   my($This) = @_;
 734   my($FingerprintsString);
 735 
 736   # Type of fingerprint...
 737   $FingerprintsString = "Fingerprint type: $This->{Type}; AtomIdentifierType: $This->{AtomIdentifierType}; MinNeighborhoodRadius: $This->{MinNeighborhoodRadius}; MaxNeighborhoodRadius: $This->{MaxNeighborhoodRadius}";
 738 
 739   if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
 740     my($AtomicInvariant, @AtomicInvariants, @AtomicInvariantsOrder, %AvailableAtomicInvariants);
 741 
 742     @AtomicInvariantsOrder = AtomTypes::AtomicInvariantsAtomTypes::GetAtomicInvariantsOrder();
 743     %AvailableAtomicInvariants = AtomTypes::AtomicInvariantsAtomTypes::GetAvailableAtomicInvariants();
 744 
 745     for $AtomicInvariant (@AtomicInvariantsOrder) {
 746       push @AtomicInvariants, "$AtomicInvariant: $AvailableAtomicInvariants{$AtomicInvariant}";
 747     }
 748 
 749     $FingerprintsString .= "; AtomicInvariantsToUse: <" . TextUtil::JoinWords(\@{$This->{AtomicInvariantsToUse}}, ", ", 0) . ">";
 750     $FingerprintsString .= "; AtomicInvariantsOrder: <" . TextUtil::JoinWords(\@AtomicInvariantsOrder, ", ", 0) . ">";
 751     $FingerprintsString .= "; AvailableAtomicInvariants: <" . TextUtil::JoinWords(\@AtomicInvariants, ", ", 0) . ">";
 752   }
 753   elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
 754     my($FunctionalClass, @FunctionalClasses, @FunctionalClassesOrder, %AvailableFunctionalClasses);
 755 
 756     @FunctionalClassesOrder = AtomTypes::FunctionalClassAtomTypes::GetFunctionalClassesOrder();
 757     %AvailableFunctionalClasses = AtomTypes::FunctionalClassAtomTypes::GetAvailableFunctionalClasses();
 758 
 759     for $FunctionalClass (@FunctionalClassesOrder) {
 760       push @FunctionalClasses, "$FunctionalClass: $AvailableFunctionalClasses{$FunctionalClass}";
 761     }
 762 
 763     $FingerprintsString .= "; FunctionalClassesToUse: <" . TextUtil::JoinWords(\@{$This->{FunctionalClassesToUse}}, ", ", 0) . ">";
 764     $FingerprintsString .= "; FunctionalClassesOrder: <" . TextUtil::JoinWords(\@FunctionalClassesOrder, ", ", 0) . ">";
 765     $FingerprintsString .= "; AvailableFunctionalClasses: <" . TextUtil::JoinWords(\@FunctionalClasses, ", ", 0) . ">";
 766   }
 767 
 768   # Total number of atom neighborhood atom IDs...
 769   $FingerprintsString .= "; NumOfAtomNeighborhoodAtomIdentifiers: " . $This->{FingerprintsVector}->GetNumOfValues();
 770 
 771   # FingerprintsVector...
 772   $FingerprintsString .= "; FingerprintsVector: < $This->{FingerprintsVector} >";
 773 
 774   return $FingerprintsString;
 775 }
 776