robert 2.1.1__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. {robert-2.1.1 → robert-2.1.2}/PKG-INFO +2 -2
  2. {robert-2.1.1 → robert-2.1.2}/robert/api.py +13 -11
  3. {robert-2.1.1 → robert-2.1.2}/robert/aqme.py +3 -2
  4. {robert-2.1.1 → robert-2.1.2}/robert/argument_parser.py +2 -1
  5. {robert-2.1.1 → robert-2.1.2}/robert/curate.py +3 -0
  6. {robert-2.1.1 → robert-2.1.2}/robert/generate.py +8 -3
  7. {robert-2.1.1 → robert-2.1.2}/robert/generate_utils.py +4 -0
  8. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/easyrob_launcher.py +37 -2
  9. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/main/window.py +99 -46
  10. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tabs/aqme.py +119 -21
  11. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/utils/aqme_utils.py +79 -9
  12. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/utils/utils_gui.py +15 -9
  13. {robert-2.1.1 → robert-2.1.2}/robert/predict.py +12 -0
  14. {robert-2.1.1 → robert-2.1.2}/robert/predict_utils.py +40 -32
  15. {robert-2.1.1 → robert-2.1.2}/robert/utils.py +221 -104
  16. {robert-2.1.1 → robert-2.1.2}/robert.egg-info/PKG-INFO +2 -2
  17. {robert-2.1.1 → robert-2.1.2}/setup.cfg +1 -1
  18. {robert-2.1.1 → robert-2.1.2}/setup.py +1 -1
  19. {robert-2.1.1 → robert-2.1.2}/tests/test_1curate.py +22 -1
  20. {robert-2.1.1 → robert-2.1.2}/tests/test_7api.py +65 -0
  21. {robert-2.1.1 → robert-2.1.2}/tests/test_easyrob.py +1 -1
  22. {robert-2.1.1 → robert-2.1.2}/LICENSE +0 -0
  23. {robert-2.1.1 → robert-2.1.2}/README.md +0 -0
  24. {robert-2.1.1 → robert-2.1.2}/robert/__init__.py +0 -0
  25. {robert-2.1.1 → robert-2.1.2}/robert/__main__.py +0 -0
  26. {robert-2.1.1 → robert-2.1.2}/robert/evaluate.py +0 -0
  27. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/__init__.py +0 -0
  28. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/easyrob.py +0 -0
  29. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/main/__init__.py +0 -0
  30. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tabs/__init__.py +0 -0
  31. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tabs/advanced_options.py +0 -0
  32. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tabs/images.py +0 -0
  33. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tabs/molssi.py +0 -0
  34. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tabs/predictions.py +0 -0
  35. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tabs/results.py +0 -0
  36. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/chemdraw.md +0 -0
  37. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/csv.md +0 -0
  38. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/descriptors.md +0 -0
  39. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/overview.md +0 -0
  40. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/predictions.md +0 -0
  41. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_1.png +0 -0
  42. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_10.png +0 -0
  43. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_11.png +0 -0
  44. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_12.png +0 -0
  45. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_13.png +0 -0
  46. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_2.png +0 -0
  47. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_3.png +0 -0
  48. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_4.png +0 -0
  49. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_5.png +0 -0
  50. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_6.png +0 -0
  51. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_7.png +0 -0
  52. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_8.png +0 -0
  53. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/chemdraw/chemdraw_9.png +0 -0
  54. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_1.png +0 -0
  55. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_1_1.png +0 -0
  56. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_1_2.png +0 -0
  57. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_1_3.png +0 -0
  58. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_1_4.png +0 -0
  59. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_1_5.png +0 -0
  60. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_2.png +0 -0
  61. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_3.png +0 -0
  62. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_4.png +0 -0
  63. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_5.png +0 -0
  64. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_6.png +0 -0
  65. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_7.png +0 -0
  66. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_8.png +0 -0
  67. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/csv/csv_9.png +0 -0
  68. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/descriptors/descriptors_1.png +0 -0
  69. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/descriptors/descriptors_2.png +0 -0
  70. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/descriptors/descriptors_3.png +0 -0
  71. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/descriptors/descriptors_4.png +0 -0
  72. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/descriptors/descriptors_5.png +0 -0
  73. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/descriptors/descriptors_6.png +0 -0
  74. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1.png +0 -0
  75. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1_1.png +0 -0
  76. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1_2.png +0 -0
  77. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1_3.png +0 -0
  78. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1_4.png +0 -0
  79. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1_5.png +0 -0
  80. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1_6.png +0 -0
  81. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1_7.png +0 -0
  82. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_1_8.png +0 -0
  83. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_2.png +0 -0
  84. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_3.png +0 -0
  85. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_4.png +0 -0
  86. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_5.png +0 -0
  87. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_5_1.png +0 -0
  88. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_5_2.png +0 -0
  89. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/overview/overview_5_3.png +0 -0
  90. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/predictions/predictions_1.png +0 -0
  91. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/predictions/predictions_1_1.png +0 -0
  92. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/predictions/predictions_2.png +0 -0
  93. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/predictions/predictions_3.png +0 -0
  94. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/predictions/predictions_4.png +0 -0
  95. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/predictions/predictions_5.png +0 -0
  96. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/tutorials/tutorial_images/predictions/predictions_6.png +0 -0
  97. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/utils/__init__.py +0 -0
  98. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/utils/molssi_utils.py +0 -0
  99. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/utils/predictions_utils.py +0 -0
  100. {robert-2.1.1 → robert-2.1.2}/robert/gui_easyrob/version.py +0 -0
  101. {robert-2.1.1 → robert-2.1.2}/robert/icons/Robert_icon.png +0 -0
  102. {robert-2.1.1 → robert-2.1.2}/robert/icons/Robert_logo.jpg +0 -0
  103. {robert-2.1.1 → robert-2.1.2}/robert/icons/Robert_logo_transparent.png +0 -0
  104. {robert-2.1.1 → robert-2.1.2}/robert/icons/documentation_icon.png +0 -0
  105. {robert-2.1.1 → robert-2.1.2}/robert/icons/info_icon.png +0 -0
  106. {robert-2.1.1 → robert-2.1.2}/robert/icons/pdf_icon.png +0 -0
  107. {robert-2.1.1 → robert-2.1.2}/robert/icons/play_icon.png +0 -0
  108. {robert-2.1.1 → robert-2.1.2}/robert/icons/stop_icon.png +0 -0
  109. {robert-2.1.1 → robert-2.1.2}/robert/icons/youtube_icon.ico +0 -0
  110. {robert-2.1.1 → robert-2.1.2}/robert/model_params/ADAB_params.yaml +0 -0
  111. {robert-2.1.1 → robert-2.1.2}/robert/model_params/GB_params.yaml +0 -0
  112. {robert-2.1.1 → robert-2.1.2}/robert/model_params/GP_params.yaml +0 -0
  113. {robert-2.1.1 → robert-2.1.2}/robert/model_params/MVL_params.yaml +0 -0
  114. {robert-2.1.1 → robert-2.1.2}/robert/model_params/NN_params.yaml +0 -0
  115. {robert-2.1.1 → robert-2.1.2}/robert/model_params/RF_params.yaml +0 -0
  116. {robert-2.1.1 → robert-2.1.2}/robert/model_params/VR_params.yaml +0 -0
  117. {robert-2.1.1 → robert-2.1.2}/robert/report/Robert_icon.ico +0 -0
  118. {robert-2.1.1 → robert-2.1.2}/robert/report/Robert_logo.jpg +0 -0
  119. {robert-2.1.1 → robert-2.1.2}/robert/report/Robert_logo1.jpg +0 -0
  120. {robert-2.1.1 → robert-2.1.2}/robert/report/abbrev.png +0 -0
  121. {robert-2.1.1 → robert-2.1.2}/robert/report/adv_anal.png +0 -0
  122. {robert-2.1.1 → robert-2.1.2}/robert/report/features.png +0 -0
  123. {robert-2.1.1 → robert-2.1.2}/robert/report/generate.png +0 -0
  124. {robert-2.1.1 → robert-2.1.2}/robert/report/outliers.png +0 -0
  125. {robert-2.1.1 → robert-2.1.2}/robert/report/pred.png +0 -0
  126. {robert-2.1.1 → robert-2.1.2}/robert/report/repro.png +0 -0
  127. {robert-2.1.1 → robert-2.1.2}/robert/report/score.png +0 -0
  128. {robert-2.1.1 → robert-2.1.2}/robert/report/score_0.jpg +0 -0
  129. {robert-2.1.1 → robert-2.1.2}/robert/report/score_1.jpg +0 -0
  130. {robert-2.1.1 → robert-2.1.2}/robert/report/score_10.jpg +0 -0
  131. {robert-2.1.1 → robert-2.1.2}/robert/report/score_2.jpg +0 -0
  132. {robert-2.1.1 → robert-2.1.2}/robert/report/score_3.jpg +0 -0
  133. {robert-2.1.1 → robert-2.1.2}/robert/report/score_4.jpg +0 -0
  134. {robert-2.1.1 → robert-2.1.2}/robert/report/score_5.jpg +0 -0
  135. {robert-2.1.1 → robert-2.1.2}/robert/report/score_6.jpg +0 -0
  136. {robert-2.1.1 → robert-2.1.2}/robert/report/score_7.jpg +0 -0
  137. {robert-2.1.1 → robert-2.1.2}/robert/report/score_8.jpg +0 -0
  138. {robert-2.1.1 → robert-2.1.2}/robert/report/score_9.jpg +0 -0
  139. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_1_0.jpg +0 -0
  140. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_1_1.jpg +0 -0
  141. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_2_0.jpg +0 -0
  142. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_2_1.jpg +0 -0
  143. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_2_2.jpg +0 -0
  144. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_3_0.jpg +0 -0
  145. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_3_1.jpg +0 -0
  146. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_3_2.jpg +0 -0
  147. {robert-2.1.1 → robert-2.1.2}/robert/report/score_w_3_3.jpg +0 -0
  148. {robert-2.1.1 → robert-2.1.2}/robert/report/transpa.png +0 -0
  149. {robert-2.1.1 → robert-2.1.2}/robert/report/y_distrib.png +0 -0
  150. {robert-2.1.1 → robert-2.1.2}/robert/report.py +0 -0
  151. {robert-2.1.1 → robert-2.1.2}/robert/report_utils.py +0 -0
  152. {robert-2.1.1 → robert-2.1.2}/robert/robert.py +0 -0
  153. {robert-2.1.1 → robert-2.1.2}/robert/verify.py +0 -0
  154. {robert-2.1.1 → robert-2.1.2}/robert.egg-info/SOURCES.txt +0 -0
  155. {robert-2.1.1 → robert-2.1.2}/robert.egg-info/dependency_links.txt +0 -0
  156. {robert-2.1.1 → robert-2.1.2}/robert.egg-info/entry_points.txt +0 -0
  157. {robert-2.1.1 → robert-2.1.2}/robert.egg-info/requires.txt +0 -0
  158. {robert-2.1.1 → robert-2.1.2}/robert.egg-info/top_level.txt +0 -0
  159. {robert-2.1.1 → robert-2.1.2}/tests/test_2generate.py +0 -0
  160. {robert-2.1.1 → robert-2.1.2}/tests/test_3verify.py +0 -0
  161. {robert-2.1.1 → robert-2.1.2}/tests/test_4predict.py +0 -0
  162. {robert-2.1.1 → robert-2.1.2}/tests/test_5aqme_n_full.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robert
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Refiner and Optimizer of a Bunch of Existing Regression Tools
5
5
  Home-page: https://github.com/jvalegre/robert
