22from typing import cast
33
44import torch
5- from torch import Tensor , tensor
5+ from torch import Tensor , arange , tensor
66from torch .ops import aten # type: ignore
77
88from torchjd .sparse ._structured_sparse_tensor import (
99 StructuredSparseTensor ,
10- encode_v_to_ps ,
11- fix_dim_encoding ,
1210 impl ,
1311 print_fallback ,
1412 to_most_efficient_tensor ,
@@ -136,10 +134,10 @@ def unsqueeze_default(t: StructuredSparseTensor, dim: int) -> StructuredSparseTe
136134 if dim < 0 :
137135 dim = t .ndim + dim + 1
138136
139- new_v_to_ps = [ p for p in t . v_to_ps ] # Deepcopy the list to not modify the original v_to_ps
140- new_v_to_ps . insert ( dim , [])
141-
142- return StructuredSparseTensor (t .physical , new_v_to_ps )
137+ new_strides = torch . concatenate (
138+ [ t . strides [: dim ], torch . zeros ( 1 , t . strides . shape [ 1 ], dtype = torch . int64 ), t . strides [ dim :]]
139+ )
140+ return StructuredSparseTensor (t .physical , new_strides )
143141
144142
145143@impl (aten .squeeze .dims )
@@ -153,17 +151,15 @@ def squeeze_dims(t: StructuredSparseTensor, dims: list[int] | int | None) -> Ten
153151 else :
154152 excluded = set (dims )
155153
156- new_v_to_ps = [pdims for i , pdims in enumerate ( t . v_to_ps ) if i not in excluded ]
157-
158- return to_most_efficient_tensor (t .physical , new_v_to_ps )
154+ is_row_kept = [i not in excluded for i in range ( t . ndim ) ]
155+ new_strides = t . strides [ is_row_kept ]
156+ return to_most_efficient_tensor (t .physical , new_strides )
159157
160158
161159@impl (aten .permute .default )
162160def permute_default (t : StructuredSparseTensor , dims : list [int ]) -> StructuredSparseTensor :
163- new_v_to_ps = [t .v_to_ps [d ] for d in dims ]
164-
165- new_physical , new_v_to_ps = fix_dim_encoding (t .physical , new_v_to_ps )
166- return StructuredSparseTensor (new_physical , new_v_to_ps )
161+ new_strides = t .strides [torch .tensor (dims )]
162+ return StructuredSparseTensor (t .physical , new_strides )
167163
168164
169165@impl (aten .cat .default )
@@ -197,25 +193,20 @@ def cat_default(tensors: list[Tensor], dim: int) -> Tensor:
197193 # Add a physical dimension pdim on which we can concatenate the physicals such that this
198194 # translates into a concatenation of the virtuals on virtual dimension dim.
199195
200- # Stride-based representation:
201- # new_stride_column = torch.zeros(ref_tensor.ndim, dtype=torch.int)
202- # new_stride_column[dim] = ref_virtual_dim_size
203-
204196 pdim = ref_tensor .physical .ndim
205- new_v_to_ps = [[d for d in pdims ] for pdims in ref_tensor .v_to_ps ]
206- new_v_to_ps [dim ] = [pdim ] + new_v_to_ps [dim ]
207- new_v_to_ps , destination = encode_v_to_ps (new_v_to_ps )
208- source = list (range (len (destination )))
209- physicals = [t .physical .unsqueeze (- 1 ).movedim (source , destination ) for t in tensors_ ]
197+ physicals = [t .physical .unsqueeze (- 1 ) for t in tensors_ ]
198+ new_stride_column = torch .zeros (ref_tensor .ndim , 1 , dtype = torch .int64 )
199+ new_stride_column [dim , 0 ] = ref_virtual_dim_size
200+ new_strides = torch .concatenate ([ref_tensor .strides , new_stride_column ], dim = 1 )
210201 else :
211202 # Such a physical dimension already exists. Note that an alternative implementation would be
212203 # to simply always add the physical dimension, and squash it if it ends up being not needed.
213204 physicals = [t .physical for t in tensors_ ]
214205 pdim = indices [0 ][0 ]
215- new_v_to_ps = ref_tensor .v_to_ps
206+ new_strides = ref_tensor .strides
216207
217208 new_physical = aten .cat .default (physicals , dim = pdim )
218- return StructuredSparseTensor (new_physical , new_v_to_ps )
209+ return StructuredSparseTensor (new_physical , new_strides )
219210
220211
221212@impl (aten .expand .default )
@@ -225,32 +216,32 @@ def expand_default(t: StructuredSparseTensor, sizes: list[int]) -> StructuredSpa
225216 assert isinstance (sizes , list )
226217 assert len (sizes ) >= t .ndim
227218
219+ # Add as many dimensions as needed at the beginning of the tensor (as torch.expand works)
228220 for _ in range (len (sizes ) - t .ndim ):
229221 t = t .unsqueeze (0 )
230222
231- assert len (sizes ) == t .ndim
232-
223+ # Try to expand each dimension to its new size
233224 new_physical = t .physical
234- new_v_to_ps = t .v_to_ps
235- n_added_physical_dims = 0
236- for dim , (ps , orig_size , new_size ) in enumerate (zip (t .v_to_ps , t .shape , sizes , strict = True )):
237- if len (ps ) > 0 and orig_size != new_size and new_size != - 1 :
225+ new_strides = t .strides
226+ for d , (vstride , orig_size , new_size ) in enumerate (zip (t .strides , t .shape , sizes , strict = True )):
227+ if vstride .sum () > 0 and orig_size != new_size and new_size != - 1 :
238228 raise ValueError (
239- f"Cannot expand dim { dim } of size != 1. Found size { orig_size } and target size "
229+ f"Cannot expand dim { d } of size != 1. Found size { orig_size } and target size "
240230 f"{ new_size } ."
241231 )
242232
243- if len ( ps ) == 0 and new_size != 1 and new_size != - 1 :
233+ if vstride . sum ( ) == 0 and new_size != 1 and new_size != - 1 :
244234 # Add a dimension of size new_size at the end of the physical tensor.
245235 new_physical_shape = list (new_physical .shape ) + [new_size ]
246236 new_physical = new_physical .unsqueeze (- 1 ).expand (new_physical_shape )
247- new_v_to_ps [dim ] = [t .physical .ndim + n_added_physical_dims ]
248- n_added_physical_dims += 1
249237
250- new_v_to_ps , destination = encode_v_to_ps (new_v_to_ps )
251- new_physical = new_physical .movedim (list (range (len (destination ))), destination )
238+ # Make this new physical dimension have a stride of 1 at virtual dimension d and 0 at
239+ # every other virtual dimension
240+ new_stride_column = torch .zeros (t .ndim , 1 , dtype = torch .int64 )
241+ new_stride_column [d , 0 ] = 1
242+ new_strides = torch .cat ([new_strides , new_stride_column ], dim = 1 )
252243
253- return StructuredSparseTensor (new_physical , new_v_to_ps )
244+ return StructuredSparseTensor (new_physical , new_strides )
254245
255246
256247@impl (aten .broadcast_tensors .default )
@@ -280,49 +271,14 @@ def broadcast_tensors_default(tensors: list[Tensor]) -> tuple[Tensor, Tensor]:
280271 return aten .expand .default (t1 , new_shape ), aten .expand .default (t2 , new_shape )
281272
282273
283- @impl (aten .slice .Tensor )
284- def slice_Tensor (
285- t : StructuredSparseTensor , dim : int , start : int | None , end : int | None , step : int = 1
286- ) -> StructuredSparseTensor :
287- assert isinstance (t , StructuredSparseTensor )
288-
289- physical_dims = t .v_to_ps [dim ]
290-
291- if len (physical_dims ) > 1 :
292- raise ValueError (
293- "Cannot yet slice virtual dim corresponding to several physical dims.\n "
294- f"{ t .debug_info ()} \n "
295- f"dim={ dim } , start={ start } , end={ end } , step={ step } ."
296- )
297- elif len (physical_dims ) == 0 :
298- # Trying to slice a virtual dim of size 1.
299- # Either
300- # - the element of this dim is included in the slice: keep it as it is
301- # - it's not included in the slice (e.g. end<=start): we would end up with a size of 0 on
302- # that dimension, so we'd need to add a dimension of size 0 to the physical. This is not
303- # implemented yet.
304- start_ = start if start is not None else 0
305- end_ = end if end is not None else 1
306- if end_ <= start_ : # TODO: the condition might be a bit more complex if step != 1
307- raise NotImplementedError (
308- "Slicing of dimension of size 1 leading to dimension of size 0 not implemented yet."
309- )
310- else :
311- new_physical = t .physical
312- else :
313- physical_dim = physical_dims [0 ]
314- new_physical = aten .slice .Tensor (t .physical , physical_dim , start , end , step )
315-
316- return StructuredSparseTensor (new_physical , t .v_to_ps )
317-
318-
319274@impl (aten .transpose .int )
320275def transpose_int (t : StructuredSparseTensor , dim0 : int , dim1 : int ) -> StructuredSparseTensor :
321276 assert isinstance (t , StructuredSparseTensor )
277+ return StructuredSparseTensor (t .physical , _swap_rows (t .strides , dim0 , dim1 ))
322278
323- new_v_to_ps = [dims for dims in t .v_to_ps ]
324- new_v_to_ps [dim0 ] = t .v_to_ps [dim1 ]
325- new_v_to_ps [dim1 ] = t .v_to_ps [dim0 ]
326279
327- new_physical , new_v_to_ps = fix_dim_encoding (t .physical , new_v_to_ps )
328- return StructuredSparseTensor (new_physical , new_v_to_ps )
280+ def _swap_rows (matrix : Tensor , c0 : int , c1 : int ) -> Tensor :
281+ index = arange (matrix .shape [0 ])
282+ index [c0 ] = c1
283+ index [c1 ] = c0
284+ return matrix [index ]
0 commit comments