spmd_types 0.2.1__tar.gz → 0.2.3__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.
- {spmd_types-0.2.1/spmd_types.egg-info → spmd_types-0.2.3}/PKG-INFO +34 -17
- spmd_types-0.2.3/README.md +69 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/pyproject.toml +1 -2
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/__init__.py +5 -1
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_checker/__init__.py +148 -99
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_collectives.py +38 -27
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_local.py +35 -28
- spmd_types-0.2.3/spmd_types/_mesh.py +211 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_mesh_axis.py +44 -16
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_state.py +51 -14
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/checker.py +2 -2
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/runtime.py +178 -42
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/types.py +77 -20
- {spmd_types-0.2.1 → spmd_types-0.2.3/spmd_types.egg-info}/PKG-INFO +34 -17
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types.egg-info/SOURCES.txt +0 -1
- spmd_types-0.2.1/README.md +0 -51
- spmd_types-0.2.1/spmd_types/_mesh.py +0 -95
- spmd_types-0.2.1/spmd_types.egg-info/requires.txt +0 -1
- {spmd_types-0.2.1 → spmd_types-0.2.3}/LICENSE +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/setup.cfg +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_backward_hooks.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_dist.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_dtensor.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_dtensor_checker.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_dtype_utils.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_frame.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_mesh_region.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_raw_dist.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_reinterpret_mesh.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_scalar.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_scalar_sentinel.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_test_utils.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_testing.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_traceback.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_type_attr.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types/_vmap.py +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types.egg-info/dependency_links.txt +0 -0
- {spmd_types-0.2.1 → spmd_types-0.2.3}/spmd_types.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spmd_types
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A type system for distributed (SPMD) tensor computations in PyTorch
|
|
5
5
|
Author: Meta Platforms, Inc.
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -47,17 +47,31 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
47
47
|
Requires-Python: >=3.10
|
|
48
48
|
Description-Content-Type: text/markdown
|
|
49
49
|
License-File: LICENSE
|
|
50
|
-
Requires-Dist: torch>=2.10.0
|
|
51
50
|
Dynamic: license-file
|
|
52
51
|
|
|
53
|
-
# spmd_types
|
|
52
|
+
# `spmd_types`
|
|
54
53
|
|
|
55
|
-
A type system for distributed (SPMD) tensor computations in PyTorch.
|
|
54
|
+
A type system for distributed (SPMD) tensor computations in PyTorch. This
|
|
55
|
+
package provides two type systems:
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
* **Local SPMD types**, which allow you to use Megatron-style differentiable
|
|
58
|
+
collectives in a safe way by tracking whether or not your backward gradients
|
|
59
|
+
are pending reduction or not.
|
|
60
|
+
|
|
61
|
+
* **Global SPMD types**, a DTensor-like abstraction for writing code that has
|
|
62
|
+
the same semantics whether run on a single device or in a distributed
|
|
63
|
+
fashion, but with explicit communication operations so you are never
|
|
64
|
+
guessing when a redistribute occurs.
|
|
65
|
+
|
|
66
|
+
In both cases, the SPMD types makes it possible for you to check that your
|
|
67
|
+
code computes correct gradients (local SPMD) or gives equivalent results
|
|
68
|
+
across different parallelizations (global SPMD), without having to actually
|
|
69
|
+
run a full E2E distributed training run to check for loss matching.
|
|
70
|
+
|
|
71
|
+
The goal of this package is to provide a flexible type system that can
|
|
72
|
+
typecheck realistic training code. We have used local SPMD types to typecheck
|
|
73
|
+
a realistic pretraining codebase, and global SPMD types is actively under
|
|
74
|
+
construction!
|
|
61
75
|
|
|
62
76
|
## Installation
|
|
63
77
|
|
|
@@ -71,6 +85,7 @@ pip install spmd_types
|
|
|
71
85
|
import torch
|
|
72
86
|
import torch.distributed as dist
|
|
73
87
|
import spmd_types as spmd
|
|
88
|
+
import spmd_types.checker
|
|
74
89
|
from torch.distributed.device_mesh import init_device_mesh
|
|
75
90
|
|
|
76
91
|
# Set up a fake process group (no GPUs needed)
|
|
@@ -79,12 +94,12 @@ mesh = init_device_mesh("cpu", (2, 4), mesh_dim_names=("dp", "tp"))
|
|
|
79
94
|
dp = mesh.get_group("dp")
|
|
80
95
|
tp = mesh.get_group("tp")
|
|
81
96
|
|
|
82
|
-
with spmd.set_current_mesh(mesh), spmd.typecheck():
|
|
97
|
+
with spmd.set_current_mesh(mesh), spmd.checker.typecheck():
|
|
83
98
|
x = torch.randn(4)
|
|
84
|
-
spmd.assert_type(x, {dp: spmd.R, tp: spmd.P})
|
|
99
|
+
spmd.assert_type(x, {dp: spmd.R, tp: spmd.P}) # R on dp, partial on tp
|
|
85
100
|
y = spmd.all_reduce(x, tp, src=spmd.P, dst=spmd.R) # sum across tp ranks
|
|
86
|
-
spmd.assert_type(y, {dp: spmd.R, tp: spmd.R})
|
|
87
|
-
z = torch.mul(y, y)
|
|
101
|
+
spmd.assert_type(y, {dp: spmd.R, tp: spmd.R}) # now replicated everywhere
|
|
102
|
+
z = torch.mul(y, y) # type inference: R * R -> R
|
|
88
103
|
spmd.assert_type(z, {dp: spmd.R, tp: spmd.R})
|
|
89
104
|
|
|
90
105
|
dist.destroy_process_group()
|
|
@@ -92,12 +107,14 @@ dist.destroy_process_group()
|
|
|
92
107
|
|
|
93
108
|
## Documentation
|
|
94
109
|
|
|
95
|
-
See [
|
|
96
|
-
Megatron-derived training frameworks
|
|
110
|
+
See [Local SPMD types](docs/local_spmd_types.md) for a hands-on guide on
|
|
111
|
+
porting Megatron-derived training frameworks, including the Megatron-to-spmd_types
|
|
112
|
+
function mapping table and advice on Invariant vs Replicate.
|
|
97
113
|
|
|
98
|
-
See [
|
|
99
|
-
|
|
114
|
+
See [Design](docs/design.md) for the full type system specification, including
|
|
115
|
+
local vs global SPMD modes, collective signatures with diagrams, forward-backward
|
|
116
|
+
pairs, expert mode, cross-mesh compatibility, and partition spec redistribute.
|
|
100
117
|
|
|
101
118
|
## License
|
|
102
119
|
|
|
103
|
-
BSD 3-Clause License. See [
|
|
120
|
+
BSD 3-Clause License. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# `spmd_types`
|
|
2
|
+
|
|
3
|
+
A type system for distributed (SPMD) tensor computations in PyTorch. This
|
|
4
|
+
package provides two type systems:
|
|
5
|
+
|
|
6
|
+
* **Local SPMD types**, which allow you to use Megatron-style differentiable
|
|
7
|
+
collectives in a safe way by tracking whether or not your backward gradients
|
|
8
|
+
are pending reduction or not.
|
|
9
|
+
|
|
10
|
+
* **Global SPMD types**, a DTensor-like abstraction for writing code that has
|
|
11
|
+
the same semantics whether run on a single device or in a distributed
|
|
12
|
+
fashion, but with explicit communication operations so you are never
|
|
13
|
+
guessing when a redistribute occurs.
|
|
14
|
+
|
|
15
|
+
In both cases, the SPMD types makes it possible for you to check that your
|
|
16
|
+
code computes correct gradients (local SPMD) or gives equivalent results
|
|
17
|
+
across different parallelizations (global SPMD), without having to actually
|
|
18
|
+
run a full E2E distributed training run to check for loss matching.
|
|
19
|
+
|
|
20
|
+
The goal of this package is to provide a flexible type system that can
|
|
21
|
+
typecheck realistic training code. We have used local SPMD types to typecheck
|
|
22
|
+
a realistic pretraining codebase, and global SPMD types is actively under
|
|
23
|
+
construction!
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install spmd_types
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import torch
|
|
35
|
+
import torch.distributed as dist
|
|
36
|
+
import spmd_types as spmd
|
|
37
|
+
import spmd_types.checker
|
|
38
|
+
from torch.distributed.device_mesh import init_device_mesh
|
|
39
|
+
|
|
40
|
+
# Set up a fake process group (no GPUs needed)
|
|
41
|
+
dist.init_process_group(backend="fake", rank=0, world_size=8)
|
|
42
|
+
mesh = init_device_mesh("cpu", (2, 4), mesh_dim_names=("dp", "tp"))
|
|
43
|
+
dp = mesh.get_group("dp")
|
|
44
|
+
tp = mesh.get_group("tp")
|
|
45
|
+
|
|
46
|
+
with spmd.set_current_mesh(mesh), spmd.checker.typecheck():
|
|
47
|
+
x = torch.randn(4)
|
|
48
|
+
spmd.assert_type(x, {dp: spmd.R, tp: spmd.P}) # R on dp, partial on tp
|
|
49
|
+
y = spmd.all_reduce(x, tp, src=spmd.P, dst=spmd.R) # sum across tp ranks
|
|
50
|
+
spmd.assert_type(y, {dp: spmd.R, tp: spmd.R}) # now replicated everywhere
|
|
51
|
+
z = torch.mul(y, y) # type inference: R * R -> R
|
|
52
|
+
spmd.assert_type(z, {dp: spmd.R, tp: spmd.R})
|
|
53
|
+
|
|
54
|
+
dist.destroy_process_group()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Documentation
|
|
58
|
+
|
|
59
|
+
See [Local SPMD types](docs/local_spmd_types.md) for a hands-on guide on
|
|
60
|
+
porting Megatron-derived training frameworks, including the Megatron-to-spmd_types
|
|
61
|
+
function mapping table and advice on Invariant vs Replicate.
|
|
62
|
+
|
|
63
|
+
See [Design](docs/design.md) for the full type system specification, including
|
|
64
|
+
local vs global SPMD modes, collective signatures with diagrams, forward-backward
|
|
65
|
+
pairs, expert mode, cross-mesh compatibility, and partition spec redistribute.
|
|
66
|
+
|
|
67
|
+
## License
|
|
68
|
+
|
|
69
|
+
BSD 3-Clause License. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "spmd_types"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "A type system for distributed (SPMD) tensor computations in PyTorch"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {file = "LICENSE"}
|
|
@@ -24,7 +24,6 @@ classifiers = [
|
|
|
24
24
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
|
-
"torch>=2.10.0",
|
|
28
27
|
]
|
|
29
28
|
|
|
30
29
|
[project.urls]
|
|
@@ -41,7 +41,6 @@ from spmd_types._state import ( # noqa: F401
|
|
|
41
41
|
current_mesh_all_names,
|
|
42
42
|
current_mesh_names,
|
|
43
43
|
is_type_checking,
|
|
44
|
-
no_typecheck,
|
|
45
44
|
)
|
|
46
45
|
from spmd_types._traceback import traceback_filtering # noqa: F401
|
|
47
46
|
from spmd_types._type_attr import ( # noqa: F401
|
|
@@ -53,12 +52,16 @@ from spmd_types._type_attr import ( # noqa: F401
|
|
|
53
52
|
# Collectives and operations -- runtime API (no _checker dependency)
|
|
54
53
|
from spmd_types.runtime import ( # noqa: F401
|
|
55
54
|
assert_local_type,
|
|
55
|
+
assert_local_type_like,
|
|
56
56
|
assert_type,
|
|
57
57
|
assert_type_like,
|
|
58
|
+
get_partition_spec,
|
|
59
|
+
has_local_type,
|
|
58
60
|
Infer,
|
|
59
61
|
local,
|
|
60
62
|
local_map,
|
|
61
63
|
mutate_type,
|
|
64
|
+
no_typecheck,
|
|
62
65
|
register_autograd_function,
|
|
63
66
|
register_decomposition,
|
|
64
67
|
register_local_autograd_function,
|
|
@@ -84,6 +87,7 @@ from spmd_types.types import ( # noqa: F401
|
|
|
84
87
|
Replicate,
|
|
85
88
|
S,
|
|
86
89
|
Shard,
|
|
90
|
+
SpmdType,
|
|
87
91
|
SpmdTypeError,
|
|
88
92
|
TensorSharding,
|
|
89
93
|
V,
|
|
@@ -68,14 +68,13 @@ from spmd_types.runtime import ( # noqa: F401
|
|
|
68
68
|
get_partition_spec,
|
|
69
69
|
has_local_type,
|
|
70
70
|
mutate_type,
|
|
71
|
+
no_typecheck,
|
|
71
72
|
register_autograd_function,
|
|
72
73
|
register_decomposition,
|
|
73
74
|
register_local_autograd_function,
|
|
74
75
|
trace,
|
|
75
76
|
)
|
|
76
77
|
from spmd_types.types import (
|
|
77
|
-
_canonicalize_shard,
|
|
78
|
-
_check_orthogonality,
|
|
79
78
|
DeviceMeshAxis,
|
|
80
79
|
DTensorPropagationError,
|
|
81
80
|
format_axis,
|
|
@@ -885,13 +884,15 @@ class _OpSpec:
|
|
|
885
884
|
tensor_kwargs: Kwarg names that are tensor inputs.
|
|
886
885
|
tensor_varargs_from: If set, all positional args from this index onward
|
|
887
886
|
are tensor inputs (for ops with *args like einsum).
|
|
887
|
+
nonlinear_args: Positional arg indices whose operand enters the op
|
|
888
|
+
nonlinearly and therefore must not be P.
|
|
888
889
|
"""
|
|
889
890
|
|
|
890
891
|
linearity: OpLinearity
|
|
891
892
|
tensor_args: tuple[int, ...] = ()
|
|
892
893
|
tensor_kwargs: tuple[str, ...] = ()
|
|
893
894
|
tensor_varargs_from: int | None = None
|
|
894
|
-
|
|
895
|
+
nonlinear_args: tuple[int, ...] = ()
|
|
895
896
|
|
|
896
897
|
|
|
897
898
|
_OP_REGISTRY: dict[Callable, _OpSpec] = {
|
|
@@ -941,6 +942,8 @@ _OP_REGISTRY: dict[Callable, _OpSpec] = {
|
|
|
941
942
|
torch.Tensor.mean: _OpSpec(OpLinearity.LINEAR, (0,)),
|
|
942
943
|
torch.nansum: _OpSpec(OpLinearity.LINEAR, (0,)),
|
|
943
944
|
torch.nanmean: _OpSpec(OpLinearity.LINEAR, (0,)),
|
|
945
|
+
torch.cumsum: _OpSpec(OpLinearity.LINEAR, (0,)),
|
|
946
|
+
torch.Tensor.cumsum: _OpSpec(OpLinearity.LINEAR, (0,)),
|
|
944
947
|
# =================================================================
|
|
945
948
|
# LINEAR -- concat / stack (tensor list at pos 0)
|
|
946
949
|
# =================================================================
|
|
@@ -1028,31 +1031,68 @@ _OP_REGISTRY: dict[Callable, _OpSpec] = {
|
|
|
1028
1031
|
torch.Tensor.repeat_interleave: _OpSpec(OpLinearity.LINEAR, (0,)),
|
|
1029
1032
|
torch.Tensor.unfold: _OpSpec(OpLinearity.LINEAR, (0,)),
|
|
1030
1033
|
# =================================================================
|
|
1031
|
-
#
|
|
1034
|
+
# MULTILINEAR with nonlinear_args -- division (linear in numerator only)
|
|
1035
|
+
# Division is multiplicative, not additive: a lone P numerator scaled by an
|
|
1036
|
+
# R/scalar divisor stays P, while LINEAR would reject this as affine.
|
|
1037
|
+
# nonlinear_args additionally forbids P in the divisor because 1/y is
|
|
1038
|
+
# nonlinear in y.
|
|
1032
1039
|
# =================================================================
|
|
1033
|
-
torch.div: _OpSpec(
|
|
1040
|
+
torch.div: _OpSpec(
|
|
1041
|
+
OpLinearity.MULTILINEAR,
|
|
1042
|
+
(0, 1),
|
|
1043
|
+
("input", "other"),
|
|
1044
|
+
nonlinear_args=(1,),
|
|
1045
|
+
),
|
|
1034
1046
|
torch.divide: _OpSpec(
|
|
1035
|
-
OpLinearity.
|
|
1047
|
+
OpLinearity.MULTILINEAR,
|
|
1048
|
+
(0, 1),
|
|
1049
|
+
("input", "other"),
|
|
1050
|
+
nonlinear_args=(1,),
|
|
1036
1051
|
),
|
|
1037
1052
|
torch.true_divide: _OpSpec(
|
|
1038
|
-
OpLinearity.
|
|
1053
|
+
OpLinearity.MULTILINEAR,
|
|
1054
|
+
(0, 1),
|
|
1055
|
+
("input", "other"),
|
|
1056
|
+
nonlinear_args=(1,),
|
|
1057
|
+
),
|
|
1058
|
+
torch.Tensor.div: _OpSpec(
|
|
1059
|
+
OpLinearity.MULTILINEAR,
|
|
1060
|
+
(0, 1),
|
|
1061
|
+
("other",),
|
|
1062
|
+
nonlinear_args=(1,),
|
|
1039
1063
|
),
|
|
1040
|
-
torch.Tensor.div: _OpSpec(OpLinearity.LINEAR, (0, 1), ("other",), fixed_args=(1,)),
|
|
1041
1064
|
torch.Tensor.divide: _OpSpec(
|
|
1042
|
-
OpLinearity.
|
|
1065
|
+
OpLinearity.MULTILINEAR,
|
|
1066
|
+
(0, 1),
|
|
1067
|
+
("other",),
|
|
1068
|
+
nonlinear_args=(1,),
|
|
1043
1069
|
),
|
|
1044
1070
|
torch.Tensor.true_divide: _OpSpec(
|
|
1045
|
-
OpLinearity.
|
|
1071
|
+
OpLinearity.MULTILINEAR,
|
|
1072
|
+
(0, 1),
|
|
1073
|
+
("other",),
|
|
1074
|
+
nonlinear_args=(1,),
|
|
1075
|
+
),
|
|
1076
|
+
torch.Tensor.div_: _OpSpec(
|
|
1077
|
+
OpLinearity.MULTILINEAR,
|
|
1078
|
+
(0, 1),
|
|
1079
|
+
("other",),
|
|
1080
|
+
nonlinear_args=(1,),
|
|
1046
1081
|
),
|
|
1047
|
-
torch.Tensor.div_: _OpSpec(OpLinearity.LINEAR, (0, 1), ("other",), fixed_args=(1,)),
|
|
1048
1082
|
torch.Tensor.__truediv__: _OpSpec(
|
|
1049
|
-
OpLinearity.
|
|
1083
|
+
OpLinearity.MULTILINEAR,
|
|
1084
|
+
(0, 1),
|
|
1085
|
+
("other",),
|
|
1086
|
+
nonlinear_args=(1,),
|
|
1050
1087
|
),
|
|
1051
1088
|
torch.Tensor.__rtruediv__: _OpSpec(
|
|
1052
|
-
OpLinearity.
|
|
1089
|
+
OpLinearity.MULTILINEAR, (0, 1), ("other",), nonlinear_args=(0,)
|
|
1053
1090
|
),
|
|
1054
1091
|
torch.Tensor.__itruediv__: _OpSpec(
|
|
1055
|
-
OpLinearity.
|
|
1092
|
+
OpLinearity.MULTILINEAR,
|
|
1093
|
+
(0, 1),
|
|
1094
|
+
("other",),
|
|
1095
|
+
nonlinear_args=(1,),
|
|
1056
1096
|
),
|
|
1057
1097
|
# =================================================================
|
|
1058
1098
|
# MULTILINEAR -- multiplication
|
|
@@ -1539,80 +1579,40 @@ def _set_result_type(result: object, output_type: LocalSpmdType) -> None:
|
|
|
1539
1579
|
_apply(item)
|
|
1540
1580
|
|
|
1541
1581
|
|
|
1542
|
-
def
|
|
1582
|
+
def _validate_nonlinear_args(
|
|
1543
1583
|
func: Callable,
|
|
1544
1584
|
args: tuple,
|
|
1545
|
-
kwargs: dict,
|
|
1546
1585
|
spec: _OpSpec,
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
"""Filter fixed_args for LINEAR ops when Partial is present.
|
|
1550
|
-
|
|
1551
|
-
``fixed_args`` lists positional arg indices that must be held fixed (not P)
|
|
1552
|
-
for the op to be linear in the remaining args. For example, ``div(a, b)``
|
|
1553
|
-
is linear in ``a`` when ``b`` is fixed, so ``fixed_args=(1,)``.
|
|
1554
|
-
|
|
1555
|
-
When P is present among the non-fixed tensor args:
|
|
1556
|
-
1. Validate that tensor args at fixed_args positions don't have P on any axis.
|
|
1557
|
-
2. Exclude their types from the returned list so LINEAR inference sees only
|
|
1558
|
-
the "free" args (and doesn't reject P + R mixing).
|
|
1559
|
-
|
|
1560
|
-
When no P is present in the free args, return the original list unchanged
|
|
1561
|
-
so normal inference rules apply (e.g., R + V -> V).
|
|
1586
|
+
) -> None:
|
|
1587
|
+
"""Validate that operands entering the op nonlinearly are not Partial.
|
|
1562
1588
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
kwargs: Keyword arguments to the torch operation.
|
|
1567
|
-
spec: The op specification (must have non-empty fixed_args).
|
|
1568
|
-
input_types_list: The already-collected input types list.
|
|
1589
|
+
Division is the current user: ``x / y`` is linear (scaling) in ``x`` only,
|
|
1590
|
+
since ``1/y`` is nonlinear in ``y``. Nonlinear operands may be untyped
|
|
1591
|
+
Python scalars or typed non-Partial values, but they cannot be P.
|
|
1569
1592
|
"""
|
|
1570
|
-
# Identify which input_types_list entries came from fixed_args positions.
|
|
1571
|
-
# We re-walk args to figure out which types are "fixed" vs "free".
|
|
1572
|
-
fixed_positions = set(spec.fixed_args)
|
|
1573
|
-
free_types: list[LocalSpmdType] = []
|
|
1574
|
-
fixed_types: list[LocalSpmdType] = []
|
|
1575
1593
|
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
if isinstance(val, torch.Tensor):
|
|
1580
|
-
if i in fixed_positions:
|
|
1581
|
-
fixed_types.append(get_local_type(val))
|
|
1582
|
-
else:
|
|
1583
|
-
free_types.append(get_local_type(val))
|
|
1584
|
-
elif isinstance(val, (list, tuple)):
|
|
1585
|
-
for item in val:
|
|
1586
|
-
if isinstance(item, torch.Tensor):
|
|
1587
|
-
if i in fixed_positions:
|
|
1588
|
-
fixed_types.append(get_local_type(item))
|
|
1589
|
-
else:
|
|
1590
|
-
free_types.append(get_local_type(item))
|
|
1591
|
-
|
|
1592
|
-
# Check if P is present in any free arg on any axis
|
|
1593
|
-
has_p_in_free = any(P in typ.values() for typ in free_types)
|
|
1594
|
-
# Also check default V (missing from dict means V, not P) -- V != P, so fine.
|
|
1595
|
-
|
|
1596
|
-
if not has_p_in_free:
|
|
1597
|
-
# No P in free args -- normal inference, include all types.
|
|
1598
|
-
return input_types_list
|
|
1599
|
-
|
|
1600
|
-
# P is present in free args -- validate fixed args don't have P
|
|
1601
|
-
for fixed_type in fixed_types:
|
|
1602
|
-
for axis, typ in fixed_type.items():
|
|
1603
|
-
if typ is P:
|
|
1594
|
+
def _check_type(typ: LocalSpmdType) -> None:
|
|
1595
|
+
for axis, axis_type in typ.items():
|
|
1596
|
+
if axis_type is P:
|
|
1604
1597
|
raise SpmdTypeError(
|
|
1605
|
-
f"{func.__name__}: Partial type in
|
|
1606
|
-
f"
|
|
1607
|
-
f"
|
|
1598
|
+
f"{func.__name__}: Partial type in nonlinear argument "
|
|
1599
|
+
f"on axis {format_axis(axis)} is not allowed. Reduce or "
|
|
1600
|
+
f"convert the Partial operand before using it in a "
|
|
1601
|
+
f"nonlinear position."
|
|
1608
1602
|
)
|
|
1609
1603
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1604
|
+
def _check_value(val: object) -> None:
|
|
1605
|
+
if isinstance(val, torch.Tensor):
|
|
1606
|
+
_check_type(get_local_type(val))
|
|
1607
|
+
elif isinstance(val, Scalar):
|
|
1608
|
+
_check_type(get_local_type(val))
|
|
1609
|
+
elif isinstance(val, (list, tuple)):
|
|
1610
|
+
for item in val:
|
|
1611
|
+
_check_value(item)
|
|
1612
|
+
|
|
1613
|
+
for i in spec.nonlinear_args:
|
|
1614
|
+
if i < len(args):
|
|
1615
|
+
_check_value(args[i])
|
|
1616
1616
|
|
|
1617
1617
|
|
|
1618
1618
|
# Every function in this registry must accept (x: Tensor, axis, *, src=..., dst=...)
|
|
@@ -1680,7 +1680,7 @@ def _set_result_partition_spec(
|
|
|
1680
1680
|
_set_partition_spec(item, spec)
|
|
1681
1681
|
|
|
1682
1682
|
|
|
1683
|
-
def _collect_shard_axes(
|
|
1683
|
+
def _collect_shard_axes( # noqa: C901
|
|
1684
1684
|
partition_specs: list[PartitionSpec | None],
|
|
1685
1685
|
global_axes: set[MeshAxis],
|
|
1686
1686
|
) -> tuple[list[MeshAxis], dict[MeshAxis, set[MeshAxis]]]:
|
|
@@ -2086,11 +2086,16 @@ class _ShardPropagator:
|
|
|
2086
2086
|
"""
|
|
2087
2087
|
mesh = self._get_mesh(axis.size())
|
|
2088
2088
|
|
|
2089
|
+
from torch.utils._python_dispatch import _disable_current_modes
|
|
2090
|
+
|
|
2091
|
+
# Keep checker-internal fake DTensor propagation invisible to active
|
|
2092
|
+
# TorchDispatchModes such as selective activation checkpointing's
|
|
2093
|
+
# caching mode.
|
|
2089
2094
|
# Disable LocalTensorMode during meta DTensor propagation.
|
|
2090
2095
|
# DTensor.from_local calls mesh.get_coordinate() which, under
|
|
2091
2096
|
# LocalTensorMode(N), tries to map all N simulated ranks into
|
|
2092
2097
|
# the propagator's fake 1-D mesh.
|
|
2093
|
-
with maybe_disable_local_tensor_mode():
|
|
2098
|
+
with maybe_disable_local_tensor_mode(), _disable_current_modes():
|
|
2094
2099
|
meta_args = torch.utils._pytree.tree_map(
|
|
2095
2100
|
lambda x: self._to_meta_dtensor(x, axis, mesh), args
|
|
2096
2101
|
)
|
|
@@ -2527,6 +2532,8 @@ def _get_autograd_function_class(func) -> type | None:
|
|
|
2527
2532
|
def _validate_partition_spec_for_global_spmd(
|
|
2528
2533
|
local_type: LocalSpmdType,
|
|
2529
2534
|
spec: PartitionSpec | None,
|
|
2535
|
+
*,
|
|
2536
|
+
is_global_axis: Callable[[MeshAxis], bool],
|
|
2530
2537
|
) -> None:
|
|
2531
2538
|
"""Validate PartitionSpec <-> local type consistency.
|
|
2532
2539
|
|
|
@@ -2539,10 +2546,12 @@ def _validate_partition_spec_for_global_spmd(
|
|
|
2539
2546
|
Args:
|
|
2540
2547
|
local_type: The tensor's local SPMD type dict.
|
|
2541
2548
|
spec: The tensor's PartitionSpec (None if absent).
|
|
2549
|
+
is_global_axis: Returns whether an axis requires global PartitionSpec
|
|
2550
|
+
validation in the current mesh context.
|
|
2542
2551
|
"""
|
|
2543
2552
|
spec_axes = spec.axes_with_partition_spec() if spec is not None else set()
|
|
2544
2553
|
for axis, typ in local_type.items():
|
|
2545
|
-
if typ is V and axis not in spec_axes:
|
|
2554
|
+
if is_global_axis(axis) and typ is V and axis not in spec_axes:
|
|
2546
2555
|
ax = format_axis(axis)
|
|
2547
2556
|
raise SpmdTypeError(
|
|
2548
2557
|
f"Tensor has Varying type on axis {ax} but no PartitionSpec entry. "
|
|
@@ -2551,7 +2560,7 @@ def _validate_partition_spec_for_global_spmd(
|
|
|
2551
2560
|
)
|
|
2552
2561
|
for axis in spec_axes:
|
|
2553
2562
|
typ = local_type.get(axis)
|
|
2554
|
-
if typ is not V:
|
|
2563
|
+
if is_global_axis(axis) and typ is not V:
|
|
2555
2564
|
ax = format_axis(axis)
|
|
2556
2565
|
raise SpmdTypeError(
|
|
2557
2566
|
f"Tensor has PartitionSpec on axis {ax} but local type is "
|
|
@@ -2619,6 +2628,23 @@ class _SpmdTypeMode(torch.overrides.TorchFunctionMode):
|
|
|
2619
2628
|
|
|
2620
2629
|
self._disabled = False
|
|
2621
2630
|
|
|
2631
|
+
def _is_global_axis(self, axis: MeshAxis) -> bool:
|
|
2632
|
+
if self._local:
|
|
2633
|
+
return False
|
|
2634
|
+
local_axes = _state._current_local_axes()
|
|
2635
|
+
if not local_axes:
|
|
2636
|
+
return True
|
|
2637
|
+
mesh = _state.current_mesh()
|
|
2638
|
+
if mesh is None or axis not in mesh:
|
|
2639
|
+
raise SpmdTypeError(
|
|
2640
|
+
f"Axis {format_axis(axis)} is not in the current mesh, so its "
|
|
2641
|
+
"local/global typechecking policy is undefined"
|
|
2642
|
+
)
|
|
2643
|
+
return axis not in local_axes
|
|
2644
|
+
|
|
2645
|
+
def _global_axes_for(self, axes: Iterable[MeshAxis]) -> set[MeshAxis]:
|
|
2646
|
+
return {axis for axis in axes if self._is_global_axis(axis)}
|
|
2647
|
+
|
|
2622
2648
|
def __torch_function__(self, func, types, args=(), kwargs=None): # noqa: C901
|
|
2623
2649
|
kwargs = kwargs or {}
|
|
2624
2650
|
# Paused via no_typecheck(): run without type checking.
|
|
@@ -2675,7 +2701,11 @@ class _SpmdTypeMode(torch.overrides.TorchFunctionMode):
|
|
|
2675
2701
|
# Global SPMD validation.
|
|
2676
2702
|
if not self._local:
|
|
2677
2703
|
for local_type, spec in zip(info.tensor_types, info.partition_specs):
|
|
2678
|
-
_validate_partition_spec_for_global_spmd(
|
|
2704
|
+
_validate_partition_spec_for_global_spmd(
|
|
2705
|
+
local_type,
|
|
2706
|
+
spec,
|
|
2707
|
+
is_global_axis=self._is_global_axis,
|
|
2708
|
+
)
|
|
2679
2709
|
|
|
2680
2710
|
# Typecheck-registered autograd function: dispatch to typecheck_forward
|
|
2681
2711
|
# INSTEAD of executing func. The mode is popped off the
|
|
@@ -2740,6 +2770,29 @@ class _SpmdTypeMode(torch.overrides.TorchFunctionMode):
|
|
|
2740
2770
|
# Resolve axis to a list of matching axes on the tensor.
|
|
2741
2771
|
# Direct match: [axis]. Flattened decomposition: [sub1, sub2, ...].
|
|
2742
2772
|
resolved_axes = _resolve_collective_axes(axis, local_type)
|
|
2773
|
+
collective_axes = resolved_axes or [axis]
|
|
2774
|
+
global_collective_axes = [
|
|
2775
|
+
ax for ax in collective_axes if self._is_global_axis(ax)
|
|
2776
|
+
]
|
|
2777
|
+
if global_collective_axes and len(global_collective_axes) != len(
|
|
2778
|
+
collective_axes
|
|
2779
|
+
):
|
|
2780
|
+
global_axis_set = set(global_collective_axes)
|
|
2781
|
+
local_collective_axes = [
|
|
2782
|
+
ax for ax in collective_axes if ax not in global_axis_set
|
|
2783
|
+
]
|
|
2784
|
+
local_names = ", ".join(
|
|
2785
|
+
format_axis(ax) for ax in local_collective_axes
|
|
2786
|
+
)
|
|
2787
|
+
global_names = ", ".join(
|
|
2788
|
+
format_axis(ax) for ax in global_collective_axes
|
|
2789
|
+
)
|
|
2790
|
+
raise SpmdTypeError(
|
|
2791
|
+
f"Collective axis {format_axis(axis)} spans mesh axes with "
|
|
2792
|
+
f"mixed typechecking policies: local axes ({local_names}) "
|
|
2793
|
+
f"and global axes ({global_names}). Pass a collective axis "
|
|
2794
|
+
f"whose component axes are all local or all global."
|
|
2795
|
+
)
|
|
2743
2796
|
|
|
2744
2797
|
if resolved_axes is not None:
|
|
2745
2798
|
# Validate input types on all resolved axes
|
|
@@ -2770,7 +2823,7 @@ class _SpmdTypeMode(torch.overrides.TorchFunctionMode):
|
|
|
2770
2823
|
output_type[ax] = local_dst
|
|
2771
2824
|
_set_local_type(result, output_type)
|
|
2772
2825
|
|
|
2773
|
-
if
|
|
2826
|
+
if global_collective_axes:
|
|
2774
2827
|
input_spec = get_partition_spec(x)
|
|
2775
2828
|
# Find S(dim) for this axis in the input spec, validating
|
|
2776
2829
|
# it is innermost in its multi-axis group.
|
|
@@ -2902,10 +2955,8 @@ class _SpmdTypeMode(torch.overrides.TorchFunctionMode):
|
|
|
2902
2955
|
linearity = (
|
|
2903
2956
|
spec.linearity if spec is not None else OpLinearity.NONLINEAR
|
|
2904
2957
|
)
|
|
2905
|
-
if spec is not None and spec.
|
|
2906
|
-
|
|
2907
|
-
func, args, kwargs, spec, input_types_list
|
|
2908
|
-
)
|
|
2958
|
+
if spec is not None and spec.nonlinear_args:
|
|
2959
|
+
_validate_nonlinear_args(func, original_args, spec)
|
|
2909
2960
|
# NB: output_type may be further updated because global
|
|
2910
2961
|
# shard propagation below may promote local type V to P.
|
|
2911
2962
|
output_type = infer_output_type(
|
|
@@ -2914,15 +2965,16 @@ class _SpmdTypeMode(torch.overrides.TorchFunctionMode):
|
|
|
2914
2965
|
)
|
|
2915
2966
|
|
|
2916
2967
|
# Stage 3: global shard propagation.
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
all_axes.update(typ.keys())
|
|
2968
|
+
all_axes: set[MeshAxis] = set()
|
|
2969
|
+
for typ in input_types_list:
|
|
2970
|
+
all_axes.update(typ.keys())
|
|
2971
|
+
global_axes = self._global_axes_for(all_axes)
|
|
2972
|
+
if global_axes:
|
|
2923
2973
|
global_shard_axes, shard_edges = _collect_shard_axes(
|
|
2924
|
-
partition_specs,
|
|
2974
|
+
partition_specs, global_axes
|
|
2925
2975
|
)
|
|
2976
|
+
else:
|
|
2977
|
+
global_shard_axes, shard_edges = [], {}
|
|
2926
2978
|
|
|
2927
2979
|
# Validate mutation safety for in-place/out operations.
|
|
2928
2980
|
mutated = _get_mutated_tensors(func, args, kwargs, result)
|
|
@@ -3084,9 +3136,6 @@ def typecheck(
|
|
|
3084
3136
|
_remove_autograd_apply_patch()
|
|
3085
3137
|
|
|
3086
3138
|
|
|
3087
|
-
from spmd_types._state import no_typecheck # noqa: F401
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
3139
|
class _SpmdTypeBackwardCompatibleMode:
|
|
3091
3140
|
"""Backwards-compatible wrapper around ``typecheck()``.
|
|
3092
3141
|
|