6
- Download-URL: https://github.com/jvalegre/robert/archive/refs/tags/2.1.1.tar.gz
6
+ Download-URL: https://github.com/jvalegre/robert/archive/refs/tags/2.1.2.tar.gz
7
7
  Author: Juan V. Alegre Requena, David Dalmau
8
8
  Author-email: jv.alegre@csic.es
9
9
  License: MIT
@@ -514,6 +514,11 @@ class RobertModel(BaseEstimator):
514
514
  umode = "cv_sd"
515
515
  else:
516
516
  umode = False
517
+ if umode in ("conformal", "both") and self.problem_type != "reg":
518
+ raise ValueError(
519
+ "return_uncertainty='conformal' or 'both' is only supported "
520
+ "for problem_type='reg'."
521
+ )
517
522
 
518
523
  descriptors = list(self.model_data_["X_descriptors"])
519
524
  y_target = str(self.model_data_["y"])
@@ -559,6 +564,7 @@ class RobertModel(BaseEstimator):
559
564
  base["csv_test"] = pred_name
560
565
  base["params_dir"] = "GENERATE/Best_model"
561
566
  base["names"] = self.names_col_
567
+ base["_api_predict"] = True
562
568
 
563
569
  with _noninteractive_mpl(), _chdir(workdir):
