11import ctypes
2+ from typing import Any , Union
23
34import numpy as np
45
@@ -17,15 +18,18 @@ class Tensor:
1718 # Public attributes describing the Tensor
1819 _underlying : _infinicore .Tensor
1920 _torch_ref : "torch.Tensor" # noqa: F821
21+ _numpy_ref : np .ndarray # noqa: F821
22+
2023 shape : list [int ]
2124 dtype : infinicore .dtype
2225 device : infinicore .device
2326
24- def __init__ (self , underlying , * , _torch_ref = None ):
27+ def __init__ (self , underlying , * , _torch_ref = None , _numpy_ref = None ):
2528 """An internal method. Please do not use this directly."""
2629
2730 self ._underlying = underlying
2831 self ._torch_ref = _torch_ref
32+ self ._numpy_ref = _numpy_ref
2933
3034 def __getattr__ (self , name ):
3135 # Lazily construct and cache an attribute.
@@ -131,6 +135,38 @@ def narrow(self, dim, start, length):
131135 return infinicore .narrow (self , dim , start , length )
132136
133137
138+ def tensor (
139+ data : Any ,
140+ * ,
141+ dtype : Union [infinicore .dtype , None ] = None ,
142+ device : Union [infinicore .device , str , int , None ] = None ,
143+ pin_memory : bool = False ,
144+ ) -> Tensor :
145+ r"""
146+ Constructs a tensor by copying `data`.
147+
148+ Args:
149+ data (array_like): Initial data for the tensor. Can be a list, tuple, NumPy, scalar.
150+
151+ Keyword args:
152+ dtype (infinicore.dtype, optional): the desired data type of returned tensor.
153+ Default: if ``None``, infers data type from :attr:`data`.
154+ device (infinicore.device, optional): the device of the constructed tensor.
155+ """
156+
157+ if isinstance (data , (list , tuple )):
158+ return from_list (data , dtype = dtype , device = device )
159+ elif isinstance (data , np .ndarray ):
160+ if dtype is not None :
161+ np_dtype = infinicore_to_numpy_dtype (dtype )
162+ data = data .astype (np_dtype )
163+ return from_numpy (data , device = device )
164+ elif isinstance (data , (int , float )):
165+ return from_list ([data ], dtype = dtype , device = device ).squeeze (0 )
166+
167+ raise ValueError (f" Can not convert data type: { type (data )} to Tensor" )
168+
169+
134170def empty (size , * , dtype = None , device = None , pin_memory = False ):
135171 return Tensor (
136172 _infinicore .empty (size , dtype ._underlying , device ._underlying , pin_memory )
@@ -198,28 +234,27 @@ def from_torch(torch_tensor) -> Tensor:
198234def from_numpy (
199235 np_array ,
200236 * ,
201- dtype : infinicore .dtype = None ,
202237 device : infinicore .device = None ,
203238) -> Tensor :
204- """Convert a NumPy ndarray to an infinicore Tensor.
239+ """
240+ Creates a Tensor from a numpy.ndarray.
205241
206242 Args:
207243 np_array: NumPy ndarray to convert to tensor
208- dtype: Optional infinicore dtype. If None, inferred from numpy array
209244 device: Optional infinicore device. If None, defaults to CPU device
210245
211246 Returns:
212247 Tensor: An infinicore tensor created from the numpy array
213248
214249 Raises:
215250 TypeError: If input data is not a numpy ndarray
216- ValueError: If input array is empty
251+ ValueError: If input array is empty or not C-contiguous
217252
218253 Note:
219254 NumPy arrays can only be created on CPU. For CUDA devices, data is first
220255 created on CPU, then copied to the target device.
221256 """
222- # Input validation
257+
223258 if not isinstance (np_array , np .ndarray ):
224259 raise TypeError (
225260 f"Input data must be a np.ndarray, got { type (np_array ).__name__ } "
@@ -228,55 +263,29 @@ def from_numpy(
228263 if np_array .size == 0 :
229264 raise ValueError ("Input array cannot be empty" )
230265
231- # Determine target numpy dtype
232- # If dtype is specified, convert it to numpy dtype first
233- if dtype is not None :
234- np_dtype = infinicore_to_numpy_dtype (dtype )
235- # Create a copy with the target dtype if dtype doesn't match
236- # Use copy=True to ensure we don't modify the original array
237- if np_dtype != np_array .dtype :
238- np_array = np_array .astype (np_dtype , copy = True )
239- # Ensure C-contiguous layout
240- elif not np_array .flags .c_contiguous :
241- np_array = np .ascontiguousarray (np_array )
242- else :
243- # Ensure C-contiguous layout
244- if not np_array .flags .c_contiguous :
245- np_array = np .ascontiguousarray (np_array )
246-
247- # Infer infinicore dtype if not provided
248- infini_type = (
249- dtype if dtype is not None else numpy_to_infinicore_dtype (np_array .dtype )
250- )
266+ if not np_array .flags .c_contiguous :
267+ raise ValueError ("Input array must be C-contiguous" )
251268
252- # Default to CPU device if not provided
253- infini_device = device if device is not None else infinicore .device ("cpu" , 0 )
269+ infini_type = numpy_to_infinicore_dtype (np_array .dtype )
254270 cpu_device = infinicore .device ("cpu" , 0 )
255271
256- # Create a temporary tensor on CPU using from_blob to reference numpy array
257- # This allows us to copy data without keeping numpy array reference
272+ # Create a infinicore.Tensor on CPU using from_blob to reference numpy array
258273 data_ptr = np_array .ctypes .data_as (ctypes .c_void_p ).value
259- temp_tensor = Tensor (
274+ infini_tensor = Tensor (
260275 _infinicore .from_blob (
261276 data_ptr ,
262277 list (np_array .shape ),
263278 dtype = infini_type ._underlying ,
264279 device = cpu_device ._underlying ,
265- )
280+ ),
281+ _numpy_ref = np_array ,
266282 )
267283
268- # Always create the result tensor on CPU first, then copy data
269- # This ensures we have a proper copy of the data
270- result = empty (list (np_array .shape ), dtype = infini_type , device = cpu_device )
271- result .copy_ (temp_tensor )
272-
273284 # If target device is not CPU, move the tensor to the target device
274- # The temporary tensor and numpy array will be garbage collected
275- # since we don't keep references to them
276- if infini_device .type != "cpu" :
277- result = result .to (infini_device )
285+ if device is not None and device .type != "cpu" :
286+ infini_tensor = infini_tensor .to (device )
278287
279- return result
288+ return infini_tensor
280289
281290
282291def from_list (data , * , dtype = None , device = None ) -> Tensor :
@@ -329,4 +338,4 @@ def from_list(data, *, dtype=None, device=None) -> Tensor:
329338
330339 # Reuse from_numpy to create the tensor
331340 # This avoids code duplication and ensures consistent behavior
332- return from_numpy (np_array , dtype = dtype , device = device )
341+ return from_numpy (np_array , device = device )
0 commit comments