Diff Coverage

Diff: origin/master...HEAD, staged and unstaged changes

Source File Diff Coverage (%) Missing Lines
hyper_parallel/core/dtensor/dtensor.py 100%  
hyper_parallel/core/dtensor/dtensor_base.py 68.8% 43-45,51,54,56,131,133,166-168,192-194,215,225,236,292,305-308,325,335,345,354,359-361,377-379,385,388
hyper_parallel/platform/mindspore/platform.py 0.0% 989,995
hyper_parallel/platform/torch/platform.py 100%  
hyper_parallel/core/dtensor/dtensor_base.py
39
40
41
42
43
44
45
46
47
48
49
            shape: Optional logical global tensor shape.
        """
        if isinstance(local_tensor, DTensorBase):
            # Copy from existing DTensorBase — use alias_placements to preserve multi-axis ordering
            t = Tensor._make_subclass(cls, local_tensor._local_tensor, local_tensor._local_tensor.requires_grad)
            copy_placements = local_tensor.layout.alias_placements if local_tensor.layout else local_tensor.placements
            t.__init_data__(
                local_tensor._local_tensor,
                local_tensor.device_mesh,
                copy_placements,
                shape=getattr(local_tensor, "_global_shape", None),
47
48
49
50
51
52
53
54
55
56
57
58
59
60
                local_tensor.device_mesh,
                copy_placements,
                shape=getattr(local_tensor, "_global_shape", None),
            )
            return t

        if device_mesh is None:
            raise ValueError("device_mesh is None, must provide a DeviceMesh instance")
        if placements is None:
            raise ValueError("placements is None, must provide placements")

        # Create Tensor subclass instance, sharing local_tensor's underlying storage
        t = Tensor._make_subclass(cls, local_tensor, local_tensor.requires_grad)
        t.__init_data__(local_tensor, device_mesh, placements, layout, shape)
127
128
129
130
131
132
133
134
135
136
137

        Args:
            value (bool): True to enable gradient computation, False to disable.
        """
        self._local_tensor.requires_grad_(value)
        # Sync DTensor wrapper's requires_grad
        super().requires_grad_(value)

    def requires_grad_(self, requires_grad: bool = True):
        """
        Enable or disable gradient computation in-place.
162
163
164
165
166
167
168
169
170
171
172

        Returns:
            DTensorBase: Self for method chaining.
        """
        if self._local_tensor.grad is not None:
            self._local_tensor.grad.zero_()
        return self

    def detach(self):
        """
        Create a detached DTensor that does not require gradient.
188
189
190
191
192
193
194
195
196
197
198

        Returns:
            DTensorBase: Self for method chaining.
        """
        self._local_tensor.detach_()
        super().detach_()
        return self

    # ====================== Computation graph related overrides ======================
    @property
    def is_leaf(self) -> bool:
211
212
213
214
215
216
217
218
219

        Returns:
            bool: True if gradients are retained for non-leaf tensors.
        """
        return self._local_tensor.retains_grad

    @retains_grad.setter
    def retains_grad(self, value: bool) -> None:
        """
221
222
223
224
225
226
227
228
229

        Args:
            value (bool): True to enable gradient retention.
        """
        self._local_tensor.retains_grad_(value)

    def backward(self, gradient=None, retain_graph=None, create_graph=False) -> None:
        """
        Compute the gradients for this tensor.
232
233
234
235
236
237
238
239
240
            gradient (Optional[Tensor]): The gradient of the loss w.r.t. this tensor.
            retain_graph (Optional[bool]): Whether to retain the computation graph.
            create_graph (bool): Whether to create a graph of the gradient computation.
        """
        self._local_tensor.backward(gradient, retain_graph, create_graph)

    # ====================== Metadata related overrides (sync with local_tensor) ======================
    @property
    def device(self) -> torch.device:
288
289
290
291
292
293
294
295
296

        Returns:
            torch.Size: The shape of the tensor.
        """
        return self._local_tensor.shape

    def type(self, dtype=None, non_blocking=False):
        """
        Convert this tensor to the specified dtype.
301
302
303
304
305
306
307
308
309
310
311
312

        Returns:
            Union[str, DTensorBase]: The type string if dtype is None, otherwise a new DTensor.
        """
        if dtype is None:
            return self._local_tensor.type()
        new_local = self._local_tensor.to(dtype=dtype, non_blocking=non_blocking)
        return self.__class__(
            new_local,
            device_mesh=self._device_mesh,
            placements=self._alias_placements(),
            shape=getattr(self, "_global_shape", None),
321
322
323
324
325
326
327
328
329

        Returns:
            Union[torch.Size, int]: The shape or size along a specific dimension.
        """
        return self._local_tensor.size(dim)

    @property
    def ndim(self) -> int:
        """
331
332
333
334
335
336
337
338
339

        Returns:
            int: The number of dimensions.
        """
        return self._local_tensor.ndim

    def data_ptr(self) -> int:
        """
        Get the pointer to the data storage of the local tensor.
341
342
343
344
345
346
347
348
349
        Returns:
            int: The memory address of the tensor's data.
        """
        # Force return local_tensor's data pointer (ensure address consistency)
        return self._local_tensor.data_ptr()

    def numel(self) -> int:
        """
        Get the total number of elements in this tensor.
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

        Returns:
            int: The total number of elements.
        """
        return self._local_tensor.numel()

    # ====================== Auxiliary print ======================
    def _alias_placements(self):
        """Return alias_placements from layout, falling back to _placements."""
        if hasattr(self, '_layout') and self._layout is not None:
            return self._layout.alias_placements
        return self._placements

    def to(self, *args, **kwargs):
        """Move the DTensor to a different device or dtype.
373
374
375
376
377
378
379
380
381
382
383

        Returns:
            DTensorBase: A new DTensor with the converted local tensor.
        """
        new_local = self._local_tensor.to(*args, **kwargs)
        new_dt = Tensor._make_subclass(type(self), new_local, new_local.requires_grad)
        new_dt.__init_data__(
            new_local,
            self._device_mesh,
            self._alias_placements(),
            shape=getattr(self, "_global_shape", None),
381
382
383
384
385
386
387
388
389
390
391
392
            self._device_mesh,
            self._alias_placements(),
            shape=getattr(self, "_global_shape", None),
        )
        return new_dt

    def __repr__(self) -> str:
        return (
            f"DTensor(\n"
            f"  local_tensor={self._local_tensor},\n"
            f"  device_mesh={self._device_mesh},\n"
            f"  placements={self._placements},\n"
hyper_parallel/platform/mindspore/platform.py
985
986
987
988
989
990
991
992
993
    # pylint: disable=W0212
    @staticmethod
    def update_param_data(param, data):
        """update param data"""
        param._update_data(data)

    @staticmethod
    def load_into_param(param, data):
        copy_tensor = MindSporePlatform.empty_like(data)
991
992
993
994
995
996
997
998
999
    @staticmethod
    def load_into_param(param, data):
        copy_tensor = MindSporePlatform.empty_like(data)
        copy_tensor.copy_(data)
        param._update(copy_tensor)

    @staticmethod
    def get_cell_construct(cell):
        return cell.construct