564
570
  predict_module(**base)
@@ -608,11 +614,6 @@ class RobertModel(BaseEstimator):
608
614
  raise RuntimeError(f"Column {sd_col!r} missing in {csv_path}")
609
615
  y_sd = ordered[sd_col].to_numpy(dtype=float)
610
616
  if umode in ("conformal", "both"):
611
- if self.problem_type != "reg":
612
- raise ValueError(
613
- "return_uncertainty='conformal' or 'both' is only supported "
614
- "for problem_type='reg'."
615
- )
616
617
  if hw_col not in result_df.columns:
617
618
  raise RuntimeError(f"Column {hw_col!r} missing in {csv_path}")
618
619
  y_hw = ordered[hw_col].to_numpy(dtype=float)
@@ -637,9 +638,10 @@ class RobertModel(BaseEstimator):
637
638
  y_hat = np.asarray(self.predict(X, return_std=False)).ravel()
638
639
  if self.problem_type == "reg":
639
640
  return float(r2_score(y_true, y_hat))
640
- return float(
641
- accuracy_score(
642
- np.round(y_true).astype(int),
643
- np.round(y_hat).astype(int),
644
- )
645
- )
641
+ try:
642
+ y_true_score = np.round(y_true).astype(int)
643
+ y_hat_score = np.round(y_hat).astype(int)
644
+ except (TypeError, ValueError):
645
+ y_true_score = y_true.astype(str)
646
+ y_hat_score = y_hat.astype(str)
647
+ return float(accuracy_score(y_true_score, y_hat_score))
@@ -127,7 +127,8 @@ class aqme:
127
127
  aqme_indv_name = f'AQME_indiv_{smi_suffix}'
128
128
 
129
129
  # run AQME-QDESCP to generate descriptors
130
- cmd_qdescp = ['python','-u', '-m', 'aqme', '--qdescp', '--input', f'{aqme_indv_name}.csv', '--program', 'xtb', '--csv_name', f'{aqme_indv_name}.csv', '--nprocs', f'{self.args.nprocs}', '--robert']
130
+ python_executable = sys.executable or 'python'
131
+ cmd_qdescp = [python_executable,'-u', '-m', 'aqme', '--qdescp', '--input', f'{aqme_indv_name}.csv', '--program', 'xtb', '--csv_name', f'{aqme_indv_name}.csv', '--nprocs', f'{self.args.nprocs}', '--robert']
131
132
  _ = self.run_aqme(cmd_qdescp, self.args.qdescp_keywords)
