diff --git a/numojo/__init__.mojo b/numojo/__init__.mojo index 6443793d..530708d4 100644 --- a/numojo/__init__.mojo +++ b/numojo/__init__.mojo @@ -19,33 +19,44 @@ from numojo.core.complex.complex_ndarray import ComplexNDArray from numojo.core.complex.complex_dtype import ( ComplexDType, ci8, - ci16, - ci32, ci64, - cisize, - cintp, + ci128, + ci256, + cint, cu8, cu16, cu32, cu64, + cu128, + cu256, + cuint, + cbf16, cf16, cf32, cf64, cboolean, + cinvalid, ) from numojo.core.datatypes import ( i8, i16, i32, i64, - isize, + i128, + i256, + int, u8, u16, u32, u64, + u128, + u256, + uint, + bf16, f16, f32, f64, + boolean, ) from numojo.core.error import ( ShapeError, diff --git a/numojo/core/__init__.mojo b/numojo/core/__init__.mojo index c1223e63..158bf9d2 100644 --- a/numojo/core/__init__.mojo +++ b/numojo/core/__init__.mojo @@ -13,33 +13,43 @@ from .complex import ( ComplexNDArray, ComplexDType, ci8, - ci16, - ci32, ci64, - cisize, - cintp, + ci128, + ci256, + cint, cu8, cu16, cu32, cu64, + cu128, + cu256, + cuint, + cbf16, cf16, cf32, cf64, cboolean, + cinvalid, ) from .datatypes import ( i8, - i16, - i32, i64, + i128, + i256, + int, u8, u16, u32, u64, + u128, + u256, + uint, + bf16, f16, f32, f64, + boolean, ) from .error import ( diff --git a/numojo/core/complex/__init__.mojo b/numojo/core/complex/__init__.mojo index e0bf1271..554e2983 100644 --- a/numojo/core/complex/__init__.mojo +++ b/numojo/core/complex/__init__.mojo @@ -3,17 +3,21 @@ from .complex_ndarray import ComplexNDArray from .complex_dtype import ( ComplexDType, ci8, - ci16, - ci32, ci64, - cisize, - cintp, + ci128, + ci256, + cint, cu8, cu16, cu32, cu64, + cu128, + cu256, + cuint, + cbf16, cf16, cf32, cf64, cboolean, + cinvalid, ) diff --git a/numojo/core/complex/complex_dtype.mojo b/numojo/core/complex/complex_dtype.mojo index 8080a87f..38c88c5a 100644 --- a/numojo/core/complex/complex_dtype.mojo +++ b/numojo/core/complex/complex_dtype.mojo @@ -12,7 +12,7 @@ from hashlib.hasher import Hasher from os import abort from sys import CompilationTarget -from sys.info import bitwidthof, size_of +from sys.info import bit_width_of, size_of from sys.intrinsics import _type_is_eq alias _mIsSigned = UInt8(1) @@ -22,34 +22,45 @@ alias _mIsFloat = UInt8(1 << 6) # rust like aliases for complex data types. alias ci8 = ComplexDType.int8 -"""Data type alias cfor ComplexDType.int8""" +"""Data type alias for ComplexDType.int8""" alias ci16 = ComplexDType.int16 -"""Data type alias cfor ComplexDType.int16""" +"""Data type alias for ComplexDType.int16""" alias ci32 = ComplexDType.int32 -"""Data type alias cfor ComplexDType.int32""" +"""Data type alias for ComplexDType.int32""" alias ci64 = ComplexDType.int64 -"""Data type alias cfor ComplexDType.int64""" -alias cisize = ComplexDType.index -"""Data type alias cfor ComplexDType.index""" -alias cintp = ComplexDType.index -"""Data type alias cfor ComplexDType.index""" +"""Data type alias for ComplexDType.int64""" +alias ci128 = ComplexDType.int128 +"""Data type alias for ComplexDType.int128""" +alias ci256 = ComplexDType.int256 +"""Data type alias for ComplexDType.int256""" +alias cint = ComplexDType.int +"""Data type alias for ComplexDType.int""" alias cu8 = ComplexDType.uint8 -"""Data type alias cfor ComplexDType.uint8""" +"""Data type alias for ComplexDType.uint8""" alias cu16 = ComplexDType.uint16 -"""Data type alias cfor ComplexDType.uint16""" +"""Data type alias for ComplexDType.uint16""" alias cu32 = ComplexDType.uint32 -"""Data type alias cfor ComplexDType.uint32""" +"""Data type alias for ComplexDType.uint32""" alias cu64 = ComplexDType.uint64 -"""Data type alias cfor ComplexDType.uint64""" +"""Data type alias for ComplexDType.uint64""" +alias cu128 = ComplexDType.uint128 +"""Data type alias for ComplexDType.uint128""" +alias cu256 = ComplexDType.uint256 +"""Data type alias for ComplexDType.uint256""" +alias cuint = ComplexDType.uint +"""Data type alias for ComplexDType.uint""" +alias cbf16 = ComplexDType.bfloat16 +"""Data type alias for ComplexDType.bfloat16""" alias cf16 = ComplexDType.float16 -"""Data type alias cfor ComplexDType.float16""" +"""Data type alias for ComplexDType.float16""" alias cf32 = ComplexDType.float32 -"""Data type alias cfor ComplexDType.float32""" +"""Data type alias for ComplexDType.float32""" alias cf64 = ComplexDType.float64 -"""Data type alias cfor ComplexDType.float64""" +"""Data type alias for ComplexDType.float64""" alias cboolean = ComplexDType.bool -"""Data type alias cfor ComplexDType.bool""" - +"""Data type alias for ComplexDType.bool""" +alias cinvalid = ComplexDType.invalid +"""Data type alias for ComplexDType.invalid""" # ===----------------------------------------------------------------------=== # # Implements the Complex Datatype. @@ -90,25 +101,27 @@ struct ComplexDType( # ===-------------------------------------------------------------------===# # Aliases # ===-------------------------------------------------------------------===# - + # Refer to DType documentation for details on each data type. alias _mlir_type = __mlir_type.`!kgen.dtype` - alias invalid = ComplexDType( mlir_value=__mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) alias bool = ComplexDType( mlir_value=__mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) - alias index = ComplexDType( + alias int = ComplexDType( mlir_value=__mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) - alias uint1 = ComplexDType( + alias uint = ComplexDType( + mlir_value=__mlir_attr.`#kgen.dtype.constant : !kgen.dtype` + ) + alias _uint1 = ComplexDType( mlir_value=__mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) - alias uint2 = ComplexDType( + alias _uint2 = ComplexDType( mlir_value=__mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) - alias uint4 = ComplexDType( + alias _uint4 = ComplexDType( mlir_value=__mlir_attr.`#kgen.dtype.constant : !kgen.dtype` ) alias uint8 = ComplexDType( @@ -204,36 +217,30 @@ struct ComplexDType( """ if str.startswith("ComplexDType."): return Self._from_str(str.removeprefix("ComplexDType.")) - elif str == "bool": - return ComplexDType.bool - elif str == "index": - return ComplexDType.index - - elif str == "uint8": - return ComplexDType.uint8 elif str == "int8": return ComplexDType.int8 + elif str == "int64": + return ComplexDType.int64 + elif str == "int128": + return ComplexDType.int128 + elif str == "int256": + return ComplexDType.int256 + elif str == "int": + return ComplexDType.int + elif str == "uint8": + return ComplexDType.uint8 elif str == "uint16": return ComplexDType.uint16 - elif str == "int16": - return ComplexDType.int16 elif str == "uint32": return ComplexDType.uint32 - elif str == "int32": - return ComplexDType.int32 elif str == "uint64": return ComplexDType.uint64 - elif str == "int64": - return ComplexDType.int64 elif str == "uint128": return ComplexDType.uint128 - elif str == "int128": - return ComplexDType.int128 elif str == "uint256": return ComplexDType.uint256 - elif str == "int256": - return ComplexDType.int256 - + elif str == "uint": + return ComplexDType.uint elif str == "float8_e3m4": return ComplexDType.float8_e3m4 elif str == "float8_e4m3fn": @@ -244,7 +251,6 @@ struct ComplexDType( return ComplexDType.float8_e5m2 elif str == "float8_e5m2fnuz": return ComplexDType.float8_e5m2fnuz - elif str == "bfloat16": return ComplexDType.bfloat16 elif str == "float16": @@ -253,7 +259,8 @@ struct ComplexDType( return ComplexDType.float32 elif str == "float64": return ComplexDType.float64 - + elif str == "bool": + return ComplexDType.bool else: return ComplexDType.invalid @@ -276,35 +283,30 @@ struct ComplexDType( writer: The object to write to. """ - if self is ComplexDType.bool: - return writer.write("bool") - elif self is ComplexDType.index: - return writer.write("index") + if self is ComplexDType.int8: + return writer.write("int8") + elif self is ComplexDType.int64: + return writer.write("int64") + elif self is ComplexDType.int128: + return writer.write("int128") + elif self is ComplexDType.int256: + return writer.write("int256") + elif self is ComplexDType.int: + return writer.write("int") elif self is ComplexDType.uint8: return writer.write("uint8") - elif self is ComplexDType.int8: - return writer.write("int8") elif self is ComplexDType.uint16: return writer.write("uint16") - elif self is ComplexDType.int16: - return writer.write("int16") elif self is ComplexDType.uint32: return writer.write("uint32") - elif self is ComplexDType.int32: - return writer.write("int32") elif self is ComplexDType.uint64: return writer.write("uint64") - elif self is ComplexDType.int64: - return writer.write("int64") elif self is ComplexDType.uint128: return writer.write("uint128") - elif self is ComplexDType.int128: - return writer.write("int128") elif self is ComplexDType.uint256: return writer.write("uint256") - elif self is ComplexDType.int256: - return writer.write("int256") - + elif self is ComplexDType.uint: + return writer.write("uint") elif self is ComplexDType.float8_e3m4: return writer.write("float8_e3m4") elif self is ComplexDType.float8_e4m3fn: @@ -315,18 +317,16 @@ struct ComplexDType( return writer.write("float8_e5m2") elif self is ComplexDType.float8_e5m2fnuz: return writer.write("float8_e5m2fnuz") - elif self is ComplexDType.bfloat16: return writer.write("bfloat16") elif self is ComplexDType.float16: return writer.write("float16") - elif self is ComplexDType.float32: return writer.write("float32") - elif self is ComplexDType.float64: return writer.write("float64") - + elif self is ComplexDType.bool: + return writer.write("bool") elif self is ComplexDType.invalid: return writer.write("invalid") @@ -476,7 +476,7 @@ struct ComplexDType( Returns: Returns True if the input type parameter is an integer. """ - return self is ComplexDType.index or self._is_non_index_integral() + return self in (DType.int, DType.uint) or self._is_non_index_integral() @always_inline("nodebug") fn is_floating_point(self) -> Bool: @@ -529,10 +529,10 @@ struct ComplexDType( @always_inline fn size_of(self) -> Int: - """Returns the size in bytes of the current ComplexDType. + """Returns the size in bytes of the current DType. Returns: - Returns the size in bytes of the current ComplexDType. + Returns the size in bytes of the current DType. """ if self._is_non_index_integral(): @@ -556,8 +556,10 @@ struct ComplexDType( elif self is ComplexDType.bool: return size_of[DType.bool]() - elif self is ComplexDType.index: - return size_of[DType.index]() + elif self is ComplexDType.int: + return size_of[DType.int]() + elif self is ComplexDType.uint: + return size_of[DType.uint]() elif self is ComplexDType.float8_e3m4: return size_of[DType.float8_e3m4]() @@ -599,7 +601,7 @@ struct ComplexDType( # ===-------------------------------------------------------------------===# @always_inline("nodebug") fn __mlir_type(self) -> __mlir_type.`!kgen.deferred`: - """Returns the MLIR type of the current ComplexDType as an MLIR type. + """Returns the MLIR type of the current DType as an MLIR type. Returns: The MLIR type of the current ComplexDType. @@ -607,7 +609,7 @@ struct ComplexDType( if self is ComplexDType.bool: return __mlir_attr.i1 - if self is ComplexDType.index: + if self is ComplexDType.int: return __mlir_attr.index if self is ComplexDType.uint8: @@ -657,97 +659,21 @@ struct ComplexDType( if self is ComplexDType.float64: return __mlir_attr.f64 - return abort[__mlir_type.`!kgen.deferred`]("invalid ComplexDType") - - @parameter - fn compare_dtype(self, dtype: DType) -> Bool: - if self.to_dtype() == dtype: - return True - return False - - @parameter - fn to_dtype(self) -> DType: - # Floating point types - if self == ComplexDType.float16: - return DType.float16 - elif self == ComplexDType.float32: - return DType.float32 - elif self == ComplexDType.float64: - return DType.float64 - elif self == ComplexDType.bfloat16: - return DType.bfloat16 - - # Float8 types - elif self == ComplexDType.float8_e3m4: - return DType.float8_e3m4 - elif self == ComplexDType.float8_e4m3fn: - return DType.float8_e4m3fn - elif self == ComplexDType.float8_e4m3fnuz: - return DType.float8_e4m3fnuz - elif self == ComplexDType.float8_e5m2: - return DType.float8_e5m2 - elif self == ComplexDType.float8_e5m2fnuz: - return DType.float8_e5m2fnuz - - # Signed integer types - elif self == ComplexDType.int8: - return DType.int8 - elif self == ComplexDType.int16: - return DType.int16 - elif self == ComplexDType.int32: - return DType.int32 - elif self == ComplexDType.int64: - return DType.int64 - elif self == ComplexDType.int128: - return DType.int128 - elif self == ComplexDType.int256: - return DType.int256 - - # Unsigned integer types - # elif self == ComplexDType.uint1: - # return DType.uint1 - # elif self == ComplexDType.uint2: - # return DType.uint2 - # elif self == ComplexDType.uint4: - # return DType.uint4 - elif self == ComplexDType.uint8: - return DType.uint8 - elif self == ComplexDType.uint16: - return DType.uint16 - elif self == ComplexDType.uint32: - return DType.uint32 - elif self == ComplexDType.uint64: - return DType.uint64 - elif self == ComplexDType.uint128: - return DType.uint128 - elif self == ComplexDType.uint256: - return DType.uint256 - - # Special types - elif self == ComplexDType.bool: - return DType.bool - elif self == ComplexDType.index: - return DType.index - elif self == ComplexDType.invalid: - return DType.invalid - - # Default case - else: - return DType.invalid + return abort[__mlir_type.`!kgen.deferred`]("invalid dtype") fn _concise_dtype_str(cdtype: ComplexDType) -> String: """Returns a concise string representation of the complex data type.""" if cdtype == ci8: return "ci8" - elif cdtype == ci16: - return "ci16" - elif cdtype == ci32: - return "ci32" elif cdtype == ci64: return "ci64" - elif cdtype == cisize: - return "cindex" + elif cdtype == ci128: + return "ci128" + elif cdtype == ci256: + return "ci256" + elif cdtype == cint: + return "cint" elif cdtype == cu8: return "cu8" elif cdtype == cu16: @@ -756,6 +682,14 @@ fn _concise_dtype_str(cdtype: ComplexDType) -> String: return "cu32" elif cdtype == cu64: return "cu64" + elif cdtype == cu128: + return "cu128" + elif cdtype == cu256: + return "cu256" + elif cdtype == cuint: + return "cuint" + elif cdtype == cbf16: + return "cbf16" elif cdtype == cf16: return "cf16" elif cdtype == cf32: @@ -764,7 +698,7 @@ fn _concise_dtype_str(cdtype: ComplexDType) -> String: return "cf64" elif cdtype == cboolean: return "cboolean" - elif cdtype == cisize: - return "cisize" + elif cdtype == cinvalid: + return "cinvalid" else: return "Unknown" diff --git a/numojo/core/complex/complex_ndarray.mojo b/numojo/core/complex/complex_ndarray.mojo index b155674b..a87a3286 100644 --- a/numojo/core/complex/complex_ndarray.mojo +++ b/numojo/core/complex/complex_ndarray.mojo @@ -201,7 +201,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( self.strides = self._re.strides self.flags = self._re.flags self.print_options = PrintOptions( - precision=2, edge_items=2, line_width=80, formatted_width=6 + precision=2, edge_items=2, line_width=100, formatted_width=6 ) @always_inline("nodebug") @@ -225,7 +225,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( self.strides = self._re.strides self.flags = self._re.flags self.print_options = PrintOptions( - precision=2, edge_items=2, line_width=80, formatted_width=6 + precision=2, edge_items=2, line_width=100, formatted_width=6 ) @always_inline("nodebug") @@ -249,7 +249,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( self.strides = self._re.strides self.flags = self._re.flags self.print_options = PrintOptions( - precision=2, edge_items=2, line_width=80, formatted_width=6 + precision=2, edge_items=2, line_width=100, formatted_width=6 ) fn __init__( @@ -269,7 +269,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( self.strides = self._re.strides self.flags = self._re.flags self.print_options = PrintOptions( - precision=2, edge_items=2, line_width=80, formatted_width=6 + precision=2, edge_items=2, line_width=100, formatted_width=6 ) fn __init__( @@ -301,7 +301,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( self._re = NDArray[Self.dtype](shape, strides, ndim, size, flags) self._im = NDArray[Self.dtype](shape, strides, ndim, size, flags) self.print_options = PrintOptions( - precision=2, edge_items=2, line_width=80, formatted_width=6 + precision=2, edge_items=2, line_width=100, formatted_width=6 ) fn __init__( @@ -331,7 +331,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( self.strides = self._re.strides self.flags = self._re.flags self.print_options = PrintOptions( - precision=2, edge_items=2, line_width=80, formatted_width=6 + precision=2, edge_items=2, line_width=100, formatted_width=6 ) @always_inline("nodebug") @@ -586,11 +586,12 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( Examples: ```mojo import numojo as nm - var a = nm.arange[nm.cf32](nm.CScalar[nm.f32](0, 0), nm.CScalar[nm.f32](12, 12), nm.CScalar[nm.f32](1, 1)).reshape(nm.Shape(3, 4)) + from numojo.prelude import * + var a = nm.arange[cf32](CScalar[cf32](0, 0), CScalar[cf32](12, 12), CScalar[cf32](1, 1)).reshape(nm.Shape(3, 4)) print(a.shape) # (3,4) print(a[1].shape) # (4,) -- 1-D slice print(a[-1].shape) # (4,) -- negative index - var b = nm.arange[nm.cf32](nm.CScalar[nm.f32](6, 6)).reshape(nm.Shape(6)) + var b = nm.arange[cf32](CScalar[cf32](6, 6)).reshape(nm.Shape(6)) print(b[2]) # 0-D array (scalar wrapper) ``` """ @@ -751,7 +752,8 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( Examples: ```mojo import numojo as nm - var a = nm.arange[nm.cf32](nm.ComplexScalar(10.0, 10.0)).reshape(nm.Shape(2, 5)) + from numojo.prelude import * + var a = nm.arange[cf32](CScalar[cf32](10.0, 10.0)).reshape(nm.Shape(2, 5)) var b = a[List[Slice](Slice(0, 2, 1), Slice(2, 4, 1))] # Equivalent to arr[:, 2:4], returns a 2x2 sliced array. print(b) ``` @@ -855,8 +857,9 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ```mojo import numojo as nm - var a = nm.fullC[nm.f32](nm.Shape(2, 5), ComplexSIMD[nm.f32](1.0, 1.0)) - var b = a[1, 2:4] + from numojo.prelude import * + var a = nm.full[cf32](nm.Shape(2, 5), CScalar[cf32](1.0, 1.0)) + var b = a[1, Slice(2,4)] print(b) ``` """ @@ -2451,7 +2454,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( offset: The offset of the current dimension. summarize: Internal flag indicating summarization already chosen. """ - var options: PrintOptions = self._re.print_options + var options: PrintOptions = self.print_options var separator = options.separator var padding = options.padding var edge_items = options.edge_items @@ -2800,6 +2803,49 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ) ) + fn squeeze(mut self, axis: Int) raises: + """ + Remove (squeeze) a single dimension of size 1 from the array shape. + + Args: + axis: The axis to squeeze. Supports negative indices. + + Raises: + IndexError: If the axis is out of range. + ShapeError: If the dimension at the given axis is not of size 1. + """ + var normalized_axis: Int = axis + if normalized_axis < 0: + normalized_axis += self.ndim + if (normalized_axis < 0) or (normalized_axis >= self.ndim): + raise Error( + IndexError( + message=String( + "Axis {} is out of range for array with {} dimensions." + ).format(axis, self.ndim), + suggestion=String( + "Use an axis value in the range [-{}, {})." + ).format(self.ndim, self.ndim), + location=String("NDArray.squeeze(axis: Int)"), + ) + ) + + if self.shape[normalized_axis] != 1: + raise Error( + ShapeError( + message=String( + "Cannot squeeze axis {} with size {}." + ).format(normalized_axis, self.shape[normalized_axis]), + suggestion=String( + "Only axes with length 1 can be removed." + ), + location=String("NDArray.squeeze(axis: Int)"), + ) + ) + self.shape = self.shape._pop(normalized_axis) + self.strides = self.strides._pop(normalized_axis) + self.ndim -= 1 + struct _ComplexNDArrayIter[ is_mutable: Bool, //, diff --git a/numojo/core/datatypes.mojo b/numojo/core/datatypes.mojo index 04d5e9e2..d4c106ea 100644 --- a/numojo/core/datatypes.mojo +++ b/numojo/core/datatypes.mojo @@ -15,10 +15,14 @@ alias i32 = DType.int32 """Data type alias for DType.int32""" alias i64 = DType.int64 """Data type alias for DType.int64""" -alias isize = DType.index -"""Data type alias for DType.index""" -alias intp = DType.index -"""Data type alias for DType.index""" +alias i128 = DType.int128 +"""Data type alias for DType.int128""" +alias i256 = DType.int256 +"""Data type alias for DType.int256""" +alias int = DType.int +"""Data type alias for DType.int""" +alias uint = DType.int +"""Data type alias for DType.uint""" alias u8 = DType.uint8 """Data type alias for DType.uint8""" alias u16 = DType.uint16 @@ -27,8 +31,14 @@ alias u32 = DType.uint32 """Data type alias for DType.uint32""" alias u64 = DType.uint64 """Data type alias for DType.uint64""" +alias u128 = DType.uint128 +"""Data type alias for DType.uint128""" +alias u256 = DType.uint256 +"""Data type alias for DType.uint256""" alias f16 = DType.float16 """Data type alias for DType.float16""" +alias bf16 = DType.bfloat16 +"""Data type alias for DType.bfloat16""" alias f32 = DType.float32 """Data type alias for DType.float32""" alias f64 = DType.float64 @@ -174,14 +184,14 @@ fn _concise_dtype_str(dtype: DType) -> String: """Returns a concise string representation of the data type.""" if dtype == i8: return "i8" - elif dtype == i16: - return "i16" - elif dtype == i32: - return "i32" elif dtype == i64: return "i64" - elif dtype == isize: - return "index" + elif dtype == i128: + return "i128" + elif dtype == i256: + return "i256" + elif dtype == int: + return "int" elif dtype == u8: return "u8" elif dtype == u16: @@ -190,6 +200,14 @@ fn _concise_dtype_str(dtype: DType) -> String: return "u32" elif dtype == u64: return "u64" + elif dtype == u128: + return "u128" + elif dtype == u256: + return "u256" + elif dtype == uint: + return "uint" + elif dtype == bf16: + return "bf16" elif dtype == f16: return "f16" elif dtype == f32: @@ -198,8 +216,6 @@ fn _concise_dtype_str(dtype: DType) -> String: return "f64" elif dtype == boolean: return "boolean" - elif dtype == isize: - return "isize" else: return "Unknown" diff --git a/numojo/core/ndarray.mojo b/numojo/core/ndarray.mojo index d4e0cf92..80fed48f 100644 --- a/numojo/core/ndarray.mojo +++ b/numojo/core/ndarray.mojo @@ -401,8 +401,9 @@ struct NDArray[dtype: DType = DType.float64]( Examples: ```mojo - import numojo - var A = numojo.ones(numojo.Shape(2,3,4)) + import numojo as nm + from numojo.prelude import * + var A = nm.ones[f32](nm.Shape(2,3,4)) print(A._getitem(1,2,3)) ``` """ @@ -428,8 +429,9 @@ struct NDArray[dtype: DType = DType.float64]( Examples: ```mojo - import numojo - var A = numojo.ones(numojo.Shape(2,3,4)) + import numojo as nm + from numojo.prelude import * + var A = nm.ones[f32](nm.Shape(2,3,4)) print(A._getitem(List[Int](1,2,3))) ``` """ @@ -789,9 +791,132 @@ struct NDArray[dtype: DType = DType.float64]( slice_len: Int = max(0, (end - start + (step - 1)) // step) else: slice_len: Int = max(0, (start - end - step - 1) // (-step)) - if ( - slice_len > 1 - ): # TODO: remember to remove this behaviour -> Numpy doesn't dimension reduce when slicing. But I am keeping it for now since it messes up the sum, means etc tests due to shape inconsistencies. + nshape.append(slice_len) + ncoefficients.append(self.strides[i] * step) + ndims += 1 + noffset += start * self.strides[i] + + if len(nshape) == 0: + nshape.append(1) + ncoefficients.append(1) + + # only C & F order are supported + var nstrides: List[Int] = self._calculate_strides_efficient( + nshape, + ) + var narr: Self = Self(offset=noffset, shape=nshape, strides=nstrides) + var index: List[Int] = List[Int](length=ndims, fill=0) + + _traverse_iterative[dtype]( + self, narr, nshape, ncoefficients, nstrides, noffset, index, 0 + ) + + return narr^ + + fn _getitem_variadic_slices(self, var *slices: Slice) raises -> Self: + """ + Alternative implementation of `__getitem__(self, owned *slices: Slice)` which reduces dimension unlike the original one which is compatible with numpy slicing. + + Args: + slices: Variadic list of `Slice` objects, one for each dimension to be sliced. + + Constraints: + - The number of slices provided must not exceed the number of array dimensions. + - Each slice must be valid for its corresponding dimension. + + Returns: + Self: A new array instance representing the sliced view of the original array. + + Raises: + IndexError: If any slice is out of bounds for its corresponding dimension. + ValueError: If the number of slices does not match the array's dimensions. + + NOTES: + - This method is for internal purposes only and is not exposed to users. + """ + var n_slices: Int = slices.__len__() + if n_slices > self.ndim: + raise Error( + IndexError( + message=String( + "Too many slices provided: expected at most {} but" + " got {}." + ).format(self.ndim, n_slices), + suggestion=String( + "Provide at most {} slices for an array with {}" + " dimensions." + ).format(self.ndim, self.ndim), + location=String("NDArray.__getitem__(slices: Slice)"), + ) + ) + var slice_list: List[Slice] = List[Slice](capacity=self.ndim) + for i in range(len(slices)): + slice_list.append(slices[i]) + + if n_slices < self.ndim: + for i in range(n_slices, self.ndim): + slice_list.append(Slice(0, self.shape[i], 1)) + + var narr: Self = self[slice_list^] + return narr^ + + fn _getitem_list_slices(self, var slice_list: List[Slice]) raises -> Self: + """ + Alternative implementation of `__getitem__(self, owned slice_list: List[Slice])` for which reduces dimension unlike the original one which is compatible with numpy slicing. + + Args: + slice_list: List of Slice objects, where each Slice defines the start, stop, and step for the corresponding dimension. + + Returns: + Self: A new array instance representing the sliced view of the original array. + + Raises: + Error: If slice_list is empty or contains invalid slices. + Error: The length of slice_list must not exceed the number of dimensions in the array. + Error: Each Slice in slice_list must be valid for its respective dimension. + + Notes: + This function is only for internal use since it's not compatible with numpy slicing. + """ + var n_slices: Int = slice_list.__len__() + if n_slices == 0: + raise Error( + IndexError( + message=String( + "Empty slice list provided to NDArray.__getitem__." + ), + suggestion=String( + "Provide a List with at least one slice to index the" + " array." + ), + location=String( + "NDArray.__getitem__(slice_list: List[Slice])" + ), + ) + ) + + # adjust slice values for user provided slices + var slices: List[Slice] = self._adjust_slice(slice_list) + if n_slices < self.ndim: + for i in range(n_slices, self.ndim): + slices.append(Slice(0, self.shape[i], 1)) + + var ndims: Int = 0 + var nshape: List[Int] = List[Int]() + var ncoefficients: List[Int] = List[Int]() + var noffset: Int = 0 + + for i in range(self.ndim): + var start: Int = slices[i].start.value() + var end: Int = slices[i].end.value() + var step: Int = slices[i].step.or_else(1) + + var slice_len: Int + if step > 0: + slice_len: Int = max(0, (end - start + (step - 1)) // step) + else: + slice_len: Int = max(0, (start - end - step - 1) // (-step)) + if slice_len > 1: nshape.append(slice_len) ncoefficients.append(self.strides[i] * step) ndims += 1 @@ -1706,8 +1831,9 @@ struct NDArray[dtype: DType = DType.float64]( Examples: ```mojo - import numojo - var A = numojo.ones(numojo.Shape(2,3,4)) + import numojo as nm + from numojo.prelude import * + var A = nm.ones[f32](nm.Shape(2,3,4)) A._setitem(1,2,3, val=10) ``` """ @@ -4765,7 +4891,6 @@ struct NDArray[dtype: DType = DType.float64]( Returns: Array of the same data with a new shape. """ - print("WTF IS HAPPENING") return numojo.reshape(self, shape=shape, order=order) fn resize(mut self, shape: NDArrayShape) raises: @@ -5123,6 +5248,49 @@ struct NDArray[dtype: DType = DType.float64]( sum = sum + self.load(i) * other.load(i) return sum + fn squeeze(mut self, axis: Int) raises: + """ + Remove (squeeze) a single dimension of size 1 from the array shape. + + Args: + axis: The axis to squeeze. Supports negative indices. + + Raises: + IndexError: If the axis is out of range. + ShapeError: If the dimension at the given axis is not of size 1. + """ + var normalized_axis: Int = axis + if normalized_axis < 0: + normalized_axis += self.ndim + if (normalized_axis < 0) or (normalized_axis >= self.ndim): + raise Error( + IndexError( + message=String( + "Axis {} is out of range for array with {} dimensions." + ).format(axis, self.ndim), + suggestion=String( + "Use an axis value in the range [-{}, {})." + ).format(self.ndim, self.ndim), + location=String("NDArray.squeeze(axis: Int)"), + ) + ) + + if self.shape[normalized_axis] != 1: + raise Error( + ShapeError( + message=String( + "Cannot squeeze axis {} with size {}." + ).format(normalized_axis, self.shape[normalized_axis]), + suggestion=String( + "Only axes with length 1 can be removed." + ), + location=String("NDArray.squeeze(axis: Int)"), + ) + ) + self.shape = self.shape._pop(normalized_axis) + self.strides = self.strides._pop(normalized_axis) + self.ndim -= 1 + # ===----------------------------------------------------------------------===# # NDArrayIterator diff --git a/numojo/prelude.mojo b/numojo/prelude.mojo index f0254dd4..60eb08fe 100644 --- a/numojo/prelude.mojo +++ b/numojo/prelude.mojo @@ -30,31 +30,40 @@ from numojo.core.complex.complex_simd import ComplexSIMD, CScalar from numojo.core.complex.complex_ndarray import ComplexNDArray from numojo.core.complex.complex_dtype import ( ci8, - ci16, - ci32, ci64, - cisize, - cintp, + ci128, + ci256, + cint, cu8, cu16, cu32, cu64, + cu128, + cu256, + cuint, + cbf16, cf16, cf32, cf64, cboolean, + cinvalid, ) from numojo.core.datatypes import ( i8, i16, i32, i64, - isize, - intp, + i128, + i256, + int, u8, u16, u32, u64, + u128, + u256, + uint, + bf16, f16, f32, f64, diff --git a/numojo/routines/creation.mojo b/numojo/routines/creation.mojo index edb34243..4694fa9e 100644 --- a/numojo/routines/creation.mojo +++ b/numojo/routines/creation.mojo @@ -10,9 +10,7 @@ Array creation routine. # TODO (In order of priority) 1) Implement axis argument for the NDArray creation functions 2) Separate `array(object)` and `NDArray.__init__(shape)`. -3) Use `Shapelike` trait to replace `NDArrayShape`, `List`, `VariadicList` and -3) Use `Shapelike` trait to replace `NDArrayShape`, `List`, `VariadicList` and - reduce the number of function reloads. +3) Use `Shapelike` trait to replace `NDArrayShape`, `List`, `VariadicList` and reduce the number of function reloads. 4) Simplify complex overloads into sum of real methods. --- @@ -26,11 +24,7 @@ overload for each function. This makes maintenance easier. Example: - `zeros`, `ones` calls `full`. - Other functions calls `zeros`, `ones`, `full`. -If overloads are needed, it is better to call the default signature in other -overloads. Example: `zeros(shape: NDArrayShape)`. All other overloads call this -If overloads are needed, it is better to call the default signature in other -overloads. Example: `zeros(shape: NDArrayShape)`. All other overloads call this -function. So it is easy for modification. +If overloads are needed, it is better to call the default signature in other overloads. Example: `zeros(shape: NDArrayShape)`. All other overloads call this function. So it is easy for modification. """ diff --git a/numojo/routines/manipulation.mojo b/numojo/routines/manipulation.mojo index 97868575..756068fd 100644 --- a/numojo/routines/manipulation.mojo +++ b/numojo/routines/manipulation.mojo @@ -141,7 +141,6 @@ fn reshape[ Returns: Array of the same data with a new shape. """ - print("HOLY") if A.size != shape.size_of_array(): raise Error("Cannot reshape: Number of elements do not match.") @@ -255,10 +254,10 @@ fn transpose[ Examples. ```mojo import numojo as nm - # A is a 2darray - print(nm.transpose(A, axes=List(0, 1))) # equal to transpose of matrix - # A is a 3darray - print(nm.transpose(A, axes=List(2, 1, 0))) # transpose 0-th and 2-th dimensions + var arr2d = nm.random.rand(2,3) + print(nm.transpose(arr2d, axes=List(0, 1))) # equal to transpose of matrix + var arr3d = nm.random.rand(2,3,4) + print(nm.transpose(arr3d, axes=List(2, 1, 0))) # transpose 0-th and 2-th dimensions ``` """ if len(axes) != A.ndim: @@ -299,15 +298,14 @@ fn transpose[ # TODO: Make this operation in place to match numpy. -fn transpose[dtype: DType](var A: NDArray[dtype]) raises -> NDArray[dtype]: +fn transpose[dtype: DType](A: NDArray[dtype]) raises -> NDArray[dtype]: """ (overload) Transpose the array when `axes` is not given. If `axes` is not given, it is equal to flipping the axes. See docstring of `transpose`. """ - if A.ndim == 1: - return A^ + return A.copy() if A.ndim == 2: var array_order = "C" if A.flags.C_CONTIGUOUS else "F" var B = NDArray[dtype](Shape(A.shape[1], A.shape[0]), order=array_order) diff --git a/numojo/routines/math/products.mojo b/numojo/routines/math/products.mojo index 031ca4db..38268dea 100644 --- a/numojo/routines/math/products.mojo +++ b/numojo/routines/math/products.mojo @@ -71,10 +71,11 @@ fn prod[ slices.append(Slice(0, A.shape[i])) else: slices.append(Slice(0, 0)) # Temp value - var result = ones[dtype](NDArrayShape(result_shape)) + var result: NDArray[dtype] = ones[dtype](NDArrayShape(result_shape)) for i in range(size_of_axis): slices[axis] = Slice(i, i + 1) - var arr_slice = A[slices.copy()] + # TODO: modify slicing getter to avoid copy. + var arr_slice: NDArray[dtype] = A._getitem_list_slices(slices.copy()) result *= arr_slice return result^ diff --git a/numojo/routines/math/sums.mojo b/numojo/routines/math/sums.mojo index b9021e49..0d1ee799 100644 --- a/numojo/routines/math/sums.mojo +++ b/numojo/routines/math/sums.mojo @@ -62,7 +62,7 @@ fn sum[dtype: DType](A: NDArray[dtype], axis: Int) raises -> NDArray[dtype]: An NDArray. """ - var normalized_axis = axis + var normalized_axis: Int = axis if normalized_axis < 0: normalized_axis += A.ndim @@ -99,10 +99,10 @@ fn sum[dtype: DType](A: NDArray[dtype], axis: Int) raises -> NDArray[dtype]: slices.append(Slice(0, A.shape[i])) else: slices.append(Slice(0, 0)) # Temp value - var result = zeros[dtype](NDArrayShape(result_shape)) + var result: NDArray[dtype] = zeros[dtype](NDArrayShape(result_shape)) for i in range(size_of_axis): slices[normalized_axis] = Slice(i, i + 1) - var arr_slice = A[slices.copy()] + var arr_slice: NDArray[dtype] = A._getitem_list_slices(slices.copy()) result += arr_slice return result^ diff --git a/pixi.toml b/pixi.toml index c68bb089..2dc34890 100644 --- a/pixi.toml +++ b/pixi.toml @@ -34,13 +34,13 @@ backend = {name = "pixi-build-mojo", version = "0.*", channels = [ name = "numojo" [package.host-dependencies] -modular = ">=25.6.0,<26" +modular = ">=25.6.1,<26" [package.build-dependencies] -modular = ">=25.6.0,<26" +modular = ">=25.6.1,<26" [package.run-dependencies] -modular = ">=25.6.0,<26" +modular = ">=25.6.1,<26" [tasks] # compile the package and copy it to the tests folder @@ -78,7 +78,7 @@ doc_pages = "mojo doc numojo/ -o docs.json" release = "clear && pixi run final && pixi run doc_pages" [dependencies] -python = ">=3.13.5,<3.14" -numpy = ">=2.3.2,<3" -scipy = ">=1.16.0,<2" -modular = ">=25.6.0,<26" +python = ">=3.13.9,<3.14" +numpy = ">=2.3.3,<3" +scipy = ">=1.16.2,<2" +modular = ">=25.6.1,<26" diff --git a/tests/core/test_array_indexing_and_slicing.mojo b/tests/core/test_array_indexing_and_slicing.mojo index ba7033c4..8c08098f 100644 --- a/tests/core/test_array_indexing_and_slicing.mojo +++ b/tests/core/test_array_indexing_and_slicing.mojo @@ -95,7 +95,7 @@ def test_slicing_getter6(): var np = Python.import_module("numpy") var b = nm.arange[i8](60).reshape(nm.Shape(3, 4, 5)) - var ind = nm.array[isize]("[[2,0,1], [1,0,1]]") + var ind = nm.array[int]("[[2,0,1], [1,0,1]]") var mask = nm.array[boolean]("[1,0,1]") var bnp = b.to_numpy() diff --git a/tests/routines/test_indexing.mojo b/tests/routines/test_indexing.mojo index 861b7a99..72c44e2c 100644 --- a/tests/routines/test_indexing.mojo +++ b/tests/routines/test_indexing.mojo @@ -73,7 +73,7 @@ fn test_take_along_axis() raises: # Test 1-D array var a1d = nm.arange[i8](10) var a1d_np = a1d.to_numpy() - var indices1d = nm.array[intp]("[2, 3, 1, 8]") + var indices1d = nm.array[int]("[2, 3, 1, 8]") var indices1d_np = indices1d.to_numpy() check( @@ -85,7 +85,7 @@ fn test_take_along_axis() raises: # Test 2-D array with axis=0 var a2d = nm.arange[i8](12).reshape(Shape(3, 4)) var a2d_np = a2d.to_numpy() - var indices2d_0 = nm.array[intp]("[[0, 1, 2, 0], [1, 2, 0, 1]]") + var indices2d_0 = nm.array[int]("[[0, 1, 2, 0], [1, 2, 0, 1]]") var indices2d_0_np = indices2d_0.to_numpy() check( @@ -95,7 +95,7 @@ fn test_take_along_axis() raises: ) # Test 2-D array with axis=1 - var indices2d_1 = nm.array[intp]( + var indices2d_1 = nm.array[int]( "[[3, 0, 2, 1], [1, 3, 0, 0], [2, 1, 0, 3]]" ) var indices2d_1_np = indices2d_1.to_numpy() @@ -111,7 +111,7 @@ fn test_take_along_axis() raises: var a3d_np = a3d.to_numpy() # Test with axis=0 - var indices3d_0 = nm.zeros[intp](Shape(1, 3, 4)) + var indices3d_0 = nm.zeros[int](Shape(1, 3, 4)) var indices3d_0_np = indices3d_0.to_numpy() check( @@ -121,7 +121,7 @@ fn test_take_along_axis() raises: ) # Test with axis=1 - var indices3d_1 = nm.array[intp]( + var indices3d_1 = nm.array[int]( "[[[0, 1, 0, 2], [2, 1, 0, 1], [1, 2, 2, 0]], [[1, 0, 1, 2], [0, 2, 1," " 0], [2, 0, 0, 1]]]" ) @@ -134,7 +134,7 @@ fn test_take_along_axis() raises: ) # Test with axis=2 - var indices3d_2 = nm.array[intp]( + var indices3d_2 = nm.array[int]( "[[[2, 0, 3, 1], [1, 3, 0, 2], [3, 1, 2, 0]], [[0, 2, 1, 3], [2, 0, 3," " 1], [1, 3, 0, 2]]]" ) @@ -160,7 +160,7 @@ fn test_take_along_axis() raises: var a2d_test_np = a2d_test.to_numpy() # For axis=0, using indices of shape (2, 4) - different first dim, same second dim - var indices2d_axis0 = nm.array[intp]("[[0, 1, 2, 0], [1, 0, 2, 1]]") + var indices2d_axis0 = nm.array[int]("[[0, 1, 2, 0], [1, 0, 2, 1]]") var indices2d_axis0_np = indices2d_axis0.to_numpy() check( @@ -173,7 +173,7 @@ fn test_take_along_axis() raises: ) # For axis=1, using indices of shape (3, 2) - same first dim, different second dim - var indices2d_axis1 = nm.array[intp]("[[0, 3], [2, 1], [1, 3]]") + var indices2d_axis1 = nm.array[int]("[[0, 3], [2, 1], [1, 3]]") var indices2d_axis1_np = indices2d_axis1.to_numpy() check( @@ -191,7 +191,7 @@ fn test_take_along_axis() raises: var a3d_test_np = a3d_test.to_numpy() # For axis=0, indices of shape (1, 3, 4) - same shape except dim 0 - var ind_axis0 = nm.zeros[intp](Shape(1, 3, 4)) + var ind_axis0 = nm.zeros[int](Shape(1, 3, 4)) var ind_axis0_np = ind_axis0.to_numpy() check( @@ -204,7 +204,7 @@ fn test_take_along_axis() raises: ) # For axis=2, indices of shape (2, 3, 2) - same shape except dim 2 - var ind_axis2 = nm.array[intp]( + var ind_axis2 = nm.array[int]( "[[[0, 3], [2, 1], [3, 0]], [[1, 2], [0, 3], [2, 1]]]" ) var ind_axis2_np = ind_axis2.to_numpy() @@ -227,7 +227,7 @@ fn test_take_along_axis_fortran_order() raises: var a3d_f_np = a3d_f.to_numpy() # Test with axis=0 - var indices3d_0 = nm.zeros[intp](Shape(1, 3, 4)) + var indices3d_0 = nm.zeros[int](Shape(1, 3, 4)) var indices3d_0_np = indices3d_0.to_numpy() check( @@ -237,7 +237,7 @@ fn test_take_along_axis_fortran_order() raises: ) # Test with axis=1 - var indices3d_1 = nm.array[intp]( + var indices3d_1 = nm.array[int]( "[[[0, 1, 0, 2], [2, 1, 0, 1], [1, 2, 2, 0]], [[1, 0, 1, 2], [0, 2, 1," " 0], [2, 0, 0, 1]]]" ) @@ -250,7 +250,7 @@ fn test_take_along_axis_fortran_order() raises: ) # Test with axis=2 - var indices3d_2 = nm.array[intp]( + var indices3d_2 = nm.array[int]( "[[[2, 0, 3, 1], [1, 3, 0, 2], [3, 1, 2, 0]], [[0, 2, 1, 3], [2, 0, 3," " 1], [1, 3, 0, 2]]]" )