Coverage for /usr/lib/python3/dist-packages/sympy/tensor/array/dense_ndim_array.py: 37%
93 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1import functools
2from typing import List
4from sympy.core.basic import Basic
5from sympy.core.containers import Tuple
6from sympy.core.singleton import S
7from sympy.core.sympify import _sympify
8from sympy.tensor.array.mutable_ndim_array import MutableNDimArray
9from sympy.tensor.array.ndim_array import NDimArray, ImmutableNDimArray, ArrayKind
10from sympy.utilities.iterables import flatten
13class DenseNDimArray(NDimArray):
15 _array: List[Basic]
17 def __new__(self, *args, **kwargs):
18 return ImmutableDenseNDimArray(*args, **kwargs)
20 @property
21 def kind(self) -> ArrayKind:
22 return ArrayKind._union(self._array)
24 def __getitem__(self, index):
25 """
26 Allows to get items from N-dim array.
28 Examples
29 ========
31 >>> from sympy import MutableDenseNDimArray
32 >>> a = MutableDenseNDimArray([0, 1, 2, 3], (2, 2))
33 >>> a
34 [[0, 1], [2, 3]]
35 >>> a[0, 0]
36 0
37 >>> a[1, 1]
38 3
39 >>> a[0]
40 [0, 1]
41 >>> a[1]
42 [2, 3]
45 Symbolic index:
47 >>> from sympy.abc import i, j
48 >>> a[i, j]
49 [[0, 1], [2, 3]][i, j]
51 Replace `i` and `j` to get element `(1, 1)`:
53 >>> a[i, j].subs({i: 1, j: 1})
54 3
56 """
57 syindex = self._check_symbolic_index(index)
58 if syindex is not None:
59 return syindex
61 index = self._check_index_for_getitem(index)
63 if isinstance(index, tuple) and any(isinstance(i, slice) for i in index):
64 sl_factors, eindices = self._get_slice_data_for_array_access(index)
65 array = [self._array[self._parse_index(i)] for i in eindices]
66 nshape = [len(el) for i, el in enumerate(sl_factors) if isinstance(index[i], slice)]
67 return type(self)(array, nshape)
68 else:
69 index = self._parse_index(index)
70 return self._array[index]
72 @classmethod
73 def zeros(cls, *shape):
74 list_length = functools.reduce(lambda x, y: x*y, shape, S.One)
75 return cls._new(([0]*list_length,), shape)
77 def tomatrix(self):
78 """
79 Converts MutableDenseNDimArray to Matrix. Can convert only 2-dim array, else will raise error.
81 Examples
82 ========
84 >>> from sympy import MutableDenseNDimArray
85 >>> a = MutableDenseNDimArray([1 for i in range(9)], (3, 3))
86 >>> b = a.tomatrix()
87 >>> b
88 Matrix([
89 [1, 1, 1],
90 [1, 1, 1],
91 [1, 1, 1]])
93 """
94 from sympy.matrices import Matrix
96 if self.rank() != 2:
97 raise ValueError('Dimensions must be of size of 2')
99 return Matrix(self.shape[0], self.shape[1], self._array)
101 def reshape(self, *newshape):
102 """
103 Returns MutableDenseNDimArray instance with new shape. Elements number
104 must be suitable to new shape. The only argument of method sets
105 new shape.
107 Examples
108 ========
110 >>> from sympy import MutableDenseNDimArray
111 >>> a = MutableDenseNDimArray([1, 2, 3, 4, 5, 6], (2, 3))
112 >>> a.shape
113 (2, 3)
114 >>> a
115 [[1, 2, 3], [4, 5, 6]]
116 >>> b = a.reshape(3, 2)
117 >>> b.shape
118 (3, 2)
119 >>> b
120 [[1, 2], [3, 4], [5, 6]]
122 """
123 new_total_size = functools.reduce(lambda x,y: x*y, newshape)
124 if new_total_size != self._loop_size:
125 raise ValueError('Expecting reshape size to %d but got prod(%s) = %d' % (
126 self._loop_size, str(newshape), new_total_size))
128 # there is no `.func` as this class does not subtype `Basic`:
129 return type(self)(self._array, newshape)
132class ImmutableDenseNDimArray(DenseNDimArray, ImmutableNDimArray): # type: ignore
133 def __new__(cls, iterable, shape=None, **kwargs):
134 return cls._new(iterable, shape, **kwargs)
136 @classmethod
137 def _new(cls, iterable, shape, **kwargs):
138 shape, flat_list = cls._handle_ndarray_creation_inputs(iterable, shape, **kwargs)
139 shape = Tuple(*map(_sympify, shape))
140 cls._check_special_bounds(flat_list, shape)
141 flat_list = flatten(flat_list)
142 flat_list = Tuple(*flat_list)
143 self = Basic.__new__(cls, flat_list, shape, **kwargs)
144 self._shape = shape
145 self._array = list(flat_list)
146 self._rank = len(shape)
147 self._loop_size = functools.reduce(lambda x,y: x*y, shape, 1)
148 return self
150 def __setitem__(self, index, value):
151 raise TypeError('immutable N-dim array')
153 def as_mutable(self):
154 return MutableDenseNDimArray(self)
156 def _eval_simplify(self, **kwargs):
157 from sympy.simplify.simplify import simplify
158 return self.applyfunc(simplify)
160class MutableDenseNDimArray(DenseNDimArray, MutableNDimArray):
162 def __new__(cls, iterable=None, shape=None, **kwargs):
163 return cls._new(iterable, shape, **kwargs)
165 @classmethod
166 def _new(cls, iterable, shape, **kwargs):
167 shape, flat_list = cls._handle_ndarray_creation_inputs(iterable, shape, **kwargs)
168 flat_list = flatten(flat_list)
169 self = object.__new__(cls)
170 self._shape = shape
171 self._array = list(flat_list)
172 self._rank = len(shape)
173 self._loop_size = functools.reduce(lambda x,y: x*y, shape) if shape else len(flat_list)
174 return self
176 def __setitem__(self, index, value):
177 """Allows to set items to MutableDenseNDimArray.
179 Examples
180 ========
182 >>> from sympy import MutableDenseNDimArray
183 >>> a = MutableDenseNDimArray.zeros(2, 2)
184 >>> a[0,0] = 1
185 >>> a[1,1] = 1
186 >>> a
187 [[1, 0], [0, 1]]
189 """
190 if isinstance(index, tuple) and any(isinstance(i, slice) for i in index):
191 value, eindices, slice_offsets = self._get_slice_data_for_array_assignment(index, value)
192 for i in eindices:
193 other_i = [ind - j for ind, j in zip(i, slice_offsets) if j is not None]
194 self._array[self._parse_index(i)] = value[other_i]
195 else:
196 index = self._parse_index(index)
197 self._setter_iterable_check(value)
198 value = _sympify(value)
199 self._array[index] = value
201 def as_immutable(self):
202 return ImmutableDenseNDimArray(self)
204 @property
205 def free_symbols(self):
206 return {i for j in self._array for i in j.free_symbols}