132
133
 
133
134
  if smi_suffix is not None:
@@ -268,4 +269,4 @@ def move_aqme():
268
269
  shutil.rmtree(f'AQME/{file}')
269
270
  else:
270
271
  os.remove(f'AQME/{file}')
271
- shutil.move(file, f'AQME/{file}')
272
+ shutil.move(file, f'AQME/{file}')
@@ -61,7 +61,8 @@ var_dict = {
61
61
  "descp_lvl": "interpret",
62
62
  "report_modules" : ['AQME','CURATE','GENERATE','VERIFY','PREDICT'],
63
63
  "debug_report": False,
64
- # Split conformal (regression): symmetric interval half-width.
64
+ # Internal API/BO uncertainty options. Normal ROBERT workflows leave them unused.
65
+ "_api_predict": False,
65
66
  "conformal_enable": True,
66
67
  "conformal_calib_frac": 0.15,
67
68
  "conformal_coverage": 0.9,
@@ -61,6 +61,7 @@ Parameters
61
61
 
62
62
  import time
63
63
  import os
64
+ import json
64
65
  import pandas as pd
65
66
  from robert.utils import (load_variables, finish_print, load_database, pearson_map,
66
67
  check_clas_problem, categorical_transform, correlation_filter)
@@ -203,6 +204,8 @@ class curate:
203
204
  options_df['csv_name'] = [csv_curate_name_general]
204
205
 
205
206
  # Save class label mapping if it exists (for classification with string labels)
207
+ if hasattr(self.args, 'class_mapping_reverse'):
208
+ options_df['class_mapping_reverse'] = [json.dumps(self.args.class_mapping_reverse)]
206
209
  if hasattr(self.args, 'class_0_label'):
207
210
  options_df['class_0_label'] = [self.args.class_0_label]
208
211
  options_df['class_1_label'] = [self.args.class_1_label]
@@ -124,8 +124,8 @@ class generate:
124
124
  # load database, discard user-defined descriptors and perform data checks
125
125
  csv_df, _, _ = load_database(self,self.args.csv_name,"generate")
126
126
 
127
- # changes type to classification if there are only two different y values
128
- if self.args.type.lower() == 'reg' and self.args.auto_type:
127
+ # adjust classification labels and auto-detect binary classification
128
+ if self.args.type.lower() == 'clas' or (self.args.type.lower() == 'reg' and self.args.auto_type):
129
129
  self = check_clas_problem(self,csv_df)
130
130
 
131
131
  # scan different ML models
@@ -171,7 +171,9 @@ class generate:
171
171
 
172
172
  # load database, discard user-defined descriptors and perform data checks
173
173
  csv_df, csv_X, csv_y = load_database(self,csv_to_load,"generate",print_info=False)
174
-
174
+ if self.args.type.lower() == 'clas':
175
+ self = check_clas_problem(self,csv_df)
176
+ csv_y = csv_df[self.args.y]
175
177
 
176
178
  # standardizes and separates an external test set
177
179
  Xy_data = prepare_sets(self,csv_df,csv_X,csv_y,None,self.args.names,None,None,None,BO_opt=True)
@@ -183,6 +185,9 @@ class generate:
183
185
  if self.args.pfi_filter:
184
186
  # load database, discard user-defined descriptors and perform data checks
185
187
  csv_df, csv_X, csv_y = load_database(self,csv_to_load,"generate",print_info=False)
188
+ if self.args.type.lower() == 'clas':
189
+ self = check_clas_problem(self,csv_df)
190
+ csv_y = csv_df[self.args.y]
186
191
 
187
192
  # standardizes and separates an external test set
188
193
  Xy_data = prepare_sets(self,csv_df,csv_X,csv_y,None,self.args.names,None,None,None,BO_opt=True)
@@ -60,6 +60,8 @@ def BO_workflow(self, Xy_data, csv_df, ML_model):
60
60
  bo_data_to_save['X_descriptors'] = json.dumps(bo_data_to_save['X_descriptors'])
61
61
 
62
62
  # Save class label mapping if it exists (for classification with string labels)
63
+ if hasattr(self.args, 'class_mapping_reverse'):
64
+ bo_data_to_save['class_mapping_reverse'] = json.dumps(self.args.class_mapping_reverse)
63
65
  if hasattr(self.args, 'class_0_label'):
64
66
  bo_data_to_save['class_0_label'] = self.args.class_0_label
65
67
  bo_data_to_save['class_1_label'] = self.args.class_1_label
@@ -152,6 +154,8 @@ def save_pfi_csv(self,csv_df,name_csv_hyperopt,PFI_dict,Xy_data_PFI,ML_model):
152
154
  path_csv_PFI = self.args.destination.joinpath(f'{name_csv_hyperopt_PFI}_PFI')
153
155
 
154
156
  # Save class label mapping if it exists (for classification with string labels)
157
+ if hasattr(self.args, 'class_mapping_reverse'):
158
+ PFI_dict['class_mapping_reverse'] = json.dumps(self.args.class_mapping_reverse)
155
159
  if hasattr(self.args, 'class_0_label'):
156
160
  PFI_dict['class_0_label'] = self.args.class_0_label
157
161
  PFI_dict['class_1_label'] = self.args.class_1_label
@@ -37,19 +37,52 @@ configure_environment()
37
37
 
38
38
  from PySide6.QtCore import Qt, QCoreApplication
39
39
  from PySide6.QtWidgets import QApplication
40
- from PySide6.QtGui import QPalette
40
+ from PySide6.QtGui import QIcon, QPalette
41
+
42
+ APP_NAME = "EasyRob"
43
+ APP_DISPLAY_NAME = "EasyRob"
44
+ APP_ORGANIZATION = "The Alegre Group"
45
+ APP_DESKTOP_FILE_NAME = "easyrob"
46
+ APP_USER_MODEL_ID = "com.thealegregroup.easyrob"
47
+
48
+
49
+ def configure_process_identity():
50
+ """Configure process-level identity used by desktop shells."""
51
+ if sys.platform != "win32":
52
+ return
53
+
54
+ try:
55
+ import ctypes
56
+
57
+ ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
58
+ APP_USER_MODEL_ID
59
+ )
60
+ except Exception:
61
+ pass
41
62
 
