ins-pricing 0.5.0__py3-none-any.whl → 0.5.3__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 (44) hide show
  1. ins_pricing/cli/BayesOpt_entry.py +15 -5
  2. ins_pricing/cli/BayesOpt_incremental.py +43 -10
  3. ins_pricing/cli/Explain_Run.py +16 -5
  4. ins_pricing/cli/Explain_entry.py +29 -8
  5. ins_pricing/cli/Pricing_Run.py +16 -5
  6. ins_pricing/cli/bayesopt_entry_runner.py +45 -12
  7. ins_pricing/cli/utils/bootstrap.py +23 -0
  8. ins_pricing/cli/utils/cli_config.py +34 -15
  9. ins_pricing/cli/utils/import_resolver.py +14 -14
  10. ins_pricing/cli/utils/notebook_utils.py +120 -106
  11. ins_pricing/cli/watchdog_run.py +15 -5
  12. ins_pricing/frontend/app.py +132 -61
  13. ins_pricing/frontend/config_builder.py +33 -0
  14. ins_pricing/frontend/example_config.json +11 -0
  15. ins_pricing/frontend/runner.py +340 -388
  16. ins_pricing/modelling/README.md +1 -1
  17. ins_pricing/modelling/__init__.py +10 -10
  18. ins_pricing/modelling/bayesopt/README.md +29 -11
  19. ins_pricing/modelling/bayesopt/config_components.py +12 -0
  20. ins_pricing/modelling/bayesopt/config_preprocess.py +50 -13
  21. ins_pricing/modelling/bayesopt/core.py +47 -19
  22. ins_pricing/modelling/bayesopt/model_plotting_mixin.py +20 -14
  23. ins_pricing/modelling/bayesopt/models/model_ft_components.py +349 -342
  24. ins_pricing/modelling/bayesopt/models/model_ft_trainer.py +11 -5
  25. ins_pricing/modelling/bayesopt/models/model_gnn.py +20 -14
  26. ins_pricing/modelling/bayesopt/models/model_resn.py +9 -3
  27. ins_pricing/modelling/bayesopt/trainers/trainer_base.py +62 -50
  28. ins_pricing/modelling/bayesopt/trainers/trainer_ft.py +61 -53
  29. ins_pricing/modelling/bayesopt/trainers/trainer_glm.py +9 -3
  30. ins_pricing/modelling/bayesopt/trainers/trainer_gnn.py +40 -32
  31. ins_pricing/modelling/bayesopt/trainers/trainer_resn.py +36 -24
  32. ins_pricing/modelling/bayesopt/trainers/trainer_xgb.py +240 -37
  33. ins_pricing/modelling/bayesopt/utils/distributed_utils.py +193 -186
  34. ins_pricing/modelling/bayesopt/utils/torch_trainer_mixin.py +23 -10
  35. ins_pricing/pricing/factors.py +67 -56
  36. ins_pricing/setup.py +1 -1
  37. ins_pricing/utils/__init__.py +7 -6
  38. ins_pricing/utils/device.py +45 -24
  39. ins_pricing/utils/logging.py +34 -1
  40. ins_pricing/utils/profiling.py +8 -4
  41. {ins_pricing-0.5.0.dist-info → ins_pricing-0.5.3.dist-info}/METADATA +182 -182
  42. {ins_pricing-0.5.0.dist-info → ins_pricing-0.5.3.dist-info}/RECORD +44 -43
  43. {ins_pricing-0.5.0.dist-info → ins_pricing-0.5.3.dist-info}/WHEEL +0 -0
  44. {ins_pricing-0.5.0.dist-info → ins_pricing-0.5.3.dist-info}/top_level.txt +0 -0
@@ -17,9 +17,9 @@ Example:
17
17
  from __future__ import annotations
18
18
 
19
19
  # =============================================================================
20
- # Logging utilities
21
- # =============================================================================
22
- from ins_pricing.utils.logging import get_logger, configure_logging
20
+ # Logging utilities
21
+ # =============================================================================
22
+ from ins_pricing.utils.logging import get_logger, configure_logging, log_print
23
23
 
24
24
  # =============================================================================
25
25
  # Metric utilities (PSI, model evaluation)
@@ -78,9 +78,10 @@ from ins_pricing.utils.device import (
78
78
  )
79
79
 
