Reference/API
SymPy.SymPySymPy.𝑄SymPy.FalseSymPy.IMSymPy.PISymPy.TrueSymPy.ooSymPy.sympySymPy.sympy_coreSymPy.sympy_matricesSymPy.sympy_plottingSymPy.zooSymPy.DifferentialSymPy.DocSymPy.SymFunctionSymPy.SymMatrixSymPy.VectorFieldBase.:~Base.getindexBase.getindexBase.matchBase.occursinBase.replaceCommonSolve.solveSymPy.Introspection.argsSymPy.Introspection.funcSymPy.Introspection.funcnameSymPy.NSymPy.NSymPy.PermutationSymPy.PermutationGroupSymPy.WildSymPy.askSymPy.doitSymPy.dsolveSymPy.elementsSymPy.free_symbolsSymPy.import_fromSymPy.import_sympySymPy.jprintSymPy.lambdifySymPy.nonlinsolveSymPy.refineSymPy.simplifySymPy.subsSymPy.symbolsSymPy.walk_expressionSymPy.@symfunsSymPy.@symsSymPy.@vars
Base.:~ — Methodlhs ~ rhsSpecify an equation.
Alternative syntax to Eq(lhs, rhs) or lhs ⩵ rhs (\Equal[tab]) following Symbolics.jl.
Base.getindex — Methodx[i]Some SymPy Python objects have index notation provided for them through __getitem__. This allows Julia's getindex to dispatch to Python's __getitem__. The index (indices) must be symbolic. This will use 0-based indexing, as it is a simple pass through to Python.
Examples:
julia> using SymPy
julia> i,j = sympy.symbols("i j", integer=true)
(i, j)
julia> x = sympy.IndexedBase("x")
x
julia> a = sympy.Sum(x[i], (i, 1, j))
j
___
╲
╲
╱ x[i]
╱
‾‾‾
i = 1Base.getindex — MethodM[i,j]Define getindex for SymPy's ImmutableMatrix class.
SymMatrix is 0-based, like python, not Julia. Use Matrix{Sym} for that.
Base.match — Methodmatch(pattern, expression, ...)Match a pattern against an expression; returns a dictionary of matches.
If a match is unsuccesful, returns an empty dictionary. (SymPy returns "nothing")
The order of the arguments follows Julia's match function, not sympy.match, which can be used directly, otherwise.
Base.occursin — Methodoccursin(x, G::SymPermutationGroup)
Does G contain x. (In SymPy, this is `contains.)
Base.replace — Methodreplace(expression, pattern, value, ...)
replace(expression, pattern => value; kwargs...)In the expression replace a mathcing pattern with the value. Returns the modified expression.
Extended help
From: SymPy Docs
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 true, then the match will only succeed if non-zero values are received for each Wild that appears in the match pattern.
Differences from SymPy:
"types" are specified via calling
funcon the head of an expression:func(sin(x))->sin, or directly throughsympy.sinfunctions are supported, but only with
PyCallcommands.
Examples (from the SymPy docs)
julia> using SymPy
julia> x, y, z = symbols("x, y, z")
(x, y, z)
julia> f = log(sin(x)) + tan(sin(x^2)); string(f) # `string(f)` only so doctest can run
"log(sin(x)) + tan(sin(x^2))"
"type" -> "type"
Types are specified through func:
julia> func = SymPy.Introspection.func
func (generic function with 1 method)
julia> replace(f, func(sin(x)), func(cos(x))) |> string # type -> type
"log(cos(x)) + tan(cos(x^2))"
julia> replace(f, sympy.sin, sympy.cos) |> string
"log(cos(x)) + tan(cos(x^2))"
julia> sin(x).replace(sympy.sin, sympy.cos, map=true)
(cos(x), Dict{Any, Any}(sin(x) => cos(x)))
The func function finds the head of an expression (sin and cos above). This could also have been written (perhaps more directly) as:
julia> replace(f, sympy.sin, sympy.cos) |> string
"log(cos(x)) + tan(cos(x^2))"
"type" -> "function"
To replace with a more complicated function, requires some assistance from Python, as an anonymous function must be defined witin Python, not Julia:
julia> import PyCall
julia> ## Anonymous function a -> sin(2a)
PyCall.py"""
from sympy import sin, Mul
def anonfn(*args):
return sin(2*Mul(*args))
""")
julia> replace(f, sympy.sin, PyCall.py"anonfn")
⎛ ⎛ 2⎞⎞
log(sin(2⋅x)) + tan⎝sin⎝2⋅x ⎠⎠"pattern" -> "expression"
Using "Wild" variables allows a pattern to be replaced by an expression:
julia> a, b = Wild("a"), Wild("b")
(a_, b_)
julia> replace(f, sin(a), tan(2a)) |> string
"log(tan(2*x)) + tan(tan(2*x^2))"
julia> replace(f, sin(a), tan(a/2)) |> string
"log(tan(x/2)) + tan(tan(x^2/2))"
julia> f.replace(sin(a), a) |> string
"log(x) + tan(x^2)"
julia> (x*y).replace(a*x, a)
y
In the SymPy docs we have:
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."
julia> replace(2x + y, a*x+b, b-a) # y - 2
y - 2
julia> replace(2x + y, a*x+b, b-a, exact=false) # y + 2/x
2
y + ─
x"pattern" -> "func"
The function is redefined, as a fixed argument is passed:
julia> PyCall.py"""
from sympy import sin
def anonfn(a):
return sin(2*a)
"""
julia> replace(f, sin(a), PyCall.py"anonfn")
⎛ ⎛ 2⎞⎞
log(sin(2⋅x)) + tan⎝sin⎝2⋅x ⎠⎠"func" -> "func"
julia> PyCall.py"""
def fn1(expr):
return expr.is_Number
def fn2(expr):
return expr**2
"""
julia> replace(2*sin(x^3), PyCall.py"fn1", PyCall.py"fn2")
⎛ 9⎞
4⋅sin⎝x ⎠julia> PyCall.py"""
def fn1(x):
return x.is_Mul
def fn2(x):
return 2*x
"""
julia> replace(x*(x*y + 1), PyCall.py"fn1", PyCall.py"fn2")
2⋅x⋅(2⋅x⋅y + 1)CommonSolve.solve — MethodsolveUse solve to solve algebraic equations.
Examples:
julia> using SymPy
julia> @syms x y a b c d
(x, y, a, b, c, d)
julia> solve(x^2 + 2x + 1, x) # [-1]
1-element Vector{Sym}:
-1
julia> solve(x^2 + 2a*x + a^2, x) # [-a]
1-element Vector{Sym}:
-a
julia> solve([a*x + b*y-3, c*x + b*y - 1], [x,y]) # Dict(y => (a - 3*c)/(b*(a - c)),x => 2/(a - c))
Dict{Any, Any} with 2 entries:
y => (a - 3*c)/(a*b - b*c)
x => 2/(a - c)
A very nice example using solve is a blog entry on Napolean's theorem by Xing Shi Cai.
SymPy.N — MethodN(x::Sym, digits::Int)N can take a precision argument, whichm when given as an integer greater than 16, we try to match the digits of accuracy using BigFloat precision on conversions to floating point.
SymPy.N — MethodN(ex)Convert a Sym value to a numeric Julian value.
In SymPy, N(ex, options...) is identifcal to ex.evalf(options...) and is used to convert expressions into floating-point approximations. A positional precision argument indicates the number of digits, keyword arguments chop can be used to trim floating point roundoff errors and subs for free variable substitution prior to conversions.
For example, symbolic roots can be computed numerically, even if not available symbolically, by calling N on the values.
Using SymPy within Julia makes having two such functions useful:
- one to do the equivalent of SymPy's
evalfcall - one to convert these expressions back into
Juliaobjects (likeconvert(T, ex))
We use N to return a Julia object and evalf to return a symbolic object. The type of Julia object is heurisitically identified.
Examples:
julia> using SymPy
julia> x = Sym("x")
x
julia> p = subs(x, x, pi)
π
julia> N(p) # float version of pi
π = 3.1415926535897...
julia> p.evalf(60) # 60 digits of pi, as a symbolic value
3.14159265358979323846264338327950288419716939937510582097494
julia> N(p, 60) # when a precision is given, "Big" values are returned
3.141592653589793238462643383279502884197169399375105820974939
julia> r = subs(x,x,1.2)
1.20000000000000
julia> N(r) # float
1.2
julia> q = subs(x, x, 1//2)
1/2
julia> N(q) # 1//2
1//2
julia> z = solve(x^2 + 1)[1] # -ⅈ
-ⅈ
julia> N(z) # 0 - 1im
0 - 1im
julia> z.evalf()
-1.0⋅ⅈ
julia> rts = solve(x^5 - x + 1)
5-element Vector{Sym}:
CRootOf(x^5 - x + 1, 0)
CRootOf(x^5 - x + 1, 1)
CRootOf(x^5 - x + 1, 2)
CRootOf(x^5 - x + 1, 3)
CRootOf(x^5 - x + 1, 4)
julia> [r.evalf() for r in rts] # numeric solutions to quintic
5-element Vector{Sym}:
-1.16730397826142
-0.181232444469875 - 1.08395410131771⋅ⅈ
-0.181232444469875 + 1.08395410131771⋅ⅈ
0.764884433600585 - 0.352471546031726⋅ⅈ
0.764884433600585 + 0.352471546031726⋅ⅈ
julia> [N(r) for r in rts]
5-element Vector{Number}:
-1.167303978261418684256045899854842180720560371525489039140082449275651903429536
-0.18123244446987538 - 1.0839541013177107im
-0.18123244446987538 + 1.0839541013177107im
0.7648844336005847 - 0.35247154603172626im
0.7648844336005847 + 0.35247154603172626imN returns the value unchanged when it has free symbols.
SymPy.Permutation — MethodPermutation(args...)This module mostly implements SymPy's Permutation module.
A permuation can be represented in different ways. Here a permutation is a reaarangment of the values 0, 1, ..., n. For example, the mapping 0->2, 1->3, 2->0, 3->1 can be presented by a vector: sigma = [2,3,0,1] where sigma[i] = j when i -> j. Otheriwse, it can be presented as a product of cycles: (0 2)(1 3) which reads 0 goes to 2 which goes to 0 (wrapping) and 1 goes to 3 and 3 goes to 1.
Either representation can be passed through the Permutation constructor.
For the vector notation – 0-based – it is passed directly to the constructor:
julia> using SymPy
julia> p = Permutation([2,3,0,1])
(0 2)(1 3)
If a range describes a permutation, it can be used as well:
julia> id = Permutation(10:-1:0)
(0 10)(1 9)(2 8)(3 7)(4 6)
Cycle notation can more compactly describe a permuation, it can be passed in as a container of cycles specified through tuples or vectors:
julia> p = Permutation([(0,2), (1,3)])
(0 2)(1 3)
The latter can be be expresed more quickly as
julia> p = Permutation(0,2)(1,3)
(0 2)(1 3)
This works as a single cycle can be passed to the Permutation constructor with values separated by commas and the "call" method for Permuation objects is overloaded: for a single argument, the mapping i -> j is created (also the notation i^p returns this) but if more than one argument is given, a cycle is created and multiplied on the right by p, so that the above becomes (0,2) * (1,3).
Here are two permutations forming the symmetries of square, naturally represented in the two ways:
julia> flip = Permutation([[0,1],[2,3]]) # or Permutation(0,1)(2,3)
(0 1)(2 3)
julia> rotate = Permutation([1,2,3,0]) # or Permutation(0,1,2,3) in cycle notation
(0 1 2 3)
Operations on permutations include:
- a function call,
p(i)to recoverjwherei -> j, alsoi^p. *for multiplication. The convention is(p*q)(i) = q(p(i))or with the^notation:i^(p*q) = (i^p)^q.+for multiplication whenpandqcommute, where a check on commuting is performed.invfor the inverse permutation./, wherep/qisp * inv(q).p^nfor powers. We haveinv(p) = p^(-1)andp^order(p)is the identity.p^qfor conjugate, defined byinv(q) * p * q.
We can see that a flip is an involution through:
julia> flip^2 # the identity
()
whereas a rotation is not (as it has order 4)
julia> rotate * rotate
(0 2)(1 3)
julia> rotate.order()
4
These two operations do not commute:
julia> flip * rotate
(0 2)
julia> rotate * flip # (1 3)
(1 3)
We can see this is the correct mapping 1 -> 3 with
julia> (1^rotate)^flip, 1^(rotate*flip), flip(rotate(1))
(3, 3, 3)
We can check that flip and rotate^2 do commute:
julia> id = Permutation(3) # (n) is the identify
()
julia> flip.commutator(rotate^2) == id
true
The conjugate for flip and rotate does the inverse of the flip, then rotates, then flips:
julia> rotate^flip
(0 3 2 1)
This is different than flip^rotate. As flip commutes with rotate^2 this will return rotate^2:
julia> (rotate^2)^flip
(0 2)(1 3)
There is no support currently for the Cycle class
SymPy.PermutationGroup — MethodPermutationGroup()Create Permutation group from group generators
A PermutationGroup is one generated by a collection of permutations.
Some pre-defined groups are built-in:
SymmetricgGroup(n): S_n or all symmetries of an n-gonCyclicGroup: the group Z_nDihedralGroup: Group formed by a flip and rotationAlternativeGroup: Subgroup of S_n of even elementsAbelianGroup: Returns the direct product of cyclic groups with the given orders.
Differences:
- use
collect(generate(G))in place oflist(G.generate())
SymPy.Wild — MethodWild(x)create a "wild card" for pattern matching
SymPy.ask — Methodask(query)Returns true, false, or nothing; ask
Example:
julia> using SymPy
julia> @vars x y integer=true
(x, y)
julia> ask(𝑄.integer(x*y), And(𝑄.integer(x), 𝑄.integer(y)))
true
julia> ## really slow isprime:
filter(x -> ask(𝑄.prime(x)), 1:10)
4-element Vector{Int64}:
2
3
5
7SymPy.doit — Methoddoit evaluates objects that are not evaluated by default.
Examples:
julia> using SymPy
julia> @syms x f()
(x, f)
julia> D = Differential(x)
Differential(x)
julia> df = D(f(x))
d
──(f(x))
dx
julia> dfx = subs(df, (f(x), x^2))
d ⎛ 2⎞
──⎝x ⎠
dx
julia> doit(dfx)
2⋅xSet deep=true to apply doit recursively to force evaluation of nested expressions:
julia> @syms g()
(g,)
julia> dgfx = g(dfx)
⎛d ⎛ 2⎞⎞
g⎜──⎝x ⎠⎟
⎝dx ⎠
julia> doit(dgfx)
⎛d ⎛ 2⎞⎞
g⎜──⎝x ⎠⎟
⎝dx ⎠
julia> doit(dgfx, deep=true)
g(2⋅x)There is also a curried form of doit:
julia> dfx |> doit
2⋅x
julia> dgfx |> doit(deep=true)
g(2⋅x)SymPy.dsolve — Methoddsolve(eqn, var, args..,; ics=nothing, kwargs...)
Call sympy.dsolve.
The initial conditions are specified with a dictionary.
Example:
julia> using SymPy
julia> @syms α, x, f(), g()
(α, x, f, g)
julia> ∂ = Differential(x)
Differential(x)
julia> eqn = ∂(f(x)) ~ α * x
d
──(f(x)) = x⋅α
dxjulia> dsolve(eqn)
2
x ⋅α
f(x) = C₁ + ────
2julia> dsolve(eqn(α=>2); ics=Dict(f(0)=>1)) |> print # fill in parameter, initial condition
Eq(f(x), x^2 + 1)
julia> eqn = ∂(∂(f(x))) ~ -f(x); print(eqn)
Eq(Derivative(f(x), (x, 2)), -f(x))
julia> dsolve(eqn)
f(x) = C₁⋅sin(x) + C₂⋅cos(x)
julia> dsolve(eqn; ics = Dict(f(0)=>1, ∂(f)(0) => -1))
f(x) = -sin(x) + cos(x)
julia> eqn = ∂(∂(f(x))) - f(x) - exp(x);
julia> dsolve(eqn, ics=Dict(f(0) => 1, f(1) => Sym(1//2))) |> print # not just 1//2
Eq(f(x), (x/2 + (-exp(2) - 2 + E)/(-2 + 2*exp(2)))*exp(x) + (-E + 3*exp(2))*exp(-x)/(-2 + 2*exp(2)))Systems. Use a tuple, not a vector, of equations, as such are now deprecated by SymPy.
julia> @syms x() y() t g
(x, y, t, g)
julia> ∂ = Differential(t)
Differential(t)
julia> eqns = (∂(x(t)) ~ y(t), ∂(y(t)) ~ x(t))
(Eq(Derivative(x(t), t), y(t)), Eq(Derivative(y(t), t), x(t)))
julia> dsolve(eqns)
2-element Vector{Sym}:
Eq(x(t), -C1*exp(-t) + C2*exp(t))
Eq(y(t), C1*exp(-t) + C2*exp(t))
julia> dsolve(eqns, ics = Dict(x(0) => 1, y(0) => 2))
2-element Vector{Sym}:
Eq(x(t), 3*exp(t)/2 - exp(-t)/2)
Eq(y(t), 3*exp(t)/2 + exp(-t)/2)
julia> eqns = (∂(∂(x(t))) ~ 0, ∂(∂(y(t))) ~ -g)
(Eq(Derivative(x(t), (t, 2)), 0), Eq(Derivative(y(t), (t, 2)), -g))
julia> dsolve(eqns) # can't solve for initial conditions though! (NotAlgebraic)
2-element Vector{Sym}:
x(t) = C₁ + C₂⋅t
Eq(y(t), C3 + C4*t - g*t^2/2)
julia> @syms t x() y()
(t, x, y)
julia> eq = (∂(x)(t) ~ x(t)*y(t)*sin(t), ∂(y)(t) ~ y(t)^2 * sin(t))
(Eq(Derivative(x(t), t), x(t)*y(t)*sin(t)), Eq(Derivative(y(t), t), y(t)^2*sin(t)))julia> dsolve(eq) # returns a set to be `collect`ed:
PyObject {Eq(x(t), -exp(C1)/(C2*exp(C1) - cos(t))), Eq(y(t), -1/(C1 - cos(t)))}julia> dsolve(eq) |> collect
2-element Vector{Any}:
Eq(x(t), -exp(C1)/(C2*exp(C1) - cos(t)))
Eq(y(t), -1/(C1 - cos(t)))SymPy.elements — Methodelements(s)return elements of a set s as an array, unlike convert(Set,s)
SymPy.free_symbols — Methodfree_symbols(ex)
free_symbols(ex::Vector{Sym})Return vector of free symbols of expression or vector of expressions. The results are orderded by sortperm(string.(fs)).
Example:
julia> using SymPy
julia> @syms x y z a
(x, y, z, a)
julia> free_symbols(2*x + a*y) # [a, x, y]
3-element Vector{Sym}:
a
x
ySymPy.import_from — Methodimport_from(module, meths; kwargs...)Import methods from a python module. Implements functionality of from module import function in Python.
module: a python module, such assympymeths: nothing or a tuple of symbols to import. Ifnothing, then all member functions of the module are imported (but not constructors or other objects)Ms: additional Julia Modules to import from. By default, a few base modules are searched for to avoid namespace collisions.typ: a symbol indicating variable type for first argument that the new function should be restricted to. For most, the default,:SymbolicObjectwill be appropriateexclude: when importing all (meths=nothing), this can be used to avoid importing some methods by name. The default has a few to avoid.
Examples:
import_from(sympy) # bring in functions from sympy (done `import_sympy`)
import_from(sympy, (:sin, :cos)) # just bring in a few methods
import_from(sympy , (:Wild,), typ=:Any) # Allows `Wild("x")`
#
import PyCall
PyCall.pyimport_conda("sympy.physics.wigner", "sympy")
import_from(sympy.physics.wigner)SymPy.import_sympy — Methodimport_sympyThis method imports all functions from mpmath and a priviledged set of functions from sympy, as well as the relational operators.
These functions are narrowed on their first argument being of type SymbolicObject.
A few modules are checked for namespace collisions.
If a function naturally takes an non-Symbolic argument as a first argument, then it can be qualified: e.g. sympy.sin(2) (as opposed to sin(Sym(2))).
If a constructor is needed (which is not a function) then it must be qualified. (E.g. sympy.Function("F"), though for this particular case, there is SymFunction defined for convenience.)
SymPy.jprint — Methodcreate basic printed output
SymPy.lambdify — Functionlambdify(ex, vars=free_symbols();
fns=Dict(), values=Dict, use_julia_code=false,
invoke_latest=true)Take a symbolic expression and return a Julia function or expression to build a function.
ex::Syma symbolic expression with 0, 1, or more free symbolsvarsa container of symbols to use for the function arguments. The default isfree_symbolswhich has a specific ordering. Specifyingvarsallows this default ordering of arguments to be customized. Ifvarsis empty, such as when the symbolic expression has no free symbols, a variable arg constant function is returned.fns::Dict,vals::Dict: Dictionaries that allow customization of the function that walks the expressionexand creates the corresponding AST for a Julia expression. SeeSymPy.fn_mapandSymPy.val_mapfor the default mappings of sympy functions and values intoJulia's AST.use_julia_code::Bool: use SymPy's conversion to an expression, the default isfalseinvoke_latest=true: iftruewill callevalandBase.invokelatestto return a function that should not have any world age issue. Iffalsewill return a Julia expression that can beevaled to produce a function.
Example:
julia> using SymPy
julia> @syms x y z
(x, y, z)
julia> ex = x^2 * sin(x)
2
x ⋅sin(x)
julia> fn = lambdify(ex);
julia> fn(pi)
0.0
julia> ex = x + 2y + 3z
x + 2⋅y + 3⋅z
julia> fn = lambdify(ex);
julia> fn(1,2,3) # order is by free_symbols
14
julia> ex(x=>1, y=>2, z=>3)
14
julia> fn = lambdify(ex, (y,x,z));
julia> fn(1,2,3)
13!!! Note:
The default produces slower functions due to the calls to eval and Base.invokelatest. In the following g2 (which, as seen, requires additional work to compute) is as fast as calling f (on non symbolic types), whereas g1 is an order of magnitude slower in this example.
julia> @vars x
(x,)
julia> f(x) = exp(cot(x))
f (generic function with 1 method)
julia> g1 = lambdify(f(x))
#88 (generic function with 1 method)
julia> ex = lambdify(f(x), invoke_latest=false)
:(function var"##271"(x)
exp(cot(x))
end)
julia> @eval g2(x) = ($ex)(x)
g2 (generic function with 1 method)An alternative, say, is to use GeneralizedGenerated's mk_function, as follows:
julia> using GeneralizedGenerated
julia> body = convert(Expr, f(x))
:(exp(cot(x)))
julia> g3 = mk_function((:x,), (), body)
function = (x;) -> begin
(Main).exp((Main).cot(x))
endThis function will be about 2-3 times slower than f.
SymPy.nonlinsolve — MethodnonlinsolveNote: if passing variables in use a tuple (e.g., (x,y)) and not a vector (e.g., [x,y]).
SymPy.refine — Method refineSimplify an expression using assumptions; refine.
SymPy.simplify — MethodsimplifySymPy has dozens of functions to perform various kinds of simplification. There is also one general function called simplify that attempts to apply all of these functions in an intelligent way to arrive at the simplest form of an expression. (See Simplification for details on simplify and other related functionality).
For non-symbolic expressions, simplify returns its first argument.
SymPy.subs — Methodsubs is used to subsitute a value in an expression with another value. Examples:
julia> using SymPy
julia> x,y = symbols("x,y")
(x, y)
julia> ex = (x-y)*(x+2y)
(x - y)⋅(x + 2⋅y)
julia> subs(ex, (y, y^2))
⎛ 2⎞ ⎛ 2⎞
⎝x - y ⎠⋅⎝x + 2⋅y ⎠
julia> subs(ex, (x,1), (y,2))
-5
julia> subs(ex, (x,y^3), (y,2))
72
julia> subs(ex, y, 3)
(x - 3)⋅(x + 6)There is a curried form of subs to use with the chaining |> operator
julia> ex |> subs(x,ℯ)
(ℯ - y)⋅(2⋅y + ℯ)The use of pairs gives a convenient alternative:
julia> subs(ex, x=>1, y=>2)
-5
julia> ex |> subs(x=>1, y=>2)
-5SymPy.symbols — Methodsymbols(name(s), assumptions...)Calls sympy.symbols to produce symbolic variables and symbolic functions. An alternate to the recommended @syms, (when applicable)
In sympy sympy.symbols and sympy.Symbol both allow the construction of symbolic variables and functions. The Julia function symbols is an alias for sympy.symbols.
- Variables are created through
x=symbols("x"); - Assumptions on variables by
x=symbols("x", real=true); - Multiple symbols
x1,x2 = symbols("x[1:3]")can be created. Unlike@syms, the number of variables can be specified with a variable through interpolation. - Symbolic functions can be created py passing
cls=sympy.Function,symbols("F", cls=sympy.Function, real=true)
SymPy.walk_expression — Methodwalk_expression(ex; values=Dict(), fns=Dict())Convert a symbolic SymPy expression into a Julia expression. This is needed to use functions in external packages in lambdified functions.
Example
using SymPy
@syms x y
ex = sympy.hyper((2,2),(3,3),x) * yCalling lambdify(ex) will fail to make a valid function, as hyper is implemented in HypergeometricFunctions.pFq. So, we have:
using HypergeometricFunctions
d = Dict("hyper" => :pFq)
body = SymPy.walk_expression(ex, fns=d)
syms = Symbol.(free_symbols(ex))
fn = eval(Expr(:function, Expr(:call, gensym(), syms...), body));
fn(1,1) # 1.6015187080185656SymPy.False — ConstantFalse from SymPy
SymPy.IM — ConstantIM is a symbolic im
SymPy.PI — ConstantPI is symbolic pi
SymPy.True — ConstantTrue from SymPy
SymPy.oo — Constantoo is a symbolic infinity. Example: integrate(exp(-x), x, 0, oo).
SymPy.sympy — ConstantsympyVariable from pyimport("sympy"). Numerous methods are available through Python's dot-call syntax.
SymPy.sympy_core — Constantsympy_coreVariable from pyimport("sympy.core").
SymPy.sympy_matrices — Constantsympy_matricesVariable from pyimport("sympy.matrices").
SymPy.sympy_plotting — ConstantPlotting of symbolic objects.
The Plots package provide a uniform interface to many of Julia's plotting packages. SymPy plugs into Plots' "recipes."
The basic goal is that when Plots provides an interface for function objects, this package extends the interface to symbolic expressions.
In particular:
plot(ex::Sym, a, b; kwargs...)will plot a function evaluatingexover [a,b]
Example. Here we use the default backend for Plots to make a plot:
using Plots
@syms x
plot(x^2 - 2x, 0, 4)plot(ex1, ex2, a, b; kwargs...)will plot the two expressions in a parametric plot over the interval[a,b].
Example:
@syms x
plot(sin(2x), cos(3x), 0, 4pi) ## alsoFor a few backends (those that support :path3d) a third symbolic expression may be added to have a 3d parametric plot rendered:
plot(sin(x), cos(x), x, 0, 4pi) # helix in 3dplot(xs, ys, expression)will make a contour plot (for many backends).
@syms x y
plot(range(0,stop=5, length=50), range(0,stop=5, length=50), x*y)- To plot the surface
z=ex(x,y)over a region we havePlots.surface. For example,
@syms x y
surface(-5:5, -5:5, 25 - x^2 - y^2)- a vectorfield plot can (inefficiently but directly) be produced following this example:
function vfieldplot(fx, fy; xlim=(-5,5), ylim=(-5,5), n=8)
xs = range(xlim[1], stop=xlim[2], length=n)
ys = range(ylim[1], stop=ylim[2], length=n)
us = vec([x for x in xs, y in ys])
vs = vec([y for x in xs, y in ys])
fxs = vec([fx(x,y) for x in xs, y in ys])
fys = vec([fy(x,y) for x in xs, y in ys])
mxs = maximum(abs.(filter(!isinf, filter(!isnan, fxs))))
mys = maximum(abs.(filter(!isinf, filter(!isnan, fys))))
d = 1/2 * max((xlim[2]-xlim[1])/mxs, (ylim[2]-ylim[1])/mys) / n
quiver(us, vs, quiver=(fxs.*d, fys.*d))
end
fx = (x + y) / sqrt(x^2 + y^2)
fy = (x - y) / sqrt(x^2 + y^2)
vfieldplot(fx, fy)
- To plot two or more functions at once, the style
plot([ex1, ex2], a, b)does not work. Rather, useplot(ex1, a, b); plot!(ex2), as in:
@syms x
plot(sin(x), 0, 2pi)
plot!(cos(x))Some graphics provided by SymPy are available if PyPlot is installed, such as:
sympy.plotting.plot3d_parametric_surfacesympy.plotting.plot_implicit
Plot the parametrically defined surface [exs[1](u,v), exs[2](u,v), exs[3](u,v)] over [a0,a1] x [b0,b1]. The specification of the variables uses a tuple of the form (Sym, Real, Real) following the style of SymPy in integrate, say, where disambiguation of variable names is needed.
@syms theta, phi
r = 1
sympy.plotting.plot3d_parametric_surface((r*sin(theta)*sin(phi), r*sin(theta)*cos(phi), r*cos(theta)),
(theta, 0, pi), (phi, 0, pi/2))sympy.plotting.plot_implicit(equation, (xvar, x0, x1), (yvar, y0, y1))will plot implicitly the equation.
@syms x y
sympy.plotting.plot_implicit(Eq(x^2+ y^2,3), (x, -2, 2), (y, -2, 2)) # draw a circleSymPy.zoo — Constantzoo complex inifinity
SymPy.@symfuns — Macro@symfunsThanks to @alhirzel for the contribution.
!!! Note: The @symfuns macro is deprecated. The more general @syms macro should be used for constructing symbolic functions of type SymFunction and symbols can be used to construct symbolic functions in general.
SymPy.@syms — Macro@syms a n::integer x::(real,positive)=>"x₀" y[-1:1] u() v()::real w()::(real,positive) y()[1:3]::realConstruct symbolic variables or functions along with specified assumptions. Similar to @vars, sympy.symbols, and sympy.Function, but the specification of the assumptions is more immediate than those interfaces which follow sympy's constructors.
Allows the specification of assumptions on the variables and functions.
- a type-like annontation, such as
n::integeris equivalent tosympy.symbols("n", integer=true). Multiple assumptions are combined using parentheses (e.g.,n::(integer,nonnegative).
The possible values for assumptions are: "commutative", "complex", "imaginary", "real", "integer", "odd", "even", "prime", "composite", "zero", "nonzero", "rational", "algebraic", "transcendental", "irrational", "finite", "infinite", "negative", "nonnegative", "positive", "nonpositive", "hermitian", "antihermetian".
a tensor declaration form is provided to define arrays of variables, e.g.
x[-1:1]ory[1:4, 2:5].a symbolic function can be specified using a pair of parentheses after the name, as in
u().The return type of a function can have assumptions specified, as with a variable. E.g.,
h()::complex. How the symbolic function prints can be set as with a variable, e.g.h()::complex=>"h̄".multiple definitions can be separated by commas
How the symbol prints (the
__str__()value) can be specified using the syntax=>"name", as inx=>"xₒ"
Examples:
julia> using SymPy
julia> @syms a b::nonnegative
(a, b)
julia> sqrt(a^2), sqrt(b^2)
(sqrt(a^2), b)julia> @syms x::prime
(x,)
julia> ask(𝑄.negative(x)), ask(𝑄.integer(x)), ask(𝑄.even(x)) # (false, true, nothing)
(false, true, nothing)julia> @syms a[0:5], x
(Sym[a₀, a₁, a₂, a₃, a₄, a₅], x)
julia> sum( aᵢ*x^(i) for (i,aᵢ) ∈ zip(0:5, a)) |> print
a₀ + a₁*x + a₂*x^2 + a₃*x^3 + a₄*x^4 + a₅*x^5julia> @syms x u() v()::nonnegative
(x, u, v)
julia> sqrt(u(x)^2), sqrt(v(x)^2) # sqrt(u(x)^2), Abs(v(x))
(sqrt(u(x)^2), Abs(v(x)))!!! Note: Many thanks to @matthieubulte for this contribution.
SymPy.@vars — Macro@vars x y zDefine symbolic values, possibly with names and assumptions
Examples:
@vars x y
@vars a1=>"α₁"
@vars a b real=true!!! Note: The @vars macro is deprecated and will be removed. Use @syms.
SymPy.Differential — TypeDifferential(x)Use to find (partial) derivatives.
Example
@syms x y u()
Dx = Differential(x)
Dx(u(x,y)) # resolves to diff(u(x,y),x)
Dx(u) # will evaluate diff(u(x), x)SymPy.Doc — TypeSymPy.Doc(f::Symbol, [module=sympy])Return docstring of f found within the specified module.
Examples
SymPy.Doc(:sin)
SymPy.Doc(:det, sympy.matrices)
## add module to query
SymPy.pyimport_conda("sympy.crypto.crypto", "sympy")
SymPy.Doc(:padded_key, sympy.crypto)SymPy.SymFunction — TypeSymFunctionA type and constructor to create symbolic functions. Such objects can be used for specifying differential equations. The macro @syms is also available for constructing SymFunctions (@syms f())
Examples:
julia> using SymPy
julia> u = SymFunction("u");
julia> @syms v();
Alternatively, we can pass a comma separated string of variable names to create more than one at a time.
julia> F,G,H = SymFunction("F, G, H")
3-element Vector{SymFunction}:
F
G
HFor symbolic functions not wrapped in the SymFunction type, the sympy.Function constructor can be used, as can the symbols function to construct symbolic functions (F=sympy.Function("F", real=true); F = sympy.symbols("F", cls=sympy.Function, real=true)).
julia> @syms u(), v()::real, t
(u, v, t)
julia> sqrt(u(t)^2), sqrt(v(t)^2) # real values have different simplification rules
(sqrt(u(t)^2), Abs(v(t)))
Such functions are undefined functions in SymPy, and can be used symbolically, such as with taking derivatives:
julia> @syms x y u()
(x, y, u)
julia> diff(u(x), x) |> string
"Derivative(u(x), x)"
julia> diff(u(x, y), x) |> string
"Derivative(u(x, y), x)"Here is one way to find the second derivative of an inverse function to f, utilizing the SymFunction class and the convenience Differential function:
@syms f() f⁻¹() x
D = Differential(x) # ∂(f) is diff(f(x),x)
D² = D∘D
u1 = solve(diff((f⁻¹∘f)(x), x) ~ 1, D(f⁻¹)(f(x)))[1]
u2 = solve(diff((f⁻¹∘f)(x), x,2) ~ 0, D²(f⁻¹)(f(x)))[1]
u2(D(f⁻¹)(f(x)) => u1) # f''/[f']^3SymPy.SymMatrix — TypeSymMatrixType to store a SymPy matrix, as created by sympy.ImmutableMatrix.
These have 0-based indexing defined for them to match SymPy
The traditional infix mathmatical operations are defined, but no dot broadcasting.
The convert(Matrix{Sym}, M) call is useful to covert to a Julia matrix
SymPy.VectorField — TypeVectorField(fx, fy): create an object that can be plotted as a vector field.
A vectorfield plot draws arrows at grid points proportional to [fx(x_i,y_i), fy(x_i,y_i)] to visualize the field generated by [fx, fy].
The plot command: plot(VectorField(fx, fy), xlims=(-5,5), ylims=(-5,5), n=8) will draw the vectorfield. This uses the default values, so the same graph would be rendered by plot(VectorField(fx,fy)).
To faciliate the visualization of solution to the ODE y' = F(x, y(x)), the call plot(VectorField(F)) will work. (The order is x then y, though often this is written as F(y(x),x).)
SymPy objects can be passed to VectorField, but this is a bit fragile, as they must each have two variables so that they can be called with two variables. (E.g., y(1,2) will be 1 not 2, as might be intended.)
Examples:
using Plots
fx(x,y) = sin(y); fy(x,y) = cos(y)
plot(VectorField(fx, fy), xlims=(-2pi, 2pi), ylims=(-2pi,2pi))
# plot field of y' = 3y*x over (-5,5) x (-5,5)
F(x,y) = 3*y*x
plot(VectorField(F))
# plot field and solution u' = u*(1-u)
@syms x u()
F(x,y) = y*(1-y)
out = dsolve(u'(x) - F(x, u(x)), x, (u, 0, 1))
plot(VectorField(F), xlims=(0,5), ylims=(0,2))
plot!(rhs(out))SymPy.SymPy — ModuleSymPy package to interface with Python's SymPy library through PyCall.
The basic idea is that a new type – Sym – is made to hold symbolic objects. For this type, the basic functions from SymPy and appropriate functions of Julia are overloaded for Sym objects so that the expressions are treated symbolically and not evaluated immediately. Instances of this type are created by the constructor Sym, the function symbols or the macro @vars.
On loading, a priviledged set of the functions from the sympy module are defined as generic functions with their first argument narrowed to symbolic types. Others may be accessed by qualification, as in sympy.trigsimp. Calling import_from(sympy) will import the rest. SymPy methods are called through Python's dot-call syntax. To find documentation on SymPy functions and methods, one should refer to SymPy's website.
Plotting is provided through Plots recipes. For details, see the help page for sympy_plotting.
The package documentation provides many examples.
Access to SymPy's help system for most functions is available through SymPy.Doc.
SymPy.𝑄 — Module𝑄
SymPy.QTheSymPy.𝑄 module adds features of the sympy.Q module. Also accesible through SymPy.Q.
SymPy allows for assumptions on variables. These may be placed on free sympols at construction.
For example, the following creates a real value variable x and a postive, real variable y:
julia> using SymPy
julia> @vars x real=true
(x,)
julia> @vars y real=true positive=true
(y,)The 𝑄 module exposes a means to query the assumptions on a variable. For example,
julia> ask(𝑄.positive(y)) # true
true
julia> ask(𝑄.negative(y)) # false
false
julia> ask(SymPy.Q.positive(x)) # `nothing`
julia> ask(SymPy.Q.positive(x^2)) # `nothing` -- might be 0
julia> ask(SymPy.Q.positive(1 + x^2)) # true -- must be postive now.
trueThe ask function uses tri-state logic, returning one of 3 values: true; false; or nothing, when the query is indeterminate.
The construction of predicates is done through Q methods. These can be combined logically. For example, this will be true:
julia> ask(𝑄.positive(y) & 𝑄.negative(-x^2 - 1))
The above use & as an infix operation for the binary operator And. Values can also be combined with Or, Not, Xor, Nand, Nor, Implies, Equivalent, and satisfiable.
𝑄 is entered as [slash]itQ[tab]) or SymPy.Q.query(value) but not as sympy.Q.query(value)
As SymPy.jl converts symbolic matrices into Julia's Array
type and not as matrices within Python, the predicate functions from SymPy for matrices are not used, though a replacement is given.
SymPy.Introspection.args — Methodargs(x)Return arguments of x, as a tuple. (Empty if no :args property.)
SymPy.Introspection.func — Methodfunc(x)
Return function head from an expression
Every well-formed SymPy expression ex must either have length(args(ex)) == 0 or func(ex)(args(ex)...) = ex.
SymPy.Introspection.funcname — Methodfuncname(x)Return name or ""