42
63
  def main():
43
64
  """Main entry point for the EasyROB application."""
65
+ configure_process_identity()
44
66
  QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL)
45
67
 
46
68
  app = QApplication(sys.argv)
69
+ app.setApplicationName(APP_NAME)
70
+ app.setApplicationDisplayName(APP_DISPLAY_NAME)
71
+ app.setOrganizationName(APP_ORGANIZATION)
72
+ app.setDesktopFileName(APP_DESKTOP_FILE_NAME)
47
73
 
48
74
  # Import AFTER QApplication to avoid lunching issues
49
75
  try:
50
76
  from easyrob import get_main_window_class
77
+ from utils.utils_gui import AssetLibrary
51
78
  except ImportError:
52
79
  from robert.gui_easyrob.easyrob import get_main_window_class
80
+ from robert.gui_easyrob.utils.utils_gui import AssetLibrary
81
+
82
+ with AssetLibrary.Robert_icon.get_path() as path_icon:
83
+ icon = QIcon(str(path_icon))
84
+ if not icon.isNull():
85
+ app.setWindowIcon(icon)
53
86
 
54
87
  EasyROB = get_main_window_class()
55
88
 
@@ -60,9 +93,11 @@ def main():
60
93
  app.setStyleSheet(f"QLabel {{ color: {text_color}; }}")
61
94
 
62
95
  window = EasyROB()
96
+ if not app.windowIcon().isNull():
97
+ window.setWindowIcon(app.windowIcon())
63
98
  window.show()
64
99
 
65
100
  sys.exit(app.exec())
66
101
 
67
102
  if __name__ == "__main__":
68
- main()
103
+ main()
@@ -196,6 +196,21 @@ class EasyROB(QMainWindow):
196
196
 
197
197
  QApplication.quit()
198
198
 
199
+ def _resolve_python_executable(self):
200
+ """Return the Python interpreter that should be reused for child workflows."""
201
+ python_pointer = sys.executable or "python"
202
+
203
+ if getattr(sys, "frozen", False):
204
+ embedded_env = Path.cwd() / "_internal" / "robert_env"
205
+ if sys.platform == "win32":
206
+ python_pointer = embedded_env / "python.exe"
207
+ elif sys.platform == "darwin":
208
+ python_pointer = embedded_env / "bin" / "python3"
209
+ else:
210
+ python_pointer = embedded_env / "bin" / "python"
211
+
212
+ return str(python_pointer)
213
+
199
214
  def move_to_selected(self):
200
215
  """Move selected items from available_list to selected_list."""
201
216
  selected_items = self.available_list.selectedItems()
@@ -1461,16 +1476,20 @@ class EasyROB(QMainWindow):
1461
1476
  if not hasattr(self, "tab_widget_aqme"):
1462
1477
  return
1463
1478
 
1464
- if self.csv_test_path:
1465
- unified_smiles = self.tab_widget_aqme.build_unified_smiles_context(
1466
- self.file_path,
1467
- self.csv_test_path
1468
- )
1469
- else:
1470
- unified_smiles = self.tab_widget_aqme.build_unified_smiles_context(
1471
- self.file_path
1472
- )
1473
-
1479
+ try:
1480
+ if self.csv_test_path:
1481
+ unified_smiles = self.tab_widget_aqme.build_unified_smiles_context(
1482
+ self.file_path,
1483
+ self.csv_test_path
1484
+ )
1485
+ else:
1486
+ unified_smiles = self.tab_widget_aqme.build_unified_smiles_context(
1487
+ self.file_path
1488
+ )
1489
+ except ValueError:
1490
+ # Missing SMILES is a valid non-AQME situation; keep the GUI quiet.
1491
+ unified_smiles = []
1492
+
1474
1493
  self.tab_widget_aqme.unified_smiles = unified_smiles