80
80
  __all__ = [
81
- # Logging
82
- "get_logger",
83
- "configure_logging",
81
+ # Logging
82
+ "get_logger",
83
+ "configure_logging",
84
+ "log_print",
84
85
  # Metrics
85
86
  "psi_numeric",
86
87
  "psi_categorical",
@@ -58,32 +58,53 @@ class GPUMemoryManager:
58
58
  _logger = get_logger("ins_pricing.gpu")
59
59
 
60
60
  @classmethod
61
- def clean(cls, verbose: bool = False) -> None:
62
- """Clean up GPU memory.
63
-
64
- Args:
65
- verbose: If True, log cleanup details
66
- """
67
- gc.collect()
68
-
69
- if TORCH_AVAILABLE and torch.cuda.is_available():
70
- torch.cuda.empty_cache()
71
- torch.cuda.synchronize()
72
- if verbose:
73
- cls._logger.debug("CUDA cache cleared and synchronized")
74
-
75
- # Optional: Force IPC collect for multi-process scenarios
76
- if os.environ.get("BAYESOPT_CUDA_IPC_COLLECT", "0") == "1":
77
- try:
78
- torch.cuda.ipc_collect()
79
- if verbose:
80
- cls._logger.debug("CUDA IPC collect performed")
81
- except Exception:
82
- pass
61
+ def clean(
62
+ cls,
63
+ verbose: bool = False,
64
+ *,
65
+ synchronize: bool = True,
66
+ empty_cache: bool = True,
67
+ ) -> None:
68
+ """Clean up GPU memory.
69
+
70
+ Args:
71
+ verbose: If True, log cleanup details
72
+ synchronize: If True, synchronize CUDA device after cleanup
73
+ empty_cache: If True, clear CUDA cache
74
+ """
75
+ gc.collect()
76
+
77
+ if TORCH_AVAILABLE and torch.cuda.is_available():
78
+ if empty_cache:
79
+ torch.cuda.empty_cache()
80
+ if synchronize:
81
+ torch.cuda.synchronize()
82
+ if verbose:
83
+ if empty_cache and synchronize:
84
+ cls._logger.debug("CUDA cache cleared and synchronized")
85
+ elif empty_cache:
86
+ cls._logger.debug("CUDA cache cleared")
87
+ elif synchronize:
88
+ cls._logger.debug("CUDA synchronized")
89
+
90
+ # Optional: Force IPC collect for multi-process scenarios
91
+ if os.environ.get("BAYESOPT_CUDA_IPC_COLLECT", "0") == "1":
92
+ try:
93
+ torch.cuda.ipc_collect()
94
+ if verbose:
95
+ cls._logger.debug("CUDA IPC collect performed")
96
+ except Exception:
97
+ pass
83
98
 
84
99
  @classmethod
85
100
  @contextmanager
86
- def cleanup_context(cls, verbose: bool = False):
101
+ def cleanup_context(
102
+ cls,
103
+ verbose: bool = False,
104
+ *,
105
+ synchronize: bool = True,
106
+ empty_cache: bool = True,
107
+ ):
87
108
  """Context manager that cleans GPU memory on exit.
88
109
 
89
110
  Args:
@@ -95,7 +116,7 @@ class GPUMemoryManager:
95
116
  try:
96
117
  yield
97
118
  finally:
98
- cls.clean(verbose=verbose)
119
+ cls.clean(verbose=verbose, synchronize=synchronize, empty_cache=empty_cache)
99
120
 
100
121
  @classmethod
101
122
  def move_model_to_cpu(cls, model: Any) -> Any:
@@ -18,7 +18,7 @@ from __future__ import annotations
18
18
  import logging
19
19
  import os
20
20
  from functools import lru_cache
21
- from typing import Optional
21
+ from typing import Optional, Union
22
22
 
23
23
 
24
24
  @lru_cache(maxsize=1)
@@ -72,3 +72,36 @@ def configure_logging(
72
72
  formatter = logging.Formatter(format_string)
73
73
  for handler in logger.handlers:
74
74
  handler.setFormatter(formatter)
75
+
76
+
77
+ def log_print(
78
+ logger: logging.Logger,
79
+ *args,
80
+ level: Optional[Union[int, str]] = None,
81
+ **kwargs,
82
+ ) -> None:
83
+ """Print-like helper that routes messages to a logger.
84
+
85
+ This preserves basic print semantics (sep/end) while ignoring file/flush,
86
+ and it auto-detects severity when level is not provided.
87
+ """
88
+ sep = kwargs.get("sep", " ")
89
+ msg = sep.join(str(arg) for arg in args)
90
+ if not msg:
91
+ return
92
+
93
+ if level is None:
94
+ lowered = msg.lstrip().lower()
95
+ if lowered.startswith(("warn", "[warn]", "warning")):
96
+ level_value = logging.WARNING
97
+ elif lowered.startswith(("error", "[error]", "err")):
98
+ level_value = logging.ERROR
99
+ else:
100
+ level_value = logging.INFO
101
+ else:
102
+ if isinstance(level, str):
103
+ level_value = getattr(logging, level.upper(), logging.INFO)
104
+ else:
105
+ level_value = int(level)
106
+
107
+ logger.log(level_value, msg)
@@ -18,6 +18,10 @@ import time
18
18
  from contextlib import contextmanager
19
19
  from typing import Optional
20
20
 
21
+ from ins_pricing.utils import get_logger
22
+
23
+ _logger = get_logger("ins_pricing.utils.profiling")
24
+
21
25
  try:
22
26
  import psutil
23
27
  HAS_PSUTIL = True
@@ -96,7 +100,7 @@ def profile_section(
96
100
  if logger:
97
101
  logger.log(log_level, msg)
98
102
  else:
99
- print(msg)
103
+ _logger.log(log_level, msg)
100
104
 
101
105
 
102
106
  def get_memory_info() -> dict:
@@ -250,7 +254,7 @@ def cleanup_memory(logger: Optional[logging.Logger] = None) -> None:
250
254
  if logger:
251
255
  logger.info(msg)
252
256
  else:
253
- print(msg)
257
+ _logger.info(msg)
254
258
 
255
259
 
256
260
  class MemoryMonitor:
@@ -301,7 +305,7 @@ class MemoryMonitor:
301
305
  if self.logger:
302
306
  self.logger.info(msg)
303
307
  else:
304
- print(msg)
308
+ _logger.info(msg)
305
309
 
306
310
  return self
307
311
 
@@ -323,7 +327,7 @@ class MemoryMonitor:
323
327
  if self.logger:
324
328
  self.logger.info(msg)
325
329
  else:
326
- print(msg)
330
+ _logger.info(msg)
327
331
 
328
332
  # Check threshold and cleanup if needed
329
333
  if self.threshold_gb is not None:
@@ -1,182 +1,182 @@
1
- Metadata-Version: 2.4
2
- Name: ins_pricing
3
- Version: 0.5.0
4
- Summary: Reusable modelling, pricing, governance, and reporting utilities.
5
- Author: meishi125478
6
- License: Proprietary
7
- Keywords: pricing,insurance,bayesopt,ml
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3 :: Only
10
- Classifier: Programming Language :: Python :: 3.9
11
- Classifier: License :: Other/Proprietary License
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Intended Audience :: Developers
14
- Requires-Python: >=3.9
15
- Description-Content-Type: text/markdown
16
- Requires-Dist: numpy>=1.20
17
- Requires-Dist: pandas>=1.4
18
- Provides-Extra: bayesopt
19
- Requires-Dist: torch>=1.13; extra == "bayesopt"
20
- Requires-Dist: optuna>=3.0; extra == "bayesopt"
21
- Requires-Dist: xgboost>=1.6; extra == "bayesopt"
22
- Requires-Dist: scikit-learn>=1.1; extra == "bayesopt"
23
- Requires-Dist: statsmodels>=0.13; extra == "bayesopt"
24
- Requires-Dist: joblib>=1.2; extra == "bayesopt"
25
- Requires-Dist: matplotlib>=3.5; extra == "bayesopt"
26
- Provides-Extra: plotting
27
- Requires-Dist: matplotlib>=3.5; extra == "plotting"
28
- Requires-Dist: scikit-learn>=1.1; extra == "plotting"
29
- Provides-Extra: explain
30
- Requires-Dist: torch>=1.13; extra == "explain"
31
- Requires-Dist: shap>=0.41; extra == "explain"
32
- Requires-Dist: scikit-learn>=1.1; extra == "explain"
33
- Provides-Extra: geo
34
- Requires-Dist: contextily>=1.3; extra == "geo"
35
- Requires-Dist: matplotlib>=3.5; extra == "geo"
36
- Provides-Extra: gnn
37
- Requires-Dist: torch>=1.13; extra == "gnn"
38
- Requires-Dist: pynndescent>=0.5; extra == "gnn"
39
- Requires-Dist: torch-geometric>=2.3; extra == "gnn"
40
- Provides-Extra: all
41
- Requires-Dist: torch>=1.13; extra == "all"
42
- Requires-Dist: optuna>=3.0; extra == "all"
43
- Requires-Dist: xgboost>=1.6; extra == "all"
44
- Requires-Dist: scikit-learn>=1.1; extra == "all"
45
- Requires-Dist: statsmodels>=0.13; extra == "all"
46
- Requires-Dist: joblib>=1.2; extra == "all"
47
- Requires-Dist: matplotlib>=3.5; extra == "all"
48
- Requires-Dist: shap>=0.41; extra == "all"
49
- Requires-Dist: contextily>=1.3; extra == "all"
50
- Requires-Dist: pynndescent>=0.5; extra == "all"
51
- Requires-Dist: torch-geometric>=2.3; extra == "all"
52
-
53
- # Insurance-Pricing
54
-
55
- A reusable toolkit for insurance modeling, pricing, governance, and reporting.
56
-
57
- ## Overview
58
-
59
- Insurance-Pricing (ins_pricing) is an enterprise-grade Python library designed for machine learning
60
- model training, pricing calculations, and model governance workflows in the insurance industry.
61
-
62
- ### Core Modules
63
-
64
- | Module | Description |
65
- |--------|-------------|
66
- | modelling | ML model training (GLM, XGBoost, ResNet, FT-Transformer, GNN) and model interpretability |
67
- | pricing | Factor table construction, numeric binning, premium calibration, exposure calculation, PSI monitoring |
68
- | production | Model prediction, batch scoring, data drift detection, production metrics monitoring |
69
- | governance | Model registry, version management, approval workflows, audit logging |
70
- | reporting | Report generation (Markdown format), report scheduling |
71
- | utils | Data validation, performance profiling, device management, logging configuration |
72
-
73
- ### Quick Start
74
-
75
- ```python
76
- # Model training with Bayesian optimization
77
- from ins_pricing import bayesopt as ropt
78
-
79
- model = ropt.BayesOptModel(
80
- train_data, test_data,
81
- model_name='my_model',
82
- resp_nme='target',
83
- weight_nme='weight',
84
- factor_nmes=feature_list,
85
- cate_list=categorical_features,
86
- )
87
- model.bayesopt_xgb(max_evals=100) # Train XGBoost
88
- model.bayesopt_resnet(max_evals=50) # Train ResNet
89
- model.bayesopt_ft(max_evals=50) # Train FT-Transformer
90
-
91
- # Pricing: build factor table
92
- from ins_pricing.pricing import build_factor_table
93
- factors = build_factor_table(
94
- df,
95
- factor_col='age_band',
96
- loss_col='claim_amount',
97
- exposure_col='exposure',
98
- )
99
-
100
- # Production: batch scoring
101
- from ins_pricing.production import batch_score
102
- scores = batch_score(model.trainers['xgb'].predict, df)
103
-
104
- # Model governance
105
- from ins_pricing.governance import ModelRegistry
106
- registry = ModelRegistry('models.json')
107
- registry.register(model_name, version, metrics=metrics)
108
- ```
109
-
110
- ### Project Structure
111
-
112
- ```
113
- ins_pricing/
114
- cli/ # Command-line entry points
115
- modelling/
116
- core/bayesopt/ # ML model training core
117
- explain/ # Model interpretability
118
- plotting/ # Model visualization
119
- pricing/ # Insurance pricing module
120
- production/ # Production deployment module
121
- governance/ # Model governance
122
- reporting/ # Report generation
123
- utils/ # Utilities
124
- tests/ # Test suite
125
- ```
126
-
127
- ### Installation
128
-
129
- ```bash
130
- # Basic installation
131
- pip install ins_pricing
132
-
133
- # Full installation (all optional dependencies)
134
- pip install ins_pricing[all]
135
-
136
- # Install specific extras
137
- pip install ins_pricing[bayesopt] # Model training
138
- pip install ins_pricing[explain] # Model explanation
139
- pip install ins_pricing[plotting] # Visualization
140
- pip install ins_pricing[gnn] # Graph neural networks
141
- ```
142
-
143
- #### Multi-platform and GPU notes
144
-
145
- - Install the correct PyTorch build for your platform/GPU before installing extras.
146
- - Torch Geometric requires platform-specific wheels; follow the official PyG install guide.
147
- - Multi-GPU uses torch.distributed/DataParallel where supported; Windows disables CUDA DDP.
148
-
149
- ---
150
- ## PyPI Upload (scripts)
151
-
152
- This repo includes upload scripts for Windows and Linux/macOS.
153
-
154
- ### Windows
155
-
156
- ```cmd
157
- set TWINE_PASSWORD=your_pypi_token_here
158
- python -m build
159
- upload_to_pypi.bat
160
- ```
161
-
162
- ### Linux / macOS
163
-
164
- ```bash
165
- chmod +x upload_to_pypi.sh
166
- export TWINE_PASSWORD='your_pypi_token_here'
167
- python -m build
168
- ./upload_to_pypi.sh
169
- ```
170
-
171
- ### Makefile (if make is available)
172
-
173
- ```bash
174
- make build
175
- make upload
176
- ```
177
-
178
- ### Tips
179
-
180
- - Never commit tokens to version control.
181
- - Use environment variables or secret managers to store credentials.
182
- - Test with TestPyPI before publishing when needed.
1
+ Metadata-Version: 2.4
2
+ Name: ins_pricing
3
+ Version: 0.5.3
4
+ Summary: Reusable modelling, pricing, governance, and reporting utilities.
5
+ Author: meishi125478
6
+ License: Proprietary
7
+ Keywords: pricing,insurance,bayesopt,ml
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: License :: Other/Proprietary License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Intended Audience :: Developers
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: numpy>=1.20
17
+ Requires-Dist: pandas>=1.4
18
+ Provides-Extra: bayesopt
19
+ Requires-Dist: torch>=1.13; extra == "bayesopt"
20
+ Requires-Dist: optuna>=3.0; extra == "bayesopt"
21
+ Requires-Dist: xgboost>=1.6; extra == "bayesopt"
22
+ Requires-Dist: scikit-learn>=1.1; extra == "bayesopt"
23
+ Requires-Dist: statsmodels>=0.13; extra == "bayesopt"
24
+ Requires-Dist: joblib>=1.2; extra == "bayesopt"
25
+ Requires-Dist: matplotlib>=3.5; extra == "bayesopt"
26
+ Provides-Extra: plotting
27
+ Requires-Dist: matplotlib>=3.5; extra == "plotting"
28
+ Requires-Dist: scikit-learn>=1.1; extra == "plotting"
29
+ Provides-Extra: explain
30
+ Requires-Dist: torch>=1.13; extra == "explain"
31
+ Requires-Dist: shap>=0.41; extra == "explain"
32
+ Requires-Dist: scikit-learn>=1.1; extra == "explain"
33
+ Provides-Extra: geo
34
+ Requires-Dist: contextily>=1.3; extra == "geo"
35
+ Requires-Dist: matplotlib>=3.5; extra == "geo"
36
+ Provides-Extra: gnn
37
+ Requires-Dist: torch>=1.13; extra == "gnn"
38
+ Requires-Dist: pynndescent>=0.5; extra == "gnn"
39
+ Requires-Dist: torch-geometric>=2.3; extra == "gnn"
40
+ Provides-Extra: all
41
+ Requires-Dist: torch>=1.13; extra == "all"
42
+ Requires-Dist: optuna>=3.0; extra == "all"
43
+ Requires-Dist: xgboost>=1.6; extra == "all"
44
+ Requires-Dist: scikit-learn>=1.1; extra == "all"
45
+ Requires-Dist: statsmodels>=0.13; extra == "all"
46
+ Requires-Dist: joblib>=1.2; extra == "all"
47
+ Requires-Dist: matplotlib>=3.5; extra == "all"
48
+ Requires-Dist: shap>=0.41; extra == "all"
49
+ Requires-Dist: contextily>=1.3; extra == "all"
50
+ Requires-Dist: pynndescent>=0.5; extra == "all"
51
+ Requires-Dist: torch-geometric>=2.3; extra == "all"
52
+
53
+ # Insurance-Pricing
54
+
55
+ A reusable toolkit for insurance modeling, pricing, governance, and reporting.
56
+
57
+ ## Overview
58
+
59
+ Insurance-Pricing (ins_pricing) is an enterprise-grade Python library designed for machine learning
60
+ model training, pricing calculations, and model governance workflows in the insurance industry.
61
+
62
+ ### Core Modules
63
+
64
+ | Module | Description |
65
+ |--------|-------------|
66
+ | modelling | ML model training (GLM, XGBoost, ResNet, FT-Transformer, GNN) and model interpretability |
67
+ | pricing | Factor table construction, numeric binning, premium calibration, exposure calculation, PSI monitoring |
68
+ | production | Model prediction, batch scoring, data drift detection, production metrics monitoring |
69
+ | governance | Model registry, version management, approval workflows, audit logging |
70
+ | reporting | Report generation (Markdown format), report scheduling |
71
+ | utils | Data validation, performance profiling, device management, logging configuration |
72
+
73
+ ### Quick Start
74
+
75
+ ```python
76
+ # Model training with Bayesian optimization
77
+ from ins_pricing import bayesopt as ropt
78
+
79
+ model = ropt.BayesOptModel(
80
+ train_data, test_data,
81
+ model_name='my_model',
82
+ resp_nme='target',
83
+ weight_nme='weight',
84
+ factor_nmes=feature_list,
85
+ cate_list=categorical_features,
86
+ )
87
+ model.bayesopt_xgb(max_evals=100) # Train XGBoost
88
+ model.bayesopt_resnet(max_evals=50) # Train ResNet
89
+ model.bayesopt_ft(max_evals=50) # Train FT-Transformer
90
+
91
+ # Pricing: build factor table
92
+ from ins_pricing.pricing import build_factor_table
93
+ factors = build_factor_table(
94
+ df,
95
+ factor_col='age_band',
96
+ loss_col='claim_amount',
97
+ exposure_col='exposure',
98
+ )
99
+
100
+ # Production: batch scoring
101
+ from ins_pricing.production import batch_score
102
+ scores = batch_score(model.trainers['xgb'].predict, df)
103
+
104
+ # Model governance
105
+ from ins_pricing.governance import ModelRegistry
106
+ registry = ModelRegistry('models.json')
107
+ registry.register(model_name, version, metrics=metrics)
108
+ ```
109
+
110
+ ### Project Structure
111
+
112
+ ```
113
+ ins_pricing/
114
+ cli/ # Command-line entry points
115
+ modelling/
116
+ core/bayesopt/ # ML model training core
117
+ explain/ # Model interpretability
118
+ plotting/ # Model visualization
119
+ pricing/ # Insurance pricing module
120
+ production/ # Production deployment module
121
+ governance/ # Model governance
122
+ reporting/ # Report generation
123
+ utils/ # Utilities
124
+ tests/ # Test suite
125
+ ```
126
+
127
+ ### Installation
128
+
129
+ ```bash
130
+ # Basic installation
131
+ pip install ins_pricing
132
+
133
+ # Full installation (all optional dependencies)
134
+ pip install ins_pricing[all]
135
+
136
+ # Install specific extras
137
+ pip install ins_pricing[bayesopt] # Model training
138
+ pip install ins_pricing[explain] # Model explanation
139
+ pip install ins_pricing[plotting] # Visualization
140
+ pip install ins_pricing[gnn] # Graph neural networks
141
+ ```
142
+
143
+ #### Multi-platform and GPU notes
144
+
145
+ - Install the correct PyTorch build for your platform/GPU before installing extras.
146
+ - Torch Geometric requires platform-specific wheels; follow the official PyG install guide.
147
+ - Multi-GPU uses torch.distributed/DataParallel where supported; Windows disables CUDA DDP.
148
+
149
+ ---
150
+ ## PyPI Upload (scripts)
151
+
152
+ This repo includes upload scripts for Windows and Linux/macOS.
153
+
154
+ ### Windows
155
+
156
+ ```cmd
157
+ set TWINE_PASSWORD=your_pypi_token_here
158
+ python -m build
159
+ upload_to_pypi.bat
160
+ ```
161
+
162
+ ### Linux / macOS
163
+
164
+ ```bash
165
+ chmod +x upload_to_pypi.sh
166
+ export TWINE_PASSWORD='your_pypi_token_here'
167
+ python -m build
168
+ ./upload_to_pypi.sh
169
+ ```
170
+
171
+ ### Makefile (if make is available)
172
+
173
+ ```bash
174
+ make build
175
+ make upload
176
+ ```
177
+
178
+ ### Tips
179
+
180
+ - Never commit tokens to version control.
181
+ - Use environment variables or secret managers to store credentials.
182
+ - Test with TestPyPI before publishing when needed.