diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index f1885bc3..2476dd9d 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -37,14 +37,46 @@ jobs: - name: Pixi install run: | - pixi install + pixi install --locked - name: Build package run: | pixi run mojo package numojo cp numojo.mojopkg tests/ - - name: Run tests + - name: Run Core Tests run: | - pixi run mojo test tests -I . - pixi run mojo test tests/core/test_matrix.mojo -I . -D F_CONTIGUOUS + echo "Testing core modules..." + pixi run mojo run -I tests/ tests/core/test_array_indexing_and_slicing.mojo + pixi run mojo run -I tests/ tests/core/test_array_methods.mojo + pixi run mojo run -I tests/ tests/core/test_bool_masks.mojo + pixi run mojo run -I tests/ tests/core/test_complexArray.mojo + pixi run mojo run -I tests/ tests/core/test_complexSIMD.mojo + pixi run mojo run -I tests/ tests/core/test_matrix.mojo + pixi run mojo run -I tests/ -D F_CONTIGUOUS tests/core/test_matrix.mojo + pixi run mojo run -I tests/ tests/core/test_shape_strides_item.mojo + + - name: Run Routine Tests + run: | + echo "Testing routines..." + pixi run mojo run -I tests/ tests/routines/test_creation.mojo + pixi run mojo run -I tests/ tests/routines/test_functional.mojo + pixi run mojo run -I tests/ tests/routines/test_indexing.mojo + pixi run mojo run -I tests/ tests/routines/test_io.mojo + pixi run mojo run -I tests/ tests/routines/test_linalg.mojo + pixi run mojo run -I tests/ tests/routines/test_manipulation.mojo + pixi run mojo run -I tests/ tests/routines/test_math.mojo + pixi run mojo run -I tests/ tests/routines/test_random.mojo + pixi run mojo run -I tests/ tests/routines/test_statistics.mojo + pixi run mojo run -I tests/ tests/routines/test_sorting.mojo + pixi run mojo run -I tests/ tests/routines/test_searching.mojo + + - name: Run Science Tests + run: | + echo "Testing science modules..." + pixi run mojo run -I tests/ tests/science/test_signal.mojo + + - name: Cleanup + if: always() + run: | + rm -f tests/numojo.mojopkg diff --git a/.gitignore b/.gitignore index 84d1b604..b4c52fb5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ # pixi environments .pixi -pixi.lock /venv @@ -28,6 +27,7 @@ numojo.mojopkg /bench.mojo /test*.mojo /test*.ipynb +bench_*.mojo /tempCodeRunnerFile.mojo kgen.trace.* diff --git a/README.MD b/README.MD index b71af871..71ac1454 100644 --- a/README.MD +++ b/README.MD @@ -205,18 +205,18 @@ backend = {name = "pixi-build-mojo", version = "0.*"} name = "your_package_name" [package.host-dependencies] -modular = ">=25.5.0,<26" +modular = ">=25.7.0,<26" [package.build-dependencies] -modular = ">=25.5.0,<26" +modular = ">=25.7.0,<26" numojo = { git = "https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo", branch = "main"} [package.run-dependencies] -modular = ">=25.5.0,<26" +modular = ">=25.7.0,<26" numojo = { git = "https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo", branch = "main"} [dependencies] -max = "=25.5.0" +modular = ">=25.7.0,<26" numojo = { git = "https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo", branch = "main"} ``` @@ -227,7 +227,7 @@ pixi install **Branch Selection:** - **`main` branch**: Provides stable release. Currently supports NuMojo v0.7.0, compatible with Mojo 25.3.0. For earlier NuMojo versions, use Method 2. -- **`pre-x.y` branches**: Active development branch supporting the latest Mojo version (currently 25.5.0). Note that this branch receives frequent updates and may have breaking changes in features and syntax. +- **`pre-x.y` branches**: Active development branch supporting the latest Mojo version (currently 25.7.0). Note that this branch receives frequent updates and may have breaking changes in features and syntax. The package will be automatically available in your Pixi environment, and VSCode LSP will provide intelligent code hints. diff --git a/assets/matrix_test.mojo b/assets/matrix_test.mojo new file mode 100644 index 00000000..a9ceec04 --- /dev/null +++ b/assets/matrix_test.mojo @@ -0,0 +1,1811 @@ +""" +`numojo.Matrix` provides: + +- `Matrix` type (2DArray). +- `_MatrixIter` type (for iteration). +- Dunder methods for initialization, indexing, slicing, and arithmetics. +- Auxiliary functions. +""" + +from algorithm import parallelize, vectorize +from memory import UnsafePointer, memcpy, memset_zero +from random import random_float64 +from sys import simd_width_of +from python import PythonObject, Python + +from numojo.core.flags import Flags +from numojo.core.ndarray import NDArray +from numojo.core.data_container import DataContainer +from numojo.core.utility import _get_offset +from numojo.routines.manipulation import broadcast_to, reorder_layout +from numojo.routines.linalg.misc import issymmetric + + +# ===----------------------------------------------------------------------===# +# Matrix struct +# ===----------------------------------------------------------------------===# + + +struct Matrix[dtype: DType = DType.float64]( + ImplicitlyCopyable, Movable, Sized, Stringable, Writable +): + # TODO: Add buffer_type in the parameters. + """ + `Matrix` is a special case of `NDArray` (2DArray) but has some targeted + optimization since the number of dimensions is known at the compile time. + It has simpler indexing and slicing methods, which is very useful when users + only want to work with 2-dimensional arrays. + + NuMojo's `Matrix` is `NDArray` with fixed `ndim` known at compile time. + It may be different in some behaviors compared to `numpy.matrix`. + + - For `__getitem__`, passing in two `Int` returns a scalar, + and passing in one `Int` or two `Slice` returns a `Matrix`. + - We do not need auxiliary types `NDArrayShape` and `NDArrayStrides` + as the shape and strides information is fixed in length `Tuple[Int,Int]`. + + Parameters: + dtype: Type of item in NDArray. Default type is DType.float64. + + The matrix can be uniquely defined by the following features: + 1. The data buffer of all items. + 2. The shape of the matrix. + 3. The data type of the elements (compile-time known). + + Attributes: + - _buf (saved as row-majored, C-type) + - shape + - size (shape[0] * shape[1]) + - strides (shape[1], 1) + + Default constructor: + - [dtype], shape + - [dtype], data + + [checklist] CORE METHODS that have been implemented: + - [x] `Matrix.any` and `mat.logic.all` + - [x] `Matrix.any` and `mat.logic.any` + - [x] `Matrix.argmax` and `mat.sorting.argmax` + - [x] `Matrix.argmin` and `mat.sorting.argmin` + - [x] `Matrix.argsort` and `mat.sorting.argsort` + - [x] `Matrix.astype` + - [x] `Matrix.cumprod` and `mat.mathematics.cumprod` + - [x] `Matrix.cumsum` and `mat.mathematics.cumsum` + - [x] `Matrix.fill` and `mat.creation.full` + - [x] `Matrix.flatten` + - [x] `Matrix.inv` and `mat.linalg.inv` + - [x] `Matrix.max` and `mat.sorting.max` + - [x] `Matrix.mean` and `mat.statistics.mean` + - [x] `Matrix.min` and `mat.sorting.min` + - [x] `Matrix.prod` and `mat.mathematics.prod` + - [x] `Matrix.reshape` + - [x] `Matrix.resize` + - [x] `Matrix.round` and `mat.mathematics.round` (TODO: Check this after next Mojo update) + - [x] `Matrix.std` and `mat.statistics.std` + - [x] `Matrix.sum` and `mat.mathematics.sum` + - [x] `Matrix.trace` and `mat.linalg.trace` + - [x] `Matrix.transpose` and `mat.linalg.transpose` (also `Matrix.T`) + - [x] `Matrix.variance` and `mat.statistics.variance` (`var` is primitive) + """ + + alias width: Int = simd_width_of[dtype]() # + """Vector size of the data type.""" + + # var _buf: DataContainer[dtype] + var _buf: UnsafePointer[Scalar[dtype], **_] + """Data buffer of the items in the NDArray.""" + + var shape: Tuple[Int, Int] + """Shape of Matrix.""" + + var size: Int + """Size of Matrix.""" + + var strides: Tuple[Int, Int] + """Strides of matrix.""" + + var flags: Flags + "Information about the memory layout of the array." + + # ===-------------------------------------------------------------------===# + # Life cycle methods + # ===-------------------------------------------------------------------===# + + @always_inline("nodebug") + fn __init__( + out self, + shape: Tuple[Int, Int], + order: String = "C", + ): + """ + Create a new matrix of the given shape,without initializing data. + + Args: + shape: Tuple representing (rows, columns). + order: Use "C" for row-major (C-style) layout or "F" for column-major + (Fortran-style) layout. Defaults to "C". + """ + + self.shape = (shape[0], shape[1]) + if order == "C": + self.strides = (shape[1], 1) + else: + self.strides = (1, shape[0]) + self.size = shape[0] * shape[1] + # self._buf = DataContainer[dtype](size=self.size) + self._buf = UnsafePointer[ + Scalar[dtype], mut=True, origin = MutableOrigin.empty + ].alloc(self.size) + self.flags = Flags( + self.shape, self.strides, owndata=True, writeable=True + ) + + # * Should we take var ref and transfer ownership or take a read ref and copy it? + # @always_inline("nodebug") + # fn __init__( + # out self, + # var data: Self, + # ): + # """ + # Construct a matrix from matrix. + # """ + + # self = data^ + + # @always_inline("nodebug") + # fn __init__( + # out self, + # data: NDArray[dtype], + # ) raises: + # """ + # Construct a matrix from array. + # """ + + # if data.ndim == 1: + # self.shape = (1, data.shape[0]) + # self.strides = (data.shape[0], 1) + # self.size = data.shape[0] + # elif data.ndim == 2: + # self.shape = (data.shape[0], data.shape[1]) + # self.strides = (data.shape[1], 1) + # self.size = data.shape[0] * data.shape[1] + # else: + # raise Error(String("Shape too large to be a matrix.")) + + # self._buf = DataContainer[dtype](self.size) + + # self.flags = Flags( + # self.shape, self.strides, owndata=True, writeable=True + # ) + + # if data.flags["C_CONTIGUOUS"]: + # for i in range(data.shape[0]): + # memcpy( + # self._buf.ptr.offset(i * self.shape[0]), + # data._buf.ptr.offset(i * data.shape[0]), + # self.shape[0], + # ) + # else: + # for i in range(data.shape[0]): + # for j in range(data.shape[1]): + # self._store(i, j, data._getitem(i, j)) + + @always_inline("nodebug") + fn __init__( + out self, + shape: Tuple[Int, Int], + strides: Tuple[Int, Int], + offset: Int, + ptr: UnsafePointer[Scalar[dtype], *_], + ): + """ + Initialize Matrix that does not own the data. + The data is owned by another Matrix. + + Args: + shape: Shape of the view. + strides: Strides of the view. + offset: Offset in pointer of the data buffer. + ptr: Pointer to the data buffer of the original array. + """ + self.shape = shape + self.strides = strides + self.size = shape[0] * shape[1] + # self._buf = DataContainer(ptr=ptr.offset(offset)) + self._buf = ptr.offset(offset) + self.flags = Flags( + self.shape, self.strides, owndata=False, writeable=False + ) + + @always_inline("nodebug") + fn __copyinit__(out self, other: Self): + """ + Copy other into self. + """ + self.shape = (other.shape[0], other.shape[1]) + self.strides = (other.strides[0], other.strides[1]) + self.size = other.size + # self._buf = DataContainer[dtype](other.size) + # memcpy(self._buf.ptr, other._buf.ptr, other.size) + self._buf = UnsafePointer[ + Scalar[dtype], mut=True, origin = MutableOrigin.empty + ].alloc(other.size) + memcpy(self._buf, other._buf, other.size) + self.flags = other.flags + + @always_inline("nodebug") + fn __moveinit__(out self, deinit other: Self): + """ + Move other into self. + """ + self.shape = other.shape^ + self.strides = other.strides^ + self.size = other.size + self._buf = other._buf + self.flags = other.flags^ + + @always_inline("nodebug") + fn __del__(deinit self): + var owndata: Bool = self.flags.OWNDATA + if owndata: + print("Matrix __del__ called", self.size, self.flags.OWNDATA) + self._buf.free() + + # ===-------------------------------------------------------------------===# + # Slicing and indexing methods + # ===-------------------------------------------------------------------===# + + fn __getitem__(self, var x: Int, var y: Int) raises -> Scalar[dtype]: + """ + Return the scalar at the index. + + Args: + x: The row number. + y: The column number. + + Returns: + A scalar matching the dtype of the array. + """ + + if x < 0: + x = self.shape[0] + x + + if y < 0: + y = self.shape[1] + y + + if (x >= self.shape[0]) or (y >= self.shape[1]): + raise Error( + String( + "Index ({}, {}) exceed the matrix shape ({}, {})" + ).format(x, y, self.shape[0], self.shape[1]) + ) + + # return self._buf.load(x * self.strides[0] + y * self.strides[1]) + return self._buf.load(x * self.strides[0] + y * self.strides[1]) + + fn __getitem__(ref self: Self, var x: Int) -> Matrix[dtype]: + """ + Return the corresponding row at the index. + + Args: + x: The row number. + """ + print("_getitem__ called") + # var new_ptr = self._buf.origin_cast[ + # target_mut = True, + # target_origin=MutableOrigin.cast_from[__origin_of(self)], + # ]() + var new_ptr = self._buf.origin_cast[ + Origin(__origin_of(self)).mut, __origin_of(self) + ]() + return Matrix[dtype]( + shape=(1, self.shape[1]), + strides=(self.strides[0], self.strides[1]), + offset=x * self.strides[0], + ptr=new_ptr, + # ptr = self._buf.get_ptr() + ) + + fn _store[ + width: Int = 1 + ](mut self, var x: Int, simd: SIMD[dtype, width]) raises: + """ + `__setitem__` for row with width. + Unsafe: No boundary check! + """ + self._buf.store(x, simd) + + # fn __getitem__(self, var x: Int) raises -> Self: + # """ + # Return the corresponding row at the index. + + # Args: + # x: The row number. + # """ + + # if x < 0: + # x = self.shape[0] + x + + # if x >= self.shape[0]: + # raise Error( + # String("Index {} exceed the row number {}").format( + # x, self.shape[0] + # ) + # ) + + # var res = Self(shape=(1, self.shape[1]), order=self.order()) + + # if self.flags.C_CONTIGUOUS: + # var ptr = self._buf.ptr.offset(x * self.strides[0]) + # memcpy(res._buf.ptr, ptr, self.shape[1]) + # else: + # for j in range(self.shape[1]): + # res[0, j] = self[x, j] + + # return res^ + + fn __getitem__(self, x: Slice, y: Slice) -> Self: + """ + Get item from two slices. + """ + var start_x: Int + var end_x: Int + var step_x: Int + var start_y: Int + var end_y: Int + var step_y: Int + start_x, end_x, step_x = x.indices(self.shape[0]) + start_y, end_y, step_y = y.indices(self.shape[1]) + var range_x = range(start_x, end_x, step_x) + var range_y = range(start_y, end_y, step_y) + + # The new matrix with the corresponding shape + var B = Matrix[dtype]( + shape=(len(range_x), len(range_y)), order=self.order() + ) + + # Fill in the values at the corresponding index + var row = 0 + for i in range_x: + var col = 0 + for j in range_y: + B._store(row, col, self._load(i, j)) + col += 1 + row += 1 + + return B^ + + # fn __getitem__(self, x: Slice, var y: Int) -> Self: + # """ + # Get item from one slice and one int. + # """ + # if y < 0: + # y = self.shape[1] + y + + # var start_x: Int + # var end_x: Int + # var step_x: Int + # start_x, end_x, step_x = x.indices(self.shape[0]) + # var range_x = range(start_x, end_x, step_x) + + # # The new matrix with the corresponding shape + # var B = Matrix[dtype](shape=(len(range_x), 1), order=self.order()) + + # # Fill in the values at the corresponding index + # var row = 0 + # for i in range_x: + # B._store(row, 0, self._load(i, y)) + # row += 1 + + # return B^ + + # fn __getitem__(self, var x: Int, y: Slice) -> Self: + # """ + # Get item from one int and one slice. + # """ + # if x < 0: + # x = self.shape[0] + x + + # var start_y: Int + # var end_y: Int + # var step_y: Int + # start_y, end_y, step_y = y.indices(self.shape[1]) + # var range_y = range(start_y, end_y, step_y) + + # # The new matrix with the corresponding shape + # var B = Matrix[dtype](shape=(1, len(range_y)), order=self.order()) + + # # Fill in the values at the corresponding index + # var col = 0 + # for j in range_y: + # B._store(0, col, self._load(x, j)) + # col += 1 + + # return B^ + + # fn __getitem__(self, indices: List[Int]) raises -> Self: + # """ + # Get item by a list of integers. + # """ + + # var ncol = self.shape[1] + # var nrow = len(indices) + # var res = Matrix.zeros[dtype](shape=(nrow, ncol)) + # for i in range(nrow): + # res[i] = self[indices[i]] + # return res^ + + fn _load[width: Int = 1](self, x: Int, y: Int) -> SIMD[dtype, width]: + """ + `__getitem__` with width. + Unsafe: No boundary check! + """ + return self._buf.load[width=width]( + x * self.strides[0] + y * self.strides[1] + ) + + fn __setitem__(self, x: Int, y: Int, value: Scalar[dtype]) raises: + """ + Return the scalar at the index. + + Args: + x: The row number. + y: The column number. + value: The value to be set. + """ + + if (x >= self.shape[0]) or (y >= self.shape[1]): + raise Error( + String( + "Index ({}, {}) exceed the matrix shape ({}, {})" + ).format(x, y, self.shape[0], self.shape[1]) + ) + + self._buf.store(x * self.strides[0] + y * self.strides[1], value) + + fn __setitem__(self, var x: Int, value: Self) raises: + """ + Set the corresponding row at the index with the given matrix. + + Args: + x: The row number. + value: Matrix (row vector). + """ + + if x < 0: + x = self.shape[0] + x + + if x >= self.shape[0]: + raise Error( + String( + "Error: Elements of `index` ({}) \n" + "exceed the matrix shape ({})." + ).format(x, self.shape[0]) + ) + + if value.shape[0] != 1: + raise Error( + String( + "Error: The value should has only 1 row, " + "but it has {} rows." + ).format(value.shape[0]) + ) + + if self.shape[1] != value.shape[1]: + raise Error( + String( + "Error: Matrix has {} columns, " + "but the value has {} columns." + ).format(self.shape[1], value.shape[1]) + ) + + var ptr = self._buf.offset(x * self.shape[1]) + memcpy(ptr, value._buf, value.size) + + fn _store[ + width: Int = 1 + ](mut self, x: Int, y: Int, simd: SIMD[dtype, width]): + """ + `__setitem__` with width. + Unsafe: No boundary check! + """ + self._buf.store(x * self.strides[0] + y * self.strides[1], simd) + + # ===-------------------------------------------------------------------===# + # Other dunders and auxiliary methods + # ===-------------------------------------------------------------------===# + + fn __iter__(self) raises -> _MatrixIter[__origin_of(self), dtype]: + """Iterate over elements of the Matrix, returning copied value. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((4,4)) + for i in A: + print(i) + ``` + + Returns: + An iterator of Matrix elements. + """ + + return _MatrixIter[__origin_of(self), dtype]( + matrix=self, + length=self.shape[0], + ) + + fn __len__(self) -> Int: + """ + Returns length of 0-th dimension. + """ + return self.shape[0] + + fn __reversed__( + self, + ) raises -> _MatrixIter[__origin_of(self), dtype, forward=False]: + """Iterate backwards over elements of the Matrix, returning + copied value. + + Returns: + A reversed iterator of Matrix elements. + """ + + return _MatrixIter[__origin_of(self), dtype, forward=False]( + matrix=self, + length=self.shape[0], + ) + + fn __str__(self) -> String: + return String.write(self) + + fn write_to[W: Writer](self, mut writer: W): + fn print_row(self: Self, i: Int, sep: String) raises -> String: + var result: String = String("[") + var number_of_sep: Int = 1 + if self.shape[1] <= 6: + for j in range(self.shape[1]): + if j == self.shape[1] - 1: + number_of_sep = 0 + result += String(self[i, j]) + sep * number_of_sep + else: + for j in range(3): + result += String(self[i, j]) + sep + result += String("...") + sep + for j in range(self.shape[1] - 3, self.shape[1]): + if j == self.shape[1] - 1: + number_of_sep = 0 + result += String(self[i, j]) + sep * number_of_sep + result += String("]") + return result + + var sep: String = String("\t") + var newline: String = String("\n ") + var number_of_newline: Int = 1 + var result: String = "[" + + try: + if self.shape[0] <= 6: + for i in range(self.shape[0]): + if i == self.shape[0] - 1: + number_of_newline = 0 + result += ( + print_row(self, i, sep) + newline * number_of_newline + ) + else: + for i in range(3): + result += print_row(self, i, sep) + newline + result += String("...") + newline + for i in range(self.shape[0] - 3, self.shape[0]): + if i == self.shape[0] - 1: + number_of_newline = 0 + result += ( + print_row(self, i, sep) + newline * number_of_newline + ) + result += String("]") + writer.write( + result + + "\nDType: " + + String(self.dtype) + + " Shape: " + + String(self.shape[0]) + + "x" + + String(self.shape[1]) + + " Strides: " + + String(self.strides[0]) + + "," + + String(self.strides[1]) + + " C: " + + String(self.flags["C_CONTIGUOUS"]) + + " F: " + + String(self.flags["F_CONTIGUOUS"]) + + " Own: " + + String(self.flags["OWNDATA"]) + ) + except e: + print("Cannot transfer matrix to string!", e) + + # ===-------------------------------------------------------------------===# + # Arithmetic dunder methods + # ===-------------------------------------------------------------------===# + + fn __add__( + read self: Matrix[dtype, *_], read other: Matrix[dtype, *_] + ) raises -> Matrix[dtype, *_]: + # if (self.shape[0] == other.shape[0]) and ( + # self.shape[1] == other.shape[1] + # ): + return _arithmetic_func_matrix_matrix_to_matrix[dtype, SIMD.__add__]( + self, other + ) + + # fn __add__(self, other: Self) raises -> Self: + # if (self.shape[0] == other.shape[0]) and ( + # self.shape[1] == other.shape[1] + # ): + # return _arithmetic_func_matrix_matrix_to_matrix[ + # dtype, SIMD.__add__ + # ](self, other) + # elif (self.shape[0] < other.shape[0]) or ( + # self.shape[1] < other.shape[1] + # ): + # return _arithmetic_func_matrix_matrix_to_matrix[ + # dtype, SIMD.__add__ + # ](broadcast_to(self.copy(), other.shape, self.order()), other) + # else: + # return _arithmetic_func_matrix_matrix_to_matrix[ + # dtype, SIMD.__add__ + # ](self, broadcast_to(other.copy(), self.shape, self.order())) + + # fn __add__(self, other: Scalar[dtype]) raises -> Self: + # """Add matrix to scalar. + + # ```mojo + # from numojo import Matrix + # var A = Matrix.ones(shape=(4, 4)) + # print(A + 2) + # ``` + # """ + # return self + broadcast_to[dtype](other, self.shape, self.order()) + + fn __radd__(self, other: Scalar[dtype]) raises -> Self: + """ + Right-add. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(2 + A) + ``` + """ + return broadcast_to[dtype](other, self.shape, self.order()) + self + + fn __sub__(self, other: Self) raises -> Self: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__sub__ + ](self, other) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__sub__ + ](broadcast_to(self.copy(), other.shape, self.order()), other) + else: + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__sub__ + ](self, broadcast_to(other.copy(), self.shape, self.order())) + + fn __sub__(self, other: Scalar[dtype]) raises -> Self: + """Subtract matrix by scalar. + + ```mojo + from numojo import Matrix + A = Matrix(shape=(4, 4)) + print(A - 2) + ``` + """ + return self - broadcast_to[dtype](other, self.shape, self.order()) + + fn __rsub__(self, other: Scalar[dtype]) raises -> Self: + """ + Right-sub. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(2 - A) + ``` + """ + return broadcast_to[dtype](other, self.shape, self.order()) - self + + fn __mul__(self, other: Self) raises -> Self: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__mul__ + ](self, other) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__mul__ + ](broadcast_to(self.copy(), other.shape, self.order()), other) + else: + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__mul__ + ](self, broadcast_to(other.copy(), self.shape, self.order())) + + fn __mul__(self, other: Scalar[dtype]) raises -> Self: + """Mutiply matrix by scalar. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A * 2) + ``` + """ + return self * broadcast_to[dtype](other, self.shape, self.order()) + + fn __rmul__(self, other: Scalar[dtype]) raises -> Self: + """ + Right-mul. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(2 * A) + ``` + """ + return broadcast_to[dtype](other, self.shape, self.order()) * self + + fn __truediv__(self, other: Self) raises -> Self: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__truediv__ + ](self, other) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__truediv__ + ](broadcast_to(self.copy(), other.shape, self.order()), other) + else: + return _arithmetic_func_matrix_matrix_to_matrix[ + dtype, SIMD.__truediv__ + ](self, broadcast_to(other.copy(), self.shape, self.order())) + + fn __truediv__(self, other: Scalar[dtype]) raises -> Self: + """Divide matrix by scalar.""" + return self / broadcast_to[dtype](other, self.shape, order=self.order()) + + # Shouldn't we do the operation inplace? + fn __pow__(self, rhs: Scalar[dtype]) raises -> Self: + """Power of items.""" + var result: Self = self.copy() + for i in range(self.size): + result._buf.ptr[i] = self._buf.ptr[i].__pow__(rhs) + return result^ + + fn __lt__(self, other: Self) raises -> Matrix[DType.bool]: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.lt]( + self, other + ) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.lt]( + broadcast_to(self.copy(), other.shape, self.order()), other + ) + else: + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.lt]( + self, broadcast_to(other.copy(), self.shape, self.order()) + ) + + fn __lt__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: + """Matrix less than scalar. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A < 2) + ``` + """ + return self < broadcast_to[dtype](other, self.shape, self.order()) + + fn __le__(self, other: Self) raises -> Matrix[DType.bool]: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.le]( + self, other + ) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.le]( + broadcast_to(self.copy(), other.shape, self.order()), other + ) + else: + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.le]( + self, broadcast_to(other.copy(), self.shape, self.order()) + ) + + fn __le__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: + """Matrix less than and equal to scalar. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A <= 2) + ``` + """ + return self <= broadcast_to[dtype](other, self.shape, self.order()) + + fn __gt__(self, other: Self) raises -> Matrix[DType.bool]: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.gt]( + self, other + ) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.gt]( + broadcast_to(self.copy(), other.shape, self.order()), other + ) + else: + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.gt]( + self, broadcast_to(other.copy(), self.shape, self.order()) + ) + + fn __gt__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: + """Matrix greater than scalar. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A > 2) + ``` + """ + return self > broadcast_to[dtype](other, self.shape, self.order()) + + fn __ge__(self, other: Self) raises -> Matrix[DType.bool]: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ge]( + self, other + ) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ge]( + broadcast_to(self.copy(), other.shape, self.order()), other + ) + else: + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ge]( + self, broadcast_to(other.copy(), self.shape, self.order()) + ) + + fn __ge__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: + """Matrix greater than and equal to scalar. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A >= 2) + ``` + """ + return self >= broadcast_to[dtype](other, self.shape, self.order()) + + fn __eq__(self, other: Self) raises -> Matrix[DType.bool]: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.eq]( + self, other + ) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.eq]( + broadcast_to(self.copy(), other.shape, self.order()), other + ) + else: + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.eq]( + self, broadcast_to(other.copy(), self.shape, self.order()) + ) + + fn __eq__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: + """Matrix less than and equal to scalar. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A == 2) + ``` + """ + return self == broadcast_to[dtype](other, self.shape, self.order()) + + fn __ne__(self, other: Self) raises -> Matrix[DType.bool]: + if (self.shape[0] == other.shape[0]) and ( + self.shape[1] == other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ne]( + self, other + ) + elif (self.shape[0] < other.shape[0]) or ( + self.shape[1] < other.shape[1] + ): + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ne]( + broadcast_to(self.copy(), other.shape, self.order()), other + ) + else: + return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ne]( + self, broadcast_to(other.copy(), self.shape, self.order()) + ) + + fn __ne__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: + """Matrix less than and equal to scalar. + + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A != 2) + ``` + """ + return self != broadcast_to[dtype](other, self.shape, self.order()) + + fn __matmul__(self, other: Self) raises -> Self: + return numojo.linalg.matmul(self, other) + + # ===-------------------------------------------------------------------===# + # Core methods + # ===-------------------------------------------------------------------===# + + fn all(self) -> Scalar[dtype]: + """ + Test whether all array elements evaluate to True. + """ + return numojo.logic.all(self) + + fn all(self, axis: Int) raises -> Self: + """ + Test whether all array elements evaluate to True along axis. + """ + return numojo.logic.all(self, axis=axis) + + fn any(self) -> Scalar[dtype]: + """ + Test whether any array elements evaluate to True. + """ + return numojo.logic.any(self) + + fn any(self, axis: Int) raises -> Self: + """ + Test whether any array elements evaluate to True along axis. + """ + return numojo.logic.any(self, axis=axis) + + fn argmax(self) raises -> Scalar[DType.int]: + """ + Index of the max. It is first flattened before sorting. + """ + return numojo.math.argmax(self) + + fn argmax(self, axis: Int) raises -> Matrix[DType.int]: + """ + Index of the max along the given axis. + """ + return numojo.math.argmax(self, axis=axis) + + fn argmin(self) raises -> Scalar[DType.int]: + """ + Index of the min. It is first flattened before sorting. + """ + return numojo.math.argmin(self) + + fn argmin(self, axis: Int) raises -> Matrix[DType.int]: + """ + Index of the min along the given axis. + """ + return numojo.math.argmin(self, axis=axis) + + fn argsort(self) raises -> Matrix[DType.int]: + """ + Argsort the Matrix. It is first flattened before sorting. + """ + return numojo.math.argsort(self) + + fn argsort(self, axis: Int) raises -> Matrix[DType.int]: + """ + Argsort the Matrix along the given axis. + """ + return numojo.math.argsort(self.copy(), axis=axis) + + fn astype[asdtype: DType](self) -> Matrix[asdtype]: + """ + Copy of the matrix, cast to a specified type. + """ + var res = Matrix[asdtype]( + shape=(self.shape[0], self.shape[1]), order=self.order() + ) + for i in range(self.size): + res._buf.ptr[i] = self._buf.ptr[i].cast[asdtype]() + return res^ + + fn cumprod(self) raises -> Matrix[dtype]: + """ + Cumprod of flattened matrix. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.cumprod()) + ``` + """ + return numojo.math.cumprod(self.copy()) + + fn cumprod(self, axis: Int) raises -> Matrix[dtype]: + """ + Cumprod of Matrix along the axis. + + Args: + axis: 0 or 1. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.cumprod(axis=0)) + print(A.cumprod(axis=1)) + ``` + """ + return numojo.math.cumprod(self.copy(), axis=axis) + + fn cumsum(self) raises -> Matrix[dtype]: + return numojo.math.cumsum(self.copy()) + + fn cumsum(self, axis: Int) raises -> Matrix[dtype]: + return numojo.math.cumsum(self.copy(), axis=axis) + + fn fill(self, fill_value: Scalar[dtype]): + """ + Fill the matrix with value. + + See also function `mat.creation.full`. + """ + for i in range(self.size): + self._buf.ptr[i] = fill_value + + fn flatten(self) -> Self: + """ + Return a flattened copy of the matrix. + """ + var res = Self(shape=(1, self.size), order=self.order()) + memcpy(res._buf.ptr, self._buf.ptr, res.size) + return res^ + + fn inv(self) raises -> Self: + """ + Inverse of matrix. + """ + return numojo.linalg.inv(self) + + fn order(self) -> String: + """ + Returns the order. + """ + var order: String = "F" + if self.flags.C_CONTIGUOUS: + order = "C" + return order + + fn max(self) raises -> Scalar[dtype]: + """ + Find max item. It is first flattened before sorting. + """ + return numojo.math.extrema.max(self) + + fn max(self, axis: Int) raises -> Self: + """ + Find max item along the given axis. + """ + return numojo.math.extrema.max(self, axis=axis) + + fn mean[ + returned_dtype: DType = DType.float64 + ](self) raises -> Scalar[returned_dtype]: + """ + Calculate the arithmetic average of all items in the Matrix. + """ + return numojo.statistics.mean[returned_dtype](self) + + fn mean[ + returned_dtype: DType = DType.float64 + ](self, axis: Int) raises -> Matrix[returned_dtype]: + """ + Calculate the arithmetic average of a Matrix along the axis. + + Args: + axis: 0 or 1. + """ + return numojo.statistics.mean[returned_dtype](self, axis=axis) + + fn min(self) raises -> Scalar[dtype]: + """ + Find min item. It is first flattened before sorting. + """ + return numojo.math.extrema.min(self) + + fn min(self, axis: Int) raises -> Self: + """ + Find min item along the given axis. + """ + return numojo.math.extrema.min(self, axis=axis) + + fn prod(self) -> Scalar[dtype]: + """ + Product of all items in the Matrix. + """ + return numojo.math.prod(self) + + fn prod(self, axis: Int) raises -> Self: + """ + Product of items in a Matrix along the axis. + + Args: + axis: 0 or 1. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.prod(axis=0)) + print(A.prod(axis=1)) + ``` + """ + return numojo.math.prod(self, axis=axis) + + fn reshape(self, shape: Tuple[Int, Int]) raises -> Self: + """ + Change shape and size of matrix and return a new matrix. + """ + if shape[0] * shape[1] != self.size: + raise Error( + String( + "Cannot reshape matrix of size {} into shape ({}, {})." + ).format(self.size, shape[0], shape[1]) + ) + var res = Self(shape=shape, order="C") + if self.flags.F_CONTIGUOUS: + var temp = self.reorder_layout() + memcpy(res._buf.ptr, temp._buf.ptr, res.size) + res = res.reorder_layout() + else: + memcpy(res._buf.ptr, self._buf.ptr, res.size) + return res^ + + fn resize(mut self, shape: Tuple[Int, Int]) raises: + """ + Change shape and size of matrix in-place. + """ + if shape[0] * shape[1] > self.size: + var other = Self(shape=shape) + if self.flags.C_CONTIGUOUS: + memcpy(other._buf.ptr, self._buf.ptr, self.size) + for i in range(self.size, other.size): + other._buf.ptr[i] = 0 + else: + var idx = 0 + for i in range(other.size): + other._buf.ptr.store(i, 0.0) + if idx < self.size: + other._buf.ptr[i] = self._buf.ptr[ + (i % self.shape[1]) * self.shape[0] + + (i // self.shape[1]) + ] + idx += 1 + other = other.reorder_layout() + self = other^ + else: + self.shape[0] = shape[0] + self.shape[1] = shape[1] + self.size = shape[0] * shape[1] + + if self.flags.C_CONTIGUOUS: + self.strides[0] = shape[1] + else: + self.strides[1] = shape[0] + + fn round(self, decimals: Int) raises -> Self: + return numojo.math.rounding.round(self.copy(), decimals=decimals) + + fn std[ + returned_dtype: DType = DType.float64 + ](self, ddof: Int = 0) raises -> Scalar[returned_dtype]: + """ + Compute the standard deviation. + + Args: + ddof: Delta degree of freedom. + """ + return numojo.statistics.std[returned_dtype](self, ddof=ddof) + + fn std[ + returned_dtype: DType = DType.float64 + ](self, axis: Int, ddof: Int = 0) raises -> Matrix[returned_dtype]: + """ + Compute the standard deviation along axis. + + Args: + axis: 0 or 1. + ddof: Delta degree of freedom. + """ + return numojo.statistics.std[returned_dtype](self, axis=axis, ddof=ddof) + + fn sum(self) -> Scalar[dtype]: + """ + Sum up all items in the Matrix. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.sum()) + ``` + """ + return numojo.math.sum(self) + + fn sum(self, axis: Int) raises -> Self: + """ + Sum up the items in a Matrix along the axis. + + Args: + axis: 0 or 1. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.sum(axis=0)) + print(A.sum(axis=1)) + ``` + """ + return numojo.math.sum(self, axis=axis) + + fn trace(self) raises -> Scalar[dtype]: + """ + Trace of matrix. + """ + return numojo.linalg.trace(self) + + fn issymmetric(self) -> Bool: + """ + Transpose of matrix. + """ + return issymmetric(self) + + fn transpose(self) -> Self: + """ + Transpose of matrix. + """ + return transpose(self) + + fn reorder_layout(self) raises -> Self: + """ + Reorder_layout matrix. + """ + return reorder_layout(self) + + fn T(self) -> Self: + return transpose(self) + + fn variance[ + returned_dtype: DType = DType.float64 + ](self, ddof: Int = 0) raises -> Scalar[returned_dtype]: + """ + Compute the variance. + + Args: + ddof: Delta degree of freedom. + """ + return numojo.statistics.variance[returned_dtype](self, ddof=ddof) + + fn variance[ + returned_dtype: DType = DType.float64 + ](self, axis: Int, ddof: Int = 0) raises -> Matrix[returned_dtype]: + """ + Compute the variance along axis. + + Args: + axis: 0 or 1. + ddof: Delta degree of freedom. + """ + return numojo.statistics.variance[returned_dtype]( + self, axis=axis, ddof=ddof + ) + + # ===-------------------------------------------------------------------===# + # To other data types + # ===-------------------------------------------------------------------===# + + fn to_ndarray(self) raises -> NDArray[dtype]: + """Create `NDArray` from `Matrix`. + + It makes a copy of the buffer of the matrix. + """ + + var ndarray: NDArray[dtype] = NDArray[dtype]( + shape=List[Int](self.shape[0], self.shape[1]), order="C" + ) + memcpy(ndarray._buf.ptr, self._buf.ptr, ndarray.size) + + return ndarray^ + + fn to_numpy(self) raises -> PythonObject: + """See `numojo.core.utility.to_numpy`.""" + try: + var np = Python.import_module("numpy") + + var np_arr_dim = Python.list() + np_arr_dim.append(self.shape[0]) + np_arr_dim.append(self.shape[1]) + + np.set_printoptions(4) + + # Implement a dictionary for this later + var numpyarray: PythonObject + var np_dtype = np.float64 + if dtype == DType.float16: + np_dtype = np.float16 + elif dtype == DType.float32: + np_dtype = np.float32 + elif dtype == DType.int64: + np_dtype = np.int64 + elif dtype == DType.int32: + np_dtype = np.int32 + elif dtype == DType.int16: + np_dtype = np.int16 + elif dtype == DType.int8: + np_dtype = np.int8 + elif dtype == DType.uint64: + np_dtype = np.uint64 + elif dtype == DType.uint32: + np_dtype = np.uint32 + elif dtype == DType.uint16: + np_dtype = np.uint16 + elif dtype == DType.uint8: + np_dtype = np.uint8 + elif dtype == DType.bool: + np_dtype = np.bool_ + elif dtype == DType.int: + np_dtype = np.int64 + + var order = "C" if self.flags.C_CONTIGUOUS else "F" + numpyarray = np.empty(np_arr_dim, dtype=np_dtype, order=order) + var pointer_d = numpyarray.__array_interface__["data"][ + 0 + ].unsafe_get_as_pointer[dtype]() + memcpy(pointer_d, self._buf.ptr, self.size) + + return numpyarray^ + + except e: + print("Error in converting to numpy", e) + return PythonObject() + + # ===-----------------------------------------------------------------------===# + # Static methods to construct matrix + # ===-----------------------------------------------------------------------===# + + @staticmethod + fn full[ + dtype: DType = DType.float64 + ]( + shape: Tuple[Int, Int], + fill_value: Scalar[dtype] = 0, + order: String = "C", + ) -> Matrix[dtype]: + """Return a matrix with given shape and filled value. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.full(shape=(10, 10), fill_value=100) + ``` + """ + + var matrix = Matrix[dtype](shape, order) + for i in range(shape[0] * shape[1]): + matrix._buf.ptr.store(i, fill_value) + + return matrix^ + + @staticmethod + fn zeros[ + dtype: DType = DType.float64 + ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[dtype]: + """Return a matrix with given shape and filled with zeros. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(10, 10)) + ``` + """ + + var M = Matrix[dtype](shape, order) + memset_zero(M._buf.ptr, M.size) + return M^ + + @staticmethod + fn ones[ + dtype: DType = DType.float64 + ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[dtype]: + """Return a matrix with given shape and filled with ones. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(10, 10)) + ``` + """ + + return Matrix.full[dtype](shape=shape, fill_value=1) + + @staticmethod + fn identity[ + dtype: DType = DType.float64 + ](len: Int, order: String = "C") -> Matrix[dtype]: + """Return an identity matrix with given size. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.identity(12) + ``` + """ + var matrix = Matrix.zeros[dtype]((len, len), order) + for i in range(len): + matrix._buf.ptr.store( + i * matrix.strides[0] + i * matrix.strides[1], 1 + ) + return matrix^ + + @staticmethod + fn rand[ + dtype: DType = DType.float64 + ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[dtype]: + """Return a matrix with random values uniformed distributed between 0 and 1. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((12, 12)) + ``` + + Parameters: + dtype: The data type of the NDArray elements. + + Args: + shape: The shape of the Matrix. + order: The order of the Matrix. "C" or "F". + """ + var result = Matrix[dtype](shape, order) + for i in range(result.size): + result._buf.ptr.store(i, random_float64(0, 1).cast[dtype]()) + return result^ + + @staticmethod + fn fromlist[ + dtype: DType + ]( + object: List[Scalar[dtype]], + shape: Tuple[Int, Int] = (0, 0), + order: String = "C", + ) raises -> Matrix[dtype]: + """Create a matrix from a 1-dimensional list into given shape. + + If no shape is passed, the return matrix will be a row vector. + + Example: + ```mojo + from numojo import Matrix + fn main() raises: + print(Matrix.fromlist(List[Float64](1, 2, 3, 4, 5), (5, 1))) + ``` + """ + + if (shape[0] == 0) and (shape[1] == 0): + var M = Matrix[dtype](shape=(1, len(object))) + memcpy(M._buf.ptr, object.unsafe_ptr(), M.size) + return M^ + + if shape[0] * shape[1] != len(object): + var message = String( + "The input has {} elements, but the target has the shape {}x{}" + ).format(len(object), shape[0], shape[1]) + raise Error(message) + var M = Matrix[dtype](shape=shape, order="C") + memcpy(M._buf.ptr, object.unsafe_ptr(), M.size) + if order == "F": + M = M.reorder_layout() + return M^ + + @staticmethod + fn fromstring[ + dtype: DType = DType.float64 + ]( + text: String, shape: Tuple[Int, Int] = (0, 0), order: String = "C" + ) raises -> Matrix[dtype]: + """Matrix initialization from string representation of an matrix. + + Comma, right brackets, and whitespace are treated as seperators of numbers. + Digits, underscores, and minus signs are treated as a part of the numbers. + + If now shape is passed, the return matrix will be a row vector. + + Example: + ```mojo + from numojo.prelude import * + from numojo import Matrix + fn main() raises: + var A = Matrix.fromstring[f32]( + "1 2 .3 4 5 6.5 7 1_323.12 9 10, 11.12, 12 13 14 15 16", (4, 4)) + ``` + ```console + [[1.0 2.0 0.30000001192092896 4.0] + [5.0 6.5 7.0 1323.1199951171875] + [9.0 10.0 11.119999885559082 12.0] + [13.0 14.0 15.0 16.0]] + Size: 4x4 DType: float32 + ``` + + Args: + text: String representation of a matrix. + shape: Shape of the matrix. + order: Order of the matrix. "C" or "F". + """ + + var data = List[Scalar[dtype]]() + var bytes = text.as_bytes() + var number_as_str: String = "" + var size = shape[0] * shape[1] + + for i in range(len(bytes)): + var b = bytes[i] + if ( + chr(Int(b)).isdigit() + or (chr(Int(b)) == ".") + or (chr(Int(b)) == "-") + ): + number_as_str = number_as_str + chr(Int(b)) + if i == len(bytes) - 1: # Last byte + var number = atof(number_as_str).cast[dtype]() + data.append(number) # Add the number to the data buffer + number_as_str = "" # Clean the number cache + if ( + (chr(Int(b)) == ",") + or (chr(Int(b)) == "]") + or (chr(Int(b)) == " ") + ): + if number_as_str != "": + var number = atof(number_as_str).cast[dtype]() + data.append(number) # Add the number to the data buffer + number_as_str = "" # Clean the number cache + + if (shape[0] == 0) and (shape[1] == 0): + return Matrix.fromlist(data) + + if size != len(data): + var message = String( + "The number of items in the string is {}, which does not match" + " the given shape {}x{}." + ).format(len(data), shape[0], shape[1]) + raise Error(message) + + var result = Matrix[dtype](shape=shape) + for i in range(len(data)): + result._buf.ptr[i] = data[i] + return result^ + + +# ===-----------------------------------------------------------------------===# +# MatrixIter struct +# ===-----------------------------------------------------------------------===# + + +# ! Should the iterator be mutable or not? +struct _MatrixIter[ + is_mutable: Bool, //, + lifetime: Origin[is_mutable], + dtype: DType, + forward: Bool = True, +](Copyable, Movable): + """Iterator for Matrix. + + Parameters: + is_mutable: Whether the iterator is mutable. + lifetime: The lifetime of the underlying Matrix data. + dtype: The data type of the item. + forward: The iteration direction. `False` is backwards. + """ + + var index: Int + var matrix: Matrix[dtype] + var length: Int + + fn __init__( + out self, + matrix: Matrix[dtype], + length: Int, + ): + self.index = 0 if forward else length + self.length = length + self.matrix = matrix.copy() + + fn __iter__(self) -> Self: + return self.copy() + + fn __next__(mut self) raises -> Matrix[dtype]: + @parameter + if forward: + var current_index = self.index + self.index += 1 + return self.matrix[current_index] + else: + var current_index = self.index + self.index -= 1 + return self.matrix[current_index] + + @always_inline + fn __has_next__(self) -> Bool: + @parameter + if forward: + return self.index < self.length + else: + return self.index > 0 + + fn __len__(self) -> Int: + @parameter + if forward: + return self.length - self.index + else: + return self.index + + +# ===-----------------------------------------------------------------------===# +# Backend fucntions using SMID functions +# ===-----------------------------------------------------------------------===# + + +fn _arithmetic_func_matrix_matrix_to_matrix[ + dtype: DType, + simd_func: fn[type: DType, simd_width: Int] ( + SIMD[type, simd_width], SIMD[type, simd_width] + ) -> SIMD[type, simd_width], +](A: Matrix[dtype], B: Matrix[dtype]) raises -> Matrix[dtype]: + """ + Matrix[dtype] & Matrix[dtype] -> Matrix[dtype] + + For example: `__add__`, `__sub__`, etc. + """ + alias simd_width = simd_width_of[dtype]() + if A.order() != B.order(): + raise Error( + String("Matrix order {} does not match {}.").format( + A.order(), B.order() + ) + ) + + if (A.shape[0] != B.shape[0]) or (A.shape[1] != B.shape[1]): + raise Error( + String("Shape {}x{} does not match {}x{}.").format( + A.shape[0], A.shape[1], B.shape[0], B.shape[1] + ) + ) + + var C = Matrix[dtype](shape=A.shape, order=A.order()) + + @parameter + fn vec_func[simd_width: Int](i: Int): + C._buf.store( + i, + simd_func( + A._buf.load[width=simd_width](i), + B._buf.load[width=simd_width](i), + ), + ) + + vectorize[vec_func, simd_width](A.size) + + return C^ + + +fn _arithmetic_func_matrix_to_matrix[ + dtype: DType, + simd_func: fn[type: DType, simd_width: Int] ( + SIMD[type, simd_width] + ) -> SIMD[type, simd_width], +](A: Matrix[dtype]) -> Matrix[dtype]: + """ + Matrix[dtype] -> Matrix[dtype] + + For example: `sin`, `cos`, etc. + """ + alias simd_width: Int = simd_width_of[dtype]() + + var C: Matrix[dtype] = Matrix[dtype](shape=A.shape, order=A.order()) + + @parameter + fn vec_func[simd_width: Int](i: Int): + C._buf.ptr.store(i, simd_func(A._buf.ptr.load[width=simd_width](i))) + + vectorize[vec_func, simd_width](A.size) + + return C^ + + +fn _logic_func_matrix_matrix_to_matrix[ + dtype: DType, + simd_func: fn[type: DType, simd_width: Int] ( + SIMD[type, simd_width], SIMD[type, simd_width] + ) -> SIMD[DType.bool, simd_width], +](A: Matrix[dtype], B: Matrix[dtype]) raises -> Matrix[DType.bool]: + """ + Matrix[dtype] & Matrix[dtype] -> Matrix[bool] + """ + alias width = simd_width_of[dtype]() + + if A.order() != B.order(): + raise Error( + String("Matrix order {} does not match {}.").format( + A.order(), B.order() + ) + ) + + if (A.shape[0] != B.shape[0]) or (A.shape[1] != B.shape[1]): + raise Error( + String("Shape {}x{} does not match {}x{}.").format( + A.shape[0], A.shape[1], B.shape[0], B.shape[1] + ) + ) + + var t0 = A.shape[0] + var t1 = A.shape[1] + var C = Matrix[DType.bool](shape=A.shape, order=A.order()) + + @parameter + fn calculate_CC(m: Int): + @parameter + fn vec_func[simd_width: Int](n: Int): + C._store[simd_width]( + m, + n, + simd_func(A._load[simd_width](m, n), B._load[simd_width](m, n)), + ) + + vectorize[vec_func, width](t1) + + parallelize[calculate_CC](t0, t0) + + var _t0 = t0 + var _t1 = t1 + var _A = ( + A.copy() + ) # ! perhaps remove this explicit copy if we don't need to extend it's lifetime. + var _B = B.copy() + + return C^ diff --git a/numojo/__init__.mojo b/numojo/__init__.mojo index 50a16eee..e75f9416 100644 --- a/numojo/__init__.mojo +++ b/numojo/__init__.mojo @@ -12,7 +12,7 @@ alias __version__: String = "V0.8.0" from numojo.core.ndarray import NDArray from numojo.core.ndshape import NDArrayShape, Shape from numojo.core.ndstrides import NDArrayStrides, Strides -from numojo.core.item import Item, item +from numojo.core.item import Item from numojo.core.matrix import Matrix from numojo.core.complex.complex_simd import ComplexSIMD, CScalar from numojo.core.complex.complex_ndarray import ComplexNDArray @@ -208,7 +208,7 @@ from numojo.routines.creation import ( ) from numojo.routines import indexing -from numojo.routines.indexing import where, compress, take_along_axis +from numojo.routines.indexing import `where`, compress, take_along_axis from numojo.routines.functional import apply_along_axis diff --git a/numojo/core/__init__.mojo b/numojo/core/__init__.mojo index e90a56d7..5d70c5d7 100644 --- a/numojo/core/__init__.mojo +++ b/numojo/core/__init__.mojo @@ -5,11 +5,14 @@ from .ndarray import NDArray from .item import Item from .ndshape import NDArrayShape from .ndstrides import NDArrayStrides +from .own_data import OwnData +from .ref_data import RefData from .complex import ( ComplexSIMD, ComplexScalar, CScalar, + `1j`, ComplexNDArray, ComplexDType, ci8, @@ -64,5 +67,4 @@ from .error import ( ) alias idx = Item -alias shape = NDArrayShape alias Shape = NDArrayShape diff --git a/numojo/core/array_methods.mojo b/numojo/core/array_methods.mojo new file mode 100644 index 00000000..0a6fb7a6 --- /dev/null +++ b/numojo/core/array_methods.mojo @@ -0,0 +1,92 @@ +comptime newaxis: NewAxis = NewAxis() +comptime ellipsis: Ellipsis = Ellipsis() + + +struct Ellipsis(Stringable): + """ + Represents an ellipsis (`...`) used in array slicing to indicate the inclusion of all remaining dimensions. + This can be used to simplify slicing operations when the exact number of dimensions is not known or when you want to include all remaining dimensions without explicitly specifying them. + + Example: + ``` + from numojo.prelude import * + from numojo.routines.creation import arange + + var arr = arange(Shape(3, 4, 5, 6)) + sliced_arr = arr[nm.ellipsis, 2] # Equivalent to arr[:, :, :, 2] + ``` + """ + + fn __init__(out self): + """ + Initializes an Ellipsis instance. + """ + pass + + fn __repr__(self) -> String: + """ + Returns a string representation of the Ellipsis instance. + + Returns: + Str: The string "Ellipsis()". + """ + return "numojo.ellipsis()" + + fn __str__(self) -> String: + """ + Returns a string representation of the Ellipsis instance. + + Returns: + Str: The string "Ellipsis()". + """ + return "numojo.ellipsis()" + + fn __eq__(self, other: Self) -> Bool: + """ + Checks equality between two Ellipsis instances. + """ + return True + + fn __ne__(self, other: Self) -> Bool: + """ + Checks inequality between two Ellipsis instances. + """ + return False + + +struct NewAxis(Stringable): + fn __init__(out self): + """ + Initializes a NewAxis instance. + """ + pass + + fn __repr__(self) -> String: + """ + Returns a string representation of the NewAxis instance. + + Returns: + Str: The string "NewAxis()". + """ + return "numojo.newaxis()" + + fn __str__(self) -> String: + """ + Returns a string representation of the NewAxis instance. + + Returns: + Str: The string "NewAxis()". + """ + return "numojo.newaxis()" + + fn __eq__(self, other: Self) -> Bool: + """ + Checks equality between two NewAxis instances. + """ + return True + + fn __ne__(self, other: Self) -> Bool: + """ + Checks inequality between two NewAxis instances. + """ + return False diff --git a/numojo/core/complex/__init__.mojo b/numojo/core/complex/__init__.mojo index 243acad3..76dbbfff 100644 --- a/numojo/core/complex/__init__.mojo +++ b/numojo/core/complex/__init__.mojo @@ -1,4 +1,4 @@ -from .complex_simd import ComplexSIMD, ComplexScalar, CScalar +from .complex_simd import ComplexSIMD, ComplexScalar, CScalar, `1j` from .complex_ndarray import ComplexNDArray from .complex_dtype import ( ComplexDType, diff --git a/numojo/core/complex/complex_dtype.mojo b/numojo/core/complex/complex_dtype.mojo index 38c88c5a..aa2eca6c 100644 --- a/numojo/core/complex/complex_dtype.mojo +++ b/numojo/core/complex/complex_dtype.mojo @@ -86,16 +86,6 @@ struct ComplexDType( `ComplexDType` behaves like an enum rather than a typical object. You don't instantiate it, but instead use its compile-time constants (aliases) to declare data types for complex SIMD vectors, tensors, and other data structures. - - Example: - - ```mojo - import numojo as nm - var A = nm.CScalar[nm.cf32](re=1.0, im=2.0) - print("A:", A) # A: (1.0 + 2.0i) - var A1 = nm.ComplexSIMD[nm.cf32, 2](SIMD[nm.f32, 2](1.0, 1.0), SIMD[nm.f32, 2](2.0, 2.0)) - print("A1:", A1) # A1: ([1.0, 1.0], [2.0, 2.0] j) - ``` """ # ===-------------------------------------------------------------------===# @@ -219,6 +209,10 @@ struct ComplexDType( return Self._from_str(str.removeprefix("ComplexDType.")) elif str == "int8": return ComplexDType.int8 + elif str == "int16": + return ComplexDType.int16 + elif str == "int32": + return ComplexDType.int32 elif str == "int64": return ComplexDType.int64 elif str == "int128": @@ -476,7 +470,10 @@ struct ComplexDType( Returns: Returns True if the input type parameter is an integer. """ - return self in (DType.int, DType.uint) or self._is_non_index_integral() + return ( + self in (ComplexDType.int, ComplexDType.uint) + or self._is_non_index_integral() + ) @always_inline("nodebug") fn is_floating_point(self) -> Bool: @@ -596,6 +593,14 @@ struct ComplexDType( 2 * 8 * self.size_of() ) # 2 * because complex number has real and imaginary parts + fn component_bitwidth(self) -> Int: + """Returns the size in bits of the component type of the current ComplexDType. + + Returns: + Returns the size in bits of the component type of the current ComplexDType. + """ + return self.bitwidth() // 2 + # ===-------------------------------------------------------------------===# # __mlir_type # ===-------------------------------------------------------------------===# @@ -661,11 +666,18 @@ struct ComplexDType( return abort[__mlir_type.`!kgen.deferred`]("invalid dtype") + fn component_dtype(self) -> DType: + return self._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 == ci128: diff --git a/numojo/core/complex/complex_ndarray.mojo b/numojo/core/complex/complex_ndarray.mojo index c1d8cf1c..92c9e1f9 100644 --- a/numojo/core/complex/complex_ndarray.mojo +++ b/numojo/core/complex/complex_ndarray.mojo @@ -40,7 +40,8 @@ import builtin.math as builtin_math from builtin.type_aliases import Origin from collections.optional import Optional from math import log10, sqrt -from memory import UnsafePointer, memset_zero, memcpy +from memory import memset_zero, memcpy +from memory import LegacyUnsafePointer from python import PythonObject from sys import simd_width_of from utils import Variant @@ -55,7 +56,7 @@ from numojo.core.ndshape import NDArrayShape from numojo.core.ndstrides import NDArrayStrides from numojo.core.complex.complex_simd import ComplexSIMD, ComplexScalar, CScalar from numojo.core.complex.complex_dtype import ComplexDType -from numojo.core.own_data import OwnData +from numojo.core.data_container import DataContainer from numojo.core.utility import ( _get_offset, _transfer_offset, @@ -101,28 +102,59 @@ import numojo.routines.searching as searching # Implements N-Dimensional Complex Array # ===----------------------------------------------------------------------=== # struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( - Copyable, Movable, Representable, Sized, Stringable, Writable + Copyable, + FloatableRaising, + IntableRaising, + Movable, + Representable, + Sized, + Stringable, + Writable, ): """ - Represents a Complex N-Dimensional Array. + N-dimensional Complex array. + + ComplexNDArray represents an N-dimensional array whose elements are complex numbers, supporting efficient storage, indexing, and mathematical operations. Each element consists of a real and imaginary part, stored in separate buffers. Parameters: - cdtype: Complex data type. + cdtype: The complex data type of the array elements (default: ComplexDType.float64). + + Attributes: + - _re: NDArray[Self.dtype] + Buffer for real parts. + - _im: NDArray[Self.dtype] + Buffer for imaginary parts. + - ndim: Int + Number of dimensions. + - shape: NDArrayShape + Shape of the array. + - size: Int + Total number of elements. + - strides: NDArrayStrides + Stride information for each dimension. + - flags: Flags + Memory layout information. + - print_options: PrintOptions + Formatting options for display. + + Notes: + - The array is uniquely defined by its data buffers, shape, strides, and element datatype. + - Supports both row-major (C) and column-major (F) memory order. + - Provides rich indexing, slicing, and broadcasting semantics. + - ComplexNDArray should be created using factory functions in `nomojo.routines.creation` module for convenience. """ - # ===----------------------------------------------------------------------===# - # Aliases - # ===----------------------------------------------------------------------===# - - alias dtype: DType = cdtype._dtype # corresponding real data type - - # ===----------------------------------------------------------------------===# - # FIELDS - # ===----------------------------------------------------------------------===# + # --- Aliases --- + alias dtype: DType = cdtype._dtype + """corresponding real data type""" + # --- FIELDS --- var _re: NDArray[Self.dtype] + """Buffer for real parts.""" var _im: NDArray[Self.dtype] - # It's redundant, but better to have the following as fields. + """Buffer for imaginary parts.""" + + # TODO: add methods to for users to access the following properties directly from _re, _im and remove them from here. var ndim: Int """Number of Dimensions.""" var shape: NDArrayShape @@ -134,11 +166,9 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( var flags: Flags "Information about the memory layout of the array." var print_options: PrintOptions + """Per-instance print options (formerly global).""" - # ===-------------------------------------------------------------------===# - # Life cycle methods - # ===-------------------------------------------------------------------===# - + # --- Life cycle methods --- @always_inline("nodebug") fn __init__( out self, var re: NDArray[Self.dtype], var im: NDArray[Self.dtype] @@ -182,19 +212,20 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( order: String = "C", ) raises: """ - Initialize a ComplexNDArray with given shape. - - The memory is not filled with values. + Initialize a ComplexNDArray with given shape. The memory is not filled with values. Args: shape: Variadic shape. order: Memory order C or F. Example: - ```mojo - from numojo.prelude import * - var A = nm.ComplexNDArray[cf32](Shape(2,3,4)) - ``` + ```mojo + from numojo.prelude import * + var A = nm.ComplexNDArray[cf32](Shape(2,3,4)) + ``` + + Notes: + This constructor should not be used by users directly. Use factory functions in `numojo.routines.creation` module instead. """ self._re = NDArray[Self.dtype](shape, order) self._im = NDArray[Self.dtype](shape, order) @@ -219,6 +250,15 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( Args: shape: List of shape. order: Memory order C or F. + + Example: + ```mojo + from numojo.prelude import * + var A = nm.ComplexNDArray[cf32](List[Int](2,3,4)) + ``` + + Notes: + This constructor should not be used by users directly. Use factory functions in `numojo.routines.creation` module instead. """ self._re = NDArray[Self.dtype](shape, order) self._im = NDArray[Self.dtype](shape, order) @@ -243,6 +283,15 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( Args: shape: Variadic List of shape. order: Memory order C or F. + + Example: + ```mojo + from numojo.prelude import * + var A = nm.ComplexNDArray[cf32](VariadicList(2,3,4)) + ``` + + Notes: + This constructor should not be used by users directly. Use factory functions in `numojo.routines.creation` module instead. """ self._re = NDArray[Self.dtype](shape, order) self._im = NDArray[Self.dtype](shape, order) @@ -262,7 +311,26 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( strides: List[Int], ) raises: """ - Extremely specific ComplexNDArray initializer. + Initialize a ComplexNDArray with a specific shape, offset, and strides. + + Args: + shape: List of integers specifying the shape of the array. + offset: Integer offset into the underlying buffer. + strides: List of integers specifying the stride for each dimension. + + Example: + ```mojo + from numojo.prelude import * + var shape = List[Int](2, 3) + var offset = 0 + var strides = List[Int](3, 1) + var arr = ComplexNDArray[cf32](shape, offset, strides) + ``` + + Notes: + - This constructor is intended for advanced use cases requiring precise control over memory layout. + - The resulting array is uninitialized and should be filled before use. + - Both real and imaginary buffers are created with the same shape, offset, and strides. """ self._re = NDArray[Self.dtype](shape, offset, strides) self._im = NDArray[Self.dtype](shape, offset, strides) @@ -284,16 +352,19 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( flags: Flags, ): """ - Constructs an extremely specific ComplexNDArray, with value uninitialized. - The properties do not need to be compatible and are not checked. - For example, it can construct a 0-D array (numojo scalar). + Initialize a ComplexNDArray with explicit shape, strides, number of dimensions, size, and flags. This constructor creates an uninitialized ComplexNDArray with the provided properties. No compatibility checks are performed between shape, strides, ndim, size, or flags. This allows construction of arrays with arbitrary metadata, including 0-D arrays (scalars). Args: - shape: Shape of array. - strides: Strides of array. + shape: Shape of the array. + strides: Strides for each dimension. ndim: Number of dimensions. - size: Size of array. - flags: Flags of array. + size: Total number of elements. + flags: Memory layout flags. + + Notes: + - This constructor is intended for advanced or internal use cases requiring manual control. + - The resulting array is uninitialized; values must be set before use. + - No validation is performed on the consistency of the provided arguments. """ self.shape = shape @@ -310,21 +381,32 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( fn __init__( out self, shape: NDArrayShape, - ref buffer_re: UnsafePointer[Scalar[Self.dtype]], - ref buffer_im: UnsafePointer[Scalar[Self.dtype]], + ref buffer_re: LegacyUnsafePointer[Scalar[Self.dtype]], + ref buffer_im: LegacyUnsafePointer[Scalar[Self.dtype]], offset: Int, strides: NDArrayStrides, ) raises: """ - Initialize an ComplexNDArray view with given shape, buffer, offset, and strides. - ***Unsafe!*** This function is currently unsafe. Only for internal use. + Initialize a ComplexNDArray view with explicit shape, raw buffers, offset, and strides. + + This constructor creates a view over existing memory buffers for the real and imaginary parts, + using the provided shape, offset, and stride information. It is intended for advanced or internal + use cases where direct control over memory layout is required. + + ***Unsafe!*** This function is unsafe and should only be used internally. The caller is responsible + for ensuring that the buffers are valid and that the shape, offset, and strides are consistent. Args: - shape: Shape of the array. - buffer_re: Unsafe pointer to the real part of the buffer. - buffer_im: Unsafe pointer to the imaginary part of the buffer. - offset: Offset value. - strides: Strides of the array. + shape: NDArrayShape specifying the dimensions of the array. + buffer_re: Unsafe pointer to the buffer containing the real part data. + buffer_im: Unsafe pointer to the buffer containing the imaginary part data. + offset: Integer offset into the buffers. + strides: NDArrayStrides specifying the stride for each dimension. + + Notes: + - No validation is performed on the buffers or metadata. + - The resulting ComplexNDArray shares memory with the provided buffers. + - Incorrect usage may lead to undefined behavior. """ self._re = NDArray(shape, buffer_re, offset, strides) self._im = NDArray(shape, buffer_im, offset, strides) @@ -365,18 +447,10 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( self.flags = existing.flags self.print_options = existing.print_options - # Explicit deallocation - # @always_inline("nodebug") - # fn __del__(var self): - # """ - # Deallocate memory. - # """ - # self._re.__del__() - # self._im.__del__() - # ===-------------------------------------------------------------------===# # Indexing and slicing # Getter dunders and other getter methods + # FIXME: currently most of the getitem and setitem methods don't match exactly between NDArray and ComplexNDArray in it's implementation, docstring, argument mutability etc. Fix this. # 1. Basic Indexing Operations # fn _getitem(self, *indices: Int) -> ComplexSIMD[cdtype] # Direct unsafe getter @@ -393,7 +467,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( # fn __getitem__(self, *slices: Variant[Slice, Int]) raises -> Self # Get by mix of slices/ints # # 4. Advanced Indexing - # fn __getitem__(self, indices: NDArray[DType.index]) raises -> Self # Get by index array + # fn __getitem__(self, indices: NDArray[DType.int]) raises -> Self # Get by index array # fn __getitem__(self, indices: List[Int]) raises -> Self # Get by list of indices # fn __getitem__(self, mask: NDArray[DType.bool]) raises -> Self # Get by boolean mask # fn __getitem__(self, mask: List[Bool]) raises -> Self # Get by boolean list @@ -406,6 +480,32 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( # fn load[width: Int](self, *indices: Int) raises -> ComplexSIMD[Self.dtype, width] # Load SIMD at coordinates # ===-------------------------------------------------------------------===# + @always_inline + fn normalize(self, idx: Int, dim: Int) -> Int: + """ + Normalize a potentially negative index to its positive equivalent + within the bounds of the given dimension. + + Args: + idx: The index to normalize. Can be negative to indicate indexing + from the end (e.g., -1 refers to the last element). + dim: The size of the dimension to normalize against. + + Returns: + The normalized index as a non-negative integer. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix[f32](shape=(3, 4)) + var norm_idx = mat.normalize(-1, mat.shape[0]) # Normalize -1 to 2 + ``` + """ + var idx_norm = idx + if idx_norm < 0: + idx_norm = dim + idx_norm + return idx_norm + fn _getitem(self, *indices: Int) -> ComplexSIMD[cdtype]: """ Get item at indices and bypass all boundary checks. @@ -417,23 +517,22 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( Returns: The element of the array at the indices. - Notes: - This function is unsafe and should be used only on internal use. - Examples: + ```mojo + import numojo as nm + var A = nm.ones[nm.cf32](nm.Shape(2,3,4)) + print(A._getitem(1,2,3)) + ``` - ```mojo - import numojo as nm - var A = nm.ones[nm.cf32](nm.Shape(2,3,4)) - print(A._getitem(1,2,3)) - ``` + Notes: + This function is unsafe and should be used only on internal use. """ var index_of_buffer: Int = 0 for i in range(self.ndim): index_of_buffer += indices[i] * Int(self.strides._buf[i]) return ComplexSIMD[cdtype]( - re=self._re._buf.ptr.load[width=1](index_of_buffer), - im=self._im._buf.ptr.load[width=1](index_of_buffer), + re=self._re._buf.ptr[index_of_buffer], + im=self._im._buf.ptr[index_of_buffer], ) fn _getitem(self, indices: List[Int]) -> ComplexScalar[cdtype]: @@ -447,26 +546,25 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( Returns: The element of the array at the indices. - Notes: - This function is unsafe and should be used only on internal use. - Examples: + ```mojo + import numojo as nm + var A = nm.ones[nm.cf32](numojo.Shape(2,3,4)) + print(A._getitem(List[Int](1,2,3))) + ``` - ```mojo - import numojo as nm - var A = nm.ones[nm.cf32](numojo.Shape(2,3,4)) - print(A._getitem(List[Int](1,2,3))) - ``` + Notes: + This function is unsafe and should be used only on internal use. """ var index_of_buffer: Int = 0 for i in range(self.ndim): index_of_buffer += indices[i] * Int(self.strides._buf[i]) return ComplexSIMD[cdtype]( - re=self._re._buf.ptr.load[width=1](index_of_buffer), - im=self._im._buf.ptr.load[width=1](index_of_buffer), + re=self._re._buf.ptr[index_of_buffer], + im=self._im._buf.ptr[index_of_buffer], ) - fn __getitem__(self) raises -> ComplexSIMD[cdtype]: + fn __getitem__(self) raises -> ComplexSIMD[cdtype, 1]: """ Gets the value of the 0-D Complex array. @@ -478,10 +576,10 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( Examples: - ```console - >>> import numojo as nm - >>> var A = nm.ones[nm.f32](nm.Shape(2,3,4)) - >>> print(A[]) # gets values of the 0-D array. + ```mojo + import numojo as nm + var a = nm.arange[nm.cf32](3)[0] + print(a[]) # gets values of the 0-D complex array. ```. """ if self.ndim != 0: @@ -503,7 +601,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( im=self._im._buf.ptr[], ) - fn __getitem__(self, index: Item) raises -> ComplexSIMD[cdtype]: + fn __getitem__(self, index: Item) raises -> ComplexSIMD[cdtype, 1]: """ Get the value at the index list. @@ -586,15 +684,15 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( stride-based copier is used for both components. (Future: return a non-owning view). - Examples: + Example: ```mojo import numojo as nm 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)) + var a = nm.arange[cf32](CScalar[cf32](0), CScalar[cf32](12), CScalar[cf32](1)).reshape(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[cf32](CScalar[cf32](6, 6)).reshape(nm.Shape(6)) + var b = nm.arange[cf32](CScalar[cf32](6)).reshape(nm.Shape(6)) print(b[2]) # 0-D array (scalar wrapper) ``` """ @@ -637,23 +735,37 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ) ) - var out_shape = self.shape[1:] - var alloc_order = String("C") + var out_shape: NDArrayShape = self.shape[1:] + var alloc_order: String = String("C") if self.flags.F_CONTIGUOUS: alloc_order = String("F") - var result = ComplexNDArray[cdtype](shape=out_shape, order=alloc_order) + var result: ComplexNDArray[cdtype] = ComplexNDArray[cdtype]( + shape=out_shape, order=alloc_order + ) # Fast path for C-contiguous if self.flags.C_CONTIGUOUS: - var block = self.size // self.shape[0] - memcpy(result._re._buf.ptr, self._re._buf.ptr + norm * block, block) - memcpy(result._im._buf.ptr, self._im._buf.ptr + norm * block, block) + var block: Int = self.size // self.shape[0] + memcpy( + dest=result._re._buf.ptr, + src=self._re._buf.ptr + norm * block, + count=block, + ) + memcpy( + dest=result._im._buf.ptr, + src=self._im._buf.ptr + norm * block, + count=block, + ) + return result^ + else: + # F layout + self[Self.dtype]._re._copy_first_axis_slice( + self._re, norm, result._re + ) + self[Self.dtype]._im._copy_first_axis_slice( + self._im, norm, result._im + ) return result^ - - # F layout - self._re._copy_first_axis_slice[Self.dtype](self._re, norm, result._re) - self._im._copy_first_axis_slice[Self.dtype](self._im, norm, result._im) - return result^ fn __getitem__(self, var *slices: Slice) raises -> Self: """ @@ -711,7 +823,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( var narr: Self = self[slice_list^] return narr^ - fn _calculate_strides_efficient(self, shape: List[Int]) -> List[Int]: + fn _calculate_strides(self, shape: List[Int]) -> List[Int]: var strides = List[Int](capacity=len(shape)) if self.flags.C_CONTIGUOUS: # C_CONTIGUOUS @@ -752,7 +864,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( - This method supports advanced slicing similar to NumPy's multi-dimensional slicing. - The returned array shares data with the original array if possible. - Examples: + Example: ```mojo import numojo as nm from numojo.prelude import * @@ -812,12 +924,13 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ncoefficients.append(1) # only C & F order are supported - var nstrides: List[Int] = self._calculate_strides_efficient( + var nstrides: List[Int] = self._calculate_strides( nshape, ) var narr = ComplexNDArray[cdtype]( offset=noffset, shape=nshape, strides=nstrides ) + # TODO: combine the two traverses into one. var index_re: List[Int] = List[Int](length=ndims, fill=0) _traverse_iterative[Self.dtype]( self._re, @@ -927,7 +1040,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( narr = self.__getitem__(slice_list^) return narr^ - fn __getitem__(self, indices: NDArray[DType.index]) raises -> Self: + fn __getitem__(self, indices: NDArray[DType.int]) raises -> Self: """ Get items from 0-th dimension of a ComplexNDArray of indices. If the original array is of shape (i,j,k) and @@ -947,7 +1060,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( var shape = indices.shape.join(self.shape._pop(0)) var result: ComplexNDArray[cdtype] = ComplexNDArray[cdtype](shape) - var size_per_item = self.size // self.shape[0] + var size_per_item: Int = self.size // self.shape[0] # Fill in the values for i in range(indices.size): @@ -969,14 +1082,14 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ) ) memcpy( - result._re._buf.ptr + i * size_per_item, - self._re._buf.ptr + indices.item(i) * size_per_item, - size_per_item, + dest=result._re._buf.ptr + i * size_per_item, + src=self._re._buf.ptr + indices.item(i) * size_per_item, + count=size_per_item, ) memcpy( - result._im._buf.ptr + i * size_per_item, - self._im._buf.ptr + indices.item(i) * size_per_item, - size_per_item, + dest=result._im._buf.ptr + i * size_per_item, + src=self._im._buf.ptr + indices.item(i) * size_per_item, + count=size_per_item, ) return result^ @@ -985,7 +1098,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( """ Get items from 0-th dimension of a ComplexNDArray of indices. It is an overload of - `__getitem__(self, indices: NDArray[DType.index]) raises -> Self`. + `__getitem__(self, indices: NDArray[DType.int]) raises -> Self`. Args: indices: A list of Int. @@ -998,7 +1111,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( """ - var indices_array = NDArray[DType.index](shape=Shape(len(indices))) + var indices_array = NDArray[DType.int](shape=Shape(len(indices))) for i in range(len(indices)): (indices_array._buf.ptr + i).init_pointee_copy(indices[i]) @@ -1108,14 +1221,14 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( for i in range(mask.size): if mask.item(i): memcpy( - result._re._buf.ptr + offset * size_per_item, - self._re._buf.ptr + i * size_per_item, - size_per_item, + dest=result._re._buf.ptr + offset * size_per_item, + src=self._re._buf.ptr + i * size_per_item, + count=size_per_item, ) memcpy( - result._im._buf.ptr + offset * size_per_item, - self._im._buf.ptr + i * size_per_item, - size_per_item, + dest=result._im._buf.ptr + offset * size_per_item, + src=self._im._buf.ptr + i * size_per_item, + count=size_per_item, ) offset += 1 @@ -1184,8 +1297,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ) ) - if index < 0: - index += self.size + index = self.normalize(index, self.size) if (index < 0) or (index >= self.size): raise Error( @@ -1312,8 +1424,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ```. """ - if index < 0: - index += self.size + index = self.normalize(index, self.size) if (index >= self.size) or (index < 0): raise Error( @@ -1334,7 +1445,9 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( im=self._im._buf.ptr[index], ) - fn load[width: Int = 1](self, index: Int) raises -> ComplexSIMD[cdtype]: + fn load[ + width: Int = 1 + ](self, index: Int) raises -> ComplexSIMD[cdtype, width]: """ Safely loads a ComplexSIMD element of size `width` at `index` from the underlying buffer. @@ -1364,7 +1477,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ) ) - return ComplexSIMD[cdtype]( + return ComplexSIMD[cdtype, width]( re=self._re._buf.ptr.load[width=1](index), im=self._im._buf.ptr.load[width=1](index), ) @@ -1413,23 +1526,31 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ) ) + # NOTE: if we take in an owned instances of indices, we can modify it in place. + var indices_list: List[Int] = List[Int](capacity=self.ndim) for i in range(self.ndim): - if (indices[i] < 0) or (indices[i] >= self.shape[i]): + var idx_i = indices[i] + if idx_i < 0 or idx_i >= self.shape[i]: raise Error( IndexError( message=String( - "Index {} out of range for dim {} (size {})." - ).format(indices[i], i, self.shape[i]), + "Index out of range at dim {}: got {}; valid range" + " is [0, {})." + ).format(i, idx_i, self.shape[i]), suggestion=String( - "Valid range for dim {} is [0, {})." - ).format(i, self.shape[i]), + "Clamp or validate indices against the dimension" + " size ({})." + ).format(self.shape[i]), location=String( - "ComplexNDArray.load[width](*indices: Int)" + "NDArray.load[width: Int = 1](*indices: Int) ->" + " SIMD[dtype, width]" ), ) ) + idx_i = self.normalize(idx_i, self.shape[i]) + indices_list.append(idx_i) - var idx: Int = _get_offset(indices, self.strides) + var idx: Int = _get_offset(indices_list, self.strides) return ComplexSIMD[cdtype, width=width]( re=self._re._buf.ptr.load[width=width](idx), im=self._im._buf.ptr.load[width=width](idx), @@ -1507,7 +1628,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( end = slice_list[i].end.value() if end < 0: end += dim_size - # Clamp to valid bounds once + # NOTE: Clamp to valid bounds once. This is an implicit behavior right now instead of raising errors. not sure if this should be kept. if step > 0: end = 0 if end < 0 else ( dim_size if end > dim_size else end @@ -1530,7 +1651,23 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( fn _setitem(self, *indices: Int, val: ComplexSIMD[cdtype]): """ (UNSAFE! for internal use only.) - Get item at indices and bypass all boundary checks. + Set item at indices and bypass all boundary checks. + + Args: + indices: Indices to set the value. + val: Value to set. + + Notes: + This function is unsafe and for internal use only. + + Examples: + + ```mojo + import numojo as nm + from numojo.prelude import * + var A = nm.full[cf32](Shape(2, 2), CScalar[cf32](1.0, 1.0)) + A._setitem(0, 1, val=CScalar[cf32](3.0, 4.0)) + ``` """ var index_of_buffer: Int = 0 for i in range(self.ndim): @@ -1570,8 +1707,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ) var norm = idx - if norm < 0: - norm += self.shape[0] + norm = self.normalize(norm, self.shape[0]) if (norm < 0) or (norm >= self.shape[0]): raise Error( IndexError( @@ -1608,21 +1744,6 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( self._im._buf.ptr.store(norm, val._im._buf.ptr.load[width=1](0)) return - if val.ndim != self.ndim - 1: - raise Error( - ShapeError( - message=String( - "Shape mismatch: expected {} dims in source but got {}." - ).format(self.ndim - 1, val.ndim), - suggestion=String("Ensure RHS has shape {}.").format( - self.shape[1:] - ), - location=String( - "ComplexNDArray.__setitem__(idx: Int, val: Self)" - ), - ) - ) - if val.shape != self.shape[1:]: raise Error( ShapeError( @@ -1642,50 +1763,78 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( if self.flags.C_CONTIGUOUS & val.flags.C_CONTIGUOUS: var block = self.size // self.shape[0] - if val.size != block: - raise Error( - ShapeError( - message=String( - "Internal mismatch: computed block {} but" - " val.size {}." - ).format(block, val.size), - suggestion=String( - "Report this issue; unexpected size mismatch." - ), - location=String( - "ComplexNDArray.__setitem__(idx: Int, val: Self)" - ), - ) - ) - memcpy(self._re._buf.ptr + norm * block, val._re._buf.ptr, block) - memcpy(self._im._buf.ptr + norm * block, val._im._buf.ptr, block) + memcpy( + dest=self._re._buf.ptr + norm * block, + src=val._re._buf.ptr, + count=block, + ) + memcpy( + dest=self._im._buf.ptr + norm * block, + src=val._im._buf.ptr, + count=block, + ) return # F order - self._re._write_first_axis_slice[Self.dtype](self._re, norm, val._re) - self._im._write_first_axis_slice[Self.dtype](self._im, norm, val._im) + self[Self.dtype]._re._write_first_axis_slice(self._re, norm, val._re) + self[Self.dtype]._im._write_first_axis_slice(self._im, norm, val._im) - fn __setitem__(mut self, index: Item, val: ComplexSIMD[cdtype]) raises: + fn __setitem__(mut self, var index: Item, val: ComplexSIMD[cdtype]) raises: """ - Set the value at the index list. + Sets the value at the index list. + + Args: + index: Index list. + val: Value to set. + + Raises: + Error: If the length of index does not match the number of dimensions. + Error: If any of the indices is out of bound. + + Examples: + + ```mojo + import numojo as nm + from numojo.prelude import * + var A = nm.full[cf32](Shape(2, 2), CScalar[cf32](1.0)) + A[Item(0, 1)] = CScalar[cf32](3.0, 4.0) + ``` """ if index.__len__() != self.ndim: - var message = String( - "Error: Length of `index` does not match the number of" - " dimensions!\n" - "Length of indices is {}.\n" - "The array dimension is {}." - ).format(index.__len__(), self.ndim) - raise Error(message) + raise Error( + IndexError( + message=String( + "Invalid index length: expected {} but got {}." + ).format(self.ndim, index.__len__()), + suggestion=String( + "Pass exactly {} indices (one per dimension)." + ).format(self.ndim), + location=String( + "ComplexNDArray.__setitem__(index: Item, val:" + " Scalar[dtype])" + ), + ) + ) for i in range(index.__len__()): if index[i] >= self.shape[i]: - var message = String( - "Error: `index` exceeds the size!\n" - "For {}-th dimension:\n" - "The index value is {}.\n" - "The size of the corresponding dimension is {}" - ).format(i, index[i], self.shape[i]) - raise Error(message) + raise Error( + IndexError( + message=String( + "Index out of range at dim {}: got {}; valid range" + " is [0, {})." + ).format(i, index[i], self.shape[i]), + suggestion=String( + "Clamp or validate indices against the dimension" + " size ({})." + ).format(self.shape[i]), + location=String( + "NDArray.__setitem__(index: Item, val:" + " Scalar[dtype])" + ), + ) + ) + index[i] = self.normalize(index[i], self.shape[i]) + var idx: Int = _get_offset(index, self.strides) self._re._buf.ptr.store(idx, val.re) self._im._buf.ptr.store(idx, val.im) @@ -1709,7 +1858,9 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( if mask._im._buf.ptr.load[width=1](i): self._im._buf.ptr.store(i, value.im) - fn __setitem__(mut self, var *slices: Slice, val: Self) raises: + fn __setitem__( + mut self, var *slices: Slice, val: ComplexNDArray[cdtype] + ) raises: """ Retreive slices of an ComplexNDArray from variadic slices. @@ -1722,7 +1873,9 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( # self.__setitem__(slices=slice_list, val=val) self[slice_list^] = val - fn __setitem__(mut self, var slices: List[Slice], val: Self) raises: + fn __setitem__( + mut self, slices: List[Slice], val: ComplexNDArray[cdtype] + ) raises: """ Sets the slices of an ComplexNDArray from list of slices and ComplexNDArray. @@ -1735,6 +1888,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( var spec: List[Int] = List[Int]() var slice_list: List[Slice] = self._adjust_slice(slices) for i in range(n_slices): + # TODO: these conditions can be removed since _adjust_slice takes care of them. But verify it once before removing. if ( slice_list[i].start.value() >= self.shape[i] or slice_list[i].end.value() > self.shape[i] @@ -1828,33 +1982,50 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( val._im, self._im, nshape, ncoefficients, nstrides, noffset, index ) - ### compiler doesn't accept this. - # fn __setitem__(self, var *slices: Variant[Slice, Int], val: NDArray[Self.dtype]) raises: - # """ - # Get items by a series of either slices or integers. - # """ - # var n_slices: Int = slices.__len__() - # if n_slices > self.ndim: - # raise Error("Error: No of slices greater than rank of array") - # var slice_list: List[Slice] = List[Slice]() - - # var count_int = 0 - # for i in range(len(slices)): - # if slices[i].isa[Slice](): - # slice_list.append(slices[i]._get_ptr[Slice]()[0]) - # elif slices[i].isa[Int](): - # count_int += 1 - # var int: Int = slices[i]._get_ptr[Int]()[0] - # slice_list.append(Slice(int, int + 1)) - - # if n_slices < self.ndim: - # for i in range(n_slices, self.ndim): - # var size_at_dim: Int = self.shape[i] - # slice_list.append(Slice(0, size_at_dim)) - - # self.__setitem__(slices=slice_list, val=val) - - fn __setitem__(self, index: NDArray[DType.index], val: Self) raises: + ## compiler doesn't accept this. + fn __setitem__( + self, var *slices: Variant[Slice, Int], val: ComplexNDArray[cdtype] + ) raises: + """ + Get items by a series of either slices or integers. + """ + var n_slices: Int = slices.__len__() + if n_slices > self.ndim: + raise Error( + IndexError( + message=String( + "Too many indices or slices: received {} but array has" + " only {} dimensions." + ).format(n_slices, self.ndim), + suggestion=String( + "Pass at most {} indices/slices (one per dimension)." + ).format(self.ndim), + location=String( + "NDArray.__setitem__(*slices: Variant[Slice, Int], val:" + " Self)" + ), + ) + ) + var slice_list: List[Slice] = List[Slice]() + + var count_int = 0 + for i in range(len(slices)): + if slices[i].isa[Slice](): + slice_list.append(slices[i]._get_ptr[Slice]()[0]) + elif slices[i].isa[Int](): + count_int += 1 + var int: Int = slices[i]._get_ptr[Int]()[0] + slice_list.append(Slice(int, int + 1, 1)) + + if n_slices < self.ndim: + for i in range(n_slices, self.ndim): + var size_at_dim: Int = self.shape[i] + slice_list.append(Slice(0, size_at_dim, 1)) + + # self.__setitem__(slices=slice_list, val=val) + self[slice_list^] = val + + fn __setitem__(self, index: NDArray[DType.int], val: Self) raises: """ Returns the items of the ComplexNDArray from an array of indices. @@ -1869,6 +2040,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( Int(index.load(i)), rebind[Scalar[Self.dtype]](val._im.load(i)) ) + # TODO: implement itemset(). fn __setitem__( mut self, mask: ComplexNDArray[cdtype], @@ -3141,7 +3313,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( fn __iter__( self, - ) raises -> _ComplexNDArrayIter[__origin_of(self._re), cdtype]: + ) raises -> _ComplexNDArrayIter[origin_of(self._re), cdtype]: """ Iterates over elements of the ComplexNDArray and return sub-arrays as view. @@ -3149,16 +3321,14 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( An iterator of ComplexNDArray elements. """ - return _ComplexNDArrayIter[__origin_of(self._re), cdtype]( + return _ComplexNDArrayIter[origin_of(self._re), cdtype]( self, dimension=0, ) fn __reversed__( self, - ) raises -> _ComplexNDArrayIter[ - __origin_of(self._re), cdtype, forward=False - ]: + ) raises -> _ComplexNDArrayIter[origin_of(self._re), cdtype, forward=False]: """ Iterates backwards over elements of the ComplexNDArray, returning copied value. @@ -3167,9 +3337,7 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( A reversed iterator of NDArray elements. """ - return _ComplexNDArrayIter[ - __origin_of(self._re), cdtype, forward=False - ]( + return _ComplexNDArrayIter[origin_of(self._re), cdtype, forward=False]( self, dimension=0, ) @@ -3272,13 +3440,13 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( var result: NDArray[dtype = Self.dtype] = NDArray[ dtype = Self.dtype ](self.shape) - memcpy(result._buf.ptr, self._re._buf.ptr, self.size) + memcpy(dest=result._buf.ptr, src=self._re._buf.ptr, count=self.size) return result^ elif type == "im": var result: NDArray[dtype = Self.dtype] = NDArray[ dtype = Self.dtype ](self.shape) - memcpy(result._buf.ptr, self._im._buf.ptr, self.size) + memcpy(dest=result._buf.ptr, src=self._im._buf.ptr, count=self.size) return result^ else: raise Error( @@ -3939,8 +4107,8 @@ struct ComplexNDArray[cdtype: ComplexDType = ComplexDType.float64]( ) ) - var diag_re = self._re.diagonal[Self.dtype](offset) - var diag_im = self._im.diagonal[Self.dtype](offset) + var diag_re = self[Self.dtype]._re.diagonal(offset) + var diag_im = self[Self.dtype]._im.diagonal(offset) return Self(diag_re^, diag_im^) fn trace(self) raises -> ComplexSIMD[cdtype]: @@ -4057,8 +4225,8 @@ struct _ComplexNDArrayIter[ # FIELDS var index: Int - var re_ptr: UnsafePointer[Scalar[Self.dtype]] - var im_ptr: UnsafePointer[Scalar[Self.dtype]] + var re_ptr: LegacyUnsafePointer[Scalar[Self.dtype]] + var im_ptr: LegacyUnsafePointer[Scalar[Self.dtype]] var dimension: Int var length: Int var shape: NDArrayShape @@ -4117,7 +4285,7 @@ struct _ComplexNDArrayIter[ for offset in range(self.size_of_item): var remainder = offset - var item = Item(ndim=self.ndim, initialized=False) + var item: Item = Item(ndim=self.ndim) for i in range(self.ndim - 1, -1, -1): if i != self.dimension: @@ -4180,7 +4348,7 @@ struct _ComplexNDArrayIter[ for offset in range(self.size_of_item): var remainder = offset - var item = Item(ndim=self.ndim, initialized=False) + var item: Item = Item(ndim=self.ndim) for i in range(self.ndim - 1, -1, -1): if i != self.dimension: diff --git a/numojo/core/complex/complex_simd.mojo b/numojo/core/complex/complex_simd.mojo index 9c23f775..96e67c7b 100644 --- a/numojo/core/complex/complex_simd.mojo +++ b/numojo/core/complex/complex_simd.mojo @@ -11,62 +11,77 @@ This module provides a ComplexSIMD type that represents complex numbers using SI operations for efficient computation. It supports basic arithmetic operations like addition, subtraction, multiplication, and division, as well as other complex number operations like conjugation and absolute value. - -The implementation allows for vectorized operations on complex numbers which can -significantly improve performance for numerical computations. """ -from math import sqrt - +from math import sqrt, sin, cos from numojo.core.complex.complex_dtype import ComplexDType -# ComplexScalar alias is for internal purposes -alias ComplexScalar[cdtype: ComplexDType] = ComplexSIMD[cdtype, width=1] -# CScalar is short alias for ComplexScalar for user convenience -alias CScalar[cdtype: ComplexDType] = ComplexSIMD[cdtype, width=1] +alias ComplexScalar = ComplexSIMD[_, width=1] +"""ComplexScalar alias is for internal purposes (width=1 specialization).""" + +alias CScalar = ComplexSIMD[_, width=1] +"""User-friendly alias for scalar complex numbers.""" + +alias `1j` = ImaginaryUnit() +"""Constant representing the imaginary unit complex number 0 + 1j. +Enables Python like syntax for complex numbers, e.g., (3 + 4 * `1j`).""" +# TODO: add overloads for arithmetic functions to accept Scalar[dtype]. @register_passable("trivial") -struct ComplexSIMD[cdtype: ComplexDType, width: Int = 1]( +struct ComplexSIMD[cdtype: ComplexDType = ComplexDType.float64, width: Int = 1]( ImplicitlyCopyable, Movable, Stringable, Writable ): """ - A SIMD-enabled complex number type that supports vectorized operations. - - Parameters: - cdtype: The complex data type (like cf32 or cf64) that determines precision. - width: The SIMD vector width, defaulting to 1 for scalar operations. - - The struct contains two SIMD vectors - one for the real part and one for the - imaginary part. This allows complex arithmetic to be performed efficiently using - SIMD operations. When width=1 it acts as a regular complex scalar type. - - Example: - ```mojo - import numojo as nm - var A = nm.ComplexSIMD[nm.cf32](1.0, 2.0) - var B = nm.ComplexSIMD[nm.cf32](3.0, 4.0) - var C = A + B - print(C) # Output: (4.0 + 6.0 j) - - var A1 = nm.ComplexSIMD[nm.cf32, 2](SIMD[nm.f32](1.0, 1.0), SIMD[nm.f32](2.0, 2.0)) - print(A1) # Output: ([1.0, 1.0] + [2.0, 2.0] j) - ``` + A SIMD-enabled complex number container (SoA layout). + + Fields: + re: SIMD vector of real parts. + im: SIMD vector of imaginary parts. + + The parameter `cdtype` determines the component precision (e.g. cf32, cf64). + The parameter `width` is the SIMD lane count; when `width == 1` this acts like a scalar complex number. + + Examples: + ```mojo + from numojo.prelude import * + var a = ComplexSIMD[cf32](1.0, 2.0) + var b = ComplexSIMD[cf32](3.0, 4.0) + print(a + b) # (4.0 + 6.0 j) + + # SIMD width=2: + var a2 = ComplexSIMD[cf32, 2]( + SIMD[cf32._dtype, 2](1.0, 1.5), + SIMD[cf32._dtype, 2](2.0, -0.5) + ) + print(a2) # ( [1.0 2.0] + [1.5 -0.5]j ) + ``` + Convenience factories: + ComplexSIMD[cf64].zero() + ComplexSIMD[cf64].one() + ComplexSIMD[cf64].i() + ComplexSIMD[cf64].from_polar(2.0, 0.5) """ - # FIELDS - alias dtype: DType = cdtype._dtype # the corresponding DType - # The underlying data real and imaginary parts of the complex number. + alias dtype: DType = cdtype._dtype + """Component dtype alias (underlying real/imag dtype).""" + var re: SIMD[Self.dtype, width] var im: SIMD[Self.dtype, width] + # --- Internal helper for broadcasting scalar to SIMD lanes --- + @staticmethod + @always_inline + fn _broadcast(val: Scalar[Self.dtype]) -> SIMD[Self.dtype, Self.width]: + return SIMD[Self.dtype, Self.width](val) + + # --- Constructors --- @always_inline fn __init__(out self, other: Self): """ - Initializes a ComplexSIMD instance by copying another instance. + Copy constructor for ComplexSIMD. - Arguments: - other: Another ComplexSIMD instance to copy from. + Initializes a new ComplexSIMD instance by copying the values from another instance. """ self = other @@ -77,20 +92,11 @@ struct ComplexSIMD[cdtype: ComplexDType, width: Int = 1]( im: SIMD[Self.dtype, Self.width], ): """ - Initializes a ComplexSIMD instance with specified real and imaginary parts. - - Arguments: - re: The real part of the complex number. - im: The imaginary part of the complex number. + Constructs a ComplexSIMD from SIMD vectors of real and imaginary parts. - Example: - ```mojo - import numojo as nm - var A = nm.ComplexSIMD[nm.cf32](1.0, 2.0) - var B = nm.ComplexSIMD[nm.cf32](3.0, 4.0) - var C = A + B - print(C) - ``` + Args: + re: SIMD vector containing the real components. + im: SIMD vector containing the imaginary components. """ self.re = re self.im = im @@ -98,94 +104,582 @@ struct ComplexSIMD[cdtype: ComplexDType, width: Int = 1]( @always_inline fn __init__(out self, val: SIMD[Self.dtype, Self.width]): """ - Initializes a ComplexSIMD instance with specified real and imaginary parts. + Constructs a ComplexSIMD where both real and imaginary parts are set to the same SIMD value. - Arguments: - re: The real part of the complex number. - im: The imaginary part of the complex number. + Args: + val: SIMD vector to broadcast to both real and imaginary components. """ self.re = val self.im = val + # Factory constructors. + @staticmethod + fn zero() -> Self: + """ + Returns a ComplexSIMD instance with all real and imaginary components set to zero. + + Example: + ```mojo + from numojo.prelude import * + var comp = ComplexSIMD[cf64].zero() # (0 + 0j) + ``` + """ + return Self(Self._broadcast(0), Self._broadcast(0)) + + @staticmethod + fn one() -> Self: + """ + Returns a ComplexSIMD instance representing the complex number 1 + 0j. + + Example: + ```mojo + from numojo.prelude import * + var comp = ComplexSIMD[cf64].one() # (1 + 0j) + ``` + """ + return Self(Self._broadcast(1), Self._broadcast(0)) + + @staticmethod + fn i() -> Self: + """ + Returns a ComplexSIMD instance representing the imaginary unit 0 + 1j. + + Returns: + ComplexSIMD instance with real part 0 and imaginary part 1 for all lanes. + + Examples: + ```mojo + from numojo.prelude import * + + # Create imaginary unit for different types + var i_f64 = ComplexSIMD[cf64].i() # (0 + 1j) + var i_f32 = ComplexSIMD[cf32].i() # (0 + 1j) + print(i_f64) # (0 + 1j) + + # Use in complex arithmetic + var z = 3.0 + 4.0 * ComplexSIMD[cf64].i() # 3 + 4j + ``` + """ + return Self(Self._broadcast(0), Self._broadcast(1)) + + @staticmethod + fn from_real_imag(re: Scalar[Self.dtype], im: Scalar[Self.dtype]) -> Self: + """ + Constructs a ComplexSIMD instance from scalar real and imaginary values. + + Args: + re: Scalar value for the real component. + im: Scalar value for the imaginary component. + + Example: + ```mojo + from numojo.prelude import * + var comp = ComplexSIMD[cf64].from_real_imag(2.0, 3.0) # (2.0 + 3.0j) + ``` + """ + return Self(re, im) + + @staticmethod + fn from_polar(r: Scalar[Self.dtype], theta: Scalar[Self.dtype]) -> Self: + """ + Constructs a ComplexSIMD instance from polar coordinates. + + Args: + r: Magnitude (radius). + theta: Angle (in radians). + + Returns: + ComplexSIMD instance with real part r * cos(theta) and imaginary part r * sin(theta). + + Example: + ```mojo + from numojo.prelude import * + var comp = ComplexSIMD[cf64].from_polar(2.0, 0.5) + ``` + """ + return Self( + Self._broadcast(r * cos(theta)), + Self._broadcast(r * sin(theta)), + ) + + # --- Arithmetic operators --- fn __add__(self, other: Self) -> Self: """ - Adds two ComplexSIMD instances. + Returns the element-wise sum of two ComplexSIMD instances. - Arguments: - other: The ComplexSIMD instance to add. + Args: + other: Another ComplexSIMD instance. Returns: - Self: A new ComplexSIMD instance representing the sum. + ComplexSIMD instance where each lane is the sum of corresponding lanes. """ return Self(self.re + other.re, self.im + other.im) + fn __add__(self, other: Scalar[Self.dtype]) -> Self: + """ + Returns the sum of this ComplexSIMD instance and a scalar added to the real part. + + Args: + other: Scalar value to add to the real component. + + Returns: + ComplexSIMD instance where each lane's real part is increased by the scalar. + """ + return Self(self.re + Self._broadcast(other), self.im) + + # FIXME: currently mojo doesn't allow overloading with both SIMD[Self.dtype, Self.width] and SIMD[*_, size=Self.width]. So keep SIMD[*_, size=Self.width] only for now. We need this method to create complex numbers with syntax like (1 + 2 * `1j`). + fn __add__(self, other: SIMD[*_, size = Self.width]) -> Self: + """ + Returns the sum of this ComplexSIMD instance and a SIMD vector added to the real part. + + Args: + other: SIMD vector to add to the real component. + + Returns: + ComplexSIMD instance where each lane's real part is increased by the corresponding lane in the SIMD vector. + """ + return Self(self.re + other.cast[Self.dtype](), self.im) + + fn __add__(self, other: ImaginaryUnit) -> Self: + """ + Returns the sum of this ComplexSIMD instance and the imaginary unit 1j. + + Args: + other: Imaginary unit (1j) to add to this complex number. + + Returns: + ComplexSIMD instance where each lane's imaginary part is increased by 1. + If self = a + bj, then result = a + (b+1)j. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + var result = z + `1j` # 3 + 3j + print(result) # (3 + 3j) + ``` + """ + return Self(self.re, self.im + Self._broadcast(1)) + fn __iadd__(mut self, other: Self): """ - Performs in-place addition of another ComplexSIMD instance. + In-place addition of another ComplexSIMD instance. - Arguments: - other: The ComplexSIMD instance to add. + Args: + other: Another ComplexSIMD instance. """ self.re += other.re self.im += other.im + fn __iadd__(mut self, other: Scalar[Self.dtype]): + """ + In-place addition of a scalar to the real part of this ComplexSIMD instance. + + Args: + other: Scalar value to add to the real component. + """ + self.re += Self._broadcast(other) + + fn __iadd__(mut self, other: SIMD[*_, size = Self.width]): + """ + In-place addition of a SIMD vector to the real part of this ComplexSIMD instance. + + Args: + other: SIMD vector to add to the real component. + """ + self.re += other.cast[Self.dtype]() + + fn __iadd__(mut self, other: ImaginaryUnit): + """ + In-place addition of the imaginary unit 1j to this ComplexSIMD instance. + + Args: + other: Imaginary unit (1j) to add to this complex number. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + z += `1j` # Now z = 3 + 3j + print(z) # (3 + 3j) + ``` + """ + self.im += Self._broadcast(1) + + fn __radd__(self, other: Scalar[Self.dtype]) -> Self: + """ + Returns the sum of a scalar and this ComplexSIMD instance, adding to the real part. + + Args: + other: Scalar value to add to the real component. + + Returns: + ComplexSIMD instance where each lane's real part is increased by the scalar. + """ + return Self(Self._broadcast(other) + self.re, self.im) + + fn __radd__(self, other: SIMD[*_, size = Self.width]) -> Self: + """ + Returns the sum of a SIMD vector and this ComplexSIMD instance, adding to the real part. + + Args: + other: SIMD vector to add to the real component. + + Returns: + ComplexSIMD instance where each lane's real part is increased by the corresponding lane in the SIMD vector. + """ + return Self(other.cast[Self.dtype]() + self.re, self.im) + + fn __radd__(self, other: ImaginaryUnit) -> Self: + """ + Returns the sum of the imaginary unit 1j and this ComplexSIMD instance. + + Args: + other: Imaginary unit (1j) to add to this complex number. + + Returns: + ComplexSIMD instance where each lane's imaginary part is increased by 1. + If self = a + bj, then result = a + (b+1)j. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + var result = `1j` + z # 3 + 3j + print(result) # (3 + 3j) + ``` + """ + return Self(self.re, self.im + Self._broadcast(1)) + fn __sub__(self, other: Self) -> Self: """ - Subtracts another ComplexSIMD instance from this one. + Returns the element-wise difference of two ComplexSIMD instances. - Arguments: - other: The ComplexSIMD instance to subtract. + Args: + other: Another ComplexSIMD instance. Returns: - Self: A new ComplexSIMD instance representing the difference. + ComplexSIMD instance where each lane is the difference of corresponding lanes. """ return Self(self.re - other.re, self.im - other.im) + fn __sub__(self, other: Scalar[Self.dtype]) -> Self: + """ + Returns the difference of this ComplexSIMD instance and a scalar subtracted from the real part. + + Args: + other: Scalar value to subtract from the real component. + + Returns: + ComplexSIMD instance where each lane's real part is decreased by the scalar. + """ + return Self(self.re - Self._broadcast(other), self.im) + + fn __sub__(self, other: SIMD[*_, size = Self.width]) -> Self: + """ + Returns the difference of this ComplexSIMD instance and a SIMD vector subtracted from the real part. + + Args: + other: SIMD vector to subtract from the real component. + + Returns: + ComplexSIMD instance where each lane's real part is decreased by the corresponding lane in the SIMD vector. + """ + return Self(self.re - other.cast[Self.dtype](), self.im) + + fn __sub__(self, other: ImaginaryUnit) -> Self: + """ + Subtracts the imaginary unit 1j from this ComplexSIMD instance. + + Args: + other: Imaginary unit (1j) to subtract from this complex number. + + Returns: + ComplexSIMD instance where each lane's imaginary part is decreased by 1. + If self = a + bj, then result = a + (b-1)j. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + var result = z - `1j` # 3 + 1j + print(result) # (3 + 1j) + ``` + """ + return Self(self.re, self.im - Self._broadcast(1)) + fn __isub__(mut self, other: Self): """ - Performs in-place subtraction of another ComplexSIMD instance. + In-place subtraction of another ComplexSIMD instance. - Arguments: - other: The ComplexSIMD instance to subtract. + Args: + other: Another ComplexSIMD instance. """ self.re -= other.re self.im -= other.im + fn __isub__(mut self, other: Scalar[Self.dtype]): + """ + In-place subtraction of a scalar from the real part of this ComplexSIMD instance. + + Args: + other: Scalar value to subtract from the real component. + """ + self.re -= Self._broadcast(other) + + fn __isub__(mut self, other: SIMD[*_, size = Self.width]): + """ + In-place subtraction of a SIMD vector from the real part of this ComplexSIMD instance. + + Args: + other: SIMD vector to subtract from the real component. + """ + self.re -= other.cast[Self.dtype]() + + fn __isub__(mut self, other: ImaginaryUnit): + """ + In-place subtraction of the imaginary unit 1j from this ComplexSIMD instance. + + Args: + other: Imaginary unit (1j) to subtract from this complex number. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + z -= `1j` # Now z = 3 + 1j + print(z) # (3 + 1j) + ``` + """ + self.im -= Self._broadcast(1) + + fn __rsub__(self, other: Scalar[Self.dtype]) -> Self: + """ + Returns the difference of a scalar and this ComplexSIMD instance, subtracting from the real part. + + Args: + other: Scalar value to subtract from the real component. + + Returns: + ComplexSIMD instance where each lane's real part is (scalar - self.re). + """ + return Self(Self._broadcast(other) - self.re, -self.im) + + fn __rsub__(self, other: SIMD[*_, size = Self.width]) -> Self: + """ + Returns the difference of a SIMD vector and this ComplexSIMD instance, subtracting from the real part. + + Args: + other: SIMD vector to subtract from the real component. + + Returns: + ComplexSIMD instance where each lane's real part is (SIMD lane - self.re). + """ + var other_casted = other.cast[Self.dtype]() + return Self(other_casted - self.re, -self.im) + + fn __rsub__(self, other: ImaginaryUnit) -> Self: + """ + Returns the difference of the imaginary unit 1j and this ComplexSIMD instance. + + Args: + other: Imaginary unit (1j) from which this complex number is subtracted. + + Returns: + ComplexSIMD instance equal to 1j - self. + If self = a + bj, then result = -a + (1-b)j. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + var result = `1j` - z # -3 + (-1)j = -3 - 1j + print(result) # (-3 - 1j) + ``` + """ + return Self(-self.re, Self._broadcast(1) - self.im) + fn __mul__(self, other: Self) -> Self: """ - Multiplies two ComplexSIMD instances. + Returns the element-wise product of two ComplexSIMD instances. - Arguments: - other: The ComplexSIMD instance to multiply with. + Args: + other: Another ComplexSIMD instance. Returns: - Self: A new ComplexSIMD instance representing the product. + ComplexSIMD instance where each lane is the product of corresponding lanes, using complex multiplication: (a+bi)(c+di) = (ac - bd) + (ad + bc)i. """ return Self( self.re * other.re - self.im * other.im, self.re * other.im + self.im * other.re, ) + fn __mul__(self, other: Scalar[Self.dtype]) -> Self: + """ + Returns the product of this ComplexSIMD instance and a scalar. + + Args: + other: Scalar value to multiply with both real and imaginary parts. + + Returns: + ComplexSIMD instance where each lane is scaled by the scalar. + """ + var scalar_simd = Self._broadcast(other) + return Self(self.re * scalar_simd, self.im * scalar_simd) + + fn __mul__(self, other: SIMD[*_, size = Self.width]) -> Self: + """ + Returns the product of this ComplexSIMD instance and a SIMD vector. + + Args: + other: SIMD vector to multiply with both real and imaginary parts. + + Returns: + ComplexSIMD instance where each lane is scaled by the corresponding lane in the SIMD vector. + """ + var other_casted = other.cast[Self.dtype]() + return Self(self.re * other_casted, self.im * other_casted) + + fn __mul__(self, other: ImaginaryUnit) -> Self: + """ + Returns the product of this ComplexSIMD instance and the imaginary unit 1j. + + Args: + other: Imaginary unit (1j) to multiply with this complex number. + + Returns: + ComplexSIMD instance where each lane is multiplied by 1j. + If self = a + bj, then result = (a + bj) * 1j = -b + aj. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + var result = z * `1j` # -2 + 3j + print(result) # (-2 + 3j) + ``` + """ + return Self(-self.im, self.re) + fn __imul__(mut self, other: Self): """ - Performs in-place multiplication with another ComplexSIMD instance. + In-place complex multiplication with another ComplexSIMD instance. - Arguments: - other: The ComplexSIMD instance to multiply with. + Args: + other: Another ComplexSIMD instance. """ - var re = self.re * other.re - self.im * other.im + var new_re = self.re * other.re - self.im * other.im self.im = self.re * other.im + self.im * other.re - self.re = re + self.re = new_re + + fn __imul__(mut self, other: Scalar[Self.dtype]): + """ + In-place multiplication of this ComplexSIMD instance by a scalar. + + Args: + other: Scalar value to multiply with both real and imaginary parts. + """ + var scalar_simd = Self._broadcast(other) + self.re *= scalar_simd + self.im *= scalar_simd + + fn __imul__(mut self, other: SIMD[*_, size = Self.width]): + """ + In-place multiplication of this ComplexSIMD instance by a SIMD vector. + + Args: + other: SIMD vector to multiply with both real and imaginary parts. + """ + var other_casted = other.cast[Self.dtype]() + self.re *= other_casted + self.im *= other_casted + + fn __imul__(mut self, other: ImaginaryUnit): + """ + In-place multiplication of this ComplexSIMD instance by the imaginary unit 1j. + + Args: + other: Imaginary unit (1j) to multiply with this complex number. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + z *= `1j` # Now z = -2 + 3j + print(z) # (-2 + 3j) + ``` + """ + var new_re = -self.im + self.im = self.re + self.re = new_re + + fn __rmul__(self, other: Scalar[Self.dtype]) -> Self: + """ + Returns the product of a scalar and this ComplexSIMD instance. + + Args: + other: Scalar value to multiply with both real and imaginary parts. + + Returns: + ComplexSIMD instance where each lane is scaled by the scalar. + """ + var scalar_simd = Self._broadcast(other) + return Self(scalar_simd * self.re, scalar_simd * self.im) + + fn __rmul__(self, other: SIMD[*_, size = Self.width]) -> Self: + """ + Returns the product of a SIMD vector and this ComplexSIMD instance. + + Args: + other: SIMD vector to multiply with both real and imaginary parts. + + Returns: + ComplexSIMD instance where each lane is scaled by the corresponding lane in the SIMD vector. + """ + var other_casted = other.cast[Self.dtype]() + return Self(other_casted * self.re, other_casted * self.im) + + fn __rmul__(self, other: ImaginaryUnit) -> Self: + """ + Returns the product of the imaginary unit 1j and this ComplexSIMD instance. + + Args: + other: Imaginary unit (1j) to multiply with this complex number. + + Returns: + ComplexSIMD instance where each lane is multiplied by 1j. + If self = a + bj, then result = 1j * (a + bj) = -b + aj. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + var result = `1j` * z # -2 + 3j + print(result) # (-2 + 3j) + ``` + """ + return Self(-self.im, self.re) fn __truediv__(self, other: Self) -> Self: """ - Divides this ComplexSIMD instance by another. + Performs element-wise complex division of two ComplexSIMD instances. - Arguments: - other: The ComplexSIMD instance to divide by. + Args: + other: Another ComplexSIMD instance to divide by. Returns: - Self: A new ComplexSIMD instance representing the quotient. + ComplexSIMD instance where each lane is the result of dividing the corresponding lanes: + (a + bi) / (c + di) = [(ac + bd) / (c^2 + d^2)] + [(bc - ad) / (c^2 + d^2)]i + where a, b are self.re, self.im and c, d are other.re, other.im. """ var denom = other.re * other.re + other.im * other.im return Self( @@ -193,214 +687,1075 @@ struct ComplexSIMD[cdtype: ComplexDType, width: Int = 1]( (self.im * other.re - self.re * other.im) / denom, ) + fn __truediv__(self, other: Scalar[Self.dtype]) -> Self: + """ + Performs element-wise division of this ComplexSIMD instance by a scalar. + + Args: + other: Scalar value to divide both real and imaginary parts by. + + Returns: + ComplexSIMD instance where each lane is divided by the scalar. + """ + var scalar_simd = Self._broadcast(other) + return Self(self.re / scalar_simd, self.im / scalar_simd) + + fn __truediv__(self, other: SIMD[*_, size = Self.width]) -> Self: + """ + Performs element-wise division of this ComplexSIMD instance by a SIMD vector. + + Args: + other: SIMD vector to divide both real and imaginary parts by. + + Returns: + ComplexSIMD instance where each lane is divided by the corresponding lane in the SIMD vector. + """ + var other_casted = other.cast[Self.dtype]() + return Self(self.re / other_casted, self.im / other_casted) + + fn __truediv__(self, other: ImaginaryUnit) -> Self: + """ + Performs division of this ComplexSIMD instance by the imaginary unit 1j. + + Args: + other: Imaginary unit (1j) to divide this complex number by. + + Returns: + ComplexSIMD instance where each lane is divided by 1j. + If self = a + bj, then result = (a + bj) / 1j = b - aj. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + var result = z / `1j` # 2 - 3j + print(result) # (2 - 3j) + ``` + """ + return Self(self.im, -self.re) + fn __itruediv__(mut self, other: Self): """ - Performs in-place division by another ComplexSIMD instance. + Performs in-place element-wise complex division of self by another ComplexSIMD instance. - Arguments: - other: The ComplexSIMD instance to divide by. + Args: + other: Another ComplexSIMD instance to divide by. """ var denom = other.re * other.re + other.im * other.im - var re = (self.re * other.re + self.im * other.im) / denom + var new_re = (self.re * other.re + self.im * other.im) / denom self.im = (self.im * other.re - self.re * other.im) / denom - self.re = re + self.re = new_re - fn __pow__(self, other: Self) -> Self: + fn __itruediv__(mut self, other: Scalar[Self.dtype]): """ - Raises this ComplexSIMD instance to the power of another. + Performs in-place element-wise division of this ComplexSIMD instance by a scalar. - Arguments: - other: The ComplexSIMD instance to raise to the power of. + Args: + other: Scalar value to divide both real and imaginary parts by. + """ + var scalar_simd = Self._broadcast(other) + self.re /= scalar_simd + self.im /= scalar_simd + + fn __itruediv__(mut self, other: SIMD[*_, size = Self.width]): + """ + Performs in-place element-wise division of this ComplexSIMD instance by a SIMD vector. + + Args: + other: SIMD vector to divide both real and imaginary parts by. + """ + var other_casted = other.cast[Self.dtype]() + self.re /= other_casted + self.im /= other_casted + + fn __itruediv__(mut self, other: ImaginaryUnit): + """ + Performs in-place division of this ComplexSIMD instance by the imaginary unit 1j. + + Args: + other: Imaginary unit (1j) to divide this complex number by. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 2.0) # 3 + 2j + z /= `1j` # Now z = 2 - 3j + print(z) # (2 - 3j) + ``` + """ + var new_re = self.im + self.im = -self.re + self.re = new_re + + fn __rtruediv__(self, other: Scalar[Self.dtype]) -> Self: + """ + Performs element-wise division of a scalar by this ComplexSIMD instance. + + Args: + other: Scalar value to be divided by this ComplexSIMD instance. Returns: - Self: A new ComplexSIMD instance representing the result. + ComplexSIMD instance where each lane is the result of dividing the scalar by the corresponding lane: + other / (a + bi) = [other * a / (a^2 + b^2)] + [-other * b / (a^2 + b^2)]i + where a, b are self.re, self.im. + """ + var denom = self.re * self.re + self.im * self.im + var scalar_simd = Self._broadcast(other) + return Self( + (scalar_simd * self.re) / denom, + (-scalar_simd * self.im) / denom, + ) + + fn __rtruediv__(self, other: SIMD[*_, size = Self.width]) -> Self: + """ + Performs element-wise division of a SIMD vector by this ComplexSIMD instance. + + Args: + other: SIMD vector to be divided by this ComplexSIMD instance. + + Returns: + ComplexSIMD instance where each lane is the result of dividing the corresponding lane in the SIMD vector by the corresponding lane in this ComplexSIMD: + other[i] / (a + bi) = [other[i] * a / (a^2 + b^2)] + [-other[i] * b / (a^2 + b^2)]i + where a, b are self.re, self.im. + """ + var denom = self.re * self.re + self.im * self.im + var other_casted = other.cast[Self.dtype]() + return Self( + (other_casted * self.re) / denom, + (-other_casted * self.im) / denom, + ) + + fn __rtruediv__(self, other: ImaginaryUnit) -> Self: + """ + Performs division of the imaginary unit 1j by this ComplexSIMD instance. + + Args: + other: Imaginary unit (1j) to be divided by this ComplexSIMD instance. + + Returns: + ComplexSIMD instance where each lane is the result of dividing 1j by the corresponding lane. + If self = a + bj, then result = 1j / (a + bj) = [b / (a² + b²)] + [-a / (a² + b²)]j. + + Examples: + ```mojo + from numojo.prelude import * + + var z = ComplexSIMD[cf64, 1](3.0, 4.0) # 3 + 4j + var result = `1j` / z # 1j / (3 + 4j) = 0.16 - 0.12j + print(result) # (0.16 - 0.12j) + ``` + """ + var denom = self.re * self.re + self.im * self.im + return Self( + self.im / denom, + -self.re / denom, + ) + + fn reciprocal(self) raises -> Self: + """ + Returns the element-wise reciprocal (1 / self) of the ComplexSIMD instance. + + Returns: + ComplexSIMD instance representing the reciprocal of each lane: + 1 / (a + bi) = (a / (a^2 + b^2)) + (-b / (a^2 + b^2)). + """ + var d = self.norm() + if d == 0: + raise Error( + "Cannot compute reciprocal of zero norm complex number." + ) + return Self(self.re / d, -self.im / d) + + # --- Power helpers --- + fn elem_pow(self, other: Self) -> Self: + """ + Raises each component of this ComplexSIMD to the power of the corresponding component in another ComplexSIMD. + + Args: + other: Another ComplexSIMD instance. + + Returns: + ComplexSIMD instance where each lane is (re^other.re, im^other.im). """ return Self(self.re**other.re, self.im**other.im) - fn __pow__(self, other: Scalar[Self.dtype]) -> Self: + fn elem_pow(self, exponent: Scalar[Self.dtype]) -> Self: """ - Raises this ComplexSIMD instance to the power of a scalar. + Raises each component of this ComplexSIMD to a scalar exponent. - Arguments: - other: The scalar to raise to the power of. + Args: + exponent: Scalar exponent to apply to both real and imaginary parts. Returns: - Self: A new ComplexSIMD instance representing the result. + ComplexSIMD instance where each lane is (re^exponent, im^exponent). """ - return Self(self.re**other, self.im**other) + return Self(self.re**exponent, self.im**exponent) + fn __pow__(self, n: Int) -> Self: + """ + Raises this ComplexSIMD to an integer. + + Args: + n: Integer exponent. + + Returns: + ComplexSIMD instance raised to the power n. + For negative n, returns the reciprocal of self raised to -n. + """ + if n == 0: + return Self.one() + var base = self + var exp = n + var result = Self.one() + var is_negative = exp < 0 + if is_negative: + exp = -exp + while exp > 0: + if (exp & 1) == 1: + result = result * base + base = base * base + exp >>= 1 + if is_negative: + return Self.one() / result + return result + + # --- Unary operators --- fn __pos__(self) -> Self: """ - Returns the ComplexSIMD instance itself. + Returns the positive value of this ComplexSIMD (identity operation). Returns: - Self: The ComplexSIMD instance itself. + The same ComplexSIMD instance. """ return self fn __neg__(self) -> Self: """ - Negates the ComplexSIMD instance. + Returns the negation of this ComplexSIMD. Returns: - Self: The negated ComplexSIMD instance. + ComplexSIMD instance with both real and imaginary parts negated. """ - return self * Self(-1, -1) + return Self(-self.re, -self.im) + + # --- Helpers --- + @staticmethod + @always_inline + fn _abs_simd( + x: SIMD[Self.dtype, Self.width] + ) -> SIMD[Self.dtype, Self.width]: + return sqrt(x * x) + # --- Equality --- fn __eq__(self, other: Self) -> Bool: """ - Checks if two ComplexSIMD instances are equal. - - Arguments: - self: The first ComplexSIMD instance. - other: The second ComplexSIMD instance to compare with. + Checks if two ComplexSIMD instances are exactly equal. Returns: - Bool: True if the instances are equal, False otherwise. + True if both the real and imaginary parts are equal for all lanes, otherwise False. """ return (self.re == other.re) and (self.im == other.im) + fn __eq__(self, other: ImaginaryUnit) -> Bool: + """ + Checks if this ComplexSIMD instance is equal to the imaginary unit 1j. + + Args: + other: Imaginary unit (1j) to compare with this ComplexSIMD instance. + + Returns: + True if the real part is 0 and the imaginary part is 1 for all lanes, otherwise False. + + Examples: + ```mojo + from numojo.prelude import * + + var z1 = ComplexSIMD[cf64, 1](0.0, 1.0) # 0 + 1j + var z2 = ComplexSIMD[cf64, 1](1.0, 1.0) # 1 + 1j + print(z1 == `1j`) # True + print(z2 == `1j`) # False + ``` + """ + return (self.re == Self._broadcast(0)) and ( + self.im == Self._broadcast(1) + ) + fn __ne__(self, other: Self) -> Bool: """ Checks if two ComplexSIMD instances are not equal. - Arguments: - self: The first ComplexSIMD instance. - other: The second ComplexSIMD instance to compare with. + Returns: + True if either the real or imaginary parts differ for any lane, otherwise False. + """ + return ~(self == other) + + fn __ne__(self, other: ImaginaryUnit) -> Bool: + """ + Checks if this ComplexSIMD instance is not equal to the imaginary unit 1j. + + Args: + other: Imaginary unit (1j) to compare with this ComplexSIMD instance. Returns: - Bool: True if the instances are not equal, False otherwise. + True if either the real part is not 0 or the imaginary part is not 1 for any lane, otherwise False. + + Examples: + ```mojo + from numojo.prelude import * + + var z1 = ComplexSIMD[cf64, 1](0.0, 1.0) # 0 + 1j + var z2 = ComplexSIMD[cf64, 1](1.0, 1.0) # 1 + 1j + print(z1 != `1j`) # False + print(z2 != `1j`) # True + ``` """ return ~(self == other) - fn __str__(self) -> String: + fn allclose( + self, + other: Self, + *, + rtol: Scalar[Self.dtype] = 1e-5, + atol: Scalar[Self.dtype] = 1e-8, + ) -> Bool: """ - Returns a string representation of the ComplexSIMD instance. + Checks if two ComplexSIMD instances are approximately equal within given tolerances. + + For each lane, compares the real and imaginary parts using the formula: + abs(a - b) <= atol + rtol * abs(b) + where a and b are the corresponding components of self and other. + + Args: + other: Another ComplexSIMD instance to compare against. + rtol: Relative tolerance. + atol: Absolute tolerance. Returns: - String: The string representation of the ComplexSIMD instance. + True if all lanes of both real and imaginary parts are within the specified tolerances, otherwise False. + + Note: + For SIMD width > 1, all lanes must satisfy the tolerance criteria. """ + # TODO: Optionally return a SIMD[Bool] mask instead of a single Bool. + var diff_re = Self._abs_simd(self.re - other.re) + var diff_im = Self._abs_simd(self.im - other.im) + var rtol_b = Self._broadcast(rtol) + var atol_b = Self._broadcast(atol) + var thresh_re = atol_b + rtol_b * Self._abs_simd(other.re) + var thresh_im = atol_b + rtol_b * Self._abs_simd(other.im) + var ok_re = diff_re <= thresh_re + var ok_im = diff_im <= thresh_im + return ok_re and ok_im + + # --- Representations --- + fn __str__(self) -> String: return String.write(self) fn write_to[W: Writer](self, mut writer: W): """ - Writes the ComplexSIMD instance to a writer. + Returns a string representation of the ComplexSIMD instance. - Arguments: - self: The ComplexSIMD instance to write. - writer: The writer to write to. + For width == 1, the format is: (re + im j). + For width > 1, the format is: [(re0 + im0 j), (re1 + im1 j), ...]. """ try: - writer.write(String("({} + {} j)").format(self.re, self.im)) + + @parameter + if Self.width == 1: + writer.write(String("({} + {} j)").format(self.re, self.im)) + else: + var s = String("[") + for i in range(0, Self.width): + if i > 0: + s += ", " + s += String("({} + {} j)").format(self.re[i], self.im[i]) + s += "]" + writer.write(s) except e: - writer.write("Cannot convert ComplexSIMD to string") + writer.write("<>") fn __repr__(self) raises -> String: """ - Returns a string representation of the ComplexSIMD instance. - - Returns: - String: The string representation of the ComplexSIMD instance. + Returns a string representation of the ComplexSIMD instance for debugging. `ComplexSIMD[dtype](re=, im=)`. """ - return String("ComplexSIMD[{}]({}, {})").format( + return String("ComplexSIMD[{}](re={}, im={})").format( String(Self.dtype), self.re, self.im ) - fn __getitem__(self, idx: Int) raises -> SIMD[Self.dtype, Self.width]: + # --- Indexing --- + fn __getitem__(self, idx: Int) raises -> ComplexScalar[Self.cdtype]: """ - Gets the real or imaginary part of the ComplexSIMD instance. + Returns the complex number at the specified lane index. - Arguments: - self: The ComplexSIMD instance. - idx: The index to access (0 for real, 1 for imaginary). + Args: + idx: SIMD lane index (0 to width-1). Returns: - SIMD[Self.dtype, 1]: The requested part of the ComplexSIMD instance. + ComplexScalar containing the complex number at that lane index. + + Raises: + Error if lane index is out of range for the SIMD width. + + Example: + ```mojo + from numojo.prelude import * + var c_simd = ComplexSIMD[cf32, 2](SIMD[f32, 2](1, 2), SIMD[f32, 2](3, 4)) + var c0 = c_simd[0] # 1 + 3j + var c1 = c_simd[1] # 2 + 4j + ``` """ - if idx == 0: - return self.re - elif idx == 1: - return self.im - else: - raise Error("Index out of range") + if idx < 0 or idx >= Self.width: + raise Error("Lane index out of range for SIMD width") + return ComplexScalar[Self.cdtype](self.re[idx], self.im[idx]) fn __setitem__( - mut self, idx: Int, value: SIMD[Self.dtype, Self.width] + mut self, idx: Int, value: ComplexScalar[Self.cdtype] ) raises: """ - Sets the real and imaginary parts of the ComplexSIMD instance. + Sets the complex scalar at the specified lane index. + + Args: + idx: SIMD lane index (0 to width-1). + value: ComplexScalar whose values will be assigned. + + Raises: + Error if lane index is out of range for the SIMD width. + + Example: + ```mojo + from numojo.prelude import * + var c_simd = nm.ComplexSIMD[cf32, 2](SIMD[f32, 2](1, 2), SIMD[f32, 2](3, 4)) # [(1 + 3j), (2 + 4j)] + c_simd[0] = nm.CScalar[cf32](5, 6) + print(c_simd) # [(1 + 3j), (2 + 4j)] becomes [(5 + 6j), (2 + 4j)] + ``` + """ + if idx < 0 or idx >= Self.width: + raise Error("Lane index out of range for SIMD width") + self.re[idx] = value.re + self.im[idx] = value.im - Arguments: - self: The ComplexSIMD instance to modify. - idx: The index to access (0 for real, 1 for imaginary). - value: The new value to set. + fn item[name: String](self, idx: Int) raises -> Scalar[Self.dtype]: """ - if idx == 0: - self.re = value - elif idx == 1: - self.im = value + Returns the scalar value for the specified lane index and component. + + Parameters: + name: Name of the component ('re' or 'im'). + + Args: + idx: Lane index to retrieve. + + Returns: + Scalar value of the specified component at the given lane index. + + Raises: + - Error if the component name is invalid. + - Error if lane index is out of range for the SIMD width. + + Example: + ```mojo + from numojo.prelude import * + var c_simd = nm.ComplexSIMD[cf32, 2](SIMD[f32, 2](1, 2), SIMD[f32, 2](3, 4)) # [(1 + 3j), (2 + 4j)] + var re0 = c_simd.item["re"](0) # 1.0 + var im1 = c_simd.item["im"](1) # 4.0 + ``` + """ + if idx < 0 or idx >= Self.width: + raise Error("Lane index out of range for SIMD width") + + @parameter + if name == "re": + return self.re[idx] + elif name == "im": + return self.im[idx] else: - raise Error("Index out of range") + raise Error("Invalid component name: {}".format(name)) - fn __setitem__(mut self, idx: Int, value: Self) raises: + fn itemset[ + name: String + ](mut self, idx: Int, val: Scalar[Self.dtype]) raises: """ - Sets the real and imaginary parts of the ComplexSIMD instance. + Sets the scalar value for the specified lane index and component. + + Parameters: + name: Name of the component ('re' or 'im'). + + Args: + idx: Lane index to set. + val: Scalar value to assign to the specified component. - Arguments: - self: The ComplexSIMD instance to modify. - idx: The index to access (0 for real, 1 for imaginary). - value: The new value to set. + Raises: + - Error if the component name is invalid. + - Error if lane index is out of range for the SIMD width. + + Example: + ```mojo + from numojo.prelude import * + var c_simd = nm.ComplexSIMD[cf32, 2](SIMD[f32, 2](1, 2), SIMD[f32, 2](3, 4)) # [(1 + 3j), (2 + 4j)] + c_simd.itemset["re"](0, 5.0) # Now first complex number is (5 + 3j) + c_simd.itemset["im"](1, 6.0) # Now second complex number is (2 + 6j) + ``` """ - if idx == 0: - self.re = value.re - elif idx == 1: - self.im = value.im + if idx < 0 or idx >= Self.width: + raise Error("Lane index out of range for SIMD width") + + @parameter + if name == "re": + self.re[idx] = val + elif name == "im": + self.im[idx] = val else: - raise Error("Index out of range") + raise Error("Invalid component name: {}".format(name)) - fn item(self, idx: Int) raises -> SIMD[Self.dtype, Self.width]: + fn real(self) -> SIMD[Self.dtype, Self.width]: """ - Gets the real or imaginary part of the ComplexSIMD instance. + Returns the real part(s) of the complex number(s). + + Returns: + SIMD vector containing the real components. """ - return self[idx] + return self.re - fn itemset(mut self, val: ComplexSIMD[cdtype, Self.width]): + fn imag(self) -> SIMD[Self.dtype, Self.width]: """ - Sets the real and imaginary parts of the ComplexSIMD instance. + Returns the imaginary part(s) of the complex number(s). - Arguments: - self: The ComplexSIMD instance to modify. - val: The new value for the real and imaginary parts. + Returns: + SIMD vector containing the imaginary components. """ - self.re = val.re - self.im = val.im + return self.im + # --- Magnitude / norm / conjugate --- fn __abs__(self) -> SIMD[Self.dtype, Self.width]: """ - Returns the magnitude of the ComplexSIMD instance. + Returns the magnitude (absolute value) of the complex number(s). + + Returns: + SIMD vector containing the magnitude for each lane: sqrt(re^2 + im^2). """ return sqrt(self.re * self.re + self.im * self.im) fn norm(self) -> SIMD[Self.dtype, Self.width]: """ - Returns the squared magnitude of the ComplexSIMD instance. + Returns the squared magnitude (norm) of the complex number(s). + + Returns: + SIMD vector containing the squared magnitude for each lane: re^2 + im^2. """ - return sqrt(self.re * self.re + self.im * self.im) + return self.re * self.re + self.im * self.im fn conj(self) -> Self: """ Returns the complex conjugate of the ComplexSIMD instance. + + Returns: + ComplexSIMD instance with the imaginary part negated: (re, -im). """ return Self(self.re, -self.im) - fn real(self) -> SIMD[Self.dtype, Self.width]: + +@register_passable("trivial") +struct ImaginaryUnit(Boolable, Stringable, Writable): + """ + Constant representing the imaginary unit complex number 0 + 1j. + + The ImaginaryUnit struct provides a convenient way to work with the imaginary unit + in complex arithmetic operations. It supports arithmetic operations with SIMD vectors, + scalars, and other complex numbers, enabling Python-like syntax for complex number creation. + + Examples: + ```mojo + from numojo.prelude import * + + # Create complex numbers using the imaginary unit + var z1 = 3 + 4 * `1j` # 3 + 4j + var z2 = `1j` * 2 # 0 + 2j + var z3 = 5.0 + `1j` # 5 + 1j + + # Powers of the imaginary unit + print(`1j` ** 0) # 1 + 0j + print(`1j` ** 1) # 0 + 1j + print(`1j` ** 2) # -1 + 0j + print(`1j` ** 3) # 0 - 1j + + # SIMD complex vectors + var c4 = SIMD[f32, 4](1.0) + `1j` * SIMD[f32, 4](2.0) # ComplexSIMD[cf32, 4] + var c5 = SIMD[f64, 2](3.0, 4.0) + `1j` # ComplexSIMD[cf64, 2] + var d = SIMD[f32, 2](1) + SIMD[f32, 2](2) * `1j` # creates [( 1 + 2 j) (1 + 2 j)] + + # Mathematical properties + var c6 = `1j` * `1j` # -1 (Scalar[f64]) + var c7 = `1j` ** 3 # (0 - 1j) (ComplexScalar[cf64]) + var c8 = (1 + `1j`) / `1j` # (1 - 1j) (ComplexScalar[cf64]) + ``` + """ + + fn __init__(out self): """ - Returns the real part of the ComplexSIMD instance. + Constructor for ImaginaryUnit. + + Creates an instance representing the imaginary unit 1j. """ - return self.re + pass - fn imag(self) -> SIMD[Self.dtype, Self.width]: + fn conj(self) -> ComplexSIMD[cf64, 1]: + """ + Returns the complex conjugate of the imaginary unit. + + Returns: + ComplexSIMD representing -1j (the conjugate of 1j). + + Examples: + ```mojo + from numojo.prelude import * + + var conj_i = `1j`.conj() # -1j + print(conj_i) # (0 - 1j) + ``` """ - Returns the imaginary part of the ComplexSIMD instance. + return -self + + # --- Arithmetic operators with SIMD and Scalar types --- + # Addition: 1j + SIMD -> ComplexSIMD + fn __add__[ + dtype: DType, width: Int + ](self, other: SIMD[dtype, width]) -> ComplexSIMD[ + ComplexDType(mlir_value=dtype._mlir_value), width + ]: """ - return self.im + Returns the sum of the imaginary unit 1j and a SIMD vector. + + Args: + other: SIMD vector to add to the imaginary unit. + + Returns: + ComplexSIMD where real part equals the SIMD vector and imaginary part is 1. + + Examples: + ```mojo + from numojo.prelude import * + + var vec = SIMD[DType.float32, 4](1.0, 2.0, 3.0, 4.0) + var result = `1j` + vec # [1+1j, 2+1j, 3+1j, 4+1j] + ``` + """ + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), width]( + other, SIMD[dtype, width](1) + ) + + # Addition: 1j + Scalar -> ComplexScalar + fn __add__[ + dtype: DType + ](self, other: Scalar[dtype]) -> ComplexScalar[ + ComplexDType(mlir_value=dtype._mlir_value) + ]: + """ + Returns the sum of the imaginary unit 1j and a scalar. + + Args: + other: Scalar to add to the imaginary unit. + + Returns: + ComplexScalar with real part equal to the scalar and imaginary part 1. + + Examples: + ```mojo + from numojo.prelude import * + + var result = `1j` + 3.5 # 3.5 + 1j + ``` + """ + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), 1]( + other, 1 + ) + + fn __add__(self, other: Int) -> ComplexScalar[ComplexDType.int]: + """ + Returns the sum of the imaginary unit 1j and an integer. + + Args: + other: Integer to add to the imaginary unit. + + Returns: + ComplexScalar with real part equal to the integer and imaginary part 1. + + Examples: + ```mojo + from numojo.prelude import * + + var result = `1j` + 5 # 5 + 1j + ``` + """ + return ComplexSIMD[ComplexDType.int, 1](other, 1) + + # SIMD + 1j -> ComplexSIMD + fn __radd__[ + dtype: DType, width: Int + ](self, other: SIMD[dtype, width]) -> ComplexSIMD[ + ComplexDType(mlir_value=dtype._mlir_value), width + ]: + """Returns the sum of a SIMD vector and the imaginary unit.""" + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), width]( + other, SIMD[dtype, width](1) + ) + + # Addition: Scalar + 1j -> ComplexScalar + fn __radd__[ + dtype: DType + ](self, other: Scalar[dtype]) -> ComplexScalar[ + ComplexDType(mlir_value=dtype._mlir_value) + ]: + """Returns the sum of a scalar and the imaginary unit.""" + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), 1]( + other, 1 + ) + + # Addition: Int + 1j -> ComplexScalar + fn __radd__(self, other: Int) -> ComplexScalar[ComplexDType.int]: + """Returns the sum of an integer and the imaginary unit.""" + return ComplexSIMD[ComplexDType.int, 1](other, 1) + + # Subtraction: 1j - SIMD -> ComplexSIMD + fn __sub__[ + dtype: DType, width: Int + ](self, other: SIMD[dtype, width]) -> ComplexSIMD[ + ComplexDType(mlir_value=dtype._mlir_value), width + ]: + """Returns the difference of the imaginary unit and a SIMD vector.""" + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), width]( + -other, SIMD[dtype, width](1) + ) + + # Subtraction: 1j - Scalar -> ComplexScalar + fn __sub__[ + dtype: DType + ](self, other: Scalar[dtype]) -> ComplexScalar[ + ComplexDType(mlir_value=dtype._mlir_value) + ]: + """Returns the difference of the imaginary unit and a scalar.""" + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), 1]( + -other, 1 + ) + + fn __sub__(self, other: Int) -> ComplexScalar[ComplexDType.int]: + """Returns the difference of the imaginary unit and an integer.""" + return ComplexSIMD[ComplexDType.int, 1](-other, 1) + + # Subtraction: SIMD - 1j -> ComplexSIMD + fn __rsub__[ + dtype: DType, width: Int + ](self, other: SIMD[dtype, width]) -> ComplexSIMD[ + ComplexDType(mlir_value=dtype._mlir_value), width + ]: + """Returns the difference of a SIMD vector and the imaginary unit.""" + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), width]( + other, SIMD[dtype, width](-1) + ) + + # Subtraction: Scalar - 1j -> ComplexScalar + fn __rsub__[ + dtype: DType + ](self, other: Scalar[dtype]) -> ComplexScalar[ + ComplexDType(mlir_value=dtype._mlir_value) + ]: + """Returns the difference of a scalar and the imaginary unit.""" + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), 1]( + other, -1 + ) + + # Subtraction: Int - 1j -> ComplexScalar + fn __rsub__(self, other: Int) -> ComplexScalar[ComplexDType.int]: + """Returns the difference of an integer and the imaginary unit.""" + return ComplexSIMD[ComplexDType.int, 1](other, -1) + + # Multiplication: 1j * SIMD -> ComplexSIMD + fn __mul__[ + dtype: DType, width: Int + ](self, other: SIMD[dtype, width]) -> ComplexSIMD[ + ComplexDType(mlir_value=dtype._mlir_value), width + ]: + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), width]( + SIMD[dtype, width](0), other + ) + + # Multiplication: 1j * Scalar -> ComplexScalar + fn __mul__[ + dtype: DType + ](self, other: Scalar[dtype]) -> ComplexScalar[ + ComplexDType(mlir_value=dtype._mlir_value) + ]: + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), 1]( + 0, other + ) + + # Multiplication: 1j * Int -> ComplexScalar + fn __mul__(self, other: Int) -> ComplexScalar[ComplexDType.int]: + return ComplexSIMD[ComplexDType.int, 1](0, other) + + fn __rmul__[ + dtype: DType, + width: Int, + ](self, other: SIMD[dtype, width]) -> ComplexSIMD[ + ComplexDType(mlir_value=dtype._mlir_value), width + ]: + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), width]( + SIMD[dtype, width](0), other + ) + + fn __rmul__[ + dtype: DType + ](self, other: Scalar[dtype]) -> ComplexScalar[ + ComplexDType(mlir_value=dtype._mlir_value) + ]: + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), 1]( + 0, other + ) + + # Multiplication: Scalar * 1j -> ComplexScalar + fn __rmul__(self, other: Int) -> ComplexScalar[ComplexDType.int]: + return ComplexSIMD[ComplexDType.int, 1](0, other) + + # Division: 1j / SIMD -> ComplexSIMD + fn __truediv__[ + dtype: DType, width: Int + ](self, other: SIMD[dtype, width]) -> ComplexSIMD[ + ComplexDType(mlir_value=dtype._mlir_value), width + ]: + """Returns the division of the imaginary unit by a SIMD vector.""" + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), width]( + SIMD[dtype, width](0), 1 / other + ) + + # Division: 1j / Scalar -> ComplexScalar + fn __truediv__[ + dtype: DType + ](self, other: Scalar[dtype]) -> ComplexScalar[ + ComplexDType(mlir_value=dtype._mlir_value) + ]: + """Returns the division of the imaginary unit by a scalar.""" + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), 1]( + 0, 1 / other + ) + + # Division: SIMD / 1j -> ComplexSIMD + fn __rtruediv__[ + dtype: DType, width: Int + ](self, other: SIMD[dtype, width]) -> ComplexSIMD[ + ComplexDType(mlir_value=dtype._mlir_value), width + ]: + """ + Returns the division of a SIMD vector by the imaginary unit. + + Args: + other: SIMD vector to be divided by the imaginary unit. + + Returns: + ComplexSIMD representing (0 - other j). + """ + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), width]( + SIMD[dtype, width](0), -other + ) + + # Division: Scalar / 1j -> ComplexScalar + fn __rtruediv__[ + dtype: DType + ](self, other: Scalar[dtype]) -> ComplexScalar[ + ComplexDType(mlir_value=dtype._mlir_value) + ]: + """ + Returns the division of a scalar by the imaginary unit. + + Args: + other: Scalar to be divided by the imaginary unit. + + Returns: + ComplexScalar representing (0 - other j). + """ + return ComplexSIMD[ComplexDType(mlir_value=dtype._mlir_value), 1]( + 0, -other + ) + + # Division: Int / 1j -> ComplexScalar + fn __rtruediv__(self, other: Int) -> ComplexScalar[ComplexDType.int]: + """ + Returns the division of an integer by the imaginary unit. + + Args: + other: Integer to be divided by the imaginary unit. + + Returns: + ComplexScalar representing (0 - other j). + """ + return ComplexSIMD[ComplexDType.int, 1](0, -other) + + # Self-operations: 1j with 1j + fn __mul__(self, other: ImaginaryUnit) -> Scalar[DType.float64]: + """ + Returns the product of the imaginary unit with itself: 1j * 1j = -1. + + Args: + other: Another imaginary unit to multiply with. + + Returns: + Scalar value -1, since (1j) * (1j) = -1. + + Examples: + ```mojo + from numojo.prelude import * + + var result = `1j` * `1j` # -1 + print(result) # -1 + ``` + """ + return -1 + + fn __add__(self, other: ImaginaryUnit) -> ComplexScalar[cf64]: + """ + Returns the sum of the imaginary unit with itself: 1j + 1j = 2j. + + Args: + other: Another imaginary unit to add. + + Returns: + ComplexScalar representing 2j. + + Examples: + ```mojo + from numojo.prelude import * + + var result = `1j` + `1j` # 2j + print(result) # (0 + 2j) + ``` + """ + return ComplexSIMD[cf64, 1](0, 2) + + fn __sub__(self, other: ImaginaryUnit) -> Scalar[DType.float64]: + """ + Returns the difference of the imaginary unit with itself: 1j - 1j = 0. + + Args: + other: Another imaginary unit to subtract. + + Returns: + Scalar value 0. + + Examples: + ```mojo + from numojo.prelude import * + + var result = `1j` - `1j` # 0 + print(result) # 0 + ``` + """ + return 0 + + fn __truediv__(self, other: ImaginaryUnit) -> Scalar[DType.float64]: + """ + Returns the division of the imaginary unit by itself: 1j / 1j = 1. + + Args: + other: Another imaginary unit to divide by. + + Returns: + Scalar value 1. + + Examples: + ```mojo + from numojo.prelude import * + + var result = `1j` / `1j` # 1 + print(result) # 1 + ``` + """ + return 1 + + fn __pow__(self, exponent: Int) -> ComplexScalar[cf64]: + """ + Returns the imaginary unit raised to an integer power. + + The powers of 1j cycle with period 4: + - 1j^0 = 1 + - 1j^1 = 1j + - 1j^2 = -1 + - 1j^3 = -1j + - 1j^4 = 1 (cycle repeats) + + Args: + exponent: Integer exponent. + + Returns: + ComplexScalar representing (1j) ** exponent. + + Examples: + ```mojo + from numojo.prelude import * + + print(`1j` ** 0) # (1 + 0j) + print(`1j` ** 1) # (0 + 1j) + print(`1j` ** 2) # (-1 + 0j) + print(`1j` ** 3) # (0 - 1j) + print(`1j` ** 4) # (1 + 0j) + ``` + """ + var remainder = exponent % 4 + if remainder == 0: + return ComplexSIMD[cf64, 1](1, 0) + elif remainder == 1: + return ComplexSIMD[cf64, 1](0, 1) + elif remainder == 2: + return ComplexSIMD[cf64, 1](-1, 0) + else: + return ComplexSIMD[cf64, 1](0, -1) + + fn __neg__(self) -> ComplexScalar[cf64]: + """ + Returns the negation of the imaginary unit: -1j. + + Returns: + ComplexScalar representing -1j. + + Examples: + ```mojo + from numojo.prelude import * + + var result = -`1j` # -1j + print(result) # (0 - 1j) + ``` + """ + return ComplexSIMD[cf64, 1](0, -1) + + fn __str__(self) -> String: + """ + Returns the string representation of the imaginary unit. + + Returns: + String representing the imaginary unit as "(0 + 1 j)". + """ + return "(0 + 1 j)" + + fn write_to[W: Writer](self, mut writer: W): + """ + Writes the string representation of the imaginary unit to a writer. + + Args: + writer: Writer instance to write the string representation to. + """ + writer.write("(0 + 1 j)") + + fn __bool__(self) -> Bool: + """ + Returns the boolean value of the imaginary unit. + + Returns: + Always True, since the imaginary unit 1j is non-zero. + + Examples: + ```mojo + from numojo.prelude import * + + if `1j`: + print("Imaginary unit is truthy") # This will execute + ``` + """ + return True diff --git a/numojo/core/data_container.mojo b/numojo/core/data_container.mojo new file mode 100644 index 00000000..3f96a6cf --- /dev/null +++ b/numojo/core/data_container.mojo @@ -0,0 +1,157 @@ +# ===----------------------------------------------------------------------=== # +# Define `DataContainer` type +# +# TODO: fields in traits are not supported yet by Mojo +# Currently use `get_ptr()` to get pointer, in future, use `ptr` directly. +# var ptr: LegacyUnsafePointer[Scalar[dtype]] +# ===----------------------------------------------------------------------=== + +from memory import UnsafePointer, LegacyUnsafePointer + + +# temporary DataContainer to support transition from LegacyUnsafePointer to UnsafePointer. +struct DataContainerNew[dtype: DType, origin: MutOrigin](ImplicitlyCopyable): + var ptr: UnsafePointer[Scalar[dtype], origin] + + fn __init__(out self, size: Int): + """ + Allocate given space on memory. + The bytes allocated is `size` * `byte size of dtype`. + + Notes: + `ndarray.flags['OWN_DATA']` should be set as True. + The memory should be freed by `__del__`. + """ + self.ptr: UnsafePointer[Scalar[dtype], origin] = alloc[Scalar[dtype]]( + size + ).unsafe_origin_cast[origin]() + + fn __init__(out self, ptr: UnsafePointer[Scalar[dtype], origin]): + """ + Do not use this if you know what it means. + If the pointer is associated with another array, it might cause + dangling pointer problem. + + Notes: + `ndarray.flags['OWN_DATA']` should be set as False. + The memory should not be freed by `__del__`. + """ + self.ptr = ptr + + fn __moveinit__(out self, deinit other: Self): + """ + Move-initializes this DataContainerNew from another instance. + + Transfers ownership of the pointer from `other` to `self`. + After this operation, `other` should not be used. + """ + self.ptr = other.ptr + + fn get_ptr( + self, + ) -> ref [origin_of(self.ptr)] UnsafePointer[Scalar[dtype], origin]: + """ + Returns the internal pointer to the data buffer. + + Returns: + UnsafePointer[Scalar[dtype], origin]: The pointer to the underlying data. + """ + return self.ptr + + fn __str__(self) -> String: + """ + Returns a string representation of the DataContainerNew. + + Returns: + String: A string describing the container and its pointer. + """ + return "DatContainer with ptr: " + String(self.ptr) + + fn __getitem__(self, idx: Int) -> Scalar[dtype]: + """ + Gets the value at the specified index in the data buffer. + + Args: + idx: Index of the element to retrieve. + + Returns: + Scalar[dtype]: The value at the given index. + """ + return self.ptr[idx] + + fn __setitem__(mut self, idx: Int, val: Scalar[dtype]): + """ + Sets the value at the specified index in the data buffer. + + Args: + idx: Index of the element to set. + val: Value to assign. + """ + self.ptr[idx] = val + + fn offset(self, offset: Int) -> UnsafePointer[Scalar[dtype], origin]: + """ + Returns a pointer offset by the given number of elements. + + Args: + offset: Number of elements to offset the pointer. + + Returns: + UnsafePointer[Scalar[dtype], origin]: The offset pointer. + """ + return self.ptr.offset(offset) + + fn load[width: Int](self, offset: Int) -> SIMD[dtype, width]: + """ + Loads a value from the data buffer at the specified offset. + + Args: + offset: Offset from the start of the buffer. + + Returns: + Scalar[dtype]: The loaded value. + """ + return self.ptr.load[width=width](offset) + + fn store[width: Int](mut self, offset: Int, value: SIMD[dtype, width]): + """ + Stores a value into the data buffer at the specified offset. + + Args: + offset: Offset from the start of the buffer. + value: Value to store. + """ + self.ptr.store[width=width](offset, value) + + +struct DataContainer[dtype: DType](ImplicitlyCopyable): + var ptr: LegacyUnsafePointer[Scalar[dtype]] + + fn __init__(out self, size: Int): + """ + Allocate given space on memory. + The bytes allocated is `size` * `byte size of dtype`. + + Notes: + `ndarray.flags['OWN_DATA']` should be set as True. + The memory should be freed by `__del__`. + """ + self.ptr = LegacyUnsafePointer[Scalar[dtype]]().alloc(size) + + fn __init__(out self, ptr: LegacyUnsafePointer[Scalar[dtype]]): + """ + Do not use this if you know what it means. + If the pointer is associated with another array, it might cause + dangling pointer problem. + + Notes: + `ndarray.flags['OWN_DATA']` should be set as False. + The memory should not be freed by `__del__`. + """ + self.ptr = ptr + + fn __moveinit__(out self, deinit other: Self): + self.ptr = other.ptr + + fn get_ptr(self) -> LegacyUnsafePointer[Scalar[dtype]]: + return self.ptr diff --git a/numojo/core/item.mojo b/numojo/core/item.mojo index 9d3d8f9a..0ab2c5bd 100644 --- a/numojo/core/item.mojo +++ b/numojo/core/item.mojo @@ -5,8 +5,9 @@ Implements Item type. """ from builtin.type_aliases import Origin -from builtin.int import index as index_int -from memory import UnsafePointer, memset_zero, memcpy +from builtin.int import index as convert_to_int +from memory import memcpy, memset_zero +from memory import UnsafePointer from memory import memcmp from os import abort from sys import simd_width_of @@ -17,23 +18,39 @@ from numojo.core.traits.indexer_collection_element import ( IndexerCollectionElement, ) -# simple alias for users. Use `Item` internally. -alias item = Item - @register_passable struct Item( ImplicitlyCopyable, Movable, Representable, Sized, Stringable, Writable ): """ - Specifies the indices of an item of an array. + Represents a multi-dimensional index for array access. + + The `Item` struct is used to specify the coordinates of an element within an N-dimensional array. + For example, `arr[Item(1, 2, 3)]` retrieves the element at position (1, 2, 3) in a 3D array. + + Each `Item` instance holds a sequence of integer indices, one for each dimension of the array. + This allows for precise and flexible indexing into arrays of arbitrary dimensionality. + + Example: + ```mojo + from numojo.prelude import * + import numojo as nm + var arr = nm.arange[f32](0, 27).reshape(Shape(3, 3, 3)) + var value = arr[Item(1, 2, 3)] # Accesses arr[1, 2, 3] + ``` + + Fields: + _buf: Pointer to the buffer storing the indices. + ndim: Number of dimensions (length of the index tuple). """ # Aliases alias _type: DType = DType.int + alias _origin: MutOrigin = MutOrigin.external # Fields - var _buf: UnsafePointer[Scalar[Self._type]] + var _buf: UnsafePointer[Scalar[Self._type], Self._origin] var ndim: Int @always_inline("nodebug") @@ -46,10 +63,10 @@ struct Item( Args: args: Initial values. """ - self._buf = UnsafePointer[Scalar[Self._type]]().alloc(args.__len__()) + self._buf = alloc[Scalar[Self._type]](len(args)) self.ndim = args.__len__() for i in range(args.__len__()): - self._buf[i] = index(args[i]) + (self._buf + i).init_pointee_copy(convert_to_int(args[i])) @always_inline("nodebug") fn __init__[T: IndexerCollectionElement](out self, args: List[T]) raises: @@ -62,9 +79,9 @@ struct Item( args: Initial values. """ self.ndim = len(args) - self._buf = UnsafePointer[Scalar[Self._type]]().alloc(self.ndim) + self._buf = alloc[Scalar[Self._type]](self.ndim) for i in range(self.ndim): - (self._buf + i).init_pointee_copy(index(args[i])) + (self._buf + i).init_pointee_copy(convert_to_int(args[i])) @always_inline("nodebug") fn __init__(out self, args: VariadicList[Int]) raises: @@ -74,53 +91,20 @@ struct Item( args: Initial values. """ self.ndim = len(args) - self._buf = UnsafePointer[Scalar[Self._type]]().alloc(self.ndim) + self._buf = alloc[Scalar[Self._type]](len(args)) for i in range(self.ndim): - (self._buf + i).init_pointee_copy(Int(args[i])) + (self._buf + i).init_pointee_copy(args[i]) - fn __init__( - out self, - *, - ndim: Int, - initialized: Bool, - ) raises: - """ - Construct Item with number of dimensions. - This method is useful when you want to create a Item with given ndim - without knowing the Item values. + @always_inline("nodebug") + fn __init__(out self, ndim: Int): + """Construct the Item with given length and initialize to zero. Args: - ndim: Number of dimensions. - initialized: Whether the shape is initialized. - If yes, the values will be set to 0. - If no, the values will be uninitialized. - - Raises: - Error: If the number of dimensions is negative. + ndim: The length of the tuple. """ - if ndim < 0: - raise Error( - IndexError( - message=String( - "Invalid ndim: got {}; must be >= 0." - ).format(ndim), - suggestion=String( - "Pass a non-negative dimension count when constructing" - " Item." - ), - location=String("Item.__init__(ndim: Int)"), - ) - ) - - if ndim == 0: - self.ndim = 0 - self._buf = UnsafePointer[Scalar[Self._type]]() - else: - self.ndim = ndim - self._buf = UnsafePointer[Scalar[Self._type]]().alloc(ndim) - if initialized: - for i in range(ndim): - (self._buf + i).init_pointee_copy(0) + self.ndim = ndim + self._buf = alloc[Scalar[Self._type]](ndim) + memset_zero(self._buf, ndim) @always_inline("nodebug") fn __copyinit__(out self, other: Self): @@ -130,8 +114,8 @@ struct Item( other: The tuple to copy. """ self.ndim = other.ndim - self._buf = UnsafePointer[Scalar[Self._type]]().alloc(self.ndim) - memcpy(self._buf, other._buf, self.ndim) + self._buf = alloc[Scalar[Self._type]](self.ndim) + memcpy(dest=self._buf, src=other._buf, count=self.ndim) @always_inline("nodebug") fn __del__(deinit self): @@ -157,10 +141,10 @@ struct Item( Returns: The normalized index. """ - var normalized_idx: Int = index - if normalized_idx < 0: - normalized_idx += self.ndim - return normalized_idx + var norm_idx: Int = index + if norm_idx < 0: + norm_idx += self.ndim + return norm_idx @always_inline("nodebug") fn __getitem__[T: Indexer](self, idx: T) raises -> Int: @@ -175,12 +159,12 @@ struct Item( Returns: The value at the specified index. """ - var index: Int = index_int(idx) + var index: Int = convert_to_int(idx) if index >= self.ndim or index < -self.ndim: raise Error( IndexError( message=String("Index {} out of range [{} , {}).").format( - index_int(idx), -self.ndim, self.ndim + convert_to_int(idx), -self.ndim, self.ndim ), suggestion=String( "Use indices in [-ndim, ndim) (negative indices wrap)." @@ -188,7 +172,7 @@ struct Item( location=String("Item.__getitem__"), ) ) - var normalized_idx: Int = self.normalize_index(index_int(idx)) + var normalized_idx: Int = self.normalize_index(convert_to_int(idx)) return Int(self._buf[normalized_idx]) @always_inline("nodebug") @@ -219,11 +203,18 @@ struct Item( var length = updated_slice[2] if length <= 0: - var empty_result = Self(ndim=0, initialized=False) - return empty_result + raise Error( + ShapeError( + message="Provided slice results in an empty Item.", + suggestion=( + "Adjust slice parameters to obtain non-empty result." + ), + location="Item.__getitem__(self, slice_list: Slice)", + ) + ) - var result = Self(ndim=length, initialized=False) - var idx = start + var result: Item = Self(ndim=length) + var idx: Int = start for i in range(length): (result._buf + i).init_pointee_copy(self._buf[idx]) idx += step @@ -241,16 +232,12 @@ struct Item( idx: The index of the value to set. val: The value to set. """ - - var normalized_idx: Int = index_int(idx) - if normalized_idx < 0: - normalized_idx = index_int(idx) + self.ndim - - if normalized_idx < 0 or normalized_idx >= self.ndim: + var norm_idx: Int = self.normalize_index(convert_to_int(idx)) + if norm_idx < 0 or norm_idx >= self.ndim: raise Error( IndexError( message=String("Index {} out of range [{} , {}).").format( - index_int(idx), -self.ndim, self.ndim + convert_to_int(idx), -self.ndim, self.ndim ), suggestion=String( "Use indices in [-ndim, ndim) (negative indices wrap)." @@ -259,7 +246,7 @@ struct Item( ) ) - self._buf[normalized_idx] = index(val) + self._buf[norm_idx] = index(val) fn __iter__(self) raises -> _ItemIter: """Iterate over elements of the NDArray, returning copied value. @@ -277,7 +264,7 @@ struct Item( ) fn __repr__(self) -> String: - var result: String = "numojo.Item" + String(self) + var result: String = "Item" + String(self) return result fn __str__(self) -> String: @@ -285,18 +272,12 @@ struct Item( for i in range(self.ndim): result += String(self._buf[i]) if i < self.ndim - 1: - result += "," + result += ", " result += ")" return result fn write_to[W: Writer](self, mut writer: W): - writer.write( - "Item at index: " - + String(self) - + " " - + "Length: " - + String(self.ndim) - ) + writer.write("Coordinates: " + String(self) + " ") @always_inline("nodebug") fn __eq__(self, other: Self) -> Bool: @@ -347,17 +328,16 @@ struct Item( # ===-------------------------------------------------------------------===# # Other methods # ===-------------------------------------------------------------------===# - @always_inline("nodebug") - fn copy(read self) raises -> Self: + fn deep_copy(read self) raises -> Self: """ Returns a deep copy of the item. Returns: A new Item with the same values. """ - var res = Self(ndim=self.ndim, initialized=False) - memcpy(res._buf, self._buf, self.ndim) + var res: Item = Item(ndim=self.ndim) + memcpy(dest=res._buf, src=self._buf, count=self.ndim) return res^ fn swapaxes(self, axis1: Int, axis2: Int) raises -> Self: @@ -371,7 +351,7 @@ struct Item( Returns: A new item with the given axes swapped. """ - var res: Self = Self(ndim=self.ndim, initialized=False) + var res: Item = Item(ndim=self.ndim) memcpy(dest=res._buf, src=self._buf, count=self.ndim) res[axis1] = self[axis2] res[axis2] = self[axis1] @@ -401,7 +381,7 @@ struct Item( for i in range(len(others)): total_dims += others[i].ndim - var new_item: Self = Self(ndim=total_dims, initialized=False) + var new_item: Item = Item(ndim=total_dims) var index: UInt = 0 for i in range(self.ndim): @@ -435,11 +415,11 @@ struct Item( print(item._flip()) # Item: (3, 2, 1) ``` """ - var result: Self = Self(ndim=self.ndim, initialized=False) - memcpy(dest=result._buf, src=self._buf, count=self.ndim) - for i in range(result.ndim): - result._buf[i] = self._buf[self.ndim - 1 - i] - return result^ + var res: Item = Item(ndim=self.ndim) + memcpy(dest=res._buf, src=self._buf, count=self.ndim) + for i in range(res.ndim): + res._buf[i] = self._buf[self.ndim - 1 - i] + return res^ fn _move_axis_to_end(self, var axis: Int) raises -> Self: """ @@ -463,17 +443,17 @@ struct Item( if axis < 0: axis += self.ndim - var result: Self = Self(ndim=self.ndim, initialized=False) - memcpy(dest=result._buf, src=self._buf, count=self.ndim) + var res: Item = Item(ndim=self.ndim) + memcpy(dest=res._buf, src=self._buf, count=self.ndim) if axis == self.ndim - 1: - return result^ + return res^ - var value: Scalar[Self._type] = result._buf[axis] - for i in range(axis, result.ndim - 1): - result._buf[i] = result._buf[i + 1] - result._buf[result.ndim - 1] = value - return result^ + var value: Scalar[Self._type] = res._buf[axis] + for i in range(axis, res.ndim - 1): + res._buf[i] = res._buf[i + 1] + res._buf[res.ndim - 1] = value + return res^ fn _pop(self, axis: Int) raises -> Self: """ @@ -486,7 +466,7 @@ struct Item( Returns: A new item with the item at the given axis (index) dropped. """ - var res: Self = Self(ndim=self.ndim - 1, initialized=False) + var res: Item = Item(ndim=self.ndim - 1) memcpy(dest=res._buf, src=self._buf, count=axis) memcpy( dest=res._buf + axis, @@ -515,7 +495,7 @@ struct Item( ``` """ var total_dims: Int = self.ndim + len(values) - var new_item: Self = Self(ndim=total_dims, initialized=False) + var new_item: Item = Item(ndim=total_dims) var offset: UInt = 0 for i in range(self.ndim): @@ -529,7 +509,7 @@ struct Item( fn _compute_slice_params( self, slice_index: Slice - ) raises -> (Int, Int, Int): + ) raises -> Tuple[Int, Int, Int]: """ Compute normalized slice parameters (start, step, length). @@ -711,7 +691,7 @@ struct _ItemIter[ item: Item, length: Int, ): - self.index = 0 if forward else length + self.index = 0 if forward else length - 1 self.length = length self.item = item @@ -723,9 +703,9 @@ struct _ItemIter[ if forward: return self.index < self.length else: - return self.index > 0 + return self.index >= 0 - fn __next__(mut self) raises -> Scalar[DType.index]: + fn __next__(mut self) raises -> Scalar[DType.int]: @parameter if forward: var current_index = self.index diff --git a/numojo/core/matrix.mojo b/numojo/core/matrix.mojo index 93fbf3a5..41e1e6b5 100644 --- a/numojo/core/matrix.mojo +++ b/numojo/core/matrix.mojo @@ -1,10 +1,16 @@ """ -`numojo.Matrix` provides: +NuMojo Matrix Module -- `Matrix` type (2DArray). -- `_MatrixIter` type (for iteration). -- Dunder methods for initialization, indexing, slicing, and arithmetics. -- Auxiliary functions. +This file implements the core 2D matrix type for the NuMojo numerical computing library. It provides efficient, flexible, and memory-safe matrix operations for scientific and engineering applications. + +Features: +- `Matrix`: The primary 2D array type for owning matrix data. +- `MatrixView`: Lightweight, non-owning views for fast slicing and submatrix access. +- Iterators for traversing matrix elements. +- Comprehensive dunder methods for initialization, indexing, slicing, and arithmetic. +- Utility functions for broadcasting, memory layout, and linear algebra routines. + +Use this module to create, manipulate, and analyze matrices with high performance and safety guarantees. """ from algorithm import parallelize, vectorize @@ -12,41 +18,172 @@ from memory import UnsafePointer, memcpy, memset_zero from random import random_float64 from sys import simd_width_of from python import PythonObject, Python +from math import ceil from numojo.core.flags import Flags from numojo.core.ndarray import NDArray +from numojo.core.data_container import DataContainerNew as DataContainer +from numojo.core.traits.buffered import Buffered from numojo.core.own_data import OwnData +from numojo.core.ref_data import RefData from numojo.core.utility import _get_offset from numojo.routines.manipulation import broadcast_to, reorder_layout from numojo.routines.linalg.misc import issymmetric +# TODO: currently a lot of the __getitem__ and __setitem__ methods raises if the index is out of bounds. An alternative is to clamp the indices to be within bounds, this will remove a lot of if conditions and improve performance I guess. Need to decide which behavior is preferred. # ===----------------------------------------------------------------------===# # Matrix struct # ===----------------------------------------------------------------------===# -struct Matrix[dtype: DType = DType.float64]( - Copyable, Movable, Sized, Stringable, Writable -): - # TODO: Matrix[dtype: DType = DType.float64, - # Buffer: Bufferable[dtype] = OwnData[dtype]] - """ - `Matrix` is a special case of `NDArray` (2DArray) but has some targeted - optimization since the number of dimensions is known at the compile time. - It has simpler indexing and slicing methods, which is very useful when users - only want to work with 2-dimensional arrays. +alias Matrix = MatrixBase[_, own_data=True, origin = MutOrigin.external] +""" +Primary Matrix type for creating and manipulating 2D matrices in NuMojo. - NuMojo's `Matrix` is `NDArray` with fixed `ndim` known at compile time. - It may be different in some behaviors compared to `numpy.matrix`. +This is the main user-facing type alias for working with matrices. It represents +a matrix that owns and manages its underlying memory buffer. The data type parameter +is inferred from context or can be explicitly specified. - - For `__getitem__`, passing in two `Int` returns a scalar, - and passing in one `Int` or two `Slice` returns a `Matrix`. - - We do not need auxiliary types `NDArrayShape` and `NDArrayStrides` - as the shape and strides information is fixed in length `Tuple[Int,Int]`. +The `Matrix` type is designed for standard matrix operations where full ownership +and control of the data is required. It allocates its own memory and is responsible +for cleanup when it goes out of scope. - Parameters: - dtype: Type of item in NDArray. Default type is DType.float64. +Type Parameters: + dtype: The data type of matrix elements. + +Usage: + ```mojo + from numojo.prelude import * + + # Create a matrix with explicit type + var mat = Matrix.zeros[nm.f32](shape=Tuple(3, 4)) + + # Create with default type DType.float64 + var mat2 = Matrix.zeros(shape=Tuple(2, 3)) + ``` + +Notes: + - This matrix owns its data and manages memory allocation/deallocation. + - For non-owning views into existing data, use methods like `get()`, `view()` which return `MatrixView`. + - Direct instantiation of `MatrixBase` should be avoided; always use this alias. +""" + +alias MatrixView[dtype: DType, origin: MutOrigin] = MatrixBase[ + dtype, own_data=False, origin=origin +] +""" +Non-owning view into matrix data for efficient memory access without copying. + +`MatrixView` represents a lightweight reference to matrix data that is owned by +another `Matrix` instance. It does not allocate or manage its own memory, instead +pointing to a subset or reinterpretation of existing matrix data. This enables +efficient slicing, row/column access, and memory sharing without data duplication. + +**IMPORTANT**: This type is for internal use and should not be directly instantiated +by users. Views are created automatically by matrix operations like indexing, +slicing, through the `get()` method. A full view of the matrix can be obtained via `view()` method. + +Type Parameters: + dtype: The data type of the matrix elements being viewed. + origin: Tracks the lifetime and mutability of the referenced data, ensuring + the view doesn't outlive the original data or violate mutability constraints. + +Key Characteristics: + - Does not own the underlying data buffer. + - Cannot be copied (to prevent dangling references) (Will be relaxed in future). + - Lifetime is tied to the owning Matrix instance. + - May have different shape/strides than the original matrix (e.g., for slices). + - Changes to the view affect the original matrix by default. + +Common Creation Patterns: + Views are typically created through: + - `matrix.get(row_idx)` - Get a view of a single row + - `matrix.get(row_slice, col_slice)` - Get a view of a submatrix + - `matrix.view()` - Get a view of the entire matrix + +Example: + ```mojo + from numojo.prelude import * + + var mat = Matrix.ones(shape=(4, 4)) + var row_view = mat.get(0) # Returns MatrixView of first row + # Modifying row_view would modify mat + ``` + +Safety Notes: + - The view must not outlive the owning Matrix + - Origin tracking ensures compile-time lifetime safety + - Attempting to use a view after its owner is deallocated is undefined behavior +""" + + +struct MatrixBase[ + dtype: DType = DType.float64, + *, + own_data: Bool, + origin: MutOrigin, +](Copyable, Movable, Sized, Stringable, Writable): + """ + Core implementation struct for 2D matrix operations with flexible ownership semantics. + + `MatrixBase` is the underlying implementation for both owning matrices (`Matrix`) + and non-owning matrix views (`MatrixView`). It provides a complete set of operations + for 2D array manipulation with compile-time known dimensions, enabling optimizations + not possible with generic N-dimensional arrays. + + This struct represents a specialized case of `NDArray` optimized for 2D operations. + The fixed dimensionality allows for simpler, more efficient indexing using direct + `(row, col)` access patterns rather than generic coordinate tuples. This makes it + particularly suitable for linear algebra, image processing, and other applications + where 2D structure is fundamental. + + **Important**: Users should not instantiate `MatrixBase` directly. Instead, use: + - `Matrix[dtype]` for matrices that own their data (standard usage) + - Methods like `get()` that return `MatrixView` for non-owning views + + Direct instantiation of `MatrixBase` may lead to undefined behavior related to + memory management and lifetime tracking. + + Type Parameters: + dtype: The data type of matrix elements (e.g., DType.float32, DType.float64). + Default is DType.float32. This is a compile-time parameter that determines + the size and interpretation of stored values. + own_data: Boolean flag indicating whether this instance owns and manages its + underlying memory buffer. When True, the matrix allocates and frees + its own memory. When False, it's a view into externally-owned data. + origin: Tracks the lifetime and mutability of the underlying data buffer, + enabling compile-time safety checks to prevent use-after-free and + other memory safety issues. Default is MutOrigin.external. + + Memory Layout: + Matrices can be stored in either: + - Row-major (C-style) layout: consecutive elements in a row are adjacent in memory + - Column-major (Fortran-style) layout: consecutive elements in a column are adjacent + + The layout affects cache efficiency for different access patterns and is tracked + via the `strides` and `flags` attributes. + + Ownership Semantics: + **Owning matrices** (own_data=True): + - Allocate their own memory buffer during construction + - Responsible for freeing memory in destructor + - Can be copied (creates new independent matrix with copied data) + - Can be moved (transfers ownership efficiently) + + **View matrices** (own_data=False): + - Reference existing data from an owning matrix + - Do not allocate or free memory + - Cannot be copied currently. + + Indexing and Slicing: + - `mat[i, j]` - Returns scalar element at row i, column j + - `mat[i]` - Returns a copy of row i as a new Matrix + - `mat.get(i)` - Returns a MatrixView of row i (no copy) + - `mat[row_slice, col_slice]` - Returns a copy of the submatrix + - `mat.get(row_slice, col_slice)` - Returns a MatrixView of the submatrix (no copy) + + Negative indices are supported and follow Python conventions (wrap from end). The matrix can be uniquely defined by the following features: 1. The data buffer of all items. @@ -57,7 +194,7 @@ struct Matrix[dtype: DType = DType.float64]( - _buf (saved as row-majored, C-type) - shape - size (shape[0] * shape[1]) - - strides (shape[1], 1) + - strides Default constructor: - [dtype], shape @@ -89,11 +226,19 @@ struct Matrix[dtype: DType = DType.float64]( - [x] `Matrix.variance` and `mat.statistics.variance` (`var` is primitive) """ + comptime IteratorType[ + is_mutable: Bool, //, + matrix_origin: MutOrigin, + iterator_origin: Origin[is_mutable], + forward: Bool, + ] = _MatrixIter[dtype, matrix_origin, iterator_origin, forward] + """Iterator type for the Matrix.""" + alias width: Int = simd_width_of[dtype]() # """Vector size of the data type.""" - var _buf: OwnData[dtype] - """Data buffer of the items in the NDArray.""" + var _buf: DataContainer[dtype, origin] + """Data buffer of the items in the Matrix.""" var shape: Tuple[Int, Int] """Shape of Matrix.""" @@ -116,93 +261,240 @@ struct Matrix[dtype: DType = DType.float64]( out self, shape: Tuple[Int, Int], order: String = "C", - ): + ) where own_data == True: """ - Create a new matrix of the given shape,without initializing data. + Initialize a new matrix with the specified shape and memory layout. + + This constructor creates a matrix of the given shape without initializing + its data. The memory layout can be specified as either row-major ("C") or + column-major ("F"). Args: - shape: Tuple representing (rows, columns). - order: Use "C" for row-major (C-style) layout or "F" for column-major - (Fortran-style) layout. Defaults to "C". - """ + shape: A tuple representing the dimensions of the matrix as (rows, columns). + order: A string specifying the memory layout. Use "C" for row-major + (C-style) layout or "F" for column-major (Fortran-style) layout. Defaults to "C". + Example: + ```mojo + from numojo.prelude import * + var mat_c = Matrix[f32](shape=(3, 4), order="C") # Row-major + var mat_f = Matrix[f32](shape=(3, 4), order="F") # Column-major + ``` + """ self.shape = (shape[0], shape[1]) if order == "C": self.strides = (shape[1], 1) else: self.strides = (1, shape[0]) self.size = shape[0] * shape[1] - self._buf = OwnData[dtype](size=self.size) + self._buf = DataContainer[dtype, origin](size=self.size) self.flags = Flags( self.shape, self.strides, owndata=True, writeable=True ) - # * Should we take var ref and transfer ownership or take a read ref and copy it? + # * Should we take var ref and transfer ownership or take a read ref and copy the data? @always_inline("nodebug") fn __init__( out self, var data: Self, - ): - """ - Construct a matrix from matrix. + ) where own_data == True: """ + Initialize a new matrix by transferring ownership from another matrix. + This constructor creates a new matrix instance by taking ownership of the + data from an existing matrix. The source matrix (`data`) will no longer + own its data after this operation. + + Args: + data: The source matrix from which ownership of the data will be transferred. + + Notes: + - This operation is efficient as it avoids copying the data buffer. + - The source matrix (`data`) becomes invalid after the transfer and should not be used. + + Example: + ```mojo + from numojo.prelude import * + var mat1 = Matrix[f32](shape=(2, 3)) + # ... (initialize mat1 with data) ... + var mat2 = Matrix[f32](mat1^) # Transfer ownership from mat1 to mat2 + ``` + """ self = data^ @always_inline("nodebug") fn __init__( out self, - data: NDArray[dtype], - ) raises: + data: Self, + ) where own_data == True: """ - Construct a matrix from array. + Construct a new matrix by copying from another matrix. + + This initializer creates a new matrix instance by copying the data, shape and order from an existing matrix. The new matrix will have its own independent copy of the data. + + Args: + data: The source matrix to copy from. + """ + self = Self(data.shape, data.order()) + memcpy(dest=self._buf.ptr, src=data._buf.ptr, count=data.size) + + @always_inline("nodebug") + fn __init__( + out self, + data: NDArray[dtype], + ) raises where own_data == True: """ + Initialize a new matrix by copying data from an existing NDArray. + + This constructor creates a matrix instance with the same shape, data, and + memory layout as the provided NDArray. The data is copied into a new memory buffer owned by the matrix. + + Args: + data: An NDArray instance containing the data to initialize the matrix. + Raises: + Error: If the provided NDArray has more than 2 dimensions, as it cannot be represented as a matrix. + + Example: + ```mojo + from numojo.prelude import * + var arr = NDArray[f32](Shape(2, 3)) + # ... (initialize arr with data) ... + var mat = Matrix[f32](arr) # Create a matrix from the NDArray + ``` + """ if data.ndim == 1: self.shape = (1, data.shape[0]) self.strides = (data.shape[0], 1) self.size = data.shape[0] elif data.ndim == 2: self.shape = (data.shape[0], data.shape[1]) - self.strides = (data.shape[1], 1) + if data.flags["C_CONTIGUOUS"]: + self.strides = (data.shape[1], 1) + else: + self.strides = (1, data.shape[0]) self.size = data.shape[0] * data.shape[1] else: raise Error(String("Shape too large to be a matrix.")) - self._buf = OwnData[dtype](self.size) - + self._buf = DataContainer[dtype, origin](self.size) self.flags = Flags( self.shape, self.strides, owndata=True, writeable=True ) + memcpy( + dest=self._buf.ptr, + src=data._buf.ptr, + count=self.size, + ) - if data.flags["C_CONTIGUOUS"]: - for i in range(data.shape[0]): - memcpy( - self._buf.ptr.offset(i * self.shape[0]), - data._buf.ptr.offset(i * data.shape[0]), - self.shape[0], - ) - else: - for i in range(data.shape[0]): - for j in range(data.shape[1]): - self._store(i, j, data._getitem(i, j)) + # to construct views + @always_inline("nodebug") + fn __init__( + out self, + shape: Tuple[Int, Int], + strides: Tuple[Int, Int], + data: DataContainer[dtype, origin], + ) where own_data == False: + """ + Initialize Matrix that does not own the data. + The data is owned by another Matrix. + + Args: + shape: Shape of the view. + strides: Strides of the view. + data: DataContainer that holds the data buffer. + """ + self.shape = shape + self.strides = strides + self.size = shape[0] * shape[1] + self._buf = data + self.flags = Flags( + self.shape, self.strides, owndata=False, writeable=False + ) + # TODO: prevent copying from views to views or views to owning matrices right now.`where` clause isn't working here either for now, So we use constrained. Move to 'where` clause when it's stable. + # TODO: Current copyinit creates an instance with same origin. This should be external origin. fix this so that we can use default `.copy()` method and remove `create_copy()` method. @always_inline("nodebug") fn __copyinit__(out self, other: Self): """ - Copy other into self. - """ + Initialize a new matrix by copying data from another matrix. + + This method creates a deep copy of the `other` matrix into `self`. It ensures that the copied matrix is independent of the source matrix, with its own memory allocation. + + Constraints: + - Copying is only allowed between matrices that own their data. + Views cannot be copied to ensure memory safety. + + Args: + other: The source matrix to copy from. Must be an owning matrix. + + Notes: + - This method uses the `constrained` mechanism to enforce the restriction that both the source and destination matrices must own their data. + - The copied matrix will have the same shape, strides, and data as the source matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat1 = Matrix[f32](shape=(2, 3)) + # ... (initialize mat1 with data) ... + var mat2 = mat1.copy() # Calls __copyinit__ to create a copy of mat1 + ``` + """ + constrained[ + other.own_data == True and own_data == True, + ( + "`.copy()` is only allowed for Matrices that own the data and" + " not views." + ), + ]() self.shape = (other.shape[0], other.shape[1]) self.strides = (other.strides[0], other.strides[1]) self.size = other.size - self._buf = OwnData[dtype](other.size) - memcpy(self._buf.ptr, other._buf.ptr, other.size) - self.flags = other.flags + self._buf = DataContainer[dtype, origin](other.size) + memcpy(dest=self._buf.ptr, src=other._buf.ptr, count=other.size) + self.flags = Flags( + other.shape, other.strides, owndata=True, writeable=True + ) + + fn create_copy(self) -> Matrix[dtype]: + """ + Create a deep copy of the current matrix. + + This method creates a new `Matrix` instance with the same shape, data, and + memory layout as the original matrix. The data is copied into a new memory + buffer owned by the new matrix, ensuring that the original and the copy are completely independent. + + Returns: + A new `Matrix` instance that is an exact copy of the + current matrix, including its shape and data. + + Example: + ```mojo + from numojo.prelude import * + var mat1 = Matrix[f32](shape=(2, 3)) + # ... (initialize mat1 with data) ... + var mat2 = mat1.create_copy() # Create a deep copy of mat1 + ``` + """ + var new_matrix = Matrix[dtype](shape=self.shape, order=self.order()) + memcpy(dest=new_matrix._buf.ptr, src=self._buf.ptr, count=self.size) + return new_matrix^ @always_inline("nodebug") fn __moveinit__(out self, deinit other: Self): """ - Move other into self. + Transfer ownership of resources from `other` to `self`. + + This method moves the data and metadata from the `other` matrix instance + into the current instance (`self`). After the move, the `other` instance + is left in an invalid state and should not be used. + + Args: + other: The source matrix instance whose resources will be moved. + + Notes: + - This operation is efficient as it avoids copying data. + - The `other` instance is deinitialized as part of this operation. """ self.shape = other.shape^ self.strides = other.strides^ @@ -212,78 +504,273 @@ struct Matrix[dtype: DType = DType.float64]( @always_inline("nodebug") fn __del__(deinit self): - var owndata: Bool - try: - owndata = self.flags["OWNDATA"] - except: - owndata = True - print("Invalid `OWNDATA` flag. Treat as `True`.") - if owndata: + """ + Destructor for the matrix instance. + + This method is called when the matrix instance is deinitialized. It ensures that resources owned by the matrix, such as its memory buffer, are properly released. + + Notes: + - This method only frees resources if the matrix owns its data. + - The `own_data` flag determines whether the memory buffer is freed. + """ + + @parameter + if own_data: self._buf.ptr.free() # ===-------------------------------------------------------------------===# # Slicing and indexing methods # ===-------------------------------------------------------------------===# - fn __getitem__(self, var x: Int, var y: Int) raises -> Scalar[dtype]: + @always_inline + fn index(self, row: Int, col: Int) -> Int: + """ + Calculate the linear index in the underlying data buffer for a given + 2D index (row, col) based on the matrix's strides. + + Args: + row: The row index. + col: The column index. + + Returns: + The corresponding 1D index in the data buffer. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix[f32](shape=(3, 4)) + var idx = mat.index(1, 2) # Calculate linear index for (1, 2) + ``` + """ + return row * self.strides[0] + col * self.strides[1] + + @always_inline + fn normalize(self, idx: Int, dim: Int) -> Int: """ - Return the scalar at the index. + Normalize a potentially negative index to its positive equivalent + within the bounds of the given dimension. Args: - x: The row number. - y: The column number. + idx: The index to normalize. Can be negative to indicate indexing + from the end (e.g., -1 refers to the last element). + dim: The size of the dimension to normalize against. Returns: - A scalar matching the dtype of the array. + The normalized index as a non-negative integer. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix[f32](shape=(3, 4)) + var norm_idx = mat.normalize(-1, mat.shape[0]) # Normalize -1 to 2 + ``` """ + var idx_norm = idx + if idx_norm < 0: + idx_norm = dim + idx_norm + return idx_norm - if x < 0: - x = self.shape[0] + x + fn __getitem__(self, x: Int, y: Int) raises -> Scalar[dtype]: + """ + Retrieve the scalar value at the specified row and column indices. - if y < 0: - y = self.shape[1] + y + Args: + x: The row index. Can be negative to index from the end. + y: The column index. Can be negative to index from the end. - if (x >= self.shape[0]) or (y >= self.shape[1]): + Returns: + The value at the specified (x, y) position in the matrix. + + Raises: + Error: If the provided indices are out of bounds for the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(3, 4)) + var value = mat[1, 2] # Retrieve value at row 1, column 2 + ``` + """ + if ( + x >= self.shape[0] + or x < -self.shape[0] + or y >= self.shape[1] + or y < -self.shape[1] + ): raise Error( String( "Index ({}, {}) exceed the matrix shape ({}, {})" ).format(x, y, self.shape[0], self.shape[1]) ) + var x_norm = self.normalize(x, self.shape[0]) + var y_norm = self.normalize(y, self.shape[1]) + return self._buf[self.index(x_norm, y_norm)] - return self._buf.ptr.load(x * self.strides[0] + y * self.strides[1]) - - fn __getitem__(self, var x: Int) raises -> Self: + # TODO: temporarily renaming all view returning functions to be `get` or `set` due to a Mojo bug with overloading `__getitem__` and `__setitem__` with different argument types. Created an issue in Mojo GitHub + fn get[ + is_mutable: Bool, //, view_origin: Origin[is_mutable] + ](ref [view_origin]self, x: Int) raises -> MatrixView[ + dtype, MutOrigin.cast_from[view_origin] + ]: """ - Return the corresponding row at the index. + Retrieve a view of the specified row in the matrix. This method returns a non-owning `MatrixView` that references the data of the specified row in the original matrix. The view does not allocate new memory and directly points to the existing data buffer of the matrix. + + Parameters: + is_mutable: An inferred boolean indicating whether the returned view should allow modifications to the underlying data. + view_origin: Tracks the mutability and lifetime of the data being viewed. Should not be specified directly by users as it can lead to unsafe behavior. Args: - x: The row number. - """ + x: The row index to retrieve. Negative indices are supported and follow Python conventions (e.g., -1 refers to the last row). + + Returns: + A `MatrixView` representing the specified row as a row vector. - if x < 0: - x = self.shape[0] + x + Raises: + Error: If the provided row index is out of bounds. - if x >= self.shape[0]: + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(3, 4)) + var row_view = mat.get(1) # Get a view of the second row + ``` + """ + constrained[ + Self.own_data == True, + ( + "Creating views from views is not supported currently to ensure" + " memory safety." + ), + ]() + if x >= self.shape[0] or x < -self.shape[0]: raise Error( String("Index {} exceed the row number {}").format( x, self.shape[0] ) ) - var res = Self(shape=(1, self.shape[1]), order=self.order()) + var x_norm = self.normalize(x, self.shape[0]) + var new_data = DataContainer[dtype, MutOrigin.cast_from[view_origin]]( + ptr=self._buf.get_ptr().unsafe_origin_cast[ + MutOrigin.cast_from[view_origin] + ]() + + x_norm * self.strides[0] + ) + var row_view = MatrixView[dtype, MutOrigin.cast_from[view_origin]]( + shape=(1, self.shape[1]), + strides=(self.strides[0], self.strides[1]), + data=new_data, + ) + return row_view^ + # for creating a copy of the row. + fn __getitem__(self, var x: Int) raises -> Matrix[dtype]: + """ + Retrieve a copy of the specified row in the matrix. This method creates and returns a new `Matrix` instance that contains a copy of the data from the specified row of the original matrix. The returned matrix is a row vector with a shape of (1, number_of_columns). + + Args: + x: The row index to retrieve. Negative indices are supported and follow Python conventions (e.g., -1 refers to the last row). + + Returns: + A `Matrix` instance representing the specified row as a row vector. + + Raises: + Error: If the provided row index is out of bounds. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(3, 4)) + var row_copy = mat[1] # Get a copy of the second row + ``` + """ + if x >= self.shape[0] or x < -self.shape[0]: + raise Error( + String("Index {} exceed the row size {}").format( + x, self.shape[0] + ) + ) + var x_norm = self.normalize(x, self.shape[0]) + var result = Matrix[dtype](shape=(1, self.shape[1]), order=self.order()) if self.flags.C_CONTIGUOUS: - var ptr = self._buf.ptr.offset(x * self.strides[0]) - memcpy(res._buf.ptr, ptr, self.shape[1]) + var ptr = self._buf.ptr.offset(x_norm * self.strides[0]) + memcpy(dest=result._buf.ptr, src=ptr, count=self.shape[1]) else: for j in range(self.shape[1]): - res[0, j] = self[x, j] + result[0, j] = self[x_norm, j] - return res^ + return result^ + + fn get[ + is_mutable: Bool, //, view_origin: Origin[is_mutable] + ](ref [view_origin]self, x: Slice, y: Slice) -> MatrixView[ + dtype, MutOrigin.cast_from[view_origin] + ] where (own_data == True): + """ + Retrieve a view of the specified slice in the matrix. + + This method returns a non-owning `MatrixView` that references the data of the specified row in the original matrix. The view does not allocate new memory and directly points to the existing data buffer of the matrix. + + Parameters: + is_mutable: An inferred boolean indicating whether the returned view should allow modifications to the underlying data. + view_origin: Tracks the mutability and lifetime of the data being viewed. Should not be specified directly by users as it can lead to unsafe behavior. + + Args: + x: The row slice to retrieve. + y: The column slice to retrieve. + + Returns: + A `MatrixView` representing the specified slice of the matrix. + + Notes: + - Out of bounds indices are clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(4, 4)) + var slice_view = mat.get(Slice(1, 3), Slice(0, 2)) # Get a view of the submatrix + ``` + """ + start_x, end_x, step_x = x.indices(self.shape[0]) + start_y, end_y, step_y = y.indices(self.shape[1]) + + var new_data = DataContainer[dtype, MutOrigin.cast_from[view_origin]]( + ptr=self._buf.get_ptr() + .unsafe_origin_cast[MutOrigin.cast_from[view_origin]]() + .offset(start_x * self.strides[0] + start_y * self.strides[1]) + ) + var sliced_view = MatrixView[dtype, MutOrigin.cast_from[view_origin]]( + shape=( + Int(ceil((end_x - start_x) / step_x)), + Int(ceil((end_y - start_y) / step_y)), + ), + strides=(self.strides[0] * step_x, self.strides[1] * step_y), + data=new_data, + ) + return sliced_view^ - fn __getitem__(self, x: Slice, y: Slice) -> Self: + # for creating a copy of the slice. + fn __getitem__(self, x: Slice, y: Slice) -> Matrix[dtype]: """ - Get item from two slices. + Retrieve a copy of the specified slice in the matrix. This method creates and returns a new `Matrix` instance that contains a copy of the data from the specified slice of the original matrix. The returned matrix will have the shape determined by the slice ranges. + + Args: + x: The row slice to retrieve. Supports Python slice syntax. + y: The column slice to retrieve. Supports Python slice syntax. + + Returns: + A `Matrix` instance representing the specified slice of the matrix. + + Notes: + - Out of bounds indices are clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(4, 4)) + var slice_copy = mat[1:3, 0:2] # Get a copy of the submatrix + ``` """ var start_x: Int var end_x: Int @@ -296,12 +783,9 @@ struct Matrix[dtype: DType = DType.float64]( var range_x = range(start_x, end_x, step_x) var range_y = range(start_y, end_y, step_y) - # The new matrix with the corresponding shape var B = Matrix[dtype]( shape=(len(range_x), len(range_y)), order=self.order() ) - - # Fill in the values at the corresponding index var row = 0 for i in range_x: var col = 0 @@ -312,9 +796,88 @@ struct Matrix[dtype: DType = DType.float64]( return B^ - fn __getitem__(self, x: Slice, var y: Int) -> Self: + fn get[ + is_mutable: Bool, //, view_origin: Origin[is_mutable] + ](ref [view_origin]self, x: Slice, var y: Int) raises -> MatrixView[ + dtype, MutOrigin.cast_from[view_origin] + ] where (own_data == True): + """ + Retrieve a view of a specific column slice in the matrix. This method returns a non-owning `MatrixView` that references the data of the specified column slice in the original matrix. The view does not allocate new memory and directly points to the existing data buffer of the matrix. + + Parameters: + is_mutable: An inferred boolean indicating whether the returned view should allow modifications to the underlying data. + view_origin: Tracks the mutability and lifetime of the data being viewed. Should not be specified directly by users as it can lead to unsafe behavior. + + Args: + x: The row slice to retrieve. This defines the range of rows to include in the view. + y: The column index to retrieve. This specifies the column to include in the view. + + Returns: + A `MatrixView` representing the specified column slice of the matrix. + + Raises: + Error: If the provided column index `y` is out of bounds. + + Notes: + - Out-of-bounds indices for `x` are clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(4, 4)) + var column_view = mat.get(Slice(0, 4), 2) # Get a view of the third column + ``` + """ + if y >= self.shape[1] or y < -self.shape[1]: + raise Error( + String("Index {} exceed the column number {}").format( + y, self.shape[1] + ) + ) + y = self.normalize(y, self.shape[1]) + var start_x: Int + var end_x: Int + var step_x: Int + start_x, end_x, step_x = x.indices(self.shape[0]) + + var new_data = DataContainer[dtype, MutOrigin.cast_from[view_origin]]( + ptr=self._buf.get_ptr() + .unsafe_origin_cast[MutOrigin.cast_from[view_origin]]() + .offset(start_x * self.strides[0] + y * self.strides[1]) + ) + var column_view = MatrixView[dtype, MutOrigin.cast_from[view_origin]]( + shape=( + Int(ceil((end_x - start_x) / step_x)), + 1, + ), + strides=(self.strides[0] * step_x, self.strides[1]), + data=new_data, + ) + + return column_view^ + + fn __getitem__(self, x: Slice, var y: Int) -> Matrix[dtype]: """ - Get item from one slice and one int. + Retrieve a copy of a specific column slice in the matrix. This method creates and returns a new `Matrix` instance that contains a copy + of the data from the specified and column slice of the original matrix. The returned matrix will have a shape determined by the row slice and a single column. + + Args: + x: The row slice to retrieve. This defines the range of rows to include in the copy. + y: The column index to retrieve. This specifies the column to include in the copy. + + Returns: + A `Matrix` instance representing the specified column slice of the matrix. + + Notes: + - Negative indices for `y` are normalized to their positive equivalent. + - Out-of-bounds indices for `x` are clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(4, 4)) + var column_copy = mat[0:4, 2] # Get a copy of the third column + ``` """ if y < 0: y = self.shape[1] + y @@ -324,35 +887,118 @@ struct Matrix[dtype: DType = DType.float64]( var step_x: Int start_x, end_x, step_x = x.indices(self.shape[0]) var range_x = range(start_x, end_x, step_x) - - # The new matrix with the corresponding shape - var B = Matrix[dtype](shape=(len(range_x), 1), order=self.order()) - - # Fill in the values at the corresponding index + var res = Matrix[dtype]( + shape=( + len(range_x), + 1, + ), + order=self.order(), + ) var row = 0 for i in range_x: - B._store(row, 0, self._load(i, y)) + res._store(row, 0, self._load(i, y)) row += 1 + return res^ - return B^ + fn get[ + is_mutable: Bool, //, view_origin: Origin[is_mutable] + ](ref [view_origin]self, var x: Int, y: Slice) raises -> MatrixView[ + dtype, MutOrigin.cast_from[view_origin] + ] where (own_data == True): + """ + Retrieve a view of a specific row slice in the matrix. This method returns a non-owning `MatrixView` that references the data of the specified row slice in the original matrix. The view does not allocate new memory and directly points to the existing data buffer of the matrix. + + Parameters: + is_mutable: An inferred boolean indicating whether the returned view should allow modifications to the underlying data. + view_origin: Tracks the mutability and lifetime of the data being viewed. Should not be specified directly by users as it can lead to unsafe behavior. + + Args: + x: The row index to retrieve. This specifies the row to include in the view. Negative indices are supported and follow Python conventions (e.g., -1 refers to the last row). + y: The column slice to retrieve. This defines the range of columns to include in the view. - fn __getitem__(self, var x: Int, y: Slice) -> Self: + Returns: + A `MatrixView` representing the specified row slice of the matrix. + + Raises: + Error: If the provided row index `x` is out of bounds. + + Notes: + - Out-of-bounds indices for `y` are clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(4, 4)) + var row_view = mat.get(1, Slice(0, 3)) # Get a view of the second row, columns 0 to 2 + ``` """ - Get item from one int and one slice. + if x >= self.shape[0] or x < -self.shape[0]: + raise Error( + String("Index {} exceed the row size {}").format( + x, self.shape[0] + ) + ) + x = self.normalize(x, self.shape[0]) + var start_y: Int + var end_y: Int + var step_y: Int + start_y, end_y, step_y = y.indices(self.shape[1]) + var new_data = DataContainer[dtype, MutOrigin.cast_from[view_origin]]( + ptr=self._buf.get_ptr() + .unsafe_origin_cast[MutOrigin.cast_from[view_origin]]() + .offset(x * self.strides[0] + start_y * self.strides[1]) + ) + var row_slice_view = MatrixView[ + dtype, MutOrigin.cast_from[view_origin] + ]( + shape=( + 1, + Int(ceil((end_y - start_y) / step_y)), + ), + strides=(self.strides[0], self.strides[1] * step_y), + data=new_data, + ) + return row_slice_view^ + + fn __getitem__(self, var x: Int, y: Slice) raises -> Matrix[dtype]: """ - if x < 0: - x = self.shape[0] + x + Retrieve a copy of a specific row slice in the matrix. This method creates and returns a new `Matrix` instance that contains a copy + of the data from the specified row and column slice of the original matrix. The returned matrix will have a shape of (1, number_of_columns_in_slice). + + Args: + x: The row index to retrieve. This specifies the row to include in the copy. Negative indices are supported and follow Python conventions (e.g., -1 refers to the last row). + y: The column slice to retrieve. This defines the range of columns to include in the copy. + + Returns: + A `Matrix` instance representing the specified row slice of the matrix. + + Raises: + Error: If the provided row index `x` is out of bounds. + Notes: + - Out-of-bounds indices for `y` are clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(4, 4)) + var row_copy = mat[1, 0:3] # Get a copy of the second row, columns 0 to 2 + ``` + """ + if x >= self.shape[0] or x < -self.shape[0]: + raise Error( + String("Index {} exceed the row size {}").format( + x, self.shape[0] + ) + ) + x = self.normalize(x, self.shape[0]) var start_y: Int var end_y: Int var step_y: Int start_y, end_y, step_y = y.indices(self.shape[1]) var range_y = range(start_y, end_y, step_y) - # The new matrix with the corresponding shape var B = Matrix[dtype](shape=(1, len(range_y)), order=self.order()) - - # Fill in the values at the corresponding index var col = 0 for j in range_y: B._store(0, col, self._load(x, j)) @@ -360,17 +1006,71 @@ struct Matrix[dtype: DType = DType.float64]( return B^ - fn __getitem__(self, indices: List[Int]) raises -> Self: + fn __getitem__(self, indices: List[Int]) raises -> Matrix[dtype]: """ - Get item by a list of integers. + Retrieve a copy of specific rows in the matrix based on the provided indices. This method creates and returns a new `Matrix` instance that contains a copy of the data from the specified rows of the original matrix. The returned matrix will have a shape of (number_of_indices, number_of_columns). + + Args: + indices: A list of row indices to retrieve. Each index specifies a row to include in the resulting matrix. Negative indices are supported and follow Python conventions (e.g., -1 refers to the last row). + + Returns: + A `Matrix` instance containing the selected rows as a new matrix. + + Raises: + Error: If any of the provided indices are out of bounds. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(4, 4)) + var selected_rows = mat[List[Int](0, 1, 0)] # Get a copy of the + # first and second and first rows in a new matrix with shape (3, 4) + ``` + """ + var num_cols = self.shape[1] + var num_rows = len(indices) + var selected_rows = Matrix.zeros[dtype](shape=(num_rows, num_cols)) + for i in range(num_rows): + if indices[i] >= self.shape[0] or indices[i] < -self.shape[0]: + raise Error( + String("Index {} exceed the row size {}").format( + indices[i], self.shape[0] + ) + ) + selected_rows[i] = self[indices[i]] + return selected_rows^ + + fn load[width: Int = 1](self, idx: Int) raises -> SIMD[dtype, width]: """ + Load a SIMD element from the matrix at the specified linear index. - var ncol = self.shape[1] - var nrow = len(indices) - var res = Matrix.zeros[dtype](shape=(nrow, ncol)) - for i in range(nrow): - res[i] = self[indices[i]] - return res^ + Parameters: + width: The width of the SIMD element to load. Defaults to 1. + + Args: + idx: The linear index of the element to load. Negative indices are supported and follow Python conventions. + + Returns: + A SIMD element of the specified width containing the data at the given index. + + Raises: + Error: If the provided index is out of bounds. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.ones(shape=(4, 4)) + var simd_element = mat.load[4](2) # Load a SIMD element of width 4 from index 2 + ``` + """ + if idx >= self.size or idx < -self.size: + raise Error( + String("Index {} exceed the matrix size {}").format( + idx, self.size + ) + ) + var idx_norm = self.normalize(idx, self.size) + return self._buf.ptr.load[width=width](idx_norm) fn _load[width: Int = 1](self, x: Int, y: Int) -> SIMD[dtype, width]: """ @@ -381,38 +1081,150 @@ struct Matrix[dtype: DType = DType.float64]( x * self.strides[0] + y * self.strides[1] ) - fn __setitem__(self, x: Int, y: Int, value: Scalar[dtype]) raises: + fn _load[width: Int = 1](self, idx: Int) -> SIMD[dtype, width]: + """ + `__getitem__` with width. + Unsafe: No boundary check! + """ + return self._buf.ptr.load[width=width](idx) + + fn __setitem__(mut self, x: Int, y: Int, value: Scalar[dtype]) raises: """ - Return the scalar at the index. + Set the value at the specified row and column indices in the matrix. Args: - x: The row number. - y: The column number. - value: The value to be set. - """ + x: The row index. Can be negative to index from the end. + y: The column index. Can be negative to index from the end. + value: The value to set at the specified position. - if (x >= self.shape[0]) or (y >= self.shape[1]): + Raises: + Error: If the provided indices are out of bounds for the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(3, 4)) + mat[1, 2] = 5.0 # Set value at row 1, column 2 to 5.0 + ``` + """ + if ( + x >= self.shape[0] + or x < -self.shape[0] + or y >= self.shape[1] + or y < -self.shape[1] + ): raise Error( String( "Index ({}, {}) exceed the matrix shape ({}, {})" ).format(x, y, self.shape[0], self.shape[1]) ) + var x_norm: Int = self.normalize(x, self.shape[0]) + var y_norm: Int = self.normalize(y, self.shape[1]) - self._buf.ptr.store(x * self.strides[0] + y * self.strides[1], value) + self._buf.store(self.index(x_norm, y_norm), value) - fn __setitem__(self, var x: Int, value: Self) raises: + # FIXME: Setting with views is currently only supported through `.set()` method of the Matrix. Once Mojo resolve the symmetric getter setter issue, we can remove `.set()` methods. + fn __setitem__( + self, var x: Int, value: MatrixBase[dtype, **_] + ) raises where Self.own_data == True and value.own_data == True: """ - Set the corresponding row at the index with the given matrix. + Assign a row in the matrix at the specified index with the given matrix. This method replaces the row at the specified index `x` with the data from + the provided `value` matrix. The `value` matrix must be a row vector with + the same number of columns as the target matrix. Args: - x: The row number. - value: Matrix (row vector). + x: The row index where the data will be assigned. Negative indices are + supported and follow Python conventions (e.g., -1 refers to the last row). + value: A `Matrix` instance representing the row vector to assign. + The `value` matrix can be in either C-contiguous or F-contiguous order. + + Raises: + Error: If the row index `x` is out of bounds. + Error: If the `value` matrix does not have exactly one row. + Error: If the number of columns in the `value` matrix does not match + the number of columns in the target matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(3, 4)) + var row_vector = Matrix.ones(shape=(1, 4)) + mat[1] = row_vector # Set the second row of mat to row_vector + ``` + """ + if x >= self.shape[0] or x < -self.shape[0]: + raise Error( + String( + "Error: Elements of `index` ({}) \n" + "exceed the matrix shape ({})." + ).format(x, self.shape[0]) + ) + + if value.shape[0] != 1: + raise Error( + String( + "Error: The value should have only 1 row, " + "but it has {} rows." + ).format(value.shape[0]) + ) + + if self.shape[1] != value.shape[1]: + raise Error( + String( + "Error: Matrix has {} columns, " + "but the value has {} columns." + ).format(self.shape[1], value.shape[1]) + ) + + if self.flags.C_CONTIGUOUS: + if value.flags.C_CONTIGUOUS: + var dest_ptr = self._buf.ptr.offset(x * self.strides[0]) + memcpy(dest=dest_ptr, src=value._buf.ptr, count=self.shape[1]) + else: + for j in range(self.shape[1]): + self._store(x, j, value._load(0, j)) + + # For F-contiguous + else: + if value.flags.F_CONTIGUOUS: + for j in range(self.shape[1]): + self._buf.ptr.offset(x + j * self.strides[1]).store( + value._buf.ptr.load(j * value.strides[1]) + ) + else: + for j in range(self.shape[1]): + self._store(x, j, value._load(0, j)) + + fn set(self, var x: Int, value: MatrixBase[dtype, **_]) raises: """ + Assign a row in the matrix at the specified index with the given matrix. This method replaces the row at the specified index `x` with the data from + the provided `value` matrix. The `value` matrix must be a row vector with + the same number of columns as the target matrix. + + Args: + x: The row index where the data will be assigned. Negative indices are + supported and follow Python conventions (e.g., -1 refers to the last row). + value: A `Matrix` instance representing the row vector to assign. + The `value` matrix can be in either C-contiguous or F-contiguous order. - if x < 0: - x = self.shape[0] + x + Raises: + Error: If the row index `x` is out of bounds. + Error: If the `value` matrix does not have exactly one row. + Error: If the number of columns in the `value` matrix does not match + the number of columns in the target matrix. - if x >= self.shape[0]: + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(3, 4)) + var row_vector = Matrix.ones(shape=(1, 4)) + mat.set(1, row_vector) # Set the second row of mat to row_vector + + var view = row_vector.view() # create a view of row_vector + mat.set(2, view) # Set the third row of mat to the view + ``` + """ + if x >= self.shape[0] or x < -self.shape[0]: raise Error( String( "Error: Elements of `index` ({}) \n" @@ -423,7 +1235,7 @@ struct Matrix[dtype: DType = DType.float64]( if value.shape[0] != 1: raise Error( String( - "Error: The value should has only 1 row, " + "Error: The value should have only 1 row, " "but it has {} rows." ).format(value.shape[0]) ) @@ -436,67 +1248,503 @@ struct Matrix[dtype: DType = DType.float64]( ).format(self.shape[1], value.shape[1]) ) - var ptr = self._buf.ptr.offset(x * self.shape[1]) - memcpy(ptr, value._buf.ptr, value.size) + if self.flags.C_CONTIGUOUS: + if value.flags.C_CONTIGUOUS: + var dest_ptr = self._buf.ptr.offset(x * self.strides[0]) + memcpy(dest=dest_ptr, src=value._buf.ptr, count=self.shape[1]) + else: + for j in range(self.shape[1]): + self._store(x, j, value._load(0, j)) + + # For F-contiguous + else: + if value.flags.F_CONTIGUOUS: + for j in range(self.shape[1]): + self._buf.ptr.offset(x + j * self.strides[1]).store( + value._buf.ptr.load(j * value.strides[1]) + ) + else: + for j in range(self.shape[1]): + self._store(x, j, value._load(0, j)) + + fn __setitem__( + self, x: Slice, y: Int, value: MatrixBase[dtype, **_] + ) raises: + """ + Assign values to a column in the matrix at the specified column index `y` + and row slice `x` with the given matrix. This method replaces the values + in the specified column and row slice with the data from the provided + `value` matrix. + + Args: + x: The row slice where the data will be assigned. Supports Python slice syntax (e.g., `start:stop:step`). + y: The column index where the data will be assigned. Negative indices + are supported and follow Python conventions (e.g., -1 refers to the + last column). + value: A `Matrix` instance representing the column vector to assign. + The `value` matrix must have the same number of rows as the + specified slice `x` and exactly one column. + + Raises: + Error: If the column index `y` is out of bounds. + Error: If the shape of the `value` matrix does not match the target + slice dimensions. + + Notes: + - Out of bound slice `x` is clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(4, 4)) + var col_vector = Matrix.ones(shape=(4, 1)) + mat[0:4, 2] = col_vector # Set the third column of mat to col_vector + ``` + """ + if y >= self.shape[1] or y < -self.shape[1]: + raise Error( + String("Index {} exceed the column number {}").format( + y, self.shape[1] + ) + ) + var y_norm = self.normalize(y, self.shape[1]) + var start_x: Int + var end_x: Int + var step_x: Int + start_x, end_x, step_x = x.indices(self.shape[0]) + var range_x = range(start_x, end_x, step_x) + var len_range_x: Int = len(range_x) + + if len_range_x != value.shape[0] or value.shape[1] != 1: + raise Error( + String( + "Shape mismatch when assigning to slice: " + "target shape ({}, {}) vs value shape ({}, {})" + ).format(len_range_x, 1, value.shape[0], value.shape[1]) + ) + + var row = 0 + for i in range_x: + self._store(i, y_norm, value._load(row, 0)) + row += 1 + + fn set(self, x: Slice, y: Int, value: MatrixBase[dtype, **_]) raises: + """ + Assign values to a column in the matrix at the specified column index `y` + and row slice `x` with the given matrix. This method replaces the values + in the specified column and row slice with the data from the provided + `value` matrix. + + Args: + x: The row slice where the data will be assigned. Supports Python slice syntax (e.g., `start:stop:step`). + y: The column index where the data will be assigned. Negative indices + are supported and follow Python conventions (e.g., -1 refers to the + last column). + value: A `Matrix` instance representing the column vector to assign. + The `value` matrix must have the same number of rows as the + specified slice `x` and exactly one column. + + Raises: + Error: If the column index `y` is out of bounds. + Error: If the shape of the `value` matrix does not match the target + slice dimensions. + + Notes: + - Out of bound slice `x` is clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(4, 4)) + var col_vector = Matrix.ones(shape=(4, 1)) + mat.set(Slice(0, 4), 2, col_vector) # Set the third column of mat to col_vector - fn _store[ - width: Int = 1 - ](mut self, x: Int, y: Int, simd: SIMD[dtype, width]): + var view = col_vector.view() # create a view of col_vector + mat.set(Slice(0, 4), 3, view) # Set the fourth column of mat to the view + ``` + """ + if y >= self.shape[1] or y < -self.shape[1]: + raise Error( + String("Index {} exceed the column number {}").format( + y, self.shape[1] + ) + ) + var y_norm = self.normalize(y, self.shape[1]) + var start_x: Int + var end_x: Int + var step_x: Int + start_x, end_x, step_x = x.indices(self.shape[0]) + var range_x = range(start_x, end_x, step_x) + var len_range_x: Int = len(range_x) + + if len_range_x != value.shape[0] or value.shape[1] != 1: + raise Error( + String( + "Shape mismatch when assigning to slice: " + "target shape ({}, {}) vs value shape ({}, {})" + ).format(len_range_x, 1, value.shape[0], value.shape[1]) + ) + + var row = 0 + for i in range_x: + self._store(i, y_norm, value._load(row, 0)) + row += 1 + + fn __setitem__( + self, x: Int, y: Slice, value: MatrixBase[dtype, **_] + ) raises: + """ + Assign values to a row in the matrix at the specified row index `x` + and column slice `y` with the given matrix. This method replaces the values in the specified row and column slice with the data from the provided `value` matrix. + + Args: + x: The row index where the data will be assigned. Negative indices + are supported and follow Python conventions (e.g., -1 refers to the + last row). + y: The column slice where the data will be assigned. Supports Python slice syntax (e.g., `start:stop:step`). + value: A `Matrix` instance representing the row vector to assign. + The `value` matrix must have the same number of columns as the + specified slice `y` and exactly one row. + + Raises: + Error: If the row index `x` is out of bounds. + Error: If the shape of the `value` matrix does not match the target + slice dimensions. + + Notes: + - Out of bound slice `y` is clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(4, 4)) + var row_vector = Matrix.ones(shape=(1, 3)) + mat[1, 0:3] = row_vector # Set the second row, columns 0 to 2 of mat to row_vector + ``` + """ + if x >= self.shape[0] or x < -self.shape[0]: + raise Error( + String("Index {} exceed the row size {}").format( + x, self.shape[0] + ) + ) + var x_norm = self.normalize(x, self.shape[0]) + var start_y: Int + var end_y: Int + var step_y: Int + start_y, end_y, step_y = y.indices(self.shape[1]) + var range_y = range(start_y, end_y, step_y) + var len_range_y: Int = len(range_y) + + if len_range_y != value.shape[1] or value.shape[0] != 1: + raise Error( + String( + "Shape mismatch when assigning to slice: " + "target shape ({}, {}) vs value shape ({}, {})" + ).format(1, len_range_y, value.shape[0], value.shape[1]) + ) + + var col = 0 + for j in range_y: + self._store(x_norm, j, value._load(0, col)) + col += 1 + + fn set(self, x: Int, y: Slice, value: MatrixBase[dtype, **_]) raises: + """ + Assign values to a row in the matrix at the specified row index `x` + and column slice `y` with the given matrix. This method replaces the values in the specified row and column slice with the data from the provided `value` matrix. + + Args: + x: The row index where the data will be assigned. Negative indices + are supported and follow Python conventions (e.g., -1 refers to the + last row). + y: The column slice where the data will be assigned. Supports Python slice syntax (e.g., `start:stop:step`). + value: A `Matrix` instance representing the row vector to assign. + The `value` matrix must have the same number of columns as the + specified slice `y` and exactly one row. + + Raises: + Error: If the row index `x` is out of bounds. + Error: If the shape of the `value` matrix does not match the target + slice dimensions. + + Notes: + - Out of bound slice `y` is clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(4, 4)) + var row_vector = Matrix.ones(shape=(1, 3)) + mat.set(1, Slice(0, 3), row_vector) # Set the second row, columns 0 to 2 of mat to row_vector + + var view = row_vector.view() # create a view of row_vector + mat.set(2, Slice(0, 3), view) # Set the third row, columns 0 to 2 of mat to the view + ``` + """ + if x >= self.shape[0] or x < -self.shape[0]: + raise Error( + String("Index {} exceed the row size {}").format( + x, self.shape[0] + ) + ) + var x_norm = self.normalize(x, self.shape[0]) + var start_y: Int + var end_y: Int + var step_y: Int + start_y, end_y, step_y = y.indices(self.shape[1]) + var range_y = range(start_y, end_y, step_y) + var len_range_y: Int = len(range_y) + + if len_range_y != value.shape[1] or value.shape[0] != 1: + raise Error( + String( + "Shape mismatch when assigning to slice: " + "target shape ({}, {}) vs value shape ({}, {})" + ).format(1, len_range_y, value.shape[0], value.shape[1]) + ) + + var col = 0 + for j in range_y: + self._store(x_norm, j, value._load(0, col)) + col += 1 + + fn __setitem__( + self, x: Slice, y: Slice, value: MatrixBase[dtype, **_] + ) raises: + """ + Assign values to a submatrix of the matrix defined by row slice `x` and column slice `y` using the provided `value` matrix. This method replaces the elements in the specified row and column slices with the corresponding elements from `value`. + + Args: + x: Row slice specifying which rows to assign to. Supports Python slice syntax (e.g., `start:stop:step`). + y: Column slice specifying which columns to assign to. Supports Python slice syntax (e.g., `start:stop:step`). + value: A `Matrix` instance containing the values to assign. + + Raises: + Error: If the shape of `value` does not match the shape of the target slice. + + Notes: + - Out of bounds slices are clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(4, 4)) + var submatrix = Matrix.ones(shape=(2, 2)) + mat[1:3, 1:3] = submatrix # Set the 2x2 submatrix starting at (1,1) to ones + ``` + """ + var start_x: Int + var end_x: Int + var step_x: Int + var start_y: Int + var end_y: Int + var step_y: Int + start_x, end_x, step_x = x.indices(self.shape[0]) + start_y, end_y, step_y = y.indices(self.shape[1]) + var range_x = range(start_x, end_x, step_x) + var range_y = range(start_y, end_y, step_y) + + if len(range_x) != value.shape[0] or len(range_y) != value.shape[1]: + raise Error( + String( + "Shape mismatch when assigning to slice: " + "target shape ({}, {}) vs value shape ({}, {})" + ).format( + len(range_x), len(range_y), value.shape[0], value.shape[1] + ) + ) + + var row = 0 + for i in range_x: + var col = 0 + for j in range_y: + self._store(i, j, value._load(row, col)) + col += 1 + row += 1 + + fn set(self, x: Slice, y: Slice, value: MatrixBase[dtype, **_]) raises: + """ + Assign values to a submatrix of the matrix defined by row slice `x` and column slice `y` using the provided `value` matrix. This method replaces the elements in the specified row and column slices with the corresponding elements from `value`. + + Args: + x: Row slice specifying which rows to assign to. Supports Python slice syntax (e.g., `start:stop:step`). + y: Column slice specifying which columns to assign to. Supports Python slice syntax (e.g., `start:stop:step`). + value: A `Matrix` instance containing the values to assign. + + Raises: + Error: If the shape of `value` does not match the shape of the target slice. + + Notes: + - Out of bounds slices are clamped using the shape of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix.zeros(shape=(4, 4)) + var submatrix = Matrix.ones(shape=(2, 2)) + mat.set(Slice(1, 3), Slice(1, 3), submatrix) # Set the 2x2 submatrix starting at (1,1) to ones + + var view = submatrix.view() # create a view of submatrix + mat.set(Slice(2, 4), Slice(2, 4), view + ) # Set the 2x2 submatrix starting at (2,2) to the view + ``` + """ + var start_x: Int + var end_x: Int + var step_x: Int + var start_y: Int + var end_y: Int + var step_y: Int + start_x, end_x, step_x = x.indices(self.shape[0]) + start_y, end_y, step_y = y.indices(self.shape[1]) + var range_x = range(start_x, end_x, step_x) + var range_y = range(start_y, end_y, step_y) + + if len(range_x) != value.shape[0] or len(range_y) != value.shape[1]: + raise Error( + String( + "Shape mismatch when assigning to slice: " + "target shape ({}, {}) vs value shape ({}, {})" + ).format( + len(range_x), len(range_y), value.shape[0], value.shape[1] + ) + ) + + var row = 0 + for i in range_x: + var col = 0 + for j in range_y: + self._store(i, j, value._load(row, col)) + col += 1 + row += 1 + + fn _store[width: Int = 1](self, x: Int, y: Int, simd: SIMD[dtype, width]): """ `__setitem__` with width. Unsafe: No boundary check! """ self._buf.ptr.store(x * self.strides[0] + y * self.strides[1], simd) + fn _store_idx[width: Int = 1](self, idx: Int, val: SIMD[dtype, width]): + """ + `__setitem__` with width. + Unsafe: No boundary check! + """ + self._buf.ptr.store(idx, val) + # ===-------------------------------------------------------------------===# # Other dunders and auxiliary methods # ===-------------------------------------------------------------------===# + fn view(ref self) -> MatrixView[dtype, MutOrigin.cast_from[origin]]: + """ + Return a non-owning view of the matrix. This method creates and returns a `MatrixView` that references the data of the original matrix. The view does not allocate new memory and directly points to the existing data buffer. Modifications to the view affect the original matrix. - fn __iter__(self) raises -> _MatrixIter[__origin_of(self), dtype]: - """Iterate over elements of the Matrix, returning copied value. + Returns: + A `MatrixView` referencing the original matrix data. Example: - ```mojo - from numojo import Matrix - var A = Matrix.rand((4,4)) - for i in A: - print(i) - ``` + ```mojo + from numojo import Matrix + var mat = Matrix.rand((4, 4)) + var mat_view = mat.view() # Create a view of the original matrix + ``` + """ + var new_data = DataContainer[dtype, MutOrigin.cast_from[origin]]( + ptr=self._buf.get_ptr().unsafe_origin_cast[ + MutOrigin.cast_from[origin] + ]() + ) + var matrix_view = MatrixView[dtype, MutOrigin.cast_from[origin]]( + shape=self.shape, + strides=self.strides, + data=new_data, + ) + return matrix_view^ - Returns: - An iterator of Matrix elements. + fn __iter__( + self, + ) -> Self.IteratorType[origin, origin_of(self), True] where ( + own_data == True + ): """ + Returns an iterator over the rows of the Matrix. Each iteration yields a MatrixView representing a single row. + + Returns: + Iterator that yields MatrixView objects for each row. - return _MatrixIter[__origin_of(self), dtype]( - matrix=self, - length=self.shape[0], + Example: + ```mojo + from numojo import Matrix + var mat = Matrix.rand((4, 4)) + for row in mat: + print(row) # Each row is a MatrixView + ``` + """ + return Self.IteratorType[origin, origin_of(self), True]( + index=0, + src=rebind[ + Pointer[ + MatrixBase[dtype, own_data=True, origin=origin], + origin_of(self), + ] + ](Pointer(to=self)), ) fn __len__(self) -> Int: """ - Returns length of 0-th dimension. + Return the number of rows in the matrix (length of the first dimension). + + Returns: + The number of rows (self.shape[0]). + + Example: + ```mojo + from numojo import Matrix + var mat = Matrix.rand((4, 4)) + print(len(mat)) # Outputs: 4 + ``` """ return self.shape[0] fn __reversed__( - self, - ) raises -> _MatrixIter[__origin_of(self), dtype, forward=False]: - """Iterate backwards over elements of the Matrix, returning - copied value. - - Returns: - A reversed iterator of Matrix elements. + mut self, + ) raises -> Self.IteratorType[origin, origin_of(self), False] where ( + own_data == True + ): """ + Return an iterator that traverses the matrix rows in reverse order. - return _MatrixIter[__origin_of(self), dtype, forward=False]( - matrix=self, - length=self.shape[0], + Returns: + A reversed iterator over the rows of the matrix, yielding copies of each row. + """ + return Self.IteratorType[origin, origin_of(self), False]( + index=0, + src=rebind[ + Pointer[ + MatrixBase[dtype, own_data=True, origin=origin], + origin_of(self), + ] + ](Pointer(to=self)), ) fn __str__(self) -> String: + """ + Return a string representation of the matrix. + + Returns: + A string showing the matrix contents, shape, strides, order, and ownership. + """ return String.write(self) fn write_to[W: Writer](self, mut writer: W): + """ + Write the string representation of the matrix to a writer. + + Args: + writer: The writer to output the matrix string to. + """ + fn print_row(self: Self, i: Int, sep: String) raises -> String: var result: String = String("[") var number_of_sep: Int = 1 @@ -552,10 +1800,8 @@ struct Matrix[dtype: DType = DType.float64]( + String(self.strides[0]) + "," + String(self.strides[1]) - + " C: " - + String(self.flags["C_CONTIGUOUS"]) - + " F: " - + String(self.flags["F_CONTIGUOUS"]) + + " order: " + + String("C" if self.flags["C_CONTIGUOUS"] else "F") + " Own: " + String(self.flags["OWNDATA"]) ) @@ -566,7 +1812,27 @@ struct Matrix[dtype: DType = DType.float64]( # Arithmetic dunder methods # ===-------------------------------------------------------------------===# - fn __add__(self, other: Self) raises -> Self: + fn __add__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: + """ + Add two matrices element-wise. + + Args: + other: Matrix to add to self. Must be broadcastable to self's shape. + + Returns: + A new Matrix containing the element-wise sum. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + var B = Matrix.ones(shape=(4, 4)) + print(A + B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -578,36 +1844,71 @@ struct Matrix[dtype: DType = DType.float64]( ): return _arithmetic_func_matrix_matrix_to_matrix[ dtype, SIMD.__add__ - ](broadcast_to(self.copy(), other.shape, self.order()), other) + ](broadcast_to[dtype](self, other.shape, self.order()), other) else: return _arithmetic_func_matrix_matrix_to_matrix[ dtype, SIMD.__add__ - ](self, broadcast_to(other.copy(), self.shape, self.order())) + ](self, broadcast_to[dtype](other, self.shape, self.order())) + + fn __add__(self, other: Scalar[dtype]) raises -> Matrix[dtype]: + """ + Add a scalar to every element of the matrix. + + Args: + other: Scalar value to add. - fn __add__(self, other: Scalar[dtype]) raises -> Self: - """Add matrix to scalar. + Returns: + A new Matrix with the scalar added to each element. - ```mojo - from numojo import Matrix - var A = Matrix.ones(shape=(4, 4)) - print(A + 2) - ``` + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + print(A + 2) + ``` """ return self + broadcast_to[dtype](other, self.shape, self.order()) - fn __radd__(self, other: Scalar[dtype]) raises -> Self: + fn __radd__(self, other: Scalar[dtype]) raises -> Matrix[dtype]: """ - Right-add. + Add a matrix to a scalar (right-hand side). + + Args: + other: Scalar value to add. + + Returns: + A new Matrix with the scalar added to each element. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(2 + A) - ``` + Example: + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(2 + A) + ``` """ return broadcast_to[dtype](other, self.shape, self.order()) + self - fn __sub__(self, other: Self) raises -> Self: + fn __sub__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: + """ + Subtract two matrices element-wise. + + Args: + other: Matrix to subtract from self. Must be broadcastable to self's shape. + + Returns: + A new Matrix containing the element-wise difference. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + var B = Matrix.ones(shape=(4, 4)) + print(A - B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -619,36 +1920,71 @@ struct Matrix[dtype: DType = DType.float64]( ): return _arithmetic_func_matrix_matrix_to_matrix[ dtype, SIMD.__sub__ - ](broadcast_to(self.copy(), other.shape, self.order()), other) + ](broadcast_to(self, other.shape, self.order()), other) else: return _arithmetic_func_matrix_matrix_to_matrix[ dtype, SIMD.__sub__ - ](self, broadcast_to(other.copy(), self.shape, self.order())) + ](self, broadcast_to(other, self.shape, self.order())) + + fn __sub__(self, other: Scalar[dtype]) raises -> Matrix[dtype]: + """ + Subtract a scalar from every element of the matrix. + + Args: + other: Scalar value to subtract. - fn __sub__(self, other: Scalar[dtype]) raises -> Self: - """Subtract matrix by scalar. + Returns: + A new Matrix with the scalar subtracted from each element. - ```mojo - from numojo import Matrix - A = Matrix(shape=(4, 4)) - print(A - 2) - ``` + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + print(A - 2) + ``` """ return self - broadcast_to[dtype](other, self.shape, self.order()) - fn __rsub__(self, other: Scalar[dtype]) raises -> Self: + fn __rsub__(self, other: Scalar[dtype]) raises -> Matrix[dtype]: """ - Right-sub. + Subtract a matrix from a scalar (right-hand side). + + Args: + other: Scalar value to subtract from. + + Returns: + A new Matrix with each element being the scalar minus the corresponding matrix element. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(2 - A) - ``` + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + print(2 - A) + ``` """ return broadcast_to[dtype](other, self.shape, self.order()) - self - fn __mul__(self, other: Self) raises -> Self: + fn __mul__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: + """ + Multiply two matrices element-wise. + + Args: + other: Matrix to multiply with self. Must be broadcastable to self's shape. + + Returns: + A new Matrix containing the element-wise product. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + var B = Matrix.ones(shape=(4, 4)) + print(A * B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -660,36 +1996,71 @@ struct Matrix[dtype: DType = DType.float64]( ): return _arithmetic_func_matrix_matrix_to_matrix[ dtype, SIMD.__mul__ - ](broadcast_to(self.copy(), other.shape, self.order()), other) + ](broadcast_to(self, other.shape, self.order()), other) else: return _arithmetic_func_matrix_matrix_to_matrix[ dtype, SIMD.__mul__ - ](self, broadcast_to(other.copy(), self.shape, self.order())) + ](self, broadcast_to(other, self.shape, self.order())) + + fn __mul__(self, other: Scalar[dtype]) raises -> Matrix[dtype]: + """ + Multiply matrix by scalar. + + Args: + other: Scalar value to multiply. - fn __mul__(self, other: Scalar[dtype]) raises -> Self: - """Mutiply matrix by scalar. + Returns: + A new Matrix with each element multiplied by the scalar. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(A * 2) - ``` + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + print(A * 2) + ``` """ return self * broadcast_to[dtype](other, self.shape, self.order()) - fn __rmul__(self, other: Scalar[dtype]) raises -> Self: + fn __rmul__(self, other: Scalar[dtype]) raises -> Matrix[dtype]: """ - Right-mul. + Multiply scalar by matrix (right-hand side). + + Args: + other: Scalar value to multiply. + + Returns: + A new Matrix with each element multiplied by the scalar. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(2 * A) - ``` + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + print(2 * A) + ``` """ return broadcast_to[dtype](other, self.shape, self.order()) * self - fn __truediv__(self, other: Self) raises -> Self: + fn __truediv__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: + """ + Divide two matrices element-wise. + + Args: + other: Matrix to divide self by. Must be broadcastable to self's shape. + + Returns: + A new Matrix containing the element-wise division result. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + var B = Matrix.ones(shape=(4, 4)) + print(A / B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -701,25 +2072,76 @@ struct Matrix[dtype: DType = DType.float64]( ): return _arithmetic_func_matrix_matrix_to_matrix[ dtype, SIMD.__truediv__ - ](broadcast_to(self.copy(), other.shape, self.order()), other) + ](broadcast_to(self, other.shape, self.order()), other) else: return _arithmetic_func_matrix_matrix_to_matrix[ dtype, SIMD.__truediv__ - ](self, broadcast_to(other.copy(), self.shape, self.order())) + ](self, broadcast_to(other, self.shape, self.order())) + + fn __truediv__(self, other: Scalar[dtype]) raises -> Matrix[dtype]: + """ + Divide matrix by scalar. + + Args: + other: Scalar value to divide each element of the matrix by. + + Returns: + A new Matrix with each element divided by the scalar. - fn __truediv__(self, other: Scalar[dtype]) raises -> Self: - """Divide matrix by scalar.""" + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + print(A / 2) + ``` + """ return self / broadcast_to[dtype](other, self.shape, order=self.order()) - # Shouldn't we do the operation inplace? - fn __pow__(self, rhs: Scalar[dtype]) raises -> Self: - """Power of items.""" - var result: Self = self.copy() + fn __pow__(self, rhs: Scalar[dtype]) raises -> Matrix[dtype]: + """ + Raise each element of the matrix to the power of `rhs`. + + Args: + rhs: The scalar exponent to which each element of the matrix will be raised. + + Returns: + A new Matrix where each element is self[i] ** rhs. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + print(A ** 2) + ``` + """ + var result: Matrix[dtype] = Matrix[dtype]( + shape=self.shape, order=self.order() + ) for i in range(self.size): result._buf.ptr[i] = self._buf.ptr[i].__pow__(rhs) return result^ - fn __lt__(self, other: Self) raises -> Matrix[DType.bool]: + fn __lt__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[DType.bool]: + """ + Compare two matrices element-wise for less-than. + + Args: + other: Matrix to compare with self. Must be broadcastable to self's shape. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] < other[i, j], else False. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + var B = Matrix.ones(shape=(4, 4)) * 2 + print(A < B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -730,25 +2152,53 @@ struct Matrix[dtype: DType = DType.float64]( self.shape[1] < other.shape[1] ): return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.lt]( - broadcast_to(self.copy(), other.shape, self.order()), other + broadcast_to(self, other.shape, self.order()), other ) else: return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.lt]( - self, broadcast_to(other.copy(), self.shape, self.order()) + self, broadcast_to(other, self.shape, self.order()) ) fn __lt__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: - """Matrix less than scalar. + """ + Compare each element of the matrix to a scalar for less-than. + + Args: + other: Scalar value to compare. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] < other, else False. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(A < 2) - ``` + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + print(A < 2) + ``` """ return self < broadcast_to[dtype](other, self.shape, self.order()) - fn __le__(self, other: Self) raises -> Matrix[DType.bool]: + fn __le__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[DType.bool]: + """ + Compare two matrices element-wise for less-than-or-equal. + + Args: + other: Matrix to compare with self. Must be broadcastable to self's shape. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] <= other[i, j], else False. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + var B = Matrix.ones(shape=(4, 4)) * 2 + print(A <= B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -759,25 +2209,53 @@ struct Matrix[dtype: DType = DType.float64]( self.shape[1] < other.shape[1] ): return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.le]( - broadcast_to(self.copy(), other.shape, self.order()), other + broadcast_to(self, other.shape, self.order()), other ) else: return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.le]( - self, broadcast_to(other.copy(), self.shape, self.order()) + self, broadcast_to(other, self.shape, self.order()) ) fn __le__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: - """Matrix less than and equal to scalar. + """ + Compare each element of the matrix to a scalar for less-than-or-equal. + + Args: + other: Scalar value to compare. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(A <= 2) - ``` + Returns: + A new Matrix[bool] where each element is True if self[i, j] <= other, else False. + + Example: + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A <= 2) + ``` """ return self <= broadcast_to[dtype](other, self.shape, self.order()) - fn __gt__(self, other: Self) raises -> Matrix[DType.bool]: + fn __gt__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[DType.bool]: + """ + Compare two matrices element-wise for greater-than. + + Args: + other: Matrix to compare with self. Must be broadcastable to self's shape. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] > other[i, j], else False. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + B = Matrix.ones(shape=(4, 4)) * 2 + print(A > B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -788,25 +2266,53 @@ struct Matrix[dtype: DType = DType.float64]( self.shape[1] < other.shape[1] ): return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.gt]( - broadcast_to(self.copy(), other.shape, self.order()), other + broadcast_to(self, other.shape, self.order()), other ) else: return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.gt]( - self, broadcast_to(other.copy(), self.shape, self.order()) + self, broadcast_to(other, self.shape, self.order()) ) fn __gt__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: - """Matrix greater than scalar. + """ + Compare each element of the matrix to a scalar for greater-than. + + Args: + other: Scalar value to compare. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] > other, else False. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(A > 2) - ``` + Example: + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A > 2) + ``` """ return self > broadcast_to[dtype](other, self.shape, self.order()) - fn __ge__(self, other: Self) raises -> Matrix[DType.bool]: + fn __ge__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[DType.bool]: + """ + Compare two matrices element-wise for greater-than-or-equal. + + Args: + other: Matrix to compare with self. Must be broadcastable to self's shape. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] >= other[i, j], else False. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + B = Matrix.ones(shape=(4, 4)) * 2 + print(A >= B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -817,25 +2323,56 @@ struct Matrix[dtype: DType = DType.float64]( self.shape[1] < other.shape[1] ): return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ge]( - broadcast_to(self.copy(), other.shape, self.order()), other + broadcast_to(self, other.shape, self.order()), other ) else: return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ge]( - self, broadcast_to(other.copy(), self.shape, self.order()) + self, broadcast_to(other, self.shape, self.order()) ) fn __ge__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: - """Matrix greater than and equal to scalar. + """ + Compare each element of the matrix to a scalar for greater-than-or-equal. + + Args: + other: Scalar value to compare. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] >= other, else False. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(A >= 2) - ``` + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A >= 2) + ``` """ return self >= broadcast_to[dtype](other, self.shape, self.order()) - fn __eq__(self, other: Self) raises -> Matrix[DType.bool]: + fn __eq__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[DType.bool]: + """ + Compare two matrices element-wise for equality. + + Args: + other: Matrix to compare with self. Must be broadcastable to self's shape. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] == other[i, j], else False. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + var B = Matrix.ones(shape=(4, 4)) + print(A == B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -846,25 +2383,53 @@ struct Matrix[dtype: DType = DType.float64]( self.shape[1] < other.shape[1] ): return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.eq]( - broadcast_to(self.copy(), other.shape, self.order()), other + broadcast_to(self, other.shape, self.order()), other ) else: return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.eq]( - self, broadcast_to(other.copy(), self.shape, self.order()) + self, broadcast_to(other, self.shape, self.order()) ) fn __eq__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: - """Matrix less than and equal to scalar. + """ + Compare each element of the matrix to a scalar for equality. + + Args: + other: Scalar value to compare. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] == other, else False. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(A == 2) - ``` + Example: + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A == 2) + ``` """ return self == broadcast_to[dtype](other, self.shape, self.order()) - fn __ne__(self, other: Self) raises -> Matrix[DType.bool]: + fn __ne__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[DType.bool]: + """ + Compare two matrices element-wise for inequality. + + Args: + other: Matrix to compare with self. Must be broadcastable to self's shape. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] != other[i, j], else False. + + Raises: + Error: If the shapes are not compatible for broadcasting. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 4)) + var B = Matrix.ones(shape=(4, 4)) + print(A != B) + ``` + """ if (self.shape[0] == other.shape[0]) and ( self.shape[1] == other.shape[1] ): @@ -875,164 +2440,406 @@ struct Matrix[dtype: DType = DType.float64]( self.shape[1] < other.shape[1] ): return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ne]( - broadcast_to(self.copy(), other.shape, self.order()), other + broadcast_to(self, other.shape, self.order()), other ) else: return _logic_func_matrix_matrix_to_matrix[dtype, SIMD.ne]( - self, broadcast_to(other.copy(), self.shape, self.order()) + self, broadcast_to(other, self.shape, self.order()) ) fn __ne__(self, other: Scalar[dtype]) raises -> Matrix[DType.bool]: - """Matrix less than and equal to scalar. + """ + Compare each element of the matrix to a scalar for inequality. + + Args: + other: Scalar value to compare. + + Returns: + A new Matrix[bool] where each element is True if self[i, j] != other, else False. - ```mojo - from numojo import Matrix - A = Matrix.ones(shape=(4, 4)) - print(A != 2) - ``` + Example: + ```mojo + from numojo import Matrix + A = Matrix.ones(shape=(4, 4)) + print(A != 2) + ``` """ return self != broadcast_to[dtype](other, self.shape, self.order()) - fn __matmul__(self, other: Self) raises -> Self: + fn __matmul__(self, other: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: + """ + Matrix multiplication using the @ operator. + + Args: + other: The matrix to multiply with self. + + Returns: + A new Matrix containing the result of matrix multiplication. + + Raises: + Error: If the shapes are not compatible for matrix multiplication. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.ones(shape=(4, 3)) + var B = Matrix.ones(shape=(3, 2)) + print(A @ B) + ``` + """ return numojo.linalg.matmul(self, other) - # ===-------------------------------------------------------------------===# - # Core methods - # ===-------------------------------------------------------------------===# + # # ===-------------------------------------------------------------------===# + # # Core methods + # # ===-------------------------------------------------------------------===# + # FIXME: These return types be Scalar[DType.bool] or Matrix[DType.bool] instead to match numpy. Fix the docstring examples too. fn all(self) -> Scalar[dtype]: """ - Test whether all array elements evaluate to True. + Returns True if all elements of the matrix evaluate to True. + + Returns: + Scalar[dtype]: True if all elements are True, otherwise False. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.fromlist(List[Float64](1, 1, 1, 1, 1), (5, 1)) + print(A.all()) # Outputs: True + var B = Matrix.fromlist(List[Float64](1, 0, 2, 3, 4), (5, 1)) + print(B.all()) # Outputs: False + ``` """ return numojo.logic.all(self) - fn all(self, axis: Int) raises -> Self: + fn all(self, axis: Int) raises -> Matrix[dtype]: """ - Test whether all array elements evaluate to True along axis. + Returns a matrix indicating whether all elements along the specified axis evaluate to True. + + Args: + axis: The axis along which to perform the test. + + Returns: + Matrix[dtype]: Matrix of boolean values for each slice along the axis. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.fromlist( + List[Float64](1, 1, 1, 0, 1, 3), (2, 3) + ) + print(A.all(axis=0)) # Outputs: [[0, 1, 1]] + print(A.all(axis=1)) # Outputs: [[1], [0]] + ``` """ - return numojo.logic.all(self, axis=axis) + return numojo.logic.all[dtype](self, axis=axis) fn any(self) -> Scalar[dtype]: """ - Test whether any array elements evaluate to True. + Returns True if any element of the matrix evaluates to True. + + Returns: + Scalar[dtype]: True if any element is True, otherwise False. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.fromlist(List[Float64](0, 0, 0, 0, 0), (5, 1)) + print(A.any()) # Outputs: False + var B = Matrix.fromlist(List[Float64](0, 2, 0, 0, 0), (5, 1)) + print(B.any()) # Outputs: True + ``` """ return numojo.logic.any(self) - fn any(self, axis: Int) raises -> Self: + fn any(self, axis: Int) raises -> Matrix[dtype]: """ - Test whether any array elements evaluate to True along axis. + Returns a matrix indicating whether any element along the specified axis evaluates to True. + + Args: + axis: The axis along which to perform the test. + + Returns: + Matrix[dtype]: Matrix of boolean values for each slice along the axis. """ return numojo.logic.any(self, axis=axis) - fn argmax(self) raises -> Scalar[DType.index]: + fn argmax(self) raises -> Scalar[DType.int]: """ - Index of the max. It is first flattened before sorting. + Returns the index of the maximum element in the flattened matrix. + + Returns: + Scalar[DType.int]: Index of the maximum element. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](1, 3, 2, 5, 4), (5, 1)) + print(A.argmax()) # Outputs: 3 + ``` """ return numojo.math.argmax(self) - fn argmax(self, axis: Int) raises -> Matrix[DType.index]: + fn argmax(self, axis: Int) raises -> Matrix[DType.int]: """ - Index of the max along the given axis. + Returns the indices of the maximum elements along the specified axis. + + Args: + axis: The axis along which to find the maximum. + + Returns: + Matrix[DType.int]: Indices of the maximum elements along the axis. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](1, 3, 2, 5, 4, 6), (2, 3)) + print(A.argmax(axis=0)) # Outputs: [[1, 1, 1]] + print(A.argmax(axis=1)) # Outputs: [[1], [2]] + ``` """ return numojo.math.argmax(self, axis=axis) - fn argmin(self) raises -> Scalar[DType.index]: + fn argmin(self) raises -> Scalar[DType.int]: """ - Index of the min. It is first flattened before sorting. + Returns the index of the minimum element in the flattened matrix. + + Returns: + Scalar[DType.int]: Index of the minimum element. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](3, 1, 4, 2, 5), (5, 1)) + print(A.argmin()) # Outputs: 1 + ``` """ return numojo.math.argmin(self) - fn argmin(self, axis: Int) raises -> Matrix[DType.index]: + fn argmin(self, axis: Int) raises -> Matrix[DType.int]: """ - Index of the min along the given axis. + Returns the indices of the minimum elements along the specified axis. + + Args: + axis: The axis along which to find the minimum. + + Returns: + Matrix[DType.int]: Indices of the minimum elements along the axis. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](3, 1, 4, 2, 5, 0), (2, 3)) + print(A.argmin(axis=0)) # Outputs: [[1, 1, 1]] + print(A.argmin(axis=1)) # Outputs: [[1], [2]] + ``` """ return numojo.math.argmin(self, axis=axis) - fn argsort(self) raises -> Matrix[DType.index]: + fn argsort(self) raises -> Matrix[DType.int]: """ - Argsort the Matrix. It is first flattened before sorting. + Returns the indices that would sort the flattened matrix. + + Returns: + Matrix[DType.int]: Indices that sort the flattened matrix. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](3, 1, 4, 2), (4, 1)) + print(A.argsort()) # Outputs: [[1, 3, 0, 2]] + ``` """ return numojo.math.argsort(self) - fn argsort(self, axis: Int) raises -> Matrix[DType.index]: + fn argsort(self, axis: Int) raises -> Matrix[DType.int]: """ - Argsort the Matrix along the given axis. + Returns the indices that would sort the matrix along the specified axis. + + Args: + axis: The axis along which to sort. + + Returns: + Matrix[DType.int]: Indices that sort the matrix along the axis. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](3, 1, 4, 2, 5, 0), (2, 3)) + print(A.argsort(axis=0)) # Outputs: [[1, 1, 1], [0, 0, 0]] + print(A.argsort(axis=1)) # Outputs: [[1, 3, 0], [2, 0, 1]] + ``` """ - return numojo.math.argsort(self.copy(), axis=axis) + return numojo.math.argsort(self, axis=axis) fn astype[asdtype: DType](self) -> Matrix[asdtype]: """ - Copy of the matrix, cast to a specified type. - """ - var res = Matrix[asdtype]( + Returns a copy of the matrix cast to the specified data type. + + Parameters: + asdtype: The target data type to cast to. + + Returns: + Matrix[asdtype]: A new matrix with elements cast to the specified type. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.fromlist(List[Float32](1.5, 2.5, 3.5), (3, 1)) + var B = A.astype[i8]() + print(B) # Outputs a Matrix[i8] with values [[1], [2], [3]] + ``` + """ + var casted_matrix = Matrix[asdtype]( shape=(self.shape[0], self.shape[1]), order=self.order() ) for i in range(self.size): - res._buf.ptr[i] = self._buf.ptr[i].cast[asdtype]() - return res^ + casted_matrix._buf.ptr[i] = self._buf.ptr[i].cast[asdtype]() + return casted_matrix^ fn cumprod(self) raises -> Matrix[dtype]: """ - Cumprod of flattened matrix. + Compute the cumulative product of all elements in the matrix, flattened into a single dimension. + + Returns: + Matrix[dtype]: A matrix containing the cumulative product of the flattened input. Example: - ```mojo - from numojo import Matrix - var A = Matrix.rand(shape=(100, 100)) - print(A.cumprod()) - ``` + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.cumprod()) + ``` """ - return numojo.math.cumprod(self.copy()) + return numojo.math.cumprod(self) fn cumprod(self, axis: Int) raises -> Matrix[dtype]: """ - Cumprod of Matrix along the axis. + Compute the cumulative product of elements along a specified axis. Args: - axis: 0 or 1. + axis: The axis along which to compute the cumulative product (0 for rows, 1 for columns). + + Returns: + Matrix[dtype]: A matrix containing the cumulative product along the specified axis. Example: - ```mojo - from numojo import Matrix - var A = Matrix.rand(shape=(100, 100)) - print(A.cumprod(axis=0)) - print(A.cumprod(axis=1)) - ``` + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.cumprod(axis=0)) + print(A.cumprod(axis=1)) + ``` """ - return numojo.math.cumprod(self.copy(), axis=axis) + return numojo.math.cumprod(self, axis=axis) fn cumsum(self) raises -> Matrix[dtype]: - return numojo.math.cumsum(self.copy()) + """ + Compute the cumulative sum of all elements in the matrix, flattened into a single dimension. + + Returns: + Matrix[dtype]: A matrix containing the cumulative sum of the flattened input. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.cumsum()) + ``` + """ + return numojo.math.cumsum(self) fn cumsum(self, axis: Int) raises -> Matrix[dtype]: - return numojo.math.cumsum(self.copy(), axis=axis) + """ + Compute the cumulative sum of elements along a specified axis. + + Args: + axis: The axis along which to compute the cumulative sum (0 for rows, 1 for columns). + + Returns: + Matrix[dtype]: A matrix containing the cumulative sum along the specified axis. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.cumsum(axis=0)) + print(A.cumsum(axis=1)) + ``` + """ + return numojo.math.cumsum(self, axis=axis) fn fill(self, fill_value: Scalar[dtype]): """ - Fill the matrix with value. + Fill the matrix with the specified value. This method sets every element of the matrix to `fill_value`. + + Args: + fill_value: The value to assign to every element of the matrix. - See also function `mat.creation.full`. + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((3, 3)) + A.fill(5) + print(A) + ``` + + See also: `Matrix.full` """ for i in range(self.size): self._buf.ptr[i] = fill_value - fn flatten(self) -> Self: + # * Make it inplace? + fn flatten(self) -> Matrix[dtype]: """ - Return a flattened copy of the matrix. - """ - var res = Self(shape=(1, self.size), order=self.order()) - memcpy(res._buf.ptr, self._buf.ptr, res.size) + Return a flattened copy of the matrix. This method returns a new matrix containing all elements of the original matrix in a single row (shape (1, size)), preserving the order. + + Returns: + Matrix[dtype]: A new matrix with shape (1, self.size) containing the flattened data. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((2, 3)) + print(A.flatten()) + ``` + """ + var res = Matrix[dtype](shape=(1, self.size), order=self.order()) + memcpy(dest=res._buf.ptr, src=self._buf.ptr, count=res.size) return res^ - fn inv(self) raises -> Self: + fn inv(self) raises -> Matrix[dtype]: """ - Inverse of matrix. + Compute the inverse of the matrix. + + Returns: + Matrix[dtype]: The inverse of the matrix. + + Raises: + Error: If the matrix is not square or not invertible. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((3, 3)) + print(A.inv()) + ``` """ return numojo.linalg.inv(self) fn order(self) -> String: """ - Returns the order. + Return the memory layout order of the matrix. + + Returns: + String: "C" if the matrix is C-contiguous, "F" if F-contiguous. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((3, 3), order="F") + print(A.order()) # "F" + ``` """ var order: String = "F" if self.flags.C_CONTIGUOUS: @@ -1041,13 +2848,39 @@ struct Matrix[dtype: DType = DType.float64]( fn max(self) raises -> Scalar[dtype]: """ - Find max item. It is first flattened before sorting. + Return the maximum element in the matrix. + + The matrix is flattened before finding the maximum. + + Returns: + Scalar[dtype]: The maximum value in the matrix. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((3, 3)) + print(A.max()) + ``` """ return numojo.math.extrema.max(self) - fn max(self, axis: Int) raises -> Self: + fn max(self, axis: Int) raises -> Matrix[dtype]: """ - Find max item along the given axis. + Return the maximum values along the specified axis. + + Args: + axis: The axis along which to compute the maximum (0 for rows, 1 for columns). + + Returns: + Matrix[dtype]: A matrix containing the maximum values along the given axis. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((3, 3)) + print(A.max(axis=0)) # Max of each column + print(A.max(axis=1)) # Max of each row + ``` """ return numojo.math.extrema.max(self, axis=axis) @@ -1055,7 +2888,17 @@ struct Matrix[dtype: DType = DType.float64]( returned_dtype: DType = DType.float64 ](self) raises -> Scalar[returned_dtype]: """ - Calculate the arithmetic average of all items in the Matrix. + Compute the arithmetic mean of all elements in the matrix. + + Returns: + Scalar[returned_dtype]: The mean value of all elements. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.mean()) + ``` """ return numojo.statistics.mean[returned_dtype](self) @@ -1063,51 +2906,121 @@ struct Matrix[dtype: DType = DType.float64]( returned_dtype: DType = DType.float64 ](self, axis: Int) raises -> Matrix[returned_dtype]: """ - Calculate the arithmetic average of a Matrix along the axis. + Compute the arithmetic mean along the specified axis. Args: - axis: 0 or 1. + axis: The axis along which to compute the mean (0 for rows, 1 for columns). + + Returns: + Matrix[returned_dtype]: The mean values along the given axis. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.mean(axis=0)) + print(A.mean(axis=1)) + ``` """ return numojo.statistics.mean[returned_dtype](self, axis=axis) fn min(self) raises -> Scalar[dtype]: """ - Find min item. It is first flattened before sorting. + Return the minimum element in the matrix. + + The matrix is flattened before finding the minimum. + + Returns: + Scalar[dtype]: The minimum value in the matrix. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((3, 3)) + print(A.min()) + ``` """ return numojo.math.extrema.min(self) - fn min(self, axis: Int) raises -> Self: + fn min(self, axis: Int) raises -> Matrix[dtype]: """ - Find min item along the given axis. + Return the minimum values along the specified axis. + + Args: + axis: The axis along which to compute the minimum (0 for rows, 1 for columns). + + Returns: + Matrix[dtype]: The minimum values along the given axis. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((3, 3)) + print(A.min(axis=0)) # Min of each column + print(A.min(axis=1)) # Min of each row + ``` """ return numojo.math.extrema.min(self, axis=axis) fn prod(self) -> Scalar[dtype]: """ - Product of all items in the Matrix. + Compute the product of all elements in the matrix. + + Returns: + Scalar[dtype]: The product of all elements. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.rand(shape=(100, 100)) + print(A.prod()) + ``` """ return numojo.math.prod(self) - fn prod(self, axis: Int) raises -> Self: + fn prod(self, axis: Int) raises -> Matrix[dtype]: """ - Product of items in a Matrix along the axis. + Compute the product of elements along the specified axis. Args: - axis: 0 or 1. + axis: The axis along which to compute the product (0 for rows, 1 for columns). + + Returns: + Matrix[dtype]: The product values along the given axis. Example: - ```mojo - from numojo import Matrix - var A = Matrix.rand(shape=(100, 100)) - print(A.prod(axis=0)) - print(A.prod(axis=1)) - ``` + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.prod(axis=0)) + print(A.prod(axis=1)) + ``` """ return numojo.math.prod(self, axis=axis) - fn reshape(self, shape: Tuple[Int, Int]) raises -> Self: + fn reshape( + self, shape: Tuple[Int, Int], order: String = "C" + ) raises -> Matrix[dtype]: """ - Change shape and size of matrix and return a new matrix. + Return a new matrix with the specified shape containing the same data. + + Args: + shape: Tuple of (rows, columns) specifying the new shape. + order: Memory layout order of the new matrix. "C" for C-contiguous, "F" for F-contiguous. Default is "C". + + Returns: + Matrix[dtype]: A new matrix with the requested shape. + + Raises: + Error: If the total number of elements does not match the original matrix size. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(4, 4)) + var B = A.reshape((2, 8)) + print(B) + ``` """ if shape[0] * shape[1] != self.size: raise Error( @@ -1115,36 +3028,81 @@ struct Matrix[dtype: DType = DType.float64]( "Cannot reshape matrix of size {} into shape ({}, {})." ).format(self.size, shape[0], shape[1]) ) - var res = Self(shape=shape, order="C") - if self.flags.F_CONTIGUOUS: - var temp = self.reorder_layout() - memcpy(res._buf.ptr, temp._buf.ptr, res.size) - res = res.reorder_layout() + var res = Matrix[dtype](shape=shape, order=order) + + if self.flags.C_CONTIGUOUS and order == "F": + for i in range(shape[0]): + for j in range(shape[1]): + var flat_idx = i * shape[1] + j + res._buf[ + j * res.strides[1] + i * res.strides[0] + ] = self._buf[flat_idx] + elif self.flags.F_CONTIGUOUS and order == "C": + var k = 0 + for row in range(self.shape[0]): + for col in range(self.shape[1]): + var val = self._buf.ptr[ + row * self.strides[0] + col * self.strides[1] + ] + var dest_row = Int(k // shape[1]) + var dest_col = k % shape[1] + res._buf.ptr[ + dest_row * res.strides[0] + dest_col * res.strides[1] + ] = val + k += 1 else: - memcpy(res._buf.ptr, self._buf.ptr, res.size) + memcpy(dest=res._buf.ptr, src=self._buf.ptr, count=res.size) return res^ - fn resize(mut self, shape: Tuple[Int, Int]) raises: + # NOTE: not sure if `where` clause works correctly here yet. + fn resize(mut self, shape: Tuple[Int, Int]) raises where own_data == True: """ - Change shape and size of matrix in-place. + Change the shape and size of the matrix in-place. + + Args: + shape: Tuple of (rows, columns) specifying the new shape. + + Raises: + Error: If the new shape requires more elements than the current matrix can hold and memory allocation fails. + + Notes: + - If the new shape is larger, the matrix is reallocated and new elements are zero-initialized. + - If the new shape is smaller, the matrix shape and strides are updated without reallocating memory. + - Only allowed for matrices with own_data=True. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(2, 3)) + A.resize((4, 5)) + print(A) + ``` """ if shape[0] * shape[1] > self.size: - var other = Self(shape=shape) + var other = MatrixBase[dtype, own_data=own_data, origin=origin]( + shape=shape, order=self.order() + ) if self.flags.C_CONTIGUOUS: - memcpy(other._buf.ptr, self._buf.ptr, self.size) + memcpy(dest=other._buf.ptr, src=self._buf.ptr, count=self.size) for i in range(self.size, other.size): other._buf.ptr[i] = 0 else: - var idx = 0 - for i in range(other.size): - other._buf.ptr.store(i, 0.0) - if idx < self.size: - other._buf.ptr[i] = self._buf.ptr[ - (i % self.shape[1]) * self.shape[0] - + (i // self.shape[1]) + var min_rows = min(self.shape[0], shape[0]) + var min_cols = min(self.shape[1], shape[1]) + + for j in range(min_cols): + for i in range(min_rows): + other._buf.ptr[i + j * shape[0]] = self._buf.ptr[ + i + j * self.shape[0] ] - idx += 1 - other = other.reorder_layout() + for i in range(min_rows, shape[0]): + other._buf.ptr[i + j * shape[0]] = 0 + + # Zero the additional columns + for j in range(min_cols, shape[1]): + for i in range(shape[0]): + other._buf.ptr[i + j * shape[0]] = 0 + self = other^ else: self.shape[0] = shape[0] @@ -1156,17 +3114,44 @@ struct Matrix[dtype: DType = DType.float64]( else: self.strides[1] = shape[0] - fn round(self, decimals: Int) raises -> Self: - return numojo.math.rounding.round(self.copy(), decimals=decimals) + fn round(self, decimals: Int) raises -> Matrix[dtype]: + """ + Round each element of the matrix to the specified number of decimals. + + Args: + decimals: Number of decimal places to round to. + + Returns: + Matrix[dtype]: A new matrix with rounded values. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.fromlist(List[Float64](1.12345, 2.67891, 3.14159), (3, 1)) + var B = A.round(2) + print(B) # Outputs a Matrix[Float64] with values [[1.12], [2.68], [3.14]] + ``` + """ + return numojo.math.rounding.round(self, decimals=decimals) fn std[ returned_dtype: DType = DType.float64 ](self, ddof: Int = 0) raises -> Scalar[returned_dtype]: """ - Compute the standard deviation. + Compute the standard deviation of all elements in the matrix. Args: - ddof: Delta degree of freedom. + ddof: Delta degrees of freedom. The divisor used in calculations is N - ddof, where N is the number of elements. + + Returns: + Scalar[returned_dtype]: The standard deviation of the matrix. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.rand(shape=(100, 100)) + print(A.std()) + ``` """ return numojo.statistics.std[returned_dtype](self, ddof=ddof) @@ -1174,79 +3159,168 @@ struct Matrix[dtype: DType = DType.float64]( returned_dtype: DType = DType.float64 ](self, axis: Int, ddof: Int = 0) raises -> Matrix[returned_dtype]: """ - Compute the standard deviation along axis. + Compute the standard deviation along the specified axis. Args: - axis: 0 or 1. - ddof: Delta degree of freedom. + axis: Axis along which to compute the standard deviation (0 for rows, 1 for columns). + ddof: Delta degrees of freedom. The divisor used in calculations is N - ddof, where N is the number of elements along the axis. + + Returns: + Matrix[returned_dtype]: The standard deviation along the given axis. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.rand(shape=(100, 100)) + print(A.std(axis=0)) + print(A.std(axis=1)) + ``` """ return numojo.statistics.std[returned_dtype](self, axis=axis, ddof=ddof) fn sum(self) -> Scalar[dtype]: """ - Sum up all items in the Matrix. + Compute the sum of all elements in the matrix. + + Returns: + Scalar[dtype]: The sum of all elements. Example: - ```mojo - from numojo import Matrix - var A = Matrix.rand(shape=(100, 100)) - print(A.sum()) - ``` + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.sum()) + ``` """ return numojo.math.sum(self) - fn sum(self, axis: Int) raises -> Self: + fn sum(self, axis: Int) raises -> Matrix[dtype]: """ - Sum up the items in a Matrix along the axis. + Compute the sum of elements along the specified axis. Args: - axis: 0 or 1. + axis: Axis along which to sum (0 for rows, 1 for columns). + + Returns: + Matrix[dtype]: The sum along the given axis. Example: - ```mojo - from numojo import Matrix - var A = Matrix.rand(shape=(100, 100)) - print(A.sum(axis=0)) - print(A.sum(axis=1)) - ``` + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.sum(axis=0)) + print(A.sum(axis=1)) + ``` """ return numojo.math.sum(self, axis=axis) fn trace(self) raises -> Scalar[dtype]: """ - Trace of matrix. + Compute the trace of the matrix (sum of diagonal elements). + + Returns: + Scalar[dtype]: The trace value. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.fromlist( + List[Float64](1, 2, 3, 4, 5, 6, 7, 8, 9), (3, 3) + ) + print(A.trace()) # Outputs: 15.0 + ``` """ return numojo.linalg.trace(self) fn issymmetric(self) -> Bool: """ - Transpose of matrix. + Check if the matrix is symmetric (equal to its transpose). + + Returns: + Bool: True if the matrix is symmetric, False otherwise. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](1, 2, 2, 1), (2, 2)) + print(A.issymmetric()) # Outputs: True + var B = Matrix.fromlist(List[Float64](1, 2, 3, 4), (2, 2)) + print(B.issymmetric()) # Outputs: False + ``` """ return issymmetric(self) - fn transpose(self) -> Self: + fn transpose(self) -> Matrix[dtype]: """ - Transpose of matrix. + Return the transpose of the matrix. + + Returns: + Matrix[dtype]: The transposed matrix. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](1, 2, 3, 4), (2, 2)) + print(A.transpose()) # Outputs: [[1, 3], [2, 4]] + ``` """ return transpose(self) - fn reorder_layout(self) raises -> Self: + # TODO: we should only allow this for owndata. not for views, it'll lead to weird origin behaviours. + fn reorder_layout(self) raises -> Matrix[dtype]: """ - Reorder_layout matrix. + Reorder the memory layout of the matrix to match its current order ("C" or "F"). This method returns a new matrix with the same data but stored in the requested memory layout. Only allowed for matrices with own_data=True. + + Returns: + Matrix[dtype]: A new matrix with reordered memory layout. + + Raises: + Error: If the matrix does not have its own data. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand((3, 3), order="F") + var B = A.reorder_layout() + print(B.order()) # Outputs: "F" + ``` """ return reorder_layout(self) - fn T(self) -> Self: + fn T(self) -> Matrix[dtype]: + """ + Return the transpose of the matrix. + + Returns: + Matrix[dtype]: The transposed matrix. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.fromlist(List[Float64](1, 2, 3, 4), (2, 2)) + print(A.T()) # Outputs: [[1, 3], [2, 4]] + ``` + """ return transpose(self) fn variance[ returned_dtype: DType = DType.float64 ](self, ddof: Int = 0) raises -> Scalar[returned_dtype]: """ - Compute the variance. + Compute the variance of all elements in the matrix. Args: - ddof: Delta degree of freedom. + ddof: Delta degrees of freedom. The divisor used in calculations is N - ddof, where N is the number of elements. + + Returns: + Scalar[returned_dtype]: The variance of the matrix. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.variance()) + ``` """ return numojo.statistics.variance[returned_dtype](self, ddof=ddof) @@ -1254,35 +3328,75 @@ struct Matrix[dtype: DType = DType.float64]( returned_dtype: DType = DType.float64 ](self, axis: Int, ddof: Int = 0) raises -> Matrix[returned_dtype]: """ - Compute the variance along axis. + Compute the variance along the specified axis. Args: - axis: 0 or 1. - ddof: Delta degree of freedom. + axis: Axis along which to compute the variance (0 for rows, 1 for columns). + ddof: Delta degrees of freedom. The divisor used in calculations is N - ddof, where N is the number of elements along the axis. + + Returns: + Matrix[returned_dtype]: The variance along the given axis. + + Example: + ```mojo + from numojo import Matrix + var A = Matrix.rand(shape=(100, 100)) + print(A.variance(axis=0)) + print(A.variance(axis=1)) + ``` """ return numojo.statistics.variance[returned_dtype]( self, axis=axis, ddof=ddof ) - # ===-------------------------------------------------------------------===# - # To other data types - # ===-------------------------------------------------------------------===# + # # ===-------------------------------------------------------------------===# + # # To other data types + # # ===-------------------------------------------------------------------===# fn to_ndarray(self) raises -> NDArray[dtype]: """Create `NDArray` from `Matrix`. - It makes a copy of the buffer of the matrix. + Returns a new NDArray with the same shape and data as the Matrix. + The buffer is copied, so changes to the NDArray do not affect the original Matrix. + + Returns: + NDArray[dtype]: A new NDArray containing the same data as the Matrix. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.rand((3, 3)) + var ndarray_A = A.to_ndarray() + print(ndarray_A) + ``` """ var ndarray: NDArray[dtype] = NDArray[dtype]( - shape=List[Int](self.shape[0], self.shape[1]), order="C" + shape=List[Int](self.shape[0], self.shape[1]), order=self.order() ) - memcpy(ndarray._buf.ptr, self._buf.ptr, ndarray.size) + memcpy(dest=ndarray._buf.ptr, src=self._buf.ptr, count=ndarray.size) return ndarray^ - fn to_numpy(self) raises -> PythonObject: - """See `numojo.core.utility.to_numpy`.""" + fn to_numpy(self) raises -> PythonObject where own_data == True: + """ + Convert the Matrix to a NumPy ndarray. + + Returns: + PythonObject: A NumPy ndarray containing the same data as the Matrix. + + Notes: + - The returned NumPy array is a copy of the Matrix data. + - The dtype and memory order are matched as closely as possible. + + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.rand((3, 3)) + var np_A = A.to_numpy() + print(np_A) + ``` + """ try: var np = Python.import_module("numpy") @@ -1317,7 +3431,7 @@ struct Matrix[dtype: DType = DType.float64]( np_dtype = np.uint8 elif dtype == DType.bool: np_dtype = np.bool_ - elif dtype == DType.index: + elif dtype == DType.int: np_dtype = np.int64 var order = "C" if self.flags.C_CONTIGUOUS else "F" @@ -1325,7 +3439,7 @@ struct Matrix[dtype: DType = DType.float64]( var pointer_d = numpyarray.__array_interface__["data"][ 0 ].unsafe_get_as_pointer[dtype]() - memcpy(pointer_d, self._buf.ptr, self.size) + memcpy(dest=pointer_d, src=self._buf.get_ptr(), count=self.size) return numpyarray^ @@ -1339,72 +3453,106 @@ struct Matrix[dtype: DType = DType.float64]( @staticmethod fn full[ - dtype: DType = DType.float64 + datatype: DType = DType.float64 ]( shape: Tuple[Int, Int], - fill_value: Scalar[dtype] = 0, + fill_value: Scalar[datatype] = 0, order: String = "C", - ) -> Matrix[dtype]: - """Return a matrix with given shape and filled value. + ) -> Matrix[datatype]: + """ + Create a matrix of the specified shape, filled with the given value. + + Args: + shape: Tuple specifying the matrix dimensions (rows, columns). + fill_value: Value to fill every element of the matrix. + order: Memory layout order, "C" (row-major) or "F" (column-major). + + Returns: + Matrix[datatype]: Matrix filled with `fill_value`. Example: - ```mojo - from numojo import Matrix - var A = Matrix.full(shape=(10, 10), fill_value=100) - ``` + ```mojo + from numojo.prelude import * + var A = Matrix.full[f32](shape=(10, 10), fill_value=100) + ``` """ - var matrix = Matrix[dtype](shape, order) + var matrix = Matrix[datatype](shape, order) for i in range(shape[0] * shape[1]): - matrix._buf.ptr.store(i, fill_value) + matrix._buf.store[width=1](i, fill_value) return matrix^ @staticmethod fn zeros[ - dtype: DType = DType.float64 - ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[dtype]: - """Return a matrix with given shape and filled with zeros. + datatype: DType = DType.float64 + ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[datatype]: + """ + Create a matrix of the specified shape, filled with zeros. + + Args: + shape: Tuple specifying the matrix dimensions (rows, columns). + order: Memory layout order, "C" (row-major) or "F" (column-major). + + Returns: + Matrix[datatype]: Matrix filled with zeros. Example: - ```mojo - from numojo import Matrix - var A = Matrix.ones(shape=(10, 10)) - ``` + ```mojo + from numojo.prelude import * + var A = Matrix.zeros[i32](shape=(10, 10)) + ``` """ - var M = Matrix[dtype](shape, order) - memset_zero(M._buf.ptr, M.size) - return M^ + var res = Matrix[datatype](shape, order) + memset_zero(res._buf.ptr, res.size) + return res^ @staticmethod fn ones[ - dtype: DType = DType.float64 - ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[dtype]: - """Return a matrix with given shape and filled with ones. + datatype: DType = DType.float64 + ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[datatype]: + """ + Create a matrix of the specified shape, filled with ones. + + Args: + shape: Tuple specifying the matrix dimensions (rows, columns). + order: Memory layout order, "C" (row-major) or "F" (column-major). + + Returns: + Matrix[datatype]: Matrix filled with ones. Example: - ```mojo - from numojo import Matrix - var A = Matrix.ones(shape=(10, 10)) - ``` + ```mojo + from numojo.prelude import * + var A = Matrix.ones[f64](shape=(10, 10)) + ``` """ - return Matrix.full[dtype](shape=shape, fill_value=1) + return Matrix.full[datatype](shape=shape, fill_value=1) @staticmethod fn identity[ - dtype: DType = DType.float64 - ](len: Int, order: String = "C") -> Matrix[dtype]: - """Return an identity matrix with given size. + datatype: DType = DType.float64 + ](len: Int, order: String = "C") -> Matrix[datatype]: + """ + Create an identity matrix of the given size. + + Args: + len: Size of the identity matrix (number of rows and columns). + order: Memory layout order, "C" (row-major) or "F" (column-major). + + Returns: + Matrix[datatype]: Identity matrix of shape (len, len). Example: - ```mojo - from numojo import Matrix - var A = Matrix.identity(12) - ``` + ```mojo + from numojo.prelude import * + var A = Matrix.identity[f16](12) + print(A) + ``` """ - var matrix = Matrix.zeros[dtype]((len, len), order) + var matrix = Matrix.zeros[datatype]((len, len), order) for i in range(len): matrix._buf.ptr.store( i * matrix.strides[0] + i * matrix.strides[1], 1 @@ -1413,51 +3561,59 @@ struct Matrix[dtype: DType = DType.float64]( @staticmethod fn rand[ - dtype: DType = DType.float64 - ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[dtype]: - """Return a matrix with random values uniformed distributed between 0 and 1. + datatype: DType = DType.float64 + ](shape: Tuple[Int, Int], order: String = "C") -> Matrix[datatype]: + """ + Create a matrix of the specified shape, filled with random values uniformly distributed between 0 and 1. - Example: - ```mojo - from numojo import Matrix - var A = Matrix.rand((12, 12)) - ``` + Args: + shape: Tuple specifying the matrix dimensions (rows, columns). + order: Memory layout order, "C" (row-major) or "F" (column-major). - Parameters: - dtype: The data type of the NDArray elements. + Returns: + Matrix[datatype]: Matrix filled with random values. - Args: - shape: The shape of the Matrix. - order: The order of the Matrix. "C" or "F". + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.rand[f64]((12, 12)) + ``` """ - var result = Matrix[dtype](shape, order) + var result = Matrix[datatype](shape, order) for i in range(result.size): - result._buf.ptr.store(i, random_float64(0, 1).cast[dtype]()) + result._buf.ptr.store(i, random_float64(0, 1).cast[datatype]()) return result^ @staticmethod fn fromlist[ - dtype: DType + datatype: DType = DType.float64 ]( - object: List[Scalar[dtype]], + object: List[Scalar[datatype]], shape: Tuple[Int, Int] = (0, 0), order: String = "C", - ) raises -> Matrix[dtype]: - """Create a matrix from a 1-dimensional list into given shape. + ) raises -> Matrix[datatype]: + """ + Create a matrix from a 1-dimensional list and reshape to the given shape. + + Args: + object: List of values to populate the matrix. + shape: Tuple specifying the matrix dimensions (rows, columns). If not provided, creates a row vector. + order: Memory layout order, "C" (row-major) or "F" (column-major). - If no shape is passed, the return matrix will be a row vector. + Returns: + Matrix[datatype]: Matrix containing the values from the list. Example: - ```mojo - from numojo import Matrix - fn main() raises: - print(Matrix.fromlist(List[Float64](1, 2, 3, 4, 5), (5, 1))) - ``` + ```mojo + from numojo.prelude import * + var a = Matrix.fromlist(List[Float64](1, 2, 3, 4, 5), (5, 1)) + print(a) + ``` """ if (shape[0] == 0) and (shape[1] == 0): - var M = Matrix[dtype](shape=(1, len(object))) - memcpy(M._buf.ptr, object.unsafe_ptr(), M.size) + var M = Matrix[datatype](shape=(1, len(object))) + memcpy(dest=M._buf.ptr, src=object.unsafe_ptr(), count=M.size) return M^ if shape[0] * shape[1] != len(object): @@ -1465,48 +3621,49 @@ struct Matrix[dtype: DType = DType.float64]( "The input has {} elements, but the target has the shape {}x{}" ).format(len(object), shape[0], shape[1]) raise Error(message) - var M = Matrix[dtype](shape=shape, order="C") - memcpy(M._buf.ptr, object.unsafe_ptr(), M.size) + var M = Matrix[datatype](shape=shape, order="C") + memcpy(dest=M._buf.ptr, src=object.unsafe_ptr(), count=M.size) if order == "F": M = M.reorder_layout() return M^ @staticmethod fn fromstring[ - dtype: DType = DType.float64 + datatype: DType = DType.float64 ]( text: String, shape: Tuple[Int, Int] = (0, 0), order: String = "C" - ) raises -> Matrix[dtype]: - """Matrix initialization from string representation of an matrix. - - Comma, right brackets, and whitespace are treated as seperators of numbers. - Digits, underscores, and minus signs are treated as a part of the numbers. - - If now shape is passed, the return matrix will be a row vector. + ) raises -> Matrix[datatype]: + """ + Create a Matrix from a string representation of its elements. - Example: - ```mojo - from numojo.prelude import * - from numojo import Matrix - fn main() raises: - var A = Matrix.fromstring[f32]( - "1 2 .3 4 5 6.5 7 1_323.12 9 10, 11.12, 12 13 14 15 16", (4, 4)) - ``` - ```console - [[1.0 2.0 0.30000001192092896 4.0] - [5.0 6.5 7.0 1323.1199951171875] - [9.0 10.0 11.119999885559082 12.0] - [13.0 14.0 15.0 16.0]] - Size: 4x4 DType: float32 - ``` + The input string should contain numbers separated by commas, right brackets, or whitespace. Digits, underscores, decimal points, and minus signs are treated as part of numbers. If no shape is provided, the returned matrix will be a row vector. Args: - text: String representation of a matrix. - shape: Shape of the matrix. - order: Order of the matrix. "C" or "F". - """ + text: String containing the matrix elements. + shape: Tuple specifying the matrix dimensions (rows, columns). If not provided, creates a row vector. + order: Memory layout order, "C" (row-major) or "F" (column-major). + + Returns: + Matrix[datatype]: Matrix constructed from the string data. - var data = List[Scalar[dtype]]() + Example: + ```mojo + from numojo.prelude import * + var A = Matrix.fromstring[f32]("1 2 .3 4 5 6.5 7 1_323.12 9 10, 11.12, 12 13 14 15 16", (4, 4)) + print(A) + ``` + + Output: + ``` + [[1.0 2.0 0.30000001192092896 4.0] + [5.0 6.5 7.0 1323.1199951171875] + [9.0 10.0 11.119999885559082 12.0] + [13.0 14.0 15.0 16.0]] + Size: 4x4 datatype: float32 + ``` + """ + + var data = List[Scalar[datatype]]() var bytes = text.as_bytes() var number_as_str: String = "" var size = shape[0] * shape[1] @@ -1520,7 +3677,7 @@ struct Matrix[dtype: DType = DType.float64]( ): number_as_str = number_as_str + chr(Int(b)) if i == len(bytes) - 1: # Last byte - var number = atof(number_as_str).cast[dtype]() + var number = atof(number_as_str).cast[datatype]() data.append(number) # Add the number to the data buffer number_as_str = "" # Clean the number cache if ( @@ -1529,7 +3686,7 @@ struct Matrix[dtype: DType = DType.float64]( or (chr(Int(b)) == " ") ): if number_as_str != "": - var number = atof(number_as_str).cast[dtype]() + var number = atof(number_as_str).cast[datatype]() data.append(number) # Add the number to the data buffer number_as_str = "" # Clean the number cache @@ -1543,91 +3700,153 @@ struct Matrix[dtype: DType = DType.float64]( ).format(len(data), shape[0], shape[1]) raise Error(message) - var result = Matrix[dtype](shape=shape) + var result = Matrix[datatype](shape=shape) for i in range(len(data)): result._buf.ptr[i] = data[i] return result^ -# ===-----------------------------------------------------------------------===# -# MatrixIter struct -# ===-----------------------------------------------------------------------===# +# # ===-----------------------------------------------------------------------===# +# # MatrixIter struct +# # ===-----------------------------------------------------------------------===# -# ! Should the iterator be mutable or not? struct _MatrixIter[ is_mutable: Bool, //, - lifetime: Origin[is_mutable], dtype: DType, + matrix_origin: MutOrigin, + iterator_origin: Origin[is_mutable], forward: Bool = True, -](Copyable, Movable): - """Iterator for Matrix. +](ImplicitlyCopyable, Movable): + """ + Iterator for Matrix that yields row views. + + This struct provides iteration over the rows of a Matrix, returning a MatrixView for each row. It supports both forward and backward iteration. Parameters: - is_mutable: Whether the iterator is mutable. - lifetime: The lifetime of the underlying Matrix data. - dtype: The data type of the item. - forward: The iteration direction. `False` is backwards. + is_mutable: Whether the iterator allows mutable access to the matrix. + dtype: The data type of the matrix elements. + matrix_origin: The origin of the underlying Matrix data. + iterator_origin: The origin of the iterator itself. + forward: The iteration direction. If True, iterates forward; if False, iterates backward. """ + comptime Element = MatrixView[dtype, Self.matrix_origin] + """The type of elements yielded by the iterator (MatrixView). """ + var index: Int - var matrix: Matrix[dtype] - var length: Int + """Current index in the iteration.""" + + var matrix_ptr: Pointer[ + MatrixBase[dtype, own_data=True, origin = Self.matrix_origin], + Self.iterator_origin, + ] + """Pointer to the source Matrix being iterated over.""" fn __init__( out self, - matrix: Matrix[dtype], - length: Int, + index: Int, + src: Pointer[ + MatrixBase[dtype, own_data=True, origin = Self.matrix_origin], + Self.iterator_origin, + ], ): - self.index = 0 if forward else length - self.length = length - self.matrix = matrix.copy() + """Initialize the iterator. + + Args: + index: The starting index for iteration. + src: Pointer to the source Matrix. + """ + self.index = index + self.matrix_ptr = src - fn __iter__(self) -> Self: + @always_inline + fn __iter__(ref self) -> Self: + """Return a copy of the iterator for iteration protocol.""" return self.copy() - fn __next__(mut self) raises -> Matrix[dtype]: + @always_inline + fn __has_next__(self) -> Bool: + """Check if there are more rows to iterate over. + + Returns: + Bool: True if there are more rows to iterate, False otherwise. + """ + + @parameter + if Self.forward: + return self.index < self.matrix_ptr[].shape[0] + else: + return self.index > 0 + + fn __next__( + mut self, + ) raises -> MatrixView[dtype, MutOrigin.cast_from[Self.iterator_origin]]: + """Return a view of the next row. + + Returns: + MatrixView: A view representing the next row in the iteration. + """ + @parameter - if forward: + if Self.forward: var current_index = self.index self.index += 1 - return self.matrix[current_index] + return self.matrix_ptr[].get(current_index) else: - var current_index = self.index + var current_idx = self.index self.index -= 1 - return self.matrix[current_index] + return self.matrix_ptr[].get(current_idx) @always_inline - fn __has_next__(self) -> Bool: - @parameter - if forward: - return self.index < self.length - else: - return self.index > 0 + fn bounds(self) -> Tuple[Int, Optional[Int]]: + """Return the iteration bounds. + + Returns: + Tuple[Int, Optional[Int]]: Number of remaining rows and an optional value of the same. + """ + var remaining_rows: Int - fn __len__(self) -> Int: @parameter - if forward: - return self.length - self.index + if Self.forward: + remaining_rows = self.matrix_ptr[].shape[0] - self.index else: - return self.index + remaining_rows = self.index + + return (remaining_rows, {remaining_rows}) -# ===-----------------------------------------------------------------------===# -# Backend fucntions using SMID functions -# ===-----------------------------------------------------------------------===# +# # ===-----------------------------------------------------------------------===# +# # Backend fucntions using SMID functions +# # ===-----------------------------------------------------------------------===# +# TODO: we can move the checks in these functions to the caller functions to avoid redundant checks. fn _arithmetic_func_matrix_matrix_to_matrix[ dtype: DType, simd_func: fn[type: DType, simd_width: Int] ( SIMD[type, simd_width], SIMD[type, simd_width] ) -> SIMD[type, simd_width], -](A: Matrix[dtype], B: Matrix[dtype]) raises -> Matrix[dtype]: +](A: MatrixBase[dtype, **_], B: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: """ - Matrix[dtype] & Matrix[dtype] -> Matrix[dtype] + Perform element-wise arithmetic operation between two matrices using a SIMD function. + + Parameters: + dtype: The data type of the matrix elements. + simd_func: A SIMD function that takes two SIMD vectors and returns a SIMD vector, representing the desired arithmetic operation (e.g., addition, subtraction). - For example: `__add__`, `__sub__`, etc. + Args: + A: The first input matrix. + B: The second input matrix. + + Returns: + Matrix[dtype]: A new matrix containing the result of applying the SIMD function element-wise to A and B. + + Raises: + Error: If the matrix orders or shapes do not match. + + Notes: + - Only for internal purposes. """ alias simd_width = simd_width_of[dtype]() if A.order() != B.order(): @@ -1644,11 +3863,11 @@ fn _arithmetic_func_matrix_matrix_to_matrix[ ) ) - var C = Matrix[dtype](shape=A.shape, order=A.order()) + var res = Matrix[dtype](shape=A.shape, order=A.order()) @parameter fn vec_func[simd_width: Int](i: Int): - C._buf.ptr.store( + res._buf.ptr.store( i, simd_func( A._buf.ptr.load[width=simd_width](i), @@ -1657,8 +3876,7 @@ fn _arithmetic_func_matrix_matrix_to_matrix[ ) vectorize[vec_func, simd_width](A.size) - - return C^ + return res^ fn _arithmetic_func_matrix_to_matrix[ @@ -1668,9 +3886,20 @@ fn _arithmetic_func_matrix_to_matrix[ ) -> SIMD[type, simd_width], ](A: Matrix[dtype]) -> Matrix[dtype]: """ - Matrix[dtype] -> Matrix[dtype] + Apply a unary SIMD function element-wise to a matrix. + + Parameters: + dtype: The data type of the matrix elements. + simd_func: A SIMD function that takes a SIMD vector and returns a SIMD vector representing + + Args: + A: Input matrix of type Matrix[dtype]. - For example: `sin`, `cos`, etc. + Returns: + Matrix[dtype]: A new matrix containing the result of applying the SIMD function to each element of the input matrix. + + Notes: + - Only for internal purposes. """ alias simd_width: Int = simd_width_of[dtype]() @@ -1690,9 +3919,29 @@ fn _logic_func_matrix_matrix_to_matrix[ simd_func: fn[type: DType, simd_width: Int] ( SIMD[type, simd_width], SIMD[type, simd_width] ) -> SIMD[DType.bool, simd_width], -](A: Matrix[dtype], B: Matrix[dtype]) raises -> Matrix[DType.bool]: +](A: MatrixBase[dtype, **_], B: MatrixBase[dtype, **_]) raises -> Matrix[ + DType.bool +]: """ - Matrix[dtype] & Matrix[dtype] -> Matrix[bool] + Perform element-wise logical comparison between two matrices using a SIMD function. + + Parameters: + dtype: The data type of the input matrices. + simd_func: A SIMD function that takes two SIMD vectors of dtype and returns a SIMD vector of bools. + + Args: + A: The first input matrix. + B: The second input matrix. + + Returns: + Matrix[DType.bool]: A new matrix of bools containing the result of the element-wise logical comparison. + + Raises: + Error: If the matrix orders or shapes do not match. + + Notes: + - Only for internal purposes. + - The output matrix has the same shape and order as the input matrices. """ alias width = simd_width_of[dtype]() @@ -1714,25 +3963,33 @@ fn _logic_func_matrix_matrix_to_matrix[ var t1 = A.shape[1] var C = Matrix[DType.bool](shape=A.shape, order=A.order()) - @parameter - fn calculate_CC(m: Int): - @parameter - fn vec_func[simd_width: Int](n: Int): - C._store[simd_width]( - m, - n, - simd_func(A._load[simd_width](m, n), B._load[simd_width](m, n)), - ) - - vectorize[vec_func, width](t1) - - parallelize[calculate_CC](t0, t0) + # FIXME: Since the width is calculated for dtype (which could be some int or float type), the same width doesn't apply to DType.bool. Hence the following parallelization/vectorization code doesn't work as expected with misaligned widths. Need to figure out a better way to handle this. Till then, use a simple nested for loop. + # @parameter + # fn calculate_CC(m: Int): + # @parameter + # fn vec_func[simd_width: Int](n: Int): + # C._store[simd_width]( + # m, + # n, + # simd_func(A._load[simd_width](m, n), B._load[simd_width](m, n)), + # ) + + # vectorize[vec_func, width](t1) + + # parallelize[calculate_CC](t0, t0) + # could remove `if` and combine + if A.flags.C_CONTIGUOUS: + for i in range(t0): + for j in range(t1): + C._store[1](i, j, simd_func(A._load[1](i, j), B._load[1](i, j))) + else: + for j in range(t1): + for i in range(t0): + C._store[1](i, j, simd_func(A._load[1](i, j), B._load[1](i, j))) var _t0 = t0 var _t1 = t1 - var _A = ( - A.copy() - ) # ! perhaps remove this explicit copy if we don't need to extend it's lifetime. + var _A = A.copy() var _B = B.copy() return C^ diff --git a/numojo/core/ndarray.mojo b/numojo/core/ndarray.mojo index 7408e7c8..b4831f7d 100644 --- a/numojo/core/ndarray.mojo +++ b/numojo/core/ndarray.mojo @@ -39,7 +39,7 @@ # TODO: Return views that points to the buffer of the raw array. # This requires enhancement of functionalities of traits from Mojo's side. # The data buffer can implement an ArrayData trait (RawData or RefData) -# RawData type is just a wrapper of `UnsafePointer`. +# RawData type is just a wrapper of `LegacyUnsafePointer`. # RefData type has an extra property `indices`: getitem(i) -> A[I[i]]. # TODO: Rename some variables or methods that should not be exposed to users. # TODO: Special checks for 0d array (numojo scalar). @@ -54,7 +54,7 @@ import builtin.math as builtin_math from builtin.type_aliases import Origin from collections.optional import Optional from math import log10 -from memory import UnsafePointer, memset_zero, memcpy +from memory import LegacyUnsafePointer, memset_zero, memcpy from python import PythonObject from sys import simd_width_of from utils import Variant @@ -67,7 +67,7 @@ from numojo.core.flags import Flags from numojo.core.item import Item from numojo.core.ndshape import NDArrayShape from numojo.core.ndstrides import NDArrayStrides -from numojo.core.own_data import OwnData +from numojo.core.data_container import DataContainer from numojo.core.utility import ( _get_offset, _transfer_offset, @@ -84,6 +84,7 @@ from numojo.core.error import ( ValueError, ArithmeticError, ) +from numojo.core.array_methods import ellipsis, newaxis # ===----------------------------------------------------------------------===# # === numojo routines (creation / io / logic) === @@ -121,7 +122,7 @@ struct NDArray[dtype: DType = DType.float64]( Writable, ): # TODO: NDArray[dtype: DType = DType.float64, - # Buffer: Bufferable[dtype] = OwnData[dtype]] + # Buffer: Bufferable[dtype] = DataContainer[dtype]] """The N-dimensional array (NDArray). Parameters: @@ -142,7 +143,7 @@ struct NDArray[dtype: DType = DType.float64]( alias width: Int = simd_width_of[dtype]() """Vector size of the data type.""" - var _buf: OwnData[dtype] + var _buf: DataContainer[dtype] """Data buffer of the items in the NDArray.""" var ndim: Int """Number of Dimensions.""" @@ -175,13 +176,22 @@ struct NDArray[dtype: DType = DType.float64]( Args: shape: Variadic shape. order: Memory order C or F. + + Example: + ```mojo + import numojo as nm + var a = nm.NDArray[nm.f32](nm.Shape(2,3), order="C") + ``` + + Note: + This constructor should not be used by users directly. Use factory functions in `nomojo.routines.creation` module instead. """ self.ndim = shape.ndim self.shape = NDArrayShape(shape) self.size = self.shape.size_of_array() self.strides = NDArrayStrides(shape, order=order) - self._buf = OwnData[dtype](self.size) + self._buf = DataContainer[dtype](self.size) self.flags = Flags( self.shape, self.strides, owndata=True, writeable=True ) @@ -199,6 +209,15 @@ struct NDArray[dtype: DType = DType.float64]( Args: shape: List of shape. order: Memory order C or F. + + Example: + ```mojo + import numojo as nm + var a = nm.NDArray[nm.f32](List[Int](2,3), order="C") + ``` + + Note: + This constructor should not be used by users directly. Use factory functions in `numojo.routines.creation` module instead. """ self = Self(Shape(shape), order) @@ -215,6 +234,15 @@ struct NDArray[dtype: DType = DType.float64]( Args: shape: Variadic List of shape. order: Memory order C or F. + + Example: + ```mojo + from numojo.prelude import * + var A = nm.ComplexNDArray[cf32](VariadicList(2,3,4)) + ``` + + Note: + This constructor should not be used by users directly. Use factory functions in `numojo.routines.creation` module instead. """ self = Self(Shape(shape), order) @@ -226,18 +254,31 @@ struct NDArray[dtype: DType = DType.float64]( strides: List[Int], ) raises: """ - Extremely specific NDArray initializer. + Initialize a NDArray with a specific shape, offset, and strides. Args: - shape: List of shape. - offset: Offset value. - strides: List of strides. + shape: List of integers specifying the shape of the array. + offset: Integer offset into the underlying buffer. + strides: List of integers specifying the stride for each dimension. + + Notes: + - This constructor is intended for advanced use cases requiring precise control over memory layout. + - The resulting array is uninitialized and should be filled before use. + + Example: + ```mojo + from numojo.prelude import * + var shape = List[Int](2, 3) + var offset = 0 + var strides = List[Int](3, 1) + var arr = NDArray[f32](shape, offset, strides) + ``` """ self.shape = NDArrayShape(shape) self.ndim = self.shape.ndim self.size = self.shape.size_of_array() self.strides = NDArrayStrides(strides=strides) - self._buf = OwnData[dtype](self.size) + self._buf = DataContainer[dtype](self.size) memset_zero(self._buf.ptr, self.size) self.flags = Flags( self.shape, self.strides, owndata=True, writeable=True @@ -253,16 +294,19 @@ struct NDArray[dtype: DType = DType.float64]( flags: Flags, ): """ - Constructs an extremely specific array, with value uninitialized. - The properties do not need to be compatible and are not checked. - For example, it can construct a 0-D array (numojo scalar). + Initialize a NDArray with explicit shape, strides, number of dimensions, size, and flags. This constructor creates an uninitialized NDArray with the provided properties. No compatibility checks are performed between shape, strides, ndim, size, or flags. This allows construction of arrays with arbitrary metadata, including 0-D arrays (scalars). Args: - shape: Shape of array. - strides: Strides of array. + shape: Shape of the array. + strides: Strides for each dimension. ndim: Number of dimensions. - size: Size of array. - flags: Flags of array. + size: Total number of elements. + flags: Memory layout flags. + + Notes: + - This constructor is intended for advanced or internal use cases requiring manual control. + - The resulting array is uninitialized; values must be set before use. + - No validation is performed on the consistency of the provided arguments. """ self.shape = shape @@ -270,32 +314,43 @@ struct NDArray[dtype: DType = DType.float64]( self.ndim = ndim self.size = size self.flags = flags - self._buf = OwnData[dtype](self.size) + self._buf = DataContainer[dtype](self.size) self.print_options = PrintOptions() # for creating views (unsafe!) fn __init__( out self, shape: NDArrayShape, - ref buffer: UnsafePointer[Scalar[dtype]], + ref buffer: LegacyUnsafePointer[Scalar[dtype]], offset: Int, strides: NDArrayStrides, ) raises: """ - Initialize an NDArray view with given shape, buffer, offset, and strides. - ***Unsafe!*** This function is currently unsafe. Only for internal use. + Initialize a NDArray view with explicit shape, raw buffers, offset, and strides. + + This constructor creates a view over existing memory buffers for the real and imaginary parts, + using the provided shape, offset, and stride information. It is intended for advanced or internal + use cases where direct control over memory layout is required. + + ***Unsafe!*** This function is unsafe and should only be used internally. The caller is responsible + for ensuring that the buffers are valid and that the shape, offset, and strides are consistent. Args: - shape: Shape of the array. - buffer: Unsafe pointer to the buffer. - offset: Offset value. - strides: Strides of the array. + shape: NDArrayShape specifying the dimensions of the array. + buffer: Unsafe pointer to the buffer containing the real part data. + offset: Integer offset into the buffers. + strides: NDArrayStrides specifying the stride for each dimension. + + Notes: + - No validation is performed on the buffers or metadata. + - The resulting NDArray shares memory with the provided buffers. + - Incorrect usage may lead to undefined behavior. """ self.shape = shape self.strides = strides self.ndim = self.shape.ndim self.size = self.shape.size_of_array() - self._buf = OwnData(ptr=buffer.offset(offset)) + self._buf = DataContainer(ptr=buffer.offset(offset)) self.flags = Flags( self.shape, self.strides, owndata=False, writeable=False ) @@ -314,8 +369,8 @@ struct NDArray[dtype: DType = DType.float64]( self.shape = other.shape self.size = other.size self.strides = other.strides - self._buf = OwnData[dtype](self.size) - memcpy(self._buf.ptr, other._buf.ptr, other.size) + self._buf = DataContainer[dtype](self.size) + memcpy(dest=self._buf.ptr, src=other._buf.ptr, count=other.size) self.flags = Flags( c_contiguous=other.flags.C_CONTIGUOUS, f_contiguous=other.flags.F_CONTIGUOUS, @@ -371,7 +426,7 @@ struct NDArray[dtype: DType = DType.float64]( # fn __getitem__(self, *slices: Variant[Slice, Int]) raises -> Self # Get by mix of slices/ints # # 4. Advanced Indexing - # fn __getitem__(self, indices: NDArray[DType.index]) raises -> Self # Get by index array + # fn __getitem__(self, indices: NDArray[DType.int]) raises -> Self # Get by index array # fn __getitem__(self, indices: List[Int]) raises -> Self # Get by list of indices # fn __getitem__(self, mask: NDArray[DType.bool]) raises -> Self # Get by boolean mask # fn __getitem__(self, mask: List[Bool]) raises -> Self # Get by boolean list @@ -384,6 +439,32 @@ struct NDArray[dtype: DType = DType.float64]( # fn load[width: Int](self, *indices: Int) raises -> SIMD[dtype, width] # Load SIMD at coordinates # ===-------------------------------------------------------------------===# + @always_inline + fn normalize(self, idx: Int, dim: Int) -> Int: + """ + Normalize a potentially negative index to its positive equivalent + within the bounds of the given dimension. + + Args: + idx: The index to normalize. Can be negative to indicate indexing + from the end (e.g., -1 refers to the last element). + dim: The size of the dimension to normalize against. + + Returns: + The normalized index as a non-negative integer. + + Example: + ```mojo + from numojo.prelude import * + var mat = Matrix[f32](shape=(3, 4)) + var norm_idx = mat.normalize(-1, mat.shape[0]) # Normalize -1 to 2 + ``` + """ + var idx_norm = idx + if idx_norm < 0: + idx_norm = dim + idx_norm + return idx_norm + fn _getitem(self, *indices: Int) -> Scalar[dtype]: """ Get item at indices and bypass all boundary checks. @@ -395,17 +476,16 @@ struct NDArray[dtype: DType = DType.float64]( Returns: The element of the array at the indices. - Notes: - This function is unsafe and should be used only on internal use. - Examples: + ```mojo + import numojo as nm + from numojo.prelude import * + var A = nm.ones[f32](nm.Shape(2,3,4)) + print(A._getitem(1,2,3)) + ``` - ```mojo - import numojo as nm - from numojo.prelude import * - var A = nm.ones[f32](nm.Shape(2,3,4)) - print(A._getitem(1,2,3)) - ``` + Notes: + This function is unsafe and should be used only on internal use. """ var index_of_buffer: Int = 0 for i in range(self.ndim): @@ -423,9 +503,6 @@ struct NDArray[dtype: DType = DType.float64]( Returns: The element of the array at the indices. - Notes: - This function is unsafe and should be used only on internal use. - Examples: ```mojo @@ -434,6 +511,9 @@ struct NDArray[dtype: DType = DType.float64]( var A = nm.ones[f32](nm.Shape(2,3,4)) print(A._getitem(List[Int](1,2,3))) ``` + + Notes: + This function is unsafe and should be used only on internal use. """ var index_of_buffer: Int = 0 for i in range(self.ndim): @@ -452,10 +532,10 @@ struct NDArray[dtype: DType = DType.float64]( Examples: - ```console - >>>import numojo - >>>var a = numojo.arange(3)[0] - >>>print(a[]) # gets values of the 0-D array. + ```mojo + import numojo as nm + var a = nm.arange(3)[0] + print(a[]) # gets values of the 0-D array. ```. """ if self.ndim != 0: @@ -551,18 +631,15 @@ struct NDArray[dtype: DType = DType.float64]( IndexError: If `idx` is out of bounds after normalization. Notes: - Order preservation: The resulting copy preserves the source - array's memory order (C or F). Performance fast path: For - C-contiguous arrays the slice is a single contiguous block and is - copied with one `memcpy`. For F-contiguous or arbitrary - strided layouts a unified stride-based element loop is used. - (Future enhancement: return a non-owning view instead of + Order preservation: The resulting copy preserves the source array's memory order (C or F). Performance fast path: For C-contiguous arrays the slice is a single contiguous block and is + copied with one `memcpy`. For F-contiguous or arbitrary strided layouts a unified stride-based element loop is used. (Future enhancement: return a non-owning view instead of copying.) - Examples: + Example: ```mojo import numojo as nm - var a = nm.arange(0, 12, 1).reshape(nm.Shape(3, 4)) + from numojo.prelude import * + var a = nm.arange(0, 12, 1).reshape(Shape(3, 4)) print(a.shape) # (3,4) print(a[1].shape) # (4,) -- 1-D slice print(a[-1].shape) # (4,) -- negative index @@ -611,18 +688,22 @@ struct NDArray[dtype: DType = DType.float64]( # Fast path for C-contiguous arrays if self.flags.C_CONTIGUOUS: var block = self.size // self.shape[0] - memcpy(result._buf.ptr, self._buf.ptr + norm * block, block) + memcpy( + dest=result._buf.ptr, + src=self._buf.ptr + norm * block, + count=block, + ) return result^ - # (F-order or arbitrary stride layout) # TODO: Optimize this further (multi-axis unrolling / smarter linear index without div/mod) - self._copy_first_axis_slice[dtype](self, norm, result) - return result^ + else: + self._copy_first_axis_slice(self, norm, result) + return result^ # perhaps move these to a utility module - fn _copy_first_axis_slice[ - dtype: DType - ](self, src: NDArray[dtype], norm_idx: Int, mut dst: NDArray[dtype]): + fn _copy_first_axis_slice( + self, src: NDArray[dtype], norm_idx: Int, mut dst: NDArray[dtype] + ): """Generic stride-based copier for first-axis slice (works for any layout). """ var out_ndim = dst.ndim @@ -703,7 +784,7 @@ struct NDArray[dtype: DType = DType.float64]( var narr: Self = self[slice_list^] return narr^ - fn _calculate_strides_efficient(self, shape: List[Int]) -> List[Int]: + fn _calculate_strides(self, shape: List[Int]) -> List[Int]: var strides = List[Int](capacity=len(shape)) if self.flags.C_CONTIGUOUS: # C_CONTIGUOUS @@ -744,7 +825,7 @@ struct NDArray[dtype: DType = DType.float64]( - This method supports advanced slicing similar to NumPy's multi-dimensional slicing. - The returned array shares data with the original array if possible. - Examples: + Example: ```mojo import numojo as nm var a = nm.arange(10).reshape(nm.Shape(2, 5)) @@ -801,7 +882,7 @@ struct NDArray[dtype: DType = DType.float64]( ncoefficients.append(1) # only C & F order are supported - var nstrides: List[Int] = self._calculate_strides_efficient( + var nstrides: List[Int] = self._calculate_strides( nshape, ) var narr: Self = Self(offset=noffset, shape=nshape, strides=nstrides) @@ -927,7 +1008,7 @@ struct NDArray[dtype: DType = DType.float64]( ncoefficients.append(1) # only C & F order are supported - var nstrides: List[Int] = self._calculate_strides_efficient( + var nstrides: List[Int] = self._calculate_strides( nshape, ) var narr: Self = Self(offset=noffset, shape=nshape, strides=nstrides) @@ -1177,7 +1258,7 @@ struct NDArray[dtype: DType = DType.float64]( narr = self.__getitem__(slice_list^) return narr^ - fn __getitem__(self, indices: NDArray[DType.index]) raises -> Self: + fn __getitem__(self, indices: NDArray[DType.int]) raises -> Self: """ Get items from 0-th dimension of an ndarray of indices. If the original array is of shape (i,j,k) and @@ -1226,8 +1307,8 @@ struct NDArray[dtype: DType = DType.float64]( # var shape = indices.shape.join(self.shape._pop(0)) var shape = indices.shape.join(self.shape._pop(0)) - var result = NDArray[dtype](shape) - var size_per_item = self.size // self.shape[0] + var result: NDArray[dtype] = NDArray[dtype](shape) + var size_per_item: Int = self.size // self.shape[0] # Fill in the values for i in range(indices.size): @@ -1243,14 +1324,14 @@ struct NDArray[dtype: DType = DType.float64]( " ({})." ).format(self.shape[0]), location=String( - "NDArray.__getitem__(indices: NDArray[DType.index])" + "NDArray.__getitem__(indices: NDArray[DType.int])" ), ) ) memcpy( - result._buf.ptr + i * size_per_item, - self._buf.ptr + indices.item(i) * size_per_item, - size_per_item, + dest=result._buf.ptr + i * size_per_item, + src=self._buf.ptr + indices.item(i) * size_per_item, + count=size_per_item, ) return result^ @@ -1259,7 +1340,7 @@ struct NDArray[dtype: DType = DType.float64]( # TODO: Use trait IntLike when it is supported by Mojo. """ Get items from 0-th dimension of an array. It is an overload of - `__getitem__(self, indices: NDArray[DType.index]) raises -> Self`. + `__getitem__(self, indices: NDArray[DType.int]) raises -> Self`. Args: indices: A list of Int. @@ -1299,7 +1380,7 @@ struct NDArray[dtype: DType = DType.float64]( ```. """ - var indices_array = NDArray[DType.index](shape=Shape(len(indices))) + var indices_array = NDArray[DType.int](shape=Shape(len(indices))) for i in range(len(indices)): (indices_array._buf.ptr + i).init_pointee_copy(indices[i]) @@ -1393,9 +1474,9 @@ struct NDArray[dtype: DType = DType.float64]( for i in range(mask.size): if mask.item(i): memcpy( - result._buf.ptr + offset * size_per_item, - self._buf.ptr + i * size_per_item, - size_per_item, + dest=result._buf.ptr + offset * size_per_item, + src=self._buf.ptr + i * size_per_item, + count=size_per_item, ) offset += 1 @@ -1530,8 +1611,7 @@ struct NDArray[dtype: DType = DType.float64]( ) ) - if index < 0: - index += self.size + index = self.normalize(index, self.size) if (index < 0) or (index >= self.size): raise Error( @@ -1661,8 +1741,7 @@ struct NDArray[dtype: DType = DType.float64]( ```. """ - if index < 0: - index += self.size + index = self.normalize(index, self.size) if index >= self.size: raise Error( @@ -1765,9 +1844,7 @@ struct NDArray[dtype: DType = DType.float64]( var indices_list: List[Int] = List[Int](capacity=self.ndim) for i in range(self.ndim): var idx_i = indices[i] - if idx_i < 0: - idx_i += self.shape[i] - elif idx_i >= self.shape[i]: + if idx_i < 0 or idx_i >= self.shape[i]: raise Error( IndexError( message=String( @@ -1784,6 +1861,7 @@ struct NDArray[dtype: DType = DType.float64]( ), ) ) + idx_i = self.normalize(idx_i, self.shape[i]) indices_list.append(idx_i) # indices_list already built above @@ -1806,7 +1884,7 @@ struct NDArray[dtype: DType = DType.float64]( # fn __setitem__(mut self, *slices: Variant[Slice, Int], val: Self) raises # Set by mix of slices/ints # Index-based Setters - # fn __setitem__(self, indices: NDArray[DType.index], val: NDArray) raises # Set by index array + # fn __setitem__(self, indices: NDArray[DType.int], val: NDArray) raises # Set by index array # fn __setitem__(mut self, mask: NDArray[DType.bool], val: NDArray[dtype]) # Set by boolean mask array # Helper Methods @@ -1891,8 +1969,7 @@ struct NDArray[dtype: DType = DType.float64]( ) var norm = idx - if norm < 0: - norm += self.shape[0] + norm = self.normalize(norm, self.shape[0]) if (norm < 0) or (norm >= self.shape[0]): raise Error( IndexError( @@ -1908,21 +1985,6 @@ struct NDArray[dtype: DType = DType.float64]( ) ) - if val.ndim != self.ndim - 1: - raise Error( - ValueError( - message=String( - "Value ndim {} incompatible with target slice ndim {}." - ).format(val.ndim, self.ndim - 1), - suggestion=String( - "Reshape or expand value to ndim {}." - ).format(self.ndim - 1), - location=String( - "NDArray.__setitem__(idx: Int, val: NDArray)" - ), - ) - ) - if self.shape[1:] != val.shape: var expected_shape: NDArrayShape = self.shape[1:] raise Error( @@ -1943,16 +2005,18 @@ struct NDArray[dtype: DType = DType.float64]( # Fast path for C-contiguous arrays (single block) if self.flags.C_CONTIGUOUS and val.flags.C_CONTIGUOUS: var block = self.size // self.shape[0] - memcpy(self._buf.ptr + norm * block, val._buf.ptr, block) + memcpy( + dest=self._buf.ptr + norm * block, src=val._buf.ptr, count=block + ) return # Generic stride path (F-order or irregular) - self._write_first_axis_slice[dtype](self, norm, val) + self._write_first_axis_slice(self, norm, val) # perhaps move these to a utility module - fn _write_first_axis_slice[ - dtype: DType - ](self, dst: NDArray[dtype], norm_idx: Int, src: NDArray[dtype]): + fn _write_first_axis_slice( + self, dst: NDArray[dtype], norm_idx: Int, src: NDArray[dtype] + ): var out_ndim = src.ndim var total = src.size if total == 0: @@ -1991,10 +2055,11 @@ struct NDArray[dtype: DType = DType.float64]( Examples: - ```console - >>> import numojo - >>> var A = numojo.random.rand[numojo.i16](2, 2, 2) - >>> A[numojo.Item(0, 1, 1)] = 10 + ```mojo + import numojo as nm + from numojo.prelude import * + var A = numojo.random.rand[numojo.i16](2, 2, 2) + A[Item(0, 1, 1)] = 10 ```. """ if index.__len__() != self.ndim: @@ -2029,8 +2094,7 @@ struct NDArray[dtype: DType = DType.float64]( ), ) ) - if index[i] < 0: - index[i] += self.shape[i] + index[i] = self.normalize(index[i], self.shape[i]) var idx: Int = _get_offset(index, self.strides) self._buf.ptr.store(idx, val) @@ -2317,7 +2381,7 @@ struct NDArray[dtype: DType = DType.float64]( # TODO: fix this setter, add bound checks. Not sure about it's use case. fn __setitem__( - mut self, index: NDArray[DType.index], val: NDArray[dtype] + mut self, index: NDArray[DType.int], val: NDArray[dtype] ) raises: """ Returns the items of the array from an array of indices. @@ -2352,7 +2416,7 @@ struct NDArray[dtype: DType = DType.float64]( " each axis separately." ), location=String( - "NDArray.__setitem__(index: NDArray[DType.index], val:" + "NDArray.__setitem__(index: NDArray[DType.int], val:" " NDArray)" ), ) @@ -2370,7 +2434,7 @@ struct NDArray[dtype: DType = DType.float64]( " first dimension ({})." ).format(self.shape[0]), location=String( - "NDArray.__setitem__(index: NDArray[DType.index], val:" + "NDArray.__setitem__(index: NDArray[DType.int], val:" " NDArray)" ), ) @@ -2397,7 +2461,7 @@ struct NDArray[dtype: DType = DType.float64]( " ({})." ).format(self.shape[0]), location=String( - "NDArray.__setitem__(index: NDArray[DType.index]," + "NDArray.__setitem__(index: NDArray[DType.int]," " val: NDArray)" ), ) @@ -3819,7 +3883,7 @@ struct NDArray[dtype: DType = DType.float64]( fn __iter__( self, - ) raises -> _NDArrayIter[__origin_of(self), dtype]: + ) raises -> _NDArrayIter[origin_of(self), dtype]: """ Iterates over elements of the NDArray and return sub-arrays as view. @@ -3843,14 +3907,14 @@ struct NDArray[dtype: DType = DType.float64]( ```. """ - return _NDArrayIter[__origin_of(self), dtype]( + return _NDArrayIter[origin_of(self), dtype]( self, dimension=0, ) fn __reversed__( self, - ) raises -> _NDArrayIter[__origin_of(self), dtype, forward=False]: + ) raises -> _NDArrayIter[origin_of(self), dtype, forward=False]: """ Iterates backwards over elements of the NDArray, returning copied value. @@ -3859,7 +3923,7 @@ struct NDArray[dtype: DType = DType.float64]( A reversed iterator of NDArray elements. """ - return _NDArrayIter[__origin_of(self), dtype, forward=False]( + return _NDArrayIter[origin_of(self), dtype, forward=False]( self, dimension=0, ) @@ -4177,33 +4241,33 @@ struct NDArray[dtype: DType = DType.float64]( vectorize[vectorized_any, self.width](self.size) return result - fn argmax(self) raises -> Scalar[DType.index]: + fn argmax(self) raises -> Scalar[DType.int]: """Returns the indices of the maximum values along an axis. When no axis is specified, the array is flattened. See `numojo.argmax()` for more details. """ return searching.argmax(self) - fn argmax(self, axis: Int) raises -> NDArray[DType.index]: + fn argmax(self, axis: Int) raises -> NDArray[DType.int]: """Returns the indices of the maximum values along an axis. See `numojo.argmax()` for more details. """ return searching.argmax(self, axis=axis) - fn argmin(self) raises -> Scalar[DType.index]: + fn argmin(self) raises -> Scalar[DType.int]: """Returns the indices of the minimum values along an axis. When no axis is specified, the array is flattened. See `numojo.argmin()` for more details. """ return searching.argmin(self) - fn argmin(self, axis: Int) raises -> NDArray[DType.index]: + fn argmin(self, axis: Int) raises -> NDArray[DType.int]: """Returns the indices of the minimum values along an axis. See `numojo.argmin()` for more details. """ return searching.argmin(self, axis=axis) - fn argsort(mut self) raises -> NDArray[DType.index]: + fn argsort(mut self) raises -> NDArray[DType.int]: """ Sort the NDArray and return the sorted indices. See `numojo.argsort()` for more details. @@ -4214,7 +4278,7 @@ struct NDArray[dtype: DType = DType.float64]( return numojo.sorting.argsort(self) - fn argsort(mut self, axis: Int) raises -> NDArray[DType.index]: + fn argsort(mut self, axis: Int) raises -> NDArray[DType.int]: """ Sort the NDArray and return the sorted indices. See `numojo.argsort()` for more details. @@ -4253,17 +4317,12 @@ struct NDArray[dtype: DType = DType.float64]( return numojo.clip(self, a_min, a_max) - fn compress[ - dtype: DType - ](self, condition: NDArray[DType.bool], axis: Int) raises -> Self: + fn compress(self, condition: NDArray[DType.bool], axis: Int) raises -> Self: # TODO: @forFudan try using parallelization for this function """ Return selected slices of an array along given axis. If no axis is provided, the array is flattened before use. - Parameters: - dtype: DType. - Args: condition: 1-D array of booleans that selects which entries to return. If length of condition is less than the size of the array along the @@ -4283,17 +4342,12 @@ struct NDArray[dtype: DType = DType.float64]( return numojo.compress(condition=condition, a=self, axis=axis) - fn compress[ - dtype: DType - ](self, condition: NDArray[DType.bool]) raises -> Self: + fn compress(self, condition: NDArray[DType.bool]) raises -> Self: """ Return selected slices of an array along given axis. If no axis is provided, the array is flattened before use. This is a function ***OVERLOAD***. - Parameters: - dtype: DType. - Args: condition: 1-D array of booleans that selects which entries to return. If length of condition is less than the size of the array along the @@ -4392,23 +4446,20 @@ struct NDArray[dtype: DType = DType.float64]( """ return numojo.math.cumsum[dtype](self.copy(), axis=axis) - fn diagonal[dtype: DType](self, offset: Int = 0) raises -> Self: + fn diagonal(self, offset: Int = 0) raises -> Self: """ Returns specific diagonals. Currently supports only 2D arrays. - Raises: - Error: If the array is not 2D. - Error: If the offset is beyond the shape of the array. - - Parameters: - dtype: Data type of the array. - Args: offset: Offset of the diagonal from the main diagonal. Returns: The diagonal of the NDArray. + + Raises: + Error: If the array is not 2D. + Error: If the offset is beyond the shape of the array. """ return numojo.linalg.diagonal(self, offset=offset) @@ -4438,7 +4489,7 @@ struct NDArray[dtype: DType = DType.float64]( fn iter_along_axis[ forward: Bool = True ](self, axis: Int, order: String = "C") raises -> _NDAxisIter[ - __origin_of(self), dtype, forward + origin_of(self), dtype, forward ]: """ Returns an iterator yielding 1-d array slices along the given axis. @@ -4531,7 +4582,7 @@ struct NDArray[dtype: DType = DType.float64]( ).format(axis, -self.ndim, self.ndim) ) - return _NDAxisIter[__origin_of(self), dtype, forward]( + return _NDAxisIter[origin_of(self), dtype, forward]( self, axis=normalized_axis, order=order, @@ -4540,7 +4591,7 @@ struct NDArray[dtype: DType = DType.float64]( fn iter_over_dimension[ forward: Bool = True ](read self, dimension: Int) raises -> _NDArrayIter[ - __origin_of(self), dtype, forward + origin_of(self), dtype, forward ]: """ Returns an iterator yielding `ndim-1` arrays over the given dimension. @@ -4570,7 +4621,7 @@ struct NDArray[dtype: DType = DType.float64]( ).format(dimension, -self.ndim, self.ndim) ) - return _NDArrayIter[__origin_of(self), dtype, forward]( + return _NDArrayIter[origin_of(self), dtype, forward]( a=self, dimension=normalized_dim, ) @@ -4728,7 +4779,7 @@ struct NDArray[dtype: DType = DType.float64]( return numojo.math.min(self, axis=axis) - fn nditer(self) raises -> _NDIter[__origin_of(self), dtype]: + fn nditer(self) raises -> _NDIter[origin_of(self), dtype]: """ ***Overload*** Return an iterator yielding the array elements according to the memory layout of the array. @@ -4759,7 +4810,7 @@ struct NDArray[dtype: DType = DType.float64]( return self.nditer(order=order) - fn nditer(self, order: String) raises -> _NDIter[__origin_of(self), dtype]: + fn nditer(self, order: String) raises -> _NDIter[origin_of(self), dtype]: """ Return an iterator yielding the array elements according to the order. @@ -4798,7 +4849,7 @@ struct NDArray[dtype: DType = DType.float64]( else: axis = 0 - return _NDIter[__origin_of(self), dtype](a=self, order=order, axis=axis) + return _NDIter[origin_of(self), dtype](a=self, order=order, axis=axis) fn num_elements(self) -> Int: """ @@ -4908,7 +4959,7 @@ struct NDArray[dtype: DType = DType.float64]( if shape.size_of_array() > self.size: var other = Self(shape=shape, order=order) - memcpy(other._buf.ptr, self._buf.ptr, self.size) + memcpy(dest=other._buf.ptr, src=self._buf.ptr, count=self.size) for i in range(self.size, other.size): (other._buf.ptr + i).init_pointee_copy(0) self = other^ @@ -5164,10 +5215,10 @@ struct NDArray[dtype: DType = DType.float64]( fn unsafe_ptr( ref self, - ) -> UnsafePointer[ + ) -> LegacyUnsafePointer[ Scalar[dtype], - mut = Origin(__origin_of(self)).mut, - origin = __origin_of(self), + mut = Origin(origin_of(self)).mut, + origin = origin_of(self), ]: """ Retreive pointer without taking ownership. @@ -5176,9 +5227,9 @@ struct NDArray[dtype: DType = DType.float64]( Unsafe pointer to the data buffer. """ - return self._buf.ptr.origin_cast[ - Origin(__origin_of(self)).mut, __origin_of(self) - ]() + return self._buf.ptr.mut_cast[ + Origin(origin_of(self)).mut + ]().unsafe_origin_cast[origin_of(self)]() fn variance[ returned_dtype: DType = DType.float64 @@ -5320,7 +5371,7 @@ struct _NDArrayIter[ """ var index: Int - var ptr: UnsafePointer[Scalar[dtype]] + var ptr: LegacyUnsafePointer[Scalar[dtype]] var dimension: Int var length: Int var shape: NDArrayShape @@ -5379,7 +5430,7 @@ struct _NDArrayIter[ for offset in range(self.size_of_item): var remainder = offset - var item = Item(ndim=self.ndim, initialized=False) + var item = Item(ndim=self.ndim) for i in range(self.ndim - 1, -1, -1): if i != self.dimension: @@ -5434,7 +5485,7 @@ struct _NDArrayIter[ for offset in range(self.size_of_item): var remainder = offset - var item = Item(ndim=self.ndim, initialized=False) + var item: Item = Item(ndim=self.ndim) for i in range(self.ndim - 1, -1, -1): if i != self.dimension: @@ -5502,7 +5553,7 @@ struct _NDAxisIter[ ``` """ - var ptr: UnsafePointer[Scalar[dtype]] + var ptr: LegacyUnsafePointer[Scalar[dtype]] var axis: Int var order: String var length: Int @@ -5602,7 +5653,7 @@ struct _NDAxisIter[ self.index -= 1 var remainder = current_index * self.size_of_item - var item = Item(ndim=self.ndim, initialized=False) + var item: Item = Item(ndim=self.ndim) if self.order == "C": for i in range(self.ndim): @@ -5628,9 +5679,9 @@ struct _NDAxisIter[ ): # The memory layout is C-contiguous or F-contiguous memcpy( - res._buf.ptr, - self.ptr + _get_offset(item, self.strides), - self.size_of_item, + dest=res._buf.ptr, + src=self.ptr + _get_offset(item, self.strides), + count=self.size_of_item, ) else: @@ -5664,7 +5715,7 @@ struct _NDAxisIter[ var elements: NDArray[dtype] = NDArray[dtype](Shape(self.size_of_item)) var remainder: Int = index * self.size_of_item - var item: Item = Item(ndim=self.ndim, initialized=True) + var item: Item = Item(ndim=self.ndim) if self.order == "C": for i in range(self.ndim): @@ -5690,9 +5741,9 @@ struct _NDAxisIter[ ): # The memory layout is C-contiguous or F-contiguous memcpy( - elements._buf.ptr, - self.ptr + _get_offset(item, self.strides), - self.size_of_item, + dest=elements._buf.ptr, + src=self.ptr + _get_offset(item, self.strides), + count=self.size_of_item, ) else: for j in range(self.size_of_item): @@ -5705,7 +5756,7 @@ struct _NDAxisIter[ fn ith_with_offsets( self, index: Int - ) raises -> Tuple[NDArray[DType.index], NDArray[dtype]]: + ) raises -> Tuple[NDArray[DType.int], NDArray[dtype]]: """ Gets the i-th 1-d array of the iterator and the offsets (in C-order) of its elements. @@ -5717,7 +5768,7 @@ struct _NDAxisIter[ Offsets (in C-order) and elements of the i-th 1-d array of the iterator. """ - var offsets: NDArray[DType.index] = NDArray[DType.index]( + var offsets: NDArray[DType.int] = NDArray[DType.int]( Shape(self.size_of_item) ) var elements: NDArray[dtype] = NDArray[dtype](Shape(self.size_of_item)) @@ -5731,7 +5782,7 @@ struct _NDAxisIter[ ) var remainder: Int = index * self.size_of_item - var item: Item = Item(ndim=self.ndim, initialized=True) + var item: Item = Item(ndim=self.ndim) for i in range(self.axis): item._buf[i] = remainder // self.strides_compatible[i] remainder %= self.strides_compatible[i] @@ -5746,9 +5797,9 @@ struct _NDAxisIter[ ): # The memory layout is C-contiguous memcpy( - elements._buf.ptr, - self.ptr + _get_offset(item, self.strides), - self.size_of_item, + dest=elements._buf.ptr, + src=self.ptr + _get_offset(item, self.strides), + count=self.size_of_item, ) var begin_offset = _get_offset(item, new_strides) for j in range(self.size_of_item): @@ -5759,9 +5810,9 @@ struct _NDAxisIter[ ): # The memory layout is F-contiguous memcpy( - elements._buf.ptr, - self.ptr + _get_offset(item, self.strides), - self.size_of_item, + dest=elements._buf.ptr, + src=self.ptr + _get_offset(item, self.strides), + count=self.size_of_item, ) for j in range(self.size_of_item): (offsets._buf.ptr + j).init_pointee_copy( @@ -5790,7 +5841,7 @@ struct _NDIter[is_mutable: Bool, //, origin: Origin[is_mutable], dtype: DType]( It can be constructed by `NDArray.nditer()` method. """ - var ptr: UnsafePointer[Scalar[dtype]] + var ptr: LegacyUnsafePointer[Scalar[dtype]] var length: Int var ndim: Int var shape: NDArrayShape @@ -5843,7 +5894,7 @@ struct _NDIter[is_mutable: Bool, //, origin: Origin[is_mutable], dtype: DType]( self.index += 1 var remainder = current_index - var indices = Item(ndim=self.ndim, initialized=False) + var indices = Item(ndim=self.ndim) if self.order == "C": for i in range(self.ndim): @@ -5885,7 +5936,7 @@ struct _NDIter[is_mutable: Bool, //, origin: Origin[is_mutable], dtype: DType]( ) var remainder = index - var indices = Item(ndim=self.ndim, initialized=False) + var indices = Item(ndim=self.ndim) if self.order == "C": for i in range(self.ndim): diff --git a/numojo/core/ndshape.mojo b/numojo/core/ndshape.mojo index e4c4b1dc..65831193 100644 --- a/numojo/core/ndshape.mojo +++ b/numojo/core/ndshape.mojo @@ -8,7 +8,8 @@ Implements NDArrayShape type. """ -from memory import UnsafePointer, memcpy, memcmp +from memory import memcpy, memcmp +from memory import LegacyUnsafePointer as UnsafePointer from numojo.core.error import IndexError, ShapeError, ValueError @@ -388,7 +389,7 @@ struct NDArrayShape( """ self.ndim = shape.ndim self._buf = UnsafePointer[Scalar[Self._type]]().alloc(shape.ndim) - memcpy(self._buf, shape._buf, shape.ndim) + memcpy(dest=self._buf, src=shape._buf, count=shape.ndim) for i in range(self.ndim): (self._buf + i).init_pointee_copy(shape._buf[i]) @@ -491,7 +492,7 @@ struct NDArrayShape( """ self.ndim = other.ndim self._buf = UnsafePointer[Scalar[Self._type]]().alloc(other.ndim) - memcpy(self._buf, other._buf, other.ndim) + memcpy(dest=self._buf, src=other._buf, count=other.ndim) fn __del__(deinit self): """ @@ -547,7 +548,7 @@ struct NDArrayShape( @always_inline("nodebug") fn _compute_slice_params( self, slice_index: Slice - ) raises -> (Int, Int, Int): + ) raises -> Tuple[Int, Int, Int]: var n = self.ndim if n == 0: return (0, 1, 0) @@ -771,7 +772,7 @@ struct NDArrayShape( """ var res = Self(ndim=self.ndim, initialized=False) - memcpy(res._buf, self._buf, self.ndim) + memcpy(dest=res._buf, src=self._buf, count=self.ndim) return res fn join(self, *shapes: Self) raises -> Self: @@ -1057,7 +1058,7 @@ struct _ShapeIter[ else: return self.index > 0 - fn __next__(mut self) raises -> Scalar[DType.index]: + fn __next__(mut self) raises -> Scalar[DType.int]: @parameter if forward: var current_index = self.index diff --git a/numojo/core/ndstrides.mojo b/numojo/core/ndstrides.mojo index c8994cb6..9cffb153 100644 --- a/numojo/core/ndstrides.mojo +++ b/numojo/core/ndstrides.mojo @@ -8,11 +8,11 @@ Implements NDArrayStrides type. """ -from memory import UnsafePointer, memcmp, memcpy +from memory import memcmp, memcpy +from memory import LegacyUnsafePointer as UnsafePointer from numojo.core.error import IndexError, ValueError -alias strides = NDArrayStrides alias Strides = NDArrayStrides """An alias of the NDArrayStrides.""" @@ -133,7 +133,7 @@ struct NDArrayStrides( self.ndim = strides.ndim self._buf = UnsafePointer[Scalar[Self._type]]().alloc(self.ndim) - memcpy(self._buf, strides._buf, strides.ndim) + memcpy(dest=self._buf, src=strides._buf, count=strides.ndim) @always_inline("nodebug") fn __init__( @@ -339,7 +339,7 @@ struct NDArrayStrides( """ self.ndim = other.ndim self._buf = UnsafePointer[Scalar[Self._type]]().alloc(other.ndim) - memcpy(self._buf, other._buf, other.ndim) + memcpy(dest=self._buf, src=other._buf, count=other.ndim) fn __del__(deinit self): """ @@ -394,7 +394,7 @@ struct NDArrayStrides( @always_inline("nodebug") fn _compute_slice_params( self, slice_index: Slice - ) raises -> (Int, Int, Int): + ) raises -> Tuple[Int, Int, Int]: """ Compute normalized slice parameters (start, step, length). @@ -644,7 +644,7 @@ struct NDArrayStrides( """ var res = Self(ndim=self.ndim, initialized=False) - memcpy(res._buf, self._buf, self.ndim) + memcpy(dest=res._buf, src=self._buf, count=self.ndim) return res fn swapaxes(self, axis1: Int, axis2: Int) raises -> Self: @@ -683,7 +683,7 @@ struct NDArrayStrides( ) var res = Self(ndim=self.ndim, initialized=False) - memcpy(res._buf, self._buf, self.ndim) + memcpy(dest=res._buf, src=self._buf, count=self.ndim) res[axis1] = self[axis2] res[axis2] = self[axis1] return res^ @@ -957,7 +957,7 @@ struct _StrideIter[ else: return self.index > 0 - fn __next__(mut self) raises -> Scalar[DType.index]: + fn __next__(mut self) raises -> Scalar[DType.int]: @parameter if forward: var current_index = self.index diff --git a/numojo/core/own_data.mojo b/numojo/core/own_data.mojo index 9ccabc9c..67f88115 100644 --- a/numojo/core/own_data.mojo +++ b/numojo/core/own_data.mojo @@ -1,44 +1,23 @@ # ===----------------------------------------------------------------------=== # # Define `OwnData` type -# -# TODO: fields in traits are not supported yet by Mojo -# Currently use `get_ptr()` to get pointer, in future, use `ptr` directly. -# var ptr: UnsafePointer[Scalar[dtype]] -# TODO: implement `Bufferable` trait. # ===----------------------------------------------------------------------=== # -from memory import UnsafePointer -from numojo.core.traits.bufferable import Bufferable +from numojo.core.traits.buffered import Buffered -struct OwnData[dtype: DType]: # TODO: implement `Bufferable` trait - var ptr: UnsafePointer[Scalar[dtype]] +struct OwnData(Buffered, ImplicitlyCopyable, Movable): + """A type to denote arrays that own their data buffer.""" - fn __init__(out self, size: Int): - """ - Allocate given space on memory. - The bytes allocated is `size` * `byte size of dtype`. + fn __init__(out self): + pass - Notes: - `ndarray.flags['OWN_DATA']` should be set as True. - The memory should be freed by `__del__`. - """ - self.ptr = UnsafePointer[Scalar[dtype]]().alloc(size) + @staticmethod + fn is_own_data() -> Bool: + return True - fn __init__(out self, ptr: UnsafePointer[Scalar[dtype]]): - """ - Do not use this if you know what it means. - If the pointer is associated with another array, it might cause - dangling pointer problem. + @staticmethod + fn is_ref_data() -> Bool: + return False - Notes: - `ndarray.flags['OWN_DATA']` should be set as False. - The memory should not be freed by `__del__`. - """ - self.ptr = ptr - - fn __moveinit__(out self, deinit other: Self): - self.ptr = other.ptr - - fn get_ptr(self) -> UnsafePointer[Scalar[dtype]]: - return self.ptr + fn __str__(self) -> String: + return "OwnData" diff --git a/numojo/core/ref_data.mojo b/numojo/core/ref_data.mojo index cabcf074..e268d8c8 100644 --- a/numojo/core/ref_data.mojo +++ b/numojo/core/ref_data.mojo @@ -1,44 +1,29 @@ # ===----------------------------------------------------------------------=== # # Define `RefData` type -# -# TODO: fields in traits are not supported yet by Mojo -# Currently use `get_ptr()` to get pointer, in future, use `ptr` directly. -# var ptr: UnsafePointer[Float16] -# TODO: use parameterized trait. -# Replace `Float16` with `Scalar[dtype]` -# ===----------------------------------------------------------------------=== # - -from memory import UnsafePointer -from numojo.core.traits.bufferable import Bufferable - +# ===----------------------------------------------------------------------=== -struct RefData[is_mutable: Bool, //, origin: Origin[is_mutable]](Bufferable): - var ptr: UnsafePointer[Float16] +from numojo.core.traits.buffered import Buffered - fn __init__(out self, size: Int): - """ - Allocate given space on memory. - The bytes allocated is `size` * `byte size of dtype`. - Notes: - Although it has the lifetime of another array, it owns the data. - `ndarray.flags['OWN_DATA']` should be set as True. - The memory should be freed by `__del__`. - """ - self.ptr = UnsafePointer[Float16]().alloc(size) +struct RefData[is_mutable: Bool, //, origin: Origin[is_mutable]]( + Buffered, ImplicitlyCopyable, Movable +): + """A type to denote arrays that do not own their data. + It is used to represent views of an existing memory. + It records the parametric mutability of the origin array to ensure safety. + The origin array will be kept alive as long as the ref array is alive. + """ - fn __init__(out self, ptr: UnsafePointer[Float16]): - """ - Reads the underlying data of another array. + fn __init__(out self): + pass - Notes: - `ndarray.flags['OWN_DATA']` should be set as False. - The memory should not be freed by `__del__`. - """ - self.ptr = ptr + @staticmethod + fn is_own_data() -> Bool: + return False - fn __moveinit__(out self, deinit other: Self): - self.ptr = other.ptr + @staticmethod + fn is_ref_data() -> Bool: + return True - fn get_ptr(self) -> UnsafePointer[Float16]: - return self.ptr + fn __str__(self) -> String: + return "RefData" diff --git a/numojo/core/traits/bufferable.mojo b/numojo/core/traits/bufferable.mojo deleted file mode 100644 index ca633964..00000000 --- a/numojo/core/traits/bufferable.mojo +++ /dev/null @@ -1,29 +0,0 @@ -# ===----------------------------------------------------------------------=== # -# Define `Bufferable` traits -# ===----------------------------------------------------------------------=== # - -from memory import UnsafePointer - - -trait Bufferable: - """ - Data buffer types that can be used as a container of the underlying buffer. - """ - - # TODO: fields in traits are not supported yet by Mojo - # Currently use `get_ptr()` to get pointer, in future, use `ptr` directly. - # var ptr: UnsafePointer[Float16] - # TODO: use parameterized trait. - # Replace `Float16` with `Scalar[dtype]` - - fn __init__(out self, size: Int): - ... - - fn __init__(out self, ptr: UnsafePointer[Float16]): - ... - - fn __moveinit__(out self, deinit other: Self): - ... - - fn get_ptr(self) -> UnsafePointer[Float16]: - ... diff --git a/numojo/core/traits/buffered.mojo b/numojo/core/traits/buffered.mojo new file mode 100644 index 00000000..0675544f --- /dev/null +++ b/numojo/core/traits/buffered.mojo @@ -0,0 +1,28 @@ +# ===----------------------------------------------------------------------=== # +# Define `Buffered` traits +# ===----------------------------------------------------------------------=== # + + +trait Buffered(ImplicitlyCopyable, Movable): + """A trait to denote whether the data buffer is owned or not. + + There will be two implementations: + 1. `OwnData`: for arrays that own their data buffer. + 2. `RefData`: for arrays that do not own their data buffer. + + The `RefData` type will record the origin of the data to ensure safety. + """ + + fn __init__(out self): + ... + + @staticmethod + fn is_own_data() -> Bool: + ... + + @staticmethod + fn is_ref_data() -> Bool: + ... + + fn __str__(self) -> String: + ... diff --git a/numojo/core/utility.mojo b/numojo/core/utility.mojo index e6c035e7..89585823 100644 --- a/numojo/core/utility.mojo +++ b/numojo/core/utility.mojo @@ -21,7 +21,8 @@ Implements N-DIMENSIONAL ARRAY UTILITY FUNCTIONS from algorithm.functional import vectorize, parallelize from collections import Dict -from memory import UnsafePointer, memcpy +from memory import memcpy +from memory import LegacyUnsafePointer as UnsafePointer from python import Python, PythonObject from sys import simd_width_of @@ -154,8 +155,8 @@ fn _transfer_offset(offset: Int, strides: NDArrayStrides) raises -> Int: The offset of the array of a flipped memory layout. """ - var remainder = offset - var indices = Item(ndim=len(strides), initialized=False) + var remainder: Int = offset + var indices: Item = Item(ndim=len(strides)) for i in range(len(strides)): indices[i] = remainder // strides[i] remainder %= strides[i] @@ -169,7 +170,7 @@ fn _transfer_offset(offset: Int, strides: NDArrayStrides) raises -> Int: fn _traverse_buffer_according_to_shape_and_strides( - mut ptr: UnsafePointer[Scalar[DType.index]], + mut ptr: UnsafePointer[Scalar[DType.int]], shape: NDArrayShape, strides: NDArrayStrides, current_dim: Int = 0, @@ -194,7 +195,7 @@ fn _traverse_buffer_according_to_shape_and_strides( Example: ```console # A is a 2x3x4 array - var I = nm.NDArray[DType.index](nm.Shape(A.size)) + var I = nm.NDArray[DType.int](nm.Shape(A.size)) var ptr = I._buf _traverse_buffer_according_to_shape_and_strides( ptr, A.shape._flip(), A.strides._flip() @@ -399,7 +400,7 @@ fn to_numpy[dtype: DType](array: NDArray[dtype]) raises -> PythonObject: np_dtype = np.int16 elif dtype == DType.int8: np_dtype = np.int8 - elif dtype == DType.index: + elif dtype == DType.int: np_dtype = np.intp elif dtype == DType.uint64: np_dtype = np.uint64 @@ -417,7 +418,7 @@ fn to_numpy[dtype: DType](array: NDArray[dtype]) raises -> PythonObject: var pointer_d = numpyarray.__array_interface__["data"][ 0 ].unsafe_get_as_pointer[dtype]() - memcpy(pointer_d, array.unsafe_ptr(), array.size) + memcpy(dest=pointer_d, src=array.unsafe_ptr(), count=array.size) _ = array return numpyarray^ diff --git a/numojo/prelude.mojo b/numojo/prelude.mojo index 7edd1297..69f475fe 100644 --- a/numojo/prelude.mojo +++ b/numojo/prelude.mojo @@ -22,11 +22,16 @@ from numojo.prelude import * import numojo as nm -from numojo.core.item import Item, item +from numojo.core.item import Item from numojo.core.matrix import Matrix from numojo.core.ndarray import NDArray from numojo.core.ndshape import Shape, NDArrayShape -from numojo.core.complex.complex_simd import ComplexSIMD, CScalar +from numojo.core.complex.complex_simd import ( + ComplexSIMD, + CScalar, + ComplexScalar, + `1j`, +) from numojo.core.complex.complex_ndarray import ComplexNDArray from numojo.core.complex.complex_dtype import ( ci8, diff --git a/numojo/routines/creation.mojo b/numojo/routines/creation.mojo index b4d50ee1..1c5eaf90 100644 --- a/numojo/routines/creation.mojo +++ b/numojo/routines/creation.mojo @@ -42,7 +42,7 @@ from numojo.core.ndarray import NDArray from numojo.core.complex import ComplexScalar from numojo.core.ndshape import NDArrayShape from numojo.core.utility import _get_offset -from numojo.core.own_data import OwnData +from numojo.core.data_container import DataContainer # ===------------------------------------------------------------------------===# @@ -933,7 +933,7 @@ fn geomspace[ ) var base: ComplexSIMD[cdtype] = stop / start var power: Scalar[dtype] = 1 / Scalar[dtype](num - 1) - var r: ComplexSIMD[cdtype] = base**power + var r: ComplexSIMD[cdtype] = base.elem_pow(power) for i in range(num): result.store[1]( i, @@ -947,7 +947,7 @@ fn geomspace[ ) var base: ComplexSIMD[cdtype] = stop / start var power: Scalar[dtype] = 1 / Scalar[dtype](num) - var r: ComplexSIMD[cdtype] = base**power + var r: ComplexSIMD[cdtype] = base.elem_pow(power) for i in range(num): result.store[1]( i, @@ -2556,7 +2556,7 @@ fn array[ np_dtype = np.int16 elif dtype == DType.int8: np_dtype = np.int8 - elif dtype == DType.index: + elif dtype == DType.int: np_dtype = np.intp elif dtype == DType.uint64: np_dtype = np.uint64 @@ -2575,7 +2575,7 @@ fn array[ dtype ]() var A: NDArray[dtype] = NDArray[dtype](array_shape, order) - memcpy[Scalar[dtype]](A._buf.ptr, pointer, A.size) + memcpy[Scalar[dtype]](dest=A._buf.ptr, src=pointer, count=A.size) return A^ @@ -2634,7 +2634,7 @@ fn array[ np_dtype = np.int16 elif dtype == DType.int8: np_dtype = np.int8 - elif dtype == DType.index: + elif dtype == DType.int: np_dtype = np.intp elif dtype == DType.uint64: np_dtype = np.uint64 @@ -2657,8 +2657,10 @@ fn array[ 0 ].unsafe_get_as_pointer[dtype]() var A: ComplexNDArray[cdtype] = ComplexNDArray[cdtype](array_shape, order) - memcpy[Scalar[dtype]](A._re._buf.ptr, pointer, A._re.size) - memcpy[Scalar[dtype]](A._im._buf.ptr, pointer_imag, A._im.size) + memcpy[Scalar[dtype]](dest=A._re._buf.ptr, src=pointer, count=A._re.size) + memcpy[Scalar[dtype]]( + dest=A._im._buf.ptr, src=pointer_imag, count=A._im.size + ) return A^ @@ -2754,7 +2756,7 @@ fn _0darray[ c_contiguous=True, f_contiguous=True, owndata=True, writeable=False ), ) - b._buf = OwnData[dtype](1) + b._buf = DataContainer[dtype](1) b._buf.ptr.init_pointee_copy(val) b.flags.OWNDATA = True return b^ @@ -2779,8 +2781,8 @@ fn _0darray[ c_contiguous=True, f_contiguous=True, owndata=True, writeable=False ), ) - b._re._buf = OwnData[cdtype._dtype](1) - b._im._buf = OwnData[cdtype._dtype](1) + b._re._buf = DataContainer[cdtype._dtype](1) + b._im._buf = DataContainer[cdtype._dtype](1) b._re._buf.ptr.init_pointee_copy(val.re) b._im._buf.ptr.init_pointee_copy(val.im) b.flags.OWNDATA = True diff --git a/numojo/routines/functional.mojo b/numojo/routines/functional.mojo index b5eb8ac5..cafdbc60 100644 --- a/numojo/routines/functional.mojo +++ b/numojo/routines/functional.mojo @@ -82,12 +82,12 @@ fn apply_along_axis[ fn apply_along_axis[ dtype: DType, func1d: fn[dtype_func: DType] (NDArray[dtype_func]) raises -> Scalar[ - DType.index + DType.int ], -](a: NDArray[dtype], axis: Int) raises -> NDArray[DType.index]: +](a: NDArray[dtype], axis: Int) raises -> NDArray[DType.int]: """ Applies a function to a NDArray by axis and reduce that dimension. - The returned data type is DType.index. + The returned data type is DType.int. When the array is 1-d, the returned array will be a 0-d array. Parameters: @@ -105,14 +105,14 @@ fn apply_along_axis[ # The iterator along the axis var iterator = a.iter_along_axis(axis=axis) # The final output array will have 1 less dimension than the input array - var res: NDArray[DType.index] + var res: NDArray[DType.int] if a.ndim == 1: - res = numojo.creation._0darray[DType.index](0) + res = numojo.creation._0darray[DType.int](0) (res._buf.ptr).init_pointee_copy(func1d[dtype](a)) else: - res = NDArray[DType.index](a.shape._pop(axis=axis)) + res = NDArray[DType.int](a.shape._pop(axis=axis)) @parameter fn parallelized_func(i: Int): @@ -221,9 +221,9 @@ fn apply_along_axis[ try: var elements: NDArray[dtype] = func1d[dtype](iterator.ith(i)) memcpy( - result._buf.ptr + i * elements.size, - elements._buf.ptr, - elements.size, + dest=result._buf.ptr + i * elements.size, + src=elements._buf.ptr, + count=elements.size, ) except e: print("Error in parallelized_func", e) @@ -236,7 +236,7 @@ fn apply_along_axis[ fn parallelized_func(i: Int): try: # The indices of the input array in each iteration - var indices: NDArray[DType.index] + var indices: NDArray[DType.int] # The elements of the input array in each iteration var elements: NDArray[dtype] # The array after applied the function @@ -293,9 +293,9 @@ fn apply_along_axis[ var elements: NDArray[dtype] = iterator.ith(i) func1d[dtype](elements) memcpy( - a._buf.ptr + i * elements.size, - elements._buf.ptr, - elements.size, + dest=a._buf.ptr + i * elements.size, + src=elements._buf.ptr, + count=elements.size, ) except e: print("Error in parallelized_func", e) @@ -308,7 +308,7 @@ fn apply_along_axis[ fn parallelized_func(i: Int): try: # The indices of the input array in each iteration - var indices: NDArray[DType.index] + var indices: NDArray[DType.int] # The elements of the input array in each iteration var elements: NDArray[dtype] # The array after applied the function @@ -333,9 +333,9 @@ fn apply_along_axis[ fn apply_along_axis[ dtype: DType, func1d: fn[dtype_func: DType] (NDArray[dtype_func]) raises -> NDArray[ - DType.index + DType.int ], -](a: NDArray[dtype], axis: Int) raises -> NDArray[DType.index]: +](a: NDArray[dtype], axis: Int) raises -> NDArray[DType.int]: """ Applies a function to a NDArray by axis without reducing that dimension. The resulting array will have the same shape as the input array. @@ -357,20 +357,20 @@ fn apply_along_axis[ # The iterator along the axis var iterator = a.iter_along_axis(axis=axis) # The final output array will have the same shape as the input array - var res = NDArray[DType.index](a.shape) + var res = NDArray[DType.int](a.shape) if a.flags.C_CONTIGUOUS and (axis == a.ndim - 1): # The memory layout is C-contiguous @parameter fn parallelized_func_c(i: Int): try: - var elements: NDArray[DType.index] = func1d[dtype]( + var elements: NDArray[DType.int] = func1d[dtype]( iterator.ith(i) ) memcpy( - res._buf.ptr + i * elements.size, - elements._buf.ptr, - elements.size, + dest=res._buf.ptr + i * elements.size, + src=elements._buf.ptr, + count=elements.size, ) except e: print("Error in parallelized_func", e) @@ -383,7 +383,7 @@ fn apply_along_axis[ fn parallelized_func(i: Int): try: # The indices of the input array in each iteration - var indices: NDArray[DType.index] + var indices: NDArray[DType.int] # The elements of the input array in each iteration var elements: NDArray[dtype] # The array after applied the function @@ -391,9 +391,7 @@ fn apply_along_axis[ indices = indices_elements[0].copy() elements = indices_elements[1].copy() - var res_along_axis: NDArray[DType.index] = func1d[dtype]( - elements - ) + var res_along_axis: NDArray[DType.int] = func1d[dtype](elements) for j in range(a.shape[axis]): (res._buf.ptr + Int(indices[j])).init_pointee_copy( diff --git a/numojo/routines/indexing.mojo b/numojo/routines/indexing.mojo index 4e928391..b67a71b5 100644 --- a/numojo/routines/indexing.mojo +++ b/numojo/routines/indexing.mojo @@ -25,7 +25,7 @@ import numojo.core.utility as utility # ===----------------------------------------------------------------------=== # -fn where[ +fn `where`[ dtype: DType ]( mut x: NDArray[dtype], scalar: SIMD[dtype, 1], mask: NDArray[DType.bool] @@ -48,7 +48,7 @@ fn where[ # TODO: do it with vectorization -fn where[ +fn `where`[ dtype: DType ](mut x: NDArray[dtype], y: NDArray[dtype], mask: NDArray[DType.bool]) raises: """ @@ -166,7 +166,7 @@ fn compress[ for offset in range(current_slice.size): var remainder: Int = count - var item: Item = Item(ndim=result.ndim, initialized=False) + var item: Item = Item(ndim=result.ndim) # First along the axis var j = normalized_axis @@ -237,7 +237,7 @@ fn compress[ fn take_along_axis[ dtype: DType, //, ]( - arr: NDArray[dtype], indices: NDArray[DType.index], axis: Int = 0 + arr: NDArray[dtype], indices: NDArray[DType.int], axis: Int = 0 ) raises -> NDArray[dtype]: """ Takes values from the input array along the given axis based on indices. @@ -303,7 +303,7 @@ fn take_along_axis[ # except along the axis var broadcasted_indices: NDArray[ - DType.index + DType.int ] = indices.copy() # make this owned and don't copy if arr.shape != indices.shape: @@ -337,15 +337,15 @@ fn take_along_axis[ indices_slice ] memcpy( - result._buf.ptr + i * result.shape[normalized_axis], - arr_slice_after_applying_indices._buf.ptr, - result.shape[normalized_axis], + dest=result._buf.ptr + i * result.shape[normalized_axis], + src=arr_slice_after_applying_indices._buf.ptr, + count=result.shape[normalized_axis], ) else: # If axis is not the last axis, the data is not contiguous. for i in range(length_of_iterator): - var indices_slice_offsets: NDArray[DType.index] - var indices_slice: NDArray[DType.index] + var indices_slice_offsets: NDArray[DType.int] + var indices_slice: NDArray[DType.int] var indices_slice_offsets_slice = indices_iterator.ith_with_offsets( i ) diff --git a/numojo/routines/io/files.mojo b/numojo/routines/io/files.mojo index f9781b6b..dc774c44 100644 --- a/numojo/routines/io/files.mojo +++ b/numojo/routines/io/files.mojo @@ -79,7 +79,7 @@ fn load[ # return "' Tuple[Matrix[dtype], Matrix[dtype]]: +](A: MatrixBase[dtype, **_]) raises -> Tuple[Matrix[dtype], Matrix[dtype]]: """ Perform LU (lower-upper) decomposition for matrix. """ @@ -226,8 +227,8 @@ fn lu_decomposition[ var n: Int = A.shape[0] # Initiate upper and lower triangular matrices - var U: Matrix[dtype] = Matrix.full[dtype](shape=(n, n), order=A.order()) - var L: Matrix[dtype] = Matrix.full[dtype](shape=(n, n), order=A.order()) + var U: Matrix[dtype] = Matrix.zeros[dtype](shape=(n, n), order=A.order()) + var L: Matrix[dtype] = Matrix.zeros[dtype](shape=(n, n), order=A.order()) # Fill in L and U for i in range(0, n): @@ -305,32 +306,36 @@ fn partial_pivoting[ fn partial_pivoting[ dtype: DType -](var A: Matrix[dtype]) raises -> Tuple[Matrix[dtype], Matrix[dtype], Int]: +](A: MatrixBase[dtype, **_]) raises -> Tuple[Matrix[dtype], Matrix[dtype], Int]: """ Perform partial pivoting for matrix. """ var n = A.shape[0] - var P = Matrix.identity[dtype](n) - if A.flags.F_CONTIGUOUS: - A = A.reorder_layout() - var s: Int = 0 # Number of exchanges, for determinant + # Work on a copy that preserves the original layout + var result = A.create_copy() + var P = Matrix.identity[dtype](n, order=A.order()) + var s: Int = 0 # Number of row exchanges + for col in range(n): - var max_p = abs(A[col, col]) + var max_p = abs(result[col, col]) var max_p_row = col for row in range(col + 1, n): - if abs(A[row, col]) > max_p: - max_p = abs(A[row, col]) + if abs(result[row, col]) > max_p: + max_p = abs(result[row, col]) max_p_row = row - A[col], A[max_p_row] = A[max_p_row], A[col] - P[col], P[max_p_row] = P[max_p_row], P[col] if max_p_row != col: + # Swap rows in result and permutation matrix using element-wise swap + for j in range(n): + var t = result._load(col, j) + result._store(col, j, result._load(max_p_row, j)) + result._store(max_p_row, j, t) + var tp = P._load(col, j) + P._store(col, j, P._load(max_p_row, j)) + P._store(max_p_row, j, tp) s = s + 1 - if A.flags.F_CONTIGUOUS: - A = A.reorder_layout() - P = P.reorder_layout() - return Tuple(A^, P^, s) + return Tuple(result^, P^, s) fn qr[ @@ -376,7 +381,10 @@ fn qr[ if reorder: R = A.reorder_layout() else: - R = A.copy() + R = Matrix.zeros[dtype](shape=(m, n), order="F") + for i in range(m): + for j in range(n): + R._store(i, j, A._load(i, j)) var H = Matrix.zeros[dtype](shape=(m, min_n), order="F") @@ -392,16 +400,25 @@ fn qr[ _apply_householder(H, i, Q, i, i) if reorder: - Q = Q.reorder_layout() + var Q_reordered = Q.reorder_layout() if reduce: - R = R[:inner, :].reorder_layout() + var R_reduced = Matrix.zeros[dtype](shape=(inner, n), order="C") + for i in range(inner): + for j in range(n): + R_reduced._store(i, j, R._load(i, j)) + return Q_reordered^, R_reduced^ else: - R = R.reorder_layout() + var R_reordered = R.reorder_layout() + return Q_reordered^, R_reordered^ else: if reduce: - R = R[:inner, :] - - return Q^, R^ + var R_reduced = Matrix.zeros[dtype](shape=(inner, n), order="F") + for i in range(inner): + for j in range(n): + R_reduced._store(i, j, R._load(i, j)) + return Q^, R_reduced^ + else: + return Q^, R^ # ===----------------------------------------------------------------------=== # diff --git a/numojo/routines/linalg/misc.mojo b/numojo/routines/linalg/misc.mojo index f45776b2..26a53e42 100644 --- a/numojo/routines/linalg/misc.mojo +++ b/numojo/routines/linalg/misc.mojo @@ -13,6 +13,7 @@ from sys import simd_width_of from algorithm import parallelize, vectorize from numojo.core.ndarray import NDArray +from numojo.core.matrix import MatrixBase fn diagonal[ @@ -67,7 +68,9 @@ fn diagonal[ fn issymmetric[ dtype: DType ]( - A: Matrix[dtype], rtol: Scalar[dtype] = 1e-5, atol: Scalar[dtype] = 1e-8 + A: MatrixBase[dtype, **_], + rtol: Scalar[dtype] = 1e-5, + atol: Scalar[dtype] = 1e-8, ) -> Bool: """ Returns True if A is symmetric, False otherwise. diff --git a/numojo/routines/linalg/norms.mojo b/numojo/routines/linalg/norms.mojo index 21fd5f5d..5fdbbdda 100644 --- a/numojo/routines/linalg/norms.mojo +++ b/numojo/routines/linalg/norms.mojo @@ -3,7 +3,7 @@ # ===----------------------------------------------------------------------=== # from numojo.core.ndarray import NDArray -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase from numojo.routines.linalg.decompositions import ( lu_decomposition, partial_pivoting, @@ -121,7 +121,7 @@ fn trace[ fn trace[ dtype: DType -](A: Matrix[dtype], offset: Int = 0) raises -> Scalar[dtype]: +](A: MatrixBase[dtype, **_], offset: Int = 0) raises -> Scalar[dtype]: """ Return the sum along diagonals of the array. diff --git a/numojo/routines/linalg/products.mojo b/numojo/routines/linalg/products.mojo index 64961039..b5ecece8 100644 --- a/numojo/routines/linalg/products.mojo +++ b/numojo/routines/linalg/products.mojo @@ -15,7 +15,7 @@ from memory import memcpy import numojo.routines.math._math_funcs as _mf from numojo.core.ndarray import NDArray from numojo.core.ndshape import NDArrayShape, Shape -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase from numojo.routines.creation import zeros from numojo.routines.math.sums import sum @@ -339,36 +339,37 @@ fn matmul[ for i in range(result.size // result_sub_matrix.size): memcpy( - A_sub_matrix._buf.ptr, - A._buf.ptr + (i * A_sub_matrix.size), - A_sub_matrix.size, + dest=A_sub_matrix._buf.ptr, + src=A._buf.ptr + (i * A_sub_matrix.size), + count=A_sub_matrix.size, ) memcpy( - B_sub_matrix._buf.ptr, - B._buf.ptr + (i * B_sub_matrix.size), - B_sub_matrix.size, + dest=B_sub_matrix._buf.ptr, + src=B._buf.ptr + (i * B_sub_matrix.size), + count=B_sub_matrix.size, ) result_sub_matrix = matmul_2darray(A_sub_matrix, B_sub_matrix) memcpy( - result._buf.ptr + (i * result_sub_matrix.size), - result_sub_matrix._buf.ptr, - result_sub_matrix.size, + dest=result._buf.ptr + (i * result_sub_matrix.size), + src=result_sub_matrix._buf.ptr, + count=result_sub_matrix.size, ) return result^ fn matmul[ dtype: DType -](A: Matrix[dtype], B: Matrix[dtype]) raises -> Matrix[dtype]: +](A: MatrixBase[dtype, **_], B: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: """ Matrix multiplication. Example: ```mojo from numojo import Matrix + from numojo.routines.linalg import matmul var A = Matrix.rand(shape=(1000, 1000)) var B = Matrix.rand(shape=(1000, 1000)) - var result = mat.matmul(A, B) + var result = matmul(A, B) ``` """ @@ -448,8 +449,6 @@ fn matmul[ else: result = matmul(A.reorder_layout(), B) - # var _A = A - # var _B = B return result^ diff --git a/numojo/routines/linalg/solving.mojo b/numojo/routines/linalg/solving.mojo index 6303f7c8..a8342fed 100644 --- a/numojo/routines/linalg/solving.mojo +++ b/numojo/routines/linalg/solving.mojo @@ -13,11 +13,15 @@ Provides: from algorithm import parallelize from numojo.core.ndarray import NDArray +from numojo.core.own_data import OwnData from numojo.core.item import Item import numojo.core.matrix as matrix -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase from numojo.routines.creation import zeros, eye, full -from numojo.routines.linalg.decompositions import partial_pivoting +from numojo.routines.linalg.decompositions import ( + partial_pivoting, + lu_decomposition, +) fn forward_substitution[ @@ -113,7 +117,7 @@ fn inv[dtype: DType](A: NDArray[dtype]) raises -> NDArray[dtype]: return solve(A, I) -fn inv[dtype: DType](A: Matrix[dtype]) raises -> Matrix[dtype]: +fn inv[dtype: DType](A: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: """ Inverse of matrix. """ @@ -208,13 +212,13 @@ fn lstsq[ """Caclulate the OLS estimates. Example: - ```mojo + ```text from numojo import Matrix X = Matrix.rand((1000000, 5)) y = Matrix.rand((1000000, 1)) - print(mat.lstsq(X, y)) + print(lstsq(X, y)) ``` - ```console + ```text [[0.18731374756029967] [0.18821352688798607] [0.18717162200411439] @@ -369,7 +373,7 @@ fn solve[ fn solve[ dtype: DType -](A: Matrix[dtype], Y: Matrix[dtype]) raises -> Matrix[dtype]: +](A: MatrixBase[dtype, **_], Y: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: """ Solve `AX = Y` using LUP decomposition. """ @@ -382,10 +386,12 @@ fn solve[ var A_pivoted_Pair: Tuple[ Matrix[dtype], Matrix[dtype], Int ] = partial_pivoting(A.copy()) - A_pivoted = A_pivoted_Pair[0].copy() - P = A_pivoted_Pair[1].copy() + + var pivoted_A = A_pivoted_Pair[0].copy() + var P = A_pivoted_Pair[1].copy() + var L_U: Tuple[Matrix[dtype], Matrix[dtype]] = lu_decomposition[dtype]( - A_pivoted + pivoted_A ) L = L_U[0].copy() U = L_U[1].copy() @@ -393,9 +399,8 @@ fn solve[ var m: Int = A.shape[0] var n: Int = Y.shape[1] - var Z: Matrix[dtype] = Matrix.full[dtype]((m, n), order=A.order()) - var X: Matrix[dtype] = Matrix.full[dtype]((m, n), order=A.order()) - + var Z: Matrix[dtype] = Matrix.zeros[dtype]((m, n), order=A.order()) + var X: Matrix[dtype] = Matrix.zeros[dtype]((m, n), order=A.order()) var PY = P @ Y @parameter @@ -432,7 +437,7 @@ fn solve[ fn solve_lu[ dtype: DType -](A: Matrix[dtype], Y: Matrix[dtype]) raises -> Matrix[dtype]: +](A: MatrixBase[dtype, **_], Y: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: """ Solve `AX = Y` using LU decomposition. """ @@ -457,8 +462,6 @@ fn solve_lu[ _temp = _temp - L._load(i, j) * Z._load(j, col) _temp = _temp / L._load(i, i) Z._store(i, col, _temp) - - # Solve `UZ = Z` for `X` for each col for i in range(m - 1, -1, -1): var _temp2 = Z._load(i, col) for j in range(i + 1, m): diff --git a/numojo/routines/logic/truth.mojo b/numojo/routines/logic/truth.mojo index 0a5c5cac..b2a97d49 100644 --- a/numojo/routines/logic/truth.mojo +++ b/numojo/routines/logic/truth.mojo @@ -8,10 +8,11 @@ from sys import simd_width_of import numojo.routines.math._math_funcs as _mf from numojo.core.ndarray import NDArray -from numojo.core.matrix import Matrix +from numojo.core.own_data import OwnData +from numojo.core.matrix import Matrix, MatrixBase -fn all[dtype: DType](A: Matrix[dtype]) -> Scalar[dtype]: +fn all[dtype: DType](A: MatrixBase[dtype, **_]) -> Scalar[dtype]: """ Test whether all array elements evaluate to True. @@ -29,7 +30,9 @@ fn all[dtype: DType](A: Matrix[dtype]) -> Scalar[dtype]: return res -fn all[dtype: DType](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +fn all[ + dtype: DType +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Test whether all array elements evaluate to True along axis. """ @@ -121,7 +124,7 @@ fn any(array: NDArray[DType.bool]) raises -> Scalar[DType.bool]: return result -fn any[dtype: DType](A: Matrix[dtype]) -> Scalar[dtype]: +fn any[dtype: DType](A: MatrixBase[dtype, **_]) -> Scalar[dtype]: """ Test whether any array elements evaluate to True. @@ -139,7 +142,9 @@ fn any[dtype: DType](A: Matrix[dtype]) -> Scalar[dtype]: return res -fn any[dtype: DType](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +fn any[ + dtype: DType +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Test whether any array elements evaluate to True along axis. """ diff --git a/numojo/routines/manipulation.mojo b/numojo/routines/manipulation.mojo index 756068fd..48701797 100644 --- a/numojo/routines/manipulation.mojo +++ b/numojo/routines/manipulation.mojo @@ -10,20 +10,22 @@ Array manipulation routines. """ from memory import UnsafePointer, memcpy +from memory import LegacyUnsafePointer from sys import simd_width_of from algorithm import vectorize from numojo.core.ndarray import NDArray +from numojo.core.own_data import OwnData from numojo.core.complex import ComplexNDArray from numojo.core.ndshape import NDArrayShape, Shape from numojo.core.ndstrides import NDArrayStrides import numojo.core.matrix as matrix -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase from numojo.core.utility import _list_of_flipped_range, _get_offset # ===----------------------------------------------------------------------=== # # TODO: -# - When `OwnData` is supported, re-write `broadcast_to()`.` +# - When `DataContainer` is supported, re-write `broadcast_to()`.` # ===----------------------------------------------------------------------=== # # ===----------------------------------------------------------------------=== # @@ -207,7 +209,7 @@ fn ravel[ # TODO: Remove this one if the following function is working well: # `numojo.core.utility._traverse_buffer_according_to_shape_and_strides` fn _set_values_according_to_shape_and_strides( - mut I: NDArray[DType.index], + mut I: NDArray[DType.int], mut index: Int, current_dim: Int, previous_sum: Int, @@ -285,8 +287,8 @@ fn transpose[ new_strides._buf[i] = A.strides[axes[i]] var array_order: String = "C" if A.flags.C_CONTIGUOUS else "F" - var I = NDArray[DType.index](Shape(A.size), order=array_order) - var ptr: UnsafePointer[Scalar[DType.index]] = I._buf.ptr + var I = NDArray[DType.int](Shape(A.size), order=array_order) + var ptr: LegacyUnsafePointer[Scalar[DType.int]] = I._buf.ptr numojo.core.utility._traverse_buffer_according_to_shape_and_strides( ptr, new_shape, new_strides ) @@ -310,7 +312,7 @@ fn transpose[dtype: DType](A: NDArray[dtype]) raises -> NDArray[dtype]: 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) if A.shape[0] == 1 or A.shape[1] == 1: - memcpy(B._buf.ptr, A._buf.ptr, A.size) + memcpy(dest=B._buf.ptr, src=A._buf.ptr, count=A.size) else: for i in range(B.shape[0]): for j in range(B.shape[1]): @@ -324,7 +326,7 @@ fn transpose[dtype: DType](A: NDArray[dtype]) raises -> NDArray[dtype]: return transpose(A, axes=flipped_axes) -fn transpose[dtype: DType](A: Matrix[dtype]) -> Matrix[dtype]: +fn transpose[dtype: DType](A: MatrixBase[dtype, **_]) -> Matrix[dtype]: """ Transpose of matrix. """ @@ -335,7 +337,7 @@ fn transpose[dtype: DType](A: Matrix[dtype]) -> Matrix[dtype]: var B = Matrix[dtype](Tuple(A.shape[1], A.shape[0]), order=order) if A.shape[0] == 1 or A.shape[1] == 1: - memcpy(B._buf.ptr, A._buf.ptr, A.size) + memcpy(dest=B._buf.ptr, src=A._buf.ptr, count=A.size) else: for i in range(B.shape[0]): for j in range(B.shape[1]): @@ -343,7 +345,9 @@ fn transpose[dtype: DType](A: Matrix[dtype]) -> Matrix[dtype]: return B^ -fn reorder_layout[dtype: DType](A: Matrix[dtype]) raises -> Matrix[dtype]: +fn reorder_layout[ + dtype: DType +](A: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: """ Create a new Matrix with the opposite layout from A: if A is C-contiguous, then create a new F-contiguous matrix of the same shape. @@ -368,16 +372,15 @@ fn reorder_layout[dtype: DType](A: Matrix[dtype]) raises -> Matrix[dtype]: ) ) - var B: Matrix[dtype] = Matrix[dtype](Tuple(rows, cols), new_order) - + var B = Matrix[dtype](Tuple(rows, cols), new_order) if new_order == "C": for i in range(rows): for j in range(cols): - B._buf.ptr[i * cols + j] = A._buf.ptr[i + j * rows] + B._buf[i * cols + j] = A._buf[i + j * rows] else: for j in range(cols): for i in range(rows): - B._buf.ptr[j * rows + i] = A._buf.ptr[i * cols + j] + B._buf[j * rows + i] = A._buf[i * cols + j] return B^ @@ -419,26 +422,26 @@ fn broadcast_to[ b_strides[i] = 0 # Start broadcasting. - # TODO: When `OwnData` is supported, re-write this part. + # TODO: When `DataContainer` is supported, re-write this part. # We just need to change the shape and strides and re-use the data. var b = NDArray[dtype](shape) # Construct array of targeted shape. - # TODO: `b.strides = b_strides` when OwnData + # TODO: `b.strides = b_strides` when DataContainer # Iterate all items in the new array and fill in correct values. for offset in range(b.size): var remainder = offset - var indices = Item(ndim=b.ndim, initialized=False) + var indices = Item(ndim=b.ndim) for i in range(b.ndim): indices[i] = remainder // b.strides[i] remainder %= b.strides[i] - # TODO: Change b.strides to NDArrayStrides(b.shape) when OwnData + # TODO: Change b.strides to NDArrayStrides(b.shape) when DataContainer (b._buf.ptr + offset).init_pointee_copy( a._buf.ptr[ _get_offset(indices, b_strides) - ] # TODO: Change b_strides to b.strides when OwnData + ] # TODO: Change b_strides to b.strides when DataContainer ) return b^ @@ -447,7 +450,9 @@ fn broadcast_to[ fn broadcast_to[ dtype: DType ]( - var A: Matrix[dtype], shape: Tuple[Int, Int], override_order: String = "" + A: MatrixBase[dtype, **_], + shape: Tuple[Int, Int], + override_order: String = "", ) raises -> Matrix[dtype]: """ Broadcasts the vector to the given shape. @@ -485,11 +490,11 @@ fn broadcast_to[ else: ord = override_order - var B = Matrix[dtype](shape, order=ord) + var B: Matrix[dtype] = Matrix[dtype](shape, order=ord) if (A.shape[0] == shape[0]) and (A.shape[1] == shape[1]): - return A^ + memcpy(dest=B._buf.ptr, src=A._buf.ptr, count=A.size) elif (A.shape[0] == 1) and (A.shape[1] == 1): - B = Matrix.full[dtype](shape, A[0, 0], order=ord) + B = Matrix[dtype].full(shape, A[0, 0], order=ord) elif (A.shape[0] == 1) and (A.shape[1] == shape[1]): for i in range(shape[0]): memcpy( @@ -518,7 +523,7 @@ fn broadcast_to[ Broadcasts the scalar to the given shape. """ - var B: Matrix[dtype] = Matrix.full[dtype](shape, A, order=order) + var B: Matrix[dtype] = Matrix[dtype].full(shape, A, order=order) return B^ @@ -531,7 +536,7 @@ fn _broadcast_back_to[ it has one dimension less than `a`. This function can broadcast `b` back to the shape of `a`. It is a temporary function and should not be used by users. - When `OwnData` is supported, this function will be removed. + When `DataContainer` is supported, this function will be removed. Whether broadcasting is possible or not is not checked. """ @@ -550,7 +555,7 @@ fn _broadcast_back_to[ # Iterate all items in the new array and fill in correct values. for offset in range(b.size): var remainder = offset - var indices = Item(ndim=b.ndim, initialized=False) + var indices = Item(ndim=b.ndim) for i in range(b.ndim): indices[i] = remainder // b.strides[i] @@ -614,7 +619,7 @@ fn flip[ String("Invalid index: index out of bound [0, {}).").format(A.ndim) ) - var I = NDArray[DType.index](Shape(A.size)) + var I = NDArray[DType.int](Shape(A.size)) var ptr = I._buf.ptr numojo.core.utility._traverse_buffer_according_to_shape_and_strides( diff --git a/numojo/routines/math/_math_funcs.mojo b/numojo/routines/math/_math_funcs.mojo index c99b08d6..ccffcfe3 100644 --- a/numojo/routines/math/_math_funcs.mojo +++ b/numojo/routines/math/_math_funcs.mojo @@ -11,7 +11,7 @@ from testing import assert_raises from algorithm.functional import parallelize, vectorize from sys.info import num_physical_cores from sys import simd_width_of -from memory import UnsafePointer +from memory import LegacyUnsafePointer as UnsafePointer from numojo.core.traits.backend import Backend from numojo.core.ndarray import NDArray @@ -418,7 +418,7 @@ struct Vectorized(Backend): fn bool_simd_store[ simd_width: Int ]( - ptr: UnsafePointer[Scalar[DType.bool]], + ptr: LegacyUnsafePointer[Scalar[DType.bool]], start: Int, val: SIMD[DType.bool, simd_width], ): diff --git a/numojo/routines/math/extrema.mojo b/numojo/routines/math/extrema.mojo index 85b2abee..089483a1 100644 --- a/numojo/routines/math/extrema.mojo +++ b/numojo/routines/math/extrema.mojo @@ -1,4 +1,4 @@ -# ===----------------------------------------------------------------------=== # +# views ===----------------------------------------------------------------------=== # # Distributed under the Apache 2.0 License with LLVM Exceptions. # See LICENSE and the LLVM License for more information. # https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo/blob/main/LICENSE @@ -27,9 +27,10 @@ from builtin.math import min as builtin_min from collections.optional import Optional from sys import simd_width_of -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase import numojo.core.matrix as matrix from numojo.core.ndarray import NDArray +from numojo.core.own_data import OwnData import numojo.core.utility as utility from numojo.routines.creation import full from numojo.routines.sorting import binary_sort @@ -144,7 +145,7 @@ fn max[dtype: DType](a: NDArray[dtype], axis: Int) raises -> NDArray[dtype]: @always_inline fn matrix_extrema[ dtype: DType, find_max: Bool -](A: Matrix[dtype]) raises -> Scalar[dtype]: +](A: MatrixBase[dtype, **_]) raises -> Scalar[dtype]: """ Generic implementation for finding global min/max in a matrix. Works with any memory layout (row-major or column-major). @@ -167,7 +168,7 @@ fn matrix_extrema[ @always_inline fn matrix_extrema_axis[ dtype: DType, find_max: Bool -](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Generic implementation for finding min/max along an axis in a matrix. Works with any memory layout (row-major or column-major). @@ -213,14 +214,16 @@ fn matrix_extrema_axis[ return B^ -fn max[dtype: DType](A: Matrix[dtype]) raises -> Scalar[dtype]: +fn max[dtype: DType](A: MatrixBase[dtype, **_]) raises -> Scalar[dtype]: """ Find max item. It is first flattened before sorting. """ return matrix_extrema[dtype, True](A) -fn max[dtype: DType](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +fn max[ + dtype: DType +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Find max item along the given axis. """ @@ -230,7 +233,7 @@ fn max[dtype: DType](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: fn _max[ dtype: DType ](A: Matrix[dtype], start: Int, end: Int) raises -> Tuple[ - Scalar[dtype], Scalar[DType.index] + Scalar[dtype], Scalar[DType.int] ]: """ Auxiliary function that find the max value in a range of the buffer. @@ -243,7 +246,7 @@ fn _max[ ).format(start, end, A.size) ) - var max_index: Scalar[DType.index] = start + var max_index: Scalar[DType.int] = start var rows = A.shape[0] var cols = A.shape[1] @@ -333,14 +336,16 @@ fn min[dtype: DType](a: NDArray[dtype], axis: Int) raises -> NDArray[dtype]: ) -fn min[dtype: DType](A: Matrix[dtype]) raises -> Scalar[dtype]: +fn min[dtype: DType](A: MatrixBase[dtype, **_]) raises -> Scalar[dtype]: """ Find min item. """ return matrix_extrema[dtype, False](A) -fn min[dtype: DType](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +fn min[ + dtype: DType +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Find min item along the given axis. """ @@ -350,7 +355,7 @@ fn min[dtype: DType](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: fn _min[ dtype: DType ](A: Matrix[dtype], start: Int, end: Int) raises -> Tuple[ - Scalar[dtype], Scalar[DType.index] + Scalar[dtype], Scalar[DType.int] ]: """ Auxiliary function that find the min value in a range of the buffer. @@ -363,7 +368,7 @@ fn _min[ ).format(start, end, A.size) ) - var min_index: Scalar[DType.index] = start + var min_index: Scalar[DType.int] = start var rows = A.shape[0] var cols = A.shape[1] diff --git a/numojo/routines/math/products.mojo b/numojo/routines/math/products.mojo index 38268dea..cc7e5b68 100644 --- a/numojo/routines/math/products.mojo +++ b/numojo/routines/math/products.mojo @@ -1,9 +1,11 @@ from algorithm.functional import parallelize, vectorize from sys import simd_width_of +from memory import UnsafePointer, memcpy, memset_zero from numojo.core.ndarray import NDArray +from numojo.core.own_data import OwnData import numojo.core.matrix as matrix -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase from numojo.routines.creation import ones @@ -81,7 +83,7 @@ fn prod[ return result^ -fn prod[dtype: DType](A: Matrix[dtype]) -> Scalar[dtype]: +fn prod[dtype: DType](A: MatrixBase[dtype, **_]) -> Scalar[dtype]: """ Product of all items in the Matrix. @@ -99,7 +101,9 @@ fn prod[dtype: DType](A: Matrix[dtype]) -> Scalar[dtype]: return res -fn prod[dtype: DType](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +fn prod[ + dtype: DType +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Product of items in a Matrix along the axis. @@ -205,7 +209,7 @@ fn cumprod[ String("Invalid index: index out of bound [0, {}).").format(A.ndim) ) - var I = NDArray[DType.index](Shape(A.size)) + var I = NDArray[DType.int](Shape(A.size)) var ptr = I._buf.ptr var _shape = B.shape._move_axis_to_end(axis) @@ -222,7 +226,7 @@ fn cumprod[ return B^ -fn cumprod[dtype: DType](var A: Matrix[dtype]) raises -> Matrix[dtype]: +fn cumprod[dtype: DType](A: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: """ Cumprod of flattened matrix. @@ -236,25 +240,26 @@ fn cumprod[dtype: DType](var A: Matrix[dtype]) raises -> Matrix[dtype]: print(mat.cumprod(A)) ``` """ - var reorder = False - if A.flags.F_CONTIGUOUS: - reorder = True - A = A.reorder_layout() + alias width: Int = simd_width_of[dtype]() + var result: Matrix[dtype] = Matrix.zeros[dtype](A.shape, "C") - A.resize(shape=(1, A.size)) + if A.flags.C_CONTIGUOUS: + memcpy(dest=result._buf.ptr, src=A._buf.ptr, count=A.size) + else: + for i in range(A.shape[0]): + for j in range(A.shape[1]): + result[i, j] = A[i, j] for i in range(1, A.size): - A._buf.ptr[i] *= A._buf.ptr[i - 1] - - if reorder: - A = A.reorder_layout() + result._buf.ptr[i] *= result._buf.ptr[i - 1] - return A^ + result.resize(shape=(1, result.size)) + return result^ fn cumprod[ dtype: DType -](var A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Cumprod of Matrix along the axis. @@ -271,6 +276,19 @@ fn cumprod[ ``` """ alias width: Int = simd_width_of[dtype]() + var order: String = "C" if A.flags.C_CONTIGUOUS else "F" + var result: Matrix[dtype] = Matrix.zeros[dtype](A.shape, order) + + if order == "C": + memcpy(dest=result._buf.ptr, src=A._buf.ptr, count=A.size) + else: + for j in range(result.shape[1]): + + @parameter + fn copy_col[width: Int](i: Int): + result._store[width](i, j, A._load[width](i, j)) + + vectorize[copy_col, width](A.shape[0]) if axis == 0: if A.flags.C_CONTIGUOUS: @@ -278,34 +296,40 @@ fn cumprod[ @parameter fn cal_vec_row[width: Int](j: Int): - A._store[width]( - i, j, A._load[width](i - 1, j) * A._load[width](i, j) + result._store[width]( + i, + j, + result._load[width](i - 1, j) + * result._load[width](i, j), ) vectorize[cal_vec_row, width](A.shape[1]) - return A^ + return result^ else: for j in range(A.shape[1]): for i in range(1, A.shape[0]): - A[i, j] = A[i - 1, j] * A[i, j] - return A^ + result[i, j] = result[i - 1, j] * result[i, j] + return result^ elif axis == 1: if A.flags.C_CONTIGUOUS: for i in range(A.shape[0]): for j in range(1, A.shape[1]): - A[i, j] = A[i, j - 1] * A[i, j] - return A^ + result[i, j] = result[i, j - 1] * result[i, j] + return result^ else: for j in range(1, A.shape[1]): @parameter fn cal_vec_column[width: Int](i: Int): - A._store[width]( - i, j, A._load[width](i, j - 1) * A._load[width](i, j) + result._store[width]( + i, + j, + result._load[width](i, j - 1) + * result._load[width](i, j), ) vectorize[cal_vec_column, width](A.shape[0]) - return A^ + return result^ else: raise Error(String("The axis can either be 1 or 0!")) diff --git a/numojo/routines/math/rounding.mojo b/numojo/routines/math/rounding.mojo index bb45c538..6d6a1f56 100644 --- a/numojo/routines/math/rounding.mojo +++ b/numojo/routines/math/rounding.mojo @@ -11,20 +11,19 @@ from utils.numerics import nextafter as builtin_nextafter import numojo.routines.math._math_funcs as _mf from numojo.core.ndarray import NDArray import numojo.core.matrix as matrix -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase fn round[ dtype: DType -](var A: Matrix[dtype], decimals: Int = 0) -> Matrix[dtype]: +](A: MatrixBase[dtype, **_], decimals: Int = 0) -> Matrix[dtype]: # FIXME # The built-in `round` function is not working now. # It will be fixed in future. - + var res = Matrix.zeros[dtype](A.shape) for i in range(A.size): - A._buf.ptr[i] = builtin_math.round(A._buf.ptr[i], ndigits=decimals) - - return A^ + res._buf.ptr[i] = builtin_math.round(A._buf.ptr[i], ndigits=decimals) + return res^ fn tabs[ diff --git a/numojo/routines/math/sums.mojo b/numojo/routines/math/sums.mojo index 0d1ee799..bcda2f4b 100644 --- a/numojo/routines/math/sums.mojo +++ b/numojo/routines/math/sums.mojo @@ -1,8 +1,10 @@ from sys import simd_width_of from algorithm import parallelize, vectorize +from memory import UnsafePointer, memset_zero, memcpy from numojo.core.ndarray import NDArray -from numojo.core.matrix import Matrix +from numojo.core.own_data import OwnData +from numojo.core.matrix import Matrix, MatrixBase from numojo.routines.creation import zeros @@ -108,7 +110,7 @@ fn sum[dtype: DType](A: NDArray[dtype], axis: Int) raises -> NDArray[dtype]: return result^ -fn sum[dtype: DType](A: Matrix[dtype]) -> Scalar[dtype]: +fn sum[dtype: DType](A: MatrixBase[dtype, **_]) -> Scalar[dtype]: """ Sum up all items in the Matrix. @@ -133,7 +135,9 @@ fn sum[dtype: DType](A: Matrix[dtype]) -> Scalar[dtype]: return res -fn sum[dtype: DType](A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +fn sum[ + dtype: DType +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Sum up the items in a Matrix along the axis. @@ -263,7 +267,7 @@ fn cumsum[ String("Invalid index: index out of bound [0, {}).").format(A.ndim) ) - var I = NDArray[DType.index](Shape(A.size)) + var I = NDArray[DType.int](Shape(A.size)) var ptr = I._buf.ptr var _shape = B.shape._move_axis_to_end(axis) @@ -282,7 +286,7 @@ fn cumsum[ return B^ -fn cumsum[dtype: DType](var A: Matrix[dtype]) raises -> Matrix[dtype]: +fn cumsum[dtype: DType](A: MatrixBase[dtype, **_]) raises -> Matrix[dtype]: """ Cumsum of flattened matrix. @@ -297,24 +301,28 @@ fn cumsum[dtype: DType](var A: Matrix[dtype]) raises -> Matrix[dtype]: ``` """ var reorder = False + var order = "C" if A.flags.C_CONTIGUOUS else "F" + var result: Matrix[dtype] = Matrix.zeros[dtype](A.shape, order) + memcpy(dest=result._buf.ptr, src=A._buf.ptr, count=A.size) + if A.flags.F_CONTIGUOUS: reorder = True - A = A.reorder_layout() + result = result.reorder_layout() - A.resize(shape=(1, A.size)) + result.resize(shape=(1, A.size)) for i in range(1, A.size): - A._buf.ptr[i] += A._buf.ptr[i - 1] + result._buf.ptr[i] += result._buf.ptr[i - 1] if reorder: - A = A.reorder_layout() + result = result.reorder_layout() - return A^ + return result^ fn cumsum[ dtype: DType -](var A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[dtype]: """ Cumsum of Matrix along the axis. @@ -332,41 +340,50 @@ fn cumsum[ """ alias width: Int = simd_width_of[dtype]() + var order = "C" if A.flags.C_CONTIGUOUS else "F" + var result: Matrix[dtype] = Matrix.zeros[dtype](A.shape, order) + memcpy(dest=result._buf.ptr, src=A._buf.ptr, count=A.size) if axis == 0: - if A.flags.C_CONTIGUOUS: + if result.flags.C_CONTIGUOUS: for i in range(1, A.shape[0]): @parameter fn cal_vec_sum_column[width: Int](j: Int): - A._store[width]( - i, j, A._load[width](i - 1, j) + A._load[width](i, j) + result._store[width]( + i, + j, + result._load[width](i - 1, j) + + result._load[width](i, j), ) - vectorize[cal_vec_sum_column, width](A.shape[1]) - return A^ + vectorize[cal_vec_sum_column, width](result.shape[1]) + return result^ else: for j in range(A.shape[1]): for i in range(1, A.shape[0]): - A[i, j] = A[i - 1, j] + A[i, j] - return A^ + result[i, j] = result[i - 1, j] + result[i, j] + return result^ elif axis == 1: if A.flags.C_CONTIGUOUS: for i in range(A.shape[0]): for j in range(1, A.shape[1]): - A[i, j] = A[i, j - 1] + A[i, j] - return A^ + result[i, j] = result[i, j - 1] + result[i, j] + return result^ else: for j in range(1, A.shape[1]): @parameter fn cal_vec_sum_row[width: Int](i: Int): - A._store[width]( - i, j, A._load[width](i, j - 1) + A._load[width](i, j) + result._store[width]( + i, + j, + result._load[width](i, j - 1) + + result._load[width](i, j), ) vectorize[cal_vec_sum_row, width](A.shape[0]) - return A^ + return result^ else: raise Error(String("The axis can either be 1 or 0!")) diff --git a/numojo/routines/searching.mojo b/numojo/routines/searching.mojo index a6bb2fe2..0422ed53 100644 --- a/numojo/routines/searching.mojo +++ b/numojo/routines/searching.mojo @@ -11,13 +11,13 @@ from collections.optional import Optional from numojo.core.ndarray import NDArray from numojo.core.ndshape import NDArrayShape import numojo.core.matrix as matrix -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase from numojo.core.utility import is_inttype, is_floattype from numojo.routines.sorting import binary_sort from numojo.routines.math.extrema import _max, _min -fn argmax_1d[dtype: DType](a: NDArray[dtype]) raises -> Scalar[DType.index]: +fn argmax_1d[dtype: DType](a: NDArray[dtype]) raises -> Scalar[DType.int]: """Returns the index of the maximum value in the buffer. Regardless of the shape of input, it is treated as a 1-d array. @@ -44,7 +44,7 @@ fn argmax_1d[dtype: DType](a: NDArray[dtype]) raises -> Scalar[DType.index]: return result -fn argmin_1d[dtype: DType](a: NDArray[dtype]) raises -> Scalar[DType.index]: +fn argmin_1d[dtype: DType](a: NDArray[dtype]) raises -> Scalar[DType.int]: """Returns the index of the minimum value in the buffer. Regardless of the shape of input, it is treated as a 1-d array. @@ -71,7 +71,7 @@ fn argmin_1d[dtype: DType](a: NDArray[dtype]) raises -> Scalar[DType.index]: return result -fn argmax[dtype: DType, //](a: NDArray[dtype]) raises -> Scalar[DType.index]: +fn argmax[dtype: DType, //](a: NDArray[dtype]) raises -> Scalar[DType.int]: """Returns the indices of the maximum values of the array along an axis. When no axis is specified, the array is flattened. @@ -98,7 +98,7 @@ fn argmax[dtype: DType, //](a: NDArray[dtype]) raises -> Scalar[DType.index]: fn argmax[ dtype: DType, // -](a: NDArray[dtype], axis: Int) raises -> NDArray[DType.index]: +](a: NDArray[dtype], axis: Int) raises -> NDArray[DType.int]: """Returns the indices of the maximum values of the array along an axis. When no axis is specified, the array is flattened. @@ -161,11 +161,11 @@ fn argmax[ @always_inline fn find_extrema_index[ dtype: DType, find_max: Bool -](A: Matrix[dtype]) raises -> Scalar[DType.index]: +](A: MatrixBase[dtype, **_]) raises -> Scalar[DType.int, **_]: """Find index of min/max value, either in whole matrix or along an axis.""" var extreme_val = A[0, 0] - var extreme_idx: Scalar[DType.index] = 0 + var extreme_idx: Scalar[DType.int] = 0 for i in range(A.shape[0]): for j in range(A.shape[1]): @@ -187,13 +187,13 @@ fn find_extrema_index[ @always_inline fn find_extrema_index[ dtype: DType, find_max: Bool -](A: Matrix[dtype], axis: Optional[Int]) raises -> Matrix[DType.index]: +](A: MatrixBase[dtype, **_], axis: Optional[Int]) raises -> Matrix[DType.int]: """Find index of min/max value, either in whole matrix or along an axis.""" if axis != 0 and axis != 1: raise Error(String("The axis can either be 1 or 0!")) - var B = Matrix[DType.index]( + var B = Matrix[DType.int]( shape=(A.shape[0], 1) if axis == 1 else (1, A.shape[1]) ) @@ -237,19 +237,19 @@ fn find_extrema_index[ return B^ -fn argmax[dtype: DType](A: Matrix[dtype]) raises -> Scalar[DType.index]: +fn argmax[dtype: DType](A: MatrixBase[dtype, **_]) raises -> Scalar[DType.int]: """Find index of max value in a flattened matrix.""" return find_extrema_index[dtype, True](A) fn argmax[ dtype: DType -](A: Matrix[dtype], axis: Int) raises -> Matrix[DType.index]: +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[DType.int]: """Find indices of max values along the given axis.""" return find_extrema_index[dtype, True](A, axis) -fn argmin[dtype: DType, //](a: NDArray[dtype]) raises -> Scalar[DType.index]: +fn argmin[dtype: DType, //](a: NDArray[dtype]) raises -> Scalar[DType.int]: """Returns the indices of the minimum values of the array along an axis. When no axis is specified, the array is flattened. @@ -276,7 +276,7 @@ fn argmin[dtype: DType, //](a: NDArray[dtype]) raises -> Scalar[DType.index]: fn argmin[ dtype: DType, // -](a: NDArray[dtype], axis: Int) raises -> NDArray[DType.index]: +](a: NDArray[dtype], axis: Int) raises -> NDArray[DType.int]: """Returns the indices of the minimum values of the array along an axis. When no axis is specified, the array is flattened. @@ -309,7 +309,7 @@ fn argmin[ return numojo.apply_along_axis[func1d=argmin_1d](a=a, axis=normalized_axis) -fn argmin[dtype: DType](A: Matrix[dtype]) raises -> Scalar[DType.index]: +fn argmin[dtype: DType](A: MatrixBase[dtype, **_]) raises -> Scalar[DType.int]: """ Index of the min. It is first flattened before sorting. """ @@ -318,7 +318,7 @@ fn argmin[dtype: DType](A: Matrix[dtype]) raises -> Scalar[DType.index]: fn argmin[ dtype: DType -](A: Matrix[dtype], axis: Int) raises -> Matrix[DType.index]: +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[DType.int]: """ Index of the min along the given axis. """ diff --git a/numojo/routines/sorting.mojo b/numojo/routines/sorting.mojo index 54408959..c444ce85 100644 --- a/numojo/routines/sorting.mojo +++ b/numojo/routines/sorting.mojo @@ -23,9 +23,10 @@ import math from algorithm import vectorize from numojo.core.ndarray import NDArray +from numojo.core.own_data import OwnData from numojo.core.ndshape import NDArrayShape import numojo.core.matrix as matrix -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase import numojo.core.utility as utility from numojo.routines.manipulation import ravel, transpose @@ -149,7 +150,7 @@ fn sort[dtype: DType](A: Matrix[dtype]) raises -> Matrix[dtype]: """ Sort the Matrix. It is first flattened before sorting. """ - var I = Matrix.zeros[DType.index](shape=A.shape) + var I = Matrix[DType.int].zeros(shape=A.shape) var B = A.flatten() _quick_sort_inplace(B, I, 0, A.size - 1) @@ -167,7 +168,7 @@ fn sort[dtype: DType](var A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: for i in range(A.shape[0]): var row = Matrix[dtype](shape=(1, A.shape[1]), order="C") - var indices = Matrix.zeros[DType.index]( + var indices = Matrix[DType.int].zeros( shape=(1, A.shape[1]), order="C" ) @@ -186,7 +187,7 @@ fn sort[dtype: DType](var A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: for j in range(A.shape[1]): var col = Matrix[dtype](shape=(A.shape[0], 1), order="C") - var indices = Matrix.zeros[DType.index]( + var indices = Matrix[DType.int].zeros( shape=(A.shape[0], 1), order="C" ) @@ -203,7 +204,7 @@ fn sort[dtype: DType](var A: Matrix[dtype], axis: Int) raises -> Matrix[dtype]: raise Error(String("The axis can either be 1 or 0!")) -fn argsort[dtype: DType](a: NDArray[dtype]) raises -> NDArray[DType.index]: +fn argsort[dtype: DType](a: NDArray[dtype]) raises -> NDArray[DType.int]: """ Returns the indices that would sort an array. It is not guaranteed to be unstable. @@ -224,7 +225,7 @@ fn argsort[dtype: DType](a: NDArray[dtype]) raises -> NDArray[DType.index]: else: a_flattened = ravel(a) - var indices = arange[DType.index](a_flattened.size) + var indices = arange[DType.int](a_flattened.size) _quick_sort_inplace(a_flattened, indices) @@ -233,7 +234,7 @@ fn argsort[dtype: DType](a: NDArray[dtype]) raises -> NDArray[DType.index]: fn argsort[ dtype: DType -](mut a: NDArray[dtype], axis: Int) raises -> NDArray[DType.index]: +](mut a: NDArray[dtype], axis: Int) raises -> NDArray[DType.int]: """ Returns the indices that would sort an array. It is not guaranteed to be unstable. @@ -272,11 +273,11 @@ fn argsort[ ) -fn argsort[dtype: DType](A: Matrix[dtype]) raises -> Matrix[DType.index]: +fn argsort[dtype: DType](A: MatrixBase[dtype, **_]) raises -> Matrix[DType.int]: """ Argsort the Matrix. It is first flattened before sorting. """ - var I = Matrix[DType.index](shape=(1, A.size), order=A.order()) + var I = Matrix[DType.int](shape=(1, A.size), order=A.order()) for i in range(I.size): I._buf.ptr[i] = i var B: Matrix[dtype] @@ -291,18 +292,18 @@ fn argsort[dtype: DType](A: Matrix[dtype]) raises -> Matrix[DType.index]: fn argsort[ dtype: DType -](var A: Matrix[dtype], axis: Int) raises -> Matrix[DType.index]: +](A: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[DType.int]: """ Argsort the Matrix along the given axis. """ var order = A.order() if axis == 1: - var result = Matrix[DType.index](shape=A.shape, order=order) + var result = Matrix[DType.int](shape=A.shape, order=order) for i in range(A.shape[0]): var row = Matrix[dtype](shape=(1, A.shape[1]), order="C") - var idx = Matrix[DType.index](shape=(1, A.shape[1]), order="C") + var idx = Matrix[DType.int](shape=(1, A.shape[1]), order="C") for j in range(A.shape[1]): row._store(0, j, A._load(i, j)) @@ -316,11 +317,11 @@ fn argsort[ return result^ elif axis == 0: - var result = Matrix[DType.index](shape=A.shape, order=order) + var result = Matrix[DType.int](shape=A.shape, order=order) for j in range(A.shape[1]): var col = Matrix[dtype](shape=(A.shape[0], 1), order="C") - var idx = Matrix[DType.index](shape=(A.shape[0], 1), order="C") + var idx = Matrix[DType.int](shape=(A.shape[0], 1), order="C") for i in range(A.shape[0]): col._store(i, 0, A._load(i, j)) @@ -542,7 +543,7 @@ fn quick_sort_stable_inplace_1d[dtype: DType](mut a: NDArray[dtype]) raises: fn argsort_quick_sort_1d[ dtype: DType -](a: NDArray[dtype]) raises -> NDArray[DType.index]: +](a: NDArray[dtype]) raises -> NDArray[DType.int]: """ Returns the indices that would sort the buffer of an array. Regardless of the shape of input, it is treated as a 1-d array. @@ -559,7 +560,7 @@ fn argsort_quick_sort_1d[ """ var result: NDArray[dtype] = a.copy() - var indices = arange[DType.index](result.size) + var indices = arange[DType.int](result.size) _quick_sort_inplace(result, indices) return indices^ @@ -831,7 +832,7 @@ fn _quick_sort_inplace[dtype: DType](mut A: NDArray[dtype]) raises: fn _quick_sort_inplace[ dtype: DType -](mut A: NDArray[dtype], mut I: NDArray[DType.index]) raises: +](mut A: NDArray[dtype], mut I: NDArray[DType.int]) raises: """ Sort in-place array's buffer using quick sort method. The indices are also sorted. diff --git a/numojo/routines/statistics/averages.mojo b/numojo/routines/statistics/averages.mojo index d8f5e406..24657727 100644 --- a/numojo/routines/statistics/averages.mojo +++ b/numojo/routines/statistics/averages.mojo @@ -15,8 +15,9 @@ from collections.optional import Optional import math as mt from numojo.core.ndarray import NDArray +from numojo.core.own_data import OwnData import numojo.core.matrix as matrix -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase import numojo.core.utility as utility from numojo.routines.logic.comparison import greater, less from numojo.routines.manipulation import broadcast_to, _broadcast_back_to @@ -102,7 +103,7 @@ fn mean[ fn mean[ dtype: DType, //, returned_dtype: DType = DType.float64 -](a: Matrix[dtype]) -> Scalar[returned_dtype]: +](a: MatrixBase[dtype, **_]) -> Scalar[returned_dtype]: """ Calculate the arithmetic average of all items in the Matrix. @@ -122,7 +123,7 @@ fn mean[ fn mean[ dtype: DType, //, returned_dtype: DType = DType.float64 -](a: Matrix[dtype], axis: Int) raises -> Matrix[returned_dtype]: +](a: MatrixBase[dtype, **_], axis: Int) raises -> Matrix[returned_dtype]: """ Calculate the arithmetic average of a Matrix along the axis. @@ -373,7 +374,7 @@ fn std[ fn std[ dtype: DType, //, returned_dtype: DType = DType.float64 -](A: Matrix[dtype], ddof: Int = 0) raises -> Scalar[returned_dtype]: +](A: MatrixBase[dtype, **_], ddof: Int = 0) raises -> Scalar[returned_dtype]: """ Compute the standard deviation. @@ -398,7 +399,9 @@ fn std[ fn std[ dtype: DType, //, returned_dtype: DType = DType.float64 -](A: Matrix[dtype], axis: Int, ddof: Int = 0) raises -> Matrix[returned_dtype]: +](A: MatrixBase[dtype, **_], axis: Int, ddof: Int = 0) raises -> Matrix[ + returned_dtype +]: """ Compute the standard deviation along axis. @@ -505,7 +508,7 @@ fn variance[ fn variance[ dtype: DType, //, returned_dtype: DType = DType.float64 -](A: Matrix[dtype], ddof: Int = 0) raises -> Scalar[returned_dtype]: +](A: MatrixBase[dtype, **_], ddof: Int = 0) raises -> Scalar[returned_dtype]: """ Compute the variance. @@ -533,7 +536,9 @@ fn variance[ fn variance[ dtype: DType, //, returned_dtype: DType = DType.float64 -](A: Matrix[dtype], axis: Int, ddof: Int = 0) raises -> Matrix[returned_dtype]: +](A: MatrixBase[dtype, **_], axis: Int, ddof: Int = 0) raises -> Matrix[ + returned_dtype +]: """ Compute the variance along axis. diff --git a/pixi.lock b/pixi.lock new file mode 100644 index 00000000..f1a05552 --- /dev/null +++ b/pixi.lock @@ -0,0 +1,5546 @@ +version: 6 +environments: + default: + channels: + - url: https://conda.anaconda.org/conda-forge/ + - url: https://conda.modular.com/max/ + - url: https://repo.prefix.dev/modular-community/ + packages: + linux-64: + - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiofiles-25.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohttp-3.13.2-pyh4ca1811_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asgiref-3.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.1-h7ca4310_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.10-h346e085_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.5-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.1-h7e655bb_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.6-h3cb25bf_6.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.7-hc5c8343_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.23.3-ha76f1cc_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.13.3-h3a25ec9_10.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.10.1-hcb69869_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h7e655bb_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.7-h7e655bb_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.35.2-h2ceb62e_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.606-hd6e39bc_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.1-h3a458e0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.2-h3a5f585_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.15.0-h2a74896_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.11.0-h3d7a050_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.13.0-hf38f1be_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314hdfeb8a1_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py314h4a8dc5f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.0-py314hd8ed1ab_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.3.0-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.121.3-hf029e93_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.16-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-core-0.121.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gguf-0.17.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.72.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py314h2d847ca_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hf-transfer-0.1.9-py314h922f143_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/hf-xet-1.2.0-py310hb823017_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.7.1-py314h5bd0f2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.36.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-hbd61a6d_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-22.0.0-h773bc41_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-22.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-22.0.0-h8c2c5c3_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-22.0.0-h635bf11_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-22.0.0-h3f74fd7_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-1_h4a7cf45_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h09219d5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hd53d788_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h02bd7ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-1_h0358290_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-hcd61629_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-hdb79228_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-1_h47877c9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-hb9b0907_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-22.0.0-h7376487_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h421ea60_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsentencepiece-0.2.0-h022d5ca_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.0-hee844dc_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h454ac66_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.1-hfe17d71_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/llguidance-1.3.0-py310hc9716df_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markupsafe-3.0.3-pyh7db6752_0.conda + - conda: https://conda.modular.com/max/linux-64/max-25.7.0-3.14release.conda + - conda: https://conda.modular.com/max/linux-64/max-core-25.7.0-release.conda + - conda: https://conda.modular.com/max/noarch/max-pipelines-25.7.0-release.conda + - conda: https://conda.modular.com/max/noarch/mblack-25.7.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/modular-25.7.0-release.conda + - conda: https://conda.modular.com/max/linux-64/mojo-0.25.7.0-release.conda + - conda: https://conda.modular.com/max/linux-64/mojo-compiler-0.25.7.0-release.conda + - conda: https://conda.modular.com/max/noarch/mojo-python-0.25.7.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.19.0-py314h5bd0f2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/multidict-6.6.3-pyh62beb40_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.18-py314h0f05182_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py314h2b28147_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-0.56b0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.56b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.2.1-hd747db4_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py314ha0b5721_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.0.0-py314h72745e2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/propcache-0.3.1-pyhe1237c8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.31.1-py314h503b32b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-py314h0f05182_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-22.0.0-py314hdafbbf9_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-22.0.0-py314h52d6ec5_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.4-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.5-py314h2e6c369_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.12.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.1.1-py314h5bd0f2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.0-h32b2ec7_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.0-h4df99d1_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.6.0-py314he82b845_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hfb55c3c_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2025.11.3-py314h5bd0f2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.6.0-h8399546_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.7.0-py314ha5689aa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py314he7377e1_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sentencepiece-0.2.0-h43ba129_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sentencepiece-python-0.2.0-py314h8261406_13.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/sentencepiece-spm-0.2.0-h022d5ca_13.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.50.0-pyhfdc7a7d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/taskgroup-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.22.1-py314h7fe7e61_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py314h5bd0f2a_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.57.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.20.0-pyhefaf540_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.20.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.20.0-h4daf872_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.38.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.38.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.22.1-py314h5bd0f2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.1.1-py314ha5689aa_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-15.0.1-py314h31f8a6b_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py314h5bd0f2a_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.3-hb47aa4a_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.2.5-hde8ca8f_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314h0f05182_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiofiles-25.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiohttp-3.13.2-pyh4ca1811_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/asgiref-3.11.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.9.1-h8818502_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.10-hca30140_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.12.5-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.1-h61d5560_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.6-h18584fc_6.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.10.7-hcd69b29_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.23.3-h9710c81_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.13.3-ha255ef3_10.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.10.1-hd860258_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h61d5560_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.7-h61d5560_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.35.2-h5596a46_4.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.606-h95becb6_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.1-h88fedcc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.2-h853621b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.15.0-h10d327b_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.11.0-h7e4aa5d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.13.0-hb288d13_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314h95ef04c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.5-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py314h44086f9_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.0-py314hd8ed1ab_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/datasets-4.4.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.3.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.4.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.3.0-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.121.3-hf029e93_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.16-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-core-0.121.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.10.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/gguf-0.17.1-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.72.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.73.1-py314h7689434_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hf-transfer-0.1.9-py314h57a929c_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hf-xet-1.2.0-py310h6ce4931_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.7.1-py314h0612a62_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.36.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.17-h7eeda09_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-hd64df32_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250512.1-cxx17_hd41c47c_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-22.0.0-h4a3aeba_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-22.0.0-hc317990_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-22.0.0-h75845d1_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-22.0.0-hc317990_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-22.0.0-h144af7f_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-1_h51639a9_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h87ba0bc_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h95a88de_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hb1b9735_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-1_hb0561ab_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.6-hf598326_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.1-hce30654_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.1-h6da58f4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-hfcf01ff_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-h742603c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.39.0-head0a95_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.39.0-hfa3a374_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.73.1-h3063b79_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-1_hd9741b5_openblas.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.21.0-he15edb5_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.21.0-hce30654_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-22.0.0-h0ac143b_4_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.50-h280e0eb_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.31.1-h658db43_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h91c62da_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsentencepiece-0.2.0-h79950eb_13.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.0-h8adb53f_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h14a376c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.1-hd2415e0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.1-h0ff4647_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.1-h9329255_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llguidance-1.3.0-py310h34ed3d5_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.6-h4a912ad_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/markupsafe-3.0.3-pyh7db6752_0.conda + - conda: https://conda.modular.com/max/osx-arm64/max-25.7.0-3.14release.conda + - conda: https://conda.modular.com/max/osx-arm64/max-core-25.7.0-release.conda + - conda: https://conda.modular.com/max/noarch/max-pipelines-25.7.0-release.conda + - conda: https://conda.modular.com/max/noarch/mblack-25.7.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + - conda: https://conda.modular.com/max/noarch/modular-25.7.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/mojo-0.25.7.0-release.conda + - conda: https://conda.modular.com/max/osx-arm64/mojo-compiler-0.25.7.0-release.conda + - conda: https://conda.modular.com/max/noarch/mojo-python-0.25.7.0-release.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.19.0-py314hb84d1df_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/multidict-6.6.3-pyh62beb40_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.18-py314h9d33bd4_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h248ca61_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py314h5b5928d_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-0.56b0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.35.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.56b0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.2.1-h4fd0076_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py314ha3d490a_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.0.0-py314h73456f9_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/propcache-0.3.1-pyhe1237c8_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-6.31.1-py314hc77ea51_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.1.3-py314h9d33bd4_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-22.0.0-py314he55896b_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-22.0.0-py314hf20a12a_0_cpu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.4-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.41.5-py314haad56a0_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.12.0-pyh3cfb1c2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.1.1-py314hb84d1df_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.0-h40d2674_102_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.0-h4df99d1_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.6.0-py314h8cb506f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312hd65ceae_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h64b956e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2025.11.3-py314h0612a62_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.16.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.7.0-py314h8d4a433_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py314h624bdf2_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sentencepiece-0.2.0-h08a494e_13.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sentencepiece-python-0.2.0-py314hbf90ac2_13.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/sentencepiece-spm-0.2.0-h79950eb_13.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.50.0-pyhfdc7a7d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/taskgroup-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.22.1-py314h84b920e_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.2-py314h0612a62_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.57.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.20.0-pyhefaf540_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.20.0-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.20.0-h4daf872_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.38.0-pyh31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.38.0-h31011fe_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.22.1-py314h0612a62_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.1.1-py314h8d4a433_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-15.0.1-py314hf17b0b1_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.3-py314hb84d1df_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.3-haa4e116_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h888dc83_9.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.2.5-h3470cca_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314h9d33bd4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda +packages: +- conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 + sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 + md5: d7c89558ba9fa0495403155b64376d81 + license: None + size: 2562 + timestamp: 1578324546067 +- conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + build_number: 16 + sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 + md5: 73aaf86a425cc6e73fcf236a5a46396d + depends: + - _libgcc_mutex 0.1 conda_forge + - libgomp >=7.5.0 + constrains: + - openmp_impl 9999 + license: BSD-3-Clause + license_family: BSD + size: 23621 + timestamp: 1650670423406 +- conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda + sha256: a3967b937b9abf0f2a99f3173fa4630293979bd1644709d89580e7c62a544661 + md5: aaa2a381ccc56eac91d63b6c1240312f + depends: + - cpython + - python-gil + license: MIT + license_family: MIT + size: 8191 + timestamp: 1744137672556 +- conda: https://conda.anaconda.org/conda-forge/noarch/aiofiles-25.1.0-pyhd8ed1ab_0.conda + sha256: 1d0dcbeaab76d87aa9f9fb07ec9ba07d30f0386019328aaa11a578266f324aaf + md5: 9b7781a926808f424434003f728ea7ab + depends: + - python >=3.10 + license: Apache-2.0 + license_family: Apache + size: 19145 + timestamp: 1760127109813 +- conda: https://conda.anaconda.org/conda-forge/noarch/aiohappyeyeballs-2.6.1-pyhd8ed1ab_0.conda + sha256: 7842ddc678e77868ba7b92a726b437575b23aaec293bca0d40826f1026d90e27 + md5: 18fd895e0e775622906cdabfc3cf0fb4 + depends: + - python >=3.9 + license: PSF-2.0 + license_family: PSF + size: 19750 + timestamp: 1741775303303 +- conda: https://conda.anaconda.org/conda-forge/noarch/aiohttp-3.13.2-pyh4ca1811_0.conda + sha256: 8af88a6daa5e30f347da7faee1ee17d920a1090c0e921431bf43adff02429b50 + md5: 9b7efc1b9351892fc1b0af3fb7e44280 + depends: + - aiohappyeyeballs >=2.5.0 + - aiosignal >=1.4.0 + - async-timeout >=4.0,<6.0 + - attrs >=17.3.0 + - frozenlist >=1.1.1 + - multidict >=4.5,<7.0 + - propcache >=0.2.0 + - python >=3.10 + - yarl >=1.17.0,<2.0 + track_features: + - aiohttp_no_compile + license: MIT AND Apache-2.0 + license_family: Apache + size: 474272 + timestamp: 1761726660058 +- conda: https://conda.anaconda.org/conda-forge/noarch/aiosignal-1.4.0-pyhd8ed1ab_0.conda + sha256: 8dc149a6828d19bf104ea96382a9d04dae185d4a03cc6beb1bc7b84c428e3ca2 + md5: 421a865222cd0c9d83ff08bc78bf3a61 + depends: + - frozenlist >=1.1.0 + - python >=3.9 + - typing_extensions >=4.2 + license: Apache-2.0 + license_family: APACHE + size: 13688 + timestamp: 1751626573984 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda + sha256: cc9fbc50d4ee7ee04e49ee119243e6f1765750f0fd0b4d270d5ef35461b643b1 + md5: 52be5139047efadaeeb19c6a5103f92a + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + size: 14222 + timestamp: 1762868213144 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + size: 18074 + timestamp: 1733247158254 +- conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.11.0-pyhcf101f3_0.conda + sha256: 7378b5b9d81662d73a906fabfc2fb81daddffe8dc0680ed9cda7a9562af894b0 + md5: 814472b61da9792fae28156cb9ee54f5 + depends: + - exceptiongroup >=1.0.2 + - idna >=2.8 + - python >=3.10 + - sniffio >=1.1 + - typing_extensions >=4.5 + - python + constrains: + - trio >=0.31.0 + - uvloop >=0.21 + license: MIT + license_family: MIT + size: 138159 + timestamp: 1758634638734 +- conda: https://conda.anaconda.org/conda-forge/noarch/asgiref-3.11.0-pyhd8ed1ab_0.conda + sha256: 4c64237bf5ef6e16ef0c6ad31145dd5aed9f986c1a1becbe5abd17d9b4556ea2 + md5: 9fbe495cd313f37898d8eea42329faba + depends: + - python >=3.10 + - typing_extensions >=4 + license: BSD-3-Clause + license_family: BSD + size: 27187 + timestamp: 1763585269736 +- conda: https://conda.anaconda.org/conda-forge/noarch/async-timeout-5.0.1-pyhd8ed1ab_1.conda + sha256: 33d12250c870e06c9a313c6663cfbf1c50380b73dfbbb6006688c3134b29b45a + md5: 5d842988b11a8c3ab57fb70840c83d24 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 11763 + timestamp: 1733235428203 +- conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyh71513ae_0.conda + sha256: f6c3c19fa599a1a856a88db166c318b148cac3ee4851a9905ed8a04eeec79f45 + md5: c7944d55af26b6d2d7629e27e9a972c1 + depends: + - python >=3.10 + license: MIT + license_family: MIT + size: 60101 + timestamp: 1759762331492 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.1-h7ca4310_7.conda + sha256: 03c997e14a637fc67e237ba9ef5c8d4cbac0ea57003fe726249fcba227c971ce + md5: 6e91a9182506f6715c25c3ab80990653 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-http >=0.10.7,<0.10.8.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 122989 + timestamp: 1763068404203 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.9.1-h8818502_7.conda + sha256: faf55e041f8ebb8c013cbc53f02d8548d5bc855b192d092b7aa4f5f12cb94db6 + md5: 5911d3f258ad38448633e3cae7974dce + depends: + - __osx >=11.0 + - aws-c-http >=0.10.7,<0.10.8.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 106605 + timestamp: 1763068447505 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.10-h346e085_1.conda + sha256: 4aee0ccb53fb3ee5d9c902c7feb7464562a6cfd4ae55ac280670d26493dbe98a + md5: 7e6b378cfb6ad918a5fa52bd7741ab20 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - libgcc >=14 + - openssl >=3.5.4,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 55692 + timestamp: 1762858412739 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.10-hca30140_1.conda + sha256: ab39fc0e5146cee1c770fa8aa80a6d236506e1e44f2000408be7f62d14fef721 + md5: 4fc87188540710b79f4e4837968aff6c + depends: + - __osx >=11.0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 44939 + timestamp: 1762858956197 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.5-hb03c661_1.conda + sha256: f5876cc9792346ecdb0326f16f38b2f2fd7b5501228c56419330338fcf37e676 + md5: f1d45413e1c41a7eff162bf702c02cea + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + size: 238560 + timestamp: 1762858460824 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.12.5-hc919400_1.conda + sha256: 48577d647f5e9e7fec531b152e3e31f7845ba81ae2e59529a97eac57adb427ae + md5: 7338b3d3f6308f375c94370728df10fc + depends: + - __osx >=11.0 + license: Apache-2.0 + license_family: Apache + size: 223540 + timestamp: 1762858953852 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.1-h7e655bb_8.conda + sha256: e91d2fc0fddf069b8d39c0ce03eca834673702f7e17eda8e7ffc4558b948053d + md5: 1baf55dfcc138d98d437309e9aba2635 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 22138 + timestamp: 1762957433991 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.1-h61d5560_8.conda + sha256: c42c905ea099ddc93f1d517755fb740cc26514ca4e500f697241d04980fda03d + md5: ea7a505949c1bf4a51b2cccc89f8120d + depends: + - __osx >=11.0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 21066 + timestamp: 1762957452685 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.6-h3cb25bf_6.conda + sha256: bdf4cd6f3e5aca07cd3cb935d5913eb95b76ede7e8c24aa6a919b2b8ff2e3a6f + md5: 874d910adf3debe908b1e8e5847e0014 + depends: + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 58969 + timestamp: 1762957401979 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.6-h18584fc_6.conda + sha256: 1e6c979bc5fe42c0252ca9104b08046085222e2c384187b8030e179d6e6afb6a + md5: 217309e051c2e6cbf035b5d203154d61 + depends: + - libcxx >=19 + - __osx >=11.0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 51811 + timestamp: 1762957464804 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.7-hc5c8343_4.conda + sha256: 8d13ad2250a28e3dcebcc894615702483bf2b90cbdc7f20f329e6ecb7f9e177a + md5: b6fdadda34f2a60870980607ef469e39 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-compression >=0.3.1,<0.3.2.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + license: Apache-2.0 + license_family: APACHE + size: 224435 + timestamp: 1763054477317 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.10.7-hcd69b29_4.conda + sha256: 83c89cb858fc1f2c4f12fc48b92f0500f3b75c5f178be7c2fe11c7b40902485c + md5: 9f62f3d038641e5aaebe15e3aa0a81d2 + depends: + - __osx >=11.0 + - aws-c-compression >=0.3.1,<0.3.2.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 170786 + timestamp: 1763054502478 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.23.3-ha76f1cc_3.conda + sha256: f49cb3faa8e1dc2b4b66e9b11672c6220a387c2d431de088675388878d3f0575 + md5: 14d9fc6b1c7a823fca6cf65f595ff70d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - s2n >=1.6.0,<1.6.1.0a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + license: Apache-2.0 + license_family: APACHE + size: 181244 + timestamp: 1763043567105 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.23.3-h9710c81_3.conda + sha256: c2d6dbce4989f59ca9bcd91b3eb518649d39b760cc28f209f1d4f43f23d7ca5c + md5: 7082548c604681cc9bafafab7fb5d3c1 + depends: + - __osx >=11.0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 176167 + timestamp: 1763043601332 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.13.3-h3a25ec9_10.conda + sha256: df84140413559b860499b9540ed133d15b7eae5f17f01a98c80869be74e18071 + md5: f329cc15f3b4559cab20646245c3fc9b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-http >=0.10.7,<0.10.8.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 216089 + timestamp: 1762957365125 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.13.3-ha255ef3_10.conda + sha256: 9457b5c65135a3ea5bd52b2e9e99151366bee0f2f0c8fcb53d71af24a0f7d018 + md5: 9cd47db715a96fdfb8b4a73f1a5de587 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-http >=0.10.7,<0.10.8.0a0 + license: Apache-2.0 + license_family: APACHE + size: 150239 + timestamp: 1762957400213 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.10.1-hcb69869_2.conda + sha256: 06c47c47b6c0578da68cc3a92f059e59add1a685ea121d123e3fd267436ebdb5 + md5: 3bcec65152e70e02e8d17d296c056a82 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - aws-c-http >=0.10.7,<0.10.8.0a0 + - openssl >=3.5.4,<4.0a0 + - aws-c-auth >=0.9.1,<0.9.2.0a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + license: Apache-2.0 + license_family: APACHE + size: 149677 + timestamp: 1763077781379 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.10.1-hd860258_2.conda + sha256: 61456635298185bdd56f7aadb0c1e2ecf1c6a8967b3c9cc734e640583aa2c2a5 + md5: aedf566be89662b89085bede11c0731a + depends: + - __osx >=11.0 + - aws-c-http >=0.10.7,<0.10.8.0a0 + - aws-checksums >=0.2.7,<0.2.8.0a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + - aws-c-auth >=0.9.1,<0.9.2.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 128083 + timestamp: 1763077814498 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h7e655bb_3.conda + sha256: 8d84039ea1d33021623916edfc23f063a5bcef90e8f63ae7389e1435deb83e53 + md5: 70e83d2429b7edb595355316927dfbea + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 59204 + timestamp: 1762957305800 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h61d5560_3.conda + sha256: 5f93a440eae67085fc36c45d9169635569e71a487a8b359799281c1635befa68 + md5: 2781d442c010c31abcad68703ebbc205 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 53172 + timestamp: 1762957351489 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.7-h7e655bb_4.conda + sha256: a95b3cc8e3c0ddb664bbd26333b35986fd406f02c2c60d380833751d2d9393bd + md5: 83a6e0fc73a7f18a8024fc89455da81c + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 76774 + timestamp: 1762957236884 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.7-h61d5560_4.conda + sha256: 90b1705b8f5e42981d6dd9470218dc8994f08aa7d8ed3787dcbf5a168837d179 + md5: 4fca5f39d47042f0cb0542e0c1420875 + depends: + - __osx >=11.0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + license: Apache-2.0 + license_family: APACHE + size: 74065 + timestamp: 1762957260262 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.35.2-h2ceb62e_4.conda + sha256: 2ad7224d5db18fd94238107a0660fcbd5cd179f3b55c9633e612e1465d20f1e3 + md5: 363b3e12e49cecf931338d10114945e9 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - aws-c-event-stream >=0.5.6,<0.5.7.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - aws-c-auth >=0.9.1,<0.9.2.0a0 + - aws-c-mqtt >=0.13.3,<0.13.4.0a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + - aws-c-http >=0.10.7,<0.10.8.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-s3 >=0.10.1,<0.10.2.0a0 + license: Apache-2.0 + license_family: APACHE + size: 407871 + timestamp: 1763082700190 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.35.2-h5596a46_4.conda + sha256: 0f1930c5f9f3e94629e45117c4cf90653ae1ab81dcefc323ee74185bedba3cb6 + md5: cbecfd2ff3b568b8b206eec25e977aba + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-c-s3 >=0.10.1,<0.10.2.0a0 + - aws-c-mqtt >=0.13.3,<0.13.4.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - aws-c-cal >=0.9.10,<0.9.11.0a0 + - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 + - aws-c-auth >=0.9.1,<0.9.2.0a0 + - aws-c-event-stream >=0.5.6,<0.5.7.0a0 + - aws-c-io >=0.23.3,<0.23.4.0a0 + - aws-c-http >=0.10.7,<0.10.8.0a0 + license: Apache-2.0 + license_family: APACHE + size: 266126 + timestamp: 1763082725260 +- conda: https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.606-hd6e39bc_7.conda + sha256: 1d3c3d62ff200124be6bfad694c2d38af404f765eb9ee0ac14f249920e4138d4 + md5: 0f7a1d2e2c6cdfc3864c4c0b16ade511 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - libcurl >=8.17.0,<9.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - libzlib >=1.3.1,<2.0a0 + - aws-c-event-stream >=0.5.6,<0.5.7.0a0 + - aws-crt-cpp >=0.35.2,<0.35.3.0a0 + license: Apache-2.0 + license_family: APACHE + size: 3473236 + timestamp: 1763210963111 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.606-h95becb6_7.conda + sha256: 9b9429ac73122176eb44bcca3a1fa1987fac89c0b5b49678edd6ab611f69ea40 + md5: d761024d957bd11454accf9a181f1890 + depends: + - __osx >=11.0 + - libcxx >=19 + - aws-c-event-stream >=0.5.6,<0.5.7.0a0 + - aws-crt-cpp >=0.35.2,<0.35.3.0a0 + - libcurl >=8.17.0,<9.0a0 + - aws-c-common >=0.12.5,<0.12.6.0a0 + - libzlib >=1.3.1,<2.0a0 + license: Apache-2.0 + license_family: APACHE + size: 3121519 + timestamp: 1763210979152 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.1-h3a458e0_0.conda + sha256: cba633571e7368953520a4f66dc74c3942cc12f735e0afa8d3d5fc3edf35c866 + md5: 1d4e0d37da5f3c22ecd44033f673feba + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + size: 348231 + timestamp: 1760926677260 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.16.1-h88fedcc_0.conda + sha256: d995413e4daf19ee3120f3ab9f0c9e330771787f33cbd4a33d8e5445f52022e3 + md5: fbe485a39b05090c0b5f8bb4febcd343 + depends: + - __osx >=11.0 + - libcurl >=8.14.1,<9.0a0 + - libcxx >=19 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + size: 289984 + timestamp: 1760927117177 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.2-h3a5f585_1.conda + sha256: fc1df5ea2595f4f16d0da9f7713ce5fed20cb1bfc7fb098eda7925c7d23f0c45 + md5: 4e921d9c85e6559c60215497978b3cdb + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + size: 249684 + timestamp: 1761066654684 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.13.2-h853621b_1.conda + sha256: a4ed52062025035d9c1b3d8c70af39496fc5153cc741420139a770bc1312cfd6 + md5: fac63edc393d7035ab23fbccdeda34f4 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - libcxx >=19 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + size: 167268 + timestamp: 1761066827371 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.15.0-h2a74896_1.conda + sha256: 58879f33cd62c30a4d6a19fd5ebc59bd0c4560f575bd02645d93d342b6f881d2 + md5: ffd553ff98ce5d74d3d89ac269153149 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - azure-storage-common-cpp >=12.11.0,<12.11.1.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + size: 576406 + timestamp: 1761080005291 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.15.0-h10d327b_1.conda + sha256: 274267b458ed51f4b71113fe615121fabd6f1d7b62ebfefdad946f8436a5db8e + md5: 443b74cf38c6b0f4b675c0517879ce69 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - azure-storage-common-cpp >=12.11.0,<12.11.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + size: 425175 + timestamp: 1761080947110 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.11.0-h3d7a050_1.conda + sha256: eb590e5c47ee8e6f8cc77e9c759da860ae243eed56aceb67ce51db75f45c9a50 + md5: 89985ba2a3742f34be6aafd6a8f3af8c + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libxml2 + - libxml2-16 >=2.14.6 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + size: 149620 + timestamp: 1761066643066 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.11.0-h7e4aa5d_1.conda + sha256: 74803bd26983b599ea54ff1267a0c857ff37ccf6f849604a72eb63d8d30e4425 + md5: ac9113ea0b7ed5ecf452503f82bf2956 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - libcxx >=19 + - libxml2 + - libxml2-16 >=2.14.6 + - openssl >=3.5.4,<4.0a0 + license: MIT + license_family: MIT + size: 121744 + timestamp: 1761066874537 +- conda: https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.13.0-hf38f1be_1.conda + sha256: 9f3d0f484e97cef5f019b7faef0c07fb7ee6c584e3a6e2954980f440978a365e + md5: f10b9303c7239fbce3580a60a92bcf97 + depends: + - __glibc >=2.17,<3.0.a0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - azure-storage-blobs-cpp >=12.15.0,<12.15.1.0a0 + - azure-storage-common-cpp >=12.11.0,<12.11.1.0a0 + - libgcc >=14 + - libstdcxx >=14 + license: MIT + license_family: MIT + size: 299198 + timestamp: 1761094654852 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.13.0-hb288d13_1.conda + sha256: 2205e24d587453a04b075f86c59e3e72ad524c447fc5be61d7d1beb3cf2d7661 + md5: 595091ae43974e5059d6eabf0a6a7aa5 + depends: + - __osx >=11.0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - azure-storage-blobs-cpp >=12.15.0,<12.15.1.0a0 + - azure-storage-common-cpp >=12.11.0,<12.11.1.0a0 + - libcxx >=19 + license: MIT + license_family: MIT + size: 197152 + timestamp: 1761094913245 +- conda: https://conda.anaconda.org/conda-forge/noarch/backoff-2.2.1-pyhd8ed1ab_1.conda + sha256: f334115c6b0c6c2cd0d28595365f205ec7eaa60bcc5ff91a75d7245f728be820 + md5: a38b801f2bcc12af80c2e02a9e4ce7d9 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 18816 + timestamp: 1733771192649 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314hdfeb8a1_0.conda + sha256: 9f6d339fb78b647be35e3564dac453d8d2f1b865ba72fb961eaac41061368699 + md5: 3ef9d2a701760467b9db2338b6cd926f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - libbrotlicommon 1.2.0 h09219d5_0 + license: MIT + license_family: MIT + size: 368319 + timestamp: 1761592337171 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314h95ef04c_0.conda + sha256: 231c3e2d0a2635f51e4e0fd56ba0def25b21a7c484d31e863f261823af5351e3 + md5: 5f71e1aa8d7982bda0a87b6bfd5c71fd + depends: + - __osx >=11.0 + - libcxx >=19 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - libbrotlicommon 1.2.0 h87ba0bc_0 + license: MIT + license_family: MIT + size: 359535 + timestamp: 1761592749203 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda + sha256: c30daba32ddebbb7ded490f0e371eae90f51e72db620554089103b4a6934b0d5 + md5: 51a19bba1b8ebfb60df25cde030b7ebc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: bzip2-1.0.6 + license_family: BSD + size: 260341 + timestamp: 1757437258798 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda + sha256: b456200636bd5fecb2bec63f7e0985ad2097cf1b83d60ce0b6968dffa6d02aa1 + md5: 58fd217444c2a5701a44244faf518206 + depends: + - __osx >=11.0 + license: bzip2-1.0.6 + license_family: BSD + size: 125061 + timestamp: 1757437486465 +- conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda + sha256: f8003bef369f57396593ccd03d08a8e21966157269426f71e943f96e4b579aeb + md5: f7f0d6cc2dc986d42ac2689ec88192be + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 206884 + timestamp: 1744127994291 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.5-h5505292_0.conda + sha256: b4bb55d0806e41ffef94d0e3f3c97531f322b3cb0ca1f7cdf8e47f62538b7a2b + md5: f8cd1beb98240c7edb1a95883360ccfa + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 179696 + timestamp: 1744128058734 +- conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda + sha256: b986ba796d42c9d3265602bc038f6f5264095702dd546c14bc684e60c385e773 + md5: f0991f0f84902f6b6009b4d2350a83aa + depends: + - __unix + license: ISC + size: 152432 + timestamp: 1762967197890 +- conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda + sha256: 083a2bdad892ccf02b352ecab38ee86c3e610ba9a4b11b073ea769d55a115d32 + md5: 96a02a5c1a65470a7e4eedb644c872fd + depends: + - python >=3.10 + license: ISC + size: 157131 + timestamp: 1762976260320 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py314h4a8dc5f_1.conda + sha256: c6339858a0aaf5d939e00d345c98b99e4558f285942b27232ac098ad17ac7f8e + md5: cf45f4278afd6f4e6d03eda0f435d527 + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - pycparser + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + size: 300271 + timestamp: 1761203085220 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py314h44086f9_1.conda + sha256: 5b5ee5de01eb4e4fd2576add5ec9edfc654fbaf9293e7b7ad2f893a67780aa98 + md5: 10dd19e4c797b8f8bdb1ec1fbb6821d7 + depends: + - __osx >=11.0 + - libffi >=3.5.2,<3.6.0a0 + - pycparser + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + size: 292983 + timestamp: 1761203354051 +- conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda + sha256: b32f8362e885f1b8417bac2b3da4db7323faa12d5db62b7fd6691c02d60d6f59 + md5: a22d1fd9bf98827e280a02875d9a007a + depends: + - python >=3.10 + license: MIT + license_family: MIT + size: 50965 + timestamp: 1760437331772 +- conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh707e725_0.conda + sha256: 970b12fb186c3451eee9dd0f10235aeb75fb570b0e9dc83250673c2f0b196265 + md5: 9ba00b39e03a0afb2b1cc0767d4c6175 + depends: + - __unix + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + size: 92604 + timestamp: 1763248639281 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 27011 + timestamp: 1733218222191 +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.0-py314hd8ed1ab_102.conda + noarch: generic + sha256: 8e2a33b36d36820698840bf0c1ed50e5dd4bdeaa434c7b4f5e13d421225b0414 + md5: ff3061d315c4a988fa1c29c543800780 + depends: + - python >=3.14,<3.15.0a0 + - python_abi * *_cp314 + license: Python-2.0 + size: 49003 + timestamp: 1761175499490 +- conda: https://conda.anaconda.org/conda-forge/noarch/datasets-4.4.1-pyhcf101f3_0.conda + sha256: 61cf1884f4d00e3ec07d3ed794da3e4ace768e62eab1b7c006ffb5323cd72d1c + md5: 7eb84d1a64b52238d3b5a26db71bf85f + depends: + - python >=3.10 + - filelock + - numpy >=1.17 + - pyarrow >=21.0.0 + - dill >=0.3.0,<0.4.1 + - pandas + - requests >=2.32.2 + - httpx <1.0.0 + - tqdm >=4.66.3 + - python-xxhash + - multiprocess <0.70.19 + - fsspec >=2023.1.0,<=2025.10.0 + - huggingface_hub >=0.25.0,<2.0 + - packaging + - pyyaml >=5.1 + - aiohttp + - python + license: Apache-2.0 + license_family: APACHE + size: 367584 + timestamp: 1762623833829 +- conda: https://conda.anaconda.org/conda-forge/noarch/deprecated-1.3.1-pyhd8ed1ab_0.conda + sha256: c994a70449d548dd388768090c71c1da81e1e128a281547ab9022908d46878c5 + md5: bf74a83f7a0f2a21b5d709997402cac4 + depends: + - python >=3.10 + - wrapt <2,>=1.10 + license: MIT + license_family: MIT + size: 15815 + timestamp: 1761813872696 +- conda: https://conda.anaconda.org/conda-forge/noarch/dill-0.4.0-pyhd8ed1ab_0.conda + sha256: 43dca52c96fde0c4845aaff02bcc92f25e1c2e5266ddefc2eac1a3de0960a3b1 + md5: 885745570573eb6a08e021841928297a + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 90864 + timestamp: 1744798629464 +- conda: https://conda.anaconda.org/conda-forge/noarch/dnspython-2.8.0-pyhcf101f3_0.conda + sha256: ef1e7b8405997ed3d6e2b6722bd7088d4a8adf215e7c88335582e65651fb4e05 + md5: d73fdc05f10693b518f52c994d748c19 + depends: + - python >=3.10,<4.0.0 + - sniffio + - python + constrains: + - aioquic >=1.2.0 + - cryptography >=45 + - httpcore >=1.0.0 + - httpx >=0.28.0 + - h2 >=4.2.0 + - idna >=3.10 + - trio >=0.30 + - wmi >=1.5.1 + license: ISC + size: 196500 + timestamp: 1757292856922 +- conda: https://conda.anaconda.org/conda-forge/noarch/email-validator-2.3.0-pyhd8ed1ab_0.conda + sha256: c37320864c35ef996b0e02e289df6ee89582d6c8e233e18dc9983375803c46bb + md5: 3bc0ac31178387e8ed34094d9481bfe8 + depends: + - dnspython >=2.0.0 + - idna >=2.0.0 + - python >=3.10 + license: Unlicense + size: 46767 + timestamp: 1756221480106 +- conda: https://conda.anaconda.org/conda-forge/noarch/email_validator-2.3.0-hd8ed1ab_0.conda + sha256: 6a518e00d040fcad016fb2dde29672aa3476cd9ae33ea5b7b257222e66037d89 + md5: 2452e434747a6b742adc5045f2182a8e + depends: + - email-validator >=2.3.0,<2.3.1.0a0 + license: Unlicense + size: 7077 + timestamp: 1756221480651 +- conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda + sha256: ce61f4f99401a4bd455b89909153b40b9c823276aefcbb06f2044618696009ca + md5: 72e42d28960d875c7654614f8b50939a + depends: + - python >=3.9 + - typing_extensions >=4.6.0 + license: MIT and PSF-2.0 + size: 21284 + timestamp: 1746947398083 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-0.121.3-hf029e93_0.conda + sha256: 4279a2e710797025fbdefe9e84cceca9c4700913ee639effe1af9495b00644dd + md5: a46db4c360e4fdef0ad8121c457d4575 + depends: + - fastapi-core ==0.121.3 pyhcf101f3_0 + - email_validator + - fastapi-cli + - httpx + - jinja2 + - python-multipart + - uvicorn-standard + license: MIT + license_family: MIT + size: 4786 + timestamp: 1763582699737 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-cli-0.0.16-pyhcf101f3_1.conda + sha256: 4136b0c277188b205332983278c7b278ea946dc1c78a381e0f5bc79204b8ac97 + md5: 4f82a266e2d5b199db16cdb42341d785 + depends: + - python >=3.10 + - rich-toolkit >=0.14.8 + - tomli >=2.0.0 + - typer >=0.15.1 + - uvicorn-standard >=0.15.0 + - python + license: MIT + license_family: MIT + size: 19029 + timestamp: 1763068963965 +- conda: https://conda.anaconda.org/conda-forge/noarch/fastapi-core-0.121.3-pyhcf101f3_0.conda + sha256: 270b19634f4723e99f7679908ac7eb7308b40ad15f3094d5e976fbb71d8edc34 + md5: 1244984ef9e551a0d291ea181bd6f93c + depends: + - python >=3.10 + - annotated-doc >=0.0.2 + - starlette >=0.40.0,<0.51.0 + - typing_extensions >=4.8.0 + - pydantic >=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0 + - python + constrains: + - email_validator >=2.0.0 + - fastapi-cli >=0.0.8 + - httpx >=0.23.0,<1.0.0 + - jinja2 >=3.1.5 + - python-multipart >=0.0.18 + - uvicorn-standard >=0.12.0 + license: MIT + license_family: MIT + size: 87131 + timestamp: 1763582699733 +- conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.0-pyhd8ed1ab_0.conda + sha256: 19025a4078ff3940d97eb0da29983d5e0deac9c3e09b0eabf897daeaf9d1114e + md5: 66b8b26023b8efdf8fcb23bac4b6325d + depends: + - python >=3.10 + license: Unlicense + size: 17976 + timestamp: 1759948208140 +- conda: https://conda.anaconda.org/conda-forge/noarch/frozenlist-1.7.0-pyhf298e5d_0.conda + sha256: d065c6c76ba07c148b07102f89fd14e39e4f0b2c022ad671bbef8fda9431ba1b + md5: 3998c9592e3db2f6809e4585280415f4 + depends: + - python >=3.9 + track_features: + - frozenlist_no_compile + license: Apache-2.0 + license_family: APACHE + size: 18952 + timestamp: 1752167260183 +- conda: https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.10.0-pyhd8ed1ab_0.conda + sha256: df5cb57bb668cd5b2072d8bd66380ff7acb12e8c337f47dd4b9a75a6a6496a6d + md5: d18004c37182f83b9818b714825a7627 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + size: 146592 + timestamp: 1761840236679 +- conda: https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda + sha256: 6c33bf0c4d8f418546ba9c250db4e4221040936aef8956353bc764d4877bc39a + md5: d411fc29e338efb48c5fd4576d71d881 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-3-Clause + license_family: BSD + size: 119654 + timestamp: 1726600001928 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda + sha256: fd56ed8a1dab72ab90d8a8929b6f916a6d9220ca297ff077f8f04c5ed3408e20 + md5: 57a511a5905caa37540eb914dfcbf1fb + depends: + - __osx >=11.0 + - libcxx >=17 + license: BSD-3-Clause + license_family: BSD + size: 82090 + timestamp: 1726600145480 +- conda: https://conda.anaconda.org/conda-forge/noarch/gguf-0.17.1-pyhc364b38_0.conda + sha256: 06aa364c6ce109e21858fc016a430c22f738fe6377c67944504df7fc0da3ec20 + md5: aaaa7074fd79c4e1e79b3e1af5a77efa + depends: + - python >=3.8 + - numpy >=1.17 + - tqdm >=4.27 + - pyyaml >=5.1 + - sentencepiece >=0.1.98,<=0.2.0 + - python + license: MIT + license_family: MIT + size: 92085 + timestamp: 1750400728782 +- conda: https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda + sha256: dc824dc1d0aa358e28da2ecbbb9f03d932d976c8dca11214aa1dcdfcbd054ba2 + md5: ff862eebdfeb2fd048ae9dc92510baca + depends: + - gflags >=2.2.2,<2.3.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: BSD-3-Clause + license_family: BSD + size: 143452 + timestamp: 1718284177264 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda + sha256: 9fc77de416953aa959039db72bc41bfa4600ae3ff84acad04a7d0c1ab9552602 + md5: fef68d0a95aa5b84b5c1a4f6f3bf40e1 + depends: + - __osx >=11.0 + - gflags >=2.2.2,<2.3.0a0 + - libcxx >=16 + license: BSD-3-Clause + license_family: BSD + size: 112215 + timestamp: 1718284365403 +- conda: https://conda.anaconda.org/conda-forge/noarch/googleapis-common-protos-1.72.0-pyhd8ed1ab_0.conda + sha256: c09ba4b360a0994430d2fe4a230aa6518cd3e6bfdc51a7af9d35d35a25908bb5 + md5: 003094932fb90de018f77a273b8a509b + depends: + - protobuf >=3.20.2,<7.0.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 + - python >=3.10 + license: Apache-2.0 + license_family: APACHE + size: 142961 + timestamp: 1762522289200 +- conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py314h2d847ca_1.conda + sha256: 98e20ea067291d3ff9a2aead0f0a6b7086cc312c2024b2453ac5c5b129386d5b + md5: 6742a46f5bf364f193cfb95a80dab23e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgrpc 1.73.1 h3288cfb_1 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + size: 912735 + timestamp: 1761058730109 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.73.1-py314h7689434_1.conda + sha256: 4984cd85bf931a29c651369f5525e96489f248936fffac249862312068dea495 + md5: 9c09a98fc257aa3bde2a322f96d97282 + depends: + - __osx >=11.0 + - libcxx >=19 + - libgrpc 1.73.1 h3063b79_1 + - libzlib >=1.3.1,<2.0a0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + size: 812240 + timestamp: 1761053925680 +- conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda + sha256: f64b68148c478c3bfc8f8d519541de7d2616bf59d44485a5271041d40c061887 + md5: 4b69232755285701bc86a5afe4d9933a + depends: + - python >=3.9 + - typing_extensions + license: MIT + license_family: MIT + size: 37697 + timestamp: 1745526482242 +- conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda + sha256: 84c64443368f84b600bfecc529a1194a3b14c3656ee2e832d15a20e0329b6da3 + md5: 164fc43f0b53b6e3a7bc7dce5e4f1dc9 + depends: + - python >=3.10 + - hyperframe >=6.1,<7 + - hpack >=4.1,<5 + - python + license: MIT + license_family: MIT + size: 95967 + timestamp: 1756364871835 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hf-transfer-0.1.9-py314h922f143_2.conda + sha256: 27c84c4b9e4179696c37b9f5787a0ab60de2f867a480aca8542ad4b2386af4d3 + md5: d7dfce3c787dc5b84254a2a54aebe079 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - python_abi 3.14.* *_cp314 + - openssl >=3.5.2,<4.0a0 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 1304128 + timestamp: 1756624832097 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/hf-transfer-0.1.9-py314h57a929c_2.conda + sha256: 5851eba2dbcea7670015dd96cdf0f19ff508cc4d7397724b3daad079666ea8f6 + md5: f186b44e09452d390ee56ef214d08a76 + depends: + - python + - python 3.14.* *_cp314 + - __osx >=11.0 + - openssl >=3.5.2,<4.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 1190299 + timestamp: 1756624925269 +- conda: https://conda.anaconda.org/conda-forge/linux-64/hf-xet-1.2.0-py310hb823017_0.conda + noarch: python + sha256: c134796866f5d2a6851512f1598399df157ff02db81eb7bbac2964681d9ee96b + md5: 77edbfd5f562cb044ef73ac185581df9 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - _python_abi3_support 1.* + - cpython >=3.10 + - openssl >=3.5.4,<4.0a0 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2703061 + timestamp: 1761341459458 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/hf-xet-1.2.0-py310h6ce4931_0.conda + noarch: python + sha256: f5d646c8799db8d2b176cfa743bf2bd7527e0a67f009633eb44177429248604e + md5: fdabf4874c0a6583e5b0d17393902e68 + depends: + - python + - __osx >=11.0 + - _python_abi3_support 1.* + - cpython >=3.10 + - openssl >=3.5.4,<4.0a0 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 2493380 + timestamp: 1761341556504 +- conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda + sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba + md5: 0a802cb9888dd14eeefc611f05c40b6e + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 30731 + timestamp: 1737618390337 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda + sha256: 04d49cb3c42714ce533a8553986e1642d0549a05dc5cc48e0d43ff5be6679a5b + md5: 4f14640d58e2cc0aa0819d9d8ba125bb + depends: + - python >=3.9 + - h11 >=0.16 + - h2 >=3,<5 + - sniffio 1.* + - anyio >=4.0,<5.0 + - certifi + - python + license: BSD-3-Clause + license_family: BSD + size: 49483 + timestamp: 1745602916758 +- conda: https://conda.anaconda.org/conda-forge/linux-64/httptools-0.7.1-py314h5bd0f2a_1.conda + sha256: 91bfdf1dad0fa57efc2404ca00f5fee8745ad9b56ec1d0df298fd2882ad39806 + md5: 067a52c66f453b97771650bbb131e2b5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + size: 99037 + timestamp: 1762504051423 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/httptools-0.7.1-py314h0612a62_1.conda + sha256: 042343211aafabab79120d0deda73358ddd3cb61b9ad55307108a275976fccfa + md5: 0ca03669a236fee8ce414e166d0bbf23 + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: MIT + license_family: MIT + size: 90384 + timestamp: 1762504632522 +- conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda + sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 + md5: d6989ead454181f4f9bc987d3dc4e285 + depends: + - anyio + - certifi + - httpcore 1.* + - idna + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 63082 + timestamp: 1733663449209 +- conda: https://conda.anaconda.org/conda-forge/noarch/huggingface_hub-0.36.0-pyhd8ed1ab_0.conda + sha256: 7ba53c55530b8bbbd64805b5820a9f4dd35b3d749cdd57092b09f07f71447da6 + md5: 39e591c87bc60fcf0944f5b878ed3e27 + depends: + - filelock + - fsspec >=2023.5.0 + - hf-xet >=1.1.3,<2.0.0 + - packaging >=20.9 + - python >=3.10 + - pyyaml >=5.1 + - requests + - tqdm >=4.42.1 + - typing-extensions >=3.7.4.3 + - typing_extensions >=3.7.4.3 + license: Apache-2.0 + license_family: APACHE + size: 338701 + timestamp: 1761225975526 +- conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda + sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 + md5: 8e6923fc12f1fe8f8c4e5c9f343256ac + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 17397 + timestamp: 1737618427549 +- conda: https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda + sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e + md5: 8b189310083baabfb622af68fd9d3ae3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + license: MIT + license_family: MIT + size: 12129203 + timestamp: 1720853576813 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 + md5: 5eb22c1d7b3fc4abb50d92d621583137 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 11857802 + timestamp: 1720853997952 +- conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda + sha256: ae89d0299ada2a3162c2614a9d26557a92aa6a77120ce142f8e0109bbf0342b0 + md5: 53abe63df7e10a6ba605dc5f9f961d36 + depends: + - python >=3.10 + license: BSD-3-Clause + license_family: BSD + size: 50721 + timestamp: 1760286526795 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda + sha256: c18ab120a0613ada4391b15981d86ff777b5690ca461ea7e9e49531e8f374745 + md5: 63ccfdc3a3ce25b027b8767eb722fca8 + depends: + - python >=3.9 + - zipp >=3.20 + - python + license: Apache-2.0 + license_family: APACHE + size: 34641 + timestamp: 1747934053147 +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda + sha256: f1ac18b11637ddadc05642e8185a851c7fab5998c6f5470d716812fae943b2af + md5: 446bd6c8cb26050d528881df495ce646 + depends: + - markupsafe >=2.0 + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 112714 + timestamp: 1741263433881 +- conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda + sha256: 19d8bd5bb2fde910ec59e081eeb59529491995ce0d653a5209366611023a0b3a + md5: 4ebae00eae9705b0c3d6d1018a81d047 + depends: + - importlib-metadata >=4.8.3 + - jupyter_core >=4.12,!=5.0.* + - python >=3.9 + - python-dateutil >=2.8.2 + - pyzmq >=23.0 + - tornado >=6.2 + - traitlets >=5.3 + license: BSD-3-Clause + license_family: BSD + size: 106342 + timestamp: 1733441040958 +- conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda + sha256: 1d34b80e5bfcd5323f104dbf99a2aafc0e5d823019d626d0dce5d3d356a2a52a + md5: b38fe4e78ee75def7e599843ef4c1ab0 + depends: + - __unix + - python + - platformdirs >=2.5 + - python >=3.10 + - traitlets >=5.3 + - python + constrains: + - pywin32 >=300 + license: BSD-3-Clause + license_family: BSD + size: 65503 + timestamp: 1760643864586 +- conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda + sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 + md5: b38117a3c920364aff79f870c984b4a3 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: LGPL-2.1-or-later + size: 134088 + timestamp: 1754905959823 +- conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda + sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 + md5: 3f43953b7d3fb3aaa1d0d0723d91e368 + depends: + - keyutils >=1.6.1,<2.0a0 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - libgcc-ng >=12 + - libstdcxx-ng >=12 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1370023 + timestamp: 1719463201255 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda + sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b + md5: c6dc8a0fdec13a0565936655c33069a1 + depends: + - __osx >=11.0 + - libcxx >=16 + - libedit >=3.1.20191231,<3.2.0a0 + - libedit >=3.1.20191231,<4.0a0 + - openssl >=3.3.1,<4.0a0 + license: MIT + license_family: MIT + size: 1155530 + timestamp: 1719463474401 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda + sha256: d6a61830a354da022eae93fa896d0991385a875c6bba53c82263a289deda9db8 + md5: 000e85703f0fd9594c81710dd5066471 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 248046 + timestamp: 1739160907615 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.17-h7eeda09_0.conda + sha256: 310a62c2f074ebd5aa43b3cd4b00d46385ce680fa2132ecee255a200e2d2f15f + md5: 92a61fd30b19ebd5c1621a5bfe6d8b5f + depends: + - __osx >=11.0 + - libjpeg-turbo >=3.0.0,<4.0a0 + - libtiff >=4.7.0,<4.8.0a0 + license: MIT + license_family: MIT + size: 212125 + timestamp: 1739161108467 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-hbd61a6d_1.conda + sha256: 94ca574cba401afa9a6feacb5c1a2ef2af4f24deab2b2266064364e15fd7b642 + md5: 74973af25f7e298f684f7782f4e8b43b + depends: + - __glibc >=2.17,<3.0.a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - binutils_impl_linux-64 2.45 + license: GPL-3.0-only + size: 725624 + timestamp: 1763687084981 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda + sha256: 412381a43d5ff9bbed82cd52a0bbca5b90623f62e41007c9c42d3870c60945ff + md5: 9344155d33912347b37f0ae6c410a835 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: Apache-2.0 + license_family: Apache + size: 264243 + timestamp: 1745264221534 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-hd64df32_1.conda + sha256: 12361697f8ffc9968907d1a7b5830e34c670e4a59b638117a2cdfed8f63a38f8 + md5: a74332d9b60b62905e3d30709df08bf1 + depends: + - __osx >=11.0 + - libcxx >=18 + license: Apache-2.0 + license_family: Apache + size: 188306 + timestamp: 1745264362794 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda + sha256: dcd1429a1782864c452057a6c5bc1860f2b637dc20a2b7e6eacd57395bbceff8 + md5: 83b160d4da3e1e847bf044997621ed63 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + constrains: + - libabseil-static =20250512.1=cxx17* + - abseil-cpp =20250512.1 + license: Apache-2.0 + license_family: Apache + size: 1310612 + timestamp: 1750194198254 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20250512.1-cxx17_hd41c47c_0.conda + sha256: 7f0ee9ae7fa2cf7ac92b0acf8047c8bac965389e48be61bf1d463e057af2ea6a + md5: 360dbb413ee2c170a0a684a33c4fc6b8 + depends: + - __osx >=11.0 + - libcxx >=18 + constrains: + - libabseil-static =20250512.1=cxx17* + - abseil-cpp =20250512.1 + license: Apache-2.0 + license_family: Apache + size: 1174081 + timestamp: 1750194620012 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-22.0.0-h773bc41_4_cpu.conda + build_number: 4 + sha256: f781e543cf0884e860d80a70a53ca94e4073a7ed0691bac4ba2726362ceefa7e + md5: 9d89be0b1ca8be7eedf821a365926338 + depends: + - __glibc >=2.17,<3.0.a0 + - aws-crt-cpp >=0.35.2,<0.35.3.0a0 + - aws-sdk-cpp >=1.11.606,<1.11.607.0a0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - azure-identity-cpp >=1.13.2,<1.13.3.0a0 + - azure-storage-blobs-cpp >=12.15.0,<12.15.1.0a0 + - azure-storage-files-datalake-cpp >=12.13.0,<12.13.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libgcc >=14 + - libgoogle-cloud >=2.39.0,<2.40.0a0 + - libgoogle-cloud-storage >=2.39.0,<2.40.0a0 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.2.1,<2.2.2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - arrow-cpp <0.0a0 + - apache-arrow-proc =*=cpu + - parquet-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + size: 6314983 + timestamp: 1763230013181 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-22.0.0-h4a3aeba_4_cpu.conda + build_number: 4 + sha256: 1791eb7033721a0e94198867bc7ee54d92d45d30bfd441331ff703651d7630eb + md5: 91aa4b66daf8ac61548cd27c5112655e + depends: + - __osx >=11.0 + - aws-crt-cpp >=0.35.2,<0.35.3.0a0 + - aws-sdk-cpp >=1.11.606,<1.11.607.0a0 + - azure-core-cpp >=1.16.1,<1.16.2.0a0 + - azure-identity-cpp >=1.13.2,<1.13.3.0a0 + - azure-storage-blobs-cpp >=12.15.0,<12.15.1.0a0 + - azure-storage-files-datalake-cpp >=12.13.0,<12.13.1.0a0 + - bzip2 >=1.0.8,<2.0a0 + - glog >=0.7.1,<0.8.0a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libbrotlidec >=1.2.0,<1.3.0a0 + - libbrotlienc >=1.2.0,<1.3.0a0 + - libcxx >=19 + - libgoogle-cloud >=2.39.0,<2.40.0a0 + - libgoogle-cloud-storage >=2.39.0,<2.40.0a0 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - orc >=2.2.1,<2.2.2.0a0 + - snappy >=1.2.2,<1.3.0a0 + - zstd >=1.5.7,<1.6.0a0 + constrains: + - parquet-cpp <0.0a0 + - apache-arrow-proc =*=cpu + - arrow-cpp <0.0a0 + license: Apache-2.0 + license_family: APACHE + size: 4184287 + timestamp: 1763229706599 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-22.0.0-h635bf11_4_cpu.conda + build_number: 4 + sha256: 1d09263e6aee38d6b3a8380b2ab11cb5eefce17aee32c98dd4b7b56eccd28637 + md5: 20f1a4625bce6e9b41e01232895450d9 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 22.0.0 h773bc41_4_cpu + - libarrow-compute 22.0.0 h8c2c5c3_4_cpu + - libgcc >=14 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + size: 579976 + timestamp: 1763230195883 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-22.0.0-hc317990_4_cpu.conda + build_number: 4 + sha256: 02c86b58b5dff84c7d01be00dc470b9d53f35c67ff3c8115f1441303392dab2d + md5: e8b3dc59675ac45f8d10d31f1fd59a87 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libarrow 22.0.0 h4a3aeba_4_cpu + - libarrow-compute 22.0.0 h75845d1_4_cpu + - libcxx >=19 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + license: Apache-2.0 + license_family: APACHE + size: 518351 + timestamp: 1763230069395 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-compute-22.0.0-h8c2c5c3_4_cpu.conda + build_number: 4 + sha256: 3942bcab9ef4968ce0209a2538fe2462de5cc62e23b1a7bdf24601b04a12f707 + md5: fdecd3d6168561098fa87d767de05171 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 22.0.0 h773bc41_4_cpu + - libgcc >=14 + - libre2-11 >=2025.8.12 + - libstdcxx >=14 + - libutf8proc >=2.11.0,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + size: 2966611 + timestamp: 1763230081543 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-compute-22.0.0-h75845d1_4_cpu.conda + build_number: 4 + sha256: a94da15ab7712ef35cce7c270bed3c6e4ea56ab7f6646ce5070fc20e869a528c + md5: 461c83e1825eb0584578e7d6445ab85f + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libarrow 22.0.0 h4a3aeba_4_cpu + - libcxx >=19 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libre2-11 >=2025.8.12 + - libutf8proc >=2.11.0,<2.12.0a0 + - re2 + license: Apache-2.0 + license_family: APACHE + size: 2150204 + timestamp: 1763229832111 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-22.0.0-h635bf11_4_cpu.conda + build_number: 4 + sha256: d38262e1a40491a01ff5820f1a0320e29fb7dde62bb72b1a48286d82407cf6cf + md5: 6389644214f7707ab05f17f464863ed3 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 22.0.0 h773bc41_4_cpu + - libarrow-acero 22.0.0 h635bf11_4_cpu + - libarrow-compute 22.0.0 h8c2c5c3_4_cpu + - libgcc >=14 + - libparquet 22.0.0 h7376487_4_cpu + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + size: 578862 + timestamp: 1763230274858 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-22.0.0-hc317990_4_cpu.conda + build_number: 4 + sha256: b83e995beab71f14e2894b7f06acca803d71f08fe55a46319fbcdbf151953532 + md5: de0eff5023e9ef88889f3dd9c1834207 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libarrow 22.0.0 h4a3aeba_4_cpu + - libarrow-acero 22.0.0 hc317990_4_cpu + - libarrow-compute 22.0.0 h75845d1_4_cpu + - libcxx >=19 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libparquet 22.0.0 h0ac143b_4_cpu + - libprotobuf >=6.31.1,<6.31.2.0a0 + license: Apache-2.0 + license_family: APACHE + size: 515230 + timestamp: 1763230228332 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-22.0.0-h3f74fd7_4_cpu.conda + build_number: 4 + sha256: 305f45d97cb5e303aca8c169c3f7a4c871a19d64e1787e83d79522f4d25a05a1 + md5: 6f07bf204431fb87d8f827807d752662 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libarrow 22.0.0 h773bc41_4_cpu + - libarrow-acero 22.0.0 h635bf11_4_cpu + - libarrow-dataset 22.0.0 h635bf11_4_cpu + - libgcc >=14 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: APACHE + size: 481781 + timestamp: 1763230300086 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-substrait-22.0.0-h144af7f_4_cpu.conda + build_number: 4 + sha256: fa8614c2b82b4fbe3388709fc065822f0bd0271e0da3319a2c7ef95ac4cf6765 + md5: ec4ab23fb266c9921dfd7c724181ebc3 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libarrow 22.0.0 h4a3aeba_4_cpu + - libarrow-acero 22.0.0 hc317990_4_cpu + - libarrow-dataset 22.0.0 hc317990_4_cpu + - libcxx >=19 + - libprotobuf >=6.31.1,<6.31.2.0a0 + license: Apache-2.0 + license_family: APACHE + size: 452764 + timestamp: 1763230303022 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-1_h4a7cf45_openblas.conda + build_number: 1 + sha256: a36d1230c435d9b06c3bbd1c5c32c695bc341a413719d6e8c4bb6574818f46ea + md5: 8b39e1ae950f1b54a3959c58ca2c32b8 + depends: + - libopenblas >=0.3.30,<0.3.31.0a0 + - libopenblas >=0.3.30,<1.0a0 + constrains: + - liblapack 3.11.0 1*_openblas + - libcblas 3.11.0 1*_openblas + - mkl <2026 + - blas 2.301 openblas + - liblapacke 3.11.0 1*_openblas + license: BSD-3-Clause + license_family: BSD + size: 18492 + timestamp: 1763447017981 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-1_h51639a9_openblas.conda + build_number: 1 + sha256: 7096038e2231bfe315e7e5d3faba2371b70f9d6d897e065afd085781304dc8d1 + md5: 379254bdc34eec0bd4464935c3bff8ba + depends: + - libopenblas >=0.3.30,<0.3.31.0a0 + - libopenblas >=0.3.30,<1.0a0 + constrains: + - blas 2.301 openblas + - liblapacke 3.11.0 1*_openblas + - liblapack 3.11.0 1*_openblas + - libcblas 3.11.0 1*_openblas + - mkl <2026 + license: BSD-3-Clause + license_family: BSD + size: 18675 + timestamp: 1763447903446 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-h09219d5_0.conda + sha256: fbbcd11742bb8c96daa5f4f550f1804a902708aad2092b39bec3faaa2c8ae88a + md5: 9b3117ec960b823815b02190b41c0484 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 79664 + timestamp: 1761592192478 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-h87ba0bc_0.conda + sha256: 5968a178cf374ff6a1d247b5093174dbd91d642551f81e4cb1acbe605a86b5ae + md5: 07d43b5e2b6f4a73caed8238b60fabf5 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 79198 + timestamp: 1761592463100 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hd53d788_0.conda + sha256: f7f357c33bd10afd58072ad4402853a8522d52d00d7ae9adb161ecf719f63574 + md5: c183787d2b228775dece45842abbbe53 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 h09219d5_0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 34445 + timestamp: 1761592202559 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-h95a88de_0.conda + sha256: 9a42c71ecea8e8ffe218fda017cb394b6a2c920304518c09c0ae42f0501dfde6 + md5: 39d47dac85038e73b5f199f2b594a547 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 h87ba0bc_0 + license: MIT + license_family: MIT + size: 29366 + timestamp: 1761592481914 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-h02bd7ab_0.conda + sha256: 1370c8b1a215751c4592bf95d4b5d11bac91c577770efcb237e3a0f35c326559 + md5: b7a924e3e9ebc7938ffc7d94fe603ed3 + depends: + - __glibc >=2.17,<3.0.a0 + - libbrotlicommon 1.2.0 h09219d5_0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 298252 + timestamp: 1761592214576 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hb1b9735_0.conda + sha256: 9e05479f916548d1a383779facc4bb35a4f65a313590a81ec21818a10963eb02 + md5: 4e3fec2238527187566e26a5ddbc2f83 + depends: + - __osx >=11.0 + - libbrotlicommon 1.2.0 h87ba0bc_0 + license: MIT + license_family: MIT + size: 291133 + timestamp: 1761592499578 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-1_h0358290_openblas.conda + build_number: 1 + sha256: f39c69450d14049463a15adfffa01447cfe9e9497e323800d747ee828ae43a2b + md5: a670bff9eb7963ea41b4e09a4e4ab608 + depends: + - libblas 3.11.0 1_h4a7cf45_openblas + constrains: + - blas 2.301 openblas + - liblapack 3.11.0 1*_openblas + - liblapacke 3.11.0 1*_openblas + license: BSD-3-Clause + license_family: BSD + size: 18491 + timestamp: 1763447025579 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-1_hb0561ab_openblas.conda + build_number: 1 + sha256: 816592d4f39a30db77b5de45e532b6f536f740d333840af21fcf6daf2f0b0c18 + md5: f2b9d50745b55f4a837b333e69b5974a + depends: + - libblas 3.11.0 1_h51639a9_openblas + constrains: + - blas 2.301 openblas + - liblapacke 3.11.0 1*_openblas + - liblapack 3.11.0 1*_openblas + license: BSD-3-Clause + license_family: BSD + size: 18671 + timestamp: 1763447915947 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2 + sha256: fd1d153962764433fe6233f34a72cdeed5dcf8a883a85769e8295ce940b5b0c5 + md5: c965a5aa0d5c1c37ffc62dff36e28400 + depends: + - libgcc-ng >=9.4.0 + - libstdcxx-ng >=9.4.0 + license: BSD-3-Clause + license_family: BSD + size: 20440 + timestamp: 1633683576494 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 + sha256: 58477b67cc719060b5b069ba57161e20ba69b8695d154a719cb4b60caf577929 + md5: 32bd82a6a625ea6ce090a81c3d34edeb + depends: + - libcxx >=11.1.0 + license: BSD-3-Clause + license_family: BSD + size: 18765 + timestamp: 1633683992603 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_0.conda + sha256: 100e29ca864c32af15a5cc354f502d07b2600218740fdf2439fa7d66b50b3529 + md5: 01e149d4a53185622dc2e788281961f2 + depends: + - __glibc >=2.17,<3.0.a0 + - krb5 >=1.21.3,<1.22.0a0 + - libgcc >=14 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + size: 460366 + timestamp: 1762333743748 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_0.conda + sha256: 2980c5de44ac3ca2ecbd4a00756da1648ea2945d9e4a2ad9f216c7787df57f10 + md5: 791003efe92c17ed5949b309c61a5ab1 + depends: + - __osx >=11.0 + - krb5 >=1.21.3,<1.22.0a0 + - libnghttp2 >=1.67.0,<2.0a0 + - libssh2 >=1.11.1,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: curl + license_family: MIT + size: 394183 + timestamp: 1762334288445 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.6-hf598326_0.conda + sha256: 6c8d5c50f398035c39f118a6decf91b11d2461c88aef99f81e5c5de200d2a7fa + md5: 3ea79e55a64bff6c3cbd4588c89a527a + depends: + - __osx >=11.0 + license: Apache-2.0 WITH LLVM-exception + license_family: Apache + size: 569823 + timestamp: 1763470498512 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda + sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 + md5: 6c77a605a7a689d17d4819c0f8ac9a00 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 73490 + timestamp: 1761979956660 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda + sha256: 5e0b6961be3304a5f027a8c00bd0967fc46ae162cffb7553ff45c70f51b8314c + md5: a6130c709305cd9828b4e1bd9ba0000c + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 55420 + timestamp: 1761980066242 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda + sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 + md5: c277e0a4d549b03ac1e9d6cbbe3d017b + depends: + - ncurses + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + size: 134676 + timestamp: 1738479519902 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda + sha256: 66aa216a403de0bb0c1340a88d1a06adaff66bae2cfd196731aa24db9859d631 + md5: 44083d2d2c2025afca315c7a172eab2b + depends: + - ncurses + - __osx >=11.0 + - ncurses >=6.5,<7.0a0 + license: BSD-2-Clause + license_family: BSD + size: 107691 + timestamp: 1738479560845 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda + sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 + md5: 172bf1cd1ff8629f2b1179945ed45055 + depends: + - libgcc-ng >=12 + license: BSD-2-Clause + license_family: BSD + size: 112766 + timestamp: 1702146165126 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda + sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f + md5: 36d33e440c31857372a72137f78bacf5 + license: BSD-2-Clause + license_family: BSD + size: 107458 + timestamp: 1702146414478 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda + sha256: 2e14399d81fb348e9d231a82ca4d816bf855206923759b69ad006ba482764131 + md5: a1cfcc585f0c42bf8d5546bb1dfb668d + depends: + - libgcc-ng >=12 + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 427426 + timestamp: 1685725977222 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda + sha256: 8c136d7586259bb5c0d2b913aaadc5b9737787ae4f40e3ad1beaf96c80b919b7 + md5: 1a109764bff3bdc7bdd84088347d71dc + depends: + - openssl >=3.1.1,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 368167 + timestamp: 1685726248899 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda + sha256: 1e1b08f6211629cbc2efe7a5bca5953f8f6b3cae0eeb04ca4dacee1bd4e2db2f + md5: 8b09ae86839581147ef2e5c5e229d164 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - expat 2.7.3.* + license: MIT + license_family: MIT + size: 76643 + timestamp: 1763549731408 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda + sha256: fce22610ecc95e6d149e42a42fbc3cc9d9179bd4eb6232639a60f06e080eec98 + md5: b79875dbb5b1db9a4a22a4520f918e1a + depends: + - __osx >=11.0 + constrains: + - expat 2.7.3.* + license: MIT + license_family: MIT + size: 67800 + timestamp: 1763549994166 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda + sha256: 25cbdfa65580cfab1b8d15ee90b4c9f1e0d72128f1661449c9a999d341377d54 + md5: 35f29eec58405aaf55e01cb470d8c26a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 57821 + timestamp: 1760295480630 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda + sha256: 9b8acdf42df61b7bfe8bdc545c016c29e61985e79748c64ad66df47dbc2e295f + md5: 411ff7cd5d1472bba0f55c0faf04453b + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 40251 + timestamp: 1760295839166 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda + sha256: 4641d37faeb97cf8a121efafd6afd040904d4bca8c46798122f417c31d5dfbec + md5: f4084e4e6577797150f9b04a4560ceb0 + depends: + - libfreetype6 >=2.14.1 + license: GPL-2.0-only OR FTL + size: 7664 + timestamp: 1757945417134 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.1-hce30654_0.conda + sha256: 9de25a86066f078822d8dd95a83048d7dc2897d5d655c0e04a8a54fca13ef1ef + md5: f35fb38e89e2776994131fbf961fa44b + depends: + - libfreetype6 >=2.14.1 + license: GPL-2.0-only OR FTL + size: 7810 + timestamp: 1757947168537 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda + sha256: 4a7af818a3179fafb6c91111752954e29d3a2a950259c14a2fc7ba40a8b03652 + md5: 8e7251989bca326a28f4a5ffbd74557a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.50,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - freetype >=2.14.1 + license: GPL-2.0-only OR FTL + size: 386739 + timestamp: 1757945416744 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.1-h6da58f4_0.conda + sha256: cc4aec4c490123c0f248c1acd1aeab592afb6a44b1536734e20937cda748f7cd + md5: 6d4ede03e2a8e20eb51f7f681d2a2550 + depends: + - __osx >=11.0 + - libpng >=1.6.50,<1.7.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - freetype >=2.14.1 + license: GPL-2.0-only OR FTL + size: 346703 + timestamp: 1757947166116 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-h767d61c_7.conda + sha256: 08f9b87578ab981c7713e4e6a7d935e40766e10691732bba376d4964562bcb45 + md5: c0374badb3a5d4b1372db28d19462c53 + depends: + - __glibc >=2.17,<3.0.a0 + - _openmp_mutex >=4.5 + constrains: + - libgomp 15.2.0 h767d61c_7 + - libgcc-ng ==15.2.0=*_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 822552 + timestamp: 1759968052178 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_7.conda + sha256: 2045066dd8e6e58aaf5ae2b722fb6dfdbb57c862b5f34ac7bfb58c40ef39b6ad + md5: 280ea6eee9e2ddefde25ff799c4f0363 + depends: + - libgcc 15.2.0 h767d61c_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 29313 + timestamp: 1759968065504 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_7.conda + sha256: 9ca24328e31c8ef44a77f53104773b9fe50ea8533f4c74baa8489a12de916f02 + md5: 8621a450add4e231f676646880703f49 + depends: + - libgfortran5 15.2.0 hcd61629_7 + constrains: + - libgfortran-ng ==15.2.0=*_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 29275 + timestamp: 1759968110483 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-hfcf01ff_1.conda + sha256: e9a5d1208b9dc0b576b35a484d527d9b746c4e65620e0d77c44636033b2245f0 + md5: f699348e3f4f924728e33551b1920f79 + depends: + - libgfortran5 15.2.0 h742603c_1 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 134016 + timestamp: 1759712902814 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-hcd61629_7.conda + sha256: e93ceda56498d98c9f94fedec3e2d00f717cbedfc97c49be0e5a5828802f2d34 + md5: f116940d825ffc9104400f0d7f1a4551 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=15.2.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 1572758 + timestamp: 1759968082504 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-h742603c_1.conda + sha256: 18808697013a625ca876eeee3d86ee5b656f17c391eca4a4bc70867717cc5246 + md5: afccf412b03ce2f309f875ff88419173 + depends: + - llvm-openmp >=8.0.0 + constrains: + - libgfortran 15.2.0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 764028 + timestamp: 1759712189275 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda + sha256: e9fb1c258c8e66ee278397b5822692527c5f5786d372fe7a869b900853f3f5ca + md5: f7b4d76975aac7e5d9e6ad13845f92fe + depends: + - __glibc >=2.17,<3.0.a0 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 447919 + timestamp: 1759967942498 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-hdb79228_0.conda + sha256: d3341cf69cb02c07bbd1837968f993da01b7bd467e816b1559a3ca26c1ff14c5 + md5: a2e30ccd49f753fd30de0d30b1569789 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgcc >=14 + - libgrpc >=1.73.1,<1.74.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + - openssl >=3.5.1,<4.0a0 + constrains: + - libgoogle-cloud 2.39.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 1307909 + timestamp: 1752048413383 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.39.0-head0a95_0.conda + sha256: 209facdb8ea5b68163f146525720768fa3191cef86c82b2538e8c3cafa1e9dd4 + md5: ad7272a081abe0966d0297691154eda5 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcurl >=8.14.1,<9.0a0 + - libcxx >=19 + - libgrpc >=1.73.1,<1.74.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - openssl >=3.5.1,<4.0a0 + constrains: + - libgoogle-cloud 2.39.0 *_0 + license: Apache-2.0 + license_family: Apache + size: 876283 + timestamp: 1752047598741 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_0.conda + sha256: 59eb8365f0aee384f2f3b2a64dcd454f1a43093311aa5f21a8bb4bd3c79a6db8 + md5: bd21962ff8a9d1ce4720d42a35a4af40 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libgcc >=14 + - libgoogle-cloud 2.39.0 hdb79228_0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 804189 + timestamp: 1752048589800 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.39.0-hfa3a374_0.conda + sha256: a5160c23b8b231b88d0ff738c7f52b0ee703c4c0517b044b18f4d176e729dfd8 + md5: 147a468b9b6c3ced1fccd69b864ae289 + depends: + - __osx >=11.0 + - libabseil + - libcrc32c >=1.1.2,<1.2.0a0 + - libcurl + - libcxx >=19 + - libgoogle-cloud 2.39.0 head0a95_0 + - libzlib >=1.3.1,<2.0a0 + - openssl + license: Apache-2.0 + license_family: Apache + size: 525153 + timestamp: 1752047915306 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.conda + sha256: bc9d32af6167b1f5bcda216dc44eddcb27f3492440571ab12f6e577472a05e34 + md5: ff63bb12ac31c176ff257e3289f20770 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.5,<2.0a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libre2-11 >=2025.8.12 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.73.1 + license: Apache-2.0 + license_family: APACHE + size: 8349777 + timestamp: 1761058442526 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.73.1-h3063b79_1.conda + sha256: c2099872b1aa06bf8153e35e5b706d2000c1fc16f4dde2735ccd77a0643a4683 + md5: f5856b3b9dae4463348a7ec23c1301f2 + depends: + - __osx >=11.0 + - c-ares >=1.34.5,<2.0a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcxx >=19 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libre2-11 >=2025.8.12 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.4,<4.0a0 + - re2 + constrains: + - grpc-cpp =1.73.1 + license: Apache-2.0 + license_family: APACHE + size: 5377798 + timestamp: 1761053602943 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda + sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f + md5: 915f5995e94f60e9a4826e0b0920ee88 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: LGPL-2.1-only + size: 790176 + timestamp: 1754908768807 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.18-h23cfdf5_2.conda + sha256: de0336e800b2af9a40bdd694b03870ac4a848161b35c8a2325704f123f185f03 + md5: 4d5a7445f0b25b6a3ddbb56e790f5251 + depends: + - __osx >=11.0 + license: LGPL-2.1-only + size: 750379 + timestamp: 1754909073836 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda + sha256: cc9aba923eea0af8e30e0f94f2ad7156e2984d80d1e8e7fe6be5a1f257f0eb32 + md5: 8397539e3a0bbd1695584fb4f927485a + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 633710 + timestamp: 1762094827865 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda + sha256: 6c061c56058bb10374daaef50e81b39cf43e8aee21f0037022c0c39c4f31872f + md5: f0695fbecf1006f27f4395d64bd0c4b8 + depends: + - __osx >=11.0 + constrains: + - jpeg <0.0.0a + license: IJG AND BSD-3-Clause AND Zlib + size: 551197 + timestamp: 1762095054358 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-1_h47877c9_openblas.conda + build_number: 1 + sha256: b87938dc1220984c4313045d97422723f96ba4639676639a95ba144e2359f875 + md5: dee12a83aa4aca5077ea23c0605de044 + depends: + - libblas 3.11.0 1_h4a7cf45_openblas + constrains: + - libcblas 3.11.0 1*_openblas + - blas 2.301 openblas + - liblapacke 3.11.0 1*_openblas + license: BSD-3-Clause + license_family: BSD + size: 18486 + timestamp: 1763447033135 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-1_hd9741b5_openblas.conda + build_number: 1 + sha256: acee73900f85c8cf2db56540e905c8ac32e08bccc08d8b54bf4091b5a9ad1ed9 + md5: 5659bf8243896cb24e3de819d422b1a3 + depends: + - libblas 3.11.0 1_h51639a9_openblas + constrains: + - blas 2.301 openblas + - liblapacke 3.11.0 1*_openblas + - libcblas 3.11.0 1*_openblas + license: BSD-3-Clause + license_family: BSD + size: 18703 + timestamp: 1763447928749 +- conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda + sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 + md5: 1a580f7796c7bf6393fddb8bbbde58dc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - xz 5.8.1.* + license: 0BSD + size: 112894 + timestamp: 1749230047870 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda + sha256: 0cb92a9e026e7bd4842f410a5c5c665c89b2eb97794ffddba519a626b8ce7285 + md5: d6df911d4564d77c4374b02552cb17d1 + depends: + - __osx >=11.0 + constrains: + - xz 5.8.1.* + license: 0BSD + size: 92286 + timestamp: 1749230283517 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda + sha256: 3aa92d4074d4063f2a162cd8ecb45dccac93e543e565c01a787e16a43501f7ee + md5: c7e925f37e3b40d893459e625f6a53f1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: BSD-2-Clause + license_family: BSD + size: 91183 + timestamp: 1748393666725 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda + sha256: 0a1875fc1642324ebd6c4ac864604f3f18f57fbcf558a8264f6ced028a3c75b2 + md5: 85ccccb47823dd9f7a99d2c7f530342f + depends: + - __osx >=11.0 + license: BSD-2-Clause + license_family: BSD + size: 71829 + timestamp: 1748393749336 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda + sha256: a4a7dab8db4dc81c736e9a9b42bdfd97b087816e029e221380511960ac46c690 + md5: b499ce4b026493a13774bcf0f4c33849 + depends: + - __glibc >=2.17,<3.0.a0 + - c-ares >=1.34.5,<2.0a0 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + license: MIT + license_family: MIT + size: 666600 + timestamp: 1756834976695 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda + sha256: a07cb53b5ffa2d5a18afc6fd5a526a5a53dd9523fbc022148bd2f9395697c46d + md5: a4b4dd73c67df470d091312ab87bf6ae + depends: + - __osx >=11.0 + - c-ares >=1.34.5,<2.0a0 + - libcxx >=19 + - libev >=4.33,<4.34.0a0 + - libev >=4.33,<5.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.2,<4.0a0 + license: MIT + license_family: MIT + size: 575454 + timestamp: 1756835746393 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda + sha256: 199d79c237afb0d4780ccd2fbf829cea80743df60df4705202558675e07dd2c5 + md5: be43915efc66345cccb3c310b6ed0374 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + constrains: + - openblas >=0.3.30,<0.3.31.0a0 + license: BSD-3-Clause + license_family: BSD + size: 5927939 + timestamp: 1763114673331 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda + sha256: dcc626c7103503d1dfc0371687ad553cb948b8ed0249c2a721147bdeb8db4a73 + md5: a18a7f471c517062ee71b843ef95eb8a + depends: + - __osx >=11.0 + - libgfortran + - libgfortran5 >=14.3.0 + - llvm-openmp >=19.1.7 + constrains: + - openblas >=0.3.30,<0.3.31.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4285762 + timestamp: 1761749506256 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-hb9b0907_1.conda + sha256: ba9b09066f9abae9b4c98ffedef444bbbf4c068a094f6c77d70ef6f006574563 + md5: 1c0320794855f457dea27d35c4c71e23 + depends: + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgrpc >=1.73.1,<1.74.0a0 + - libopentelemetry-cpp-headers 1.21.0 ha770c72_1 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.21.0 + license: Apache-2.0 + license_family: APACHE + size: 885397 + timestamp: 1751782709380 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-1.21.0-he15edb5_1.conda + sha256: 4bf8f703ddd140fe54d4c8464ac96b28520fbc1083cce52c136a85a854745d5c + md5: cbcea547d6d831863ab0a4e164099062 + depends: + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcurl >=8.14.1,<9.0a0 + - libgrpc >=1.73.1,<1.74.0a0 + - libopentelemetry-cpp-headers 1.21.0 hce30654_1 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libzlib >=1.3.1,<2.0a0 + - nlohmann_json + - prometheus-cpp >=1.3.0,<1.4.0a0 + constrains: + - cpp-opentelemetry-sdk =1.21.0 + license: Apache-2.0 + license_family: APACHE + size: 564609 + timestamp: 1751782939921 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_1.conda + sha256: b3a1b36d5f92fbbfd7b6426982a99561bdbd7e4adbafca1b7f127c9a5ab0a60f + md5: 9e298d76f543deb06eb0f3413675e13a + license: Apache-2.0 + license_family: APACHE + size: 363444 + timestamp: 1751782679053 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopentelemetry-cpp-headers-1.21.0-hce30654_1.conda + sha256: ce74278453dec1e3c11158ec368c8f1b03862e279b63f79ed01f38567a1174e6 + md5: c7df4b2d612208f3a27486c113b6aefc + license: Apache-2.0 + license_family: APACHE + size: 363213 + timestamp: 1751782889359 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libparquet-22.0.0-h7376487_4_cpu.conda + build_number: 4 + sha256: d4c3328b6522d19c0be4a0997dea312e0098dd20c859446eb04e312737414290 + md5: 5e9383b1d25179787aff71aaad8208aa + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 22.0.0 h773bc41_4_cpu + - libgcc >=14 + - libstdcxx >=14 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.4,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1344185 + timestamp: 1763230168188 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-22.0.0-h0ac143b_4_cpu.conda + build_number: 4 + sha256: 4df94653e4bb1a63f501316432831ce2922f57a5a2bf4ef4bd0dd8b6d1b69b05 + md5: 028c54faa0fdd72dea6d4dd18b8c8210 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libarrow 22.0.0 h4a3aeba_4_cpu + - libcxx >=19 + - libopentelemetry-cpp >=1.21.0,<1.22.0a0 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libthrift >=0.22.0,<0.22.1.0a0 + - openssl >=3.5.4,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 1043509 + timestamp: 1763230011794 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h421ea60_1.conda + sha256: e75a2723000ce3a4b9fd9b9b9ce77553556c93e475a4657db6ed01abc02ea347 + md5: 7af8e91b0deb5f8e25d1a595dea79614 + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 317390 + timestamp: 1753879899951 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.50-h280e0eb_1.conda + sha256: a2e0240fb0c79668047b528976872307ea80cb330baf8bf6624ac2c6443449df + md5: 4d0f5ce02033286551a32208a5519884 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: zlib-acknowledgement + size: 287056 + timestamp: 1753879907258 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_2.conda + sha256: 1679f16c593d769f3dab219adb1117cbaaddb019080c5a59f79393dc9f45b84f + md5: 94cb88daa0892171457d9fdc69f43eca + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 4645876 + timestamp: 1760550892361 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.31.1-h658db43_2.conda + sha256: a01c3829eb0e3c1354ee7d61c5cde9a79dcebe6ccc7114c2feadf30aecbc7425 + md5: 155d3d17eaaf49ddddfe6c73842bc671 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 2982875 + timestamp: 1760550241203 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda + sha256: eb5d5ef4d12cdf744e0f728b35bca910843c8cf1249f758cf15488ca04a21dbb + md5: a30848ebf39327ea078cf26d114cff53 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libstdcxx >=14 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + size: 211099 + timestamp: 1762397758105 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h91c62da_0.conda + sha256: 7b525313ab16415c4a3191ccf59157c3a4520ed762c8ec61fcfb81d27daa4723 + md5: 060f099756e6baf2ed51b9065e44eda8 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcxx >=19 + constrains: + - re2 2025.11.05.* + license: BSD-3-Clause + license_family: BSD + size: 165593 + timestamp: 1762398300610 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsentencepiece-0.2.0-h022d5ca_13.conda + sha256: 5cdffeea718c5f2e843997fab28ac8682e0c683740b58169fa442893195f3e42 + md5: 47e1fb0cb8fff5e0e8ea9b1f1eb85cab + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + license: Apache-2.0 + license_family: Apache + size: 833555 + timestamp: 1758534349846 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsentencepiece-0.2.0-h79950eb_13.conda + sha256: c93c558fe2ae8368b2b139a35bb9655471cad6d19e6123f29022d21f7c0987d5 + md5: 3f6fb5f375dc2e64c69f4da0c1354314 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcxx >=19 + - libprotobuf >=6.31.1,<6.31.2.0a0 + license: Apache-2.0 + license_family: Apache + size: 754655 + timestamp: 1758534627187 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda + sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 + md5: a587892d3c13b6621a6091be690dbca2 + depends: + - libgcc-ng >=12 + license: ISC + size: 205978 + timestamp: 1716828628198 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda + sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 + md5: a7ce36e284c5faaf93c220dfc39e3abd + depends: + - __osx >=11.0 + license: ISC + size: 164972 + timestamp: 1716828607917 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.0-hee844dc_0.conda + sha256: 4c992dcd0e34b68f843e75406f7f303b1b97c248d18f3c7c330bdc0bc26ae0b3 + md5: 729a572a3ebb8c43933b30edcc628ceb + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libzlib >=1.3.1,<2.0a0 + license: blessing + size: 945576 + timestamp: 1762299687230 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.0-h8adb53f_0.conda + sha256: b43d198f147f46866e5336c4a6b91668beef698bfba69d1706158460eadb2c1b + md5: 5fb1945dbc6380e6fe7e939a62267772 + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libzlib >=1.3.1,<2.0a0 + license: blessing + size: 909508 + timestamp: 1762300078624 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda + sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 + md5: eecce068c7e4eddeb169591baac20ac4 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 304790 + timestamp: 1745608545575 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda + sha256: 8bfe837221390ffc6f111ecca24fa12d4a6325da0c8d131333d63d6c37f27e0a + md5: b68e8f66b94b44aaa8de4583d3d4cc40 + depends: + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.0,<4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 279193 + timestamp: 1745608793272 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda + sha256: 1b981647d9775e1cdeb2fab0a4dd9cd75a6b0de2963f6c3953dbd712f78334b3 + md5: 5b767048b1b3ee9a954b06f4084f93dc + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc 15.2.0 h767d61c_7 + constrains: + - libstdcxx-ng ==15.2.0=*_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 3898269 + timestamp: 1759968103436 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-h4852527_7.conda + sha256: 024fd46ac3ea8032a5ec3ea7b91c4c235701a8bf0e6520fe5e6539992a6bd05f + md5: f627678cf829bd70bccf141a19c3ad3e + depends: + - libstdcxx 15.2.0 h8f9b012_7 + license: GPL-3.0-only WITH GCC-exception-3.1 + license_family: GPL + size: 29343 + timestamp: 1759968157195 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h454ac66_1.conda + sha256: 4888b9ea2593c36ca587a5ebe38d0a56a0e6d6a9e4bb7da7d9a326aaaca7c336 + md5: 8ed82d90e6b1686f5e98f8b7825a15ef + depends: + - __glibc >=2.17,<3.0.a0 + - libevent >=2.1.12,<2.1.13.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.1,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 424208 + timestamp: 1753277183984 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.22.0-h14a376c_1.conda + sha256: 8b703f2c6e47ed5886d7298601b9416b59e823fc8d1a8fa867192c94c5911aac + md5: 3161023bb2f8c152e4c9aa59bdd40975 + depends: + - __osx >=11.0 + - libcxx >=19 + - libevent >=2.1.12,<2.1.13.0a0 + - libzlib >=1.3.1,<2.0a0 + - openssl >=3.5.1,<4.0a0 + license: Apache-2.0 + license_family: APACHE + size: 323360 + timestamp: 1753277264380 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda + sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 + md5: cd5a90476766d53e901500df9215e927 + depends: + - __glibc >=2.17,<3.0.a0 + - lerc >=4.0.0,<5.0a0 + - libdeflate >=1.25,<1.26.0a0 + - libgcc >=14 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libstdcxx >=14 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + size: 435273 + timestamp: 1762022005702 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda + sha256: e9248077b3fa63db94caca42c8dbc6949c6f32f94d1cafad127f9005d9b1507f + md5: e2a72ab2fa54ecb6abab2b26cde93500 + depends: + - __osx >=11.0 + - lerc >=4.0.0,<5.0a0 + - libcxx >=19 + - libdeflate >=1.25,<1.26.0a0 + - libjpeg-turbo >=3.1.0,<4.0a0 + - liblzma >=5.8.1,<6.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - libzlib >=1.3.1,<2.0a0 + - zstd >=1.5.7,<1.6.0a0 + license: HPND + size: 373892 + timestamp: 1762022345545 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.11.1-hfe17d71_0.conda + sha256: c05bb2ea574dd09876ece0494213d5a8b817cf515413feee92f880287635de5c + md5: 765c7e0005659d5154cdd33dc529e0a5 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 86230 + timestamp: 1763377698026 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.11.1-hd2415e0_0.conda + sha256: 616ab5af94a53978757d440d33c0ee900b1e2b09c5109763bfc048ef9a8d7107 + md5: 5af2b7345372c4bb27fc95c4e2472a46 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 87735 + timestamp: 1763378242656 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda + sha256: e5ec6d2ad7eef538ddcb9ea62ad4346fde70a4736342c4ad87bd713641eb9808 + md5: 80c07c68d2f6870250959dcc95b209d1 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + size: 37135 + timestamp: 1758626800002 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda + sha256: c180f4124a889ac343fc59d15558e93667d894a966ec6fdb61da1604481be26b + md5: 0f03292cc56bf91a077a134ea8747118 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 895108 + timestamp: 1753948278280 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda + sha256: 042c7488ad97a5629ec0a991a8b2a3345599401ecc75ad6a5af73b60e6db9689 + md5: c0d87c3c8e075daf1daf6c31b53e8083 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 421195 + timestamp: 1753948426421 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda + sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b + md5: aea31d2e5b1091feca96fcfe945c3cf9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + size: 429011 + timestamp: 1752159441324 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda + sha256: a4de3f371bb7ada325e1f27a4ef7bcc81b2b6a330e46fac9c2f78ac0755ea3dd + md5: e5e7d467f80da752be17796b87fe6385 + depends: + - __osx >=11.0 + constrains: + - libwebp 1.6.0 + license: BSD-3-Clause + license_family: BSD + size: 294974 + timestamp: 1752159906788 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda + sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa + md5: 92ed62436b625154323d40d5f2f11dd7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 395888 + timestamp: 1727278577118 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda + sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 + md5: af523aae2eca6dfa1c8eec693f5b9a79 + depends: + - __osx >=11.0 + - pthread-stubs + - xorg-libxau >=1.0.11,<2.0a0 + - xorg-libxdmcp + license: MIT + license_family: MIT + size: 323658 + timestamp: 1727278733917 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda + sha256: ec0735ae56c3549149eebd7dc22c0bed91fd50c02eaa77ff418613ddda190aa8 + md5: e512be7dc1f84966d50959e900ca121f + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libxml2-16 2.15.1 ha9997c6_0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 45283 + timestamp: 1761015644057 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.15.1-h9329255_0.conda + sha256: c409e384ddf5976a42959265100d6b2c652017d250171eb10bae47ef8166193f + md5: fb5ce61da27ee937751162f86beba6d1 + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libxml2-16 2.15.1 h0ff4647_0 + - libzlib >=1.3.1,<2.0a0 + license: MIT + license_family: MIT + size: 40607 + timestamp: 1761016108361 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda + sha256: 71436e72a286ef8b57d6f4287626ff91991eb03c7bdbe835280521791efd1434 + md5: e7733bc6785ec009e47a224a71917e84 + depends: + - __glibc >=2.17,<3.0.a0 + - icu >=75.1,<76.0a0 + - libgcc >=14 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libxml2 2.15.1 + license: MIT + license_family: MIT + size: 556302 + timestamp: 1761015637262 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-16-2.15.1-h0ff4647_0.conda + sha256: ebe2dd9da94280ad43da936efa7127d329b559f510670772debc87602b49b06d + md5: 438c97d1e9648dd7342f86049dd44638 + depends: + - __osx >=11.0 + - icu >=75.1,<76.0a0 + - libiconv >=1.18,<2.0a0 + - liblzma >=5.8.1,<6.0a0 + - libzlib >=1.3.1,<2.0a0 + constrains: + - libxml2 2.15.1 + license: MIT + license_family: MIT + size: 464952 + timestamp: 1761016087733 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 + md5: edb0dca6bc32e4f4789199455a1dbeb8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 60963 + timestamp: 1727963148474 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b + md5: 369964e85dc26bfe78f41399b366c435 + depends: + - __osx >=11.0 + constrains: + - zlib 1.3.1 *_2 + license: Zlib + license_family: Other + size: 46438 + timestamp: 1727963202283 +- conda: https://conda.anaconda.org/conda-forge/linux-64/llguidance-1.3.0-py310hc9716df_0.conda + noarch: python + sha256: 2c66769c52805d72432c821a195d30842222754b68a8cc03f66630f2b08b828f + md5: 6eb12b03bd2b1377b7218aef8c7a4cd4 + depends: + - __glibc >=2.17,<3.0.a0 + - _python_abi3_support 1.* + - cpython >=3.10 + - libgcc >=14 + - python + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 2181186 + timestamp: 1760996081801 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llguidance-1.3.0-py310h34ed3d5_0.conda + noarch: python + sha256: a2158ae3dfb7eda6c227964ab4486cbc2670b96c01ad67fab9c80271484c0ea4 + md5: 4daf10373df4f5f37b4ced4c079eaba6 + depends: + - __osx >=11.0 + - _python_abi3_support 1.* + - cpython >=3.10 + - python + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 1948564 + timestamp: 1760996983932 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.6-h4a912ad_0.conda + sha256: 51ebeacae9225649e2c3bbfc9ed2ed690400b78ba79d0d3ee9ff428e8b951fed + md5: 4a274d80967416bce3c7d89bf43923ec + depends: + - __osx >=11.0 + constrains: + - openmp 21.1.6|21.1.6.* + - intel-openmp <0.0a0 + license: Apache-2.0 WITH LLVM-exception + license_family: APACHE + size: 286206 + timestamp: 1763529774822 +- conda: https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda + sha256: 47326f811392a5fd3055f0f773036c392d26fdb32e4d8e7a8197eed951489346 + md5: 9de5350a85c4a20c685259b889aa6393 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + license: BSD-2-Clause + license_family: BSD + size: 167055 + timestamp: 1733741040117 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda + sha256: 94d3e2a485dab8bdfdd4837880bde3dd0d701e2b97d6134b8806b7c8e69c8652 + md5: 01511afc6cc1909c5303cf31be17b44f + depends: + - __osx >=11.0 + - libcxx >=18 + license: BSD-2-Clause + license_family: BSD + size: 148824 + timestamp: 1733741047892 +- conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda + sha256: 7b1da4b5c40385791dbc3cc85ceea9fad5da680a27d5d3cb8bfaa185e304a89e + md5: 5b5203189eb668f042ac2b0826244964 + depends: + - mdurl >=0.1,<1 + - python >=3.10 + license: MIT + license_family: MIT + size: 64736 + timestamp: 1754951288511 +- conda: https://conda.anaconda.org/conda-forge/noarch/markupsafe-3.0.3-pyh7db6752_0.conda + sha256: e0cbfea51a19b3055ca19428bd9233a25adca956c208abb9d00b21e7259c7e03 + md5: fab1be106a50e20f10fe5228fd1d1651 + depends: + - python >=3.10 + constrains: + - jinja2 >=3.0.0 + track_features: + - markupsafe_no_compile + license: BSD-3-Clause + license_family: BSD + size: 15499 + timestamp: 1759055275624 +- conda: https://conda.modular.com/max/linux-64/max-25.7.0-3.14release.conda + sha256: 1c77be256215fbb2c8afdd0f65fd727a6288d0037f74c3eca2fe3d26a9d98c47 + depends: + - numpy >=1.18 + - typing-extensions >=4.12.2 + - pyyaml >=6.0.1 + - python-gil + - max-core ==25.7.0 release + - python_abi 3.14.* *_cp314 + constrains: + - click >=8.0.0 + - gguf >=0.17.1 + - hf-transfer >=0.1.9 + - huggingface_hub >=0.28.0 + - jinja2 >=3.1.6 + - llguidance >=0.7.30 + - pillow >=11.0.0 + - psutil >=6.1.1 + - requests >=2.32.3 + - rich >=13.0.1 + - sentencepiece >=0.2.0 + - taskgroup >=0.2.2 + - tomli >=2.0.0 + - tqdm >=4.67.1 + - transformers >=4.57.0 + - uvicorn >=0.34.0 + - uvloop >=0.21.0 + - aiofiles >=24.1.0 + - asgiref >=3.8.1 + - fastapi >=0.115.3 + - grpcio >=1.68.0 + - httpx >=0.28.1,<0.29 + - msgspec >=0.19.0 + - opentelemetry-api >=1.29.0 + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.50b0 + - opentelemetry-sdk >=1.29.0,<1.36.0 + - prometheus_client >=0.21.0 + - protobuf >=6.31.1,<6.32.0 + - pydantic-settings >=2.7.1 + - pydantic + - pyinstrument >=5.0.1 + - python-json-logger >=2.0.7 + - pyzmq >=26.3.0 + - regex >=2024.11.6 + - scipy >=1.13.0 + - sse-starlette >=2.1.2 + - starlette >=0.47.2 + - tokenizers >=0.19.0 + license: LicenseRef-Modular-Proprietary + size: 6785232 + timestamp: 1763510721866 +- conda: https://conda.modular.com/max/osx-arm64/max-25.7.0-3.14release.conda + sha256: d8690a2b437a8ba66edefe5b86ccf23393934c7ed6b247b5a9e015c9d81434a4 + depends: + - numpy >=1.18 + - typing-extensions >=4.12.2 + - pyyaml >=6.0.1 + - python-gil + - max-core ==25.7.0 release + - python_abi 3.14.* *_cp314 + constrains: + - click >=8.0.0 + - gguf >=0.17.1 + - hf-transfer >=0.1.9 + - huggingface_hub >=0.28.0 + - jinja2 >=3.1.6 + - llguidance >=0.7.30 + - pillow >=11.0.0 + - psutil >=6.1.1 + - requests >=2.32.3 + - rich >=13.0.1 + - sentencepiece >=0.2.0 + - taskgroup >=0.2.2 + - tomli >=2.0.0 + - tqdm >=4.67.1 + - transformers >=4.57.0 + - uvicorn >=0.34.0 + - uvloop >=0.21.0 + - aiofiles >=24.1.0 + - asgiref >=3.8.1 + - fastapi >=0.115.3 + - grpcio >=1.68.0 + - httpx >=0.28.1,<0.29 + - msgspec >=0.19.0 + - opentelemetry-api >=1.29.0 + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.50b0 + - opentelemetry-sdk >=1.29.0,<1.36.0 + - prometheus_client >=0.21.0 + - protobuf >=6.31.1,<6.32.0 + - pydantic-settings >=2.7.1 + - pydantic + - pyinstrument >=5.0.1 + - python-json-logger >=2.0.7 + - pyzmq >=26.3.0 + - regex >=2024.11.6 + - scipy >=1.13.0 + - sse-starlette >=2.1.2 + - starlette >=0.47.2 + - tokenizers >=0.19.0 + license: LicenseRef-Modular-Proprietary + size: 9606918 + timestamp: 1763511077693 +- conda: https://conda.modular.com/max/linux-64/max-core-25.7.0-release.conda + sha256: 0cd23ba7252fa54be227d60a4d89b8bf13b49c45fbcbbab3ece5b8a082589bf1 + depends: + - mojo-compiler ==0.25.7.0 release + license: LicenseRef-Modular-Proprietary + size: 129533734 + timestamp: 1763510721865 +- conda: https://conda.modular.com/max/osx-arm64/max-core-25.7.0-release.conda + sha256: 16acde67793950f14d2c8e749d9feb15d32f8a293ac0a0525faeb0dcb1852976 + depends: + - mojo-compiler ==0.25.7.0 release + license: LicenseRef-Modular-Proprietary + size: 75649285 + timestamp: 1763511077693 +- conda: https://conda.modular.com/max/noarch/max-pipelines-25.7.0-release.conda + noarch: python + sha256: 9e84877462e2b44502a8cbe826c20b186aad733f1c232b71bcea7dc88e1f630e + depends: + - click >=8.0.0 + - gguf >=0.17.1 + - hf-transfer >=0.1.9 + - huggingface_hub >=0.28.0 + - jinja2 >=3.1.6 + - llguidance >=0.7.30 + - pillow >=11.0.0 + - psutil >=6.1.1 + - requests >=2.32.3 + - rich >=13.0.1 + - sentencepiece >=0.2.0 + - taskgroup >=0.2.2 + - tomli >=2.0.0 + - tqdm >=4.67.1 + - transformers >=4.57.0 + - uvicorn >=0.34.0 + - uvloop >=0.21.0 + - aiofiles >=24.1.0 + - asgiref >=3.8.1 + - fastapi >=0.115.3 + - grpcio >=1.68.0 + - httpx >=0.28.1,<0.29 + - msgspec >=0.19.0 + - opentelemetry-api >=1.29.0 + - opentelemetry-exporter-otlp-proto-http >=1.27.0 + - opentelemetry-exporter-prometheus >=0.50b0 + - opentelemetry-sdk >=1.29.0,<1.36.0 + - prometheus_client >=0.21.0 + - protobuf >=6.31.1,<6.32.0 + - pydantic-settings >=2.7.1 + - pydantic + - pyinstrument >=5.0.1 + - python-json-logger >=2.0.7 + - pyzmq >=26.3.0 + - regex >=2024.11.6 + - scipy >=1.13.0 + - sse-starlette >=2.1.2 + - starlette >=0.47.2 + - tokenizers >=0.19.0 + - max >=25.7.0,<26.0a0 + license: LicenseRef-Modular-Proprietary + size: 16776 + timestamp: 1763510771731 +- conda: https://conda.modular.com/max/noarch/mblack-25.7.0-release.conda + noarch: python + sha256: 1cc8fea28ed794435b78985f5d9dd0d030ee2b36c9ee5fc54a1a769053811ab1 + depends: + - python >=3.10 + - click >=8.0.0 + - mypy_extensions >=0.4.3 + - packaging >=22.0 + - pathspec >=0.9.0 + - platformdirs >=2 + - tomli >=1.1.0 + - typing_extensions >=v4.12.2 + - python + license: MIT + size: 138148 + timestamp: 1763510771731 +- conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda + sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 + md5: 592132998493b3ff25fd7479396e8351 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 14465 + timestamp: 1733255681319 +- conda: https://conda.modular.com/max/noarch/modular-25.7.0-release.conda + noarch: python + sha256: 44750dfc3ca3e6e8732f51075ac6d544f8d85211ad76c38a08d72fde0b316cab + depends: + - max-pipelines ==25.7.0 release + - mojo ==0.25.7.0 release + license: LicenseRef-Modular-Proprietary + size: 16230 + timestamp: 1763510771732 +- conda: https://conda.modular.com/max/linux-64/mojo-0.25.7.0-release.conda + sha256: 9a702420138ef31b77f58e64b6e8a4cf4bff768c1476787b98d72cde73d72982 + depends: + - python >=3.10 + - mojo-compiler ==0.25.7.0 release + - mblack ==25.7.0 release + - jupyter_client >=8.6.2,<8.7 + license: LicenseRef-Modular-Proprietary + size: 89019701 + timestamp: 1763510721866 +- conda: https://conda.modular.com/max/osx-arm64/mojo-0.25.7.0-release.conda + sha256: 1ebf6b78e85e8bcd01e427d01790b72e3805e6ea7475cdbdbefeb6aaa4ca5c83 + depends: + - python >=3.10 + - mojo-compiler ==0.25.7.0 release + - mblack ==25.7.0 release + - jupyter_client >=8.6.2,<8.7 + license: LicenseRef-Modular-Proprietary + size: 75228880 + timestamp: 1763511077693 +- conda: https://conda.modular.com/max/linux-64/mojo-compiler-0.25.7.0-release.conda + sha256: 7d8e2cb28ce54cc8fc0e3f3340b403c8b41125e7f2a649f437e69c56e52bb1ed + depends: + - mojo-python ==0.25.7.0 release + license: LicenseRef-Modular-Proprietary + size: 88690196 + timestamp: 1763510721865 +- conda: https://conda.modular.com/max/osx-arm64/mojo-compiler-0.25.7.0-release.conda + sha256: 2ac7a3a23d7a0d14fdfc7efc65166afba06567c5060687c3cce14ed64e71a5b4 + depends: + - mojo-python ==0.25.7.0 release + license: LicenseRef-Modular-Proprietary + size: 63177739 + timestamp: 1763511077693 +- conda: https://conda.modular.com/max/noarch/mojo-python-0.25.7.0-release.conda + noarch: python + sha256: 020a6cdde091d210a731216fa107472fdd3c5e790fea4c20af646b0ccb5be44e + depends: + - python + license: LicenseRef-Modular-Proprietary + size: 24689 + timestamp: 1763510771731 +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.19.0-py314h5bd0f2a_2.conda + sha256: a708e1d60bdeb78ea158ae6f113f38eebe13218699bea75bcadd829cb314acf0 + md5: 7f543f29a631dbcbc306eb93a15fd2e8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14.0rc2,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 217255 + timestamp: 1758232402448 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.19.0-py314hb84d1df_2.conda + sha256: cf433a3146dfe7c4c4049a6650ed5d965db73a3337d013836cade8a0314f71ee + md5: 101c3f46f4d807acbcabf1e98980095a + depends: + - __osx >=11.0 + - python >=3.14.0rc2,<3.15.0a0 + - python >=3.14.0rc2,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 208432 + timestamp: 1758232305177 +- conda: https://conda.anaconda.org/conda-forge/noarch/multidict-6.6.3-pyh62beb40_0.conda + sha256: c4257649d1be3d19a97213457032073737cd3179bd0ed3bd2b9885955d11f6b8 + md5: 36b9579bd0896b224df0424e46efc1b5 + depends: + - python >=3.9 + - typing-extensions >=4.1.0 + track_features: + - multidict_no_compile + license: Apache-2.0 + license_family: APACHE + size: 37036 + timestamp: 1751310675422 +- conda: https://conda.anaconda.org/conda-forge/linux-64/multiprocess-0.70.18-py314h0f05182_2.conda + sha256: 13dd807d9c30258d837cb889b64a417c0720d2e141e4eace8d74c2b6b7bf49dc + md5: 7556a730cd5aa98710374f4ebd6ef2da + depends: + - python + - dill >=0.3.9 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 381150 + timestamp: 1762474498822 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/multiprocess-0.70.18-py314h9d33bd4_2.conda + sha256: 52735fcf25b8839af53cade23a99f341e30e5e3f35f5f855a933176aa34f9ef6 + md5: b115db204a1ed572962111e8632ce9fb + depends: + - python + - dill >=0.3.9 + - __osx >=11.0 + - python 3.14.* *_cp314 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 384119 + timestamp: 1762474190292 +- conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda + sha256: 6ed158e4e5dd8f6a10ad9e525631e35cee8557718f83de7a4e3966b1f772c4b1 + md5: e9c622e0d00fa24a6292279af3ab6d06 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 11766 + timestamp: 1745776666688 +- conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda + sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 + md5: 47e340acb35de30501a76c7c799c41d7 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: X11 AND BSD-3-Clause + size: 891641 + timestamp: 1738195959188 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda + sha256: 2827ada40e8d9ca69a153a45f7fd14f32b2ead7045d3bbb5d10964898fe65733 + md5: 068d497125e4bf8a66bf707254fff5ae + depends: + - __osx >=11.0 + license: X11 AND BSD-3-Clause + size: 797030 + timestamp: 1738196177597 +- conda: https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda + sha256: fd2cbd8dfc006c72f45843672664a8e4b99b2f8137654eaae8c3d46dca776f63 + md5: 16c2a0e9c4a166e53632cfca4f68d020 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + size: 136216 + timestamp: 1758194284857 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/nlohmann_json-3.12.0-h248ca61_1.conda + sha256: f6aa432b073778c3970d3115d291267f32ae85adfa99d80ff1abdf0b806aa249 + md5: 3ba9d0c21af2150cb92b2ab8bdad3090 + constrains: + - nlohmann_json-abi ==3.12.0 + license: MIT + license_family: MIT + size: 136912 + timestamp: 1758194464430 +- conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py314h2b28147_0.conda + sha256: 4fa3b8b80dd848a70f679b31d74d6fb28f9c4de9cd81086aa8e10256e9de20d1 + md5: 6d2cff81447b8fe424645d7dd3bde8bf + depends: + - python + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 8983459 + timestamp: 1763350996398 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py314h5b5928d_0.conda + sha256: a8731e3e31013be69cb585dbc57cb225437bb0c945ddce9a550c1cd10b6fad37 + md5: e126981f973ddc2510d7a249c5b69533 + depends: + - python + - python 3.14.* *_cp314 + - __osx >=11.0 + - libcxx >=19 + - libcblas >=3.9.0,<4.0a0 + - libblas >=3.9.0,<4.0a0 + - python_abi 3.14.* *_cp314 + - liblapack >=3.9.0,<4.0a0 + constrains: + - numpy-base <0a0 + license: BSD-3-Clause + license_family: BSD + size: 6861174 + timestamp: 1763350930747 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda + sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d + md5: 11b3379b191f63139e29c0d19dee24cd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libpng >=1.6.50,<1.7.0a0 + - libstdcxx >=14 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 355400 + timestamp: 1758489294972 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda + sha256: dd73e8f1da7dd6a5494c5586b835cbe2ec68bace55610b1c4bf927400fe9c0d7 + md5: 6bf3d24692c157a41c01ce0bd17daeea + depends: + - __osx >=11.0 + - libcxx >=19 + - libpng >=1.6.50,<1.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-2-Clause + license_family: BSD + size: 319967 + timestamp: 1758489514651 +- conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda + sha256: a47271202f4518a484956968335b2521409c8173e123ab381e775c358c67fe6d + md5: 9ee58d5c534af06558933af3c845a780 + depends: + - __glibc >=2.17,<3.0.a0 + - ca-certificates + - libgcc >=14 + license: Apache-2.0 + license_family: Apache + size: 3165399 + timestamp: 1762839186699 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda + sha256: ebe93dafcc09e099782fe3907485d4e1671296bc14f8c383cb6f3dfebb773988 + md5: b34dc4172653c13dcf453862f251af2b + depends: + - __osx >=11.0 + - ca-certificates + license: Apache-2.0 + license_family: Apache + size: 3108371 + timestamp: 1762839712322 +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-api-1.35.0-pyhd8ed1ab_0.conda + sha256: 6228c870ad994ea843b78505c3df818dada38a6e9a8c658a02552898c8ddb218 + md5: 241b102f0e44e7992f58c2419b84cf2e + depends: + - deprecated >=1.2.6 + - importlib-metadata <8.8.0,>=6.0 + - python >=3.9 + - typing_extensions >=4.5.0 + license: Apache-2.0 + license_family: APACHE + size: 45773 + timestamp: 1752286891826 +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-common-1.35.0-pyhd8ed1ab_0.conda + sha256: ff2776168c26365290ab480ac14f8f27392d4286c6f8fabd9c33884bd9fff094 + md5: d98d06fedf338be8773b6c9bb023952d + depends: + - backoff >=1.10.0,<3.0.0 + - opentelemetry-proto 1.35.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 19234 + timestamp: 1752327590965 +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-otlp-proto-http-1.35.0-pyhd8ed1ab_0.conda + sha256: 41c96d6d309eedfd9c2ef49784e79ab0e228351fb9ef6ccbdb3839ac110fcb7c + md5: 2582574aa069164d1127c0b84e31bf47 + depends: + - deprecated >=1.2.6 + - googleapis-common-protos >=1.52,<2.dev0 + - opentelemetry-api >=1.15,<2.dev0 + - opentelemetry-exporter-otlp-proto-common 1.35.0 + - opentelemetry-proto 1.35.0 + - opentelemetry-sdk >=1.35.0,<1.36.dev0 + - python >=3.9 + - requests >=2.7,<3.dev0 + - typing_extensions >=4.5.0 + license: Apache-2.0 + license_family: APACHE + size: 18011 + timestamp: 1752362461602 +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-exporter-prometheus-0.56b0-pyhe01879c_1.conda + sha256: 145d87a756d2f6db6963d9105c26f09c04f79a24278b631f672d13adbb469c70 + md5: 372d2c49b89dbb827ec2e85998a75095 + depends: + - python >=3.9 + - opentelemetry-api >=1.12,<2.dev0 + - opentelemetry-sdk >=1.35.0,<1.36.dev0 + - prometheus_client >=0.5.0,<1.0.0 + - python + license: Apache-2.0 + license_family: APACHE + size: 22901 + timestamp: 1754090360044 +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-proto-1.35.0-pyhd8ed1ab_0.conda + sha256: 53f20256a65df56031b8d285dd76c5181fe987682efe8286dd02f5fee31e3ce9 + md5: 67e3d4dd1e0ced032ef8fa99340e50c5 + depends: + - protobuf <7.0,>=5.0 + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 45741 + timestamp: 1752308297180 +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-sdk-1.35.0-pyhd8ed1ab_0.conda + sha256: f091363a1a0dd8d1c9b889f9ee433f28efb122edbc4222b8468790689fd106b1 + md5: 226ec4d220a74e1fcc8c658f365bd3ef + depends: + - opentelemetry-api 1.35.0 + - opentelemetry-semantic-conventions 0.56b0 + - python >=3.9 + - typing-extensions >=3.7.4 + - typing_extensions >=4.5.0 + license: Apache-2.0 + license_family: APACHE + size: 78751 + timestamp: 1752299653515 +- conda: https://conda.anaconda.org/conda-forge/noarch/opentelemetry-semantic-conventions-0.56b0-pyh3cfb1c2_0.conda + sha256: 9d439ad39d33f3ea61553b5a48b4250fd06d8a4ad99ccb3bac6d8d1a273339ba + md5: 251c0dfb684e8f43a71d579091191580 + depends: + - deprecated >=1.2.6 + - opentelemetry-api 1.35.0 + - python >=3.9 + - typing_extensions >=4.5.0 + license: Apache-2.0 + license_family: APACHE + size: 107441 + timestamp: 1752290820962 +- conda: https://conda.anaconda.org/conda-forge/linux-64/orc-2.2.1-hd747db4_0.conda + sha256: 8d91d6398fc63a94d238e64e4983d38f6f9555460f11bed00abb2da04dbadf7c + md5: ddab8b2af55b88d63469c040377bd37e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.2,<1.3.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 1316445 + timestamp: 1759424644934 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.2.1-h4fd0076_0.conda + sha256: f0a31625a647cb8d55a7016950c11f8fabc394df5054d630e9c9b526bf573210 + md5: b5dea50c77ab3cc18df48bdc9994ac44 + depends: + - __osx >=11.0 + - libcxx >=19 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libzlib >=1.3.1,<2.0a0 + - lz4-c >=1.10.0,<1.11.0a0 + - snappy >=1.2.2,<1.3.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Apache-2.0 + license_family: Apache + size: 487298 + timestamp: 1759424875005 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda + sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 + md5: 58335b26c38bf4a20f399384c33cbcf9 + depends: + - python >=3.8 + - python + license: Apache-2.0 + license_family: APACHE + size: 62477 + timestamp: 1745345660407 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py314ha0b5721_1.conda + sha256: 8e4d81448484f3ae2ef54202a49bda0365093ac459045d43f3d151f88cfe4c23 + md5: 4e72e31689d2141ac77fd6a6dcb740d8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + - numpy >=1.22.4 + - numpy >=1.23,<3 + - python >=3.14.0rc3,<3.15.0a0 + - python-dateutil >=2.8.2 + - python-tzdata >=2022.7 + - python_abi 3.14.* *_cp314 + - pytz >=2020.1 + constrains: + - psycopg2 >=2.9.6 + - blosc >=1.21.3 + - beautifulsoup4 >=4.11.2 + - pyreadstat >=1.2.0 + - gcsfs >=2022.11.0 + - s3fs >=2022.11.0 + - pyxlsb >=1.0.10 + - xlsxwriter >=3.0.5 + - matplotlib >=3.6.3 + - openpyxl >=3.1.0 + - sqlalchemy >=2.0.0 + - numexpr >=2.8.4 + - xarray >=2022.12.0 + - pyqt5 >=5.15.9 + - xlrd >=2.0.1 + - zstandard >=0.19.0 + - pytables >=3.8.0 + - odfpy >=1.4.1 + - lxml >=4.9.2 + - pyarrow >=10.0.1 + - bottleneck >=1.3.6 + - html5lib >=1.1 + - scipy >=1.10.0 + - fsspec >=2022.11.0 + - fastparquet >=2022.12.0 + - tabulate >=0.9.0 + - python-calamine >=0.1.7 + - qtpy >=2.3.0 + - numba >=0.56.4 + - tzdata >=2022.7 + - pandas-gbq >=0.19.0 + license: BSD-3-Clause + license_family: BSD + size: 15395500 + timestamp: 1759266072181 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py314ha3d490a_1.conda + sha256: 48b32ef03a360c6365efd3799a1f65fd510a1a0c22ac364fa07e79369db0daba + md5: 9ddeb938ece18b5d9b534494cfe0facd + depends: + - __osx >=11.0 + - libcxx >=19 + - numpy >=1.22.4 + - numpy >=1.23,<3 + - python >=3.14.0rc3,<3.15.0a0 + - python >=3.14.0rc3,<3.15.0a0 *_cp314 + - python-dateutil >=2.8.2 + - python-tzdata >=2022.7 + - python_abi 3.14.* *_cp314 + - pytz >=2020.1 + constrains: + - html5lib >=1.1 + - pyarrow >=10.0.1 + - gcsfs >=2022.11.0 + - xlrd >=2.0.1 + - matplotlib >=3.6.3 + - tabulate >=0.9.0 + - zstandard >=0.19.0 + - bottleneck >=1.3.6 + - lxml >=4.9.2 + - python-calamine >=0.1.7 + - pyxlsb >=1.0.10 + - xarray >=2022.12.0 + - qtpy >=2.3.0 + - fastparquet >=2022.12.0 + - s3fs >=2022.11.0 + - pyreadstat >=1.2.0 + - sqlalchemy >=2.0.0 + - numba >=0.56.4 + - pandas-gbq >=0.19.0 + - scipy >=1.10.0 + - odfpy >=1.4.1 + - pyqt5 >=5.15.9 + - numexpr >=2.8.4 + - blosc >=1.21.3 + - openpyxl >=3.1.0 + - tzdata >=2022.7 + - psycopg2 >=2.9.6 + - pytables >=3.8.0 + - beautifulsoup4 >=4.11.2 + - xlsxwriter >=3.0.5 + - fsspec >=2022.11.0 + license: BSD-3-Clause + license_family: BSD + size: 14227769 + timestamp: 1759267028292 +- conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda + sha256: 9f64009cdf5b8e529995f18e03665b03f5d07c0b17445b8badef45bde76249ee + md5: 617f15191456cc6a13db418a275435e5 + depends: + - python >=3.9 + license: MPL-2.0 + license_family: MOZILLA + size: 41075 + timestamp: 1733233471940 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.0.0-py314h72745e2_0.conda + sha256: 1dec7a825154fce8705892a4cc178f8edfa78253c56de06000b409f6cfe2cea9 + md5: 47fdb59e9753d0af064c25247ab4f47c + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - openjpeg >=2.5.4,<3.0a0 + - lcms2 >=2.17,<3.0a0 + - python_abi 3.14.* *_cp314 + - libjpeg-turbo >=3.1.0,<4.0a0 + - tk >=8.6.13,<8.7.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libwebp-base >=1.6.0,<2.0a0 + - zlib-ng >=2.2.5,<2.3.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - libxcb >=1.17.0,<2.0a0 + license: HPND + size: 1071171 + timestamp: 1761655794835 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.0.0-py314h73456f9_0.conda + sha256: 688b0d8d2860e3dd02fc6783200fa0b7dc5a2f6c5b373cec3bcfd10168c6f3a1 + md5: 010b484f18a2dc253972adff3281c12f + depends: + - python + - __osx >=11.0 + - python 3.14.* *_cp314 + - openjpeg >=2.5.4,<3.0a0 + - libwebp-base >=1.6.0,<2.0a0 + - zlib-ng >=2.2.5,<2.3.0a0 + - libfreetype >=2.14.1 + - libfreetype6 >=2.14.1 + - libjpeg-turbo >=3.1.0,<4.0a0 + - tk >=8.6.13,<8.7.0a0 + - libtiff >=4.7.1,<4.8.0a0 + - lcms2 >=2.17,<3.0a0 + - python_abi 3.14.* *_cp314 + - libxcb >=1.17.0,<2.0a0 + license: HPND + size: 992758 + timestamp: 1761655970284 +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.0-pyhcf101f3_0.conda + sha256: 7efd51b48d908de2d75cbb3c4a2e80dd9454e1c5bb8191b261af3136f7fa5888 + md5: 5c7a868f8241e64e1cf5fdf4962f23e2 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + size: 23625 + timestamp: 1759953252315 +- conda: https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda + sha256: 013669433eb447548f21c3c6b16b2ed64356f726b5f77c1b39d5ba17a8a4b8bc + md5: a83f6a2fdc079e643237887a37460668 + depends: + - __glibc >=2.17,<3.0.a0 + - libcurl >=8.10.1,<9.0a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + size: 199544 + timestamp: 1730769112346 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/prometheus-cpp-1.3.0-h0967b3e_0.conda + sha256: 851a77ae1a8e90db9b9f3c4466abea7afb52713c3d98ceb0d37ba6ff27df2eff + md5: 7172339b49c94275ba42fec3eaeda34f + depends: + - __osx >=11.0 + - libcurl >=8.10.1,<9.0a0 + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - zlib + license: MIT + license_family: MIT + size: 173220 + timestamp: 1730769371051 +- conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda + sha256: 13dc67de68db151ff909f2c1d2486fa7e2d51355b25cee08d26ede1b62d48d40 + md5: a1e91db2d17fd258c64921cb38e6745a + depends: + - python >=3.10 + license: Apache-2.0 + license_family: Apache + size: 54592 + timestamp: 1758278323953 +- conda: https://conda.anaconda.org/conda-forge/noarch/propcache-0.3.1-pyhe1237c8_0.conda + sha256: d8927d64b35e1fb82285791444673e47d3729853be962c7045e75fc0fd715cec + md5: b1cda654f58d74578ac9786909af84cd + depends: + - python >=3.9 + track_features: + - propcache_no_compile + license: Apache-2.0 + license_family: APACHE + size: 17693 + timestamp: 1744525054494 +- conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.31.1-py314h503b32b_2.conda + sha256: 55c4d82eaa400d3d21701ce152397489b077177527564674aff8021fae536401 + md5: 1699ff22b094378d3a4b20019a995cf3 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - libprotobuf 6.31.1 + license: BSD-3-Clause + license_family: BSD + size: 487685 + timestamp: 1760393455342 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-6.31.1-py314hc77ea51_2.conda + sha256: 22d043cd622f2db53aeec9a16fbb8b13794237e629d6d9db073c664c930d592d + md5: 86bed25422f2e8bb5b099806806df326 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcxx >=19 + - libzlib >=1.3.1,<2.0a0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - libprotobuf 6.31.1 + license: BSD-3-Clause + license_family: BSD + size: 471093 + timestamp: 1760394536621 +- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-py314h0f05182_0.conda + sha256: 7c5d69ad61fe4e0d3657185f51302075ef5b9e34686238c6b3bde102344d4390 + md5: aee1c9aecc66339ea6fd89e6a143a282 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 509226 + timestamp: 1762092897605 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.1.3-py314h9d33bd4_0.conda + sha256: e69d9bdc482596abb10a7d54094e3f6a80ccba5b710353e9bda7d3313158985f + md5: 7259e501bb4288143582312017bb1e44 + depends: + - python + - python 3.14.* *_cp314 + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 523325 + timestamp: 1762093068430 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda + sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 + md5: b3c17d95b5a10c6e64a21fa17573e70e + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: MIT + license_family: MIT + size: 8252 + timestamp: 1726802366959 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda + sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 + md5: 415816daf82e0b23a736a069a75e9da7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 8381 + timestamp: 1726802424786 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-22.0.0-py314hdafbbf9_0.conda + sha256: c10ea8100848236cda04307a00cdeba5a86358fc537132ffcc5cac8cc27f5547 + md5: ecb1085032bfa2bbd310807ca6c0c7f6 + depends: + - libarrow-acero 22.0.0.* + - libarrow-dataset 22.0.0.* + - libarrow-substrait 22.0.0.* + - libparquet 22.0.0.* + - pyarrow-core 22.0.0 *_0_* + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + size: 26193 + timestamp: 1761648748916 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-22.0.0-py314he55896b_0.conda + sha256: 1c15052ed5cdd0478964ea0b0f73bbc5db1c49f9b6923a378ba4b8dd2d9b802d + md5: 27b21816e9427b5bb9f5686c122b8730 + depends: + - libarrow-acero 22.0.0.* + - libarrow-dataset 22.0.0.* + - libarrow-substrait 22.0.0.* + - libparquet 22.0.0.* + - pyarrow-core 22.0.0 *_0_* + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: APACHE + size: 26356 + timestamp: 1761649037869 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-22.0.0-py314h52d6ec5_0_cpu.conda + sha256: 89d1fdb21ca6488c2e7a262d84eaf3ab4fbdd555a3ce91915869d9bfe640b92e + md5: 3c690d2816c2fe6e8d02a0f60549a393 + depends: + - __glibc >=2.17,<3.0.a0 + - libarrow 22.0.0.* *cpu + - libarrow-compute 22.0.0.* *cpu + - libgcc >=14 + - libstdcxx >=14 + - libzlib >=1.3.1,<2.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - apache-arrow-proc * cpu + - numpy >=1.21,<3 + license: Apache-2.0 + license_family: APACHE + size: 4814230 + timestamp: 1761648682122 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyarrow-core-22.0.0-py314hf20a12a_0_cpu.conda + sha256: d06476026a96d93bc44b0269e8b9abcc2b18adb56d82cd69d2f33e8cc0b47299 + md5: e02b151500dcd291ab7cd8f2bd46fef3 + depends: + - __osx >=11.0 + - libarrow 22.0.0.* *cpu + - libarrow-compute 22.0.0.* *cpu + - libcxx >=18 + - libzlib >=1.3.1,<2.0a0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - numpy >=1.21,<3 + - apache-arrow-proc * cpu + license: Apache-2.0 + license_family: APACHE + size: 3912295 + timestamp: 1761648977007 +- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + size: 110100 + timestamp: 1733195786147 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.12.4-pyh3cfb1c2_0.conda + sha256: c51297f0f6ef13776cc5b61c37d00c0d45faaed34f81d196e64bebc989f3e497 + md5: bf6ce72315b6759453d8c90a894e9e4c + depends: + - annotated-types >=0.6.0 + - pydantic-core 2.41.5 + - python >=3.10 + - typing-extensions >=4.6.1 + - typing-inspection >=0.4.2 + - typing_extensions >=4.14.1 + license: MIT + license_family: MIT + size: 320446 + timestamp: 1762379584494 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.41.5-py314h2e6c369_1.conda + sha256: 7e0ae379796e28a429f8e48f2fe22a0f232979d65ec455e91f8dac689247d39f + md5: 432b0716a1dfac69b86aa38fdd59b7e6 + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.14.* *_cp314 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 1943088 + timestamp: 1762988995556 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.41.5-py314haad56a0_1.conda + sha256: dded9092d89f1d8c267d5ce8b5e21f935c51acb7a64330f507cdfb3b69a98116 + md5: 420a4b8024e9b22880f1e03b612afa7d + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - __osx >=11.0 + - python 3.14.* *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 1784478 + timestamp: 1762989019956 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-settings-2.12.0-pyh3cfb1c2_0.conda + sha256: 17d552dd19501909d626ff50cd23753d56e03ab670ce9096f1c4068e1eb90f2a + md5: 0a3042ce18b785982c64a8567cc3e512 + depends: + - pydantic >=2.7.0 + - python >=3.10 + - python-dotenv >=0.21.0 + - typing-inspection >=0.4.0 + license: MIT + license_family: MIT + size: 43752 + timestamp: 1762786342653 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda + sha256: 5577623b9f6685ece2697c6eb7511b4c9ac5fb607c9babc2646c811b428fd46a + md5: 6b6ece66ebcae2d5f326c77ef2c5a066 + depends: + - python >=3.9 + license: BSD-2-Clause + license_family: BSD + size: 889287 + timestamp: 1750615908735 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyinstrument-5.1.1-py314h5bd0f2a_1.conda + sha256: 7a9d20baeb72a0421bb9b028a51ffbceb52f138a9a1c617685070cc78ef13953 + md5: 5a5e1add557c80c864b3b877e1c9a111 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14.0rc2,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 191406 + timestamp: 1756310993800 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyinstrument-5.1.1-py314hb84d1df_1.conda + sha256: 09168b017a66e1b2ae1235c8c17358c7ed46b7328553b34bea9e1b8ae74101fa + md5: d6fd0614fa9c78a51fa920ecd1bb67de + depends: + - __osx >=11.0 + - python >=3.14.0rc2,<3.15.0a0 + - python >=3.14.0rc2,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 191337 + timestamp: 1756311392758 +- conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda + sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 + md5: 461219d1a5bd61342293efa2c0c90eac + depends: + - __unix + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 21085 + timestamp: 1733217331982 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.0-h32b2ec7_102_cp314.conda + build_number: 102 + sha256: 76d750045b94fded676323bfd01975a26a474023635735773d0e4d80aaa72518 + md5: 0a19d2cc6eb15881889b0c6fa7d6a78d + depends: + - __glibc >=2.17,<3.0.a0 + - bzip2 >=1.0.8,<2.0a0 + - ld_impl_linux-64 >=2.36.1 + - libexpat >=2.7.1,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - liblzma >=5.8.1,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libuuid >=2.41.2,<3.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.4,<4.0a0 + - python_abi 3.14.* *_cp314 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Python-2.0 + size: 36681389 + timestamp: 1761176838143 + python_site_packages_path: lib/python3.14/site-packages +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.0-h40d2674_102_cp314.conda + build_number: 102 + sha256: 3ca1da026fe5df8a479d60e1d3ed02d9bc50fcbafd5f125d86abe70d21a34cc7 + md5: a9ff09231c555da7e30777747318321b + depends: + - __osx >=11.0 + - bzip2 >=1.0.8,<2.0a0 + - libexpat >=2.7.1,<3.0a0 + - libffi >=3.5.2,<3.6.0a0 + - liblzma >=5.8.1,<6.0a0 + - libmpdec >=4.0.0,<5.0a0 + - libsqlite >=3.50.4,<4.0a0 + - libzlib >=1.3.1,<2.0a0 + - ncurses >=6.5,<7.0a0 + - openssl >=3.5.4,<4.0a0 + - python_abi 3.14.* *_cp314 + - readline >=8.2,<9.0a0 + - tk >=8.6.13,<8.7.0a0 + - tzdata + - zstd >=1.5.7,<1.6.0a0 + license: Python-2.0 + size: 13590581 + timestamp: 1761177195716 + python_site_packages_path: lib/python3.14/site-packages +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda + sha256: d6a17ece93bbd5139e02d2bd7dbfa80bee1a4261dced63f65f679121686bf664 + md5: 5b8d21249ff20967101ffa321cab24e8 + depends: + - python >=3.9 + - six >=1.5 + - python + license: Apache-2.0 + license_family: APACHE + size: 233310 + timestamp: 1751104122689 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-dotenv-1.2.1-pyhcf101f3_0.conda + sha256: aa98e0b1f5472161318f93224f1cfec1355ff69d2f79f896c0b9e033e4a6caf9 + md5: 083725d6cd3dc007f06d04bcf1e613a2 + depends: + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + size: 26922 + timestamp: 1761503229008 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.0-h4df99d1_102.conda + sha256: e68c9796fba0825ebc1338ceb94496683ab7d45dcd281b378ec2a56365d3c555 + md5: d152e423d80848fe95f0f4b43448030e + depends: + - cpython 3.14.0.* + - python_abi * *_cp314 + license: Python-2.0 + size: 48968 + timestamp: 1761175555295 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda + sha256: 4790787fe1f4e8da616edca4acf6a4f8ed4e7c6967aa31b920208fc8f95efcca + md5: a61bf9ec79426938ff785eb69dbb1960 + depends: + - python >=3.6 + license: BSD-2-Clause + license_family: BSD + size: 13383 + timestamp: 1677079727691 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-multipart-0.0.20-pyhff2d567_0.conda + sha256: 1b03678d145b1675b757cba165a0d9803885807792f7eb4495e48a38858c3cca + md5: a28c984e0429aff3ab7386f7de56de6f + depends: + - python >=3.9 + license: Apache-2.0 + license_family: Apache + size: 27913 + timestamp: 1734420869885 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda + sha256: e8392a8044d56ad017c08fec2b0eb10ae3d1235ac967d0aab8bd7b41c4a5eaf0 + md5: 88476ae6ebd24f39261e0854ac244f33 + depends: + - python >=3.9 + license: Apache-2.0 + license_family: APACHE + size: 144160 + timestamp: 1742745254292 +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.6.0-py314he82b845_1.conda + sha256: 2f717c70b01bc090f44b8805a50848d11ea3955d77eb934ae557fb5b21d10f66 + md5: 21dce7c80bbdb9785633011ad348e530 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - xxhash >=0.8.3,<0.8.4.0a0 + license: BSD-2-Clause + license_family: BSD + size: 24040 + timestamp: 1762516286201 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-xxhash-3.6.0-py314h8cb506f_1.conda + sha256: e0cda3e7c3fbe4f0734300613243bf17fd332e7d504338b65857becb5ec6960a + md5: 51da513d16efa9a6a5582ab82f244191 + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + - xxhash >=0.8.3,<0.8.4.0a0 + license: BSD-2-Clause + license_family: BSD + size: 22456 + timestamp: 1762516760125 +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda + build_number: 8 + sha256: ad6d2e9ac39751cc0529dd1566a26751a0bf2542adb0c232533d32e176e21db5 + md5: 0539938c55b6b1a59b560e843ad864a4 + constrains: + - python 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 6989 + timestamp: 1752805904792 +- conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda + sha256: 8d2a8bf110cc1fc3df6904091dead158ba3e614d8402a83e51ed3a8aa93cdeb0 + md5: bc8e3267d44011051f2eb14d22fb0960 + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 189015 + timestamp: 1742920947249 +- conda: https://conda.anaconda.org/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_0.conda + sha256: 828af2fd7bb66afc9ab1c564c2046be391aaf66c0215f05afaf6d7a9a270fe2a + md5: b12f41c0d7fb5ab81709fcc86579688f + depends: + - python >=3.10.* + - yaml + track_features: + - pyyaml_no_compile + license: MIT + license_family: MIT + size: 45223 + timestamp: 1758891992558 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hfb55c3c_0.conda + noarch: python + sha256: a00a41b66c12d9c60e66b391e9a4832b7e28743348cf4b48b410b91927cd7819 + md5: 3399d43f564c905250c1aea268ebb935 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + - _python_abi3_support 1.* + - cpython >=3.12 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 212218 + timestamp: 1757387023399 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312hd65ceae_0.conda + noarch: python + sha256: ef33812c71eccf62ea171906c3e7fc1c8921f31e9cc1fbc3f079f3f074702061 + md5: bbd22b0f0454a5972f68a5f200643050 + depends: + - python + - __osx >=11.0 + - libcxx >=19 + - _python_abi3_support 1.* + - cpython >=3.12 + - zeromq >=4.3.5,<4.4.0a0 + license: BSD-3-Clause + license_family: BSD + size: 191115 + timestamp: 1757387128258 +- conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda + sha256: 2f225ddf4a274743045aded48053af65c31721e797a45beed6774fdc783febfb + md5: 0227d04521bc3d28c7995c7e1f99a721 + depends: + - libre2-11 2025.11.05 h7b12aa8_0 + license: BSD-3-Clause + license_family: BSD + size: 27316 + timestamp: 1762397780316 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-h64b956e_0.conda + sha256: 29c4bceb6b4530bac6820c30ba5a2f53fd26ed3e7003831ecf394e915b975fbc + md5: 1b35e663ed321840af65e7c5cde419f2 + depends: + - libre2-11 2025.11.05 h91c62da_0 + license: BSD-3-Clause + license_family: BSD + size: 27422 + timestamp: 1762398340843 +- conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c + md5: 283b96675859b20a825f8fa30f311446 + depends: + - libgcc >=13 + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 282480 + timestamp: 1740379431762 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda + sha256: 7db04684d3904f6151eff8673270922d31da1eea7fa73254d01c437f49702e34 + md5: 63ef3f6e6d6d5c589e64f11263dc5676 + depends: + - ncurses >=6.5,<7.0a0 + license: GPL-3.0-only + license_family: GPL + size: 252359 + timestamp: 1740379663071 +- conda: https://conda.anaconda.org/conda-forge/linux-64/regex-2025.11.3-py314h5bd0f2a_1.conda + sha256: 730079bfddd870fcbd53af6d179a8538847f3759f1accadab1e75ca81fa06360 + md5: 97ae548b60abe1ab43fb93f68291ea33 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 AND CNRI-Python + license_family: PSF + size: 412412 + timestamp: 1762507054987 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/regex-2025.11.3-py314h0612a62_1.conda + sha256: 2f741f2c5e9ba487f2ff657bbe2d14bf4cdd04b8d500063d29e9ac0e77fb3aba + md5: 79090b6d73e483775625108df9303a6d + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 AND CNRI-Python + license_family: PSF + size: 377443 + timestamp: 1762507332932 +- conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda + sha256: 8dc54e94721e9ab545d7234aa5192b74102263d3e704e6d0c8aa7008f2da2a7b + md5: db0c6b99149880c8ba515cf4abe93ee4 + depends: + - certifi >=2017.4.17 + - charset-normalizer >=2,<4 + - idna >=2.5,<4 + - python >=3.9 + - urllib3 >=1.21.1,<3 + constrains: + - chardet >=3.0.2,<6 + license: Apache-2.0 + license_family: APACHE + size: 59263 + timestamp: 1755614348400 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda + sha256: edfb44d0b6468a8dfced728534c755101f06f1a9870a7ad329ec51389f16b086 + md5: a247579d8a59931091b16a1e932bbed6 + depends: + - markdown-it-py >=2.2.0 + - pygments >=2.13.0,<3.0.0 + - python >=3.10 + - typing_extensions >=4.0.0,<5.0.0 + - python + license: MIT + license_family: MIT + size: 200840 + timestamp: 1760026188268 +- conda: https://conda.anaconda.org/conda-forge/noarch/rich-toolkit-0.16.0-pyhcf101f3_0.conda + sha256: 76857d1dfaf2bae7ace02f9cdb8ad9f2d654b0cbba291514bfd9785351ff470f + md5: 4d6a58603c5bfdffa4d4e93176c46e73 + depends: + - python >=3.10 + - rich >=13.7.1 + - click >=8.1.7 + - typing_extensions >=4.12.2 + - python + license: MIT + license_family: MIT + size: 30208 + timestamp: 1763570584515 +- conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.6.0-h8399546_1.conda + sha256: f5b294ce9b40d15a4bc31b315364459c0d702dd3e8751fe8735c88ac6a9ddc67 + md5: 8dbc626b1b11e7feb40a14498567b954 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - openssl >=3.5.4,<4.0a0 + license: Apache-2.0 + license_family: Apache + size: 393615 + timestamp: 1762176592236 +- conda: https://conda.anaconda.org/conda-forge/linux-64/safetensors-0.7.0-py314ha5689aa_0.conda + sha256: c426ea6d97904fa187ede5f329b0152b11d0a1cbc195e17341fe0403fc78f685 + md5: a89e88bc4a311084a6393bffd5e69bab + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 452508 + timestamp: 1763569634991 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/safetensors-0.7.0-py314h8d4a433_0.conda + sha256: b4985ee189e8ea2e012206ee5196e37b0f9759cc3390d8a0a4cc6530e062d58e + md5: edc25331f7b299e2e777f8749b4599bc + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 396052 + timestamp: 1763570163071 +- conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py314he7377e1_1.conda + sha256: ac76c6187848e529dd0ada06748c7470417ea3994dae24ce9844ff43adf07901 + md5: 881c9466d204a11f424225793bc3c27a + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=14 + - numpy <2.6 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 16864022 + timestamp: 1763220800462 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py314h624bdf2_1.conda + sha256: 34034cbd27588eb8522c90930da556a272555384d3d35952dc2f1750971c390d + md5: 8ff6098e9df32259abcd8475c46c419a + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - libgfortran5 >=15.2.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.6 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 14084720 + timestamp: 1763220862474 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sentencepiece-0.2.0-h43ba129_13.conda + sha256: 124f047fec61dbb43364fb072c9584f85ea1b24d021b574121fe8f92e3fca9ef + md5: accb7ef7308ec704be6e84e830ea8d1b + depends: + - libsentencepiece 0.2.0 h022d5ca_13 + - python_abi 3.14.* *_cp314 + - sentencepiece-python 0.2.0 py314h8261406_13 + - sentencepiece-spm 0.2.0 h022d5ca_13 + license: Apache-2.0 + license_family: Apache + size: 20099 + timestamp: 1758534789319 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sentencepiece-0.2.0-h08a494e_13.conda + sha256: c7a27fd6e3f0ddf2856a52f56d8a88cc9818d28e2c8d28db2b23e722621494dd + md5: 32de3959321c48f813552eba9f5c1fb4 + depends: + - libsentencepiece 0.2.0 h79950eb_13 + - python_abi 3.14.* *_cp314 + - sentencepiece-python 0.2.0 py314hbf90ac2_13 + - sentencepiece-spm 0.2.0 h79950eb_13 + license: Apache-2.0 + license_family: Apache + size: 20316 + timestamp: 1758535236730 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sentencepiece-python-0.2.0-py314h8261406_13.conda + sha256: fee984c26aef91fa1957f617ccfa805b286823ffce1ab0405296e97366bbaf43 + md5: a58ec7106c38a490434f5835d63d32d9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libsentencepiece 0.2.0 h022d5ca_13 + - libstdcxx >=14 + - python >=3.14.0rc3,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: Apache + size: 2402059 + timestamp: 1758534390142 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sentencepiece-python-0.2.0-py314hbf90ac2_13.conda + sha256: aeb6cb2d03c042de60762f205ecefd7d65a9ad547afb5e8d82cee5758681c65e + md5: dddddd77d0ff11d389456eab0f58e44a + depends: + - __osx >=11.0 + - libcxx >=19 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libsentencepiece 0.2.0 h79950eb_13 + - python >=3.14.0rc3,<3.15.0a0 + - python >=3.14.0rc3,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: Apache + size: 2608077 + timestamp: 1758534773309 +- conda: https://conda.anaconda.org/conda-forge/linux-64/sentencepiece-spm-0.2.0-h022d5ca_13.conda + sha256: 0336ccf7c2ec97080ad49a5a066e381bd02de237604f80aa53f48facefc611ca + md5: 4b441a00859cd2127c8cf76d3efd9884 + depends: + - __glibc >=2.17,<3.0.a0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libgcc >=14 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libsentencepiece 0.2.0 h022d5ca_13 + - libstdcxx >=14 + license: Apache-2.0 + license_family: Apache + size: 90615 + timestamp: 1758534774341 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/sentencepiece-spm-0.2.0-h79950eb_13.conda + sha256: 2af0c8c6523ff5551a75a4a5a78bf0214a02ed27fc909fb7af56710756218f38 + md5: e47f3ca0da4b6b1d7afd130c9af51624 + depends: + - __osx >=11.0 + - libabseil * cxx17* + - libabseil >=20250512.1,<20250513.0a0 + - libcxx >=19 + - libprotobuf >=6.31.1,<6.31.2.0a0 + - libsentencepiece 0.2.0 h79950eb_13 + license: Apache-2.0 + license_family: Apache + size: 84848 + timestamp: 1758535193868 +- conda: https://conda.anaconda.org/conda-forge/noarch/shellingham-1.5.4-pyhd8ed1ab_2.conda + sha256: 1d6534df8e7924d9087bd388fbac5bd868c5bf8971c36885f9f016da0657d22b + md5: 83ea3a2ddb7a75c1b09cea582aa4f106 + depends: + - python >=3.10 + license: MIT + license_family: MIT + size: 15018 + timestamp: 1762858315311 +- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d + md5: 3339e3b65d58accf4ca4fb8748ab16b3 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + size: 18455 + timestamp: 1753199211006 +- conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda + sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 + md5: 98b6c9dc80eb87b2519b97bcf7e578dd + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libstdcxx >=14 + - libgcc >=14 + license: BSD-3-Clause + license_family: BSD + size: 45829 + timestamp: 1762948049098 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda + sha256: cb9305ede19584115f43baecdf09a3866bfcd5bcca0d9e527bd76d9a1dbe2d8d + md5: fca4a2222994acd7f691e57f94b750c5 + depends: + - libcxx >=19 + - __osx >=11.0 + license: BSD-3-Clause + license_family: BSD + size: 38883 + timestamp: 1762948066818 +- conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda + sha256: dce518f45e24cd03f401cb0616917773159a210c19d601c5f2d4e0e5879d30ad + md5: 03fe290994c5e4ec17293cfb6bdce520 + depends: + - python >=3.10 + license: Apache-2.0 + license_family: Apache + size: 15698 + timestamp: 1762941572482 +- conda: https://conda.anaconda.org/conda-forge/noarch/sse-starlette-3.0.3-pyhd8ed1ab_0.conda + sha256: e96f603ddc9979ae20c42dfb02e3c0c67e8e1bfd938b9a0e9d66f21d0acf14f9 + md5: dceec34e1e7394d50a7b67f09962ab44 + depends: + - anyio >=4.7.0 + - python >=3.10 + - starlette >=0.41.3 + license: BSD-3-Clause + license_family: BSD + size: 17374 + timestamp: 1761854658607 +- conda: https://conda.anaconda.org/conda-forge/noarch/starlette-0.50.0-pyhfdc7a7d_0.conda + sha256: ab9ab67faa3cf12f45f5ced316e2c50dc72b4046cd275612fae756fe9d4cf82c + md5: 68bcb398c375177cf117cf608c274f9d + depends: + - anyio >=3.6.2,<5 + - python >=3.10 + - typing_extensions >=4.10.0 + - python + license: BSD-3-Clause + license_family: BSD + size: 64760 + timestamp: 1762016292582 +- conda: https://conda.anaconda.org/conda-forge/noarch/taskgroup-0.2.2-pyhd8ed1ab_0.conda + sha256: 6f8db6da8de445930de55b708e6a5d3ab5f076bc14a39578db0190b2a9b8e437 + md5: 9fa69537fb68a095fbac139210575bad + depends: + - exceptiongroup + - python >=3.9 + - typing_extensions >=4.12.2,<5 + license: MIT + license_family: MIT + size: 17330 + timestamp: 1736003478648 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda + sha256: 1544760538a40bcd8ace2b1d8ebe3eb5807ac268641f8acdc18c69c5ebfeaf64 + md5: 86bc20552bf46075e3d92b67f089172d + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib >=1.3.1,<2.0a0 + constrains: + - xorg-libx11 >=1.8.12,<2.0a0 + license: TCL + license_family: BSD + size: 3284905 + timestamp: 1763054914403 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda + sha256: ad0c67cb03c163a109820dc9ecf77faf6ec7150e942d1e8bb13e5d39dc058ab7 + md5: a73d54a5abba6543cb2f0af1bfbd6851 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: TCL + license_family: BSD + size: 3125484 + timestamp: 1763055028377 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tokenizers-0.22.1-py314h7fe7e61_1.conda + sha256: 0fe828cb672fbc758bb6d4c17d38424b135205ce4c7cb4192d63bb9dd8a28b38 + md5: 588446ad310fdee5245da28704d37238 + depends: + - __glibc >=2.17,<3.0.a0 + - huggingface_hub >=0.16.4,<2.0 + - libgcc >=14 + - libstdcxx >=14 + - openssl >=3.6.0,<4.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + size: 2466693 + timestamp: 1762916761482 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tokenizers-0.22.1-py314h84b920e_1.conda + sha256: 4593f52046a408851d0b3b6b8fa612bbd45a73cfccd3f093dcdd1fe76c58a155 + md5: 5028ec27bfd710e794fc5503ce7d0b5e + depends: + - __osx >=11.0 + - huggingface_hub >=0.16.4,<2.0 + - libcxx >=19 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + size: 2229424 + timestamp: 1762917734250 +- conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda + sha256: cb77c660b646c00a48ef942a9e1721ee46e90230c7c570cdeb5a893b5cce9bff + md5: d2732eb636c264dc9aa4cbee404b1a53 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + size: 20973 + timestamp: 1760014679845 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.2-py314h5bd0f2a_2.conda + sha256: a4482fff049ad4e2907969b2c11242b712b33cdad9bbf88122a705e179af04da + md5: 972071a83bc345cb2a13c2c5b662ff5b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: Apache + size: 902474 + timestamp: 1762506844640 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.2-py314h0612a62_2.conda + sha256: aec65f3c244255c75e4f6e093f094f851a8566ea5ece7d8cbfffb2af745676a3 + md5: a085241420b4c86f8efc85830b0690b6 + depends: + - __osx >=11.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: Apache-2.0 + license_family: Apache + size: 901904 + timestamp: 1762507135570 +- conda: https://conda.anaconda.org/conda-forge/noarch/tqdm-4.67.1-pyhd8ed1ab_1.conda + sha256: 11e2c85468ae9902d24a27137b6b39b4a78099806e551d390e394a8c34b48e40 + md5: 9efbfdc37242619130ea42b1cc4ed861 + depends: + - colorama + - python >=3.9 + license: MPL-2.0 or MIT + size: 89498 + timestamp: 1735661472632 +- conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda + sha256: f39a5620c6e8e9e98357507262a7869de2ae8cc07da8b7f84e517c9fd6c2b959 + md5: 019a7385be9af33791c989871317e1ed + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + size: 110051 + timestamp: 1733367480074 +- conda: https://conda.anaconda.org/conda-forge/noarch/transformers-4.57.1-pyhd8ed1ab_0.conda + sha256: aacf4b2f50605e8c79a3aa825e4b93d3dd6b12fbf8ff816d2a5e508b105cfd8f + md5: 6dc46b44cb5269894eefef339e21f2fe + depends: + - datasets !=2.5.0 + - filelock + - huggingface_hub >=0.34.0,<1.0 + - numpy >=1.17 + - packaging >=20.0 + - python >=3.10 + - pyyaml >=5.1 + - regex !=2019.12.17 + - requests + - safetensors >=0.4.1 + - tokenizers >=0.22,<=0.23 + - tqdm >=4.27 + license: Apache-2.0 + license_family: APACHE + size: 4357806 + timestamp: 1760462342042 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-0.20.0-pyhefaf540_1.conda + sha256: 17a1e572939af33d709248170871d4da74f7e32b48f2e9b5abca613e201c6e64 + md5: 23a53fdefc45ba3f4e075cc0997fd13b + depends: + - typer-slim-standard ==0.20.0 h4daf872_1 + - python >=3.10 + - python + license: MIT + license_family: MIT + size: 79829 + timestamp: 1762984042927 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-0.20.0-pyhcf101f3_1.conda + sha256: 4b5ded929080b91367f128e7299619f6116f08bc77d9924a2f8766e2a1b18161 + md5: 4b02a515f3e882dcfe9cfbf0a1f5cd3a + depends: + - python >=3.10 + - click >=8.0.0 + - typing_extensions >=3.7.4.3 + - python + constrains: + - typer 0.20.0.* + - rich >=10.11.0 + - shellingham >=1.3.0 + license: MIT + license_family: MIT + size: 47951 + timestamp: 1762984042920 +- conda: https://conda.anaconda.org/conda-forge/noarch/typer-slim-standard-0.20.0-h4daf872_1.conda + sha256: 5027768bc9a580c8ffbf25872bb2208c058cbb79ae959b1cf2cc54b5d32c0377 + md5: 37b26aafb15a6687b31a3d8d7a1f04e7 + depends: + - typer-slim ==0.20.0 pyhcf101f3_1 + - rich + - shellingham + license: MIT + license_family: MIT + size: 5322 + timestamp: 1762984042927 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c + md5: edd329d7d3a4ab45dcf905899a7a6115 + depends: + - typing_extensions ==4.15.0 pyhcf101f3_0 + license: PSF-2.0 + license_family: PSF + size: 91383 + timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhd8ed1ab_0.conda + sha256: 8aaf69b828c2b94d0784f18f70f11aa032950d304e57e88467120b45c18c24fd + md5: 399701494e731ce73fdd86c185a3d1b4 + depends: + - python >=3.10 + - typing_extensions >=4.12.0 + license: MIT + license_family: MIT + size: 18799 + timestamp: 1759301271883 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 + md5: 0caa1af407ecff61170c9437a808404d + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + size: 51692 + timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + sha256: 5aaa366385d716557e365f0a4e9c3fca43ba196872abbbe3d56bb610d131e192 + md5: 4222072737ccff51314b5ece9c7d6f5a + license: LicenseRef-Public-Domain + size: 122968 + timestamp: 1742727099393 +- conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda + sha256: 4fb9789154bd666ca74e428d973df81087a697dbb987775bc3198d2215f240f8 + md5: 436c165519e140cb08d246a4472a9d6a + depends: + - brotli-python >=1.0.9 + - h2 >=4,<5 + - pysocks >=1.5.6,<2.0,!=1.5.7 + - python >=3.9 + - zstandard >=0.18.0 + license: MIT + license_family: MIT + size: 101735 + timestamp: 1750271478254 +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-0.38.0-pyh31011fe_0.conda + sha256: 32e637726fd7cfeb74058e829b116e17514d001846fef56d8c763ec9ec5ac887 + md5: d3aa78bc38d9478e9eed5f128ba35f41 + depends: + - __unix + - click >=7.0 + - h11 >=0.8 + - python >=3.10 + - typing_extensions >=4.0 + license: BSD-3-Clause + license_family: BSD + size: 51717 + timestamp: 1760803935306 +- conda: https://conda.anaconda.org/conda-forge/noarch/uvicorn-standard-0.38.0-h31011fe_0.conda + sha256: 3629a349257c0e129cbb84fd593759a31d68ac1219c0af8b8ed89b95b9574c9b + md5: 1ce870d7537376362672f5ff57109529 + depends: + - __unix + - httptools >=0.6.3 + - python-dotenv >=0.13 + - pyyaml >=5.1 + - uvicorn 0.38.0 pyh31011fe_0 + - uvloop >=0.14.0,!=0.15.0,!=0.15.1 + - watchfiles >=0.13 + - websockets >=10.4 + license: BSD-3-Clause + license_family: BSD + size: 7719 + timestamp: 1760803936446 +- conda: https://conda.anaconda.org/conda-forge/linux-64/uvloop-0.22.1-py314h5bd0f2a_1.conda + sha256: ad3058ed67e1de5f9a73622a44a5c7a51af6a4527cf4881ae22b8bb6bd30bceb + md5: 41f06d5cb2a80011c7da5a835721acdd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libuv >=1.51.0,<2.0a0 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: MIT OR Apache-2.0 + size: 593392 + timestamp: 1762472837997 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/uvloop-0.22.1-py314h0612a62_1.conda + sha256: 7850dd9238beb14f9c7db1901229cc5d2ecd10d031cbdb712a95eba57a5d5992 + md5: 74683034f513752be1467c9232480a13 + depends: + - __osx >=11.0 + - libuv >=1.51.0,<2.0a0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: MIT OR Apache-2.0 + size: 492509 + timestamp: 1762473163613 +- conda: https://conda.anaconda.org/conda-forge/linux-64/watchfiles-1.1.1-py314ha5689aa_0.conda + sha256: fcec93ca26320764c55042fc56b772a88533ed01f1c713553c985b379e174d09 + md5: fb190bbf05b3b963bea7ab7c20624d5d + depends: + - __glibc >=2.17,<3.0.a0 + - anyio >=3.0.0 + - libgcc >=14 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + size: 421969 + timestamp: 1760456771978 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/watchfiles-1.1.1-py314h8d4a433_0.conda + sha256: b9446970047031e66edf76548fa427fe0ce7e81655208dc2e2a0b0bf94ebf7ba + md5: 33c8e4a66a7cb5d75ba8165a6075cd28 + depends: + - __osx >=11.0 + - anyio >=3.0.0 + - python >=3.14,<3.15.0a0 + - python >=3.14,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 367150 + timestamp: 1760457260426 +- conda: https://conda.anaconda.org/conda-forge/linux-64/websockets-15.0.1-py314h31f8a6b_2.conda + sha256: 102c0acc2301908bcc0bd0c792e059cf8a6b93fc819f56c8a3b8a6b473afe58a + md5: e05c3cce47cc4f32f886eb17091ba6e2 + depends: + - python + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 380425 + timestamp: 1756476367704 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/websockets-15.0.1-py314hf17b0b1_2.conda + sha256: c00677dc11e5f20e115ab7252c60893cd0bac9fc78b12678d62ba6b1b5dcb3f7 + md5: 22ef4a8d9fdd426f7fb9d5b3bf168c2a + depends: + - python + - python 3.14.* *_cp314 + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 383627 + timestamp: 1756476437332 +- conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.17.3-py314h5bd0f2a_1.conda + sha256: e2b6545651aed5e7dead39b7ba3bf8c2669f194c71e89621343bd0bb321a87f1 + md5: 82da729c870ada2f675689a39b4f697f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.14.0rc2,<3.15.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-2-Clause + license_family: BSD + size: 64997 + timestamp: 1756851739706 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-1.17.3-py314hb84d1df_1.conda + sha256: 0f35a19fd99724e8620dc89a6fb9eb100d300f117292adde2c7e8cf12d566e10 + md5: 104bf69250e32a42ca144d7f7abd5d5c + depends: + - __osx >=11.0 + - python >=3.14.0rc2,<3.15.0a0 + - python >=3.14.0rc2,<3.15.0a0 *_cp314 + - python_abi 3.14.* *_cp314 + license: BSD-2-Clause + license_family: BSD + size: 61800 + timestamp: 1756851815321 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda + sha256: 6bc6ab7a90a5d8ac94c7e300cc10beb0500eeba4b99822768ca2f2ef356f731b + md5: b2895afaf55bf96a8c8282a2e47a5de0 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 15321 + timestamp: 1762976464266 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda + sha256: adae11db0f66f86156569415ed79cda75b2dbf4bea48d1577831db701438164f + md5: 78b548eed8227a689f93775d5d23ae09 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 14105 + timestamp: 1762976976084 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda + sha256: 25d255fb2eef929d21ff660a0c687d38a6d2ccfbcbf0cc6aa738b12af6e9d142 + md5: 1dafce8548e38671bea82e3f5c6ce22f + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + license: MIT + license_family: MIT + size: 20591 + timestamp: 1762976546182 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda + sha256: f7fa0de519d8da589995a1fe78ef74556bb8bc4172079ae3a8d20c3c81354906 + md5: 9d1299ace1924aa8f4e0bc8e71dd0cf7 + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 19156 + timestamp: 1762977035194 +- conda: https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.3-hb47aa4a_0.conda + sha256: 08e12f140b1af540a6de03dd49173c0e5ae4ebc563cabdd35ead0679835baf6f + md5: 607e13a8caac17f9a664bcab5302ce06 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + license: BSD-2-Clause + license_family: BSD + size: 108219 + timestamp: 1746457673761 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/xxhash-0.8.3-haa4e116_0.conda + sha256: 5e2e58fbaa00eeab721a86cb163a54023b3b260e91293dde7e5334962c5c96e3 + md5: 54a24201d62fc17c73523e4b86f71ae8 + depends: + - __osx >=11.0 + license: BSD-2-Clause + license_family: BSD + size: 98913 + timestamp: 1746457827085 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + sha256: 6d9ea2f731e284e9316d95fa61869fe7bbba33df7929f82693c121022810f4ad + md5: a77f85f77be52ff59391544bfe73390a + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + size: 85189 + timestamp: 1753484064210 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + sha256: b03433b13d89f5567e828ea9f1a7d5c5d697bf374c28a4168d71e9464f5dafac + md5: 78a0fe9e9c50d2c381e8ee47e3ea437d + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + size: 83386 + timestamp: 1753484079473 +- conda: https://conda.anaconda.org/conda-forge/noarch/yarl-1.22.0-pyh7db6752_0.conda + sha256: b04271f56c68483b411c5465afff73b8eabdea564e942f0e7afed06619272635 + md5: ca3c00c764cee005798a518cba79885c + depends: + - idna >=2.0 + - multidict >=4.0 + - propcache >=0.2.1 + - python >=3.10 + track_features: + - yarl_no_compile + license: Apache-2.0 + license_family: Apache + size: 73066 + timestamp: 1761337117132 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h387f397_9.conda + sha256: 47cfe31255b91b4a6fa0e9dbaf26baa60ac97e033402dbc8b90ba5fee5ffe184 + md5: 8035e5b54c08429354d5d64027041cad + depends: + - libstdcxx >=14 + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libsodium >=1.0.20,<1.0.21.0a0 + - krb5 >=1.21.3,<1.22.0a0 + license: MPL-2.0 + license_family: MOZILLA + size: 310648 + timestamp: 1757370847287 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h888dc83_9.conda + sha256: b6f9c130646e5971f6cad708e1eee278f5c7eea3ca97ec2fdd36e7abb764a7b8 + md5: 26f39dfe38a2a65437c29d69906a0f68 + depends: + - __osx >=11.0 + - libcxx >=19 + - libsodium >=1.0.20,<1.0.21.0a0 + - krb5 >=1.21.3,<1.22.0a0 + license: MPL-2.0 + license_family: MOZILLA + size: 244772 + timestamp: 1757371008525 +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda + sha256: 7560d21e1b021fd40b65bfb72f67945a3fcb83d78ad7ccf37b8b3165ec3b68ad + md5: df5e78d904988eb55042c0c97446079f + depends: + - python >=3.9 + license: MIT + license_family: MIT + size: 22963 + timestamp: 1749421737203 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda + sha256: 5d7c0e5f0005f74112a34a7425179f4eb6e73c92f5d109e6af4ddeca407c92ab + md5: c9f075ab2f33b3bbee9e62d4ad0a6cd8 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libzlib 1.3.1 hb9d3cd8_2 + license: Zlib + license_family: Other + size: 92286 + timestamp: 1727963153079 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.1-h8359307_2.conda + sha256: 58f8860756680a4831c1bf4f294e2354d187f2e999791d53b1941834c4b37430 + md5: e3170d898ca6cb48f1bb567afb92f775 + depends: + - __osx >=11.0 + - libzlib 1.3.1 h8359307_2 + license: Zlib + license_family: Other + size: 77606 + timestamp: 1727963209370 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.2.5-hde8ca8f_0.conda + sha256: 3a8e7798deafd0722b6b5da50c36b7f361a80b30165d600f7760d569a162ff95 + md5: 1920c3502e7f6688d650ab81cd3775fd + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - libstdcxx >=14 + license: Zlib + license_family: Other + size: 110843 + timestamp: 1754587144298 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.2.5-h3470cca_0.conda + sha256: 82e3b57478d536b68229d1dbcdabe728fada5dbe77f9238a5fff5fc37a7fa758 + md5: c86493f35e79c93b04ff0279092b53e2 + depends: + - __osx >=11.0 + - libcxx >=19 + license: Zlib + license_family: Other + size: 87296 + timestamp: 1761843121173 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.25.0-py314h0f05182_1.conda + sha256: e589f694b44084f2e04928cabd5dda46f20544a512be2bdb0d067d498e4ac8d0 + md5: 2930a6e1c7b3bc5f66172e324a8f5fc3 + depends: + - python + - cffi >=1.11 + - zstd >=1.5.7,<1.5.8.0a0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - zstd >=1.5.7,<1.6.0a0 + - python_abi 3.14.* *_cp314 + license: BSD-3-Clause + license_family: BSD + size: 473605 + timestamp: 1762512687493 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstandard-0.25.0-py314h9d33bd4_1.conda + sha256: cdeb350914094e15ec6310f4699fa81120700ca7ab7162a6b3421f9ea9c690b4 + md5: 8a92a736ab23b4633ac49dcbfcc81e14 + depends: + - python + - cffi >=1.11 + - zstd >=1.5.7,<1.5.8.0a0 + - python 3.14.* *_cp314 + - __osx >=11.0 + - python_abi 3.14.* *_cp314 + - zstd >=1.5.7,<1.6.0a0 + license: BSD-3-Clause + license_family: BSD + size: 397786 + timestamp: 1762512730914 +- conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda + sha256: a4166e3d8ff4e35932510aaff7aa90772f84b4d07e9f6f83c614cba7ceefe0eb + md5: 6432cb5d4ac0046c3ac0a8a0f95842f9 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=13 + - libstdcxx >=13 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 567578 + timestamp: 1742433379869 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-h6491c7d_2.conda + sha256: 0d02046f57f7a1a3feae3e9d1aa2113788311f3cf37a3244c71e61a93177ba67 + md5: e6f69c7bcccdefa417f056fa593b40f0 + depends: + - __osx >=11.0 + - libzlib >=1.3.1,<2.0a0 + license: BSD-3-Clause + license_family: BSD + size: 399979 + timestamp: 1742433432699 diff --git a/pixi.toml b/pixi.toml index 02f9a46d..cc540621 100644 --- a/pixi.toml +++ b/pixi.toml @@ -33,14 +33,15 @@ backend = {name = "pixi-build-mojo", version = "0.*", channels = [ [package.build.config.pkg] name = "numojo" +# TODO: update these modular version before release. [package.host-dependencies] -modular = ">=25.6.1,<26" +modular = ">=25.7.0,<26" [package.build-dependencies] -modular = ">=25.6.1,<26" +modular = ">=25.7.0,<26" [package.run-dependencies] -modular = ">=25.6.1,<26" +modular = ">=25.7.0,<26" [tasks] # compile the package and copy it to the tests folder @@ -50,22 +51,48 @@ p = "clear && pixi run package" # format the package format = "pixi run mojo format ./" -# test whether tests pass on the built package -test = "pixi run package && pixi run mojo test tests -I tests/ && rm tests/numojo.mojopkg" -t = "clear && pixi run test" - -# run individual tests to avoid overheat -test_core = "pixi run package && pixi run mojo test tests/core -I tests/ && rm tests/numojo.mojopkg" -test_creation = "pixi run package && pixi run mojo test tests/routines/test_creation.mojo -I tests/ && rm tests/numojo.mojopkg" -test_functional = "pixi run package && pixi run mojo test tests/routines/test_functional.mojo -I tests/ && rm tests/numojo.mojopkg" -test_indexing = "pixi run package && pixi run mojo test tests/routines/test_indexing.mojo -I tests/ && rm tests/numojo.mojopkg" -test_linalg = "pixi run package && pixi run mojo test tests/routines/test_linalg.mojo -I tests/ && rm tests/numojo.mojopkg" -test_manipulation = "pixi run package && pixi run mojo test tests/routines/test_manipulation.mojo -I tests/ && rm tests/numojo.mojopkg" -test_math = "pixi run package && pixi run mojo test tests/routines/test_math.mojo -I tests/ && rm tests/numojo.mojopkg" -test_random = "pixi run package && pixi run mojo test tests/routines/test_random.mojo -I tests/ && rm tests/numojo.mojopkg" -test_statistics = "pixi run package && pixi run mojo test tests/routines/test_statistics.mojo -I tests/ && rm tests/numojo.mojopkg" -test_sorting = "pixi run package && pixi run mojo test tests/routines/test_sorting.mojo -I tests/ && rm tests/numojo.mojopkg" -test_searching = "pixi run package && pixi run mojo test tests/routines/test_searching.mojo -I tests/ && rm tests/numojo.mojopkg" +# to run individual test files +run-test = { cmd = "pixi run mojo run -I tests/ $TEST_FILE", env = { TEST_FILE = "" } } + +# Test core category +test_core = """ +pixi run package && \ +pixi run mojo run -I tests/ tests/core/test_array_indexing_and_slicing.mojo && \ +pixi run mojo run -I tests/ tests/core/test_array_methods.mojo && \ +pixi run mojo run -I tests/ tests/core/test_bool_masks.mojo && \ +pixi run mojo run -I tests/ tests/core/test_complexArray.mojo && \ +pixi run mojo run -I tests/ tests/core/test_complexSIMD.mojo && \ +pixi run mojo run -I tests/ tests/core/test_matrix.mojo && \ +pixi run mojo run -I tests/ -D F_CONTIGUOUS tests/core/test_matrix.mojo && \ +pixi run mojo run -I tests/ tests/core/test_shape_strides_item.mojo && \ +rm tests/numojo.mojopkg +""" + +# Test routines category +test_routines = """ +pixi run package && \ +pixi run mojo run -I tests/ tests/routines/test_creation.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_functional.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_indexing.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_io.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_linalg.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_manipulation.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_math.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_random.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_statistics.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_sorting.mojo && \ +pixi run mojo run -I tests/ tests/routines/test_searching.mojo && \ +rm tests/numojo.mojopkg +""" + +# Test science category +test_signal = "pixi run package && pixi run mojo run -I tests/ tests/science/test_signal.mojo && rm tests/numojo.mojopkg" + +test = """ +pixi run test_core && \ +pixi run test_routines && \ +pixi run test_signal +""" # run all final checks before a commit final = "pixi run format && pixi run test" @@ -78,7 +105,7 @@ doc_pages = "mojo doc numojo/ -o docs.json" release = "clear && pixi run final && pixi run doc_pages" [dependencies] -python = ">=3.13.9,<3.14" -numpy = ">=2.3.3,<3" -scipy = ">=1.16.2,<2" -modular = ">=25.6.1,<26" +python = ">=3.14.0,<3.15" +numpy = ">=2.3.5,<3" +scipy = ">=1.16.3,<2" +modular = ">=25.7.0,<26" diff --git a/tests/core/test_array_indexing_and_slicing.mojo b/tests/core/test_array_indexing_and_slicing.mojo index 8c08098f..4e9ed527 100644 --- a/tests/core/test_array_indexing_and_slicing.mojo +++ b/tests/core/test_array_indexing_and_slicing.mojo @@ -3,6 +3,7 @@ from numojo.prelude import * from testing.testing import assert_true, assert_almost_equal, assert_equal from utils_for_test import check, check_is_close from python import Python +from testing import TestSuite def test_getitem_scalar(): @@ -620,3 +621,7 @@ def test_3d_array_basic_slicing(): # nm_slice3 = nm_arr[::2, 1::2] # np_sliced3 = np_arr[::2, 1::2] # check(nm_slice3, np_sliced3, "F-order step [::2, 1::2] failed") + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/core/test_array_methods.mojo b/tests/core/test_array_methods.mojo index c49a79df..6d072a8a 100644 --- a/tests/core/test_array_methods.mojo +++ b/tests/core/test_array_methods.mojo @@ -3,6 +3,7 @@ from python import Python from numojo.prelude import * from testing.testing import assert_true, assert_almost_equal, assert_equal from utils_for_test import check, check_is_close, check_values_close +from testing import TestSuite def test_constructors(): @@ -148,3 +149,7 @@ def test_iterator(): fnp_nditer_f.__next__(), "`_NDIter` or `nditer()` of F array by order F breaks", ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/core/test_bool_masks.mojo b/tests/core/test_bool_masks.mojo index 33c99dab..aa65b7bb 100644 --- a/tests/core/test_bool_masks.mojo +++ b/tests/core/test_bool_masks.mojo @@ -3,6 +3,7 @@ from numojo import * from testing.testing import assert_true, assert_almost_equal, assert_equal from utils_for_test import check, check from python import Python +from testing import TestSuite # TODO: there's something wrong with bool comparision even though result looks same. @@ -65,3 +66,7 @@ def test_bool_masks_eq(): var np_mask = np_A[np_A > 10] var mask = A[A > Scalar[nm.i16](10)] check(mask, np_mask, "Masked array") + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/core/test_complexArray.mojo b/tests/core/test_complexArray.mojo index 1c260695..9d2fcf39 100644 --- a/tests/core/test_complexArray.mojo +++ b/tests/core/test_complexArray.mojo @@ -1,5 +1,6 @@ from testing import assert_equal, assert_almost_equal from numojo import * +from testing import TestSuite # TODO: Added getter and setter tests @@ -104,3 +105,7 @@ fn test_complex_array_div() raises: assert_almost_equal(quot.item(0).re, 0.44, "div failed") assert_almost_equal(quot.item(0).im, 0.08, "div failed") + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/core/test_complexSIMD.mojo b/tests/core/test_complexSIMD.mojo index fa0a9ec0..b2036288 100644 --- a/tests/core/test_complexSIMD.mojo +++ b/tests/core/test_complexSIMD.mojo @@ -1,5 +1,6 @@ from testing import assert_equal, assert_almost_equal from numojo import * +from testing import TestSuite fn test_complex_init() raises: @@ -9,8 +10,12 @@ fn test_complex_init() raises: assert_equal(c1.im, 2.0, "init failed") var c2 = ComplexSIMD[cf32](c1) - assert_equal(c2.re, c1.re) - assert_equal(c2.im, c1.im) + assert_equal(c2.re, c1.re, "init failed") + assert_equal(c2.im, c1.im, "init failed") + + var c3 = ComplexSIMD[cf32, 2](1.0) + assert_equal(c3.re[0], 1.0, "init failed") + assert_equal(c3.im[0], 1.0, "init failed") fn test_complex_add() raises: @@ -69,3 +74,7 @@ fn test_complex_div() raises: var quot = c1 / c2 assert_almost_equal(quot.re, 0.44, " division failed") assert_almost_equal(quot.im, 0.08, " division failed") + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/core/test_matrix.mojo b/tests/core/test_matrix.mojo index c22a4a23..dc86c9a2 100644 --- a/tests/core/test_matrix.mojo +++ b/tests/core/test_matrix.mojo @@ -1,9 +1,10 @@ import numojo as nm from numojo.prelude import * -from numojo.core.matrix import Matrix +from numojo.core.matrix import Matrix, MatrixBase from python import Python, PythonObject from testing.testing import assert_raises, assert_true from sys import is_defined +from testing import assert_equal, TestSuite alias order: String = String("F") if is_defined["F_CONTIGUOUS"]() else String( "C" @@ -58,8 +59,6 @@ def test_manipulation(): "Reshape is broken", ) - _ = A.resize((1000, 100)) - _ = Anp.resize(1000, 100) check_matrices_equal( A, Anp, @@ -124,6 +123,7 @@ def test_arithmetic(): check_matrices_close(A**0.5, np.power(Ap, 0.5), "Pow (to int) is broken") +# FIXME: the gt, lt tests are failing when run together with all other tests even though they pass in isolation. weird behaviour. Commmenting it out temporarily and fix later. def test_logic(): var np = Python.import_module("numpy") var A = Matrix.ones((5, 1), order=order) @@ -135,8 +135,13 @@ def test_logic(): var Bnp = np.matrix(B.to_numpy()) var Lnp = np.matrix(L.to_numpy()) - check_matrices_equal(A > B, Anp > Bnp, "gt is broken") - check_matrices_equal(A < B, Anp < Bnp, "lt is broken") + var gt_res = A > B + var gt_res_np = Anp > Bnp + var lt_res = A < B + var lt_res_np = Anp < Bnp + check_matrices_equal[DType.bool](gt_res, gt_res_np, "gt is broken") + check_matrices_equal[DType.bool](lt_res, lt_res_np, "lt is broken") + assert_true( np.equal(nm.all(L), np.all(Lnp)), "`all` is broken", @@ -158,298 +163,291 @@ def test_logic(): String("`any` by axis {i} is broken"), ) + # ===-----------------------------------------------------------------------===# + # Linear algebra + # ===-----------------------------------------------------------------------===# -# ===-----------------------------------------------------------------------===# -# Linear algebra -# ===-----------------------------------------------------------------------===# - - -def test_linalg(): - var np = Python.import_module("numpy") - var A = Matrix.rand[f64]((100, 100), order=order) - var B = Matrix.rand[f64]((100, 100), order=order) - var E = Matrix.fromstring( - "[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]", shape=(4, 3), order=order - ) - var Y = Matrix.rand((100, 1), order=order) - var Anp = A.to_numpy() - var Bnp = B.to_numpy() - var Ynp = Y.to_numpy() - var Enp = E.to_numpy() - check_matrices_close( - nm.linalg.solve(A, B), - np.linalg.solve(Anp, Bnp), - "Solve is broken", - ) - check_matrices_close( - nm.linalg.inv(A), - np.linalg.inv(Anp), - "Inverse is broken", - ) - check_matrices_close( - nm.linalg.lstsq(A, Y), - np.linalg.lstsq(Anp, Ynp)[0], - "Least square is broken", - ) - check_matrices_close( - A.transpose(), - Anp.transpose(), - "Transpose is broken", - ) - check_matrices_close( - Y.transpose(), - Ynp.transpose(), - "Transpose is broken", - ) - assert_true( - np.all(np.isclose(nm.linalg.det(A), np.linalg.det(Anp), atol=0.1)), - "Determinant is broken", - ) - for i in range(-10, 10): + def test_linalg(): + var np = Python.import_module("numpy") + var A = Matrix.rand[f64]((100, 100), order=order) + var B = Matrix.rand[f64]((100, 100), order=order) + var E = Matrix.fromstring( + "[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]", shape=(4, 3), order=order + ) + var Y = Matrix.rand((100, 1), order=order) + var Anp = A.to_numpy() + var Bnp = B.to_numpy() + var Ynp = Y.to_numpy() + var Enp = E.to_numpy() + check_matrices_close( + nm.linalg.solve(A, B), + np.linalg.solve(Anp, Bnp), + "Solve is broken", + ) + check_matrices_close( + nm.linalg.inv(A), + np.linalg.inv(Anp), + "Inverse is broken", + ) + check_matrices_close( + nm.linalg.lstsq(A, Y), + np.linalg.lstsq(Anp, Ynp)[0], + "Least square is broken", + ) + check_matrices_close( + A.transpose(), + Anp.transpose(), + "Transpose is broken", + ) + check_matrices_close( + Y.transpose(), + Ynp.transpose(), + "Transpose is broken", + ) assert_true( - np.all( - np.isclose( - nm.linalg.trace(E, offset=i), - np.trace(Enp, offset=i), - atol=0.1, - ) - ), - "Trace is broken", + np.all(np.isclose(nm.linalg.det(A), np.linalg.det(Anp), atol=0.1)), + "Determinant is broken", + ) + for i in range(-10, 10): + assert_true( + np.all( + np.isclose( + nm.linalg.trace(E, offset=i), + np.trace(Enp, offset=i), + atol=0.1, + ) + ), + "Trace is broken", + ) + + def test_qr_decomposition(): + var A = Matrix.rand[f64]((20, 20), order=order) + + var np = Python.import_module("numpy") + + var Q_R = nm.linalg.qr(A) + Q = Q_R[0].create_copy() + R = Q_R[1].create_copy() + + # Check if Q^T Q is close to the identity matrix, i.e Q is orthonormal + var id = Q.transpose() @ Q + assert_true(np.allclose(id.to_numpy(), np.eye(Q.shape[0]), atol=1e-14)) + + # Check if R is upper triangular + assert_true( + np.allclose(R.to_numpy(), np.triu(R.to_numpy()), atol=1e-14) ) + # Check if A = QR + var A_test = Q @ R + assert_true(np.allclose(A_test.to_numpy(), A.to_numpy(), atol=1e-14)) -def test_qr_decomposition(): - A = Matrix.rand[f64]((20, 20), order=order) - - var np = Python.import_module("numpy") - - var Q_R = nm.linalg.qr(A) - Q = Q_R[0].copy() - R = Q_R[1].copy() - - # Check if Q^T Q is close to the identity matrix, i.e Q is orthonormal - var id = Q.transpose() @ Q - assert_true(np.allclose(id.to_numpy(), np.eye(Q.shape[0]), atol=1e-14)) - - # Check if R is upper triangular - assert_true(np.allclose(R.to_numpy(), np.triu(R.to_numpy()), atol=1e-14)) - - # Check if A = QR - var A_test = Q @ R - assert_true(np.allclose(A_test.to_numpy(), A.to_numpy(), atol=1e-14)) - - -def test_qr_decomposition_asym_reduced(): - var np = Python.import_module("numpy") - var A = Matrix.rand[f64]((12, 5), order=order) - var Q_R = nm.linalg.qr(A, mode="reduced") - Q = Q_R[0].copy() - R = Q_R[1].copy() - - assert_true( - Q.shape[0] == 12 and Q.shape[1] == 5, - "Q has unexpected shape for reduced.", - ) - assert_true( - R.shape[0] == 5 and R.shape[1] == 5, - "R has unexpected shape for reduced.", - ) - - var id = Q.transpose() @ Q - assert_true( - np.allclose(id.to_numpy(), np.eye(Q.shape[1]), atol=1e-14), - "Q not orthonormal for reduced.", - ) - assert_true( - np.allclose(R.to_numpy(), np.triu(R.to_numpy()), atol=1e-14), - "R not upper triangular for reduced.", - ) - - var A_test = Q @ R - assert_true(np.allclose(A_test.to_numpy(), A.to_numpy(), atol=1e-14)) - - -def test_qr_decomposition_asym_complete(): - var np = Python.import_module("numpy") - var A = Matrix.rand[f64]((12, 5), order=order) - var Q_R = nm.linalg.qr(A, mode="complete") - var Q = Q_R[0].copy() - var R = Q_R[1].copy() - - assert_true( - Q.shape[0] == 12 and Q.shape[1] == 12, - "Q has unexpected shape for complete.", - ) - assert_true( - R.shape[0] == 12 and R.shape[1] == 5, - "R has unexpected shape for complete.", - ) - - var id = Q.transpose() @ Q - assert_true( - np.allclose(id.to_numpy(), np.eye(Q.shape[0]), atol=1e-14), - "Q not orthonormal for complete.", - ) - assert_true( - np.allclose(R.to_numpy(), np.triu(R.to_numpy()), atol=1e-14), - "R not upper triangular for complete.", - ) - - var A_test = Q @ R - assert_true(np.allclose(A_test.to_numpy(), A.to_numpy(), atol=1e-14)) + def test_qr_decomposition_asym_reduced(): + var np = Python.import_module("numpy") + var A = Matrix.rand[f64]((12, 5), order=order) + var Q_R = nm.linalg.qr(A, mode="reduced") + Q = Q_R[0].copy() + R = Q_R[1].copy() + assert_true( + Q.shape[0] == 12 and Q.shape[1] == 5, + "Q has unexpected shape for reduced.", + ) + assert_true( + R.shape[0] == 5 and R.shape[1] == 5, + "R has unexpected shape for reduced.", + ) -def test_qr_decomposition_asym_complete2(): - var np = Python.import_module("numpy") - var A = Matrix.rand[f64]((5, 12), order=order) - var Q_R = nm.linalg.qr(A, mode="complete") - var Q = Q_R[0].copy() - var R = Q_R[1].copy() + var id = Q.transpose() @ Q + assert_true( + np.allclose(id.to_numpy(), np.eye(Q.shape[1]), atol=1e-14), + "Q not orthonormal for reduced.", + ) + assert_true( + np.allclose(R.to_numpy(), np.triu(R.to_numpy()), atol=1e-14), + "R not upper triangular for reduced.", + ) - assert_true( - Q.shape[0] == 5 and Q.shape[1] == 5, - "Q has unexpected shape for complete.", - ) - assert_true( - R.shape[0] == 5 and R.shape[1] == 12, - "R has unexpected shape for complete.", - ) + var A_test = Q @ R + assert_true(np.allclose(A_test.to_numpy(), A.to_numpy(), atol=1e-14)) - var id = Q.transpose() @ Q - assert_true( - np.allclose(id.to_numpy(), np.eye(Q.shape[0]), atol=1e-14), - "Q not orthonormal for complete.", - ) - assert_true( - np.allclose(R.to_numpy(), np.triu(R.to_numpy()), atol=1e-14), - "R not upper triangular for complete.", - ) + def test_qr_decomposition_asym_complete(): + var np = Python.import_module("numpy") + var A = Matrix.rand[f64]((12, 5), order=order) + var Q_R = nm.linalg.qr(A, mode="complete") + var Q = Q_R[0].copy() + var R = Q_R[1].copy() - var A_test = Q @ R - assert_true(np.allclose(A_test.to_numpy(), A.to_numpy(), atol=1e-14)) + assert_true( + Q.shape[0] == 12 and Q.shape[1] == 12, + "Q has unexpected shape for complete.", + ) + assert_true( + R.shape[0] == 12 and R.shape[1] == 5, + "R has unexpected shape for complete.", + ) + var id = Q.transpose() @ Q + assert_true( + np.allclose(id.to_numpy(), np.eye(Q.shape[0]), atol=1e-14), + "Q not orthonormal for complete.", + ) + assert_true( + np.allclose(R.to_numpy(), np.triu(R.to_numpy()), atol=1e-14), + "R not upper triangular for complete.", + ) -def test_eigen_decomposition(): - var np = Python.import_module("numpy") + var A_test = Q @ R + assert_true(np.allclose(A_test.to_numpy(), A.to_numpy(), atol=1e-14)) - # Create a symmetric matrix by adding a matrix to its transpose - var A_random = Matrix.rand[f64]((10, 10), order=order) - var A = A_random + A_random.transpose() - var Anp = A.to_numpy() + def test_qr_decomposition_asym_complete2(): + var np = Python.import_module("numpy") + var A = Matrix.rand[f64]((5, 12), order=order) + var Q_R = nm.linalg.qr(A, mode="complete") + var Q = Q_R[0].copy() + var R = Q_R[1].copy() - # Compute eigendecomposition - var Q_Lambda = nm.linalg.eig(A) - var Q = Q_Lambda[0].copy() - var Lambda = Q_Lambda[1].copy() + assert_true( + Q.shape[0] == 5 and Q.shape[1] == 5, + "Q has unexpected shape for complete.", + ) + assert_true( + R.shape[0] == 5 and R.shape[1] == 12, + "R has unexpected shape for complete.", + ) - # Use NumPy for comparison - namedtuple = np.linalg.eig(Anp) + var id = Q.transpose() @ Q + assert_true( + np.allclose(id.to_numpy(), np.eye(Q.shape[0]), atol=1e-14), + "Q not orthonormal for complete.", + ) + assert_true( + np.allclose(R.to_numpy(), np.triu(R.to_numpy()), atol=1e-14), + "R not upper triangular for complete.", + ) - np_eigenvalues = namedtuple.eigenvalues - print(np_eigenvalues) - print(Lambda.to_numpy()) - print(np.diag(Lambda.to_numpy())) + var A_test = Q @ R + assert_true(np.allclose(A_test.to_numpy(), A.to_numpy(), atol=1e-14)) - # Sort eigenvalues and eigenvectors for comparison (numpy doesn't guarantee order) - var np_sorted_eigenvalues = np.sort(np_eigenvalues) - var eigenvalues = np.diag(Lambda.to_numpy()) - var sorted_eigenvalues = np.sort(eigenvalues) + def test_eigen_decomposition(): + var np = Python.import_module("numpy") - assert_true( - np.allclose(sorted_eigenvalues, np_sorted_eigenvalues, atol=1e-10), - "Eigenvalues don't match expected values", - ) + # Create a symmetric matrix by adding a matrix to its transpose + var A_random = Matrix.rand[f64]((10, 10), order=order) + var A = A_random + A_random.transpose() + var Anp = A.to_numpy() - # Check that eigenvectors are orthogonal (Q^T Q = I) - var id = Q.transpose() @ Q - assert_true( - np.allclose(id.to_numpy(), np.eye(Q.shape[0]), atol=1e-10), - "Eigenvectors are not orthogonal", - ) + # Compute eigendecomposition + var Q_Lambda = nm.linalg.eig(A) + var Q = Q_Lambda[0].copy() + var Lambda = Q_Lambda[1].copy() - # Check that A = Q * Lambda * Q^T (eigendecomposition property) - var A_reconstructed = Q @ Lambda @ Q.transpose() - print(A_reconstructed - A) - assert_true( - np.allclose(A_reconstructed.to_numpy(), Anp, atol=1e-10), - "A ≠ Q * Lambda * Q^T", - ) + # Use NumPy for comparison + namedtuple = np.linalg.eig(Anp) - # Verify A*v = λ*v for each eigenvector and eigenvalue - for i in range(A.shape[0]): - var eigenvector = Matrix.zeros[f64]((A.shape[0], 1), order=order) - for j in range(A.shape[0]): - eigenvector[j, 0] = Q[j, i] + np_eigenvalues = namedtuple.eigenvalues - var Av = A @ eigenvector - var lambda_times_v = eigenvector * Lambda[i, i] + # Sort eigenvalues and eigenvectors for comparison (numpy doesn't guarantee order) + var np_sorted_eigenvalues = np.sort(np_eigenvalues) + var eigenvalues = np.diag(Lambda.to_numpy()) + var sorted_eigenvalues = np.sort(eigenvalues) assert_true( - np.allclose(Av.to_numpy(), lambda_times_v.to_numpy(), atol=1e-10), - "Eigenvector verification failed: A*v ≠ λ*v", + np.allclose(sorted_eigenvalues, np_sorted_eigenvalues, atol=1e-10), + "Eigenvalues don't match expected values", ) - # Verify A*v = λ*v for each eigenvector and eigenvalue - for i in range(A.shape[0]): - var eigenvector = Matrix.zeros[f64]((A.shape[0], 1), order=order) - for j in range(A.shape[0]): - eigenvector[j, 0] = Q[j, i] - - var Av = A @ eigenvector - var lambda_times_v = eigenvector * Lambda[i, i] - + # Check that eigenvectors are orthogonal (Q^T Q = I) + var id = Q.transpose() @ Q assert_true( - np.allclose(Av.to_numpy(), lambda_times_v.to_numpy(), atol=1e-10), - "Eigenvector verification failed: A*v ≠ λ*v", + np.allclose(id.to_numpy(), np.eye(Q.shape[0]), atol=1e-10), + "Eigenvectors are not orthogonal", ) + # Check that A = Q * Lambda * Q^T (eigendecomposition property) + var A_reconstructed = Q @ Lambda @ Q.transpose() + assert_true( + np.allclose(A_reconstructed.to_numpy(), Anp, atol=1e-10), + "A ≠ Q * Lambda * Q^T", + ) -# ===-----------------------------------------------------------------------===# -# Mathematics -# ===-----------------------------------------------------------------------===# - + # Verify A*v = λ*v for each eigenvector and eigenvalue + for i in range(A.shape[0]): + var eigenvector = Matrix.zeros[f64]((A.shape[0], 1), order=order) + for j in range(A.shape[0]): + eigenvector[j, 0] = Q[j, i] + + var Av = A @ eigenvector + var lambda_times_v = eigenvector * Lambda[i, i] + + assert_true( + np.allclose( + Av.to_numpy(), lambda_times_v.to_numpy(), atol=1e-10 + ), + "Eigenvector verification failed: A*v ≠ λ*v", + ) + + # Verify A*v = λ*v for each eigenvector and eigenvalue + for i in range(A.shape[0]): + var eigenvector = Matrix.zeros[f64]((A.shape[0], 1), order=order) + for j in range(A.shape[0]): + eigenvector[j, 0] = Q[j, i] + + var Av = A @ eigenvector + var lambda_times_v = eigenvector * Lambda[i, i] + + assert_true( + np.allclose( + Av.to_numpy(), lambda_times_v.to_numpy(), atol=1e-10 + ), + "Eigenvector verification failed: A*v ≠ λ*v", + ) + + # ===-----------------------------------------------------------------------===# + # Mathematics + # ===-----------------------------------------------------------------------===# + + def test_math(): + var np = Python.import_module("numpy") + var A = Matrix.rand[f64]((100, 100), order=order) + var Anp = np.matrix(A.to_numpy()) -def test_math(): - var np = Python.import_module("numpy") - var A = Matrix.rand[f64]((100, 100), order=order) - var Anp = np.matrix(A.to_numpy()) - - assert_true( - np.all(np.isclose(nm.sum(A), np.sum(Anp), atol=0.1)), - "`sum` is broken", - ) - for i in range(2): - check_matrices_close( - nm.sum(A, axis=i), - np.sum(Anp, axis=i), - String("`sum` by axis {i} is broken"), + assert_true( + np.all(np.isclose(nm.sum(A), np.sum(Anp), atol=0.1)), + "`sum` is broken", ) + for i in range(2): + check_matrices_close( + nm.sum(A, axis=i), + np.sum(Anp, axis=i), + String("`sum` by axis {i} is broken"), + ) - assert_true( - np.all(np.isclose(nm.prod(A), np.prod(Anp), atol=0.1)), - "`prod` is broken", - ) - for i in range(2): - check_matrices_close( - nm.prod(A, axis=i), - np.prod(Anp, axis=i), - String("`prod` by axis {i} is broken"), + assert_true( + np.all(np.isclose(nm.prod(A), np.prod(Anp), atol=0.1)), + "`prod` is broken", ) + for i in range(2): + check_matrices_close( + nm.prod(A, axis=i), + np.prod(Anp, axis=i), + String("`prod` by axis {i} is broken"), + ) check_matrices_close( - nm.cumsum(A.copy()), + nm.cumsum(A), np.cumsum(Anp), "`cumsum` is broken", ) for i in range(2): check_matrices_close( - nm.cumsum(A.copy(), axis=i), + nm.cumsum(A, axis=i), np.cumsum(Anp, axis=i), String("`cumsum` by axis {i} is broken"), ) check_matrices_close( - nm.cumprod(A.copy()), + nm.cumprod(A), np.cumprod(Anp), "`cumprod` is broken", ) @@ -460,101 +458,107 @@ def test_math(): String("`cumprod` by axis {i} is broken"), ) - -def test_trigonometric(): - var np = Python.import_module("numpy") - var A = Matrix.rand[f64]((100, 100), order=order) - var Anp = np.matrix(A.to_numpy()) - check_matrices_close(nm.sin(A), np.sin(Anp), "sin is broken") - check_matrices_close(nm.cos(A), np.cos(Anp), "cos is broken") - check_matrices_close(nm.tan(A), np.tan(Anp), "tan is broken") - check_matrices_close(nm.arcsin(A), np.arcsin(Anp), "arcsin is broken") - check_matrices_close(nm.asin(A), np.arcsin(Anp), "asin is broken") - check_matrices_close(nm.arccos(A), np.arccos(Anp), "arccos is broken") - check_matrices_close(nm.acos(A), np.arccos(Anp), "acos is broken") - check_matrices_close(nm.arctan(A), np.arctan(Anp), "arctan is broken") - check_matrices_close(nm.atan(A), np.arctan(Anp), "atan is broken") - - -def test_hyperbolic(): - var np = Python.import_module("numpy") - var A = Matrix.fromstring( - "[[1,2,3],[4,5,6],[7,8,9]]", shape=(3, 3), order=order - ) - var B = A / 10 - var Anp = np.matrix(A.to_numpy()) - var Bnp = np.matrix(B.to_numpy()) - check_matrices_close(nm.sinh(A), np.sinh(Anp), "sinh is broken") - check_matrices_close(nm.cosh(A), np.cosh(Anp), "cosh is broken") - check_matrices_close(nm.tanh(A), np.tanh(Anp), "tanh is broken") - check_matrices_close(nm.arcsinh(A), np.arcsinh(Anp), "arcsinh is broken") - check_matrices_close(nm.asinh(A), np.arcsinh(Anp), "asinh is broken") - check_matrices_close(nm.arccosh(A), np.arccosh(Anp), "arccosh is broken") - check_matrices_close(nm.acosh(A), np.arccosh(Anp), "acosh is broken") - check_matrices_close(nm.arctanh(B), np.arctanh(Bnp), "arctanh is broken") - check_matrices_close(nm.atanh(B), np.arctanh(Bnp), "atanh is broken") - - -def test_sorting(): - var np = Python.import_module("numpy") - var A = Matrix.rand[f64]((10, 10), order=order) - var Anp = np.matrix(A.to_numpy()) - - check_matrices_close( - nm.sort(A), np.sort(Anp, axis=None), String("Sort is broken") - ) - for i in range(2): + def test_trigonometric(): + var np = Python.import_module("numpy") + var A = Matrix.rand[f64]((100, 100), order=order) + var Anp = np.matrix(A.to_numpy()) + check_matrices_close(nm.sin(A), np.sin(Anp), "sin is broken") + check_matrices_close(nm.cos(A), np.cos(Anp), "cos is broken") + check_matrices_close(nm.tan(A), np.tan(Anp), "tan is broken") + check_matrices_close(nm.arcsin(A), np.arcsin(Anp), "arcsin is broken") + check_matrices_close(nm.asin(A), np.arcsin(Anp), "asin is broken") + check_matrices_close(nm.arccos(A), np.arccos(Anp), "arccos is broken") + check_matrices_close(nm.acos(A), np.arccos(Anp), "acos is broken") + check_matrices_close(nm.arctan(A), np.arctan(Anp), "arctan is broken") + check_matrices_close(nm.atan(A), np.arctan(Anp), "atan is broken") + + def test_hyperbolic(): + var np = Python.import_module("numpy") + var A = Matrix.fromstring( + "[[1,2,3],[4,5,6],[7,8,9]]", shape=(3, 3), order=order + ) + var B = A / 10 + var Anp = np.matrix(A.to_numpy()) + var Bnp = np.matrix(B.to_numpy()) + check_matrices_close(nm.sinh(A), np.sinh(Anp), "sinh is broken") + check_matrices_close(nm.cosh(A), np.cosh(Anp), "cosh is broken") + check_matrices_close(nm.tanh(A), np.tanh(Anp), "tanh is broken") check_matrices_close( - nm.sort(A.copy(), axis=i), - np.sort(Anp, axis=i), - String("Sort by axis {} is broken").format(i), + nm.arcsinh(A), np.arcsinh(Anp), "arcsinh is broken" ) - - check_matrices_close( - nm.argsort(A), np.argsort(Anp, axis=None), String("Argsort is broken") - ) - for i in range(2): + check_matrices_close(nm.asinh(A), np.arcsinh(Anp), "asinh is broken") check_matrices_close( - nm.argsort(A.copy(), axis=i), - np.argsort(Anp, axis=i), - String("Argsort by axis {} is broken").format(i), + nm.arccosh(A), np.arccosh(Anp), "arccosh is broken" ) - - -def test_searching(): - var np = Python.import_module("numpy") - var A = Matrix.rand[f64]((10, 10), order=order) - var Anp = np.matrix(A.to_numpy()) - - check_values_close( - nm.max(A), np.max(Anp, axis=None), String("`max` is broken") - ) - for i in range(2): + check_matrices_close(nm.acosh(A), np.arccosh(Anp), "acosh is broken") check_matrices_close( - nm.max(A, axis=i), - np.max(Anp, axis=i), - String("`max` by axis {} is broken").format(i), + nm.arctanh(B), np.arctanh(Bnp), "arctanh is broken" ) + check_matrices_close(nm.atanh(B), np.arctanh(Bnp), "atanh is broken") + + def test_sorting(): + var np = Python.import_module("numpy") + var A = Matrix.rand[f64]((10, 10), order=order) + var Anp = np.matrix(A.to_numpy()) - check_values_close( - nm.argmax(A), np.argmax(Anp, axis=None), String("`argmax` is broken") - ) - for i in range(2): check_matrices_close( - nm.argmax(A, axis=i), - np.argmax(Anp, axis=i), - String("`argmax` by axis {} is broken").format(i), + nm.sort(A), np.sort(Anp, axis=None), String("Sort is broken") ) + for i in range(2): + check_matrices_close( + nm.sort(A.copy(), axis=i), + np.sort(Anp, axis=i), + String("Sort by axis {} is broken").format(i), + ) - check_values_close( - nm.min(A), np.min(Anp, axis=None), String("`min` is broken.") - ) - for i in range(2): check_matrices_close( - nm.min(A, axis=i), - np.min(Anp, axis=i), - String("`min` by axis {} is broken").format(i), + nm.argsort(A), + np.argsort(Anp, axis=None), + String("Argsort is broken"), ) + for i in range(2): + check_matrices_close( + nm.argsort(A.copy(), axis=i), + np.argsort(Anp, axis=i), + String("Argsort by axis {} is broken").format(i), + ) + + def test_searching(): + var np = Python.import_module("numpy") + var A = Matrix.rand[f64]((10, 10), order=order) + var Anp = np.matrix(A.to_numpy()) + + check_values_close( + nm.max(A), np.max(Anp, axis=None), String("`max` is broken") + ) + for i in range(2): + check_matrices_close( + nm.max(A, axis=i), + np.max(Anp, axis=i), + String("`max` by axis {} is broken").format(i), + ) + + check_values_close( + nm.argmax(A), + np.argmax(Anp, axis=None), + String("`argmax` is broken"), + ) + for i in range(2): + check_matrices_close( + nm.argmax(A, axis=i), + np.argmax(Anp, axis=i), + String("`argmax` by axis {} is broken").format(i), + ) + + check_values_close( + nm.min(A), np.min(Anp, axis=None), String("`min` is broken.") + ) + for i in range(2): + check_matrices_close( + nm.min(A, axis=i), + np.min(Anp, axis=i), + String("`min` by axis {} is broken").format(i), + ) check_values_close( nm.argmin(A), np.argmin(Anp, axis=None), String("`argmin` is broken.") @@ -565,3 +569,7 @@ def test_searching(): np.argmin(Anp, axis=i), String("`argmin` by axis {} is broken").format(i), ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/core/test_shape_strides_item.mojo b/tests/core/test_shape_strides_item.mojo index 163b65da..e96d712f 100644 --- a/tests/core/test_shape_strides_item.mojo +++ b/tests/core/test_shape_strides_item.mojo @@ -1,6 +1,7 @@ from numojo.prelude import * from testing.testing import assert_true, assert_almost_equal, assert_equal from utils_for_test import check, check_is_close +from testing import TestSuite def test_shape(): @@ -29,3 +30,7 @@ def test_item(): A[-1] == 4, msg=String("`NDArrayStrides.__getitem__()` fails: may overflow"), ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_creation.mojo b/tests/routines/test_creation.mojo index e32060ac..99e0894f 100644 --- a/tests/routines/test_creation.mojo +++ b/tests/routines/test_creation.mojo @@ -10,6 +10,7 @@ from testing.testing import ( from python import Python, PythonObject import random as builtin_random from utils_for_test import check, check_is_close +from testing import TestSuite def test_arange(): @@ -143,26 +144,28 @@ def test_eye(): ) -def test_fromstring(): - var A = nm.fromstring("[[[1,2],[3,4]],[[5,6],[7,8]]]") - var B = nm.array[DType.int32](String("[0.1, -2.3, 41.5, 19.29145, -199]")) - print(A) - print(B) +# TODO: modify these tests to use assert_equal and check function +# def test_fromstring(): +# var A = nm.fromstring("[[[1,2],[3,4]],[[5,6],[7,8]]]") +# var B = nm.array[DType.int32](String("[0.1, -2.3, 41.5, 19.29145, -199]")) +# # print(A) +# # print(B) -def test_fromstring_complicated(): - var s = """ - [[[[1,2,10], - [3,4,2]], - [[5,6,4], - [7,8,10]]], - [[[1,2,12], - [3,4,41]], - [[5,6,12], - [7,8,99]]]] - """ - var A = nm.fromstring(s) - print(A) +# TODO: modify these tests to use assert_equal and check function +# def test_fromstring_complicated(): +# var s = """ +# [[[[1,2,10], +# [3,4,2]], +# [[5,6,4], +# [7,8,10]]], +# [[[1,2,12], +# [3,4,41]], +# [[5,6,12], +# [7,8,99]]]] +# """ +# var A = nm.fromstring(s) +# print(A) def test_diag(): @@ -357,3 +360,7 @@ def test_arr_manipulation(): # assert_equal( # image == image_converted_via_array, True, "Tensor conversion is broken" # ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_functional.mojo b/tests/routines/test_functional.mojo index e6fef22b..592d9207 100644 --- a/tests/routines/test_functional.mojo +++ b/tests/routines/test_functional.mojo @@ -10,6 +10,7 @@ Test functional programming module `numojo.routines.functional`. from python import Python from testing.testing import assert_true, assert_almost_equal, assert_equal from utils_for_test import check, check_is_close +from testing import TestSuite from numojo.prelude import * @@ -36,3 +37,7 @@ fn test_apply_along_axis() raises: "`apply_along_axis` F-order array along axis {} is broken" ).format(i), ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_indexing.mojo b/tests/routines/test_indexing.mojo index 72c44e2c..0a2ea4d7 100644 --- a/tests/routines/test_indexing.mojo +++ b/tests/routines/test_indexing.mojo @@ -10,6 +10,7 @@ Test indexing module `numojo.routines.indexing`. from python import Python from testing.testing import assert_true, assert_almost_equal, assert_equal from utils_for_test import check, check_is_close +from testing import TestSuite from numojo.prelude import * @@ -298,3 +299,7 @@ fn test_take_along_axis_fortran_order() raises: " array is broken" ), ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_io.mojo b/tests/routines/test_io.mojo index 4c81167d..4b5895eb 100644 --- a/tests/routines/test_io.mojo +++ b/tests/routines/test_io.mojo @@ -2,6 +2,7 @@ from numojo.routines.io.files import load, save, loadtxt, savetxt from numojo import ones, full from python import Python import os +from testing import TestSuite fn test_save_and_load() raises: @@ -32,3 +33,7 @@ fn test_savetxt_and_loadtxt() raises: np.allclose(arr2.to_numpy(), arr.to_numpy()) # Clean up os.remove(fname) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_linalg.mojo b/tests/routines/test_linalg.mojo index 81f03f8d..3de07f31 100644 --- a/tests/routines/test_linalg.mojo +++ b/tests/routines/test_linalg.mojo @@ -2,6 +2,7 @@ import numojo as nm from numojo.prelude import * from python import Python, PythonObject from utils_for_test import check, check_is_close, check_values_close +from testing import TestSuite # ===-----------------------------------------------------------------------===# # Matmul @@ -117,3 +118,7 @@ def test_misc(): np.diagonal(np_arr, offset=i), String("`diagonal` by axis {} is broken").format(i), ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_manipulation.mojo b/tests/routines/test_manipulation.mojo index ef0716b7..fc784cc3 100644 --- a/tests/routines/test_manipulation.mojo +++ b/tests/routines/test_manipulation.mojo @@ -3,6 +3,7 @@ from numojo import * from testing.testing import assert_true, assert_almost_equal, assert_equal from utils_for_test import check, check_is_close from python import Python +from testing import TestSuite fn test_arr_manipulation() raises: @@ -137,3 +138,7 @@ def test_broadcast(): np.broadcast_to(a.to_numpy(), Python.tuple(2, 2, 2, 3)), "`broadcast_to` fails.", ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_math.mojo b/tests/routines/test_math.mojo index aa230a67..a73b62d0 100644 --- a/tests/routines/test_math.mojo +++ b/tests/routines/test_math.mojo @@ -8,6 +8,7 @@ from utils_for_test import ( check_values_close, check_with_dtype, ) +from testing import TestSuite # ===-----------------------------------------------------------------------===# # Sums, products, differences @@ -444,3 +445,7 @@ fn test_misc() raises: np.clip(cfnp, 0.02, -0.01), String("`clip` 3d f-order is broken"), ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_random.mojo b/tests/routines/test_random.mojo index 9c741000..d192e553 100644 --- a/tests/routines/test_random.mojo +++ b/tests/routines/test_random.mojo @@ -4,6 +4,7 @@ from numojo.prelude import * from python import Python, PythonObject from utils_for_test import check, check_is_close from testing.testing import assert_true, assert_almost_equal +from testing import TestSuite def test_rand(): @@ -36,8 +37,8 @@ def test_randminmax(): def test_randint(): """Test random int array generation with min and max values.""" - var arr_low_high = nm.random.randint(Shape(10, 10, 10), 0, 10) - var arr_high = nm.random.randint(Shape(10, 10, 10), 6) + var arr_low_high = nm.random.randint(Shape(30, 30, 30), 0, 10) + var arr_high = nm.random.randint(Shape(30, 30, 30), 6) var arr_low_high_mean = nm.mean(arr_low_high) var arr_high_mean = nm.mean(arr_high) assert_almost_equal( @@ -215,3 +216,7 @@ def test_rand_exponential(): arr_list._buf.ptr[i] >= 0, "Exponential distribution should only produce non-negative values", ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_searching.mojo b/tests/routines/test_searching.mojo index 54816c80..ea77c2b9 100644 --- a/tests/routines/test_searching.mojo +++ b/tests/routines/test_searching.mojo @@ -1,6 +1,7 @@ from numojo.prelude import * from python import Python, PythonObject from utils_for_test import check, check_is_close, check_values_close +from testing import TestSuite fn test_argmax() raises: @@ -223,3 +224,7 @@ fn test_take_along_axis_with_argmax_argmin() raises: np.take_along_axis(a2d_np, reshaped_min_indices_np, axis=1), "`take_along_axis` with argmin is broken", ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_sorting.mojo b/tests/routines/test_sorting.mojo index 501b8683..68b53188 100644 --- a/tests/routines/test_sorting.mojo +++ b/tests/routines/test_sorting.mojo @@ -1,6 +1,7 @@ import numojo as nm from python import Python, PythonObject from utils_for_test import check, check_is_close +from testing import TestSuite fn test_sorting() raises: @@ -125,3 +126,7 @@ fn test_sorting() raises: np.sort(S.to_numpy(), axis=i, stable=True), String("`sort` 6d stably by axis {} is broken").format(i), ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/routines/test_statistics.mojo b/tests/routines/test_statistics.mojo index 17e75845..11567091 100644 --- a/tests/routines/test_statistics.mojo +++ b/tests/routines/test_statistics.mojo @@ -4,6 +4,7 @@ from numojo.core.matrix import Matrix from python import Python, PythonObject from testing.testing import assert_raises, assert_true from utils_for_test import check, check_is_close +from testing import TestSuite # ===-----------------------------------------------------------------------===# # Statistics @@ -74,3 +75,7 @@ def test_mean_median_var_std(): np.std(Anp, axis), String("`std` is broken for axis {}").format(axis), ) + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run() diff --git a/tests/science/test_signal.mojo b/tests/science/test_signal.mojo index 46e2d496..7249fc17 100644 --- a/tests/science/test_signal.mojo +++ b/tests/science/test_signal.mojo @@ -3,6 +3,7 @@ from numojo.prelude import * from python import Python, PythonObject from utils_for_test import check, check_is_close from testing.testing import assert_raises +from testing import TestSuite def test_convolve2d(): @@ -16,3 +17,7 @@ def test_convolve2d(): res1 = nm.science.signal.convolve2d(in1, in2) res2 = sp.signal.convolve2d(npin1, npin2, mode="valid") check(res1, res2, "test_convolve2d failed #2\n") + + +def main(): + TestSuite.discover_tests[__functions_in_module()]().run()