Coverage for /usr/lib/python3/dist-packages/sympy/tensor/functions.py: 35%
55 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
1from collections.abc import Iterable
2from functools import singledispatch
4from sympy.core.expr import Expr
5from sympy.core.mul import Mul
6from sympy.core.singleton import S
7from sympy.core.sympify import sympify
8from sympy.core.parameters import global_parameters
11class TensorProduct(Expr):
12 """
13 Generic class for tensor products.
14 """
15 is_number = False
17 def __new__(cls, *args, **kwargs):
18 from sympy.tensor.array import NDimArray, tensorproduct, Array
19 from sympy.matrices.expressions.matexpr import MatrixExpr
20 from sympy.matrices.matrices import MatrixBase
21 from sympy.strategies import flatten
23 args = [sympify(arg) for arg in args]
24 evaluate = kwargs.get("evaluate", global_parameters.evaluate)
26 if not evaluate:
27 obj = Expr.__new__(cls, *args)
28 return obj
30 arrays = []
31 other = []
32 scalar = S.One
33 for arg in args:
34 if isinstance(arg, (Iterable, MatrixBase, NDimArray)):
35 arrays.append(Array(arg))
36 elif isinstance(arg, (MatrixExpr,)):
37 other.append(arg)
38 else:
39 scalar *= arg
41 coeff = scalar*tensorproduct(*arrays)
42 if len(other) == 0:
43 return coeff
44 if coeff != 1:
45 newargs = [coeff] + other
46 else:
47 newargs = other
48 obj = Expr.__new__(cls, *newargs, **kwargs)
49 return flatten(obj)
51 def rank(self):
52 return len(self.shape)
54 def _get_args_shapes(self):
55 from sympy.tensor.array import Array
56 return [i.shape if hasattr(i, "shape") else Array(i).shape for i in self.args]
58 @property
59 def shape(self):
60 shape_list = self._get_args_shapes()
61 return sum(shape_list, ())
63 def __getitem__(self, index):
64 index = iter(index)
65 return Mul.fromiter(
66 arg.__getitem__(tuple(next(index) for i in shp))
67 for arg, shp in zip(self.args, self._get_args_shapes())
68 )
71@singledispatch
72def shape(expr):
73 """
74 Return the shape of the *expr* as a tuple. *expr* should represent
75 suitable object such as matrix or array.
77 Parameters
78 ==========
80 expr : SymPy object having ``MatrixKind`` or ``ArrayKind``.
82 Raises
83 ======
85 NoShapeError : Raised when object with wrong kind is passed.
87 Examples
88 ========
90 This function returns the shape of any object representing matrix or array.
92 >>> from sympy import shape, Array, ImmutableDenseMatrix, Integral
93 >>> from sympy.abc import x
94 >>> A = Array([1, 2])
95 >>> shape(A)
96 (2,)
97 >>> shape(Integral(A, x))
98 (2,)
99 >>> M = ImmutableDenseMatrix([1, 2])
100 >>> shape(M)
101 (2, 1)
102 >>> shape(Integral(M, x))
103 (2, 1)
105 You can support new type by dispatching.
107 >>> from sympy import Expr
108 >>> class NewExpr(Expr):
109 ... pass
110 >>> @shape.register(NewExpr)
111 ... def _(expr):
112 ... return shape(expr.args[0])
113 >>> shape(NewExpr(M))
114 (2, 1)
116 If unsuitable expression is passed, ``NoShapeError()`` will be raised.
118 >>> shape(Integral(x, x))
119 Traceback (most recent call last):
120 ...
121 sympy.tensor.functions.NoShapeError: shape() called on non-array object: Integral(x, x)
123 Notes
124 =====
126 Array-like classes (such as ``Matrix`` or ``NDimArray``) has ``shape``
127 property which returns its shape, but it cannot be used for non-array
128 classes containing array. This function returns the shape of any
129 registered object representing array.
131 """
132 if hasattr(expr, "shape"):
133 return expr.shape
134 raise NoShapeError(
135 "%s does not have shape, or its type is not registered to shape()." % expr)
138class NoShapeError(Exception):
139 """
140 Raised when ``shape()`` is called on non-array object.
142 This error can be imported from ``sympy.tensor.functions``.
144 Examples
145 ========
147 >>> from sympy import shape
148 >>> from sympy.abc import x
149 >>> shape(x)
150 Traceback (most recent call last):
151 ...
152 sympy.tensor.functions.NoShapeError: shape() called on non-array object: x
153 """
154 pass