Module dewret.workflow
Overarching workflow concepts.
Basic constructs for describing a workflow.
Variables
LazyFactory
RetType
StepExecution
T
Target
U
logger
Functions
is_task
def is_task(
task: 'Lazy'
) -> 'bool'
Decide whether this is a task.
Checks whether the wrapped function has the magic
attribute __step_expression__
set to True, which is
done within task creation.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
task | None | lazy-evaluated value, suspected to be a task. | None |
Returns:
Type | Description |
---|---|
None | True if task is indeed a task. |
merge_workflows
def merge_workflows(
*workflows: 'Workflow'
) -> 'Workflow'
Combine several workflows into one.
Merges a series of workflows by combining steps and tasks.
Argument: *workflows: series of workflows to combine.
Returns:
Type | Description |
---|---|
None | One workflow with all steps. |
param
def param(
name: 'str',
default: 'T'
) -> 'T'
Create a parameter.
Will cast so it looks like the original type.
Returns:
Type | Description |
---|---|
None | Parameter class cast to the type of the supplied default. |
Classes
Lazy
class Lazy(
*args,
**kwargs
)
Requirements for a lazy-evaluatable function.
Ancestors (in MRO)
- typing.Protocol
- typing.Generic
Descendants
- dewret.workflow.LazyEvaluation
LazyEvaluation
class LazyEvaluation(
fn: 'Callable[..., RetType]'
)
Tracks a single evaluation of a lazy function.
Ancestors (in MRO)
- dewret.workflow.Lazy
- typing.Protocol
- typing.Generic
Parameter
class Parameter(
name: 'str',
default: 'T'
)
Global parameter.
Independent parameter that will be used when a task is spotted reaching outside its scope. This wraps the variable it uses.
To allow for potential arithmetic operations, etc. it is a Sympy symbol.
Attributes
Name | Type | Description | Default |
---|---|---|---|
name | None | name of the parameter. | None |
default | None | captured default value from the original value. | None |
Ancestors (in MRO)
- typing.Generic
- sympy.core.symbol.Symbol
- sympy.core.expr.AtomicExpr
- sympy.core.basic.Atom
- sympy.core.expr.Expr
- sympy.logic.boolalg.Boolean
- sympy.core.basic.Basic
- sympy.printing.defaults.Printable
- sympy.core.evalf.EvalfMixin
Class variables
default_assumptions
is_Add
is_AlgebraicNumber
is_Atom
is_Boolean
is_Derivative
is_Dummy
is_Equality
is_Float
is_Function
is_Indexed
is_Integer
is_MatAdd
is_MatMul
is_Matrix
is_Mul
is_Not
is_Number
is_NumberSymbol
is_Order
is_Piecewise
is_Point
is_Poly
is_Pow
is_Rational
is_Relational
is_Symbol
is_Vector
is_Wild
is_comparable
is_number
is_scalar
is_symbol
name
Static methods
class_key
def class_key(
)
Nice order of classes.
fromiter
def fromiter(
args,
**assumptions
)
Create a new object from an iterable.
This is a convenience function that allows one to create objects from any iterable, without having to convert to a list or tuple first.
Examples
from sympy import Tuple Tuple.fromiter(i for i in range(5)) (0, 1, 2, 3, 4)
Instance variables
args
Returns a tuple of arguments of 'self'.
Examples
from sympy import cot from sympy.abc import x, y
cot(x).args (x,)
cot(x).args[0] x
(x*y).args (x, y)
(x*y).args[1] y
Notes
Never use self._args, always use self.args. Only use _args in new when creating a new function. Do not override .args() from Basic (so that it is easy to change the interface in the future if needed).
assumptions0
binary_symbols
canonical_variables
Return a dictionary mapping any variable defined in
self.bound_symbols
to Symbols that do not clash
with any free symbols in the expression.
Examples
from sympy import Lambda from sympy.abc import x Lambda(x, 2*x).canonical_variables {x: _0}
expr_free_symbols
free_symbols
func
The top-level function in an expression.
The following should hold for all objects::
>> x == x.func(*x.args)
Examples
from sympy.abc import x a = 2x a.func
a.args (2, x) a.func( a.args) 2x a == a.func(a.args) True
is_algebraic
is_antihermitian
is_commutative
is_complex
is_composite
is_even
is_extended_negative
is_extended_nonnegative
is_extended_nonpositive
is_extended_nonzero
is_extended_positive
is_extended_real
is_finite
is_hermitian
is_imaginary
is_infinite
is_integer
is_irrational
is_negative
is_noninteger
is_nonnegative
is_nonpositive
is_nonzero
is_odd
is_polar
is_positive
is_prime
is_rational
is_real
is_transcendental
is_zero
kind
Methods
adjoint
def adjoint(
self
)
apart
def apart(
self,
x=None,
**args
)
See the apart function in sympy.polys
args_cnc
def args_cnc(
self,
cset=False,
warn=True,
split_1=True
)
Return [commutative factors, non-commutative factors] of self.
Explanation
self is treated as a Mul and the ordering of the factors is maintained.
If cset
is True the commutative factors will be returned in a set.
If there were repeated factors (as may happen with an unevaluated Mul)
then an error will be raised unless it is explicitly suppressed by
setting warn
to False.
Note: -1 is always separated from a Number unless split_1 is False.
Examples
from sympy import symbols, oo A, B = symbols('A B', commutative=0) x, y = symbols('x y') (-2xy).args_cnc() [[-1, 2, x, y], []] (-2.5x).args_cnc() [[-1, 2.5, x], []] (-2xABy).args_cnc() [[-1, 2, x, y], [A, B]] (-2xABy).args_cnc(split_1=False) [[-2, x, y], [A, B]] (-2x*y).args_cnc(cset=True) [{-1, 2, x, y}, []]
The arg is always treated as a Mul:
(-2 + x + A).args_cnc() [[], [x - 2 + A]] (-oo).args_cnc() # -oo is a singleton [[-1, oo], []]
as_base_exp
def as_base_exp(
self
) -> 'tuple[Expr, Expr]'
as_coeff_Add
def as_coeff_Add(
self,
rational=False
) -> "tuple['Number', Expr]"
Efficiently extract the coefficient of a summation.
as_coeff_Mul
def as_coeff_Mul(
self,
rational: 'bool' = False
) -> "tuple['Number', Expr]"
Efficiently extract the coefficient of a product.
as_coeff_add
def as_coeff_add(
self,
*deps
) -> 'tuple[Expr, tuple[Expr, ...]]'
Return the tuple (c, args) where self is written as an Add, a
.
c should be a Rational added to any terms of the Add that are independent of deps.
args should be a tuple of all other terms of a
; args is empty
if self is a Number or if self is independent of deps (when given).
This should be used when you do not know if self is an Add or not but you want to treat self as an Add or if you want to process the individual arguments of the tail of self as an Add.
- if you know self is an Add and want only the head, use self.args[0];
- if you do not want to process the arguments of the tail but need the tail then use self.as_two_terms() which gives the head and tail.
- if you want to split self into an independent and dependent parts
use
self.as_independent(*deps)
from sympy import S from sympy.abc import x, y (S(3)).as_coeff_add() (3, ()) (3 + x).as_coeff_add() (3, (x,)) (3 + x + y).as_coeff_add(x) (y + 3, (x,)) (3 + y).as_coeff_add(x) (y + 3, ())
as_coeff_exponent
def as_coeff_exponent(
self,
x
) -> 'tuple[Expr, Expr]'
c*x**e -> c,e
where x can be any symbolic expression.
as_coeff_mul
def as_coeff_mul(
self,
*deps,
**kwargs
) -> 'tuple[Expr, tuple[Expr, ...]]'
Return the tuple (c, args) where self is written as a Mul, m
.
c should be a Rational multiplied by any factors of the Mul that are independent of deps.
args should be a tuple of all other factors of m; args is empty if self is a Number or if self is independent of deps (when given).
This should be used when you do not know if self is a Mul or not but you want to treat self as a Mul or if you want to process the individual arguments of the tail of self as a Mul.
- if you know self is a Mul and want only the head, use self.args[0];
- if you do not want to process the arguments of the tail but need the tail then use self.as_two_terms() which gives the head and tail;
- if you want to split self into an independent and dependent parts
use
self.as_independent(*deps)
from sympy import S from sympy.abc import x, y (S(3)).as_coeff_mul() (3, ()) (3xy).as_coeff_mul() (3, (x, y)) (3xy).as_coeff_mul(x) (3y, (x,)) (3y).as_coeff_mul(x) (3*y, ())
as_coefficient
def as_coefficient(
self,
expr
)
Extracts symbolic coefficient at the given expression. In
other words, this functions separates 'self' into the product of 'expr' and 'expr'-free coefficient. If such separation is not possible it will return None.
Examples
from sympy import E, pi, sin, I, Poly from sympy.abc import x
E.as_coefficient(E) 1 (2E).as_coefficient(E) 2 (2sin(E)*E).as_coefficient(E)
Two terms have E in them so a sum is returned. (If one were desiring the coefficient of the term exactly matching E then the constant from the returned expression could be selected. Or, for greater precision, a method of Poly can be used to indicate the desired term from which the coefficient is desired.)
(2E + xE).as_coefficient(E) x + 2 _.args[0] # just want the exact match 2 p = Poly(2E + xE); p Poly(xE + 2E, x, E, domain='ZZ') p.coeff_monomial(E) 2 p.nth(0, 1) 2
Since the following cannot be written as a product containing
E as a factor, None is returned. (If the coefficient 2*x
is
desired then the coeff
method should be used.)
(2Ex + x).as_coefficient(E) (2Ex + x).coeff(E) 2*x
(E*(x + 1) + x).as_coefficient(E)
(2piI).as_coefficient(piI) 2 (2I).as_coefficient(pi*I)
See Also
coeff: return sum of terms have a given factor as_coeff_Add: separate the additive constant from an expression as_coeff_Mul: separate the multiplicative constant from an expression as_independent: separate x-dependent terms/factors from others sympy.polys.polytools.Poly.coeff_monomial: efficiently find the single coefficient of a monomial in Poly sympy.polys.polytools.Poly.nth: like coeff_monomial but powers of monomial terms are used
as_coefficients_dict
def as_coefficients_dict(
self,
*syms
)
Return a dictionary mapping terms to their Rational coefficient.
Since the dictionary is a defaultdict, inquiries about terms which were not present will return a coefficient of 0.
If symbols syms
are provided, any multiplicative terms
independent of them will be considered a coefficient and a
regular dictionary of syms-dependent generators as keys and
their corresponding coefficients as values will be returned.
Examples
from sympy.abc import a, x, y (3x + ax + 4).as_coefficients_dict() {1: 4, x: 3, ax: 1} _[a] 0 (3ax).as_coefficients_dict() {ax: 3} (3ax).as_coefficients_dict(x) {x: 3a} (3ax).as_coefficients_dict(y) {1: 3a*x}
as_content_primitive
def as_content_primitive(
self,
radical=False,
clear=True
)
This method should recursively remove a Rational from all arguments
and return that (content) and the new self (primitive). The content
should always be positive and Mul(*foo.as_content_primitive()) == foo
.
The primitive need not be in canonical form and should try to preserve
the underlying structure if possible (i.e. expand_mul should not be
applied to self).
Examples
from sympy import sqrt from sympy.abc import x, y, z
eq = 2 + 2x + 2y(3 + 3y)
The as_content_primitive function is recursive and retains structure:
eq.as_content_primitive() (2, x + 3y(y + 1) + 1)
Integer powers will have Rationals extracted from the base:
((2 + 6x)2).as_content_primitive() (4, (3x + 1)2) ((2 + 6*x)(2y)).as_content_primitive() (1, (2(3x + 1))(2y))
Terms may end up joining once their as_content_primitives are added:
((5(x(1 + y)) + 2x(3 + 3y))).as_content_primitive() (11, x(y + 1)) ((3(x(1 + y)) + 2x(3 + 3y))).as_content_primitive() (9, x(y + 1)) ((3(z(1 + y)) + 2.0x(3 + 3y))).as_content_primitive() (1, 6.0x(y + 1) + 3z(y + 1)) ((5(x(1 + y)) + 2x(3 + 3y))2).as_content_primitive() (121, x2(y + 1)2) ((x(1 + y) + 0.4x(3 + 3y))2).as_content_primitive() (1, 4.84x2*(y + 1)2)
Radical content can also be factored out of the primitive:
(2sqrt(2) + 4sqrt(10)).as_content_primitive(radical=True) (2, sqrt(2)(1 + 2sqrt(5)))
If clear=False (default is True) then content will not be removed from an Add if it can be distributed to leave one or more terms with integer coefficients.
(x/2 + y).as_content_primitive() (1/2, x + 2*y) (x/2 + y).as_content_primitive(clear=False) (1, x/2 + y)
as_dummy
def as_dummy(
self
)
Return the expression with any objects having structurally
bound symbols replaced with unique, canonical symbols within the object in which they appear and having only the default assumption for commutativity being True. When applied to a symbol a new symbol having only the same commutativity will be returned.
Examples
from sympy import Integral, Symbol from sympy.abc import x r = Symbol('r', real=True) Integral(r, (r, x)).as_dummy() Integral(0, (_0, x)) .variables[0].is_real is None True r.as_dummy() _r
Notes
Any object that has structurally bound variables should have
a property, bound_symbols
that returns those symbols
appearing in the object.
as_expr
def as_expr(
self,
*gens
)
Convert a polynomial to a SymPy expression.
Examples
from sympy import sin from sympy.abc import x, y
f = (x2 + x*y).as_poly(x, y) f.as_expr() x2 + x*y
sin(x).as_expr() sin(x)
as_independent
def as_independent(
self,
*deps,
**hint
) -> 'tuple[Expr, Expr]'
A mostly naive separation of a Mul or Add into arguments that are not
are dependent on deps. To obtain as complete a separation of variables as possible, use a separation method first, e.g.:
- separatevars() to change Mul, Add and Pow (including exp) into Mul
- .expand(mul=True) to change Add or Mul into Add
- .expand(log=True) to change log expr into an Add
The only non-naive thing that is done here is to respect noncommutative
ordering of variables and to always return (0, 0) for self
of zero
regardless of hints.
For nonzero self
, the returned tuple (i, d) has the
following interpretation:
- i will has no variable that appears in deps
- d will either have terms that contain variables that are in deps, or be equal to 0 (when self is an Add) or 1 (when self is a Mul)
- if self is an Add then self = i + d
- if self is a Mul then self = i*d
- otherwise (self, S.One) or (S.One, self) is returned.
To force the expression to be treated as an Add, use the hint as_Add=True
Examples
-- self is an Add
from sympy import sin, cos, exp from sympy.abc import x, y, z
(x + xy).as_independent(x) (0, xy + x) (x + xy).as_independent(y) (x, xy) (2xsin(x) + y + x + z).as_independent(x) (y + z, 2xsin(x) + x) (2xsin(x) + y + x + z).as_independent(x, y) (z, 2xsin(x) + x + y)
-- self is a Mul
(xsin(x)cos(y)).as_independent(x) (cos(y), x*sin(x))
non-commutative terms cannot always be separated out when self is a Mul
from sympy import symbols n1, n2, n3 = symbols('n1 n2 n3', commutative=False) (n1 + n1n2).as_independent(n2) (n1, n1n2) (n2n1 + n1n2).as_independent(n2) (0, n1n2 + n2n1) (n1n2n3).as_independent(n1) (1, n1n2n3) (n1n2n3).as_independent(n2) (n1, n2n3) ((x-n1)(x-y)).as_independent(x) (1, (x - y)*(x - n1))
-- self is anything else:
(sin(x)).as_independent(x) (1, sin(x)) (sin(x)).as_independent(y) (sin(x), 1) exp(x+y).as_independent(x) (1, exp(x + y))
-- force self to be treated as an Add:
(3x).as_independent(x, as_Add=True) (0, 3x)
-- force self to be treated as a Mul:
(3+x).as_independent(x, as_Add=False) (1, x + 3) (-3+x).as_independent(x, as_Add=False) (1, x - 3)
Note how the below differs from the above in making the constant on the dep term positive.
(y*(-3+x)).as_independent(x) (y, x - 3)
-- use .as_independent() for true independence testing instead of .has(). The former considers only symbols in the free symbols while the latter considers all symbols
from sympy import Integral I = Integral(x, (x, 1, 2)) I.has(x) True x in I.free_symbols False I.as_independent(x) == (I, 1) True (I + x).as_independent(x) == (I, x) True
Note: when trying to get independent terms, a separation method might need to be used first. In this case, it is important to keep track of what you send to this routine so you know how to interpret the returned values
from sympy import separatevars, log separatevars(exp(x+y)).as_independent(x) (exp(y), exp(x)) (x + xy).as_independent(y) (x, xy) separatevars(x + xy).as_independent(y) (x, y + 1) (x(1 + y)).as_independent(y) (x, y + 1) (x(1 + y)).expand(mul=True).as_independent(y) (x, xy) a, b=symbols('a b', positive=True) (log(a*b).expand(log=True)).as_independent(b) (log(a), log(b))
See Also
separatevars expand_log sympy.core.add.Add.as_two_terms sympy.core.mul.Mul.as_two_terms as_coeff_mul
as_leading_term
def as_leading_term(
self,
*symbols,
logx=None,
cdir=0
)
Returns the leading (nonzero) term of the series expansion of self.
The _eval_as_leading_term routines are used to do this, and they must always return a non-zero value.
Examples
from sympy.abc import x (1 + x + x2).as_leading_term(x) 1 (1/x2 + x + x2).as_leading_term(x) x(-2)
as_numer_denom
def as_numer_denom(
self
)
Return the numerator and the denominator of an expression.
expression -> a/b -> a, b
This is just a stub that should be defined by an object's class methods to get anything else.
See Also
normal: return a/b
instead of (a, b)
as_ordered_factors
def as_ordered_factors(
self,
order=None
)
Return list of ordered factors (if Mul) else [self].
as_ordered_terms
def as_ordered_terms(
self,
order=None,
data=False
)
Transform an expression to an ordered list of terms.
Examples
from sympy import sin, cos from sympy.abc import x
(sin(x)2*cos(x) + sin(x)2 + 1).as_ordered_terms() [sin(x)2*cos(x), sin(x)2, 1]
as_poly
def as_poly(
self,
*gens,
**args
)
Converts self
to a polynomial or returns None
.
Explanation
from sympy import sin from sympy.abc import x, y
print((x2 + x*y).as_poly()) Poly(x2 + x*y, x, y, domain='ZZ')
print((x2 + x*y).as_poly(x, y)) Poly(x2 + x*y, x, y, domain='ZZ')
print((x**2 + sin(y)).as_poly(x, y)) None
as_powers_dict
def as_powers_dict(
self
)
Return self as a dictionary of factors with each factor being
treated as a power. The keys are the bases of the factors and the values, the corresponding exponents. The resulting dictionary should be used with caution if the expression is a Mul and contains non- commutative factors since the order that they appeared will be lost in the dictionary.
See Also
as_ordered_factors: An alternative for noncommutative applications, returning an ordered list of factors. args_cnc: Similar to as_ordered_factors, but guarantees separation of commutative and noncommutative factors.
as_real_imag
def as_real_imag(
self,
deep=True,
**hints
)
Performs complex expansion on 'self' and returns a tuple
containing collected both real and imaginary parts. This method cannot be confused with re() and im() functions, which does not perform complex expansion at evaluation.
However it is possible to expand both re() and im() functions and get exactly the same results as with a single call to this function.
from sympy import symbols, I
x, y = symbols('x,y', real=True)
(x + y*I).as_real_imag() (x, y)
from sympy.abc import z, w
(z + w*I).as_real_imag() (re(z) - im(w), re(w) + im(z))
as_set
def as_set(
self
)
Rewrites Boolean expression in terms of real sets.
Examples
from sympy import Symbol, Eq, Or, And x = Symbol('x', real=True) Eq(x, 0).as_set() {0} (x > 0).as_set() Interval.open(0, oo) And(-2 < x, x < 2).as_set() Interval.open(-2, 2) Or(x < -2, 2 < x).as_set() Union(Interval.open(-oo, -2), Interval.open(2, oo))
as_terms
def as_terms(
self
)
Transform an expression to a list of terms.
aseries
def aseries(
self,
x=None,
n=6,
bound=0,
hir=False
)
Asymptotic Series expansion of self.
This is equivalent to self.series(x, oo, n)
.
Parameters
self : Expression The expression whose series is to be expanded.
x : Symbol It is the variable of the expression to be calculated.
n : Value
The value used to represent the order in terms of x**n
,
up to which the series is to be expanded.
hir : Boolean Set this parameter to be True to produce hierarchical series. It stops the recursion at an early level and may provide nicer and more useful results.
bound : Value, Integer
Use the bound
parameter to give limit on rewriting
coefficients in its normalised form.
Examples
from sympy import sin, exp from sympy.abc import x
e = sin(1/x + exp(-x)) - sin(1/x)
e.aseries(x) (1/(24x4) - 1/(2x2) + 1 + O(x(-6), (x, oo)))*exp(-x)
e.aseries(x, n=3, hir=True) -exp(-2x)sin(1/x)/2 + exp(-x)cos(1/x) + O(exp(-3x), (x, oo))
e = exp(exp(x)/(1 - 1/x))
e.aseries(x) exp(exp(x)/(1 - 1/x))
e.aseries(x, bound=3) # doctest: +SKIP exp(exp(x)/x2)exp(exp(x)/x)exp(-exp(x) + exp(x)/(1 - 1/x) - exp(x)/x - exp(x)/x2)*exp(exp(x))
For rational expressions this method may return original expression without the Order term.
(1/x).aseries(x, n=8) 1/x
Returns
Expr Asymptotic series expansion of the expression.
Notes
This algorithm is directly induced from the limit computational algorithm provided by Gruntz. It majorly uses the mrv and rewrite sub-routines. The overall idea of this algorithm is first to look for the most rapidly varying subexpression w of a given expression f and then expands f in a series in w. Then same thing is recursively done on the leading coefficient till we get constant coefficients.
If the most rapidly varying subexpression of a given expression f is f itself, the algorithm tries to find a normalised representation of the mrv set and rewrites f using this normalised representation.
If the expansion contains an order term, it will be either O(x ** (-n))
or O(w ** (-n))
where w
belongs to the most rapidly varying expression of self
.
References
.. [1] Gruntz, Dominik. A new algorithm for computing asymptotic series. In: Proc. 1993 Int. Symp. Symbolic and Algebraic Computation. 1993. pp. 239-244. .. [2] Gruntz thesis - p90 .. [3] https://en.wikipedia.org/wiki/Asymptotic_expansion
See Also
Expr.aseries: See the docstring of this function for complete details of this wrapper.
atoms
def atoms(
self,
*types
)
Returns the atoms that form the current object.
By default, only objects that are truly atomic and cannot be divided into smaller pieces are returned: symbols, numbers, and number symbols like I and pi. It is possible to request atoms of any type, however, as demonstrated below.
Examples
from sympy import I, pi, sin from sympy.abc import x, y (1 + x + 2sin(y + Ipi)).atoms() {1, 2, I, pi, x, y}
If one or more types are given, the results will contain only those types of atoms.
from sympy import Number, NumberSymbol, Symbol (1 + x + 2sin(y + Ipi)).atoms(Symbol) {x, y}
(1 + x + 2sin(y + Ipi)).atoms(Number) {1, 2}
(1 + x + 2sin(y + Ipi)).atoms(Number, NumberSymbol) {1, 2, pi}
(1 + x + 2sin(y + Ipi)).atoms(Number, NumberSymbol, I) {1, 2, I, pi}
Note that I (imaginary unit) and zoo (complex infinity) are special types of number symbols and are not part of the NumberSymbol class.
The type can be given implicitly, too:
(1 + x + 2sin(y + Ipi)).atoms(x) # x is a Symbol {x, y}
Be careful to check your assumptions when using the implicit option
since S(1).is_Integer = True
but type(S(1))
is One
, a special type
of SymPy atom, while type(S(2))
is type Integer
and will find all
integers in an expression:
from sympy import S (1 + x + 2sin(y + Ipi)).atoms(S(1)) {1}
(1 + x + 2sin(y + Ipi)).atoms(S(2)) {1, 2}
Finally, arguments to atoms() can select more than atomic atoms: any SymPy type (loaded in core/init.py) can be listed as an argument and those types of "atoms" as found in scanning the arguments of the expression recursively:
from sympy import Function, Mul from sympy.core.function import AppliedUndef f = Function('f') (1 + f(x) + 2sin(y + Ipi)).atoms(Function) {f(x), sin(y + Ipi)} (1 + f(x) + 2sin(y + I*pi)).atoms(AppliedUndef) {f(x)}
(1 + x + 2sin(y + Ipi)).atoms(Mul) {Ipi, 2sin(y + I*pi)}
cancel
def cancel(
self,
*gens,
**args
)
See the cancel function in sympy.polys
coeff
def coeff(
self,
x,
n=1,
right=False,
_first=True
)
Returns the coefficient from the term(s) containing x**n
. If n
is zero then all terms independent of x
will be returned.
Explanation
When x
is noncommutative, the coefficient to the left (default) or
right of x
can be returned. The keyword 'right' is ignored when
x
is commutative.
Examples
from sympy import symbols from sympy.abc import x, y, z
You can select terms that have an explicit negative in front of them:
(-x + 2y).coeff(-1) x (x - 2y).coeff(-1) 2*y
You can select terms with no Rational coefficient:
(x + 2y).coeff(1) x (3 + 2x + 4x*2).coeff(1) 0
You can select terms independent of x by making n=0; in this case expr.as_independent(x)[0] is returned (and 0 will be returned instead of None):
(3 + 2x + 4x2).coeff(x, 0) 3 eq = ((x + 1)3).expand() + 1 eq x3 + 3*x2 + 3*x + 2 [eq.coeff(x, i) for i in reversed(range(4))] [1, 3, 3, 2] eq -= 2 [eq.coeff(x, i) for i in reversed(range(4))] [1, 3, 3, 0]
You can select terms that have a numerical term in front of them:
(-x - 2y).coeff(2) -y from sympy import sqrt (x + sqrt(2)x).coeff(sqrt(2)) x
The matching is exact:
(3 + 2x + 4x2).coeff(x) 2 (3 + 2x + 4x2).coeff(x2) 4 (3 + 2x + 4x2).coeff(x3) 0 (z*(x + y)2).coeff((x + y)2) z (z*(x + y)2).coeff(x + y) 0
In addition, no factoring is done, so 1 + z*(1 + y) is not obtained from the following:
(x + z(x + xy)).coeff(x) 1
If such factoring is desired, factor_terms can be used first:
from sympy import factor_terms factor_terms(x + z(x + xy)).coeff(x) z*(y + 1) + 1
n, m, o = symbols('n m o', commutative=False) n.coeff(n) 1 (3n).coeff(n) 3 (nm + mnm).coeff(n) # = (1 + m)nm 1 + m (nm + mnm).coeff(n, right=True) # = (1 + m)n*m m
If there is more than one possible coefficient 0 is returned:
(nm + mn).coeff(n) 0
If there is only one possible coefficient, it is returned:
(nm + xmn).coeff(mn) x (nm + xmn).coeff(mn, right=1) 1
See Also
as_coefficient: separate the expression into a coefficient and factor as_coeff_Add: separate the additive constant from an expression as_coeff_Mul: separate the multiplicative constant from an expression as_independent: separate x-dependent terms/factors from others sympy.polys.polytools.Poly.coeff_monomial: efficiently find the single coefficient of a monomial in Poly sympy.polys.polytools.Poly.nth: like coeff_monomial but powers of monomial terms are used
collect
def collect(
self,
syms,
func=None,
evaluate=True,
exact=False,
distribute_order_term=True
)
See the collect function in sympy.simplify
combsimp
def combsimp(
self
)
See the combsimp function in sympy.simplify
compare
def compare(
self,
other
)
Return -1, 0, 1 if the object is smaller, equal, or greater than other.
Not in the mathematical sense. If the object is of a different type from the "other" then their classes are ordered according to the sorted_classes list.
Examples
from sympy.abc import x, y x.compare(y) -1 x.compare(x) 0 y.compare(x) 1
compute_leading_term
def compute_leading_term(
self,
x,
logx=None
)
Deprecated function to compute the leading term of a series.
as_leading_term is only allowed for results of .series() This is a wrapper to compute a series first.
conjugate
def conjugate(
self
)
Returns the complex conjugate of 'self'.
copy
def copy(
self
)
could_extract_minus_sign
def could_extract_minus_sign(
self
)
Return True if self has -1 as a leading factor or has
more literal negative signs than positive signs in a sum, otherwise False.
Examples
from sympy.abc import x, y e = x - y {i.could_extract_minus_sign() for i in (e, -e)} {False, True}
Though the y - x
is considered like -(x - y)
, since it
is in a product without a leading factor of -1, the result is
false below:
(x*(y - x)).could_extract_minus_sign() False
To put something in canonical form wrt to sign, use signsimp
:
from sympy import signsimp signsimp(x(y - x)) -x(x - y) _.could_extract_minus_sign() True
count
def count(
self,
query
)
Count the number of matching subexpressions.
count_ops
def count_ops(
self,
visual=None
)
Wrapper for count_ops that returns the operation count.
diff
def diff(
self,
*symbols,
**assumptions
)
dir
def dir(
self,
x,
cdir
)
doit
def doit(
self,
**hints
)
Evaluate objects that are not evaluated by default like limits,
integrals, sums and products. All objects of this kind will be evaluated recursively, unless some species were excluded via 'hints' or unless the 'deep' hint was set to 'False'.
from sympy import Integral from sympy.abc import x
2Integral(x, x) 2Integral(x, x)
(2Integral(x, x)).doit() x*2
(2Integral(x, x)).doit(deep=False) 2Integral(x, x)
dummy_eq
def dummy_eq(
self,
other,
symbol=None
)
Compare two expressions and handle dummy symbols.
Examples
from sympy import Dummy from sympy.abc import x, y
u = Dummy('u')
(u2 + 1).dummy_eq(x2 + 1) True (u2 + 1) == (x2 + 1) False
(u2 + y).dummy_eq(x2 + y, x) True (u2 + y).dummy_eq(x2 + y, y) False
equals
def equals(
self,
other,
failing_expression=False
)
Return True if self == other, False if it does not, or None. If
failing_expression is True then the expression which did not simplify to a 0 will be returned instead of None.
Explanation
If self
is a Number (or complex number) that is not zero, then
the result is False.
If self
is a number and has not evaluated to zero, evalf will be
used to test whether the expression evaluates to zero. If it does so
and the result has significance (i.e. the precision is either -1, for
a Rational result, or is greater than 1) then the evalf value will be
used to return True or False.
evalf
def evalf(
self,
n=15,
subs=None,
maxn=100,
chop=False,
strict=False,
quad=None,
verbose=False
)
Evaluate the given formula to an accuracy of n digits.
Parameters
subs : dict, optional
Substitute numerical values for symbols, e.g.
subs={x:3, y:1+pi}
. The substitutions must be given as a
dictionary.
maxn : int, optional Allow a maximum temporary working precision of maxn digits.
chop : bool or number, optional Specifies how to replace tiny real or imaginary parts in subresults by exact zeros.
When ``True`` the chop value defaults to standard precision.
Otherwise the chop value is used to determine the
magnitude of "small" for purposes of chopping.
>>> from sympy import N
>>> x = 1e-4
>>> N(x, chop=True)
0.000100000000000000
>>> N(x, chop=1e-5)
0.000100000000000000
>>> N(x, chop=1e-4)
0
strict : bool, optional
Raise PrecisionExhausted
if any subresult fails to
evaluate to full accuracy, given the available maxprec.
quad : str, optional
Choose algorithm for numerical quadrature. By default,
tanh-sinh quadrature is used. For oscillatory
integrals on an infinite interval, try quad='osc'
.
verbose : bool, optional Print debug information.
Notes
When Floats are naively substituted into an expression, precision errors may adversely affect the result. For example, adding 1e16 (a Float) to 1 will truncate to 1e16; if 1e16 is then subtracted, the result will be 0. That is exactly what happens in the following:
from sympy.abc import x, y, z values = {x: 1e16, y: 1, z: 1e16} (x + y - z).subs(values) 0
Using the subs argument for evalf is the accurate way to evaluate such an expression:
(x + y - z).evalf(subs=values) 1.00000000000000
expand
def expand(
self,
deep=True,
modulus=None,
power_base=True,
power_exp=True,
mul=True,
log=True,
multinomial=True,
basic=True,
**hints
)
Expand an expression using hints.
See the docstring of the expand() function in sympy.core.function for more information.
extract_additively
def extract_additively(
self,
c
)
Return self - c if it's possible to subtract c from self and
make all matching coefficients move towards zero, else return None.
Examples
from sympy.abc import x, y e = 2x + 3 e.extract_additively(x + 1) x + 2 e.extract_additively(3x) e.extract_additively(4) (y(x + 1)).extract_additively(x + 1) ((x + 1)(x + 2y + 1) + 3).extract_additively(x + 1) (x + 1)(x + 2*y) + 3
See Also
extract_multiplicatively coeff as_coefficient
extract_branch_factor
def extract_branch_factor(
self,
allow_half=False
)
Try to write self as exp_polar(2*pi*I*n)*z
in a nice way.
Return (z, n).
from sympy import exp_polar, I, pi from sympy.abc import x, y exp_polar(Ipi).extract_branch_factor() (exp_polar(Ipi), 0) exp_polar(2Ipi).extract_branch_factor() (1, 1) exp_polar(-piI).extract_branch_factor() (exp_polar(Ipi), -1) exp_polar(3piI + x).extract_branch_factor() (exp_polar(x + Ipi), 1) (yexp_polar(-5piI)exp_polar(3piI + 2pix)).extract_branch_factor() (yexp_polar(2pix), -1) exp_polar(-Ipi/2).extract_branch_factor() (exp_polar(-Ipi/2), 0)
If allow_half is True, also extract exp_polar(I*pi):
exp_polar(Ipi).extract_branch_factor(allow_half=True) (1, 1/2) exp_polar(2Ipi).extract_branch_factor(allow_half=True) (1, 1) exp_polar(3Ipi).extract_branch_factor(allow_half=True) (1, 3/2) exp_polar(-Ipi).extract_branch_factor(allow_half=True) (1, -1/2)
extract_multiplicatively
def extract_multiplicatively(
self,
c
)
Return None if it's not possible to make self in the form
c * something in a nice way, i.e. preserving the properties of arguments of self.
Examples
from sympy import symbols, Rational
x, y = symbols('x,y', real=True)
((xy)3).extract_multiplicatively(x2 * y) xy**2
((xy)3).extract_multiplicatively(x*4 * y)
(2*x).extract_multiplicatively(2) x
(2*x).extract_multiplicatively(3)
(Rational(1, 2)*x).extract_multiplicatively(3) x/6
factor
def factor(
self,
*gens,
**args
)
See the factor() function in sympy.polys.polytools
find
def find(
self,
query,
group=False
)
Find all subexpressions matching a query.
fourier_series
def fourier_series(
self,
limits=None
)
Compute fourier sine/cosine series of self.
See the docstring of the :func:fourier_series
in sympy.series.fourier
for more information.
fps
def fps(
self,
x=None,
x0=0,
dir=1,
hyper=True,
order=4,
rational=True,
full=False
)
Compute formal power power series of self.
See the docstring of the :func:fps
function in sympy.series.formal for
more information.
gammasimp
def gammasimp(
self
)
See the gammasimp function in sympy.simplify
getO
def getO(
self
)
Returns the additive O(..) symbol if there is one, else None.
getn
def getn(
self
)
Returns the order of the expression.
Explanation
The order is determined either from the O(...) term. If there is no O(...) term, it returns None.
Examples
from sympy import O from sympy.abc import x (1 + x + O(x**2)).getn() 2 (1 + x).getn()
has
def has(
self,
*patterns
)
Test whether any subexpression matches any of the patterns.
Examples
from sympy import sin from sympy.abc import x, y, z (x2 + sin(x*y)).has(z) False (x2 + sin(x*y)).has(x, y, z) True x.has(x) True
Note has
is a structural algorithm with no knowledge of
mathematics. Consider the following half-open interval:
from sympy import Interval i = Interval.Lopen(0, 5); i Interval.Lopen(0, 5) i.args (0, 5, True, False) i.has(4) # there is no "4" in the arguments False i.has(0) # there is a "0" in the arguments True
Instead, use contains
to determine whether a number is in the
interval or not:
i.contains(4) True i.contains(0) False
Note that expr.has(*patterns)
is exactly equivalent to
any(expr.has(p) for p in patterns)
. In particular, False
is
returned when the list of patterns is empty.
x.has() False
has_free
def has_free(
self,
*patterns
)
Return True if self has object(s) x
as a free expression
else False.
Examples
from sympy import Integral, Function from sympy.abc import x, y f = Function('f') g = Function('g') expr = Integral(f(x), (f(x), 1, g(y))) expr.free_symbols {y} expr.has_free(g(y)) True expr.has_free(*(x, f(x))) False
This works for subexpressions and types, too:
expr.has_free(g) True (x + y + 1).has_free(y + 1) True
has_xfree
def has_xfree(
self,
s: 'set[Basic]'
)
Return True if self has any of the patterns in s as a
free argument, else False. This is like Basic.has_free
but this will only report exact argument matches.
Examples
from sympy import Function from sympy.abc import x, y f = Function('f') f(x).has_xfree({f}) False f(x).has_xfree({f(x)}) True f(x + 1).has_xfree({x}) True f(x + 1).has_xfree({x + 1}) True f(x + y + 1).has_xfree({x + 1}) False
integrate
def integrate(
self,
*args,
**kwargs
)
See the integrate function in sympy.integrals
invert
def invert(
self,
g,
*gens,
**args
)
Return the multiplicative inverse of self
mod g
where self
(and g
) may be symbolic expressions).
See Also
sympy.core.numbers.mod_inverse, sympy.polys.polytools.invert
is_algebraic_expr
def is_algebraic_expr(
self,
*syms
)
This tests whether a given expression is algebraic or not, in the
given symbols, syms. When syms is not given, all free symbols will be used. The rational function does not have to be in expanded or in any kind of canonical form.
This function returns False for expressions that are "algebraic expressions" with symbolic exponents. This is a simple extension to the is_rational_function, including rational exponentiation.
Examples
from sympy import Symbol, sqrt x = Symbol('x', real=True) sqrt(1 + x).is_rational_function() False sqrt(1 + x).is_algebraic_expr() True
This function does not attempt any nontrivial simplifications that may result in an expression that does not appear to be an algebraic expression to become one.
from sympy import exp, factor a = sqrt(exp(x)*2 + 2exp(x) + 1)/(exp(x) + 1) a.is_algebraic_expr(x) False factor(a).is_algebraic_expr() True
See Also
is_rational_function
References
.. [1] https://en.wikipedia.org/wiki/Algebraic_expression
is_constant
def is_constant(
self,
*wrt,
**flags
)
Return True if self is constant, False if not, or None if
the constancy could not be determined conclusively.
Explanation
If an expression has no free symbols then it is a constant. If there are free symbols it is possible that the expression is a constant, perhaps (but not necessarily) zero. To test such expressions, a few strategies are tried:
1) numerical evaluation at two random points. If two such evaluations
give two different values and the values have a precision greater than
1 then self is not constant. If the evaluations agree or could not be
obtained with any precision, no decision is made. The numerical testing
is done only if wrt
is different than the free symbols.
2) differentiation with respect to variables in 'wrt' (or all free symbols if omitted) to see if the expression is constant or not. This will not always lead to an expression that is zero even though an expression is constant (see added test in test_expr.py). If all derivatives are zero then self is constant with respect to the given symbols.
3) finding out zeros of denominator expression with free_symbols. It will not be constant if there are zeros. It gives more negative answers for expression that are not constant.
If neither evaluation nor differentiation can prove the expression is
constant, None is returned unless two numerical values happened to be
the same and the flag failing_number
is True -- in that case the
numerical value will be returned.
If flag simplify=False is passed, self will not be simplified; the default is True since self should be simplified before testing.
Examples
from sympy import cos, sin, Sum, S, pi from sympy.abc import a, n, x, y x.is_constant() False S(2).is_constant() True Sum(x, (x, 1, 10)).is_constant() True Sum(x, (x, 1, n)).is_constant() False Sum(x, (x, 1, n)).is_constant(y) True Sum(x, (x, 1, n)).is_constant(n) False Sum(x, (x, 1, n)).is_constant(x) True eq = acos(x)2 + asin(x)**2 - a eq.is_constant() True eq.subs({x: pi, a: 2}) == eq.subs({x: pi, a: 3}) == 0 True
(0x).is_constant() False x.is_constant() False (xx).is_constant() False one = cos(x)2 + sin(x)2 one.is_constant() True ((one - 1)**(x + 1)).is_constant() in (True, False) # could be 0 or 1 True
is_hypergeometric
def is_hypergeometric(
self,
k
)
is_meromorphic
def is_meromorphic(
self,
x,
a
)
This tests whether an expression is meromorphic as
a function of the given symbol x
at the point a
.
This method is intended as a quick test that will return None if no decision can be made without simplification or more detailed analysis.
Examples
from sympy import zoo, log, sin, sqrt from sympy.abc import x
f = 1/x2 + 1 - 2*x3 f.is_meromorphic(x, 0) True f.is_meromorphic(x, 1) True f.is_meromorphic(x, zoo) True
g = x**log(3) g.is_meromorphic(x, 0) False g.is_meromorphic(x, 1) True g.is_meromorphic(x, zoo) False
h = sin(1/x)x*2 h.is_meromorphic(x, 0) False h.is_meromorphic(x, 1) True h.is_meromorphic(x, zoo) True
Multivalued functions are considered meromorphic when their branches are meromorphic. Thus most functions are meromorphic everywhere except at essential singularities and branch points. In particular, they will be meromorphic also on branch cuts except at their endpoints.
log(x).is_meromorphic(x, -1) True log(x).is_meromorphic(x, 0) False sqrt(x).is_meromorphic(x, -1) True sqrt(x).is_meromorphic(x, 0) False
is_polynomial
def is_polynomial(
self,
*syms
)
Return True if self is a polynomial in syms and False otherwise.
This checks if self is an exact polynomial in syms. This function returns False for expressions that are "polynomials" with symbolic exponents. Thus, you should be able to apply polynomial algorithms to expressions for which this returns True, and Poly(expr, *syms) should work if and only if expr.is_polynomial(*syms) returns True. The polynomial does not have to be in expanded form. If no symbols are given, all free symbols in the expression will be used.
This is not part of the assumptions system. You cannot do Symbol('z', polynomial=True).
Examples
from sympy import Symbol, Function x = Symbol('x') ((x2 + 1)4).is_polynomial(x) True ((x2 + 1)4).is_polynomial() True (2x + 1).is_polynomial(x) False (2x + 1).is_polynomial(2**x) True f = Function('f') (f(x) + 1).is_polynomial(x) False (f(x) + 1).is_polynomial(f(x)) True (1/f(x) + 1).is_polynomial(f(x)) False
n = Symbol('n', nonnegative=True, integer=True) (x**n + 1).is_polynomial(x) False
This function does not attempt any nontrivial simplifications that may result in an expression that does not appear to be a polynomial to become one.
from sympy import sqrt, factor, cancel y = Symbol('y', positive=True) a = sqrt(y*2 + 2y + 1) a.is_polynomial(y) False factor(a) y + 1 factor(a).is_polynomial(y) True
b = (y*2 + 2y + 1)/(y + 1) b.is_polynomial(y) False cancel(b) y + 1 cancel(b).is_polynomial(y) True
See also .is_rational_function()
is_rational_function
def is_rational_function(
self,
*syms
)
Test whether function is a ratio of two polynomials in the given
symbols, syms. When syms is not given, all free symbols will be used. The rational function does not have to be in expanded or in any kind of canonical form.
This function returns False for expressions that are "rational functions" with symbolic exponents. Thus, you should be able to call .as_numer_denom() and apply polynomial algorithms to the result for expressions for which this returns True.
This is not part of the assumptions system. You cannot do Symbol('z', rational_function=True).
Examples
from sympy import Symbol, sin from sympy.abc import x, y
(x/y).is_rational_function() True
(x**2).is_rational_function() True
(x/sin(y)).is_rational_function(y) False
n = Symbol('n', integer=True) (x**n + 1).is_rational_function(x) False
This function does not attempt any nontrivial simplifications that may result in an expression that does not appear to be a rational function to become one.
from sympy import sqrt, factor y = Symbol('y', positive=True) a = sqrt(y*2 + 2y + 1)/y a.is_rational_function(y) False factor(a) (y + 1)/y factor(a).is_rational_function(y) True
See also is_algebraic_expr().
leadterm
def leadterm(
self,
x,
logx=None,
cdir=0
)
Returns the leading term ax*b as a tuple (a, b).
Examples
from sympy.abc import x (1+x+x2).leadterm(x) (1, 0) (1/x2+x+x**2).leadterm(x) (1, -2)
limit
def limit(
self,
x,
xlim,
dir='+'
)
Compute limit x->xlim.
lseries
def lseries(
self,
x=None,
x0=0,
dir='+',
logx=None,
cdir=0
)
Wrapper for series yielding an iterator of the terms of the series.
Note: an infinite series will yield an infinite iterator. The following, for exaxmple, will never terminate. It will just keep printing terms of the sin(x) series::
for term in sin(x).lseries(x): print term
The advantage of lseries() over nseries() is that many times you are just interested in the next term in the series (i.e. the first term for example), but you do not know how many you should ask for in nseries() using the "n" parameter.
See also nseries().
match
def match(
self,
pattern,
old=False
)
Pattern matching.
Wild symbols match all.
Return None
when expression (self) does not match
with pattern. Otherwise return a dictionary such that::
pattern.xreplace(self.match(pattern)) == self
Examples
from sympy import Wild, Sum from sympy.abc import x, y p = Wild("p") q = Wild("q") r = Wild("r") e = (x+y)(x+y) e.match(pp) {p_: x + y} e.match(pq) {p_: x + y, q_: x + y} e = (2*x)2 e.match(pqr) {p_: 4, q_: x, r_: 2} (pqr).xreplace(e.match(p*qr)) 4x*2
Structurally bound symbols are ignored during matching:
Sum(x, (x, 1, 2)).match(Sum(y, (y, 1, p))) {p_: 2}
But they can be identified if desired:
Sum(x, (x, 1, 2)).match(Sum(q, (q, 1, p))) {p_: 2, q_: x}
The old
flag will give the old-style pattern matching where
expressions and patterns are essentially solved to give the
match. Both of the following give None unless old=True
:
(x - 2).match(p - x, old=True) {p_: 2x - 2} (2/x).match(px, old=True) {p_: 2/x**2}
matches
def matches(
self,
expr,
repl_dict=None,
old=False
)
Helper method for match() that looks for a match between Wild symbols
in self and expressions in expr.
Examples
from sympy import symbols, Wild, Basic a, b, c = symbols('a b c') x = Wild('x') Basic(a + x, x).matches(Basic(a + b, c)) is None True Basic(a + x, x).matches(Basic(a + b + c, b + c)) {x_: b + c}
n
def n(
self,
n=15,
subs=None,
maxn=100,
chop=False,
strict=False,
quad=None,
verbose=False
)
Evaluate the given formula to an accuracy of n digits.
Parameters
subs : dict, optional
Substitute numerical values for symbols, e.g.
subs={x:3, y:1+pi}
. The substitutions must be given as a
dictionary.
maxn : int, optional Allow a maximum temporary working precision of maxn digits.
chop : bool or number, optional Specifies how to replace tiny real or imaginary parts in subresults by exact zeros.
When ``True`` the chop value defaults to standard precision.
Otherwise the chop value is used to determine the
magnitude of "small" for purposes of chopping.
>>> from sympy import N
>>> x = 1e-4
>>> N(x, chop=True)
0.000100000000000000
>>> N(x, chop=1e-5)
0.000100000000000000
>>> N(x, chop=1e-4)
0
strict : bool, optional
Raise PrecisionExhausted
if any subresult fails to
evaluate to full accuracy, given the available maxprec.
quad : str, optional
Choose algorithm for numerical quadrature. By default,
tanh-sinh quadrature is used. For oscillatory
integrals on an infinite interval, try quad='osc'
.
verbose : bool, optional Print debug information.
Notes
When Floats are naively substituted into an expression, precision errors may adversely affect the result. For example, adding 1e16 (a Float) to 1 will truncate to 1e16; if 1e16 is then subtracted, the result will be 0. That is exactly what happens in the following:
from sympy.abc import x, y, z values = {x: 1e16, y: 1, z: 1e16} (x + y - z).subs(values) 0
Using the subs argument for evalf is the accurate way to evaluate such an expression:
(x + y - z).evalf(subs=values) 1.00000000000000
normal
def normal(
self
)
Return the expression as a fraction.
expression -> a/b
See Also
as_numer_denom: return (a, b)
instead of a/b
nseries
def nseries(
self,
x=None,
x0=0,
n=6,
dir='+',
logx=None,
cdir=0
)
Wrapper to _eval_nseries if assumptions allow, else to series.
If x is given, x0 is 0, dir='+', and self has x, then _eval_nseries is called. This calculates "n" terms in the innermost expressions and then builds up the final series just by "cross-multiplying" everything out.
The optional logx
parameter can be used to replace any log(x) in the
returned series with a symbolic value to avoid evaluating log(x) at 0. A
symbol to use in place of log(x) should be provided.
Advantage -- it's fast, because we do not have to determine how many terms we need to calculate in advance.
Disadvantage -- you may end up with less terms than you may have expected, but the O(x**n) term appended will always be correct and so the result, though perhaps shorter, will also be correct.
If any of those assumptions is not met, this is treated like a wrapper to series which will try harder to return the correct number of terms.
See also lseries().
Examples
from sympy import sin, log, Symbol from sympy.abc import x, y sin(x).nseries(x, 0, 6) x - x3/6 + x5/120 + O(x6) log(x+1).nseries(x, 0, 5) x - x2/2 + x3/3 - x4/4 + O(x**5)
Handling of the logx
parameter --- in the following example the
expansion fails since sin
does not have an asymptotic expansion
at -oo (the limit of log(x) as x approaches 0):
e = sin(log(x)) e.nseries(x, 0, 6) Traceback (most recent call last): ... PoleError: ... ... logx = Symbol('logx') e.nseries(x, 0, 6, logx=logx) sin(logx)
In the following example, the expansion works but only returns self
unless the logx
parameter is used:
e = xy e.nseries(x, 0, 2) xy e.nseries(x, 0, 2, logx=logx) exp(logx*y)
nsimplify
def nsimplify(
self,
constants=(),
tolerance=None,
full=False
)
See the nsimplify function in sympy.simplify
powsimp
def powsimp(
self,
*args,
**kwargs
)
See the powsimp function in sympy.simplify
primitive
def primitive(
self
)
Return the positive Rational that can be extracted non-recursively
from every term of self (i.e., self is treated like an Add). This is like the as_coeff_Mul() method but primitive always extracts a positive Rational (never a negative or a Float).
Examples
from sympy.abc import x (3(x + 1)2).primitive() (3, (x + 1)2) a = (6x + 2); a.primitive() (2, 3x + 1) b = (x/2 + 3); b.primitive() (1/2, x + 6) (ab).primitive() == (1, a*b) True
radsimp
def radsimp(
self,
**kwargs
)
See the radsimp function in sympy.simplify
ratsimp
def ratsimp(
self
)
See the ratsimp function in sympy.simplify
rcall
def rcall(
self,
*args
)
Apply on the argument recursively through the expression tree.
This method is used to simulate a common abuse of notation for operators. For instance, in SymPy the following will not work:
(x+Lambda(y, 2*y))(z) == x+2*z
,
however, you can use:
from sympy import Lambda from sympy.abc import x, y, z (x + Lambda(y, 2y)).rcall(z) x + 2z
refine
def refine(
self,
assumption=True
)
See the refine function in sympy.assumptions
removeO
def removeO(
self
)
Removes the additive O(..) symbol if there is one
replace
def replace(
self,
query,
value,
map=False,
simultaneous=True,
exact=None
)
Replace matching subexpressions of self
with value
.
If map = True
then also return the mapping {old: new} where old
was a sub-expression found with query and new
is the replacement
value for it. If the expression itself does not match the query, then
the returned value will be self.xreplace(map)
otherwise it should
be self.subs(ordered(map.items()))
.
Traverses an expression tree and performs replacement of matching
subexpressions from the bottom to the top of the tree. The default
approach is to do the replacement in a simultaneous fashion so
changes made are targeted only once. If this is not desired or causes
problems, simultaneous
can be set to False.
In addition, if an expression containing more than one Wild symbol
is being used to match subexpressions and the exact
flag is None
it will be set to True so the match will only succeed if all non-zero
values are received for each Wild that appears in the match pattern.
Setting this to False accepts a match of 0; while setting it True
accepts all matches that have a 0 in them. See example below for
cautions.
The list of possible combinations of queries and replacement values is listed below:
Examples
Initial setup
from sympy import log, sin, cos, tan, Wild, Mul, Add from sympy.abc import x, y f = log(sin(x)) + tan(sin(x**2))
1.1. type -> type obj.replace(type, newtype)
When object of type ``type`` is found, replace it with the
result of passing its argument(s) to ``newtype``.
>>> f.replace(sin, cos)
log(cos(x)) + tan(cos(x**2))
>>> sin(x).replace(sin, cos, map=True)
(cos(x), {sin(x): cos(x)})
>>> (x*y).replace(Mul, Add)
x + y
1.2. type -> func obj.replace(type, func)
When object of type ``type`` is found, apply ``func`` to its
argument(s). ``func`` must be written to handle the number
of arguments of ``type``.
>>> f.replace(sin, lambda arg: sin(2*arg))
log(sin(2*x)) + tan(sin(2*x**2))
>>> (x*y).replace(Mul, lambda *args: sin(2*Mul(*args)))
sin(2*x*y)
2.1. pattern -> expr obj.replace(pattern(wild), expr(wild))
Replace subexpressions matching ``pattern`` with the expression
written in terms of the Wild symbols in ``pattern``.
>>> a, b = map(Wild, 'ab')
>>> f.replace(sin(a), tan(a))
log(tan(x)) + tan(tan(x**2))
>>> f.replace(sin(a), tan(a/2))
log(tan(x/2)) + tan(tan(x**2/2))
>>> f.replace(sin(a), a)
log(x) + tan(x**2)
>>> (x*y).replace(a*x, a)
y
Matching is exact by default when more than one Wild symbol
is used: matching fails unless the match gives non-zero
values for all Wild symbols:
>>> (2*x + y).replace(a*x + b, b - a)
y - 2
>>> (2*x).replace(a*x + b, b - a)
2*x
When set to False, the results may be non-intuitive:
>>> (2*x).replace(a*x + b, b - a, exact=False)
2/x
2.2. pattern -> func obj.replace(pattern(wild), lambda wild: expr(wild))
All behavior is the same as in 2.1 but now a function in terms of
pattern variables is used rather than an expression:
>>> f.replace(sin(a), lambda a: sin(2*a))
log(sin(2*x)) + tan(sin(2*x**2))
3.1. func -> func obj.replace(filter, func)
Replace subexpression ``e`` with ``func(e)`` if ``filter(e)``
is True.
>>> g = 2*sin(x**3)
>>> g.replace(lambda expr: expr.is_Number, lambda expr: expr**2)
4*sin(x**9)
The expression itself is also targeted by the query but is done in such a fashion that changes are not made twice.
>>> e = x*(x*y + 1)
>>> e.replace(lambda x: x.is_Mul, lambda x: 2*x)
2*x*(2*x*y + 1)
When matching a single symbol, exact
will default to True, but
this may or may not be the behavior that is desired:
Here, we want exact=False
:
from sympy import Function f = Function('f') e = f(1) + f(0) q = f(a), lambda a: f(a + 1) e.replace(q, exact=False) f(1) + f(2) e.replace(q, exact=True) f(0) + f(2)
But here, the nature of matching makes selecting the right setting tricky:
e = x(1 + y) (x(1 + y)).replace(x(1 + a), lambda a: x-a, exact=False) x (x(1 + y)).replace(x(1 + a), lambda a: x-a, exact=True) x(-x - y + 1) (xy).replace(x(1 + a), lambda a: x-a, exact=False) x (xy).replace(x(1 + a), lambda a: x-a, exact=True) x**(1 - y)
It is probably better to use a different form of the query that describes the target expression more precisely:
(1 + x(1 + y)).replace( ... lambda x: x.is_Pow and x.exp.is_Add and x.exp.args[0] == 1, ... lambda x: x.base(1 - (x.exp - 1))) ... x**(1 - y) + 1
See Also
subs: substitution of subexpressions as defined by the objects themselves. xreplace: exact node replacement in expr tree; also capable of using matching rules
rewrite
def rewrite(
self,
*args,
deep=True,
**hints
)
Rewrite self using a defined rule.
Rewriting transforms an expression to another, which is mathematically equivalent but structurally different. For example you can rewrite trigonometric functions as complex exponentials or combinatorial functions as gamma function.
This method takes a pattern and a rule as positional arguments. pattern is optional parameter which defines the types of expressions that will be transformed. If it is not passed, all possible expressions will be rewritten. rule defines how the expression will be rewritten.
Parameters
args : Expr A rule, or pattern and rule. - pattern is a type or an iterable of types. - rule can be any object.
deep : bool, optional
If True
, subexpressions are recursively transformed. Default is
True
.
Examples
If pattern is unspecified, all possible expressions are transformed.
from sympy import cos, sin, exp, I from sympy.abc import x expr = cos(x) + Isin(x) expr.rewrite(exp) exp(Ix)
Pattern can be a type or an iterable of types.
expr.rewrite(sin, exp) exp(Ix)/2 + cos(x) - exp(-Ix)/2 expr.rewrite([cos,], exp) exp(Ix)/2 + Isin(x) + exp(-Ix)/2 expr.rewrite([cos, sin], exp) exp(Ix)
Rewriting behavior can be implemented by defining _eval_rewrite()
method.
from sympy import Expr, sqrt, pi class MySin(Expr): ... def _eval_rewrite(self, rule, args, hints): ... x, = args ... if rule == cos: ... return cos(pi/2 - x, evaluate=False) ... if rule == sqrt: ... return sqrt(1 - cos(x)2) MySin(MySin(x)).rewrite(cos) cos(-cos(-x + pi/2) + pi/2) MySin(x).rewrite(sqrt) sqrt(1 - cos(x)**2)
Defining _eval_rewrite_as_[...]()
method is supported for backwards
compatibility reason. This may be removed in the future and using it is
discouraged.
class MySin(Expr): ... def _eval_rewrite_as_cos(self, args, *hints): ... x, = args ... return cos(pi/2 - x, evaluate=False) MySin(x).rewrite(cos) cos(-x + pi/2)
round
def round(
self,
n=None
)
Return x rounded to the given decimal place.
If a complex number would results, apply round to the real and imaginary components of the number.
Examples
from sympy import pi, E, I, S, Number pi.round() 3 pi.round(2) 3.14 (2pi + EI).round() 6 + 3*I
The round method has a chopping effect:
(2pi + I/10).round() 6 (pi/10 + 2I).round() 2I (pi/10 + EI).round(2) 0.31 + 2.72*I
Notes
The Python round
function uses the SymPy round
method so it
will always return a SymPy number (not a Python float or int):
isinstance(round(S(123), -2), Number) True
separate
def separate(
self,
deep=False,
force=False
)
See the separate function in sympy.simplify
series
def series(
self,
x=None,
x0=0,
n=6,
dir='+',
logx=None,
cdir=0
)
Series expansion of "self" around x = x0
yielding either terms of
the series one by one (the lazy series given when n=None), else all the terms at once when n != None.
Returns the series expansion of "self" around the point x = x0
with respect to x
up to O((x - x0)**n, x, x0)
(default n is 6).
If x=None
and self
is univariate, the univariate symbol will
be supplied, otherwise an error will be raised.
Parameters
expr : Expression The expression whose series is to be expanded.
x : Symbol It is the variable of the expression to be calculated.
x0 : Value
The value around which x
is calculated. Can be any value
from -oo
to oo
.
n : Value
The value used to represent the order in terms of x**n
,
up to which the series is to be expanded.
dir : String, optional
The series-expansion can be bi-directional. If dir="+"
,
then (x->x0+). If dir="-", then (x->x0-). For infinite
x0(
ooor
-oo), the
dirargument is determined
from the direction of the infinity (i.e.,
dir="-"for
oo``).
logx : optional It is used to replace any log(x) in the returned series with a symbolic value rather than evaluating the actual value.
cdir : optional It stands for complex direction, and indicates the direction from which the expansion needs to be evaluated.
Examples
from sympy import cos, exp, tan from sympy.abc import x, y cos(x).series() 1 - x2/2 + x4/24 + O(x6) cos(x).series(n=4) 1 - x2/2 + O(x4) cos(x).series(x, x0=1, n=2) cos(1) - (x - 1)*sin(1) + O((x - 1)2, (x, 1)) e = cos(x + exp(y)) e.series(y, n=2) cos(x + 1) - ysin(x + 1) + O(y2) e.series(x, n=2) cos(exp(y)) - xsin(exp(y)) + O(x**2)
If n=None
then a generator of the series terms will be returned.
term=cos(x).series(n=None) [next(term) for i in range(2)] [1, -x**2/2]
For dir=+
(default) the series is calculated from the right and
for dir=-
the series from the left. For smooth functions this
flag will not alter the results.
abs(x).series(dir="+") x abs(x).series(dir="-") -x f = tan(x) f.series(x, 2, 6, "+") tan(2) + (1 + tan(2)2)*(x - 2) + (x - 2)2(tan(2)3 + tan(2)) + (x - 2)3(1/3 + 4tan(2)2/3 + tan(2)4) + (x - 2)4(tan(2)5 + 5*tan(2)3/3 + 2tan(2)/3) + (x - 2)5(2/15 + 17tan(2)2/15 + 2tan(2)4 + tan(2)6) + O((x - 2)**6, (x, 2))
f.series(x, 2, 3, "-") tan(2) + (2 - x)(-tan(2)2 - 1) + (2 - x)2(tan(2)3 + tan(2)) + O((x - 2)3, (x, 2))
For rational expressions this method may return original expression without the Order term.
(1/x).series(x, n=8) 1/x
Returns
Expr : Expression Series expansion of the expression about x0
Raises
TypeError If "n" and "x0" are infinity objects
PoleError If "x0" is an infinity object
simplify
def simplify(
self,
**kwargs
)
See the simplify function in sympy.simplify
sort_key
def sort_key(
self,
order=None
)
Return a sort key.
Examples
from sympy import S, I
sorted([S(1)/2, I, -I], key=lambda x: x.sort_key()) [1/2, -I, I]
S("[x, 1/x, 1/x2, x2, x(1/2), x(1/4), x(3/2)]") [x, 1/x, x(-2), x2, sqrt(x), x(1/4), x(3/2)] sorted(_, key=lambda x: x.sort_key()) [x(-2), 1/x, x(1/4), sqrt(x), x, x(3/2), x**2]
subs
def subs(
self,
*args,
**kwargs
)
Substitutes old for new in an expression after sympifying args.
args
is either:
- two arguments, e.g. foo.subs(old, new)
- one iterable argument, e.g. foo.subs(iterable). The iterable may be
o an iterable container with (old, new) pairs. In this case the
replacements are processed in the order given with successive
patterns possibly affecting replacements already made.
o a dict or set whose key/value items correspond to old/new pairs.
In this case the old/new pairs will be sorted by op count and in
case of a tie, by number of args and the default_sort_key. The
resulting sorted list is then processed as an iterable container
(see previous).
If the keyword simultaneous
is True, the subexpressions will not be
evaluated until all the substitutions have been made.
Examples
from sympy import pi, exp, limit, oo from sympy.abc import x, y (1 + xy).subs(x, pi) piy + 1 (1 + xy).subs({x:pi, y:2}) 1 + 2pi (1 + xy).subs([(x, pi), (y, 2)]) 1 + 2pi reps = [(y, x2), (x, 2)] (x + y).subs(reps) 6 (x + y).subs(reversed(reps)) x2 + 2
(x2 + x4).subs(x2, y) y2 + y
To replace only the x2 but not the x4, use xreplace:
(x2 + x4).xreplace({x2: y}) x4 + y
To delay evaluation until all substitutions have been made,
set the keyword simultaneous
to True:
(x/y).subs([(x, 0), (y, 0)]) 0 (x/y).subs([(x, 0), (y, 0)], simultaneous=True) nan
This has the added feature of not allowing subsequent substitutions to affect those already made:
((x + y)/y).subs({x + y: y, y: x + y}) 1 ((x + y)/y).subs({x + y: y, y: x + y}, simultaneous=True) y/(x + y)
In order to obtain a canonical result, unordered iterables are sorted by count_op length, number of arguments and by the default_sort_key to break any ties. All other iterables are left unsorted.
from sympy import sqrt, sin, cos from sympy.abc import a, b, c, d, e
A = (sqrt(sin(2x)), a) B = (sin(2x), b) C = (cos(2*x), c) D = (x, d) E = (exp(x), e)
expr = sqrt(sin(2x))sin(exp(x)x)cos(2x) + sin(2x)
expr.subs(dict([A, B, C, D, E])) acsin(d*e) + b
The resulting expression represents a literal replacement of the old arguments with the new arguments. This may not reflect the limiting behavior of the expression:
(x*3 - 3x).subs({x: oo}) nan
limit(x*3 - 3x, x, oo) oo
If the substitution will be followed by numerical evaluation, it is better to pass the substitution to evalf as
(1/x).evalf(subs={x: 3.0}, n=21) 0.333333333333333333333
rather than
(1/x).subs({x: 3.0}).evalf(21) 0.333333333333333314830
as the former will ensure that the desired level of precision is obtained.
See Also
replace: replacement capable of doing wildcard-like matching, parsing of match, and conditional replacements xreplace: exact node replacement in expr tree; also capable of using matching rules sympy.core.evalf.EvalfMixin.evalf: calculates the given formula to a desired level of precision
taylor_term
def taylor_term(
self,
n,
x,
*previous_terms
)
General method for the taylor term.
This method is slow, because it differentiates n-times. Subclasses can redefine it to make it faster by using the "previous_terms".
to_nnf
def to_nnf(
self,
simplify=True
)
together
def together(
self,
*args,
**kwargs
)
See the together function in sympy.polys
transpose
def transpose(
self
)
trigsimp
def trigsimp(
self,
**args
)
See the trigsimp function in sympy.simplify
xreplace
def xreplace(
self,
rule,
hack2=False
)
Replace occurrences of objects within the expression.
Parameters
rule : dict-like Expresses a replacement rule
Returns
xreplace : the result of the replacement
Examples
from sympy import symbols, pi, exp x, y, z = symbols('x y z') (1 + xy).xreplace({x: pi}) piy + 1 (1 + xy).xreplace({x: pi, y: 2}) 1 + 2pi
Replacements occur only if an entire node in the expression tree is matched:
(xy + z).xreplace({xy: pi}) z + pi (xyz).xreplace({xy: pi}) xyz (2x).xreplace({2x: y, x: z}) y (22x).xreplace({2x: y, x: z}) 4*z (x + y + 2).xreplace({x + y: 2}) x + y + 2 (x + 2 + exp(x + 2)).xreplace({x + 2: y}) x + exp(y) + 2
xreplace does not differentiate between free and bound symbols. In the following, subs(x, y) would not change x since it is a bound symbol, but xreplace does:
from sympy import Integral Integral(x, (x, 1, 2x)).xreplace({x: y}) Integral(y, (y, 1, 2y))
Trying to replace x with an expression raises an error:
Integral(x, (x, 1, 2x)).xreplace({x: 2y}) # doctest: +SKIP ValueError: Invalid limits given: ((2y, 1, 4y),)
See Also
replace: replacement capable of doing wildcard-like matching, parsing of match, and conditional replacements subs: substitution of subexpressions as defined by the objects themselves.
ParameterReference
class ParameterReference(
workflow: 'Workflow',
parameter: 'Parameter[RawType]'
)
Reference to an individual Parameter
.
Allows us to refer to the outputs of a Parameter
in subsequent Parameter
arguments.
Attributes
Name | Type | Description | Default |
---|---|---|---|
parameter | None | Parameter referred to. |
None |
Ancestors (in MRO)
- dewret.workflow.Reference
Instance variables
name
Reference based on the named step.
May be remapped by the workflow to something nicer than the ID.
Raw
class Raw(
value: 'RawType'
)
Value object for any raw types.
This is able to hash raw types consistently and provides a single type for validating type-consistency.
Attributes
Name | Type | Description | Default |
---|---|---|---|
value | None | the real value, e.g. a str , int , ... |
None |
Instance variables
value
Reference
class Reference(
/,
*args,
**kwargs
)
Superclass for all symbolic references to values.
Descendants
- dewret.workflow.ParameterReference
- dewret.workflow.StepReference
Instance variables
name
Referral name for this reference.
Step
class Step(
workflow: 'Workflow',
task: 'Task',
arguments: 'Mapping[str, Reference | Raw]'
)
Lazy-evaluated function call.
Individual function call to a lazy-evaluatable function, tracked
for building up the Workflow
.
Attributes
Name | Type | Description | Default |
---|---|---|---|
task | None | the Task being called in this step. |
None |
arguments | None | key-value pairs of arguments to this step. | None |
Ancestors (in MRO)
- dewret.workflow.WorkflowComponent
Instance variables
id
Consistent ID based on the value.
name
Name for this step.
May be remapped by the workflow to something nicer than the ID.
return_type
Take the type of the wrapped function from the target.
Unwraps and inspects the signature, meaning that the original wrapped function must have a typehint for the return value.
Methods
set_workflow
def set_workflow(
self,
workflow: 'Workflow'
) -> 'None'
Move the step reference to another workflow.
Primarily intended to be called by its step, as a cascade. It will attempt to update its arguments, similarly.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
workflow | None | the new target workflow. | None |
StepReference
class StepReference(
workflow: 'Workflow',
step: 'Step',
typ: 'type[U]',
field: 'str | None' = None
)
Reference to an individual Step
.
Allows us to refer to the outputs of a Step
in subsequent Step
arguments.
Attributes
Name | Type | Description | Default |
---|---|---|---|
step | None | Step referred to. |
None |
Ancestors (in MRO)
- typing.Generic
- dewret.workflow.Reference
Instance variables
field
Field within the result.
Explicitly set field (within an attrs-class) or out
.
name
Reference based on the named step.
May be remapped by the workflow to something nicer than the ID.
return_type
Type that this step reference will resolve to.
Task
class Task(
name: 'str',
target: 'Lazy'
)
Named wrapper of a lazy-evaluatable function.
Wraps a lazy-evaluatable function (dewret.workflow.Lazy
) with any
metadata needed to render it later. At present, this is the name.
Attributes
Name | Type | Description | Default |
---|---|---|---|
name | None | Name of the lazy function as it will appear in the output workflow text. | None |
target | None | Callable that is wrapped. | None |
Workflow
class Workflow(
)
Overarching workflow concept.
Represents a whole workflow, as a singleton maintaining all state information needed ahead of rendering. It is built up as the lazy-evaluations are finally evaluated.
Attributes
Name | Type | Description | Default |
---|---|---|---|
steps | None | the sequence of calls to lazy-evaluable functions, built as they are evaluated. |
None |
tasks | None | the mapping of names used in the steps to the actualTask wrappers they represent. |
None |
result | None | target reference to evaluate, if yet present. | None |
Static methods
assimilate
def assimilate(
left: 'Workflow',
right: 'Workflow'
) -> "'Workflow'"
Combine two Workflows into one Workflow.
Takes two workflows and unifies them by combining steps and tasks. If it sees mismatched identifiers for the same component, it will error. This could happen if the hashing function is flawed or some Python magic to do with Targets being passed.
Argument: left: workflow to use as base right: workflow to combine on top
from_result
def from_result(
result: 'StepReference[Any]',
simplify_ids: 'bool' = False
) -> 'Workflow'
Create from a desired result.
Starts from a result, and builds a workflow to output it.
Methods
add_step
def add_step(
self,
fn: 'Lazy',
kwargs: 'dict[str, Raw | Reference]'
) -> 'StepReference[Any]'
Append a step.
Adds a step, for running a target with key-value arguments, to the workflow.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
fn | None | the target function to turn into a step. | None |
kwargs | None | any key-value arguments to pass in the call. | None |
find_parameters
def find_parameters(
self
) -> 'set[ParameterReference]'
Crawl steps for parameter references.
As the workflow does not hold its own list of parameters, this dynamically finds them.
Returns:
Type | Description |
---|---|
None | Set of all references to parameters across the steps. |
register_task
def register_task(
self,
fn: 'Lazy'
) -> 'Task'
Note the existence of a lazy-evaluatable function, and wrap it as a Task
.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
fn | None | the wrapped function. | None |
Returns:
Type | Description |
---|---|
None | A new Task that wraps the function, and is retained in the Workflow.tasks dict. |
remap
def remap(
self,
step_id: 'str'
) -> 'str'
Apply name simplification if requested.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
step_id | None | step to check. | None |
Returns:
Type | Description |
---|---|
None | Same ID or a remapped name. |
set_result
def set_result(
self,
result: 'StepReference[Any]'
) -> 'None'
Choose the result step.
Sets a step as being the result for the entire workflow. When we evaluate a dynamic workflow, the engine (e.g. dask) creates a graph to realize the result of a single collection. Similarly, in the static case, we need to have a result that drives the calculation.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
result | None | reference to the chosen step. | None |
simplify_ids
def simplify_ids(
self
) -> 'None'
Work out mapping to simple ints from hashes.
Goes through and numbers each step by the order of use of its task.
WorkflowComponent
class WorkflowComponent(
workflow: 'Workflow'
)
Base class for anything directly tied to an individual Workflow
.
Attributes
Name | Type | Description | Default |
---|---|---|---|
workflow | None | the Workflow that this is tied to. |
None |
Descendants
- dewret.workflow.Step
WorkflowLinkedComponent
class WorkflowLinkedComponent(
*args,
**kwargs
)
Protocol for objects dynamically tied to a Workflow
.
Ancestors (in MRO)
- typing.Protocol
- typing.Generic