MayaChemTools

   1 package MolecularDescriptors::MolecularDescriptorsGenerator;
   2 #
   3 # $RCSfile: MolecularDescriptorsGenerator.pm,v $
   4 # $Date: 2015/02/28 20:49:20 $
   5 # $Revision: 1.13 $
   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 ObjectProperty;
  34 use TextUtil ();
  35 use FileUtil ();
  36 use Molecule;
  37 use MolecularDescriptors::MolecularDescriptors;
  38 
  39 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  40 
  41 @ISA = qw(ObjectProperty Exporter);
  42 @EXPORT = qw();
  43 @EXPORT_OK = qw(GetAvailableDescriptorClassNames GetAvailableClassAndDescriptorNames GetAvailableDescriptorNames GetAvailableDescriptorNamesForDescriptorClass GetAvailableClassNameForDescriptorName GetRuleOf5DescriptorNames GetRuleOf3DescriptorNames IsDescriptorClassNameAvailable IsDescriptorNameAvailable);
  44 
  45 %EXPORT_TAGS = (all  => [@EXPORT, @EXPORT_OK]);
  46 
  47 # Setup class variables...
  48 my($ClassName, %DescriptorsDataMap);
  49 _InitializeClass();
  50 
  51 # Overload Perl functions...
  52 use overload '""' => 'StringifyMolecularDescriptorsGenerator';
  53 
  54 # Class constructor...
  55 sub new {
  56   my($Class, %NamesAndValues) = @_;
  57 
  58   # Initialize object...
  59   my $This = {};
  60   bless $This, ref($Class) || $Class;
  61   $This->_InitializeMolecularDescriptorsGenerator();
  62 
  63   $This->_InitializeMolecularDescriptorsGeneratorProperties(%NamesAndValues);
  64 
  65   return $This;
  66 }
  67 
  68 # Initialize class ...
  69 sub _InitializeClass {
  70   #Class name...
  71   $ClassName = __PACKAGE__;
  72 
  73   # Load available molecular descriptor classes...
  74   _LoadMolecularDescriptorsData();
  75 
  76 }
  77 
  78 # Initialize object data...
  79 #
  80 sub _InitializeMolecularDescriptorsGenerator {
  81   my($This) = @_;
  82 
  83   # Type of desciptors to generate...
  84   #
  85   # The current release of MayaChemTools supports generation of four sets of
  86   # descriptors: All available descriptors, rule of 5 or 3 descriptors or a specified
  87   # set of descriptors.
  88   #
  89   # Possible values: All, RuleOf5, RuleOf3 or Specify
  90   #
  91   # RuleOf5 [ Ref 91 ] descriptor names: MolecularWeight, HydrogenBondDonors, HydrogenBondAcceptors,
  92   # SLogP. RuleOf5 states: MolecularWeight <= 500, HydrogenBondDonors <= 5, HydrogenBondAcceptors <= 10,
  93   # and logP <= 5.
  94   #
  95   # RuleOf3 [ Ref 92 ] descriptor names: MolecularWeight, RotatableBonds, HydrogenBondDonors,
  96   # HydrogenBondAcceptors, SLogP, TPSA. RuleOf3 states: MolecularWeight <= 300, RotatableBonds <= 3,
  97   # HydrogenBondDonors <= 3, HydrogenBondAcceptors <= 3, logP <= 3, and TPSA <= 60.
  98   #
  99   # For Specify value of Mode, a set of descritor names must be specified using
 100   # DescriptorNames parameter.
 101   #
 102   # Default: All
 103   #
 104   $This->{Mode} = '';
 105 
 106   # Descriptor names used to generate descriptor values during a specified descriptor
 107   # generation mode...
 108   #
 109   @{$This->{DescriptorNames}} = ();
 110 
 111   # Descriptor calculation control parameters for specified descriptor class names...
 112   #
 113   # These parameters are passed on to appropriate descriptor classes during
 114   # instantiations of descriptor class objects.
 115   #
 116   %{$This->{DescriptorClassParameters}} = ();
 117 
 118   $This->{DescriptorClassesInstantiated} = 0;
 119 
 120   # Descriptor class names and objects corresponding to specified descriptor names...
 121   #
 122   @{$This->{DescriptorClassNames}} = ();
 123   %{$This->{DescriptorClassObjects}} = ();
 124 
 125   # Descriptor values generated for specified descriptor names...
 126   #
 127   @{$This->{DescriptorValues}} = ();
 128 
 129   return $This;
 130 }
 131 
 132 # Initialize object properties...
 133 #
 134 sub _InitializeMolecularDescriptorsGeneratorProperties {
 135   my($This, %NamesAndValues) = @_;
 136 
 137   my($Name, $Value, $MethodName);
 138   while (($Name, $Value) = each  %NamesAndValues) {
 139     $MethodName = "Set${Name}";
 140     $This->$MethodName($Value);
 141   }
 142 
 143   # Set default value for Mode...
 144   if (!$This->{Mode}) {
 145     $This->{Mode} = 'All';
 146   }
 147 
 148   $This->_CheckAndInitializeDescriptorNames();
 149 
 150   return $This;
 151 }
 152 
 153 # Set descriptors generation mode......
 154 #
 155 sub SetMode {
 156   my($This, $Value) = @_;
 157 
 158   # All - all available descriptors
 159   # Specify - Specified set of descriptors
 160 
 161   if ($Value !~ /^(All|RuleOf5|RuleOf3|Specify)$/i) {
 162     croak "Error: ${ClassName}->SetMode: Mode value, $Value, is not valid; Supported values: All, RuleOf5, RuleOf3 or Specify...";
 163   }
 164 
 165   $This->{Mode} = $Value;
 166 
 167   return $This;
 168 }
 169 
 170 # Set descriptor names to use for generating descriptor values using an array
 171 # or reference to an array...
 172 #
 173 sub SetDescriptorNames {
 174   my($This, @Values) = @_;
 175 
 176   if ($This->{Mode} =~ /^All$/i) {
 177     croak "Error: ${ClassName}->SetDescriptorNames: Descriptor names cann't be specified during \"All\" value of descsriptors generation \"Mode\"...";
 178   }
 179 
 180   if (!@Values) {
 181     return;
 182   }
 183 
 184   my($FirstValue, $TypeOfFirstValue);
 185 
 186   $FirstValue = $Values[0];
 187   $TypeOfFirstValue = ref $FirstValue;
 188 
 189   @{$This->{DescriptorNames}} = ();
 190 
 191   if ($TypeOfFirstValue =~ /^ARRAY/) {
 192     # Initialize using array refernce...
 193     push @{$This->{DescriptorNames}}, @{$FirstValue};
 194   }
 195   else {
 196     # It's a list of values...
 197     push @{$This->{DescriptorNames}}, @Values;
 198   }
 199 
 200   # Make sure specified descriptor names are valid...
 201   $This->_ValidateDescriptorNames();
 202 
 203   return $This;
 204 }
 205 
 206 # Get descriptor names as an array...
 207 #
 208 sub GetDescriptorNames {
 209   my($This) = @_;
 210 
 211   return wantarray ? @{$This->{DescriptorNames}} : scalar @{$This->{DescriptorNames}};
 212 }
 213 
 214 # Get all descriptor values as an array...
 215 #
 216 sub GetDescriptorValues {
 217   my($This) = @_;
 218 
 219   if ($This->{DescriptorsGenerated}) {
 220     return wantarray ? @{$This->{DescriptorValues}} : scalar @{$This->{DescriptorValues}};
 221   }
 222   else {
 223     my(@DescriptorValues);
 224 
 225     @DescriptorValues = ('None') x scalar @{$This->{DescriptorNames}};
 226 
 227     return wantarray ? @DescriptorValues : scalar @DescriptorValues;
 228   }
 229 }
 230 
 231 # Get descriptor value for a specified descriptor name...
 232 #
 233 sub GetDescriptorValueByName {
 234   my($This, $Name) = @_;
 235   my(%NamesAndValues);
 236 
 237   %NamesAndValues = $This->GetDescriptorNamesAndValues();
 238 
 239   return exists $NamesAndValues{$Name} ? $NamesAndValues{$Name} : 'None';
 240 
 241 }
 242 
 243 # Get calculated molecular descriptor names sand values as a  hash with names
 244 # and values as key/value pairs...
 245 #
 246 sub GetDescriptorNamesAndValues {
 247   my($This) = @_;
 248   my(%NamesAndValues);
 249 
 250   %NamesAndValues = ();
 251   @NamesAndValues{ @{$This->{DescriptorNames}} } = $This->GetDescriptorValues();
 252 
 253   return %NamesAndValues;
 254 }
 255 
 256 # Set up descriptor calculation control parameters for a specified descriptor class name...
 257 #
 258 # The specified parameter names and values are simply passed on to specified descriptor
 259 # class during instantiation of descriptor class object without any performing any validation
 260 # of parameter names and associated values. It's up to the appropriate descriptor class methods
 261 # to validate these parameters and values.
 262 #
 263 # In addition to specified parameter names and values, the parameter hash must also contain
 264 # descriptor class name as key and value pair with DescriptorClassName as key with class
 265 # name as value.
 266 #
 267 sub SetDescriptorClassParameters {
 268   my($This, %NamesAndValues) = @_;
 269   my($DescriptorClassName, $Name, $Value);
 270 
 271   if (!exists $NamesAndValues{DescriptorClassName}) {
 272     croak "Error: ${ClassName}->_SetDescriptorNameParameters: Can't set descriptor class name paramaters: DescriptorClassName is not specified...";
 273   }
 274 
 275   $DescriptorClassName = $NamesAndValues{DescriptorClassName};
 276   if (!IsDescriptorClassNameAvailable($DescriptorClassName)) {
 277     carp "Warning: ${ClassName}->_SetDescriptorClassParameters: Can't set descriptor class name paramaters: Specified descriptor class name, $DescriptorClassName, is not available...";
 278     return $This;
 279   }
 280 
 281   if (exists $This->{DescriptorClassParameters}{$DescriptorClassName}) {
 282     carp "Warning: ${ClassName}->SetDescriptorClassParameters: Class name parameters for $DescriptorClassName have already been specified: Replacing previous values...";
 283   }
 284 
 285   %{$This->{DescriptorClassParameters}{$DescriptorClassName}} = ();
 286   NAME: while (($Name, $Value) = each  %NamesAndValues) {
 287     if ($Name =~ /^DescriptorClassName$/) {
 288       next NAME;
 289     }
 290     $This->{DescriptorClassParameters}{$DescriptorClassName}{$Name} = $Value;
 291   }
 292 
 293   return $This;
 294 }
 295 
 296 # Get descriptor name parameters as a reference to hash of hashes with hash
 297 # keys corresponding to class name and class parameter name with hash value
 298 # as class parameter value...
 299 #
 300 sub GetDescriptorClassParameters {
 301   my($This) = @_;
 302 
 303   return \%{$This->{DescriptorClassParameters}};
 304 }
 305 
 306 # Get available descriptor class names as an array.
 307 #
 308 # This functionality can be either invoked as a class function or an
 309 # object method.
 310 #
 311 sub GetAvailableDescriptorClassNames {
 312 
 313   return wantarray ? @{$DescriptorsDataMap{ClassNames}} : scalar @{$DescriptorsDataMap{ClassNames}};
 314 }
 315 
 316 # Get available descriptors class and descriptors names as a hash containing key/value
 317 # pairs corresponding to class name and an array of descriptor names available for the
 318 # class.
 319 #
 320 # This functionality can be either invoked as a class function or an
 321 # object method.
 322 #
 323 sub GetAvailableClassAndDescriptorNames {
 324   my($DescriptorClassName, @DescriptorNames, %ClassAndDescriptorNames);
 325 
 326   %ClassAndDescriptorNames = ();
 327   for $DescriptorClassName (@{$DescriptorsDataMap{ClassNames}}) {
 328     @{$ClassAndDescriptorNames{$DescriptorClassName}} = ();
 329     push @{$ClassAndDescriptorNames{$DescriptorClassName}}, @{$DescriptorsDataMap{ClassToDescriptorNames}{$DescriptorClassName}};
 330   }
 331 
 332   return %ClassAndDescriptorNames;
 333 }
 334 
 335 # Get available descriptor names as an array.
 336 #
 337 # This functionality can be either invoked as a class function or an
 338 # object method.
 339 #
 340 sub GetAvailableDescriptorNames {
 341   my(@DescriptorNames);
 342 
 343   @DescriptorNames = ();
 344   push @DescriptorNames, map { @{$DescriptorsDataMap{ClassToDescriptorNames}{$_}} } @{$DescriptorsDataMap{ClassNames}};
 345 
 346   return wantarray ? @DescriptorNames : scalar @DescriptorNames;
 347 }
 348 
 349 # Is it a valid descriptors class name?
 350 #
 351 # This functionality can be either invoked as a class function or an
 352 # object method.
 353 #
 354 sub IsDescriptorClassNameAvailable {
 355   my($FirstParameter, $SecondParameter) = @_;
 356   my($This, $DescriptorClassName);
 357 
 358   if ((@_ == 2) && (_IsMolecularDescriptorsGenerator($FirstParameter))) {
 359     ($This, $DescriptorClassName) = ($FirstParameter, $SecondParameter);
 360   }
 361   else {
 362     ($DescriptorClassName) = ($FirstParameter);
 363   }
 364 
 365   return (exists $DescriptorsDataMap{ClassToDescriptorNames}{$DescriptorClassName}) ? 1 : 0;
 366 }
 367 
 368 # Is it a valid descriptor name?
 369 #
 370 # This functionality can be either invoked as a class function or an
 371 # object method.
 372 #
 373 sub IsDescriptorNameAvailable {
 374   my($FirstParameter, $SecondParameter) = @_;
 375   my($This, $DescriptorName);
 376 
 377   if ((@_ == 2) && (_IsMolecularDescriptorsGenerator($FirstParameter))) {
 378     ($This, $DescriptorName) = ($FirstParameter, $SecondParameter);
 379   }
 380   else {
 381     ($DescriptorName) = ($FirstParameter);
 382   }
 383 
 384   return (exists $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName}) ? 1 : 0;
 385 }
 386 
 387 # Get available descriptors names for a descriptor class as an array.
 388 #
 389 # This functionality can be either invoked as a class function or an
 390 # object method.
 391 #
 392 sub GetAvailableDescriptorNamesForDescriptorClass {
 393   my($FirstParameter, $SecondParameter) = @_;
 394   my($This, $DescriptorClassName, @DescriptorNames);
 395 
 396   if ((@_ == 2) && (_IsMolecularDescriptorsGenerator($FirstParameter))) {
 397     ($This, $DescriptorClassName) = ($FirstParameter, $SecondParameter);
 398   }
 399   else {
 400     ($DescriptorClassName) = ($FirstParameter);
 401   }
 402 
 403   @DescriptorNames = ();
 404   if (exists $DescriptorsDataMap{ClassToDescriptorNames}{$DescriptorClassName}) {
 405     push @DescriptorNames, @{$DescriptorsDataMap{ClassToDescriptorNames}{$DescriptorClassName}};
 406   }
 407 
 408   return wantarray ? @DescriptorNames : scalar @DescriptorNames;
 409 }
 410 
 411 # Get available descriptors class name for a descriptor name.
 412 #
 413 # This functionality can be either invoked as a class function or an
 414 # object method.
 415 #
 416 sub GetAvailableClassNameForDescriptorName {
 417   my($FirstParameter, $SecondParameter) = @_;
 418   my($This, $DescriptorClassName, $DescriptorName);
 419 
 420   if ((@_ == 2) && (_IsMolecularDescriptorsGenerator($FirstParameter))) {
 421     ($This, $DescriptorName) = ($FirstParameter, $SecondParameter);
 422   }
 423   else {
 424     ($DescriptorName) = ($FirstParameter);
 425   }
 426 
 427   $DescriptorClassName = '';
 428   if (exists $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName}) {
 429     $DescriptorClassName = $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName};
 430   }
 431 
 432   return $DescriptorClassName;
 433 }
 434 
 435 # Get RuleOf5 descriptor names as an array.
 436 #
 437 # This functionality can be either invoked as a class function or an
 438 # object method.
 439 #
 440 sub GetRuleOf5DescriptorNames {
 441   my(@DescriptorNames);
 442 
 443   @DescriptorNames = qw(MolecularWeight HydrogenBondDonors HydrogenBondAcceptors SLogP);
 444 
 445   return wantarray ? @DescriptorNames : scalar @DescriptorNames;
 446 }
 447 
 448 # Get RuleOf3 descriptor names as an array.
 449 #
 450 # This functionality can be either invoked as a class function or an
 451 # object method.
 452 #
 453 sub GetRuleOf3DescriptorNames {
 454   my(@DescriptorNames);
 455 
 456   @DescriptorNames = qw(MolecularWeight RotatableBonds HydrogenBondDonors HydrogenBondAcceptors SLogP TPSA);
 457 
 458   return wantarray ? @DescriptorNames : scalar @DescriptorNames;
 459 }
 460 
 461 
 462 # Set molecule object...
 463 #
 464 sub SetMolecule {
 465   my($This, $Molecule) = @_;
 466 
 467   $This->{Molecule} = $Molecule;
 468 
 469   # Weaken the reference to disable increment of reference count...
 470   Scalar::Util::weaken($This->{Molecule});
 471 
 472   return $This;
 473 }
 474 
 475 # Generate specified molecular descriptors...
 476 #
 477 # After instantiating descriptor class objects at first invocation and  initialializing
 478 # descriptor values during subsequent invocations, GenerateDescriptors method
 479 # provided by each descriptor class is used to calculate descriptor values for
 480 # specified descriptors.
 481 #
 482 sub GenerateDescriptors {
 483   my($This) = @_;
 484   my($DescriptorClassName, $DescriptorClassObject);
 485 
 486   # Initialize descriptor values...
 487   $This->_InitializeDescriptorValues();
 488 
 489   # Instantiate decriptor classed corresponding to specified descriptors...
 490   if (!$This->{DescriptorClassesInstantiated}) {
 491     $This->_InstantiateDescriptorClasses();
 492   }
 493 
 494   # Check availability of molecule...
 495   if (!$This->{Molecule}) {
 496     carp "Warning: ${ClassName}->GenerateDescriptors: $This->{Type} molecular descriptors generation didn't succeed: Molecule data is not available: Molecule object hasn't been set...";
 497     return undef;
 498   }
 499 
 500   # Calculate descriptor values...
 501   for $DescriptorClassName (@{$This->{DescriptorClassNames}}) {
 502     $DescriptorClassObject = $This->{DescriptorClassObjects}{$DescriptorClassName};
 503 
 504     $DescriptorClassObject->SetMolecule($This->{Molecule});
 505     $DescriptorClassObject->GenerateDescriptors();
 506 
 507     if (!$DescriptorClassObject->IsDescriptorsGenerationSuccessful()) {
 508       return undef;
 509     }
 510   }
 511 
 512   # Set final descriptor values...
 513   $This->_SetFinalDescriptorValues();
 514 
 515   return $This;
 516 }
 517 
 518 # Initialize descriptor values...
 519 #
 520 sub _InitializeDescriptorValues {
 521   my($This) = @_;
 522 
 523   $This->{DescriptorsGenerated} = 0;
 524 
 525   @{$This->{DescriptorValues}} = ();
 526 
 527   return $This;
 528 }
 529 
 530 # Setup final descriptor values...
 531 #
 532 sub _SetFinalDescriptorValues {
 533   my($This) = @_;
 534   my($DescriptorName, $DescriptorClassName, $DescriptorClassObject);
 535 
 536   $This->{DescriptorsGenerated} = 1;
 537 
 538   @{$This->{DescriptorValues}} = ();
 539 
 540   if ($This->{Mode} =~ /^All$/i) {
 541     # Set descriptor values for all available descriptors...
 542     for $DescriptorClassName (@{$This->{DescriptorClassNames}}) {
 543       $DescriptorClassObject = $This->{DescriptorClassObjects}{$DescriptorClassName};
 544 
 545       push @{$This->{DescriptorValues}}, $DescriptorClassObject->GetDescriptorValues();
 546     }
 547   }
 548   else {
 549     # Set descriptor values for a subset of available descriptors...
 550     for $DescriptorName (@{$This->{DescriptorNames}}) {
 551       $DescriptorClassName = $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName};
 552       $DescriptorClassObject = $This->{DescriptorClassObjects}{$DescriptorClassName};
 553 
 554       push @{$This->{DescriptorValues}}, $DescriptorClassObject->GetDescriptorValueByName($DescriptorName);
 555     }
 556   }
 557 
 558   return $This;
 559 }
 560 
 561 # Is descriptors generation successful?
 562 #
 563 # Notes:
 564 #   . After successful generation of descriptor values by each descriptor class
 565 #     corresponding to specified descriptor names, DescriptorsCalculated  to 1;
 566 #     otherwise, it's set to 0.
 567 #
 568 sub IsDescriptorsGenerationSuccessful {
 569   my($This) = @_;
 570 
 571   return $This->{DescriptorsGenerated} ? 1 : 0;
 572 }
 573 
 574 # Check and set default descriptor names for generating descriptor values...
 575 #
 576 sub _CheckAndInitializeDescriptorNames {
 577   my($This) = @_;
 578 
 579   if ($This->{Mode} =~ /^(All|RuleOf5|RuleOf3)$/i) {
 580     if (@{$This->{DescriptorNames}}) {
 581       croak "Error: ${ClassName}->_CheckAndInitializeDescriptorNames: Descriptor names can't be specified during \"All, RuleOf5 or RuleOf3\" values of descsriptors generation \"Mode\"...";
 582     }
 583   }
 584 
 585   if ($This->{Mode} =~ /^All$/i) {
 586     @{$This->{DescriptorNames}} = GetAvailableDescriptorNames();
 587   }
 588   elsif ($This->{Mode} =~ /^RuleOf5$/i) {
 589     @{$This->{DescriptorNames}} = GetRuleOf5DescriptorNames();
 590   }
 591   elsif ($This->{Mode} =~ /^RuleOf3$/i) {
 592     @{$This->{DescriptorNames}} = GetRuleOf3DescriptorNames();
 593   }
 594   elsif ($This->{Mode} =~ /^Specify$/i) {
 595     if (!@{$This->{DescriptorNames}}) {
 596       croak "Error: ${ClassName}->_CheckAndInitializeDescriptorNames: DescriptorNames must be specified during Specify value for Mode...";
 597     }
 598   }
 599   else {
 600     croak "Error: ${ClassName}->_CheckAndInitializeDescriptorNames: Mode value, $This->{Mode}, is not valid...";
 601   }
 602 }
 603 
 604 # Instantiate descriptor classes corresponding to specified descriptor names...
 605 #
 606 sub _InstantiateDescriptorClasses {
 607   my($This) = @_;
 608   my($DescriptorClassName, $DescriptorName, $DescriptorClassPath);
 609 
 610   $This->{DescriptorClassesInstantiated} = 1;
 611 
 612   @{$This->{DescriptorClassNames}} = ();
 613   %{$This->{DescriptorClassObjects}} = ();
 614 
 615   NAME: for $DescriptorName (@{$This->{DescriptorNames}}) {
 616     $DescriptorClassName = $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName};
 617 
 618     if (exists $This->{DescriptorClassObjects}{$DescriptorClassName}) {
 619       next NAME;
 620     }
 621     push @{$This->{DescriptorClassNames}}, $DescriptorClassName;
 622 
 623     $DescriptorClassPath = $DescriptorsDataMap{ClassNameToClassPath}{$DescriptorClassName};
 624 
 625     if (exists $This->{DescriptorClassParameters}{$DescriptorClassName}) {
 626       $This->{DescriptorClassObjects}{$DescriptorClassName} = $DescriptorClassPath->new(%{$This->{DescriptorClassParameters}{$DescriptorClassName}});
 627     }
 628     else {
 629       $This->{DescriptorClassObjects}{$DescriptorClassName} = $DescriptorClassPath->new();
 630     }
 631   }
 632 
 633   return $This;
 634 }
 635 
 636 # Return a string containg data for MolecularDescriptorsGenerator object...
 637 #
 638 sub StringifyMolecularDescriptorsGenerator {
 639   my($This) = @_;
 640   my($TheString, $NamesAndValuesString, $Name, $Value, @NamesAndValuesInfo, %NamesAndValues);
 641 
 642   # Type of MolecularDescriptors...
 643   $TheString = "MolecularDescriptorsGenerator: Mode - $This->{Mode}; SpecifiedDescriptorNames - < @{$This->{DescriptorNames}} >; AvailableMolecularDescriptorClassNames - < @{$DescriptorsDataMap{ClassNames}} >";
 644 
 645   @NamesAndValuesInfo = ();
 646   %NamesAndValues = $This->GetDescriptorNamesAndValues();
 647 
 648   for $Name (@{$This->{DescriptorNames}}) {
 649     $Value = $NamesAndValues{$Name};
 650     $Value = (TextUtil::IsEmpty($Value) || $Value =~ /^None$/i) ? 'None' : $Value;
 651     push @NamesAndValuesInfo, "$Name - $Value";
 652   }
 653   if (@NamesAndValuesInfo) {
 654     $TheString .= "Names - Values: <" . TextUtil::JoinWords(\@NamesAndValuesInfo, ", ", 0) . ">";
 655   }
 656   else {
 657     $TheString .= "Names - Values: < None>";
 658   }
 659 
 660   return $TheString;
 661 }
 662 
 663 # Is it a MolecularDescriptorsGenerator object?
 664 sub _IsMolecularDescriptorsGenerator {
 665   my($Object) = @_;
 666 
 667   return (Scalar::Util::blessed($Object) && $Object->isa($ClassName)) ? 1 : 0;
 668 }
 669 
 670 # Validate descriptor names for generating descriptor values...
 671 #
 672 sub _ValidateDescriptorNames {
 673   my($This) = @_;
 674   my($DescriptorName);
 675 
 676   for $DescriptorName (@{$This->{DescriptorNames}}) {
 677     if (!exists $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName}) {
 678       croak "Error: ${ClassName}->_SetAndValidateDescriptorNames: Specified descriptor name, $DescriptorName, is not valid...";
 679     }
 680   }
 681 
 682   return $This;
 683 }
 684 
 685 #
 686 # Load available molecular descriptors data...
 687 #
 688 # All available molecular descriptors classes are automatically detected in
 689 # MolecularDescriptors directory under <MayaChemTools>/lib directory and
 690 # information about available descriptor names is retrieved from each descriptor
 691 # class using function GetDescriptorNames. The following %DescriptorsDataMap
 692 # is setup containing all available molecular descriptors data:
 693 #
 694 #   @{$DescriptorsDataMap{ClassNames}}
 695 #   %{$DescriptorsDataMap{ClassNameToPath}}
 696 #   %{$DescriptorsDataMap{ClassToDescriptorNames}}
 697 #   %{$DescriptorsDataMap{DescriptorToClassName}}
 698 #
 699 # GenerateDescriptors method is invoked fo each specified descriptor class
 700 # object to calculate descriptor values for specified descriptors. After successful
 701 # calculation of descriptors, GetDescriptorValues or GetDescriptorValueByName
 702 # methods provided by descriptor objects are used to retrieve calculated
 703 # descriptor values.
 704 #
 705 sub _LoadMolecularDescriptorsData {
 706 
 707   %DescriptorsDataMap = ();
 708 
 709   _RetrieveAndLoadDescriptorClasses();
 710   _SetupDescriptorsDataMap();
 711 }
 712 
 713 #
 714 # Retrieve available molecular descriptors classes from MolecularDescriptors directory under
 715 # <MayaChemTools>/lib directory...
 716 #
 717 sub _RetrieveAndLoadDescriptorClasses {
 718   my($DescriptorsDirName, $MayaChemToolsLibDir, $DescriptorsDirPath, $IncludeDirName, $DescriptorClassName, $DescriptorClassPath, $DescriptorsClassFileName, @FileNames, @DescriptorsClassFileNames);
 719 
 720   @{$DescriptorsDataMap{ClassNames}} = ();
 721   %{$DescriptorsDataMap{ClassNameToPath}} = ();
 722 
 723   $DescriptorsDirName = "MolecularDescriptors";
 724   $MayaChemToolsLibDir = FileUtil::GetMayaChemToolsLibDirName();
 725 
 726   $DescriptorsDirPath = "$MayaChemToolsLibDir/$DescriptorsDirName";
 727 
 728   if (! -d "$DescriptorsDirPath") {
 729     croak "Error: ${ClassName}::_RetrieveAndLoadDescriptorClasses: MayaChemTools package molecular descriptors directory, $DescriptorsDirPath, is missing: Possible installation problems...";
 730   }
 731 
 732   @FileNames = ("$DescriptorsDirPath/*");
 733   $IncludeDirName = 0;
 734   @DescriptorsClassFileNames = FileUtil::ExpandFileNames(\@FileNames, "pm", $IncludeDirName);
 735 
 736   if (!@DescriptorsClassFileNames) {
 737     croak "Error: ${ClassName}::_RetrieveAndLoadDescriptorClasses: MayaChemTools package molecular descriptors directory, $DescriptorsDirPath, doesn't contain any molecular descriptor class: Possible installation problems...";
 738   }
 739 
 740   FILENAME: for $DescriptorsClassFileName (sort @DescriptorsClassFileNames) {
 741     if ($DescriptorsClassFileName !~ /\.pm/) {
 742       croak "Error: ${ClassName}::_RetrieveAndLoadDescriptorClasses: MayaChemTools package molecular descriptors directory, $DescriptorsDirPath, contains invalid class file name $DescriptorsClassFileName: Possible installation problems...";
 743     }
 744 
 745     # Ignore base class and descriptors generator class...
 746     if ($DescriptorsClassFileName =~ /^(MolecularDescriptorsGenerator\.pm|MolecularDescriptors\.pm)$/) {
 747       next FILENAME;
 748     }
 749 
 750     ($DescriptorClassName) = split /\./, $DescriptorsClassFileName;
 751     $DescriptorClassPath = "${DescriptorsDirName}::${DescriptorClassName}";
 752 
 753     # Load descriptors class...
 754     eval "use $DescriptorClassPath";
 755 
 756     if ($@) {
 757       croak "Error: ${ClassName}::_RetrieveAndLoadDescriptorClasses: use $DescriptorClassPath failed: $@ ...";
 758     }
 759 
 760     push @{$DescriptorsDataMap{ClassNames}}, $DescriptorClassName;
 761 
 762     $DescriptorsDataMap{ClassNameToClassPath}{$DescriptorClassName} = $DescriptorClassPath;
 763   }
 764 }
 765 
 766 #
 767 # Setup descriptors data map using loaded descriptor classes...
 768 #
 769 sub _SetupDescriptorsDataMap {
 770   my($DescriptorClassName, $DescriptorName, $DescriptorClassPath, @DescriptorNames);
 771 
 772   # Class to decriptor names map...
 773   %{$DescriptorsDataMap{ClassToDescriptorNames}} = ();
 774 
 775   # Descriptor to class name map...
 776   %{$DescriptorsDataMap{DescriptorToClassName}} = ();
 777 
 778   for $DescriptorClassName (@{$DescriptorsDataMap{ClassNames}}) {
 779     $DescriptorClassPath = $DescriptorsDataMap{ClassNameToClassPath}{$DescriptorClassName};
 780 
 781     @DescriptorNames = $DescriptorClassPath->GetDescriptorNames();
 782 
 783     if (!@DescriptorNames) {
 784       croak "Error: ${ClassName}::_SetupDescriptorsDataMap: Molecular descriptor class $DescriptorClassName doesn't provide any descriptor names...";
 785     }
 786 
 787     if (exists $DescriptorsDataMap{ClassToDescriptorNames}{$DescriptorClassName} ) {
 788       croak "Error: ${ClassName}::_SetupDescriptorsDataMap: Molecular descriptor class $DescriptorClassName has already been processed...";
 789     }
 790 
 791     @{$DescriptorsDataMap{ClassToDescriptorNames}{$DescriptorClassName}} = ();
 792     @{$DescriptorsDataMap{ClassToDescriptorNames}{$DescriptorClassName}} = @DescriptorNames;
 793 
 794     for $DescriptorName (@DescriptorNames) {
 795       if (exists $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName}) {
 796         croak "Error: ${ClassName}::_SetupDescriptorsDataMap: Molecular descriptor name, $DescriptorName, in class name, $DescriptorClassName, has already been provided by class name $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName}...";
 797       }
 798 
 799       $DescriptorsDataMap{DescriptorToClassName}{$DescriptorName} = $DescriptorClassName;
 800     }
 801   }
 802 }
 803