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