sympy#
import ampform.sympy
Tools that facilitate in building sympy expressions.
- @unevaluated(cls: type[ExprClass]) type[ExprClass][source]#
- @unevaluated(*, implement_doit: bool = True, **assumptions: Unpack[SymPyAssumptions]) Callable[[type[ExprClass]], type[ExprClass]]
Decorator for defining ‘unevaluated’ SymPy expressions.
Unevaluated expressions are handy for defining large expressions that consist of several sub-definitions. They are ‘unfolded’ to their definition once you call their :meth`~sympy.core.expr.Expr.doit` method. For example:
>>> @unevaluated ... class MyExpr(sp.Expr): ... x: sp.Symbol ... y: sp.Symbol ... _latex_repr_ = R"z\left({x}, {y}\right)" ... ... def evaluate(self) -> sp.Expr: ... x, y = self.args ... return x**2 + y**2 >>> a, b = sp.symbols("a b") >>> expr = MyExpr(a, b**2) >>> sp.latex(expr) 'z\\left(a, b^{2}\\right)' >>> expr.doit() a**2 + b**4
A LaTeX representation for the unevaluated state can be provided by providing an f-string or method called
_latex_repr_:>>> @unevaluated ... class Function(sp.Expr): ... x: sp.Symbol ... _latex_repr_ = R"f\left({x}\right)" # not an f-string! ... ... def evaluate(self) -> sp.Expr: ... return sp.sqrt(self.x) >>> y = sp.Symbol("y", nonnegative=True) >>> expr = Function(x=y**2) >>> sp.latex(expr) 'f\\left(y^{2}\\right)' >>> expr.doit() y
Or, as a method:
>>> from sympy.printing.latex import LatexPrinter >>> @unevaluated ... class Function(sp.Expr): ... x: sp.Symbol ... ... def evaluate(self) -> sp.Expr: ... return self.x**2 ... ... def _latex_repr_(self, printer: LatexPrinter, *args) -> str: ... x = printer._print(self.x) # important to convert to string first ... x, *_ = map(printer._print, self.args) # also possible via its args ... return Rf"g\left({x}\right)" # this is an f-string >>> expr = Function(y) >>> sp.latex(expr) 'g\\left(y\\right)'
Attributes to the class are fed to the
__new__constructor of theExprclass and are therefore also called “arguments”. Just like in theExprclass, these arguments are automatically sympified. Attributes/arguments that should not be sympified withargument():>>> class Transformation: ... def __call__(self, x: sp.Basic, y: sp.Basic) -> sp.Expr: ... >>> @unevaluated ... class MyExpr(sp.Expr): ... x: Any ... y: Any ... functor: Callable = argument(sympify=False) ... ... def evaluate(self) -> sp.Expr: ... return self.functor(self.x, self.y) >>> expr = MyExpr(0, y=3.14, functor=Transformation) >>> isinstance(expr.x, sp.Integer) True >>> isinstance(expr.y, sp.Float) True >>> expr.functor is Transformation True
Added in version 0.14.8.
Changed in version 0.14.7: Renamed from
@unevaluated_expression()to@unevaluated().`
- argument(*, default: T = MISSING, kw_only: bool = MISSING, sympify: bool = True) T[source]#
- argument(*, default_factory: Callable[[], T] = MISSING, kw_only: bool = MISSING, sympify: bool = True) T
Add qualifiers to fields of
unevaluatedSymPy expression classes.Creates a
dataclasses.Fieldwith additional metadata forunevaluated()by wrapping arounddataclasses.field().Added in version 0.14.8.
SymPy assumptions
- ExprClass = ~ExprClass#
Type variable.
The preferred way to construct a type variable is via the dedicated syntax for generic functions, classes, and type aliases:
class Sequence[T]: # T is a TypeVar ...
This syntax can also be used to create bound and constrained type variables:
# S is a TypeVar bound to str class StrSequence[S: str]: ... # A is a TypeVar constrained to str or bytes class StrOrBytesSequence[A: (str, bytes)]: ...
Type variables can also have defaults:
- class IntDefault[T = int]:
…
However, if desired, reusable type variables can also be constructed manually, like so:
Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for generic types as well as for generic function and type alias definitions.
The variance of type variables is inferred by type checkers when they are created through the type parameter syntax and when
infer_variance=Trueis passed. Manually created type variables may be explicitly marked covariant or contravariant by passingcovariant=Trueorcontravariant=True. By default, manually created type variables are invariant. See PEP 484 and PEP 695 for more details.
- class SymPyAssumptions[source]#
Bases:
TypedDictSee https://docs.sympy.org/latest/guides/assumptions.html#predicates.
- partial_doit(expr: T, types: type[Basic] | tuple[type[Basic], ...], recursive: bool = False) T[source]#
- class NumPyPrintable(*args)[source]#
Bases:
ExprExprclass that can lambdify to NumPy code.This interface is for classes that derive from
sympy.Exprand that require a_numpycode()method in case the class does not correctlylambdify()to NumPy code. For more info on SymPy printers, see Printing.Several computational frameworks try to converge their interface to that of NumPy. See for instance TensorFlow’s NumPy API and jax.numpy. This fact is used in TensorWaves to
lambdify()SymPy expressions to these different backends with the same lambdification code.Warning
If you decorate this class with
unevaluated(), you usually want to do so withimplement_doit=False, because you do not want the class to be ‘unfolded’ withdoit()before lambdification.Warning
The implemented
_numpycode()method should countain as little SymPy computations as possible. Instead, it should get most information from its constructionargs, so that SymPy can use printer tricks likecse(), prior expanding withdoit(), and other simplifications that can make the generated code shorter. An example is theBoostZMatrixclass, which takes \(\beta\) as input instead of theFourMomentumSymbolfrom which \(\beta\) is computed.- abstractmethod _numpycode(printer: NumPyPrinter, *args) str[source]#
Lambdify this
NumPyPrintableclass to NumPy code.
- create_symbol_matrix(name: str, m: int, n: int) MutableDenseMatrix[source]#
Create a
Matrixwith symbols as elements.The
MatrixSymbolhas some issues when one is interested in the elements of the matrix. This function instead creates aMatrixwhere the elements areIndexedinstances.To convert these
Indexedinstances to aSymbol, usesubstitute_indexed_symbols().>>> create_symbol_matrix("A", m=2, n=3) Matrix([ [A[0, 0], A[0, 1], A[0, 2]], [A[1, 0], A[1, 1], A[1, 2]]])
- class PoolSum(expression, *indices: tuple[Symbol, Iterable[Basic]], evaluate: bool = False, **hints)[source]#
Bases:
ExprSum over indices where the values are taken from a domain set.
>>> i, j, m, n = sp.symbols("i j m n") >>> expr = PoolSum(i**m + j**n, (i, (-1, 0, +1)), (j, (2, 4, 5))) >>> expr PoolSum(i**m + j**n, (i, (-1, 0, 1)), (j, (2, 4, 5))) >>> print(sp.latex(expr)) \sum_{i=-1}^{1} \sum_{j\in\left\{2,4,5\right\}}{i^{m} + j^{n}} >>> expr.doit() 3*(-1)**m + 3*0**m + 3*2**n + 3*4**n + 3*5**n + 3
- property free_symbols: set[Basic][source]#
Return from the atoms of self those which are free symbols.
Not all free symbols are
Symbol(see examples)For most expressions, all symbols are free symbols. For some classes this is not true. e.g. Integrals use Symbols for the dummy variables which are bound variables, so Integral has a method to return all symbols except those. Derivative keeps track of symbols with respect to which it will perform a derivative; those are bound variables, too, so it has its own free_symbols method.
Any other method that uses bound variables should implement a free_symbols method.
Examples
>>> from sympy import Derivative, Integral, IndexedBase >>> from sympy.abc import x, y, n >>> (x + 1).free_symbols {x} >>> Integral(x, y).free_symbols {x, y}
Not all free symbols are actually symbols:
>>> IndexedBase('F')[0].free_symbols {F, F[0]}
The symbols of differentiation are not included unless they appear in the expression being differentiated.
>>> Derivative(x + y, y).free_symbols {x, y} >>> Derivative(x, y).free_symbols {x} >>> Derivative(x, (y, n)).free_symbols {n, x}
If you want to know if a symbol is in the variables of the Derivative you can do so as follows:
>>> Derivative(x, y).has_free(y) True
- cleanup() Expr | PoolSum[source]#
Remove redundant summations, like indices with one or no value.
>>> x, i = sp.symbols("x i") >>> PoolSum(x**i, (i, [0, 1, 2])).cleanup().doit() x**2 + x + 1 >>> PoolSum(x, (i, [0, 1, 2])).cleanup() x >>> PoolSum(x).cleanup() x >>> PoolSum(x**i, (i, [0])).cleanup() 1
- determine_indices(symbol: Basic) list[int][source]#
Extract any indices if available from a
Symbol.Indexedinstances can also be handled: >>> m_a = sp.IndexedBase(“m_a”) >>> determine_indices(m_a[0]) [0]
- rename_symbols(expression: Expr, renames: Callable[[str], str] | dict[str, str]) Expr[source]#
Rename symbols in an expression.
>>> a, b, x = sp.symbols(R"a \beta x") >>> expr = a + b * x >>> rename_symbols(expr, renames={"a": "A", R"\beta": "B"}) A + B*x >>> rename_symbols(expr, renames=lambda s: s.replace("\\", "")) a + beta*x >>> rename_symbols(expr, renames={"non-existent": "c"}) Traceback (most recent call last): ... KeyError: "No symbol with name 'non-existent' in expression"
- class NumericalIntegral(function, limits, algorithm, configuration, dummify, *args, evaluate: bool = False, **kwargs)[source]#
Bases:
IntegralExpression class representing an integral that should be evaluated numerically.
This class inherits from
sympy.Integral, but is blocked from evaluating symbolically. Instead, it should be lambdified to a numerical integration function and evaluated numerically.See also
Added in version 0.14.10.
Changed in version 0.16.0:
Renamed from
UnevaluatableIntegraltoNumericalIntegral.The integration algorithm is configured through class constructor arguments rather than class variables.
- algorithm: str | None[source]#
Name of the numerical integration algorithm to use when lambdifying this integral.
The algorithm should be in the format
module.function, for instancescipy.integrate.quad_vec()orquadax.quadgk(). By default, the algorithm isquadax.romberg()when lambdifying to JAX andscipy.integrate.quad_vec()when lambdifying to NumPy.
- configuration: dict[str, Any] | None[source]#
Keyword arguments for the numerical integration algorithm.
For example, for
scipy.integrate.quad_vec(), one can set the relative tolerance withconfiguration={'epsrel': 1e-5}.
- dummify: bool[source]#
Replace the integration variable with a dummy symbol before lambdification.
The integrand expression is lambdified to a
lambdafunction. Therefore, when the integrand expresssion contains the integration variable in a non-trivial way, and the expression is lambdified using common sub-expressions, it is better to replace it with a uniqueDummysymbol that does not appear anywhere else in the expression tree, so that is not pulled out of thelambdafunction.
Submodules and Subpackages