       1 package MolecularDescriptors::MolecularComplexityDescriptors;
       2 #
       3 # File: MolecularComplexityDescriptors.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 Scalar::Util ();
      30 use TextUtil ();
      31 use MathUtil ();
      32 use Atom;
      33 use Molecule;
      34 use MolecularDescriptors::MolecularDescriptors;
      35 use AtomTypes::AtomicInvariantsAtomTypes;
      36 use AtomTypes::FunctionalClassAtomTypes;
      37 use Fingerprints::AtomTypesFingerprints;
      38 use Fingerprints::ExtendedConnectivityFingerprints;
      39 use Fingerprints::MACCSKeys;
      40 use Fingerprints::PathLengthFingerprints;
      41 use Fingerprints::TopologicalAtomPairsFingerprints;
      42 use Fingerprints::TopologicalAtomTripletsFingerprints;
      43 use Fingerprints::TopologicalAtomTorsionsFingerprints;
      44 use Fingerprints::TopologicalPharmacophoreAtomPairsFingerprints;
      45 use Fingerprints::TopologicalPharmacophoreAtomTripletsFingerprints;
      46
      47 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
      48
      49 @ISA = qw(MolecularDescriptors::MolecularDescriptors Exporter);
      50 @EXPORT = qw();
      51 @EXPORT_OK = qw(GetDescriptorNames GetMolecularComplexityTypeAbbreviation);
      52
      53 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
      54
      55 # Setup class variables...
      56 my($ClassName, @DescriptorNames);
      57 _InitializeClass();
      58
      59 # Overload Perl functions...
      60 use overload '""' => 'StringifyMolecularComplexityDescriptors';
      61
      62 # Class constructor...
      63 sub new {
      64   my($Class, %NamesAndValues) = @_;
      65
      66   # Initialize object...
      67   my $This = $Class->SUPER::new();
      68   bless $This, ref($Class) || $Class;
      69   $This->_InitializeMolecularComplexityDescriptors();
      70
      71   $This->_InitializeMolecularComplexityDescriptorsProperties(%NamesAndValues);
      72
      73   return $This;
      74 }
      75
      76 # Initialize class ...
      77 sub _InitializeClass {
      78   #Class name...
      79   $ClassName = __PACKAGE__;
      80
      81   # Descriptor names...
      82   @DescriptorNames = ('MolecularComplexity');
      83
      84 }
      85
      86 # Get descriptor names as an array.
      87 #
      88 # This functionality can be either invoked as a class function or an
      89 # object method.
      90 #
      91 sub GetDescriptorNames {
      92   return @DescriptorNames;
      93 }
      94
      95 # Initialize object data...
      96 #
      97 sub _InitializeMolecularComplexityDescriptors {
      98   my($This) = @_;
      99
     100   # Type of MolecularDescriptor...
     101   $This->{Type} = 'MolecularComplexity';
     102
     103   #
     104   # The current release of MayaChemTools supports calculation of molecular complexity
     105   # corresponding to number of bits-set or unique keys [ Ref 117-119 ] in molecular
     106   # fingerprints. The following types of fingerprints based molecular complexity measures
     107   # are supported:
     108   #
     109   # AtomTypesFingerprints
     110   # ExtendedConnectivityFingerprints
     111   # MACCSKeys
     112   # PathLengthFingerprints
     113   # TopologicalAtomPairsFingerprints
     114   # TopologicalAtomTripletsFingerprints
     115   # TopologicalAtomTorsionsFingerprints
     116   # TopologicalPharmacophoreAtomPairsFingerprints
     117   # TopologicalPharmacophoreAtomTripletsFingerprints
     118   #
     119   # Default: MACCSKeys
     120   #
     121   $This->{MolecularComplexityType} = '';
     122
     123   # Atom types to use for generating fingerprints...
     124   #
     125   # Currently supported values are: AtomicInvariantsAtomTypes, DREIDINGAtomTypes,
     126   # EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes,
     127   # SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes
     128   #
     129   # Notes:
     130   #   . AtomicInvariantsAtomTypes for all supported MolecularComplexityType except for
     131   #     TopologicalPharmacophoreAtomPairsFingerprints and TopologicalPharmacophoreAtomTripletsFingerprints
     132   #   . This value is not used for MACCSKeys
     133   #   . FunctionalClassAtomTypes is the only valid value during topological pharmacophore fingerprints.
     134   #
     135   #   . Default values for AtomicInvariantsToUse and FunctionalClassesToUse are set appropriately
     136   #     for different types of fingerprints as shown below.
     137   #
     138   #     MolecularComplexityType              AtomicInvariantsToUse
     139   #
     140   #     AtomTypesFingerprints                AS, X, BO, H, FC
     141   #     TopologicalAtomPairsFingerprints     AS, X, BO, H, FC
     142   #     TopologicalAtomTripletsFingerprints  AS, X, BO, H, FC
     143   #     TopologicalAtomTorsionsFingerprints  AS, X, BO, H, FC
     144   #
     145   #     ExtendedConnectivityFingerprints     AS, X, BO, H, FC, MN
     146   #     PathLengthFingerprints               AS
     147   #
     148   #     Default for FunctionalClassesToUse for all fingerprints is set to:
     149   #
     150   #     HBD, HBA, PI, NI, Ar, Hal
     151   #
     152   #     except for the following two MolecularComplexityType fingerprints:
     153   #
     154   #     TopologicalPharmacophoreAtomPairsFingerprints     HBD, HBA, PI, NI, H
     155   #     TopologicalPharmacophoreAtomTripletsFingerprints  HBD, HBA, PI, NI, H, Ar
     156   #
     157   $This->{AtomIdentifierType} = '';
     158
     159   # Size of MACCS key set: 166 or 322...
     160   #
     161   $This->{MACCSKeysSize} = 166;
     162
     163   # Atomic neighborhoods radius for extended connectivity fingerprints...
     164   $This->{NeighborhoodRadius} = 2;
     165
     166   # Minimum and maximum path lengths to use for path length fingerprints...
     167   $This->{MinPathLength} = 1;
     168   $This->{MaxPathLength} = 8;
     169
     170   # By default bond symbols are included in atom path strings used to generate path length
     171   # fingerprints... ...
     172   $This->{UseBondSymbols} = 1;
     173
     174   # Minimum and maximum bond distance between atom pairs during topological
     175   # atom pairs/triplets fingerprints...
     176   $This->{MinDistance} = 1;
     177   $This->{MaxDistance} = 10;
     178
     179   # Determines whether to apply triangle inequality to distance triplets...
     180   #
     181   # Default for TopologicalAtomTripletsFingerprints: 0
     182   # Default for TopologicalPharmacophoreAtomTripletsFingerprints: 1
     183   #
     184   $This->{UseTriangleInequality} = '';
     185
     186   # Distance bin size used for binning distances during generation of
     187   # topological pharmacophore atom triplets fingerprints...
     188   #
     189   $This->{DistanceBinSize} = 2;
     190
     191   # Normalization methodology to use for scaling the number of bits-set or unique keys
     192   # for:
     193   #
     194   # ExtendedConnectivityFingerprints
     195   # TopologicalPharmacophoreAtomPairsFingerprints
     196   # TopologicalPharmacophoreAtomTripletsFingerprints
     197   #
     198   # This option is gnored for all other types of fingerprints.
     199   #
     200   # Possible values during extended connectivity fingerprints: None or ByHeavyAtomsCount. Default:
     201   # None.
     202   #
     203   # Possible values during topological pharmacophore atom pairs and tripletes fingerprints: None,
     204   # or ByPossibleKeysCount. Default: None. ByPossibleKeysCount corresponds to total number of
     205   # possible topological pharmacophore atom pairs or triplets in a molecule.
     206   #
     207   #
     208   $This->{NormalizationMethodology} = 'None';
     209
     210   # Intialize descriptor names and values...
     211   $This->_InitializeDescriptorNamesAndValues(@DescriptorNames);
     212
     213   return $This;
     214 }
     215
     216 # Initialize object properties...
     217 #
     218 sub _InitializeMolecularComplexityDescriptorsProperties {
     219   my($This, %NamesAndValues) = @_;
     220
     221   my($Name, $Value, $MethodName);
     222   while (($Name, $Value) = each  %NamesAndValues) {
     223     $MethodName = "Set${Name}";
     224     $This->$MethodName($Value);
     225   }
     226
     227   # Make sure MolecularComplexityType is set...
     228   if (!exists $NamesAndValues{MolecularComplexityType}) {
     229     $This->{MolecularComplexityType} = 'MACCSKeys';
     230   }
     231
     232   # Make sure AtomIdentifierType is set...
     233   if ($This->{MolecularComplexityType} !~ /^MACCSKeys$/i) {
     234     if (!exists $NamesAndValues{AtomIdentifierType}) {
     235       $This->_InitializeAtomIdentifierType();
     236     }
     237   }
     238
     239   # Make sure UseTriangleInequality is set...
     240   if ($This->{MolecularComplexityType} =~ /^(TopologicalAtomTripletsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
     241     if (!exists $NamesAndValues{UseTriangleInequality}) {
     242       $This->{UseTriangleInequality} =  ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomTripletsFingerprints$/i) ? 1 : 0;
     243     }
     244   }
     245
     246   return $This;
     247 }
     248
     249 # Initialize atom identifer type...
     250 #
     251 sub _InitializeAtomIdentifierType {
     252   my($This) = @_;
     253   my($AtomIdentifierType);
     254
     255   if ($This->{MolecularComplexityType} =~ /^MACCSKeys$/i) {
     256     return $This;
     257   }
     258
     259   $AtomIdentifierType = ($This->{MolecularComplexityType} =~ /^(TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) ? 'FunctionalClassAtomTypes' : 'AtomicInvariantsAtomTypes';
     260
     261   $This->SetAtomIdentifierType($AtomIdentifierType);
     262
     263   return $This;
     264 }
     265
     266 # Get abbreviation for specified molecular complexity type or using descriptors object...
     267 #
     268 # This functionality can be either invoked as a class function or an
     269 # object method.
     270 #
     271 sub GetMolecularComplexityTypeAbbreviation {
     272   my($FirstParameter) = @_;
     273   my($This, $ComplexityType, %ComplexityTypeToAbbrev);
     274
     275   if (_IsMolecularComplexityDescriptors($FirstParameter)) {
     276     $This = $FirstParameter;
     277     $ComplexityType = $This->{MolecularComplexityType};
     278   }
     279   else {
     280     $ComplexityType = $FirstParameter;
     281   }
     282
     283   %ComplexityTypeToAbbrev = (lc 'AtomTypesFingerprints' => 'ATFP', lc 'ExtendedConnectivityFingerprints' => 'ECFP',
     284                              lc 'MACCSKeys' => 'MACCSKeys', lc 'PathLengthFingerprints' => 'PLFP',
     285                              lc 'TopologicalAtomPairsFingerprints' => 'TAPFP', lc 'TopologicalAtomTripletsFingerprints' => 'TATFP',
     286                              lc 'TopologicalAtomTorsionsFingerprints' => 'TATFP',
     287                              lc 'TopologicalPharmacophoreAtomPairsFingerprints' => 'TPAPFP',
     288                              lc 'TopologicalPharmacophoreAtomTripletsFingerprints' => 'TPATFP');
     289
     290   return exists $ComplexityTypeToAbbrev{lc $ComplexityType} ? $ComplexityTypeToAbbrev{lc $ComplexityType} : '';
     291 }
     292
     293 # Set MACCS key set size...
     294 #
     295 sub SetMACCSKeysSize {
     296   my($This, $Value) = @_;
     297
     298   if (!TextUtil::IsPositiveInteger($Value)) {
     299     croak "Error: ${ClassName}->SetMACCSKeysSize: Size value, $Value, is not valid:  It must be a positive integer...";
     300   }
     301   if ($Value !~ /^(166|322)/i) {
     302     croak "Error: ${ClassName}->SetMACCSKeysSize: The current release of MayaChemTools doesn't support MDL MACCS $Value keys...";
     303   }
     304   $This->{MACCSKeysSize} = $Value;
     305
     306   return $This;
     307 }
     308
     309 # Set minimum path length...
     310 #
     311 sub SetMinPathLength {
     312   my($This, $Value) = @_;
     313
     314   if (!TextUtil::IsPositiveInteger($Value)) {
     315     croak "Error: ${ClassName}->SetMinPathLength: MinPathLength value, $Value, is not valid:  It must be a positive integer...";
     316   }
     317   $This->{MinPathLength} = $Value;
     318
     319   return $This;
     320 }
     321
     322 # Set maximum path length...
     323 #
     324 sub SetMaxPathLength {
     325   my($This, $Value) = @_;
     326
     327   if (!TextUtil::IsPositiveInteger($Value)) {
     328     croak "Error: ${ClassName}->SetMaxPathLength: MaxPathLength value, $Value, is not valid:  It must be a positive integer...";
     329   }
     330   $This->{MaxPathLength} = $Value;
     331
     332   return $This;
     333 }
     334
     335 # Set minimum  bond distance between atom pairs during topological and topological
     336 # pharmacophore atom pairs/triplets fingerprints...
     337 #
     338 sub SetMinDistance {
     339   my($This, $Value) = @_;
     340
     341   if (!TextUtil::IsPositiveInteger($Value)) {
     342     croak "Error: ${ClassName}->SetMinDistance: MinDistance value, $Value, is not valid:  It must be a positive integer...";
     343   }
     344   $This->{MinDistance} = $Value;
     345
     346   return $This;
     347 }
     348
     349 # Set maximum  bond distance between atom pairs during topological and topological
     350 # pharmacophore atom pairs/triplets fingerprints...
     351 #
     352 sub SetMaxDistance {
     353   my($This, $Value) = @_;
     354
     355   if (!TextUtil::IsPositiveInteger($Value)) {
     356     croak "Error: ${ClassName}->SetMaxDistance: MaxDistance value, $Value, is not valid:  It must be a positive integer...";
     357   }
     358   $This->{MaxDistance} = $Value;
     359
     360   return $This;
     361 }
     362
     363 # Set atom neighborhood radius...
     364 #
     365 sub SetNeighborhoodRadius {
     366   my($This, $Value) = @_;
     367
     368   if (!TextUtil::IsInteger($Value)) {
     369     croak "Error: ${ClassName}->SetNeighborhoodRadius: NeighborhoodRadius value, $Value, is not valid:  It must be an  integer...";
     370   }
     371
     372   if ($Value < 0 ) {
     373     croak "Error: ${ClassName}->SetNeighborhoodRadius: NeighborhoodRadius value, $Value, is not valid:  It must be >= 0...";
     374   }
     375   $This->{NeighborhoodRadius} = $Value;
     376
     377   return $This;
     378 }
     379
     380 # Set molecular complexity type...
     381 #
     382 sub SetMolecularComplexityType {
     383   my($This, $Value) = @_;
     384
     385   if ($Value !~ /^(AtomTypesFingerprints|ExtendedConnectivityFingerprints|MACCSKeys|PathLengthFingerprints|TopologicalAtomPairsFingerprints|TopologicalAtomTripletsFingerprints|TopologicalAtomTorsionsFingerprints|TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
     386     croak "Error: ${ClassName}->SetMolecularComplexityType: MolecularComplexityType value, $Value, is not valid. Supported values: AtomTypesFingerprints, ExtendedConnectivityFingerprints, MACCSKeys, PathLengthFingerprints, TopologicalAtomPairsFingerprints, TopologicalAtomTripletsFingerprints, TopologicalAtomTorsionsFingerprints, TopologicalPharmacophoreAtomPairsFingerprints, or TopologicalPharmacophoreAtomTripletsFingerprints...";
     387   }
     388
     389   $This->{MolecularComplexityType} = $Value;
     390
     391   return $This;
     392 }
     393
     394 # Set distance bin size for binning pharmacophore atom pair distances in atom triplets...
     395 #
     396 sub SetDistanceBinSize {
     397   my($This, $Value) = @_;
     398
     399   if (!TextUtil::IsPositiveInteger($Value)) {
     400     croak "Error: ${ClassName}->SetDistanceBinSize: DistanceBinSize value, $Value, is not valid:  It must be a positive integer...";
     401   }
     402   $This->{DistanceBinSize} = $Value;
     403
     404   return $This;
     405 }
     406
     407 # Set normalization methodology to use for scaling the number of bits-set or unique keys
     408 # in fingerprints...
     409 #
     410 sub SetNormalizationMethodology {
     411   my($This, $Value) = @_;
     412
     413   if ($Value !~ /^(ByHeavyAtomsCount|ByPossibleKeysCount|None)$/i) {
     414     croak "Error: ${ClassName}->SetNormalizationMethodology: NormalizationMethodology value, $Value, is not valid. Supported values: None, ByHeavyAtomsCount or ByPossibleKeysCount...";
     415   }
     416
     417   if ($This->{MolecularComplexityType}) {
     418     if ($This->{MolecularComplexityType} !~ /^(ExtendedConnectivityFingerprints|TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
     419       croak "Error: ${ClassName}->SetNormalizationMethodology: Normalization is not supported for MolecularComplexityType: $This->{MolecularComplexityType}. Valid MolecularComplexityType values: ExtendedConnectivityFingerprints, TopologicalPharmacophoreAtomPairsFingerprints, or TopologicalPharmacophoreAtomTripletsFingerprints...\n";
     420     }
     421
     422     if ($This->{MolecularComplexityType} =~ /^ExtendedConnectivityFingerprints$/i && $Value !~ /^(ByHeavyAtomsCount|None)$/i) {
     423       croak "Error: ${ClassName}->SetNormalizationMethodology: NormalizationMethodology value, $Value, is not valid for MolecularComplexityType: $This->{MolecularComplexityType}. Supported values: None or ByHeavyAtomsCount...";
     424     }
     425
     426     if ($This->{MolecularComplexityType} =~ /^(TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i && $Value !~ /^(ByPossibleKeysCount|None)$/i) {
     427       croak "Error: ${ClassName}->SetNormalizationMethodology: NormalizationMethodology value, $Value, is not valid for MolecularComplexityType: $This->{MolecularComplexityType}. Supported values: None or ByPossibleKeysCount...";
     428     }
     429   }
     430
     431   $This->{NormalizationMethodology} = $Value;
     432
     433   return $This;
     434 }
     435
     436 # Set intial atom identifier type..
     437 #
     438 sub SetAtomIdentifierType {
     439   my($This, $IdentifierType) = @_;
     440
     441   if ($IdentifierType !~ /^(AtomicInvariantsAtomTypes|FunctionalClassAtomTypes|DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
     442     croak "Error: ${ClassName}->SetAtomIdentifierType: Specified value, $IdentifierType, for AtomIdentifierType is not vaild. Supported types in current release of MayaChemTools: AtomicInvariantsAtomTypes, FunctionalClassAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes and UFFAtomTypes.";
     443   }
     444
     445   # FunctionalClassAtomTypes is the only valid atom identifier type for pharmacophore fingerprints...
     446   if ($This->{MolecularComplexityType} =~ /^(TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
     447     if ($IdentifierType !~ /^FunctionalClassAtomTypes$/i) {
     448       croak "Error: ${ClassName}->SetAtomIdentifierType: Specified value, $IdentifierType, for AtomIdentifierType is not vaild. Supported type for $This->{MolecularComplexityType} complexity type: FunctionalClassAtomTypes.";
     449     }
     450   }
     451
     452   if ($This->{AtomIdentifierType}) {
     453     croak "Error: ${ClassName}->SetAtomIdentifierType: Can't change intial atom identifier type:  It's already set...";
     454   }
     455
     456   $This->{AtomIdentifierType} = $IdentifierType;
     457
     458   # Initialize identifier type information...
     459   $This->_InitializeAtomIdentifierTypeInformation();
     460
     461   return $This;
     462 }
     463
     464 # Calculate molecular complexity [ Ref 117-119 ] of a molecule using its fingerprints.
     465 #
     466 # The current release of MayaChemTools supports calculation of molecular complexity
     467 # corresponding to the number of bits-set or unique keys in molecular fingerprints. The
     468 # following types of fingerprints based molecular complexity measures are supported:
     469 #
     470 # AtomTypesFingerprints
     471 # ExtendedConnectivityFingerprints
     472 # MACCSKeys
     473 # PathLengthFingerprints
     474 # TopologicalAtomPairsFingerprints
     475 # TopologicalAtomTripletsFingerprints
     476 # TopologicalAtomTorsionsFingerprints
     477 # TopologicalPharmacophoreAtomPairsFingerprints
     478 # TopologicalPharmacophoreAtomTripletsFingerprints
     479 #
     480 # After the molecular complexity value has been calculated, it can also be normalized by
     481 # by scaling the number of bits-set or unique keys for following types of fingerprints:
     482 #
     483 # ExtendedConnectivityFingerprints
     484 # TopologicalPharmacophoreAtomPairsFingerprints
     485 # TopologicalPharmacophoreAtomTripletsFingerprints
     486 #
     487 # Two types of normalization methodologies are supported: by heavy atoms count for
     488 # extended connectivity fingerprints; by possible keys count for topological pharmacophore
     489 # atom pairs and triplets fingerprints.
     490 #
     491 sub GenerateDescriptors {
     492   my($This) = @_;
     493
     494   # Initialize descriptor values...
     495   $This->_InitializeDescriptorValues();
     496
     497   # Check availability of molecule...
     498   if (!$This->{Molecule}) {
     499     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Molecule data is not available: Molecule object hasn't been set...";
     500     return undef;
     501   }
     502
     503   # Calculate descriptor values...
     504   if (!$This->_CalculateDescriptorValues()) {
     505     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Couldn't calculate MolecularComplexity values corresponding to assigned MolecularComplexity atom types...";
     506     return undef;
     507   }
     508
     509   # Set final descriptor values...
     510   $This->_SetFinalDescriptorValues();
     511
     512   return $This;
     513 }
     514
     515 # Calculate molecular complexity value...
     516 #
     517 sub _CalculateDescriptorValues {
     518   my($This) = @_;
     519   my($FingerprintsObject, $MethodName);
     520
     521   # Setup fingerprints object and generate fingerprints...
     522   $MethodName = "_Setup" . $This->{MolecularComplexityType};
     523   $FingerprintsObject = $This->$MethodName();
     524
     525   $FingerprintsObject->GenerateFingerprints();
     526
     527   # Make sure atom types fingerprints generation is successful...
     528   if (!$FingerprintsObject->IsFingerprintsGenerationSuccessful()) {
     529     return undef;
     530   }
     531
     532   if (!$This->_CalculateMolecularComplexity($FingerprintsObject)) {
     533     return undef;
     534   }
     535
     536   # Normalize molecular complexity...
     537   if ($This->{NormalizationMethodology} !~ /^None$/i) {
     538     if (!$This->_NormalizeMolecularComplexity($FingerprintsObject)) {
     539       return undef;
     540     }
     541   }
     542
     543   return $This;
     544 }
     545
     546 # Setup atom types fingerprints...
     547 #
     548 sub _SetupAtomTypesFingerprints {
     549   my($This) = @_;
     550   my($FingerprintsObject);
     551
     552   $FingerprintsObject = new Fingerprints::AtomTypesFingerprints('Molecule' => $This->{Molecule}, 'Type' => 'AtomTypesCount', 'AtomIdentifierType' => $This->{AtomIdentifierType},  'IgnoreHydrogens' => 1);
     553   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
     554
     555   return $FingerprintsObject;
     556 }
     557
     558 # Setup extended connectivity fingerprints...
     559 #
     560 sub _SetupExtendedConnectivityFingerprints {
     561   my($This) = @_;
     562   my($FingerprintsObject);
     563
     564   $FingerprintsObject = new Fingerprints::ExtendedConnectivityFingerprints('Molecule' => $This->{Molecule}, 'Type' => 'ExtendedConnectivity', 'NeighborhoodRadius' => $This->{NeighborhoodRadius}, 'AtomIdentifierType' => $This->{AtomIdentifierType});
     565   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
     566
     567   return $FingerprintsObject;
     568 }
     569
     570 # Setup MACCS keys...
     571 #
     572 sub _SetupMACCSKeys {
     573   my($This) = @_;
     574   my($FingerprintsObject);
     575
     576   $FingerprintsObject = new Fingerprints::MACCSKeys('Molecule' => $This->{Molecule}, 'Type' => 'MACCSKeyBits', 'Size' => $This->{MACCSKeysSize});
     577
     578   return $FingerprintsObject;
     579 }
     580
     581 # Set up path length fingerprints...
     582 #
     583 sub _SetupPathLengthFingerprints {
     584   my($This) = @_;
     585   my($FingerprintsObject);
     586
     587   $FingerprintsObject = new Fingerprints::PathLengthFingerprints('Molecule' => $This->{Molecule}, 'Type' => 'PathLengthCount', 'AtomIdentifierType' => $This->{AtomIdentifierType}, 'MinLength' => $This->{MinPathLength}, 'MaxLength' => $This->{MaxPathLength}, 'AllowRings' => 1, 'AllowSharedBonds' => 1, 'UseBondSymbols' => $This->{UseBondSymbols}, 'UseUniquePaths' => 1);
     588   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
     589
     590   return $FingerprintsObject;
     591 }
     592
     593 # Setup topological atom pairs fingerprints...
     594 #
     595 sub _SetupTopologicalAtomPairsFingerprints {
     596   my($This) = @_;
     597   my($FingerprintsObject);
     598
     599   $FingerprintsObject = new Fingerprints::TopologicalAtomPairsFingerprints('Molecule' => $This->{Molecule}, 'MinDistance' => $This->{MinDistance}, 'MaxDistance' => $This->{MaxDistance}, 'AtomIdentifierType' => $This->{AtomIdentifierType});
     600   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
     601
     602   return $FingerprintsObject;
     603 }
     604
     605 # Setup topological atom triplets fingerprints...
     606 #
     607 sub _SetupTopologicalAtomTripletsFingerprints {
     608   my($This) = @_;
     609   my($FingerprintsObject);
     610
     611   $FingerprintsObject = new Fingerprints::TopologicalAtomTripletsFingerprints('Molecule' => $This->{Molecule}, 'MinDistance' => $This->{MinDistance}, 'MaxDistance' => $This->{MaxDistance}, 'UseTriangleInequality' => $This->{UseTriangleInequality}, 'AtomIdentifierType' => $This->{AtomIdentifierType});
     612   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
     613
     614   return $FingerprintsObject;
     615 }
     616
     617 # Setup topological atom torsions fingerprints...
     618 #
     619 sub _SetupTopologicalAtomTorsionsFingerprints {
     620   my($This) = @_;
     621   my($FingerprintsObject);
     622
     623   $FingerprintsObject = new Fingerprints::TopologicalAtomTorsionsFingerprints('Molecule' => $This->{Molecule},  'AtomIdentifierType' => $This->{AtomIdentifierType});
     624
     625   $This->_SetAtomIdentifierTypeValuesToUse($FingerprintsObject);
     626
     627   return $FingerprintsObject;
     628 }
     629
     630 # Setup TopologicalPharmacophoreAtomPairsFingerprints...
     631 #
     632 sub _SetupTopologicalPharmacophoreAtomPairsFingerprints {
     633   my($This) = @_;
     634   my($FingerprintsObject, $AtomPairsSetSizeToUse);
     635
     636   # Use fixed size to get total number of possible keys for normalization...
     637   $AtomPairsSetSizeToUse = ($This->{NormalizationMethodology} =~ /^ByPossibleKeysCount$/i) ? 'FixedSize' : 'ArbitrarySize';
     638
     639   $FingerprintsObject = new Fingerprints::TopologicalPharmacophoreAtomPairsFingerprints('Molecule' => $This->{Molecule}, 'AtomPairsSetSizeToUse' => $AtomPairsSetSizeToUse, 'MinDistance' => $This->{MinDistance}, 'MaxDistance' => $This->{MaxDistance}, 'AtomTypesToUse' => \@{$This->{FunctionalClassesToUse}}, 'NormalizationMethodology' => 'None', 'ValuesPrecision' => 2);
     640
     641   return $FingerprintsObject;
     642 }
     643
     644 # Setup TopologicalPharmacophoreAtomTripletsFingerprints...
     645 #
     646 sub _SetupTopologicalPharmacophoreAtomTripletsFingerprints {
     647   my($This) = @_;
     648   my($FingerprintsObject, $AtomTripletsSetSizeToUse);
     649
     650   # Use fixed size to get total number of possible keys for normalization...
     651   $AtomTripletsSetSizeToUse = ($This->{NormalizationMethodology} =~ /^ByPossibleKeysCount$/i) ? 'FixedSize' : 'ArbitrarySize';
     652
     653   $FingerprintsObject = new Fingerprints::TopologicalPharmacophoreAtomTripletsFingerprints('Molecule' => $This->{Molecule}, 'AtomTripletsSetSizeToUse' => $AtomTripletsSetSizeToUse, 'MinDistance' => $This->{MinDistance}, 'MaxDistance' => $This->{MaxDistance}, 'DistanceBinSize' => $This->{DistanceBinSize}, 'UseTriangleInequality' => $This->{UseTriangleInequality}, 'AtomTypesToUse' => \@{$This->{FunctionalClassesToUse}});
     654
     655   return $FingerprintsObject;
     656 }
     657
     658 # Normalize molecular complexity value...
     659 #
     660 sub _NormalizeMolecularComplexity {
     661   my($This, $FingerprintsObject) = @_;
     662
     663   if ($This->{MolecularComplexityType} =~ /^ExtendedConnectivityFingerprints$/i && $This->{NormalizationMethodology} =~ /^ByHeavyAtomsCount$/i) {
     664     return $This->_NormalizeMolecularComplexityByHeavyAtomsCount($FingerprintsObject);
     665   }
     666   elsif ($This->{MolecularComplexityType} =~ /^(TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i && $This->{NormalizationMethodology} =~ /^ByPossibleKeysCount$/i) {
     667     return $This->_NormalizeMolecularComplexityByPossibleKeysCount($FingerprintsObject);
     668   }
     669   else {
     670     warn "Warning: ${ClassName}->_NormalizeMolecularComplexity: NormalizationMethodology value, $This->{NormalizationMethodology}, is not valid. Supported values: ByHeavyAtomsCount or ByPossibleKeysCount...";
     671   }
     672   return undef;
     673 }
     674
     675 # Normalize molecular complexity value by heavy atom count...
     676 #
     677 sub _NormalizeMolecularComplexityByHeavyAtomsCount {
     678   my($This, $FingerprintsObject) = @_;
     679   my($NumOfHeavyAtoms, $NormalizedComplexity);
     680
     681   $NumOfHeavyAtoms = $This->{Molecule}->GetNumOfHeavyAtoms();
     682   if (!$NumOfHeavyAtoms) {
     683     return $This;
     684   }
     685
     686   $NormalizedComplexity = $This->{MolecularComplexity} / $NumOfHeavyAtoms;
     687   $This->{MolecularComplexity} = MathUtil::round($NormalizedComplexity, 2) + 0;
     688
     689   return $This;
     690 }
     691
     692 # Normalize molecular complexity value by possible keys count...
     693 #
     694 sub _NormalizeMolecularComplexityByPossibleKeysCount {
     695   my($This, $FingerprintsObject) = @_;
     696   my($NumOfPossibleKeys, $NormalizedComplexity);
     697
     698   $NumOfPossibleKeys = $FingerprintsObject->GetFingerprintsVector()->GetNumOfValues();
     699   if (!$NumOfPossibleKeys) {
     700     return $This;
     701   }
     702
     703   $NormalizedComplexity = $This->{MolecularComplexity} / $NumOfPossibleKeys;
     704   $This->{MolecularComplexity} = MathUtil::round($NormalizedComplexity, 2) + 0;
     705
     706   return $This;
     707 }
     708
     709 # Calculate molecular complexity value using fingerprints objects...
     710 #
     711 sub _CalculateMolecularComplexity {
     712   my($This, $FingerprintsObject) = @_;
     713
     714   if ($FingerprintsObject->GetVectorType() =~ /^FingerprintsBitVector$/i) {
     715     return $This->_CalculateMolecularComplexityUsingFingerprintsBitVector($FingerprintsObject->GetFingerprintsBitVector());
     716   }
     717   elsif ($FingerprintsObject->GetVectorType() =~ /^FingerprintsVector$/i) {
     718     return $This->_CalculateMolecularComplexityUsingFingerprintsVector($FingerprintsObject->GetFingerprintsVector());
     719   }
     720   else {
     721     warn "Warning: ${ClassName}->_CalculateMolecularComplexity: Fingerprints vector type  is not valid. Supported values: FingerprintsBitVector or FingerprintsVector...";
     722   }
     723
     724   return undef;
     725 }
     726
     727 # Calculate molecular complexity value using fingerprints vector...
     728 #
     729 sub _CalculateMolecularComplexityUsingFingerprintsVector {
     730   my($This, $FingerprintsVector) = @_;
     731
     732   $This->{MolecularComplexity} = ($FingerprintsVector->GetType() =~ /^(OrderedNumericalValues|NumericalValues)$/i) ? $FingerprintsVector->GetNumOfNonZeroValues() : $FingerprintsVector->GetNumOfValues();
     733
     734   return $This;
     735 }
     736
     737 # Calculate molecular complexity value using fingerprints vector...
     738 #
     739 sub _CalculateMolecularComplexityUsingFingerprintsBitVector {
     740   my($This, $FingerprintsBitVector) = @_;
     741
     742   $This->{MolecularComplexity} = $FingerprintsBitVector->GetNumOfSetBits();
     743
     744   return $This;
     745 }
     746
     747 # Setup final descriptor values...
     748 #
     749 sub _SetFinalDescriptorValues {
     750   my($This) = @_;
     751
     752   $This->{DescriptorsGenerated} = 1;
     753
     754   $This->SetDescriptorValues($This->{MolecularComplexity});
     755
     756   return $This;
     757 }
     758
     759 # Set atom identifier type to use for generating fingerprints...
     760 #
     761 sub _SetAtomIdentifierTypeValuesToUse {
     762   my($This, $FingerprintsObject) = @_;
     763
     764   if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
     765     $FingerprintsObject->SetAtomicInvariantsToUse(\@{$This->{AtomicInvariantsToUse}});
     766   }
     767   elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
     768     $FingerprintsObject->SetFunctionalClassesToUse(\@{$This->{FunctionalClassesToUse}});
     769   }
     770   elsif ($This->{AtomIdentifierType} =~ /^(DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
     771     # Nothing to do for now...
     772   }
     773   else {
     774     croak "Error: The value specified, $This->{AtomIdentifierType}, for option \"-a, --AtomIdentifierType\" is not valid. Supported atom identifier types in current release of MayaChemTools: AtomicInvariantsAtomTypes, DREIDINGAtomTypes, EStateAtomTypes, FunctionalClassAtomTypes, MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes\n";
     775   }
     776 }
     777
     778 # Initialize atom indentifier type information...
     779 #
     780 # Current supported values:
     781 #
     782 # AtomicInvariantsAtomTypes, FunctionalClassAtomTypes, DREIDINGAtomTypes, EStateAtomTypes,
     783 # MMFF94AtomTypes, SLogPAtomTypes, SYBYLAtomTypes, TPSAAtomTypes, UFFAtomTypes
     784 #
     785 sub _InitializeAtomIdentifierTypeInformation {
     786   my($This) = @_;
     787
     788   IDENTIFIERTYPE: {
     789     if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
     790       $This->_InitializeAtomicInvariantsAtomTypesInformation();
     791       last IDENTIFIERTYPE;
     792     }
     793     if ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
     794       $This->_InitializeFunctionalClassAtomTypesInformation();
     795       last IDENTIFIERTYPE;
     796     }
     797     if ($This->{AtomIdentifierType} =~ /^(DREIDINGAtomTypes|EStateAtomTypes|MMFF94AtomTypes|SLogPAtomTypes|SYBYLAtomTypes|TPSAAtomTypes|UFFAtomTypes)$/i) {
     798       # Nothing to do for now...
     799       last IDENTIFIERTYPE;
     800     }
     801     carp "Warning: ${ClassName}->_InitializeAtomIdentifierTypeInformation: Unknown atom indentifier type $This->{AtomIdentifierType}...";
     802   }
     803   return $This;
     804 }
     805
     806 # Initialize atomic invariants atom types, generated by AtomTypes::AtomicInvariantsAtomTypes
     807 # class, to use for generating initial atom identifiers...
     808 #
     809 # Let:
     810 #   AS = Atom symbol corresponding to element symbol
     811 #
     812 #   X<n>   = Number of non-hydrogen atom neighbors or heavy atoms attached to atom
     813 #   BO<n> = Sum of bond orders to non-hydrogen atom neighbors or heavy atoms attached to atom
     814 #   LBO<n> = Largest bond order of non-hydrogen atom neighbors or heavy atoms attached to atom
     815 #   SB<n> = Number of single bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
     816 #   DB<n> = Number of double bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
     817 #   TB<n> = Number of triple bonds to non-hydrogen atom neighbors or heavy atoms attached to atom
     818 #   H<n>   = Number of implicit and explicit hydrogens for atom
     819 #   Ar     = Aromatic annotation indicating whether atom is aromatic
     820 #   RA     = Ring atom annotation indicating whether atom is a ring
     821 #   FC<+n/-n> = Formal charge assigned to atom
     822 #   MN<n> = Mass number indicating isotope other than most abundant isotope
     823 #   SM<n> = Spin multiplicity of atom. Possible values: 1 (singlet), 2 (doublet) or 3 (triplet)
     824 #
     825 # Then:
     826 #
     827 #   Atom type generated by AtomTypes::AtomicInvariantsAtomTypes class corresponds to:
     828 #
     829 #     AS.X<n>.BO<n>.LBO<n>.<SB><n>.<DB><n>.<TB><n>.H<n>.Ar.RA.FC<+n/-n>.MN<n>.SM<n>
     830 #
     831 # Except for AS which is a required atomic invariant in atom types, all other atomic invariants are
     832 # optional.
     833 #
     834 # Default atomic invariants used for generating inital atom identifiers are [ Ref 24 ]:
     835 #
     836 #   AS, X<n>, BO<n>, H<n>, FC<+n/-n>, MN<n>
     837 #
     838 # In addition to usage of abbreviations for specifying atomic invariants, the following descriptive words
     839 # are also allowed:
     840 #
     841 # X : NumOfNonHydrogenAtomNeighbors or NumOfHeavyAtomNeighbors
     842 # BO : SumOfBondOrdersToNonHydrogenAtoms or SumOfBondOrdersToHeavyAtoms
     843 # LBO : LargestBondOrderToNonHydrogenAtoms or LargestBondOrderToHeavyAtoms
     844 # SB :  NumOfSingleBondsToNonHydrogenAtoms or NumOfSingleBondsToHeavyAtoms
     845 # DB : NumOfDoubleBondsToNonHydrogenAtoms or NumOfDoubleBondsToHeavyAtoms
     846 # TB : NumOfTripleBondsToNonHydrogenAtoms or NumOfTripleBondsToHeavyAtoms
     847 # H :  NumOfImplicitAndExplicitHydrogens
     848 # Ar : Aromatic
     849 # RA : RingAtom
     850 # FC : FormalCharge
     851 # MN : MassNumber
     852 # SM : SpinMultiplicity
     853 #
     854 sub _InitializeAtomicInvariantsAtomTypesInformation {
     855   my($This) = @_;
     856
     857   @{$This->{AtomicInvariantsToUse}} = ();
     858
     859   if ($This->{MolecularComplexityType} =~ /^(AtomTypesFingerprints|TopologicalAtomPairsFingerprints|TopologicalAtomTripletsFingerprints|TopologicalAtomTorsionsFingerprints)$/i) {
     860     @{$This->{AtomicInvariantsToUse}} = ('AS', 'X', 'BO', 'H', 'FC');
     861   }
     862   elsif ($This->{MolecularComplexityType} =~ /^ExtendedConnectivityFingerprints$/i) {
     863     @{$This->{AtomicInvariantsToUse}} = ('AS', 'X', 'BO', 'H', 'FC', 'MN');
     864   }
     865   elsif ($This->{MolecularComplexityType} =~ /^PathLengthFingerprints$/i) {
     866     @{$This->{AtomicInvariantsToUse}} = ('AS');
     867   }
     868
     869   return $This;
     870 }
     871
     872 # Initialize functional class atom types, generated by AtomTypes::FunctionalClassAtomTypes
     873 # class, to use for generating initial atom identifiers...
     874 #
     875 # Let:
     876 #   HBD: HydrogenBondDonor
     877 #   HBA: HydrogenBondAcceptor
     878 #   PI :  PositivelyIonizable
     879 #   NI : NegativelyIonizable
     880 #   Ar : Aromatic
     881 #   Hal : Halogen
     882 #   H : Hydrophobic
     883 #   RA : RingAtom
     884 #   CA : ChainAtom
     885 #
     886 # Then:
     887 #
     888 #   Functiononal class atom type specification for an atom corresponds to:
     889 #
     890 #     Ar.CA.H.HBA.HBD.Hal.NI.PI.RA
     891 #
     892 #   Default functional classes used are: HBD, HBA, PI, NI, Ar, Hal
     893 #
     894 #   FunctionalAtomTypes are assigned using the following definitions [ Ref 60-61, Ref 65-66 ]:
     895 #
     896 #     HydrogenBondDonor: NH, NH2, OH
     897 #     HydrogenBondAcceptor: N[!H], O
     898 #     PositivelyIonizable: +, NH2
     899 #     NegativelyIonizable: -, C(=O)OH, S(=O)OH, P(=O)OH
     900 #
     901 sub _InitializeFunctionalClassAtomTypesInformation {
     902   my($This) = @_;
     903
     904   @{$This->{FunctionalClassesToUse}} = ();
     905
     906   if ($This->{MolecularComplexityType} =~ /^(AtomTypesFingerprints|ExtendedConnectivityFingerprints|PathLengthFingerprints|TopologicalAtomPairsFingerprints|TopologicalAtomTripletsFingerprints|TopologicalAtomTorsionsFingerprints)$/i) {
     907     @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'Ar', 'Hal');
     908   }
     909   elsif ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomPairsFingerprints$/i) {
     910     @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'H');
     911   }
     912   elsif ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomTripletsFingerprints$/i) {
     913     @{$This->{FunctionalClassesToUse}} = ('HBD', 'HBA', 'PI', 'NI', 'H', 'Ar');
     914   }
     915
     916   return $This;
     917 }
     918
     919 # Set atomic invariants to use for generation of intial atom indentifiers...
     920 #
     921 sub SetAtomicInvariantsToUse {
     922   my($This, @Values) = @_;
     923   my($FirstValue, $TypeOfFirstValue, $AtomicInvariant, $SpecifiedAtomicInvariant, @SpecifiedAtomicInvariants, @AtomicInvariantsToUse);
     924
     925   if (!@Values) {
     926     carp "Warning: ${ClassName}->SetAtomicInvariantsToUse: No values specified...";
     927     return;
     928   }
     929
     930   if ($This->{AtomIdentifierType} !~ /^AtomicInvariantsAtomTypes$/i) {
     931     carp "Warning: ${ClassName}->SetAtomicInvariantsToUse: AtomicInvariantsToUse can't be set for InitialAtomIdentifierType of $This->{AtomIdentifierType}...";
     932     return;
     933   }
     934
     935   $FirstValue = $Values[0];
     936   $TypeOfFirstValue = ref $FirstValue;
     937
     938   @SpecifiedAtomicInvariants = ();
     939   @AtomicInvariantsToUse = ();
     940
     941   if ($TypeOfFirstValue =~ /^ARRAY/) {
     942     push @SpecifiedAtomicInvariants, @{$FirstValue};
     943   }
     944   else {
     945     push @SpecifiedAtomicInvariants, @Values;
     946   }
     947
     948   # Make sure specified AtomicInvariants are valid...
     949   for $SpecifiedAtomicInvariant (@SpecifiedAtomicInvariants) {
     950     if (!AtomTypes::AtomicInvariantsAtomTypes::IsAtomicInvariantAvailable($SpecifiedAtomicInvariant)) {
     951       croak "Error: ${ClassName}->SetAtomicInvariantsToUse: Specified atomic invariant, $SpecifiedAtomicInvariant, is not supported...\n ";
     952     }
     953     $AtomicInvariant = $SpecifiedAtomicInvariant;
     954     push @AtomicInvariantsToUse, $AtomicInvariant;
     955   }
     956
     957   # Set atomic invariants to use...
     958   @{$This->{AtomicInvariantsToUse}} = ();
     959   push @{$This->{AtomicInvariantsToUse}}, @AtomicInvariantsToUse;
     960
     961   return $This;
     962 }
     963
     964 # Set functional classes to use for generation of intial atom indentifiers...
     965 #
     966 sub SetFunctionalClassesToUse {
     967   my($This, @Values) = @_;
     968   my($FirstValue, $TypeOfFirstValue, $FunctionalClass, $SpecifiedFunctionalClass, @SpecifiedFunctionalClasses, @FunctionalClassesToUse);
     969
     970   if (!@Values) {
     971     carp "Warning: ${ClassName}->SetFunctionalClassesToUse: No values specified...";
     972     return;
     973   }
     974
     975   if ($This->{AtomIdentifierType} !~ /^FunctionalClassAtomTypes$/i) {
     976     carp "Warning: ${ClassName}->SetFunctionalClassesToUse: FunctionalClassesToUse can't be set for InitialAtomIdentifierType of $This->{AtomIdentifierType}...";
     977     return;
     978   }
     979
     980   $FirstValue = $Values[0];
     981   $TypeOfFirstValue = ref $FirstValue;
     982
     983   @SpecifiedFunctionalClasses = ();
     984   @FunctionalClassesToUse = ();
     985
     986   if ($TypeOfFirstValue =~ /^ARRAY/) {
     987     push @SpecifiedFunctionalClasses, @{$FirstValue};
     988   }
     989   else {
     990     push @SpecifiedFunctionalClasses, @Values;
     991   }
     992
     993   # Make sure specified FunctionalClasses are valid...
     994   for $SpecifiedFunctionalClass (@SpecifiedFunctionalClasses) {
     995     if (!AtomTypes::FunctionalClassAtomTypes::IsFunctionalClassAvailable($SpecifiedFunctionalClass)) {
     996       croak "Error: ${ClassName}->SetFunctionalClassesToUse: Specified functional class, $SpecifiedFunctionalClass, is not supported...\n ";
     997     }
     998     push @FunctionalClassesToUse, $SpecifiedFunctionalClass;
     999   }
    1000
    1001   # Set functional classes to use...
    1002   @{$This->{FunctionalClassesToUse}} = ();
    1003   push @{$This->{FunctionalClassesToUse}}, @FunctionalClassesToUse;
    1004
    1005   return $This;
    1006 }
    1007
    1008 # Return a string containg data for MolecularComplexityDescriptors object...
    1009 #
    1010 sub StringifyMolecularComplexityDescriptors {
    1011   my($This) = @_;
    1012   my($ComplexityDescriptorsString, $Nothing);
    1013
    1014   $ComplexityDescriptorsString = "MolecularDescriptorType: $This->{Type}; MolecularComplexityType: $This->{MolecularComplexityType}; " . $This->_StringifyDescriptorNamesAndValues();
    1015
    1016   # Setup fingerprints specific information...
    1017   if ($This->{MolecularComplexityType} =~ /^MACCSKeys$/i) {
    1018     $ComplexityDescriptorsString .= "; MACCSKeysSize = $This->{MACCSKeysSize}";
    1019   }
    1020   elsif ($This->{MolecularComplexityType} =~ /^ExtendedConnectivityFingerprints$/i) {
    1021     $ComplexityDescriptorsString .= "; NeighborhoodRadius = $This->{NeighborhoodRadius}; NormalizationMethodology = $This->{NormalizationMethodology}";
    1022   }
    1023   elsif ($This->{MolecularComplexityType} =~ /^PathLengthFingerprints$/i) {
    1024     $ComplexityDescriptorsString .= "; MinPathLength = $This->{MinPathLength}; MaxPathLength = $This->{MaxPathLength}; UseBondSymbols: " . ($This->{UseBondSymbols} ? "Yes" : "No");
    1025   }
    1026   elsif ($This->{MolecularComplexityType} =~ /^TopologicalAtomPairsFingerprints$/i) {
    1027     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}";
    1028   }
    1029   elsif ($This->{MolecularComplexityType} =~ /^TopologicalAtomTripletsFingerprints$/i) {
    1030     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}; UseTriangleInequality: " . ($This->{UseTriangleInequality} ? "Yes" : "No");
    1031   }
    1032   elsif ($This->{MolecularComplexityType} =~ /^TopologicalAtomTorsionsFingerprints$/i) {
    1033     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}";
    1034   }
    1035   elsif ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomPairsFingerprints$/i) {
    1036     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}; NormalizationMethodology = $This->{NormalizationMethodology}";
    1037   }
    1038   elsif ($This->{MolecularComplexityType} =~ /^TopologicalPharmacophoreAtomTripletsFingerprints$/i) {
    1039     $ComplexityDescriptorsString .= "; MinDistance = $This->{MinDistance}; MaxDistance = $This->{MaxDistance}; NormalizationMethodology = $This->{NormalizationMethodology};  DistanceBinSize: $This->{DistanceBinSize}; UseTriangleInequality: " . ($This->{UseTriangleInequality} ? "Yes" : "No");
    1040   }
    1041
    1042   # Setup atom identifier information...
    1043   if ($This->{MolecularComplexityType} =~ /^(AtomTypesFingerprints|ExtendedConnectivityFingerprints|PathLengthFingerprints|TopologicalAtomPairsFingerprints|TopologicalAtomTripletsFingerprints|TopologicalAtomTorsionsFingerprints|TopologicalPharmacophoreAtomPairsFingerprints|TopologicalPharmacophoreAtomTripletsFingerprints)$/i) {
    1044     $ComplexityDescriptorsString .= "; AtomIdentifierType = $This->{AtomIdentifierType}";
    1045
    1046     if ($This->{AtomIdentifierType} =~ /^AtomicInvariantsAtomTypes$/i) {
    1047       my($AtomicInvariant, @AtomicInvariants, @AtomicInvariantsOrder, %AvailableAtomicInvariants);
    1048
    1049       @AtomicInvariantsOrder = AtomTypes::AtomicInvariantsAtomTypes::GetAtomicInvariantsOrder();
    1050       %AvailableAtomicInvariants = AtomTypes::AtomicInvariantsAtomTypes::GetAvailableAtomicInvariants();
    1051
    1052       for $AtomicInvariant (@AtomicInvariantsOrder) {
    1053         push @AtomicInvariants, "$AtomicInvariant: $AvailableAtomicInvariants{$AtomicInvariant}";
    1054       }
    1055
    1056       $ComplexityDescriptorsString .= "; AtomicInvariantsToUse: <" . TextUtil::JoinWords(\@{$This->{AtomicInvariantsToUse}}, ", ", 0) . ">";
    1057       $ComplexityDescriptorsString .= "; AtomicInvariantsOrder: <" . TextUtil::JoinWords(\@AtomicInvariantsOrder, ", ", 0) . ">";
    1058       $ComplexityDescriptorsString .= "; AvailableAtomicInvariants: <" . TextUtil::JoinWords(\@AtomicInvariants, ", ", 0) . ">";
    1059     }
    1060     elsif ($This->{AtomIdentifierType} =~ /^FunctionalClassAtomTypes$/i) {
    1061       my($FunctionalClass, @FunctionalClasses, @FunctionalClassesOrder, %AvailableFunctionalClasses);
    1062
    1063       @FunctionalClassesOrder = AtomTypes::FunctionalClassAtomTypes::GetFunctionalClassesOrder();
    1064       %AvailableFunctionalClasses = AtomTypes::FunctionalClassAtomTypes::GetAvailableFunctionalClasses();
    1065
    1066       for $FunctionalClass (@FunctionalClassesOrder) {
    1067         push @FunctionalClasses, "$FunctionalClass: $AvailableFunctionalClasses{$FunctionalClass}";
    1068       }
    1069
    1070       $ComplexityDescriptorsString .= "; FunctionalClassesToUse: <" . TextUtil::JoinWords(\@{$This->{FunctionalClassesToUse}}, ", ", 0) . ">";
    1071       $ComplexityDescriptorsString .= "; FunctionalClassesOrder: <" . TextUtil::JoinWords(\@FunctionalClassesOrder, ", ", 0) . ">";
    1072       $ComplexityDescriptorsString .= "; AvailableFunctionalClasses: <" . TextUtil::JoinWords(\@FunctionalClasses, ", ", 0) . ">";
    1073     }
    1074   }
    1075   return $ComplexityDescriptorsString;
    1076 }
    1077
    1078 # Is it a MolecularComplexityDescriptors object?
    1079 sub _IsMolecularComplexityDescriptors {
    1080   my($Object) = @_;
    1081
    1082   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
    1083 }
    1084