morphml 1.0.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.

Potentially problematic release.


This version of morphml might be problematic. Click here for more details.

Files changed (158) hide show
  1. morphml/__init__.py +14 -0
  2. morphml/api/__init__.py +26 -0
  3. morphml/api/app.py +326 -0
  4. morphml/api/auth.py +193 -0
  5. morphml/api/client.py +338 -0
  6. morphml/api/models.py +132 -0
  7. morphml/api/rate_limit.py +192 -0
  8. morphml/benchmarking/__init__.py +36 -0
  9. morphml/benchmarking/comparison.py +430 -0
  10. morphml/benchmarks/__init__.py +56 -0
  11. morphml/benchmarks/comparator.py +409 -0
  12. morphml/benchmarks/datasets.py +280 -0
  13. morphml/benchmarks/metrics.py +199 -0
  14. morphml/benchmarks/openml_suite.py +201 -0
  15. morphml/benchmarks/problems.py +289 -0
  16. morphml/benchmarks/suite.py +318 -0
  17. morphml/cli/__init__.py +5 -0
  18. morphml/cli/commands/experiment.py +329 -0
  19. morphml/cli/main.py +457 -0
  20. morphml/cli/quickstart.py +312 -0
  21. morphml/config.py +278 -0
  22. morphml/constraints/__init__.py +19 -0
  23. morphml/constraints/handler.py +205 -0
  24. morphml/constraints/predicates.py +285 -0
  25. morphml/core/__init__.py +3 -0
  26. morphml/core/crossover.py +449 -0
  27. morphml/core/dsl/README.md +359 -0
  28. morphml/core/dsl/__init__.py +72 -0
  29. morphml/core/dsl/ast_nodes.py +364 -0
  30. morphml/core/dsl/compiler.py +318 -0
  31. morphml/core/dsl/layers.py +368 -0
  32. morphml/core/dsl/lexer.py +336 -0
  33. morphml/core/dsl/parser.py +455 -0
  34. morphml/core/dsl/search_space.py +386 -0
  35. morphml/core/dsl/syntax.py +199 -0
  36. morphml/core/dsl/type_system.py +361 -0
  37. morphml/core/dsl/validator.py +386 -0
  38. morphml/core/graph/__init__.py +40 -0
  39. morphml/core/graph/edge.py +124 -0
  40. morphml/core/graph/graph.py +507 -0
  41. morphml/core/graph/mutations.py +409 -0
  42. morphml/core/graph/node.py +196 -0
  43. morphml/core/graph/serialization.py +361 -0
  44. morphml/core/graph/visualization.py +431 -0
  45. morphml/core/objectives/__init__.py +20 -0
  46. morphml/core/search/__init__.py +33 -0
  47. morphml/core/search/individual.py +252 -0
  48. morphml/core/search/parameters.py +453 -0
  49. morphml/core/search/population.py +375 -0
  50. morphml/core/search/search_engine.py +340 -0
  51. morphml/distributed/__init__.py +76 -0
  52. morphml/distributed/fault_tolerance.py +497 -0
  53. morphml/distributed/health_monitor.py +348 -0
  54. morphml/distributed/master.py +709 -0
  55. morphml/distributed/proto/README.md +224 -0
  56. morphml/distributed/proto/__init__.py +74 -0
  57. morphml/distributed/proto/worker.proto +170 -0
  58. morphml/distributed/proto/worker_pb2.py +79 -0
  59. morphml/distributed/proto/worker_pb2_grpc.py +423 -0
  60. morphml/distributed/resource_manager.py +416 -0
  61. morphml/distributed/scheduler.py +567 -0
  62. morphml/distributed/storage/__init__.py +33 -0
  63. morphml/distributed/storage/artifacts.py +381 -0
  64. morphml/distributed/storage/cache.py +366 -0
  65. morphml/distributed/storage/checkpointing.py +329 -0
  66. morphml/distributed/storage/database.py +459 -0
  67. morphml/distributed/worker.py +549 -0
  68. morphml/evaluation/__init__.py +5 -0
  69. morphml/evaluation/heuristic.py +237 -0
  70. morphml/exceptions.py +55 -0
  71. morphml/execution/__init__.py +5 -0
  72. morphml/execution/local_executor.py +350 -0
  73. morphml/integrations/__init__.py +28 -0
  74. morphml/integrations/jax_adapter.py +206 -0
  75. morphml/integrations/pytorch_adapter.py +530 -0
  76. morphml/integrations/sklearn_adapter.py +206 -0
  77. morphml/integrations/tensorflow_adapter.py +230 -0
  78. morphml/logging_config.py +93 -0
  79. morphml/meta_learning/__init__.py +66 -0
  80. morphml/meta_learning/architecture_similarity.py +277 -0
  81. morphml/meta_learning/experiment_database.py +240 -0
  82. morphml/meta_learning/knowledge_base/__init__.py +19 -0
  83. morphml/meta_learning/knowledge_base/embedder.py +179 -0
  84. morphml/meta_learning/knowledge_base/knowledge_base.py +313 -0
  85. morphml/meta_learning/knowledge_base/meta_features.py +265 -0
  86. morphml/meta_learning/knowledge_base/vector_store.py +271 -0
  87. morphml/meta_learning/predictors/__init__.py +27 -0
  88. morphml/meta_learning/predictors/ensemble.py +221 -0
  89. morphml/meta_learning/predictors/gnn_predictor.py +552 -0
  90. morphml/meta_learning/predictors/learning_curve.py +231 -0
  91. morphml/meta_learning/predictors/proxy_metrics.py +261 -0
  92. morphml/meta_learning/strategy_evolution/__init__.py +27 -0
  93. morphml/meta_learning/strategy_evolution/adaptive_optimizer.py +226 -0
  94. morphml/meta_learning/strategy_evolution/bandit.py +276 -0
  95. morphml/meta_learning/strategy_evolution/portfolio.py +230 -0
  96. morphml/meta_learning/transfer.py +581 -0
  97. morphml/meta_learning/warm_start.py +286 -0
  98. morphml/optimizers/__init__.py +74 -0
  99. morphml/optimizers/adaptive_operators.py +399 -0
  100. morphml/optimizers/bayesian/__init__.py +52 -0
  101. morphml/optimizers/bayesian/acquisition.py +387 -0
  102. morphml/optimizers/bayesian/base.py +319 -0
  103. morphml/optimizers/bayesian/gaussian_process.py +635 -0
  104. morphml/optimizers/bayesian/smac.py +534 -0
  105. morphml/optimizers/bayesian/tpe.py +411 -0
  106. morphml/optimizers/differential_evolution.py +220 -0
  107. morphml/optimizers/evolutionary/__init__.py +61 -0
  108. morphml/optimizers/evolutionary/cma_es.py +416 -0
  109. morphml/optimizers/evolutionary/differential_evolution.py +556 -0
  110. morphml/optimizers/evolutionary/encoding.py +426 -0
  111. morphml/optimizers/evolutionary/particle_swarm.py +449 -0
  112. morphml/optimizers/genetic_algorithm.py +486 -0
  113. morphml/optimizers/gradient_based/__init__.py +22 -0
  114. morphml/optimizers/gradient_based/darts.py +550 -0
  115. morphml/optimizers/gradient_based/enas.py +585 -0
  116. morphml/optimizers/gradient_based/operations.py +474 -0
  117. morphml/optimizers/gradient_based/utils.py +601 -0
  118. morphml/optimizers/hill_climbing.py +169 -0
  119. morphml/optimizers/multi_objective/__init__.py +56 -0
  120. morphml/optimizers/multi_objective/indicators.py +504 -0
  121. morphml/optimizers/multi_objective/nsga2.py +647 -0
  122. morphml/optimizers/multi_objective/visualization.py +427 -0
  123. morphml/optimizers/nsga2.py +308 -0
  124. morphml/optimizers/random_search.py +172 -0
  125. morphml/optimizers/simulated_annealing.py +181 -0
  126. morphml/plugins/__init__.py +35 -0
  127. morphml/plugins/custom_evaluator_example.py +81 -0
  128. morphml/plugins/custom_optimizer_example.py +63 -0
  129. morphml/plugins/plugin_system.py +454 -0
  130. morphml/reports/__init__.py +30 -0
  131. morphml/reports/generator.py +362 -0
  132. morphml/tracking/__init__.py +7 -0
  133. morphml/tracking/experiment.py +309 -0
  134. morphml/tracking/logger.py +301 -0
  135. morphml/tracking/reporter.py +357 -0
  136. morphml/utils/__init__.py +6 -0
  137. morphml/utils/checkpoint.py +189 -0
  138. morphml/utils/comparison.py +390 -0
  139. morphml/utils/export.py +407 -0
  140. morphml/utils/progress.py +392 -0
  141. morphml/utils/validation.py +392 -0
  142. morphml/version.py +7 -0
  143. morphml/visualization/__init__.py +50 -0
  144. morphml/visualization/analytics.py +423 -0
  145. morphml/visualization/architecture_diagrams.py +353 -0
  146. morphml/visualization/architecture_plot.py +223 -0
  147. morphml/visualization/convergence_plot.py +174 -0
  148. morphml/visualization/crossover_viz.py +386 -0
  149. morphml/visualization/graph_viz.py +338 -0
  150. morphml/visualization/pareto_plot.py +149 -0
  151. morphml/visualization/plotly_dashboards.py +422 -0
  152. morphml/visualization/population.py +309 -0
  153. morphml/visualization/progress.py +260 -0
  154. morphml-1.0.0.dist-info/METADATA +434 -0
  155. morphml-1.0.0.dist-info/RECORD +158 -0
  156. morphml-1.0.0.dist-info/WHEEL +4 -0
  157. morphml-1.0.0.dist-info/entry_points.txt +3 -0
  158. morphml-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,381 @@
