import functools from typing import List from sympy.core.basic import Basic from sympy.core.containers import Tuple from sympy.core.singleton import S from sympy.core.sympify import _sympify from sympy.simplify.simplify import simplify from sympy.tensor.array.mutable_ndim_array import MutableNDimArray from sympy.tensor.array.ndim_array import NDimArray, ImmutableNDimArray, ArrayKind from sympy.utilities.iterables import flatten class DenseNDimArray(NDimArray): _array: List[Basic] def __new__(self, *args, **kwargs): return ImmutableDenseNDimArray(*args, **kwargs) @property def kind(self) -> ArrayKind: return ArrayKind._union(self._array) def __getitem__(self, index): """ Allows to get items from N-dim array. Examples ======== >>> from sympy import MutableDenseNDimArray >>> a = MutableDenseNDimArray([0, 1, 2, 3], (2, 2)) >>> a [[0, 1], [2, 3]] >>> a[0, 0] 0 >>> a[1, 1] 3 >>> a[0] [0, 1] >>> a[1] [2, 3] Symbolic index: >>> from sympy.abc import i, j >>> a[i, j] [[0, 1], [2, 3]][i, j] Replace `i` and `j` to get element `(1, 1)`: >>> a[i, j].subs({i: 1, j: 1}) 3 """ syindex = self._check_symbolic_index(index) if syindex is not None: return syindex index = self._check_index_for_getitem(index) if isinstance(index, tuple) and any(isinstance(i, slice) for i in index): sl_factors, eindices = self._get_slice_data_for_array_access(index) array = [self._array[self._parse_index(i)] for i in eindices] nshape = [len(el) for i, el in enumerate(sl_factors) if isinstance(index[i], slice)] return type(self)(array, nshape) else: index = self._parse_index(index) return self._array[index] @classmethod def zeros(cls, *shape): list_length = functools.reduce(lambda x, y: x*y, shape, S.One) return cls._new(([0]*list_length,), shape) def tomatrix(self): """ Converts MutableDenseNDimArray to Matrix. Can convert only 2-dim array, else will raise error. Examples ======== >>> from sympy import MutableDenseNDimArray >>> a = MutableDenseNDimArray([1 for i in range(9)], (3, 3)) >>> b = a.tomatrix() >>> b Matrix([ [1, 1, 1], [1, 1, 1], [1, 1, 1]]) """ from sympy.matrices import Matrix if self.rank() != 2: raise ValueError('Dimensions must be of size of 2') return Matrix(self.shape[0], self.shape[1], self._array) def reshape(self, *newshape): """ Returns MutableDenseNDimArray instance with new shape. Elements number must be suitable to new shape. The only argument of method sets new shape. Examples ======== >>> from sympy import MutableDenseNDimArray >>> a = MutableDenseNDimArray([1, 2, 3, 4, 5, 6], (2, 3)) >>> a.shape (2, 3) >>> a [[1, 2, 3], [4, 5, 6]] >>> b = a.reshape(3, 2) >>> b.shape (3, 2) >>> b [[1, 2], [3, 4], [5, 6]] """ new_total_size = functools.reduce(lambda x,y: x*y, newshape) if new_total_size != self._loop_size: raise ValueError("Invalid reshape parameters " + newshape) # there is no `.func` as this class does not subtype `Basic`: return type(self)(self._array, newshape) class ImmutableDenseNDimArray(DenseNDimArray, ImmutableNDimArray): # type: ignore """ """ def __new__(cls, iterable, shape=None, **kwargs): return cls._new(iterable, shape, **kwargs) @classmethod def _new(cls, iterable, shape, **kwargs): shape, flat_list = cls._handle_ndarray_creation_inputs(iterable, shape, **kwargs) shape = Tuple(*map(_sympify, shape)) cls._check_special_bounds(flat_list, shape) flat_list = flatten(flat_list) flat_list = Tuple(*flat_list) self = Basic.__new__(cls, flat_list, shape, **kwargs) self._shape = shape self._array = list(flat_list) self._rank = len(shape) self._loop_size = functools.reduce(lambda x,y: x*y, shape, 1) return self def __setitem__(self, index, value): raise TypeError('immutable N-dim array') def as_mutable(self): return MutableDenseNDimArray(self) def _eval_simplify(self, **kwargs): return self.applyfunc(simplify) class MutableDenseNDimArray(DenseNDimArray, MutableNDimArray): def __new__(cls, iterable=None, shape=None, **kwargs): return cls._new(iterable, shape, **kwargs) @classmethod def _new(cls, iterable, shape, **kwargs): shape, flat_list = cls._handle_ndarray_creation_inputs(iterable, shape, **kwargs) flat_list = flatten(flat_list) self = object.__new__(cls) self._shape = shape self._array = list(flat_list) self._rank = len(shape) self._loop_size = functools.reduce(lambda x,y: x*y, shape) if shape else len(flat_list) return self def __setitem__(self, index, value): """Allows to set items to MutableDenseNDimArray. Examples ======== >>> from sympy import MutableDenseNDimArray >>> a = MutableDenseNDimArray.zeros(2, 2) >>> a[0,0] = 1 >>> a[1,1] = 1 >>> a [[1, 0], [0, 1]] """ if isinstance(index, tuple) and any(isinstance(i, slice) for i in index): value, eindices, slice_offsets = self._get_slice_data_for_array_assignment(index, value) for i in eindices: other_i = [ind - j for ind, j in zip(i, slice_offsets) if j is not None] self._array[self._parse_index(i)] = value[other_i] else: index = self._parse_index(index) self._setter_iterable_check(value) value = _sympify(value) self._array[index] = value def as_immutable(self): return ImmutableDenseNDimArray(self) @property def free_symbols(self): return {i for j in self._array for i in j.free_symbols}