SymPyCore.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)
- 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))
SymPyCore.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)
(Dx^2)(u(x)) # two derivatives in x; not () to avoid 2u(x) eager evaluation
SymPyCore.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)
SymPyCore.Introspection
— Type Introspection
Struct holding functions used to inspect an object
Introspection.func
: Return pointer to Python function.Introspection.operation
: ReturnJulia
generic function for given underlying functionIntrospection.funcname
: Returns name of functionIntrospection.args
: Returns arguments for expression or empty tupleIntrospection.arguments
: Return argumentsIntrospection.iscall
: Check if object is an expression (with operation and arguments) or notIntrospection.class
: Returns__class__
valueIntrospection.classname
: Returns__class__
value as a stringIntrospection.similarterm
: Create a similar term
As args
returns symbolic objects, this becomes: every well-formed SymPy expression ex
must either have length(args(ex)) == 0
or func(ex)(↓(args(ex))...) = ex
.
Using the methods designed for SymbolicUtils
usage, this becomes every expression one of !iscall(ex)
or operation(ex)(args(ex)...) == ex
should hold.
SymPyCore.Sym
— TypeSym{T}
Main wrapper for symbolic Python objects.
This is useful for dispatching methods for generic functions. Sym
is also used to make symbolic values, in particular numeric values can be made into symbolic values.
SymPyCore.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 SymPyPythonCall
julia> @syms t, v(); # recommended way to create a symbolic function
julia> u = SymFunction("u") # alternate
u
julia> diff(v(t), t) |> show
Derivative(v(t), t)
Extended help
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) |> show
Derivative(u(x), x)
julia> diff(u(x, y), x) |> show
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:
julia> @syms f() f⁻¹() x;
julia> D = Differential(x) # ∂(f) is diff(f(x),x)
Differential(x)
julia> D² = D∘D
Differential(x) ∘ Differential(x)
julia> u1 = only(solve(D((f⁻¹∘f)(x)) ~ 1, D(f⁻¹)(f(x)))); show(u1)
1/Derivative(f(x), x)
julia> u2 = only(solve(D²((f⁻¹∘f)(x)) ~ 0, D²(f⁻¹)(f(x)))); show(u2)
-Derivative(f(x), (x, 2))*Derivative(f⁻¹(f(x)), f(x))/Derivative(f(x), x)^2
julia> u2(D(f⁻¹)(f(x)) => u1) |> show # f''/[f']^3
-Derivative(f(x), (x, 2))/Derivative(f(x), x)^3
SymPyCore.SymbolicCallable
— TypeSymbolicCallable
Wrapper for python objects with a __call__
method. This is used by sympy.λ
to call the underlying λ
function without the user needing to manually convert Julia
objects into Python
objects and back.
There are some times where this doesn't work well, and using sympy.o.λ
along with ↓
and ↑
will work.
Base.:~
— Methodlhs ~ rhs
Specify an equation.
Alternative syntax to Eq(lhs, rhs)
or lhs ⩵ rhs
(\Equal[tab]
). Notation borrowed from Symbolics.jl
.
See rhs
or lhs
to extract the two sides.
Inequalities may be defined using other functions imported from CommonEq
.
Base.match
— Methodmatch(pattern, expression, ...)
Match a pattern against an expression; returns a dictionary of matches.
If a match is unsuccessful, 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.replace
— Methodreplace(expression, pattern, value, ...)
replace(expression, pattern => value; kwargs...)
In the expression replace a matching 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 only supported by calling into the glue package.
Examples (from the SymPy docs)
julia> using SymPyPythonCall
julia> @syms x, y, z
(x, y, z)
julia> f = log(sin(x)) + tan(sin(x^2)); show(f)
log(sin(x)) + tan(sin(x^2))
"type" -> "type"
Types are specified through func
:
julia> func = Introspection.func
#399 (generic function with 1 method)
julia> replace(f, func(sin(x)), func(cos(x))) |> show # type -> type
log(cos(x)) + tan(cos(x^2))
The value sympy.sin
does not work, as it is wrapped. Using ↓(sympy).sin
will work:
julia> replace(f, ↓(sympy).sin, ↓(sympy).cos)
log(cos(x)) + tan(cos(x^2))
"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)) |> show
log(tan(2*x)) + tan(tan(2*x^2))
julia> replace(f, sin(a), tan(a/2)) |> show
log(tan(x/2)) + tan(tan(x^2/2))
julia> f.replace(sin(a), a) |> show
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) |> show
y + 2/x
"type" -> "function"
To replace with a more complicated function, requires some assistance from Python
, as an anonymous function must be defined within Python, not Julia
. This is how it might be done:
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" -> "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
— Functionsolve
Use solve
to solve algebraic equations.
Extended help
Examples:
julia> using SymPyPythonCall
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{PythonCall.Py}}:
-1
julia> solve(x^2 + 2a*x + a^2, x) # [-a]
1-element Vector{Sym{PythonCall.Py}}:
-a
julia> u = solve([a*x + b*y-3, c*x + b*y - 1], [x,y]); show(u[x])
2/(a - c)
A very nice example using solve
is a blog entry on Napoleon's theorem by Xing Shi Cai.
Use a tuple, not a vector, of equations when there is more than one.
SymPyCore.:↑
— Method↑(::SymbolicObject)
Method to lift a python object into a symbolic counterpart.
SymPyCore.:↓
— Method↓(::SymbolicObject) ↓ₖ([kwargs...])
The \downarrrow[tab]
and \downarrow[tab]\_k[tab]
operators push a symbolic object (or a container of symbolic objects) into a Python counterpart for passing to an underlying Python function.
SymPyCore.Heaviside
— MethodHeaviside
SymPyCore.Permutation
— FunctionPermutation
PermutationGroup
Give access to the sympy.combinatorics.permutations
module
Example
julia> using SymPyPythonCall
julia> p = Permutation([1,2,3,0])
(0 1 2 3)
julia> p^2
(0 2)(1 3)
julia> p^2 * p^2
()
Rubik's cube example from SymPy documentation
julia> F = Permutation([(2, 19, 21, 8),(3, 17, 20, 10),(4, 6, 7, 5)])
(2 19 21 8)(3 17 20 10)(4 6 7 5)
julia> R = Permutation([(1, 5, 21, 14),(3, 7, 23, 12),(8, 10, 11, 9)])
(1 5 21 14)(3 7 23 12)(8 10 11 9)
julia> D = Permutation([(6, 18, 14, 10),(7, 19, 15, 11),(20, 22, 23, 21)])
(6 18 14 10)(7 19 15 11)(20 22 23 21)
julia> G = PermutationGroup(F,R,D);
julia> G.order()
3674160
SymPyCore.PermutationGroup
— FunctionPermutation
PermutationGroup
Give access to the sympy.combinatorics.permutations
module
Example
julia> using SymPyPythonCall
julia> p = Permutation([1,2,3,0])
(0 1 2 3)
julia> p^2
(0 2)(1 3)
julia> p^2 * p^2
()
Rubik's cube example from SymPy documentation
julia> F = Permutation([(2, 19, 21, 8),(3, 17, 20, 10),(4, 6, 7, 5)])
(2 19 21 8)(3 17 20 10)(4 6 7 5)
julia> R = Permutation([(1, 5, 21, 14),(3, 7, 23, 12),(8, 10, 11, 9)])
(1 5 21 14)(3 7 23 12)(8 10 11 9)
julia> D = Permutation([(6, 18, 14, 10),(7, 19, 15, 11),(20, 22, 23, 21)])
(6 18 14 10)(7 19 15 11)(20 22 23 21)
julia> G = PermutationGroup(F,R,D);
julia> G.order()
3674160
SymPyCore.Wild
— MethodWild(x)
Create a "wild card" for pattern matching
SymPyCore.apart
— Methodapart
Partial fraction decomposition. See together
. SymPy documentation
SymPyCore.ask
— MethodSymPyCore.cancel
— Methodcancel
Take any rational expression and put it into the standard canonical form, $p/q$. SymPy documentation
SymPyCore.degree
— MethodSymPyCore.doit
— Methoddoit
Evaluates objects that are not evaluated by default. Alias for object method.
Extended help
Examples:
julia> using SymPyPythonCall
julia> @syms x f()
(x, f)
julia> D = Differential(x)
Differential(x)
julia> df = D(f(x)); show(df)
Derivative(f(x), x)
julia> dfx = subs(df, (f(x), x^2)); show(dfx)
Derivative(x^2, x)
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); show(dgfx)
g(Derivative(x^2, x))
julia> doit(dgfx) |> show
g(Derivative(x^2, x))
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)
SymPyCore.dsolve
— Methoddsolve(eqn, var, args..,; ics=nothing, kwargs...)
Calls sympy.dsolve
.
ics: The initial conditions are specified with a dictionary or nothing
Extended help
Example:
julia> using SymPyPythonCall
julia> @syms α, x, f(), g()
(α, x, f, g)
julia> ∂ = Differential(x)
Differential(x)
julia> eqn = ∂(f(x)) ~ α * x; show(eqn)
Eq(Derivative(f(x), x), x*α)
julia> dsolve(eqn) |> show
Eq(f(x), C1 + x^2*α/2)
julia> dsolve(eqn(α=>2); ics=Dict(f(0)=>1))
2
f(x) = x + 1
julia> eqn = ∂(∂(f(x))) ~ -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))) |> show
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)))
Use a tuple, not a vector, of equations when there is more than one.
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));
julia> dsolve(eqns)
2-element Vector{Sym{PythonCall.Py}}:
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{PythonCall.Py}}:
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{PythonCall.Py}}:
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));
julia> dsolve(eq)
Set{Sym{PythonCall.Py}} with 2 elements:
Eq(x(t), -exp(C1)/(C2*exp(C1) - cos(t)))
Eq(y(t), -1/(C1 - cos(t)))
SymPyCore.expand
— MethodSymPyCore.factor
— Methodfactor
Factor an expression. See expand
. SymPy documentation
SymPyCore.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 SymPyPythonCall
julia> @syms x y z a
(x, y, z, a)
julia> free_symbols(2*x + a*y) # [a, x, y]
3-element Vector{Sym{PythonCall.Py}}:
a
x
y
julia> free_symbols([x^2, x^2 - 2x*y + y^2])
2-element Vector{Sym{PythonCall.Py}}:
x
y
SymPyCore.funcname
— FunctionIntrospection.funcname(x)
Return name or ""
SymPyCore.integrate
— MethodSymPyCore.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 SymPyPythonCall
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
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> @syms x
(x,)
julia> f(x) = exp(cot(x))
f (generic function with 1 method)
julia> g1 = lambdify(f(x));
julia> ex = lambdify(f(x), invoke_latest=false);
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
.
SymPyCore.lhs
— Functionrhs(eqn)
lhs(eqn)
Returns right (or left) side of an equation object. Wrappers around eqn.rhs()
and eqn.lhs()
.
SymPyCore.linsolve
— Methodlinsolve
SymPyCore.nonlinsolve
— Methodnonlinsolve
Use a tuple, not a vector, of equations when there is more than one.
SymPyCore.nroots
— Methodnroots
SymPyCore.nsolve
— Methodnsolve
SymPyCore.real_roots
— Methodreal_roots
SymPyCore.refine
— Methodrefine
SymPyCore.rhs
— Functionrhs(eqn)
lhs(eqn)
Returns right (or left) side of an equation object. Wrappers around eqn.rhs()
and eqn.lhs()
.
SymPyCore.roots
— Methodroots
Find roots of a polynomial. Not exported, so needs to be qualified, as in sympy.roots
.
Example
julia> sympy.roots(x^2 - 2x - 3)
Dict{} with 2 entries:
3 => 1
-1 => 1
SymPyCore.series
— Methodseries
SymPyCore.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). Other simplification functions are available through the sympy
object.
For non-symbolic expressions, simplify
returns its first argument.
SymPyCore.simplify
— MethodSymPyCore.solveset
— Methodsolveset
Like solve
but returns a set object. Finite sets are returned as Set
objects in Julia
. Infinite sets must be queried.
Example
julia> @syms x
(x,)
julia> u = solveset(sin(x) ~ 1//2, x)
⎧ 5⋅π │ ⎫ ⎧ π │ ⎫
⎨2⋅n⋅π + ─── │ n ∊ ℤ⎬ ∪ ⎨2⋅n⋅π + ─ │ n ∊ ℤ⎬
⎩ 6 │ ⎭ ⎩ 6 │ ⎭
julia> intersect(u, sympy.Interval(0, 2PI))
Set{Sym} with 2 elements:
pi/6
5*pi/6
SymPyCore.subs
— Methodsubs
is used to substitute a value in an expression with another value. Examples:
julia> using SymPyPythonCall
julia> @syms x,y
(x, y)
julia> ex = (x-y)*(x+2y)
(x - y)⋅(x + 2⋅y)
julia> subs(ex, (y, y^2)) |> show
(x - y^2)*(x + 2*y^2)
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
SymPyCore.summation
— Methodsummation
SymPyCore.symbols
— Methodsymbols(arg; kwargs...)
Construct symbolic values using sympy.symbols
.
SymPyCore.together
— MethodSymPyCore.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.
Extended help
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