def test_fuzzy_group(): from sympy.utilities.iterables import cartes v = [T, F, U] for i in cartes(*[v]*3): assert _fuzzy_group(i) is ( None if None in i else (True if all(j for j in i) else False)) assert _fuzzy_group(i, quick_exit=True) is ( None if (i.count(False) > 1) else (None if None in i else ( True if all(j for j in i) else False)))
def test_fuzzy_group(): v = [T, F, U] for i in product(*[v]*3): assert _fuzzy_group(i) is (None if None in i else (True if all(j for j in i) else False)) assert _fuzzy_group(i, quick_exit=True) is \ (None if (i.count(False) > 1) else (None if None in i else (True if all(j for j in i) else False))) it = (True if (i == 0) else None for i in range(2)) assert _torf(it) is None it = (True if (i == 1) else None for i in range(2)) assert _torf(it) is None
def test_closed_group(expr, assumptions, key): """ Test for membership in a group with respect to the current operation """ return _fuzzy_group((ask(key(a), assumptions) for a in expr.args), quick_exit=True)
def test_closed_group(expr, assumptions, key): """ Test for membership in a group with respect to the current operation """ return _fuzzy_group( (ask(key(a), assumptions) for a in expr.args), quick_exit=True)
class BlockMatrix(MatrixExpr): @property def dtype(self): dtype = None for arg in self.args: _dtype = arg.dtype if dtype is None or dtype in _dtype: dtype = _dtype return dtype def __new__(cls, *args, **kwargs): if len(args) > 1 and isinstance(args[-1], tuple): *args, (axis, ) = args elif 'axis' in kwargs: axis = kwargs.pop('axis') else: axis = 0 _args = [] if len(args) == 1 and isinstance(args[0], (list, tuple)): args = args[0] if all(isinstance(arg, (list, tuple)) for arg in args): args = [cls(*(x.T for x in arr)).T for arr in args] from sympy import sympify args = [*map(sympify, args)] length = max(len(arg.shape) for arg in args) for arg in args: if isinstance(arg, BlockMatrix) and len( arg.shape) == length and arg.axis == axis: _args += arg.args else: _args.append(arg) if all(not arg.shape for arg in _args): return Matrix(tuple(_args)) blocks = Basic.__new__(cls, *_args, **kwargs) blocks.axis = sympify(axis) return blocks @property def kwargs(self): # return hyper parameter of this object return {'axis': self.axis} @staticmethod def broadcast(shapes): length = 0 cols = 0 for i, shape in enumerate(shapes): if not shape: shapes[i] = (1, ) shape = shapes[i] if len(shape) > 2: ... else: if shape[-1] > cols: cols = shape[-1] if len(shape) > length: length = len(shape) if length == 1 and all( shape[0] == shapes[0][0] and len(shape) == length for shape in shapes): length += 1 for i, shape in enumerate(shapes): if len(shape) > 2: ... else: if shape[-1] < cols and len(shape) > 1: shape = shape[:-1] + (cols, ) if len(shape) < length: shape = (1, ) * (length - len(shape)) + shape shapes[i] = shape return shapes def _eval_shape(self): if self.axis: shapes = [arg.shape for arg in self.args] max_length = {len(s) for s in shapes} assert len(max_length) == 1 max_length, *_ = max_length assert self.axis < max_length for axis in {*range(max_length)} - {self.axis}: if len({s[axis] for s in shapes}) > 1: print([s[axis] for s in shapes]) assert len({s[axis] for s in shapes}) == 1 shape = shapes[0] dimension_axis = 0 for s in shapes: dimension_axis += s[self.axis] return shape[:self.axis] + (dimension_axis, ) + shape[self.axis + 1:max_length] else: shapes = [arg.shape for arg in self.args] self.broadcast(shapes) rows = sum(s[0] for s in shapes) if len(shapes[0]) > 1: return (rows, *shapes[0][1:]) else: return (rows, ) @property def shape(self): if 'shape' in self._assumptions: return self._assumptions['shape'] shape = self._eval_shape() self._assumptions['shape'] = shape return shape def __getitem__(self, key): from sympy.functions.elementary.piecewise import Piecewise if isinstance(key, slice): start, stop = key.start, key.stop if start is None: start = 0 if stop is None: stop = self.shape[0] if start == 0 and stop == self.shape[0]: return self rows = 0 args = [] len_self_shape = len(self.shape) for arg in self.args: if start >= stop: break index = rows if len(arg.shape) < len_self_shape: rows += 1 else: rows += arg.shape[0] if start < rows: if len(arg.shape) < len_self_shape: args.append(arg) start += 1 elif rows <= stop: if rows - start < arg.shape[0]: args.append(arg[start:]) else: args.append(arg) start = rows else: args.append(arg[start - index:stop - index]) start = stop if len(args) == 1: return args[0] if len(args) == 0: return ZeroMatrix(*self.shape) return self.func(*args) if isinstance(key, tuple): if len(key) == 1: key = key[0] elif len(key) == 2: i, j = key if isinstance(i, slice): if isinstance(j, slice): raise Exception('unimplemented method') else: assert i.step is None, 'unimplemented slice object %s' % i start, stop = i.start, i.stop if start is None: if stop is None: # v have the same columns args = [] for v in self.args: if len(v.shape) > 1: indexed = v[:, j] else: indexed = v[j] args.append(indexed) return self.func(*args) raise Exception('unimplemented slice object %s' % i) elif isinstance(j, slice): raise Exception('unimplemented method') from sympy.core.sympify import _sympify i, j = _sympify(i), _sympify(j) if self.valid_index(i, j) != False: args = [] length = 0 for arg in self.args: _length = length shape = arg.shape length += shape[0] cond = i < length if len(arg.shape) == 1: args.append([arg[j], cond]) else: if cond.is_BooleanFalse: continue args.append([arg[i - _length, j], cond]) args[-1][-1] = True return Piecewise(*args) else: raise IndexError("Invalid indices (%s, %s)" % (i, j)) if isinstance(key, int) or key.is_Integer or key.is_Symbol or key.is_Expr: if self.axis == 0: from sympy import S rows = S.Zero args = [] for arg in self.args: index = rows if len(arg.shape) < len(self.shape): rows += S.One else: rows += arg.shape[0] cond = key < rows if cond.is_BooleanFalse: continue if len(arg.shape) < len(self.shape): args.append([arg, cond]) else: args.append([arg[key - index], cond]) args[-1][-1] = True return Piecewise(*args) else: return self.func(*(a[key] for a in self.args), axis=self.axis - 1) raise IndexError("Invalid index, wanted %s[i,j]" % self) def _eval_determinant(self): from sympy.concrete.products import Product if self.is_upper or self.is_lower: i = self.generate_var(integer=True) return Product(self[i, i], (i, 0, self.cols)).doit() @property def is_lower(self): """Check if matrix is a lower triangular matrix. True can be returned even if the matrix is not square. Examples ======== >>> from sympy import Matrix >>> m = Matrix(2, 2, [1, 0, 0, 1]) >>> m Matrix([ [1, 0], [0, 1]]) >>> m.is_lower True >>> m = Matrix(4, 3, [0, 0, 0, 2, 0, 0, 1, 4 , 0, 6, 6, 5]) >>> m Matrix([ [0, 0, 0], [2, 0, 0], [1, 4, 0], [6, 6, 5]]) >>> m.is_lower True >>> from sympy.abc import x, y >>> m = Matrix(2, 2, [x**2 + y, y**2 + x, 0, x + y]) >>> m Matrix([ [x**2 + y, x + y**2], [ 0, x + y]]) >>> m.is_lower False See Also ======== is_upper is_diagonal is_lower_hessenberg """ from sympy import Range, Min i = self.generate_var(domain=Range(0, Min(self.rows, self.cols - 1))) j = i.generate_var(free_symbols=self.free_symbols, domain=Range(i + 1, self.cols)) assert i < j return self[i, j] == 0 @property def is_upper(self): """Check if matrix is an upper triangular matrix. True can be returned even if the matrix is not square. Examples ======== >>> from sympy import Matrix >>> m = Matrix(2, 2, [1, 0, 0, 1]) >>> m Matrix([ [1, 0], [0, 1]]) >>> m.is_upper True >>> m = Matrix(4, 3, [5, 1, 9, 0, 4 , 6, 0, 0, 5, 0, 0, 0]) >>> m Matrix([ [5, 1, 9], [0, 4, 6], [0, 0, 5], [0, 0, 0]]) >>> m.is_upper True >>> m = Matrix(2, 3, [4, 2, 5, 6, 1, 1]) >>> m Matrix([ [4, 2, 5], [6, 1, 1]]) >>> m.is_upper False See Also ======== is_lower is_diagonal is_upper_hessenberg """ from sympy import Range, Min j = self.generate_var(domain=Range(0, Min(self.cols, self.rows - 1))) i = j.generate_var(free_symbols=self.free_symbols, domain=Range(j + 1, self.rows)) assert i > j return self[i, j] == 0 def __add__(self, other): if isinstance(other, BlockMatrix): if len(self.args) == len(other.args): if all(x.shape == y.shape for x, y in zip(self.args, other.args)): return self.func( *[x + y for x, y in zip(self.args, other.args)]) return MatrixExpr.__add__(self, other) def simplify(self, deep=False, **kwargs): if deep: return MatrixExpr.simplify(self, deep=deep, **kwargs) if self.axis == 0: if self.shape[0] == len(self.args): from sympy import Indexed start = None for i, arg in enumerate(self.args): if not isinstance(arg, Indexed): return self diff = arg.indices[-1] - i if start is None: start = diff else: if start != diff: return self return arg.base[start:len(self.args)] b = None start, stop = None, None for arg in self.args: if arg.is_Slice or arg.is_Indexed: if b is None: b = arg.base elif b != arg.base or len(arg.indices) > 1: b = None break if start is None: if arg.is_Slice: start, stop = arg.index else: start = arg.index stop = start + 1 else: if arg.is_Slice: _start, _stop = arg.index else: _start = arg.index _stop = _start + 1 if _start != stop: b = None break stop = _stop if b is not None: return b[start:stop] return self @property def blocks(self): cols = None blocks = [] for X in self.args: if X.is_Transpose and X.arg.is_BlockMatrix: if cols is None: cols = len(X.arg.args) else: if cols != len(X.arg.args): return blocks.append([x.T for x in X.arg.args]) continue if len(X.shape) == 1 and X.is_BlockMatrix: if cols is None: cols = len(X.args) else: if cols != len(X.args): return blocks.append([x for x in X.args]) continue return for i in range(cols): cols = None block = [block[i] for block in blocks] matrix = [b for b in block if len(b.shape) == 2] if matrix: shape = matrix[0].shape if len(shape) == 2: cols = shape[-1] else: cols = shape[-1] if any(m.shape[-1] != cols for m in matrix): return vector = [b for b in block if len(b.shape) == 1] if any(v.shape[0] != cols for v in vector): return scalar = [b for b in block if len(b.shape) == 0] if scalar: return return blocks # {c} means center, {l} means left, {r} means right def _latex(self, p): # return r'\begin{pmatrix}%s\end{pmatrix}' % r'\\'.join('{%s}' % self._print(arg) for arg in expr.args) blocks = self.blocks if blocks is not None: cols = len(blocks[0]) array = (' & '.join('{%s}' % p._print(X) for X in block) for block in blocks) return r"\left[\begin{array}{%s}%s\end{array}\right]" % ( 'c' * cols, r'\\'.join(array)) array = [] for X in self.args: if X.is_Transpose and X.arg.is_BlockMatrix: X = X.arg latex = r"{\left[\begin{array}{%s}%s\end{array}\right]}" % ( 'c' * len(X.args), ' & '.join('{%s}' % p._print(arg.T) for arg in X.args)) else: latex = '{%s}' % p._print(X) array.append(latex) if len(self.shape) == 1 or self.axis: delimiter = ' & ' center = 'c' * len(self.args) else: delimiter = r'\\' center = 'c' latex = r"\left[\begin{array}{%s}%s\end{array}\right]" % ( center, delimiter.join(array)) if self.axis: latex = "%s_%s" % (latex, p._print(self.axis)) return latex # return r"\begin{equation}\left(\begin{array}{c}%s\end{array}\right)\end{equation}" % r'\\'.join('{%s}' % self._print(arg) for arg in expr.args) def _sympystr(self, p): tex = r"[%s]" % ','.join(p._print(arg) for arg in self.args) if self.axis: tex = '%s[%s]' % (tex, self.axis) return tex def _pretty(self, p): return p._print_seq(self.args, '[', ']') def _eval_domain_defined(self, x, **_): if x.dtype.is_set: return x.universalSet domain = x.domain for arg in self.args: domain &= arg.domain_defined(x) return domain def _eval_transpose(self): blocks = self.blocks if blocks is None: if len(self.shape) == 1: return self return rows = len(blocks) cols = len(blocks[0]) blocks_T = [[None] * rows for _ in range(cols)] for i in range(rows): for j in range(cols): blocks_T[j][i] = blocks[i][j] return self.func(*[self.func(*block).T for block in blocks_T]) def __rmul__(self, other): if not other.shape: return self.func(*(other * arg for arg in self.args)) return MatrixExpr.__rmul__(self, other) _eval_is_integer = lambda self: _fuzzy_group( (a.is_integer for a in self.args), quick_exit=True) _eval_is_rational = lambda self: _fuzzy_group( (a.is_rational for a in self.args), quick_exit=True) _eval_is_extended_real = lambda self: _fuzzy_group( (a.is_extended_real for a in self.args), quick_exit=True) _eval_is_complex = lambda self: _fuzzy_group( (a.is_complex for a in self.args), quick_exit=True) _eval_is_extended_positive = lambda self: _fuzzy_group( (a.is_extended_positive for a in self.args), quick_exit=True) _eval_is_extended_negative = lambda self: _fuzzy_group( (a.is_extended_negative for a in self.args), quick_exit=True) _eval_is_finite = lambda self: _fuzzy_group( (a.is_finite for a in self.args), quick_exit=True)
class DenseMatrix(MatrixBase): __slots__ = [] is_MatrixExpr = False _op_priority = 10.01 _class_priority = 4 def __getitem__(self, key): """Return portion of self defined by key. If the key involves a slice then a list will be returned (if key is a single slice) or a matrix (if key was a tuple involving a slice). Examples ======== >>> from sympy import Matrix, I >>> m = Matrix([ ... [1, 2 + I], ... [3, 4 ]]) If the key is a tuple that doesn't involve a slice then that element is returned: >>> m[1, 0] 3 When a tuple key involves a slice, a matrix is returned. Here, the first column is selected (all rows, column 0): >>> m[:, 0] Matrix([ [1], [3]]) If the slice is not a tuple then it selects from the underlying list of elements that are arranged in row order and a list is returned if a slice is involved: >>> m[0] 1 >>> m[::2] [1, 3] """ if isinstance(key, tuple): if len(key) == 1: key = key[0] return self[key] i, j = key try: i, j = self.key2ij(key) return self._args[i * self.cols + j] except (TypeError, IndexError): if (isinstance(i, Expr) and not i.is_number) or (isinstance(j, Expr) and not j.is_number): if ((j < 0) is True) or ((j >= self.shape[1]) is True) or\ ((i < 0) is True) or ((i >= self.shape[0]) is True): raise ValueError("index out of boundary") from sympy.matrices.expressions.matexpr import MatrixElement return MatrixElement(self, i, j) if isinstance(i, slice): i = range(self.rows)[i] elif is_sequence(i): pass else: i = [i] if isinstance(j, slice): j = range(self.cols)[j] elif is_sequence(j): pass else: j = [j] return self.extract(i, j) else: # row-wise decomposition of matrix if isinstance(key, slice): return self._new(self._args[key]) if len(self.shape) == 1: from sympy.functions.elementary.piecewise import Piecewise from sympy import Equal args = [] for i in range(len(self._args)): args.append([self._args[i], Equal(key, i)]) args[-1][1] = True return Piecewise(*args).simplify() return self._args[a2idx(key)] def __setitem__(self, key, value): raise NotImplementedError() def _cholesky(self, hermitian=True): """Helper function of cholesky. Without the error checks. To be used privately. Implements the Cholesky-Banachiewicz algorithm. Returns L such that L*L.H == self if hermitian flag is True, or L*L.T == self if hermitian is False. """ L = zeros(self.rows, self.rows) if hermitian: for i in range(self.rows): for j in range(i): L[i, j] = (1 / L[j, j]) * expand_mul(self[i, j] - sum( L[i, k] * L[j, k].conjugate() for k in range(j))) Lii2 = expand_mul(self[i, i] - sum(L[i, k] * L[i, k].conjugate() for k in range(i))) if Lii2.is_positive == False: raise ValueError("Matrix must be positive-definite") L[i, i] = sqrt(Lii2) else: for i in range(self.rows): for j in range(i): L[i, j] = (1 / L[j, j]) * (self[i, j] - sum(L[i, k] * L[j, k] for k in range(j))) L[i, i] = sqrt(self[i, i] - sum(L[i, k]**2 for k in range(i))) return self._new(L) def _diagonal_solve(self, rhs): """Helper function of function diagonal_solve, without the error checks, to be used privately. """ return self._new(rhs.rows, rhs.cols, lambda i, j: rhs[i, j] / self[i, i]) def _eval_add(self, other): # we assume both arguments are dense matrices since # sparse matrices have a higher priority mat = [a + b for a, b in zip(self._args, other._args)] return classof(self, other)._new(self.rows, self.cols, mat, copy=False) def _eval_extract(self, rowsList, colsList): mat = self._args cols = self.cols indices = (i * cols + j for i in rowsList for j in colsList) return self._new(len(rowsList), len(colsList), list(mat[i] for i in indices), copy=False) def _eval_matrix_mul(self, other): from sympy import Add # cache attributes for faster access self_rows, self_cols = self.rows, self.cols other_rows, other_cols = other.rows, other.cols other_len = other_rows * other_cols new_mat_rows = self_rows if other.rows == 1: new_mat_cols = other.rows other_rows, other_cols = other_cols, other_rows else: new_mat_cols = other.cols # preallocate the array new_mat = [self.zero] * new_mat_rows * new_mat_cols # if we multiply an n x 0 with a 0 x m, the # expected behavior is to produce an n x m matrix of zeros if self_cols != 0 and other_rows != 0: # cache self._args and other._args for performance mat = self._args other_mat = other._args for i in range(len(new_mat)): row, col = i // new_mat_cols, i % new_mat_cols row_indices = range(self_cols * row, self_cols * (row + 1)) col_indices = range(col, other_len, other_cols) vec = (mat[a] * other_mat[b] for a, b in zip(row_indices, col_indices)) try: new_mat[i] = Add(*vec) except (TypeError, SympifyError): # Block matrices don't work with `sum` or `Add` (ISSUE #11599) # They don't work with `sum` because `sum` tries to add `0` # initially, and for a matrix, that is a mix of a scalar and # a matrix, which raises a TypeError. Fall back to a # block-matrix-safe way to multiply if the `sum` fails. vec = (mat[a] * other_mat[b] for a, b in zip(row_indices, col_indices)) new_mat[i] = reduce(lambda a, b: a + b, vec) return classof(self, other)._new(new_mat_rows, new_mat_cols, new_mat, copy=False) def _eval_matrix_mul_elementwise(self, other): mat = [a * b for a, b in zip(self._args, other._args)] return classof(self, other)._new(self.rows, self.cols, mat, copy=False) def _eval_inverse(self, **kwargs): """Return the matrix inverse using the method indicated (default is Gauss elimination). kwargs ====== method : ('GE', 'LU', or 'ADJ') iszerofunc try_block_diag Notes ===== According to the ``method`` keyword, it calls the appropriate method: GE .... inverse_GE(); default LU .... inverse_LU() ADJ ... inverse_ADJ() According to the ``try_block_diag`` keyword, it will try to form block diagonal matrices using the method get_diag_blocks(), invert these individually, and then reconstruct the full inverse matrix. Note, the GE and LU methods may require the matrix to be simplified before it is inverted in order to properly detect zeros during pivoting. In difficult cases a custom zero detection function can be provided by setting the ``iszerosfunc`` argument to a function that should return True if its argument is zero. The ADJ routine computes the determinant and uses that to detect singular matrices in addition to testing for zeros on the diagonal. See Also ======== inverse_LU inverse_GE inverse_ADJ """ from sympy.matrices import diag method = kwargs.get('method', 'GE') iszerofunc = kwargs.get('iszerofunc', _iszero) if kwargs.get('try_block_diag', False): blocks = self.get_diag_blocks() r = [] for block in blocks: r.append(block.inv(method=method, iszerofunc=iszerofunc)) return diag(*r) M = self.as_mutable() if method == "GE": rv = M.inverse_GE(iszerofunc=iszerofunc) elif method == "LU": rv = M.inverse_LU(iszerofunc=iszerofunc) elif method == "ADJ": rv = M.inverse_ADJ(iszerofunc=iszerofunc) else: # make sure to add an invertibility check (as in inverse_LU) # if a new method is added. raise ValueError("Inversion method unrecognized") return self._new(rv) def _eval_scalar_mul(self, other): mat = tuple(other * a for a in self._args) return self._new(self.rows, self.cols, mat, copy=False) def _eval_scalar_rmul(self, other): mat = tuple(a * other for a in self._args) return self._new(self.rows, self.cols, mat, copy=False) def _eval_tolist(self): mat = list(self._args) cols = self.cols return [mat[i * cols:(i + 1) * cols] for i in range(self.rows)] def _LDLdecomposition(self, hermitian=True): """Helper function of LDLdecomposition. Without the error checks. To be used privately. Returns L and D such that L*D*L.H == self if hermitian flag is True, or L*D*L.T == self if hermitian is False. """ # https://en.wikipedia.org/wiki/Cholesky_decomposition#LDL_decomposition_2 D = zeros(self.rows, self.rows) L = eye(self.rows) if hermitian: for i in range(self.rows): for j in range(i): L[i, j] = (1 / D[j, j]) * expand_mul(self[i, j] - sum( L[i, k] * L[j, k].conjugate() * D[k, k] for k in range(j))) D[i, i] = expand_mul(self[i, i] - sum(L[i, k] * L[i, k].conjugate() * D[k, k] for k in range(i))) if D[i, i].is_positive == False: raise ValueError("Matrix must be positive-definite") else: for i in range(self.rows): for j in range(i): L[i, j] = (1 / D[j, j]) * (self[i, j] - sum(L[i, k] * L[j, k] * D[k, k] for k in range(j))) D[i, i] = self[i, i] - sum(L[i, k]**2 * D[k, k] for k in range(i)) return self._new(L), self._new(D) def _lower_triangular_solve(self, rhs): """Helper function of function lower_triangular_solve. Without the error checks. To be used privately. """ X = zeros(self.rows, rhs.cols) for j in range(rhs.cols): for i in range(self.rows): if self[i, i] == 0: raise TypeError("Matrix must be non-singular.") X[i, j] = (rhs[i, j] - sum(self[i, k] * X[k, j] for k in range(i))) / self[i, i] return self._new(X) def _upper_triangular_solve(self, rhs): """Helper function of function upper_triangular_solve. Without the error checks, to be used privately. """ X = zeros(self.rows, rhs.cols) for j in range(rhs.cols): for i in reversed(range(self.rows)): if self[i, i] == 0: raise ValueError("Matrix must be non-singular.") X[i, j] = (rhs[i, j] - sum(self[i, k] * X[k, j] for k in range(i + 1, self.rows))) / self[i, i] return self._new(X) def as_immutable(self): """Returns an Immutable version of this Matrix """ from .immutable import ImmutableDenseMatrix as cls if self.rows and self.cols: return cls._new(self.tolist()) return cls._new(self.rows, self.cols, []) def as_mutable(self): """Returns a mutable version of this matrix Examples ======== >>> from sympy import ImmutableMatrix >>> X = ImmutableMatrix([[1, 2], [3, 4]]) >>> Y = X.as_mutable() >>> Y[1, 1] = 5 # Can set values in Y >>> Y Matrix([ [1, 2], [3, 5]]) """ return Matrix(self) def equals(self, other, failing_expression=False): """Applies ``equals`` to corresponding elements of the matrices, trying to prove that the elements are equivalent, returning True if they are, False if any pair is not, and None (or the first failing expression if failing_expression is True) if it cannot be decided if the expressions are equivalent or not. This is, in general, an expensive operation. Examples ======== >>> from sympy.matrices import Matrix >>> from sympy.abc import x >>> from sympy import cos >>> A = Matrix([x*(x - 1), 0]) >>> B = Matrix([x**2 - x, 0]) >>> A == B False >>> A.simplify() == B.simplify() True >>> A.equals(B) True >>> A.equals(2) False See Also ======== sympy.core.expr.equals """ self_shape = getattr(self, 'shape', None) other_shape = getattr(other, 'shape', None) if None in (self_shape, other_shape): return False if self_shape != other_shape: return False rv = True for i in range(self.rows): for j in range(self.cols): ans = self[i, j].equals(other[i, j], failing_expression) if ans is False: return False elif ans is not True and rv is True: rv = ans return rv @property def dtype(self): dtype = None for arg in self._args: _dtype = arg.dtype if dtype is None or dtype in _dtype: dtype = _dtype return dtype @property def domain(self): from sympy import Interval, Range, oo, CartesianSpace shape = self.shape if self.is_integer: if self.is_positive: interval = Range(1, oo) elif self.is_nonnegative: interval = Range(0, oo) elif self.is_negative: interval = Range(-oo, 0) elif self.is_nonpositive: interval = Range(-oo, 1) else: interval = Range(-oo, oo) elif self.is_extended_real: if self.is_positive: interval = Interval(0, oo, left_open=True) elif self.is_nonnegative: interval = Interval(0, oo) elif self.is_negative: interval = Interval(-oo, 0, right_open=True) elif self.is_nonpositive: interval = Interval(-oo, 0) else: interval = Interval(-oo, oo) else: interval = S.Complexes return CartesianSpace(interval, *shape) def split(self, indices): return self.slice(indices, 0, self.shape[0]) _eval_is_complex = lambda self: _fuzzy_group( (a.is_complex for a in self._args), quick_exit=True) _eval_is_finite = lambda self: _fuzzy_group( (a.is_finite for a in self._args), quick_exit=True) _eval_is_integer = lambda self: _fuzzy_group( (a.is_integer for a in self._args), quick_exit=True) _eval_is_extended_real = lambda self: _fuzzy_group( (a.is_extended_real for a in self._args), quick_exit=True) _eval_is_extended_positive = lambda self: _fuzzy_group( (a.is_extended_positive for a in self._args), quick_exit=True) _eval_is_extended_negative = lambda self: _fuzzy_group( (a.is_extended_negative for a in self._args), quick_exit=True)