Reference/API
SymPy.SymPy
SymPy.𝑄
SymPy.False
SymPy.IM
SymPy.PI
SymPy.True
SymPy.oo
SymPy.sympy
SymPy.sympy_core
SymPy.sympy_matrices
SymPy.sympy_plotting
SymPy.zoo
SymPy.Differential
SymPy.Doc
SymPy.SymFunction
SymPy.SymMatrix
SymPy.VectorField
Base.:~
Base.getindex
Base.getindex
Base.match
Base.occursin
Base.replace
CommonSolve.solve
SymPy.Introspection.args
SymPy.Introspection.func
SymPy.Introspection.funcname
SymPy.N
SymPy.N
SymPy.Permutation
SymPy.PermutationGroup
SymPy.Wild
SymPy.ask
SymPy.doit
SymPy.dsolve
SymPy.elements
SymPy.free_symbols
SymPy.import_from
SymPy.import_sympy
SymPy.jprint
SymPy.lambdify
SymPy.nonlinsolve
SymPy.refine
SymPy.simplify
SymPy.subs
SymPy.symbols
SymPy.walk_expression
SymPy.@symfuns
SymPy.@syms
SymPy.@vars
Base.:~
— Methodlhs ~ rhs
Specify 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 = 1
Base.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
func
on the head of an expression:func(sin(x))
->sin
, or directly throughsympy.sin
functions are supported, but only with
PyCall
commands.
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
— Methodsolve
Use 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
evalf
call - one to convert these expressions back into
Julia
objects (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.35247154603172626im
N
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 recoverj
wherei -> 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 whenp
andq
commute, where a check on commuting is performed.inv
for the inverse permutation./
, wherep/q
isp * inv(q)
.p^n
for powers. We haveinv(p) = p^(-1)
andp^order(p)
is the identity.p^q
for 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
7
SymPy.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⋅x
Set 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⋅α
dx
julia> dsolve(eqn)
2
x ⋅α
f(x) = C₁ + ────
2
julia> 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
y
SymPy.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 assympy
meths
: 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,:SymbolicObject
will 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_sympy
This 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::Sym
a symbolic expression with 0, 1, or more free symbolsvars
a container of symbols to use for the function arguments. The default isfree_symbols
which has a specific ordering. Specifyingvars
allows this default ordering of arguments to be customized. Ifvars
is 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 expressionex
and creates the corresponding AST for a Julia expression. SeeSymPy.fn_map
andSymPy.val_map
for the default mappings of sympy functions and values intoJulia
's AST.use_julia_code::Bool
: use SymPy's conversion to an expression, the default isfalse
invoke_latest=true
: iftrue
will calleval
andBase.invokelatest
to return a function that should not have any world age issue. Iffalse
will return a Julia expression that can beeval
ed 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))
end
This function will be about 2-3 times slower than f
.
SymPy.nonlinsolve
— Methodnonlinsolve
Note: if passing variables in use a tuple (e.g., (x,y)
) and not a vector (e.g., [x,y]
).
SymPy.refine
— Method refine
Simplify an expression using assumptions; refine.
SymPy.simplify
— Methodsimplify
SymPy 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)
-5
SymPy.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) * y
Calling 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.6015187080185656
SymPy.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
— Constantsympy
Variable from pyimport("sympy")
. Numerous methods are available through Python's dot-call syntax.
SymPy.sympy_core
— Constantsympy_core
Variable from pyimport("sympy.core")
.
SymPy.sympy_matrices
— Constantsympy_matrices
Variable 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 evaluatingex
over [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) ## also
For 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 3d
plot(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_surface
sympy.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 circle
SymPy.zoo
— Constantzoo complex inifinity
SymPy.@symfuns
— Macro@symfuns
Thanks 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]::real
Construct 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::integer
is 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^5
julia> @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 z
Define 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
— TypeSymFunction
A type and constructor to create symbolic functions. Such objects can be used for specifying differential equations. The macro @syms
is also available for constructing SymFunction
s (@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
H
For 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']^3
SymPy.SymMatrix
— TypeSymMatrix
Type 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 plot
ted 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.Q
TheSymPy.𝑄
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.
true
The 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 ""