MayaChemTools

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