torch-molecule 0.1.0__py3-none-any.whl

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 (148) hide show
  1. torch_molecule/__init__.py +70 -0
  2. torch_molecule/base/__init__.py +8 -0
  3. torch_molecule/base/base.py +396 -0
  4. torch_molecule/base/encoder.py +44 -0
  5. torch_molecule/base/generator.py +24 -0
  6. torch_molecule/base/old_encoder.py +322 -0
  7. torch_molecule/base/old_predictor.py +904 -0
  8. torch_molecule/base/old_predictor2.py +436 -0
  9. torch_molecule/base/predictor.py +112 -0
  10. torch_molecule/encoder/attrmask/__init__.py +3 -0
  11. torch_molecule/encoder/attrmask/model.py +113 -0
  12. torch_molecule/encoder/attrmask/modeling_attrmask.py +304 -0
  13. torch_molecule/encoder/constant.py +13 -0
  14. torch_molecule/encoder/contextpred/__init__.py +3 -0
  15. torch_molecule/encoder/contextpred/model.py +207 -0
  16. torch_molecule/encoder/contextpred/modeling_contextpred.py +299 -0
  17. torch_molecule/encoder/contextpred/utils.py +206 -0
  18. torch_molecule/encoder/edgepred/__init__.py +3 -0
  19. torch_molecule/encoder/edgepred/model.py +104 -0
  20. torch_molecule/encoder/edgepred/modeling_edgepred.py +285 -0
  21. torch_molecule/encoder/graphmae/__init__.py +3 -0
  22. torch_molecule/encoder/graphmae/dataloader.py +206 -0
  23. torch_molecule/encoder/graphmae/model.py +137 -0
  24. torch_molecule/encoder/graphmae/modeling_graphmae.py +323 -0
  25. torch_molecule/encoder/infograph/__init__.py +3 -0
  26. torch_molecule/encoder/infograph/loss_function.py +128 -0
  27. torch_molecule/encoder/infograph/model.py +92 -0
  28. torch_molecule/encoder/infograph/modeling_infograph.py +301 -0
  29. torch_molecule/encoder/infograph/module.py +31 -0
  30. torch_molecule/encoder/moama/__init__.py +3 -0
  31. torch_molecule/encoder/moama/model.py +131 -0
  32. torch_molecule/encoder/moama/modeling_moama.py +307 -0
  33. torch_molecule/encoder/moama/utils.py +143 -0
  34. torch_molecule/encoder/pretrained/__init__.py +3 -0
  35. torch_molecule/encoder/pretrained/modeling_pretrained.py +274 -0
  36. torch_molecule/encoder/supervised/__init__.py +3 -0
  37. torch_molecule/encoder/supervised/model.py +97 -0
  38. torch_molecule/encoder/supervised/modeling_supervised.py +354 -0
  39. torch_molecule/generator/digress/__init__.py +3 -0
  40. torch_molecule/generator/digress/diffusion.py +194 -0
  41. torch_molecule/generator/digress/modeling_digress.py +503 -0
  42. torch_molecule/generator/digress/transformer.py +360 -0
  43. torch_molecule/generator/digress/utils.py +196 -0
  44. torch_molecule/generator/gdss/__init__.py +3 -0
  45. torch_molecule/generator/gdss/layers.py +257 -0
  46. torch_molecule/generator/gdss/model.py +204 -0
  47. torch_molecule/generator/gdss/modeling_gdss.py +474 -0
  48. torch_molecule/generator/gdss/sde.py +334 -0
  49. torch_molecule/generator/gdss/solver.py +227 -0
  50. torch_molecule/generator/gdss/toremove/ScoreNetworkA.py +167 -0
  51. torch_molecule/generator/gdss/toremove/ScoreNetworkX.py +85 -0
  52. torch_molecule/generator/gdss/utils.py +240 -0
  53. torch_molecule/generator/graph_dit/__init__.py +3 -0
  54. torch_molecule/generator/graph_dit/diffusion.py +188 -0
  55. torch_molecule/generator/graph_dit/modeling_graph_dit.py +581 -0
  56. torch_molecule/generator/graph_dit/transformer.py +207 -0
  57. torch_molecule/generator/graph_dit/utils.py +220 -0
  58. torch_molecule/generator/graph_ga/__init__.py +3 -0
  59. torch_molecule/generator/graph_ga/crossover.py +182 -0
  60. torch_molecule/generator/graph_ga/modeling_graph_ga.py +310 -0
  61. torch_molecule/generator/graph_ga/mutate.py +136 -0
  62. torch_molecule/generator/graph_ga/oracle.py +95 -0
  63. torch_molecule/generator/jtvae/__init__.py +3 -0
  64. torch_molecule/generator/jtvae/jtnn/chemutils.py +450 -0
  65. torch_molecule/generator/jtvae/jtnn/datautils.py +127 -0
  66. torch_molecule/generator/jtvae/jtnn/jtmpn.py +140 -0
  67. torch_molecule/generator/jtvae/jtnn/jtnn_dec.py +350 -0
  68. torch_molecule/generator/jtvae/jtnn/jtnn_enc.py +131 -0
  69. torch_molecule/generator/jtvae/jtnn/mol_tree.py +111 -0
  70. torch_molecule/generator/jtvae/jtnn/mpn.py +129 -0
  71. torch_molecule/generator/jtvae/jtnn/nnutils.py +66 -0
  72. torch_molecule/generator/jtvae/jtnn/vocab.py +31 -0
  73. torch_molecule/generator/jtvae/jtnn_vae.py +246 -0
  74. torch_molecule/generator/jtvae/modeling_jtvae.py +247 -0
  75. torch_molecule/generator/lstm/__init__.py +3 -0
  76. torch_molecule/generator/lstm/action_sampler.py +91 -0
  77. torch_molecule/generator/lstm/lstm.py +59 -0
  78. torch_molecule/generator/lstm/modeling_lstm.py +263 -0
  79. torch_molecule/generator/lstm/smiles_char_dict.py +123 -0
  80. torch_molecule/generator/lstm/utils.py +62 -0
  81. torch_molecule/generator/molgpt/__init__.py +3 -0
  82. torch_molecule/generator/molgpt/dataset.py +87 -0
  83. torch_molecule/generator/molgpt/gpt.py +251 -0
  84. torch_molecule/generator/molgpt/modeling_molgpt.py +427 -0
  85. torch_molecule/generator/pretrained/modeling_pretrained.py +1 -0
  86. torch_molecule/nn/__init__.py +16 -0
  87. torch_molecule/nn/attention.py +63 -0
  88. torch_molecule/nn/embedder.py +183 -0
  89. torch_molecule/nn/gnn.py +631 -0
  90. torch_molecule/nn/mlp.py +34 -0
  91. torch_molecule/predictor/bfgnn/__init__.py +3 -0
  92. torch_molecule/predictor/bfgnn/model.py +116 -0
  93. torch_molecule/predictor/bfgnn/modeling_bfgnn.py +81 -0
  94. torch_molecule/predictor/dir/__init__.py +3 -0
  95. torch_molecule/predictor/dir/model.py +235 -0
  96. torch_molecule/predictor/dir/modeling_dir.py +161 -0
  97. torch_molecule/predictor/dir/utils.py +40 -0
  98. torch_molecule/predictor/gnn/__init__.py +3 -0
  99. torch_molecule/predictor/gnn/model.py +114 -0
  100. torch_molecule/predictor/gnn/modeling_gnn.py +706 -0
  101. torch_molecule/predictor/gnn/old_modeling_gnn.py +707 -0
  102. torch_molecule/predictor/grea/__init__.py +3 -0
  103. torch_molecule/predictor/grea/model.py +190 -0
  104. torch_molecule/predictor/grea/modeling_grea.py +107 -0
  105. torch_molecule/predictor/grea/old1_features.py +229 -0
  106. torch_molecule/predictor/grea/old1_modeling_grea.py +493 -0
  107. torch_molecule/predictor/grea/utils.py +97 -0
  108. torch_molecule/predictor/grin/__init__.py +3 -0
  109. torch_molecule/predictor/grin/model.py +116 -0
  110. torch_molecule/predictor/grin/modeling_grin.py +91 -0
  111. torch_molecule/predictor/irm/__init__.py +3 -0
  112. torch_molecule/predictor/irm/model.py +127 -0
  113. torch_molecule/predictor/irm/modeling_irm.py +179 -0
  114. torch_molecule/predictor/lstm/__init__.py +3 -0
  115. torch_molecule/predictor/lstm/model.py +145 -0
  116. torch_molecule/predictor/lstm/modeling_lstm.py +627 -0
  117. torch_molecule/predictor/lstm/token_from_smiles.py +85 -0
  118. torch_molecule/predictor/old_modeling_graph.py +650 -0
  119. torch_molecule/predictor/rpgnn/__init__.py +3 -0
  120. torch_molecule/predictor/rpgnn/model.py +185 -0
  121. torch_molecule/predictor/rpgnn/modeling_rpgnn.py +76 -0
  122. torch_molecule/predictor/sgir/__init__.py +3 -0
  123. torch_molecule/predictor/sgir/modeling_sgir.py +260 -0
  124. torch_molecule/predictor/sgir/strategy.py +266 -0
  125. torch_molecule/predictor/smiles_transformer/__init__.py +3 -0
  126. torch_molecule/predictor/smiles_transformer/model.py +424 -0
  127. torch_molecule/predictor/smiles_transformer/modeling_transformer.py +252 -0
  128. torch_molecule/predictor/ssr/__init__.py +3 -0
  129. torch_molecule/predictor/ssr/model.py +283 -0
  130. torch_molecule/predictor/ssr/modeling_ssr.py +267 -0
  131. torch_molecule/predictor/ssr/utils.py +185 -0
  132. torch_molecule/utils/__init__.py +31 -0
  133. torch_molecule/utils/checker.py +158 -0
  134. torch_molecule/utils/checkpoint.py +294 -0
  135. torch_molecule/utils/format.py +140 -0
  136. torch_molecule/utils/generic/metrics.py +530 -0
  137. torch_molecule/utils/generic/pseudo_tasks.py +11 -0
  138. torch_molecule/utils/generic/weights.py +30 -0
  139. torch_molecule/utils/graph/features.py +178 -0
  140. torch_molecule/utils/graph/graph_from_smiles.py +134 -0
  141. torch_molecule/utils/graph/graph_to_smiles.py +312 -0
  142. torch_molecule/utils/hf.py +232 -0
  143. torch_molecule/utils/search.py +64 -0
  144. torch_molecule-0.1.0.dist-info/METADATA +240 -0
  145. torch_molecule-0.1.0.dist-info/RECORD +148 -0
  146. torch_molecule-0.1.0.dist-info/WHEEL +5 -0
  147. torch_molecule-0.1.0.dist-info/licenses/LICENSE +21 -0
  148. torch_molecule-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,70 @@
