pathwaysutils 0.1.12__tar.gz → 0.1.14__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 (74) hide show
  1. pathwaysutils-0.1.14/PKG-INFO +90 -0
  2. pathwaysutils-0.1.14/README.md +50 -0
  3. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/__init__.py +1 -1
  4. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/_initialize.py +3 -1
  5. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/elastic/elastic.py +4 -0
  6. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/elastic/manager.py +19 -23
  7. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/concatenate_by_mesh_axis.py +25 -1
  8. pathwaysutils-0.1.14/pathwaysutils/experimental/gke/README.md +142 -0
  9. pathwaysutils-0.1.14/pathwaysutils/experimental/gke/jobset.py +1645 -0
  10. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/profiling.py +11 -10
  11. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/reshard.py +1 -1
  12. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/README.md +2 -2
  13. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/deploy_pathways_service.py +57 -72
  14. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/dockerfiles/sample_sidecar.dockerfile +2 -2
  15. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/gke_utils.py +561 -42
  16. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/isc_pathways.py +34 -44
  17. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/run_workload.py +10 -0
  18. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/validators.py +85 -57
  19. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/split_by_mesh_axis.py +2 -2
  20. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/persistence/helper.py +7 -2
  21. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/profiling.py +130 -22
  22. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/proxy_backend.py +13 -1
  23. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/reshard.py +3 -3
  24. pathwaysutils-0.1.14/pathwaysutils/test/elastic_simulated_fault_integration_test.py +128 -0
  25. pathwaysutils-0.1.14/pathwaysutils/test/experimental/gke/jobset_test.py +2273 -0
  26. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/gke/testdata/model_lite_tpuv5e_4x8.yaml +4 -4
  27. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/shared_pathways_service/deploy_pathways_service_test.py +148 -35
  28. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/shared_pathways_service/gke_utils_test.py +1139 -70
  29. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/shared_pathways_service/isc_pathways_test.py +315 -132
  30. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/shared_pathways_service/run_workload_test.py +47 -0
  31. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/shared_pathways_service/validators_test.py +103 -21
  32. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/initialize_test.py +4 -2
  33. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/mock_fault_injection.py +51 -8
  34. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/mock_fault_injection_test.py +47 -0
  35. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/persistence_test.py +20 -0
  36. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/profiling_test.py +190 -5
  37. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/proxy_backend_test.py +12 -0
  38. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/reshard_test.py +13 -0
  39. pathwaysutils-0.1.14/pathwaysutils/test/tpu_utils_test.py +44 -0
  40. pathwaysutils-0.1.14/pathwaysutils/tpu_utils.py +32 -0
  41. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pyproject.toml +1 -1
  42. pathwaysutils-0.1.12/PKG-INFO +0 -61
  43. pathwaysutils-0.1.12/README.md +0 -21
  44. pathwaysutils-0.1.12/pathwaysutils/experimental/gke/jobset.py +0 -921
  45. pathwaysutils-0.1.12/pathwaysutils/test/experimental/gke/jobset_test.py +0 -850
  46. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/LICENSE +0 -0
  47. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/collect_profile.py +0 -0
  48. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/debug/__init__.py +0 -0
  49. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/debug/timing.py +0 -0
  50. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/debug/watchdog.py +0 -0
  51. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/elastic/__init__.py +0 -0
  52. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/__init__.py +0 -0
  53. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/gke/__init__.py +0 -0
  54. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/__init__.py +0 -0
  55. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/metrics_collector.py +0 -0
  56. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/run_connect_example.py +0 -0
  57. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/start_vscode_on_cpu_np.py +0 -0
  58. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/tpu_specs.py +0 -0
  59. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/yamls/code-server.yaml +0 -0
  60. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/experimental/shared_pathways_service/yamls/pw-proxy.yaml +0 -0
  61. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/jax/__init__.py +0 -0
  62. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/lru_cache.py +0 -0
  63. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/persistence/__init__.py +0 -0
  64. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/plugin_executable.py +0 -0
  65. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/__init__.py +0 -0
  66. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/debug/__init__.py +0 -0
  67. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/debug/timing_test.py +0 -0
  68. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/debug/watchdog_test.py +0 -0
  69. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/reshard_test.py +0 -0
  70. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/shared_pathways_service/metrics_collector_test.py +0 -0
  71. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/shared_pathways_service/start_vscode_on_cpu_np_test.py +0 -0
  72. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/experimental/shared_pathways_service/tpu_specs_test.py +0 -0
  73. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/lru_cache_test.py +0 -0
  74. {pathwaysutils-0.1.12 → pathwaysutils-0.1.14}/pathwaysutils/test/plugin_executable_test.py +0 -0
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: pathwaysutils
3
+ Version: 0.1.14
4
+ Summary: Package of Pathways-on-Cloud utilities.
5
+ Keywords:
6
+ Author-email: Pathways-on-Cloud Utilities Developers <pathwaysutils-dev@google.com>
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ Classifier: License :: OSI Approved :: Apache Software License
10
+ License-File: LICENSE
11
+ Requires-Dist: absl-py
12
+ Requires-Dist: fastapi
13
+ Requires-Dist: jax>=0.10.0
14
+ Requires-Dist: orbax-checkpoint>=0.12.3
15
+ Requires-Dist: uvicorn
16
+ Requires-Dist: requests
17
+ Requires-Dist: packaging
18
+ Requires-Dist: pylint>=2.6.0 ; extra == "dev"
19
+ Requires-Dist: pyink ; extra == "dev"
20
+ Requires-Dist: kubernetes ; extra == "gke"
21
+ Requires-Dist: pyyaml ; extra == "gke"
22
+ Requires-Dist: portpicker ; extra == "sps"
23
+ Requires-Dist: google-cloud-monitoring ; extra == "sps"
24
+ Requires-Dist: portpicker ; extra == "sps-without-monitoring"
25
+ Requires-Dist: google-cloud-monitoring ; extra == "test"
26
+ Requires-Dist: httpx ; extra == "test"
27
+ Requires-Dist: kubernetes ; extra == "test"
28
+ Requires-Dist: portpicker ; extra == "test"
29
+ Requires-Dist: pyyaml ; extra == "test"
30
+ Requires-Dist: pytest ; extra == "test"
31
+ Project-URL: changelog, https://github.com/AI-Hypercomputer/pathways-utils/blob/main/CHANGELOG.md
32
+ Project-URL: homepage, https://github.com/AI-Hypercomputer/pathways-utils
33
+ Project-URL: repository, https://github.com/AI-Hypercomputer/pathways-utils
34
+ Provides-Extra: dev
35
+ Provides-Extra: gke
36
+ Provides-Extra: sps
37
+ Provides-Extra: sps-without-monitoring
38
+ Provides-Extra: test
39
+
40
+ # APIs to get started with using Pathways on Cloud
41
+
42
+ Pathways is a compute orchestration runtime for single-controller ML workloads
43
+ across multiple Cloud TPU slices. A single Python controller orchestrates
44
+ computation across hundreds or thousands of TPU chips, unlocking slice-level
45
+ training resiliency, fast cross-slice data movement, and interactive
46
+ multi-slice development.
47
+
48
+ This package bundles the Python client integrations, resharding and elasticity
49
+ helpers, and GKE deployment builders for running JAX workloads on Pathways on
50
+ Cloud.
51
+
52
+ ## Installation
53
+
54
+ ```bash
55
+ pip install pathwaysutils
56
+ # Optional GKE JobSet generator and kubectl/Kubernetes client dependencies:
57
+ pip install 'pathwaysutils[gke]'
58
+ # Optional experimental Shared Pathways Service (SPS) dependencies:
59
+ pip install 'pathwaysutils[sps]'
60
+ ```
61
+
62
+ ## Core Modules
63
+
64
+ - `pathwaysutils.initialize()` registers the Pathways IFRT proxy backend,
65
+ configures the compilation cache and XProf trace hooks, and registers Orbax
66
+ checkpoint handlers.
67
+ - `pathwaysutils.reshard.reshard` reshards arrays and PyTrees across device
68
+ meshes directly on Pathways workers via the IFRT `RemapArray` API without
69
+ routing tensors through controller memory.
70
+ - `pathwaysutils.elastic.manager.Manager` monitors slice health in the
71
+ background and provides `@elastic_retry` and `ScaleUpSignalError` for
72
+ step-level fault recovery and dynamic slice scaling.
73
+ - [`PathwaysJobSet`][gke-readme] (`pathwaysutils.experimental.gke.jobset`)
74
+ builds, customizes, exports, and deploys Kubernetes `JobSet` manifests for
75
+ Pathways workloads on GKE.
76
+ - [Shared Pathways Service][sps-readme]
77
+ (`pathwaysutils.experimental.shared_pathways_service`) deploys a persistent
78
+ multi-tenant Pathways service on GKE for interactive JAX sessions.
79
+
80
+ ## Further Reading
81
+
82
+ - [Get started with Pathways on Google Cloud][cloud-docs]
83
+ - [Pathways Research Paper](https://arxiv.org/abs/2203.12533)
84
+
85
+ [gke-readme]: https://github.com/AI-Hypercomputer/pathways-utils/tree/main/pathwaysutils/experimental/gke
86
+ [sps-readme]: https://github.com/AI-Hypercomputer/pathways-utils/tree/main/pathwaysutils/experimental/shared_pathways_service
87
+ [cloud-docs]: https://docs.cloud.google.com/ai-hypercomputer/docs/workloads/pathways-on-cloud/pathways-intro
88
+
89
+
90
+
@@ -0,0 +1,50 @@
1
+ # APIs to get started with using Pathways on Cloud
2
+
3
+ Pathways is a compute orchestration runtime for single-controller ML workloads
4
+ across multiple Cloud TPU slices. A single Python controller orchestrates
5
+ computation across hundreds or thousands of TPU chips, unlocking slice-level
6
+ training resiliency, fast cross-slice data movement, and interactive
7
+ multi-slice development.
8
+
9
+ This package bundles the Python client integrations, resharding and elasticity
10
+ helpers, and GKE deployment builders for running JAX workloads on Pathways on
11
+ Cloud.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pip install pathwaysutils
17
+ # Optional GKE JobSet generator and kubectl/Kubernetes client dependencies:
18
+ pip install 'pathwaysutils[gke]'
19
+ # Optional experimental Shared Pathways Service (SPS) dependencies:
20
+ pip install 'pathwaysutils[sps]'
21
+ ```
22
+
23
+ ## Core Modules
24
+
25
+ - `pathwaysutils.initialize()` registers the Pathways IFRT proxy backend,
26
+ configures the compilation cache and XProf trace hooks, and registers Orbax
27
+ checkpoint handlers.
28
+ - `pathwaysutils.reshard.reshard` reshards arrays and PyTrees across device
29
+ meshes directly on Pathways workers via the IFRT `RemapArray` API without
30
+ routing tensors through controller memory.
31
+ - `pathwaysutils.elastic.manager.Manager` monitors slice health in the
32
+ background and provides `@elastic_retry` and `ScaleUpSignalError` for
33
+ step-level fault recovery and dynamic slice scaling.
34
+ - [`PathwaysJobSet`][gke-readme] (`pathwaysutils.experimental.gke.jobset`)
35
+ builds, customizes, exports, and deploys Kubernetes `JobSet` manifests for
36
+ Pathways workloads on GKE.
37
+ - [Shared Pathways Service][sps-readme]
38
+ (`pathwaysutils.experimental.shared_pathways_service`) deploys a persistent
39
+ multi-tenant Pathways service on GKE for interactive JAX sessions.
40
+
41
+ ## Further Reading
42
+
43
+ - [Get started with Pathways on Google Cloud][cloud-docs]
44
+ - [Pathways Research Paper](https://arxiv.org/abs/2203.12533)
45
+
46
+ [gke-readme]: https://github.com/AI-Hypercomputer/pathways-utils/tree/main/pathwaysutils/experimental/gke
47
+ [sps-readme]: https://github.com/AI-Hypercomputer/pathways-utils/tree/main/pathwaysutils/experimental/shared_pathways_service
48
+ [cloud-docs]: https://docs.cloud.google.com/ai-hypercomputer/docs/workloads/pathways-on-cloud/pathways-intro
49
+
50
+
@@ -21,4 +21,4 @@ is_pathways_backend_used: Callable[[], bool] = _initialize.is_pathways_backend_u
21
21
  del _initialize
22
22
 
23
23
  # When changing this, also update the CHANGELOG.md.
24
- __version__ = "v0.1.12"
24
+ __version__ = "v0.1.14"
@@ -40,7 +40,9 @@ def is_pathways_backend_used() -> bool:
40
40
  is not necessarily which backend is ultimately selected or there may be more
41
41
  than one platform specified and another may have higher priority.
42
42
  """
43
- return jax.config.jax_platforms and "proxy" in jax.config.jax_platforms
43
+ return bool(
44
+ jax.config.jax_platforms and "proxy" in jax.config.jax_platforms
45
+ )
44
46
 
45
47
 
46
48
  def _is_persistence_enabled() -> bool:
@@ -198,6 +198,10 @@ def wait_for_slices(
198
198
  TimeoutError: If the timeout is reached before the slices become
199
199
  active.
200
200
  """
201
+ if poll_interval < 0:
202
+ raise ValueError(f"poll_interval must be non-negative, got {poll_interval}")
203
+ if timeout is not None and timeout < 0:
204
+ raise ValueError(f"timeout must be non-negative, got {timeout}")
201
205
  if slice_to_devices is None:
202
206
  _logger.debug("slice_to_devices is None. Getting from jax.devices().")
203
207
  slice_to_devices = get_slice_to_devices(jax.devices())
@@ -52,7 +52,7 @@ RetryPolicy: TypeAlias = Callable[[int, Exception], bool]
52
52
 
53
53
 
54
54
  def _elastic_event_cleanup() -> None:
55
- """Cleans up JAX profiles, caches, and live arrays."""
55
+ """Stops any ongoing JAX profiler trace."""
56
56
  try:
57
57
  _logger.info("Cleaning up any ongoing traces")
58
58
  jax.profiler.stop_trace()
@@ -62,10 +62,6 @@ def _elastic_event_cleanup() -> None:
62
62
  _logger.exception("Error cleaning up ongoing traces")
63
63
  raise
64
64
 
65
- jax.clear_caches()
66
- for array in jax.live_arrays():
67
- array.delete()
68
-
69
65
 
70
66
  class ElasticRetryLimit:
71
67
  """A retry callback that limits the number of attempts."""
@@ -162,8 +158,9 @@ class Manager:
162
158
  pass
163
159
  else:
164
160
  raise
165
- self._monitor_thread = None
166
- self._stop_event = None
161
+ finally:
162
+ self._monitor_thread = None
163
+ self._stop_event = None
167
164
 
168
165
  @functools.cached_property
169
166
  def total_slice_count(self) -> int:
@@ -201,6 +198,14 @@ class Manager:
201
198
  """The number of active slices."""
202
199
  return len(self.active_slice_indices)
203
200
 
201
+ @property
202
+ def active_devices(self) -> list[jax.Device]:
203
+ """The devices belonging to the active slices, ordered by slice index."""
204
+ devices = []
205
+ for slice_index in sorted(self.active_slice_indices):
206
+ devices.extend(self.slice_to_devices[slice_index])
207
+ return devices
208
+
204
209
  def scale_by_active_slices(self, x: int | float) -> int | float:
205
210
  """Scale x by the number of active slices."""
206
211
  if isinstance(x, int):
@@ -218,20 +223,6 @@ class Manager:
218
223
  else:
219
224
  raise ValueError(f"Unsupported type: {type(x)=}")
220
225
 
221
- def _cleanup_on_retry(self):
222
- """Cleans up JAX caches and traces on retry."""
223
- try:
224
- _logger.debug("Cleaning up any ongoing traces")
225
- jax.profiler.stop_trace()
226
- except (RuntimeError, ValueError):
227
- _logger.debug("No ongoing traces to clean up")
228
- except Exception: # pylint: disable=broad-exception-caught
229
- _logger.exception("Error cleaning up ongoing traces")
230
-
231
- jax.clear_caches()
232
- for array in jax.live_arrays():
233
- array.delete()
234
-
235
226
  def _check_inactive_slices(self) -> None:
236
227
  """Checks inactive slices and updates available_inactive_slices."""
237
228
  if not self.inactive_slice_indices:
@@ -299,8 +290,7 @@ class Manager:
299
290
 
300
291
  This decorator wraps a function to automatically retry execution in case of
301
292
  `jax.errors.JaxRuntimeError` caused by slice down events. It waits for
302
- `minimum_slice_count` active slices before each attempt and cleans up JAX
303
- caches on failure.
293
+ `minimum_slice_count` active slices before each attempt.
304
294
 
305
295
  If `minimum_slice_count` is not met, the function will wait until at least
306
296
  `minimum_slice_count` slices are active before execution. If
@@ -313,6 +303,12 @@ class Manager:
313
303
  boundaries) and raise a `ScaleUpSignalError` to gracefully interrupt the
314
304
  current execution and trigger a retry with the expanded hardware.
315
305
 
306
+ On an elastic event the arrays of the failed attempt are not deleted
307
+ explicitly; JAX frees them once the attempt has unwound and nothing
308
+ references them anymore. The wrapped function and the callbacks must not
309
+ keep references to them, or the memory stays allocated during the next
310
+ attempt.
311
+
316
312
  Often, the function will dispatch JAX operations and wait for them to
317
313
  complete while creating a log message. If using Python logging, it is
318
314
  recommended to set `logging.raiseExceptions=True` to ensure that the
@@ -68,10 +68,21 @@ def concatenate_by_mesh_axis(
68
68
  if not flattened_arrays[0]:
69
69
  return array_trees[0]
70
70
 
71
- # Extract the shared mesh from each PyTree (from an arbitrary array in each).
71
+ # Extract the shared mesh from each PyTree and verify all leaves in a PyTree
72
+ # share that mesh.
72
73
  meshes_to_concatenate = [
73
74
  _get_named_sharding(array).mesh for array in input_flat_arrays[0]
74
75
  ]
76
+ for tree_arrays, expected_mesh in zip(
77
+ flattened_arrays, meshes_to_concatenate
78
+ ):
79
+ for array in tree_arrays:
80
+ sharding = _get_named_sharding(array)
81
+ if sharding.mesh != expected_mesh:
82
+ raise ValueError(
83
+ "All arrays in each PyTree must have the same mesh. Got"
84
+ f" {sharding.mesh} vs. {expected_mesh}."
85
+ )
75
86
 
76
87
  # Validate that the meshes are compatible.
77
88
  reference_mesh = meshes_to_concatenate[0]
@@ -117,6 +128,19 @@ def concatenate_by_mesh_axis(
117
128
  ) -> jax.sharding.NamedSharding:
118
129
  reference_sharding = _get_named_sharding(arrays[0])
119
130
  reference_spec = reference_sharding.spec
131
+ for array in arrays[1:]:
132
+ sharding = _get_named_sharding(array)
133
+ if sharding.spec != reference_spec:
134
+ raise ValueError(
135
+ "Corresponding arrays across PyTrees must have the same"
136
+ f" PartitionSpec. Got {sharding.spec} vs. {reference_spec}."
137
+ )
138
+ if sharding.memory_kind != reference_sharding.memory_kind:
139
+ raise ValueError(
140
+ "Corresponding arrays across PyTrees must have the same"
141
+ f" memory_kind. Got {sharding.memory_kind} vs."
142
+ f" {reference_sharding.memory_kind}."
143
+ )
120
144
  return jax.sharding.NamedSharding(
121
145
  concatenated_mesh,
122
146
  reference_spec,
@@ -0,0 +1,142 @@
1
+ # PathwaysJobSet Builder
2
+
3
+ `PathwaysJobSet` generates and deploys Kubernetes `JobSet` resources (`jobset.x-k8s.io`) for running distributed Pathways workloads on Google Kubernetes Engine (GKE) without requiring custom controllers.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install 'pathwaysutils[gke]'
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ ```python
14
+ from pathwaysutils.experimental.gke import jobset
15
+
16
+ pw_jobset = jobset.PathwaysJobSet(
17
+ name="my-workload",
18
+ namespace="default",
19
+ pathways_dir="gs://my-bucket/pathways-scratch",
20
+ tpu_type="v5e",
21
+ topology="2x4",
22
+ num_slices=1,
23
+ )
24
+
25
+ # Export to YAML or deploy directly to the active cluster
26
+ pw_jobset.export_yaml("jobset.yaml")
27
+ pw_jobset.apply(recreate=True)
28
+ ```
29
+
30
+ ## Features
31
+
32
+ ### Elasticity & Multislice
33
+ Scale across multiple TPU slices with dynamic resilience:
34
+
35
+ ```python
36
+ pw_jobset = jobset.PathwaysJobSet(
37
+ name="elastic-v6e",
38
+ namespace="default",
39
+ pathways_dir="gs://my-bucket/scratch",
40
+ tpu_type="v6e",
41
+ topology="4x4",
42
+ num_slices=4,
43
+ elastic_slices=2, # Configures Pathways Proxy for dynamic slice recovery
44
+ )
45
+ ```
46
+
47
+ ### Heterogeneous Multi-Topology Slices
48
+ Combine multiple TPU slice topologies as separate worker `replicatedJobs`
49
+ within a single `JobSet`. Call `add_worker_topology` before worker mutators
50
+ (`add_colocated_python`, `add_gcsfuse`, or `with_custom_libtpu`) so they apply
51
+ across all worker templates:
52
+
53
+ ```python
54
+ pw_jobset = (
55
+ jobset.PathwaysJobSet(
56
+ name="heterogeneous-v5e",
57
+ namespace="default",
58
+ pathways_dir="gs://my-bucket/scratch",
59
+ tpu_type="v5e",
60
+ topology="4x8",
61
+ num_slices=1,
62
+ )
63
+ .add_worker_topology(
64
+ tpu_type="ct5lp-hightpu-8t",
65
+ topology="2x4",
66
+ num_slices=1,
67
+ )
68
+ )
69
+ ```
70
+
71
+ ### In-Pod User Workload
72
+ Run training or inference directly in the head pod alongside the Resource Manager and Proxy sidecars:
73
+
74
+ ```python
75
+ pw_jobset.add_user_workload(
76
+ image="us-docker.pkg.dev/my-project/images/my-trainer:latest",
77
+ command="python3 train.py --batch_size=32",
78
+ env={"LOG_LEVEL": "INFO"},
79
+ )
80
+ ```
81
+
82
+ ### Colocated Python Sidecar
83
+ Inject a sidecar container onto worker pods sharing memory with
84
+ `pathways-worker` for zero-copy tensor transfers:
85
+
86
+ ```python
87
+ pw_jobset.add_colocated_python(
88
+ image="us-docker.pkg.dev/my-project/images/worker-sidecar:latest",
89
+ shm_mount_path="/tmp/shared-memory",
90
+ shm_size_limit="16Gi",
91
+ )
92
+ ```
93
+
94
+ ### GCSFuse Cloud Storage Mounts
95
+ Mount GCS buckets to head, worker, or all containers:
96
+
97
+ ```python
98
+ pw_jobset.add_gcsfuse(
99
+ containers="all",
100
+ mount_path="/data",
101
+ bucket="my-data-bucket",
102
+ read_only=True,
103
+ )
104
+ ```
105
+
106
+ ### Custom `libtpu.so` Runtime Override
107
+ Set `TPU_LIBRARY_PATH` across the Pathways containers (`pathways-rm`,
108
+ `pathways-proxy`, and `pathways-worker` when `containers="all"`) without
109
+ injecting it into user or sidecar containers:
110
+
111
+ ```python
112
+ pw_jobset.with_custom_libtpu(
113
+ libtpu_path="/data/libtpu/libtpu.so",
114
+ containers="all",
115
+ )
116
+ ```
117
+
118
+ ### Container & Pod Spec Overrides
119
+ Customize arguments, environment variables, resources, or volumes on any container or pod:
120
+
121
+ ```python
122
+ pw_jobset = jobset.PathwaysJobSet(
123
+ ...,
124
+ rm_container_kwargs={"args": ["--v=1"]},
125
+ proxy_container_kwargs={"env": {"XLA_FLAGS": "--xla_dump_to=/tmp"}},
126
+ worker_container_kwargs={"args": ["--stderrthreshold=0"]},
127
+ worker_pod_spec_kwargs={"priority_class_name": "high-priority"},
128
+ )
129
+ ```
130
+
131
+ ## Serialization & Round-Tripping
132
+
133
+ Convert between Python objects, YAML files, and dictionaries:
134
+
135
+ ```python
136
+ # Save and load JobSet configurations
137
+ pw_jobset.export_yaml("jobset.yaml")
138
+ loaded_jobset = jobset.PathwaysJobSet.import_yaml("jobset.yaml")
139
+
140
+ # Inspect raw Kubernetes resource dictionary
141
+ config_dict = pw_jobset.to_dict()
142
+ ```