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)" ... ... 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
Attributes to the class are fed to the
__new__
constructor of theExpr
class and are therefore also called “arguments”. Just like in theExpr
class, 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
- argument(*, default: T = MISSING, sympify: bool = True) T [source]#
- argument(*, default_factory: Callable[[], T] = MISSING, sympify: bool = True) T
Add qualifiers to fields of
unevaluated
SymPy expression classes.Creates a
dataclasses.Field
with additional metadata forunevaluated()
by wrapping arounddataclasses.field()
.
SymPy assumptions
- ExprClass = ~ExprClass#
Type variable.
Usage:
T = TypeVar('T') # Can be anything A = TypeVar('A', str, bytes) # Must be str or bytes
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 definitions. See class Generic for more information on generic types. Generic functions work as follows:
- def repeat(x: T, n: int) -> List[T]:
‘’’Return a list containing n references to x.’’’ return [x]*n
- def longest(x: A, y: A) -> A:
‘’’Return the longest of two strings.’’’ return x if len(x) >= len(y) else y
The latter example’s signature is essentially the overloading of (str, str) -> str and (bytes, bytes) -> bytes. Also note that if the arguments are instances of some subclass of str, the return type is still plain str.
At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError.
Type variables defined with covariant=True or contravariant=True can be used to declare covariant or contravariant generic types. See PEP 484 for more details. By default generic types are invariant in all type variables.
Type variables can be introspected. e.g.:
T.__name__ == ‘T’ T.__constraints__ == () T.__covariant__ == False T.__contravariant__ = False A.__constraints__ == (str, bytes)
Note that only type variables defined in global scope can be pickled.
- class SymPyAssumptions[source]#
Bases:
TypedDict
See https://docs.sympy.org/latest/guides/assumptions.html#predicates.
- class NumPyPrintable(*args)[source]#
Bases:
Expr
Expr
class that can lambdify to NumPy code.This interface is for classes that derive from
sympy.Expr
and 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 theBoostZMatrix
class, which takes \(\beta\) as input instead of theFourMomentumSymbol
from which \(\beta\) is computed.- abstract _numpycode(printer: NumPyPrinter, *args) str [source]#
Lambdify this
NumPyPrintable
class to NumPy code.
- create_symbol_matrix(name: str, m: int, n: int) MutableDenseMatrix [source]#
Create a
Matrix
with symbols as elements.The
MatrixSymbol
has some issues when one is interested in the elements of the matrix. This function instead creates aMatrix
where the elements areIndexed
instances.To convert these
Indexed
instances to aSymbol
, usesymplot.substitute_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:
Expr
Sum 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
. Eg: IndexedBase(‘I’)[0].free_symbolsFor 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.
- 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
.>>> determine_indices(sp.Symbol("m1")) [1] >>> determine_indices(sp.Symbol("m_12")) [12] >>> determine_indices(sp.Symbol("m_a2")) [2] >>> determine_indices(sp.Symbol(R"\alpha_{i2, 5}")) [2, 5] >>> determine_indices(sp.Symbol("m")) []
Indexed
instances can also be handled: >>> m_a = sp.IndexedBase(“m_a”) >>> determine_indices(m_a[0]) [0]
- perform_cached_doit(unevaluated_expr: Expr, directory: str | None = None) Expr [source]#
Perform
doit()
cache the result to disk.The cached result is fetched from disk if the hash of the original expression is the same as the hash embedded in the filename.
- Parameters:
unevaluated_expr – A
sympy.Expr
on which to calldoit()
.directory – The directory in which to cache the result. If
None
, the cache directory will be put under the home directory.
Tip
For a faster cache, set PYTHONHASHSEED to a fixed value.
Submodules and Subpackages