1
+ __version__ = "0.1.0"
2
+
3
+ """
4
+ predictor module
5
+ """
6
+ from .predictor.grea import GREAMolecularPredictor
7
+ from .predictor.gnn import GNNMolecularPredictor
8
+ from .predictor.sgir import SGIRMolecularPredictor
9
+ from .predictor.irm import IRMMolecularPredictor
10
+ from .predictor.ssr import SSRMolecularPredictor
11
+ from .predictor.dir import DIRMolecularPredictor
12
+ from .predictor.rpgnn import RPGNNMolecularPredictor
13
+ from .predictor.bfgnn import BFGNNMolecularPredictor
14
+ from .predictor.grin import GRINMolecularPredictor
15
+ from .predictor.lstm import LSTMMolecularPredictor
16
+ from .predictor.smiles_transformer import SMILESTransformerMolecularPredictor
17
+ """
18
+ encoder module
19
+ """
20
+ from .encoder.attrmask import AttrMaskMolecularEncoder
21
+ from .encoder.moama import MoamaMolecularEncoder
22
+ from .encoder.graphmae import GraphMAEMolecularEncoder
23
+ from .encoder.supervised import SupervisedMolecularEncoder
24
+ from .encoder.contextpred import ContextPredMolecularEncoder
25
+ from .encoder.edgepred import EdgePredMolecularEncoder
26
+ from .encoder.infograph import InfoGraphMolecularEncoder
27
+ from .encoder.pretrained import HFPretrainedMolecularEncoder
28
+ """
29
+ generator module
30
+ """
31
+ from .generator.graph_dit import GraphDITMolecularGenerator
32
+ from .generator.digress import DigressMolecularGenerator
33
+ from .generator.gdss import GDSSMolecularGenerator
34
+ from .generator.graph_ga import GraphGAMolecularGenerator
35
+ from .generator.jtvae import JTVAEMolecularGenerator
36
+ from .generator.lstm import LSTMMolecularGenerator
37
+ from .generator.molgpt import MolGPTMolecularGenerator
38
+
39
+ __all__ = [
40
+ # 'BaseMolecularPredictor',
41
+ # predictors
42
+ 'SGIRMolecularPredictor',
43
+ 'GREAMolecularPredictor',
44
+ 'GNNMolecularPredictor',
45
+ 'IRMMolecularPredictor',
46
+ 'SSRMolecularPredictor',
47
+ 'DIRMolecularPredictor',
48
+ 'RPGNNMolecularPredictor',
49
+ 'BFGNNMolecularPredictor',
50
+ 'GRINMolecularPredictor',
51
+ 'LSTMMolecularPredictor',
52
+ 'SMILESTransformerMolecularPredictor',
53
+ # encoders
54
+ 'SupervisedMolecularEncoder',
55
+ 'AttrMaskMolecularEncoder',
56
+ 'MoamaMolecularEncoder',
57
+ 'GraphMAEMolecularEncoder',
58
+ 'ContextPredMolecularEncoder',
59
+ 'EdgePredMolecularEncoder',
60
+ 'InfoGraphMolecularEncoder',
61
+ 'HFPretrainedMolecularEncoder',
62
+ # generators
63
+ 'GraphDITMolecularGenerator',
64
+ 'DigressMolecularGenerator',
65
+ 'GDSSMolecularGenerator',
66
+ 'GraphGAMolecularGenerator',
67
+ 'JTVAEMolecularGenerator',
68
+ 'MolGPTMolecularGenerator',
69
+ 'LSTMMolecularGenerator',
70
+ ]
@@ -0,0 +1,8 @@
1
+ from .predictor import BaseMolecularPredictor
2
+ from .encoder import BaseMolecularEncoder
3
+ from .generator import BaseMolecularGenerator
4
+ __all__ = [
5
+ "BaseMolecularPredictor",
6
+ "BaseMolecularEncoder",
7
+ "BaseMolecularGenerator",
8
+ ]
@@ -0,0 +1,396 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass, field
3
+ from typing import Optional, Dict, List, Type, Any, ClassVar, Union, Tuple, Callable, Literal
4
+ import torch
5
+ import os
6
+ import numpy as np
7
+ from ..utils.checkpoint import LocalCheckpointManager, HuggingFaceCheckpointManager
8
+ from ..utils.checker import MolecularInputChecker
9
+
10
+ @dataclass
11
+ class BaseModel(ABC):
12
+ """Base class for molecular models with shared functionality.
13
+
14
+ This abstract class provides common methods and utilities for molecular models,
15
+ including model initialization, saving/loading, and parameter management.
16
+ """
17
+
18
+ device: Optional[torch.device] = field(default=None)
19
+ model_name: str = field(default="BaseModel")
20
+ model_class: Optional[Type[torch.nn.Module]] = field(default=None, init=False) # used for model initialization
21
+ model: Optional[torch.nn.Module] = field(default=None, init=False) # initialized model
22
+ is_fitted_: bool = field(default=False, init=False)
23
+
24
+ def __post_init__(self):
25
+ """Initialize common device settings after instance creation.
26
+
27
+ Sets the device to CUDA if available, otherwise CPU, when no device is specified.
28
+ Converts string device specifications to torch.device objects.
29
+ """
30
+ if self.device is None:
31
+ self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
32
+ elif isinstance(self.device, str):
33
+ self.device = torch.device(self.device)
34
+
35
+ @abstractmethod
36
+ def _setup_optimizers(self) -> Tuple[torch.optim.Optimizer, Optional[Any]]:
37
+ """Set up optimizers for model training.
38
+
39
+ Returns
40
+ -------
41
+ Tuple[torch.optim.Optimizer, Optional[Any]]
42
+ Tuple containing the primary optimizer and an optional secondary optimizer or scheduler.
43
+ """
44
+ pass
45
+
46
+ @abstractmethod
47
+ def _train_epoch(self, train_loader, optimizer):
48
+ """Train the model for one epoch.
49
+
50
+ Parameters
51
+ ----------
52
+ train_loader : torch.utils.data.DataLoader
53
+ DataLoader containing training batches
54
+ optimizer : torch.optim.Optimizer
55
+ Optimizer to use for parameter updates
56
+
57
+ Returns
58
+ -------
59
+ dict
60
+ Training metrics for the epoch
61
+ """
62
+ pass
63
+
64
+ @abstractmethod
65
+ def _get_model_params(self, checkpoint: Optional[Dict] = None) -> Dict[str, Any]:
66
+ """Get model parameters used for model initialization.
67
+
68
+ Parameters
69
+ ----------
70
+ checkpoint : Optional[Dict], default=None
71
+ Optional dictionary containing model checkpoint data
72
+
73
+ Returns
74
+ -------
75
+ Dict[str, Any]
76
+ Dictionary of parameters to initialize the model
77
+ """
78
+ pass
79
+
80
+ @staticmethod
81
+ def _get_param_names(self) -> List[str]:
82
+ """Get parameter names in the modeling class.
83
+
84
+ Returns
85
+ -------
86
+ List[str]
87
+ List of parameter names that can be configured
88
+ """
89
+ # return ["model_name", "model_class", "is_fitted_"]
90
+ return ["model_name", "is_fitted_"]
91
+
92
+ def get_params(self, deep: bool = True) -> Dict[str, Any]:
93
+ """Get parameters for this estimator.
94
+
95
+ Parameters
96
+ ----------
97
+ deep : bool, default=True
98
+ If True, will return the parameters for this estimator and
99
+ contained subobjects that are estimators.
100
+
101
+ Returns
102
+ -------
103
+ Dict[str, Any]
104
+ Dictionary of parameter names mapped to their values
105
+ """
106
+ out = {}
107
+ for key in self._get_param_names():
108
+ value = getattr(self, key)
109
+ if deep and hasattr(value, "get_params"):
110
+ deep_items = value.get_params().items()
111
+ out.update((key + "__" + k, val) for k, val in deep_items)
112
+ out[key] = value
113
+ return out
114
+
115
+ def set_params(self, **params) -> "BaseModel":
116
+ """Set parameters for this estimator.
117
+
118
+ Parameters
119
+ ----------
120
+ **params
121
+ Parameter names mapped to their values
122
+
123
+ Returns
124
+ -------
125
+ BaseModel
126
+ Self instance for method chaining
127
+
128
+ Raises
129
+ ------
130
+ ValueError
131
+ If an invalid parameter is provided
132
+ """
133
+ valid_params = self.get_params(deep=True)
134
+ for key, value in params.items():
135
+ if key not in valid_params:
136
+ raise ValueError(f"Invalid parameter {key} for model {self}")
137
+ setattr(self, key, value)
138
+ return self
139
+
140
+ def _initialize_model(
141
+ self,
142
+ model_class: Type[torch.nn.Module],
143
+ checkpoint: Optional[Dict] = None
144
+ ) -> torch.nn.Module:
145
+ """Initialize the model with parameters or a checkpoint.
146
+
147
+ Parameters
148
+ ----------
149
+ model_class : Type[torch.nn.Module]
150
+ PyTorch module class to instantiate
151
+ checkpoint : Optional[Dict], default=None
152
+ Optional dictionary containing model checkpoint data
153
+
154
+ Returns
155
+ -------
156
+ torch.nn.Module
157
+ Initialized PyTorch model
158
+ """
159
+ model_params = self._get_model_params(checkpoint)
160
+ self.model = model_class(**model_params)
161
+ self.model = self.model.to(self.device)
162
+
163
+ if checkpoint is not None:
164
+ self.model.load_state_dict(checkpoint["model_state_dict"])
165
+ return self.model
166
+
167
+ def _validate_inputs(
168
+ self, X: List[str], y: Optional[Union[List, np.ndarray]] = None, num_task: int = 0, num_pretask: int = 0, return_rdkit_mol: bool = True
169
+ ) -> Tuple[Union[List[str], List["Chem.Mol"]], Optional[np.ndarray]]:
170
+ """Validate molecular inputs and targets.
171
+
172
+ Parameters
173
+ ----------
174
+ X : List[str]
175
+ List of SMILES strings representing molecules
176
+ y : Optional[Union[List, np.ndarray]], default=None
177
+ Optional target values for supervised learning
178
+ num_task : int, default=0
179
+ Number of prediction tasks
180
+ num_pretask : int, default=0
181
+ Number of pre-training tasks
182
+ return_rdkit_mol : bool, default=True
183
+ Whether to return RDKit Mol objects instead of SMILES
184
+
185
+ Returns
186
+ -------
187
+ Tuple[Union[List[str], List["Chem.Mol"]], Optional[np.ndarray]]
188
+ Tuple of validated inputs and targets
189
+ """
190
+ return MolecularInputChecker.validate_inputs(X, y, num_task, num_pretask, return_rdkit_mol)
191
+
192
+ def save_to_local(self, path: str) -> None:
193
+ """Save model to local disk.
194
+
195
+ Parameters
196
+ ----------
197
+ path : str
198
+ File path to save the model
199
+
200
+ Raises
201
+ ------
202
+ ValueError
203
+ If the model is not fitted
204
+ """
205
+ if not self.is_fitted_:
206
+ raise ValueError("Model must be fitted before saving to local disk.")
207
+ LocalCheckpointManager.save_model_to_local(self, path)
208
+
209
+ def load_from_local(self, path: str) -> None:
210
+ """Load model from local disk.
211
+
212
+ Parameters
213
+ ----------
214
+ path : str
215
+ File path to load the model from
216
+ """
217
+ LocalCheckpointManager.load_model_from_local(self, path)
218
+
219
+ def save_to_hf(
220
+ self,
221
+ repo_id: str,
222
+ task_id: str = "default",
223
+ metadata_dict: Optional[Dict[str, Any]] = None,
224
+ metrics: Optional[Dict[str, float]] = None,
225
+ commit_message: str = "Update model",
226
+ hf_token: Optional[str] = None,
227
+ private: bool = False,
228
+ config_filename: Optional[str] = 'config.json',
229
+ ) -> None:
230
+ """Save model to Hugging Face Hub.
231
+
232
+ Parameters
233
+ ----------
234
+ repo_id : str
235
+ Hugging Face repository ID
236
+ task_id : str, default="default"
237
+ Task identifier for the model
238
+ metadata_dict : Optional[Dict[str, Any]], default=None
239
+ Optional metadata to store with the model
240
+ metrics : Optional[Dict[str, float]], default=None
241
+ Optional performance metrics to store with the model
242
+ commit_message : str, default="Update model"
243
+ Git commit message
244
+ hf_token : Optional[str], default=None
245
+ Hugging Face authentication token
246
+ private : bool, default=False
247
+ Whether the repository should be private
248
+ config_filename : Optional[str], default='config.json'
249
+ Name of the configuration file to save to the repository
250
+ Raises
251
+ ------
252
+ ValueError
253
+ If the model is not fitted
254
+ """
255
+ if not self.is_fitted_:
256
+ raise ValueError("Model must be fitted before saving to Hugging Face Hub.")
257
+ HuggingFaceCheckpointManager.push_to_huggingface(
258
+ model_instance=self,
259
+ repo_id=repo_id,
260
+ task_id=task_id,
261
+ metadata_dict=metadata_dict,
262
+ metrics=metrics,
263
+ commit_message=commit_message,
264
+ token=hf_token,
265
+ private=private,
266
+ config_filename=config_filename,
267
+ )
268
+
269
+ def load_from_hf(self, repo_id: str, local_cache: Optional[str] = None, config_filename: Optional[str] = 'config.json') -> None:
270
+ """Load model from Hugging Face Hub.
271
+
272
+ Parameters
273
+ ----------
274
+ repo_id : str
275
+ Hugging Face repository ID
276
+ local_cache : str, default=None
277
+ Local path to save the model
278
+ config_filename : str, default='config.json'
279
+ Name of the configuration file to load from the repository
280
+ """
281
+ if local_cache is None:
282
+ local_cache = 'model.pt'
283
+ HuggingFaceCheckpointManager.load_model_from_hf(self, repo_id, local_cache, config_filename=config_filename)
284
+
285
+ def save(self, path: Optional[str] = None, repo_id: Optional[str] = None, **kwargs) -> None:
286
+ """Automatic save to either local disk or Hugging Face Hub.
287
+
288
+ Parameters
289
+ ----------
290
+ path : Optional[str], default=None
291
+ File path for local saving (required if repo_id is None)
292
+ repo_id : Optional[str], default=None
293
+ Hugging Face repository ID for remote saving
294
+ **kwargs
295
+ Additional arguments passed to save_to_hf
296
+
297
+ Raises
298
+ ------
299
+ ValueError
300
+ If path is None when repo_id is None
301
+ """
302
+ # if both path and repo_id are None, raise an error
303
+ if path is None and repo_id is None:
304
+ raise ValueError("path must be provided if repo_id is not given.")
305
+
306
+ if repo_id is not None:
307
+ self.save_to_hf(repo_id=repo_id, **kwargs)
308
+
309
+ if path is not None:
310
+ self.save_to_local(path)
311
+
312
+ def load(self, path: Optional[str] = None, repo_id: Optional[str] = None, **kwargs) -> None:
313
+ """Automatic load from either local disk or Hugging Face Hub.
314
+
315
+ Parameters
316
+ ----------
317
+ path : Optional[str], default=None
318
+ File path for local loading.
319
+ repo_id : Optional[str], default=None
320
+ Hugging Face repository ID for remote loading. If path is provided, repo_id is ignored.
321
+ **kwargs
322
+ Additional arguments passed to load_from_hf
323
+
324
+ Raises
325
+ ------
326
+ FileNotFoundError
327
+ If no local file is found and no repo_id is provided
328
+ """
329
+ if path is not None:
330
+ if os.path.exists(path):
331
+ self.load_from_local(path)
332
+ else:
333
+ raise FileNotFoundError(f"No local file found at '{path}'.")
334
+ else:
335
+ if repo_id is None:
336
+ raise ValueError("repo_id must be provided if path is not given.")
337
+ self.load_from_hf(repo_id, **kwargs)
338
+
339
+ def _check_is_fitted(self) -> None:
340
+ """Check if the model is fitted.
341
+
342
+ Raises
343
+ ------
344
+ AttributeError
345
+ If the model is not fitted
346
+ """
347
+ if not self.is_fitted_:
348
+ raise AttributeError("This model is not fitted yet. Call 'fit' before using it.")
349
+
350
+ def __str__(self, N_CHAR_MAX: int = 700) -> str:
351
+ """Return a string representation of the model.
352
+
353
+ Parameters
354
+ ----------
355
+ N_CHAR_MAX : int, default=700
356
+ Maximum number of characters in the string representation
357
+
358
+ Returns
359
+ -------
360
+ str
361
+ String representation of the model
362
+ """
363
+ attributes = {
364
+ name: value
365
+ for name, value in sorted(self.__dict__.items())
366
+ if not name.startswith("_") and not callable(value)
367
+ }
368
+ attributes = {k: v for k, v in attributes.items() if k != "fitting_loss"}
369
+
370
+ def format_value(v):
371
+ """Helper to format values for representation."""
372
+ if isinstance(v, (float, np.float32, np.float64)):
373
+ return f"{v:.3g}"
374
+ elif isinstance(v, (list, tuple, np.ndarray)) and len(v) > 6:
375
+ return f"{v[:3]}...{v[-3:]}"
376
+ elif isinstance(v, str) and len(v) > 50:
377
+ return f"'{v[:25]}...{v[-22:]}'"
378
+ elif isinstance(v, dict) and len(v) > 6:
379
+ return f"{{{', '.join(f'{k}: {v}' for k, v in list(v.items())[:3])}...}}"
380
+ elif isinstance(v, torch.nn.Module):
381
+ return f"{v.__class__.__name__}(...)"
382
+ return repr(v)
383
+
384
+ class_name = self.__class__.__name__
385
+ important_attrs = ["model_name", "is_fitted_", "task_type", "num_task"]
386
+ attributes_str = [f"{attr}={format_value(attributes.pop(attr))}" for attr in important_attrs if attr in attributes]
387
+ attributes_str += [f"{k}={format_value(v)}" for k, v in sorted(attributes.items())]
388
+
389
+ content = ",\n ".join(attributes_str)
390
+ repr_str = f"{class_name}(\n {content}\n)"
391
+
392
+ if len(repr_str) > N_CHAR_MAX:
393
+ repr_str = "\n".join([repr_str[:N_CHAR_MAX//2], "...", repr_str[-N_CHAR_MAX//2:]])
394
+
395
+ return repr_str
396
+
@@ -0,0 +1,44 @@
1
+ from dataclasses import dataclass, field
2
+ from abc import ABC, abstractmethod
3
+ from typing import Optional, ClassVar, Union, List, Dict, Any, Tuple, Callable, Type, Literal
4
+
5
+ import torch
6
+ import numpy as np
7
+ from .base import BaseModel
8
+
9
+ @dataclass
10
+ class BaseMolecularEncoder(BaseModel, ABC):
11
+ """Base class for molecular representation learning."""
12
+
13
+ model_name: str = field(default="BaseMolecularEncoder")
14
+
15
+ @abstractmethod
16
+ def encode(self, X: List[str], return_type: Literal["np", "pt"] = "pt") -> Union[np.ndarray, torch.Tensor]:
17
+ pass
18
+
19
+ @abstractmethod
20
+ def fit(self, X: List[str], y: Optional[np.ndarray] = None) -> "BaseMolecularEncoder":
21
+ pass
22
+
23
+ def _inspect_task_types(self, y: Union[np.ndarray, torch.Tensor], return_type: Literal["pt", "np"] = "pt") -> Union[np.ndarray, torch.Tensor]:
24
+ """Inspect the task types of the target values.
25
+
26
+ Parameters
27
+ ----------
28
+ y : Union[np.ndarray, torch.Tensor]
29
+ Target values: 2D array for multiple tasks
30
+ return_type : Literal["pt", "np"], default="pt"
31
+ Return type of the result
32
+
33
+ Returns
34
+ -------
35
+ result : Union[np.ndarray, torch.Tensor]
36
+ Result of the task types inspection
37
+ """
38
+ if isinstance(y, np.ndarray):
39
+ y = torch.from_numpy(y)
40
+ # 0/1/nan -> binary classification (True), otherwise -> regression (False)
41
+ result = torch.tensor([torch.all(torch.isnan(y[:, i]) | (y[:, i] == 0) | (y[:, i] == 1)) for i in range(y.shape[1])], dtype=torch.bool)
42
+ if return_type == "np":
43
+ return result.numpy()
44
+ return result
@@ -0,0 +1,24 @@
1
+ from dataclasses import dataclass, field
2
+ from abc import ABC, abstractmethod
3
+ from typing import Optional, ClassVar, Union, List, Dict, Any, Tuple, Callable, Type, Literal
4
+
5
+ import torch
6
+ import numpy as np
7
+ from .base import BaseModel
8
+
9
+ @dataclass
10
+ class BaseMolecularGenerator(BaseModel, ABC):
11
+ """Base class for molecular generation."""
12
+
13
+ model_name: str = field(default="BaseMolecularGenerator")
14
+
15
+ @abstractmethod
16
+ def fit(self, X: List[str], y: Optional[np.ndarray] = None) -> "BaseMolecularGenerator":
17
+ pass
18
+
19
+ @abstractmethod
20
+ def generate(self, n_samples: int, **kwargs) -> List[str]:
21
+ """Generate molecular structures.
22
+ """
23
+ pass
24
+