1
+ """S3/MinIO artifact storage for models and checkpoints.
2
+
3
+ Provides object storage for large binary artifacts.
4
+
5
+ Author: Eshan Roy <eshanized@proton.me>
6
+ Organization: TONMOY INFRASTRUCTURE & VISION
7
+ """
8
+
9
+ import os
10
+ from pathlib import Path
11
+ from typing import Any, Dict, List, Optional
12
+
13
+ try:
14
+ import boto3
15
+ from botocore.exceptions import ClientError
16
+
17
+ BOTO3_AVAILABLE = True
18
+ except ImportError:
19
+ BOTO3_AVAILABLE = False
20
+
21
+ from morphml.exceptions import DistributedError
22
+ from morphml.logging_config import get_logger
23
+
24
+ logger = get_logger(__name__)
25
+
26
+
27
+ class ArtifactStore:
28
+ """
29
+ S3-compatible artifact storage.
30
+
31
+ Stores large binary artifacts:
32
+ - Trained model weights (.pt, .h5, .ckpt)
33
+ - Architecture checkpoints
34
+ - Visualization plots (.png, .html)
35
+ - Training logs (.txt, .json)
36
+
37
+ Compatible with both AWS S3 and MinIO.
38
+
39
+ Args:
40
+ bucket: S3 bucket name
41
+ endpoint_url: Custom endpoint for MinIO (None for AWS S3)
42
+ aws_access_key: AWS access key ID (or from environment)
43
+ aws_secret_key: AWS secret access key (or from environment)
44
+ region: AWS region (default: us-east-1)
45
+
46
+ Example:
47
+ >>> # AWS S3
48
+ >>> store = ArtifactStore(bucket='morphml-artifacts')
49
+ >>>
50
+ >>> # MinIO
51
+ >>> store = ArtifactStore(
52
+ ... bucket='morphml',
53
+ ... endpoint_url='http://localhost:9000',
54
+ ... aws_access_key='minioadmin',
55
+ ... aws_secret_key='minioadmin'
56
+ ... )
57
+ >>>
58
+ >>> # Upload
59
+ >>> store.upload_file('model.pt', 'experiments/exp1/model.pt')
60
+ >>>
61
+ >>> # Download
62
+ >>> store.download_file('experiments/exp1/model.pt', 'model.pt')
63
+ """
64
+
65
+ def __init__(
66
+ self,
67
+ bucket: str,
68
+ endpoint_url: Optional[str] = None,
69
+ aws_access_key: Optional[str] = None,
70
+ aws_secret_key: Optional[str] = None,
71
+ region: str = "us-east-1",
72
+ ):
73
+ """Initialize artifact store."""
74
+ if not BOTO3_AVAILABLE:
75
+ raise DistributedError("boto3 not available. Install with: pip install boto3")
76
+
77
+ self.bucket = bucket
78
+ self.endpoint_url = endpoint_url
79
+
80
+ # Create S3 client
81
+ self.s3 = boto3.client(
82
+ "s3",
83
+ endpoint_url=endpoint_url,
84
+ aws_access_key_id=aws_access_key or os.getenv("AWS_ACCESS_KEY_ID"),
85
+ aws_secret_access_key=aws_secret_key or os.getenv("AWS_SECRET_ACCESS_KEY"),
86
+ region_name=region,
87
+ )
88
+
89
+ # Create bucket if not exists
90
+ self._ensure_bucket_exists()
91
+
92
+ logger.info(f"Initialized artifact store: {bucket} (endpoint: {endpoint_url or 'AWS S3'})")
93
+
94
+ def _ensure_bucket_exists(self) -> None:
95
+ """Create bucket if it doesn't exist."""
96
+ try:
97
+ self.s3.head_bucket(Bucket=self.bucket)
98
+ logger.debug(f"Bucket {self.bucket} exists")
99
+ except ClientError as e:
100
+ error_code = e.response.get("Error", {}).get("Code")
101
+
102
+ if error_code == "404":
103
+ # Bucket doesn't exist, create it
104
+ try:
105
+ self.s3.create_bucket(Bucket=self.bucket)
106
+ logger.info(f"Created bucket: {self.bucket}")
107
+ except ClientError as create_error:
108
+ raise DistributedError(f"Failed to create bucket {self.bucket}: {create_error}")
109
+ else:
110
+ raise DistributedError(f"Failed to access bucket {self.bucket}: {e}")
111
+
112
+ def upload_file(
113
+ self, local_path: str, s3_key: str, metadata: Optional[Dict[str, str]] = None
114
+ ) -> None:
115
+ """
116
+ Upload file to S3.
117
+
118
+ Args:
119
+ local_path: Local file path
120
+ s3_key: S3 object key (path in bucket)
121
+ metadata: Optional metadata tags
122
+ """
123
+ try:
124
+ extra_args = {}
125
+ if metadata:
126
+ extra_args["Metadata"] = metadata
127
+
128
+ self.s3.upload_file(local_path, self.bucket, s3_key, ExtraArgs=extra_args)
129
+
130
+ logger.info(f"Uploaded {local_path} to s3://{self.bucket}/{s3_key}")
131
+
132
+ except ClientError as e:
133
+ raise DistributedError(f"Failed to upload {local_path}: {e}")
134
+
135
+ def download_file(self, s3_key: str, local_path: str) -> None:
136
+ """
137
+ Download file from S3.
138
+
139
+ Args:
140
+ s3_key: S3 object key
141
+ local_path: Local destination path
142
+ """
143
+ try:
144
+ # Create parent directories
145
+ Path(local_path).parent.mkdir(parents=True, exist_ok=True)
146
+
147
+ self.s3.download_file(self.bucket, s3_key, local_path)
148
+
149
+ logger.info(f"Downloaded s3://{self.bucket}/{s3_key} to {local_path}")
150
+
151
+ except ClientError as e:
152
+ raise DistributedError(f"Failed to download {s3_key}: {e}")
153
+
154
+ def upload_bytes(
155
+ self, data: bytes, s3_key: str, metadata: Optional[Dict[str, str]] = None
156
+ ) -> None:
157
+ """
158
+ Upload bytes directly to S3.
159
+
160
+ Args:
161
+ data: Bytes to upload
162
+ s3_key: S3 object key
163
+ metadata: Optional metadata
164
+ """
165
+ try:
166
+ extra_args = {}
167
+ if metadata:
168
+ extra_args["Metadata"] = metadata
169
+
170
+ self.s3.put_object(Bucket=self.bucket, Key=s3_key, Body=data, **extra_args)
171
+
172
+ logger.info(f"Uploaded {len(data)} bytes to s3://{self.bucket}/{s3_key}")
173
+
174
+ except ClientError as e:
175
+ raise DistributedError(f"Failed to upload bytes to {s3_key}: {e}")
176
+
177
+ def download_bytes(self, s3_key: str) -> bytes:
178
+ """
179
+ Download bytes from S3.
180
+
181
+ Args:
182
+ s3_key: S3 object key
183
+
184
+ Returns:
185
+ Downloaded bytes
186
+ """
187
+ try:
188
+ response = self.s3.get_object(Bucket=self.bucket, Key=s3_key)
189
+ data = response["Body"].read()
190
+
191
+ logger.info(f"Downloaded {len(data)} bytes from s3://{self.bucket}/{s3_key}")
192
+
193
+ return data
194
+
195
+ except ClientError as e:
196
+ raise DistributedError(f"Failed to download {s3_key}: {e}")
197
+
198
+ def exists(self, s3_key: str) -> bool:
199
+ """
200
+ Check if object exists.
201
+
202
+ Args:
203
+ s3_key: S3 object key
204
+
205
+ Returns:
206
+ True if object exists
207
+ """
208
+ try:
209
+ self.s3.head_object(Bucket=self.bucket, Key=s3_key)
210
+ return True
211
+ except ClientError:
212
+ return False
213
+
214
+ def delete(self, s3_key: str) -> None:
215
+ """
216
+ Delete object.
217
+
218
+ Args:
219
+ s3_key: S3 object key
220
+ """
221
+ try:
222
+ self.s3.delete_object(Bucket=self.bucket, Key=s3_key)
223
+ logger.info(f"Deleted s3://{self.bucket}/{s3_key}")
224
+ except ClientError as e:
225
+ raise DistributedError(f"Failed to delete {s3_key}: {e}")
226
+
227
+ def list_objects(self, prefix: str = "", max_keys: int = 1000) -> List[Dict[str, Any]]:
228
+ """
229
+ List objects with prefix.
230
+
231
+ Args:
232
+ prefix: Key prefix to filter
233
+ max_keys: Maximum number of keys to return
234
+
235
+ Returns:
236
+ List of object metadata dictionaries
237
+ """
238
+ try:
239
+ response = self.s3.list_objects_v2(Bucket=self.bucket, Prefix=prefix, MaxKeys=max_keys)
240
+
241
+ if "Contents" not in response:
242
+ return []
243
+
244
+ return [
245
+ {
246
+ "key": obj["Key"],
247
+ "size": obj["Size"],
248
+ "last_modified": obj["LastModified"],
249
+ }
250
+ for obj in response["Contents"]
251
+ ]
252
+
253
+ except ClientError as e:
254
+ raise DistributedError(f"Failed to list objects: {e}")
255
+
256
+ def list_keys(self, prefix: str = "") -> List[str]:
257
+ """
258
+ List object keys with prefix.
259
+
260
+ Args:
261
+ prefix: Key prefix to filter
262
+
263
+ Returns:
264
+ List of object keys
265
+ """
266
+ objects = self.list_objects(prefix)
267
+ return [obj["key"] for obj in objects]
268
+
269
+ def get_metadata(self, s3_key: str) -> Dict[str, Any]:
270
+ """
271
+ Get object metadata.
272
+
273
+ Args:
274
+ s3_key: S3 object key
275
+
276
+ Returns:
277
+ Metadata dictionary
278
+ """
279
+ try:
280
+ response = self.s3.head_object(Bucket=self.bucket, Key=s3_key)
281
+
282
+ return {
283
+ "size": response["ContentLength"],
284
+ "last_modified": response["LastModified"],
285
+ "content_type": response.get("ContentType"),
286
+ "metadata": response.get("Metadata", {}),
287
+ }
288
+
289
+ except ClientError as e:
290
+ raise DistributedError(f"Failed to get metadata for {s3_key}: {e}")
291
+
292
+ def get_presigned_url(
293
+ self, s3_key: str, expiration: int = 3600, method: str = "get_object"
294
+ ) -> str:
295
+ """
296
+ Generate presigned URL for temporary access.
297
+
298
+ Args:
299
+ s3_key: S3 object key
300
+ expiration: URL expiration in seconds
301
+ method: S3 method ('get_object' or 'put_object')
302
+
303
+ Returns:
304
+ Presigned URL
305
+ """
306
+ try:
307
+ url = self.s3.generate_presigned_url(
308
+ method,
309
+ Params={"Bucket": self.bucket, "Key": s3_key},
310
+ ExpiresIn=expiration,
311
+ )
312
+
313
+ return url
314
+
315
+ except ClientError as e:
316
+ raise DistributedError(f"Failed to generate presigned URL: {e}")
317
+
318
+ def copy(self, source_key: str, dest_key: str) -> None:
319
+ """
320
+ Copy object within bucket.
321
+
322
+ Args:
323
+ source_key: Source object key
324
+ dest_key: Destination object key
325
+ """
326
+ try:
327
+ copy_source = {"Bucket": self.bucket, "Key": source_key}
328
+ self.s3.copy_object(CopySource=copy_source, Bucket=self.bucket, Key=dest_key)
329
+
330
+ logger.info(f"Copied {source_key} to {dest_key}")
331
+
332
+ except ClientError as e:
333
+ raise DistributedError(f"Failed to copy {source_key}: {e}")
334
+
335
+ def delete_prefix(self, prefix: str) -> int:
336
+ """
337
+ Delete all objects with prefix.
338
+
339
+ Args:
340
+ prefix: Key prefix
341
+
342
+ Returns:
343
+ Number of objects deleted
344
+ """
345
+ keys = self.list_keys(prefix)
346
+
347
+ if not keys:
348
+ return 0
349
+
350
+ # Delete in batches of 1000 (S3 limit)
351
+ deleted = 0
352
+ for i in range(0, len(keys), 1000):
353
+ batch = keys[i : i + 1000]
354
+
355
+ try:
356
+ self.s3.delete_objects(
357
+ Bucket=self.bucket,
358
+ Delete={"Objects": [{"Key": key} for key in batch]},
359
+ )
360
+
361
+ deleted += len(batch)
362
+
363
+ except ClientError as e:
364
+ logger.error(f"Failed to delete batch: {e}")
365
+
366
+ logger.info(f"Deleted {deleted} objects with prefix {prefix}")
367
+
368
+ return deleted
369
+
370
+ def get_total_size(self, prefix: str = "") -> int:
371
+ """
372
+ Get total size of objects with prefix.
373
+
374
+ Args:
375
+ prefix: Key prefix
376
+
377
+ Returns:
378
+ Total size in bytes
379
+ """
380
+ objects = self.list_objects(prefix)
381
+ return sum(obj["size"] for obj in objects)