1475
1494
 
1476
1495
  def set_main_chemdraw_path(self, file_path):
@@ -1742,15 +1761,7 @@ class EasyROB(QMainWindow):
1742
1761
  def _build_test_aqme_command(self, original_command=None, qdescp_keywords=None):
1743
1762
  """Builds an AQME command for generating descriptors for the test CSV."""
1744
1763
 
1745
- python_pointer = "python"
1746
- if getattr(sys, "frozen", False):
1747
- env = Path.cwd() / "_internal" / "robert_env"
1748
- if sys.platform == "win32":
1749
- python_pointer = env / "python.exe"
1750
- elif sys.platform == "darwin":
1751
- python_pointer = env / "bin" / "python3"
1752
- else:
1753
- python_pointer = env / "bin" / "python"
1764
+ python_pointer = self._resolve_python_executable()
1754
1765
 
1755
1766
  test_csv = os.path.basename(self.csv_test_path)
1756
1767
 
@@ -1854,8 +1865,9 @@ class EasyROB(QMainWindow):
1854
1865
  if not smarts or not selected_atoms:
1855
1866
  return
1856
1867
 
1857
- os.makedirs(run_dir, exist_ok=True)
1858
- dat_path = os.path.join(run_dir, filename)
1868
+ dat_dir = self._get_atom_mapping_dir(run_dir)
1869
+ os.makedirs(dat_dir, exist_ok=True)
1870
+ dat_path = os.path.join(dat_dir, filename)
1859
1871
 
1860
1872
  try:
1861
1873
  pattern_mol = Chem.MolFromSmarts(smarts)
@@ -1915,6 +1927,58 @@ class EasyROB(QMainWindow):
1915
1927
  self.console_output.append(
1916
1928
  f"<span style='color:orange;'>WARNING: Failed to write atom mapping dat: {e}</span>"
1917
1929
  )
1930
+
1931
+ def _write_atom_mapping_image(
1932
+ self,
1933
+ smarts: str,
1934
+ selected_atoms: list,
1935
+ run_dir: str,
1936
+ filename: str = "AtomMapping_preview.png"
1937
+ ):
1938
+ """Write a numbered PNG preview for the current atom-mapping contract."""
1939
+
1940
+ if not smarts or not selected_atoms:
1941
+ return None
1942
+
1943
+ image_dir = self._get_atom_mapping_dir(run_dir)
1944
+ os.makedirs(image_dir, exist_ok=True)
1945
+ image_path = os.path.join(image_dir, filename)
1946
+
1947
+ try:
1948
+ saved_path = self.tab_widget_aqme.save_atom_mapping_image(
1949
+ image_path,
1950
+ smarts=smarts,
1951
+ selected_atoms=selected_atoms,
1952
+ )
1953
+ return saved_path
1954
+ except Exception as e:
1955
+ self.console_output.append(
1956
+ f"<span style='color:orange;'>WARNING: Failed to write atom mapping image: {e}</span>"
1957
+ )
1958
+ return None
1959
+
1960
+ def _get_atom_mapping_dir(self, run_dir: str) -> str:
1961
+ """Return the dedicated folder used for atom-mapping artifacts."""
1962
+
1963
+ return os.path.join(run_dir, "AtomMapping")
1964
+
1965
+ def _resolve_atom_mapping_dat_path(
1966
+ self,
1967
+ run_dir: str,
1968
+ filename: str = "AtomMapping_data.dat"
1969
+ ) -> str | None:
1970
+ """Resolve the atom-mapping contract path, preferring the dedicated folder."""
1971
+
1972
+ candidate_paths = [
1973
+ os.path.join(self._get_atom_mapping_dir(run_dir), filename),
1974
+ os.path.join(run_dir, filename),
1975
+ ]
1976
+
1977
+ for path in candidate_paths:
1978
+ if os.path.isfile(path):
1979
+ return path
1980
+
1981
+ return None
1918
1982
  def _check_generate_folder(self, run_dir):
1919
1983
  """Checks if a GENERATE folder exists in the run directory."""
1920
1984
 
@@ -2137,6 +2201,11 @@ class EasyROB(QMainWindow):
2137
2201
  selected_atoms=selected_atoms_for_robert,
2138
2202
  run_dir=run_dir
2139
2203
  )
2204
+ self._write_atom_mapping_image(
2205
+ smarts=smarts,
2206
+ selected_atoms=selected_atoms_for_robert,
2207
+ run_dir=run_dir
2208
+ )
2140
2209
 
2141
2210
  # --------------------------------------------------
2142
2211
  # Decide REAL input CSVs for ROBERT
@@ -2182,9 +2251,9 @@ class EasyROB(QMainWindow):
2182
2251
  # -----------------------------------------------
2183
2252
  # Step 1: Detect atomic mapping contract (.dat)
2184
2253
  # -----------------------------------------------
2185
- dat_path = os.path.join(run_dir, "AtomMapping_data.dat")
2254
+ dat_path = self._resolve_atom_mapping_dat_path(run_dir)
2186
2255
 
2187
- if os.path.isfile(dat_path):
2256
+ if dat_path:
2188
2257
 
2189
2258
  self.console_output.append(
2190
2259
  f"[INFO] Atomic mapping contract detected: {dat_path}"
@@ -2404,19 +2473,7 @@ class EasyROB(QMainWindow):
2404
2473
 
2405
2474
  def build_robert_command(self, selected_file_path):
2406
2475
  """Builds the ROBERT command based on GUI selections."""
2407
- python_pointer = "python"
2408
-
2409
- # --------------------------------------------------
2410
- # Detect embedded Python (frozen app)
2411
- # --------------------------------------------------
2412
- if getattr(sys, "frozen", False):
2413
- embedded_env = Path.cwd() / "_internal" / "robert_env"
2414
- if sys.platform == "win32":
2415
- python_pointer = embedded_env / "python.exe"
2416
- elif sys.platform == "darwin":
2417
- python_pointer = embedded_env / "bin" / "python3"
2418
- else:
2419
- python_pointer = embedded_env / "bin" / "python"
2476
+ python_pointer = self._resolve_python_executable()
2420
2477
 
2421
2478
  wf = self.workflow_selector.currentText()
2422
2479
 
@@ -2857,16 +2914,7 @@ class EasyROB(QMainWindow):
2857
2914
  def build_aqme_command(self, selected_file_path, selected_atoms_override=None):
2858
2915
  """Builds the AQME command based on the GUI selections."""
2859
2916
 
2860
- python_pointer = "python"
2861
-
2862
- if getattr(sys, "frozen", False):
2863
- embeded_env = Path.cwd() / "_internal" / "robert_env"
2864
- if sys.platform == "win32":
2865
- python_pointer = embeded_env / "python.exe"
2866
- elif sys.platform == "darwin":
2867
- python_pointer = embeded_env / "bin" / "python3"
2868
- else:
2869
- python_pointer = embeded_env / "bin" / "python"
2917
+ python_pointer = self._resolve_python_executable()
2870
2918
 
2871
2919
  csv_name = os.path.basename(selected_file_path)
2872
2920
 
@@ -3001,6 +3049,11 @@ class EasyROB(QMainWindow):
3001
3049
  selected_atoms=selected_atoms_for_aqme,
3002
3050
  run_dir=run_dir
3003
3051
  )
3052
+ self._write_atom_mapping_image(
3053
+ smarts=smarts,
3054
+ selected_atoms=selected_atoms_for_aqme,
3055
+ run_dir=run_dir
3056
+ )
3004
3057
 
3005
3058
  # ------------------------------------------------
3006
3059
  # Decide REAL input CSVs for AQME
@@ -316,12 +316,14 @@ class AQMETab(QWidget):
316
316
 
317
317
  def _on_mcs_success(self, smarts):
318
318
  """Handle successful MCS detection."""
319
+ self.mcs_worker = None
319
320
  self.smarts_targets.append(smarts)
320
321
  self.mol_info_label.setText("🔬 Info here")
321
322
  self.display_molecule()
322
323
 
323
324
  def _on_mcs_error(self, message):
324
325
  """Handle MCS detection error."""
326
+ self.mcs_worker = None
325
327
  self.set_mol_viewer_message(
326
328
  message,
327
329
  tooltip="SMARTS pattern detection failed."
@@ -330,6 +332,7 @@ class AQMETab(QWidget):
330
332
 
331
333
  def _on_mcs_timeout(self):
332
334
  """Handle MCS detection timeout."""
335
+ self.mcs_worker = None
333
336
  self.set_mol_viewer_message(
334
337
  "⏱️ Timeout: MCS (Maximum Common Substructure) took too long and was aborted.",
335
338
  tooltip="SMARTS pattern detection failed."
@@ -463,6 +466,21 @@ class AQMETab(QWidget):
463
466
  # -------------------------------
464
467
  # Launch MCS worker
465
468
  # -------------------------------
469
+ existing_worker = getattr(self, "mcs_worker", None)
470
+ if existing_worker is not None:
471
+ try:
472
+ existing_worker.finished.disconnect(self._on_mcs_success)
473
+ except Exception:
474
+ pass
475
+ try:
476
+ existing_worker.error.disconnect(self._on_mcs_error)
477
+ except Exception:
478
+ pass
479
+ try:
480
+ existing_worker.timeout.disconnect(self._on_mcs_timeout)
481
+ except Exception:
482
+ pass
483
+
466
484
  self.mcs_worker = MCSProcessWorker(
467
485
  smiles_list,
468
486
  timeout_ms=60000
@@ -554,30 +572,16 @@ class AQMETab(QWidget):
554
572
  else:
555
573
  highlight_atoms = set(self.selected_atoms)
556
574
 
557
- highlight_colors = (
558
- {idx: (0.698, 0.4, 1.0) for idx in highlight_atoms}
559
- if highlight_atoms else {}
560
- )
561
-
562
- drawer = rdMolDraw2D.MolDraw2DCairo(
563
- self.molecule_image_width,
564
- self.molecule_image_height
565
- )
566
- drawer.drawOptions().bondLineWidth = 1.5
567
- drawer.DrawMolecule(
575
+ png_bytes, atom_coords = self._draw_pattern_molecule(
568
576
  self.mol,
569
- highlightAtoms=list(highlight_atoms),
570
- highlightAtomColors=highlight_colors
577
+ highlight_atoms=highlight_atoms,
578
+ selected_atoms=self.selected_atoms,
579
+ width=self.molecule_image_width,
580
+ height=self.molecule_image_height,
571
581
  )
572
- drawer.FinishDrawing()
573
-
574
- png_bytes = drawer.GetDrawingText()
575
582
  pixmap = QPixmap()
576
583
  pixmap.loadFromData(png_bytes)
577
- self.atom_coords = [
578
- drawer.GetDrawCoords(i)
579
- for i in range(self.mol.GetNumAtoms())
580
- ]
584
+ self.atom_coords = atom_coords
581
585
 
582
586
  if self.mol_viewer:
583
587
  if pixmap.isNull():
@@ -618,6 +622,100 @@ class AQMETab(QWidget):
618
622
  )
619
623
  self.mol_info_label.setText("🔬 Info here")
620
624
 
625
+ def _prepare_pattern_molecule_for_drawing(self, pattern_mol, selected_atoms=None):
626
+ """Return a copy of the SMARTS pattern with visible atom-order labels."""
627
+
628
+ mol_for_draw = Chem.Mol(pattern_mol)
629
+ selected_atoms = list(selected_atoms or [])
630
+
631
+ for atom in mol_for_draw.GetAtoms():
632
+ if atom.HasProp("atomNote"):
633
+ atom.ClearProp("atomNote")
634
+
635
+ for order, atom_idx in enumerate(selected_atoms, start=1):
636
+ if 0 <= atom_idx < mol_for_draw.GetNumAtoms():
637
+ mol_for_draw.GetAtomWithIdx(atom_idx).SetProp(
638
+ "atomNote",
639
+ str(order)
640
+ )
641
+
642
+ return mol_for_draw
643
+
644
+ def _draw_pattern_molecule(
645
+ self,
646
+ pattern_mol,
647
+ highlight_atoms,
648
+ selected_atoms,
649
+ width,
650
+ height,
651
+ ):
652
+ """Draw the SMARTS pattern with highlight and click-order numbering."""
653
+
654
+ highlight_atoms = set(highlight_atoms or [])
655
+ highlight_colors = (
656
+ {idx: (0.698, 0.4, 1.0) for idx in highlight_atoms}
657
+ if highlight_atoms else {}
658
+ )
659
+
660
+ draw_mol = self._prepare_pattern_molecule_for_drawing(
661
+ pattern_mol,
662
+ selected_atoms=selected_atoms,
663
+ )
664
+
665
+ drawer = rdMolDraw2D.MolDraw2DCairo(
666
+ max(1, int(width)),
667
+ max(1, int(height))
668
+ )
669
+ options = drawer.drawOptions()
670
+ options.bondLineWidth = 1.5
671
+ options.annotationFontScale = 1.0
672
+ drawer.DrawMolecule(
673
+ draw_mol,
674
+ highlightAtoms=list(highlight_atoms),
675
+ highlightAtomColors=highlight_colors
676
+ )
677
+ drawer.FinishDrawing()
678
+
679
+ png_bytes = drawer.GetDrawingText()
680
+ atom_coords = [
681
+ drawer.GetDrawCoords(i)
682
+ for i in range(draw_mol.GetNumAtoms())
683
+ ]
684
+ return png_bytes, atom_coords
685
+
686
+ def save_atom_mapping_image(
687
+ self,
688
+ output_path,
689
+ smarts=None,
690
+ selected_atoms=None,
691
+ width=900,
692
+ height=700,
693
+ ):
694
+ """Save the numbered SMARTS render used for atom mapping as a PNG."""
695
+
696
+ smarts = smarts or (self.smarts_targets[0] if self.smarts_targets else None)
697
+ selected_atoms = list(self.selected_atoms if selected_atoms is None else selected_atoms)
698
+
699
+ if not smarts or not selected_atoms:
700
+ return None
701
+
702
+ pattern_mol = Chem.MolFromSmarts(smarts)
703
+ if pattern_mol is None:
704
+ raise ValueError("Invalid SMARTS pattern")
705
+
706
+ png_bytes, _ = self._draw_pattern_molecule(
707
+ pattern_mol,
708
+ highlight_atoms=set(selected_atoms),
709
+ selected_atoms=selected_atoms,
710
+ width=width,
711
+ height=height,
712
+ )
713
+
714
+ with open(output_path, "wb") as f:
715
+ f.write(png_bytes)
716
+
717
+ return output_path
718
+
621
719
  def handle_atom_selection(self, atom_idx):
622
720
  """Handle the selection of an atom in the pattern."""
623
721
 
@@ -1102,4 +1200,4 @@ class AQMETab(QWidget):
1102
1200
  save_button.clicked.connect(save_to_csv)
1103
1201
 
1104
1202
  dialog.setLayout(layout)
1105
- dialog.exec()
1203
+ dialog.exec()