Skip to content

Commit c63420f

Browse files
authored
Merge pull request #277 from symengine/terminterface
update extensions
2 parents 21f8f55 + c6f2118 commit c63420f

File tree

4 files changed

+98
-70
lines changed

4 files changed

+98
-70
lines changed

Project.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,25 @@ SymEngine_jll = "3428059b-622b-5399-b16f-d347a77089a4"
1313

1414
[weakdeps]
1515
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
16+
TermInterface = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c"
1617

1718
[extensions]
1819
SymEngineSymbolicUtilsExt = "SymbolicUtils"
20+
SymEngineTermInterfaceExt = "TermInterface"
1921

2022
[compat]
2123
Compat = "0.63.0, 1, 2, 3, 4"
2224
RecipesBase = "0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0"
2325
SpecialFunctions = "0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.10, 1, 2"
2426
SymEngine_jll = "0.9, 0.10, 0.11, 0.12"
25-
SymbolicUtils = "1.4"
27+
SymbolicUtils = "3"
28+
TermInterface = "2"
2629
julia = "1.6"
2730

2831
[extras]
2932
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3033
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
34+
TermInterface = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c"
3135

3236
[targets]
33-
test = ["SymbolicUtils", "Test"]
37+
test = ["SymbolicUtils", "TermInterface", "Test"]

ext/SymEngineSymbolicUtilsExt.jl

Lines changed: 23 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,79 +2,31 @@ module SymEngineSymbolicUtilsExt
22

33
using SymEngine
44
using SymbolicUtils
5-
import SymEngine: SymbolicType
6-
7-
#
8-
function is_number(a::SymEngine.Basic)
9-
cls = SymEngine.get_symengine_class(a)
10-
any(==(cls), SymEngine.number_types) && return true
11-
false
12-
end
13-
14-
15-
λ(x::SymEngine.SymbolicType) = λ(Val(SymEngine.get_symengine_class(x)))
16-
λ(::Val{T}) where {T} = getfield(Main, Symbol(lowercase(string(T))))
17-
18-
λ(::Val{:Add}) = +; λ(::Val{:Sub}) = -
19-
λ(::Val{:Mul}) = *; λ(::Val{:Div}) = /
20-
λ(::Val{:Pow}) = ^
21-
λ(::Val{:re}) = real; λ(::Val{:im}) = imag
22-
λ(::Val{:Abs}) = abs
23-
λ(::Val{:Log}) = log
24-
λ(::Val{:Sin}) = sin; λ(::Val{:Cos}) = cos; λ(::Val{:Tan}) = tan
25-
λ(::Val{:Csc}) = csc; λ(::Val{:Sec}) = sec; λ(::Val{:Cot}) = cot
26-
λ(::Val{:Asin}) = asin; λ(::Val{:Acos}) = acos; λ(::Val{:Atan}) = atan
27-
λ(::Val{:Acsc}) = acsc; λ(::Val{:Asec}) = asec; λ(::Val{:Acot}) = acot
28-
λ(::Val{:Sinh}) = sinh; λ(::Val{:Cosh}) = cosh; λ(::Val{:Tanh}) = tanh
29-
λ(::Val{:Csch}) = csch; λ(::Val{:Sech}) = sech; λ(::Val{:Coth}) = coth
30-
λ(::Val{:Asinh}) = asinh; λ(::Val{:Acosh}) = acosh; λ(::Val{:Atanh}) = atanh
31-
λ(::Val{:Acsch}) = acsch; λ(::Val{:Asech}) = asech; λ(::Val{:Acoth}) = acoth
32-
λ(::Val{:Gamma}) = gamma; λ(::Val{:Zeta}) = zeta; λ(::Val{:LambertW}) = lambertw
33-
34-
#==
35-
Check if x represents an expression tree. If returns true, it will be assumed that operation(::T) and arguments(::T) methods are defined. Definining these three should allow use of SymbolicUtils.simplify on custom types. Optionally symtype(x) can be defined to return the expected type of the symbolic expression.
36-
==#
37-
function SymbolicUtils.istree(x::SymEngine.SymbolicType)
38-
cls = SymEngine.get_symengine_class(x)
39-
cls == :Symbol && return false
40-
cls == :Constant && return false
41-
any(==(cls), SymEngine.number_types) && return false
42-
return true
43-
end
44-
45-
SymbolicUtils.issym(x::SymEngine.SymbolicType) = SymEngine.get_symengine_class(x) == :Symbol
46-
Base.nameof(x::SymEngine.SymbolicType) = Symbol(x)
47-
48-
# no metadata(x), metadata(x, data)
49-
50-
#==
51-
Returns the head (a function object) performed by an expression tree. Called only if istree(::T) is true. Part of the API required for simplify to work. Other required methods are arguments and istree
52-
==#
53-
function SymbolicUtils.operation(x::SymEngine.SymbolicType)
54-
istree(x) || error("$(typeof(x)) doesn't have an operation!")
55-
return λ(x)
56-
end
57-
58-
59-
#==
60-
Returns the arguments (a Vector) for an expression tree. Called only if istree(x) is true. Part of the API required for simplify to work. Other required methods are operation and istree
61-
==#
62-
function SymbolicUtils.arguments(x::SymEngine.SymbolicType)
63-
get_args(x)
64-
end
65-
66-
#==
67-
Construct a new term with the operation f and arguments args, the term should be similar to t in type. if t is a SymbolicUtils.Term object a new Term is created with the same symtype as t. If not, the result is computed as f(args...). Defining this method for your term type will reduce any performance loss in performing f(args...) (esp. the splatting, and redundant type computation). T is the symtype of the output term. You can use SymbolicUtils.promote_symtype to infer this type. The exprhead keyword argument is useful when creating Exprs.
68-
==#
69-
function SymbolicUtils.similarterm(t::SymEngine.SymbolicType, f, args, symtype=nothing;
70-
metadata=nothing, exprhead=:call)
71-
f(args...) # default
72-
end
735

746
# Needed for some simplification routines
757
# a total order <ₑ
768
import SymbolicUtils: <ₑ, isterm, isadd, ismul, issym, get_degrees, monomial_lt, _arglen
9+
10+
#Base.nameof(x::SymEngine.SymbolicType) = Symbol(x)
11+
#
12+
#function is_number(a::SymEngine.Basic)
13+
# cls = SymEngine.get_symengine_class(a)
14+
# any(==(cls), SymEngine.number_types) && return true
15+
# false
16+
#end
17+
18+
7719
function SymbolicUtils.:<(a::SymEngine.Basic, b::SymEngine.Basic)
20+
if !SymbolicUtils.iscall(a) || !SymbolicUtils.iscall(a)
21+
if !SymbolicUtils.iscall(a) && !SymbolicUtils.iscall(b)
22+
return <(Symbol(a), Symbol(b))
23+
elseif SymbolicUtils.iscall(a) && !SymbolicUtils.iscall(b)
24+
return false
25+
elseif !SymbolicUtils.iscall(a) && SymbolicUtils.iscall(b)
26+
return true
27+
end
28+
end
29+
7830
da, db = get_degrees(a), get_degrees(b)
7931
fw = monomial_lt(da, db)
8032
bw = monomial_lt(db, da)
@@ -89,4 +41,7 @@ function SymbolicUtils.:<ₑ(a::SymEngine.Basic, b::SymEngine.Basic)
8941
end
9042
end
9143

44+
Base.isless(x::Number, y::SymEngine.Basic) = isless(promote(x,y)...)
45+
Base.isless(x::SymEngine.Basic,y::Number) = isless(promote(x,y)...)
46+
9247
end

ext/SymEngineTermInterfaceExt.jl

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
module SymEngineTermInterfaceExt
2+
3+
import SymEngine
4+
import SymEngine: SymbolicType
5+
import TermInterface
6+
7+
8+
λ(x::SymEngine.SymbolicType) = λ(Val(SymEngine.get_symengine_class(x)))
9+
λ(::Val{T}) where {T} = getfield(Main, Symbol(lowercase(string(T))))
10+
11+
λ(::Val{:Add}) = +; λ(::Val{:Sub}) = -
12+
λ(::Val{:Mul}) = *; λ(::Val{:Div}) = /
13+
λ(::Val{:Pow}) = ^
14+
λ(::Val{:re}) = real; λ(::Val{:im}) = imag
15+
λ(::Val{:Abs}) = abs
16+
λ(::Val{:Log}) = log
17+
λ(::Val{:Sin}) = sin; λ(::Val{:Cos}) = cos; λ(::Val{:Tan}) = tan
18+
λ(::Val{:Csc}) = csc; λ(::Val{:Sec}) = sec; λ(::Val{:Cot}) = cot
19+
λ(::Val{:Asin}) = asin; λ(::Val{:Acos}) = acos; λ(::Val{:Atan}) = atan
20+
λ(::Val{:Acsc}) = acsc; λ(::Val{:Asec}) = asec; λ(::Val{:Acot}) = acot
21+
λ(::Val{:Sinh}) = sinh; λ(::Val{:Cosh}) = cosh; λ(::Val{:Tanh}) = tanh
22+
λ(::Val{:Csch}) = csch; λ(::Val{:Sech}) = sech; λ(::Val{:Coth}) = coth
23+
λ(::Val{:Asinh}) = asinh; λ(::Val{:Acosh}) = acosh; λ(::Val{:Atanh}) = atanh
24+
λ(::Val{:Acsch}) = acsch; λ(::Val{:Asech}) = asech; λ(::Val{:Acoth}) = acoth
25+
λ(::Val{:Gamma}) = gamma; λ(::Val{:Zeta}) = zeta; λ(::Val{:LambertW}) = lambertw
26+
27+
#==
28+
Check if x represents an expression tree. If returns true, it will be assumed that operation(::T) and arguments(::T) methods are defined. Definining these three should allow use of SymbolicUtils.simplify on custom types. Optionally symtype(x) can be defined to return the expected type of the symbolic expression.
29+
==#
30+
function TermInterface.iscall(x::SymEngine.SymbolicType)
31+
cls = SymEngine.get_symengine_class(x)
32+
cls == :Symbol && return false
33+
cls == :Constant && return false
34+
any(==(cls), SymEngine.number_types) && return false
35+
return true
36+
end
37+
TermInterface.isexpr(x::SymEngine.SymbolicType) = TermInterface.iscall(x)
38+
39+
##TermInterface.issym(x::SymEngine.SymbolicType) = SymEngine.get_symengine_class(x) == :Symbol
40+
41+
function TermInterface.operation(x::SymEngine.SymbolicType)
42+
TermInterface.iscall(x) || error("$(typeof(x)) doesn't have an operation!")
43+
return λ(x)
44+
end
45+
46+
function TermInterface.arguments(x::SymEngine.SymbolicType)
47+
SymEngine.get_args(x)
48+
end
49+
50+
TermInterface.head(x::SymEngine.SymbolicType) = TermInterface.operation(x)
51+
TermInterface.children(x::SymEngine.SymbolicType) = TermInterface.arguments(x)
52+
53+
function TermInterface.maketerm(t::Type{<:SymEngine.SymbolicType}, f, args,
54+
metadata=nothing)
55+
f(args...) # default
56+
end
57+
58+
59+
# no metadata(x), metadata(x, data)
60+
61+
end

test/test-SymbolicUtils.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ using Test
22
using SymEngine
33
import SymbolicUtils: simplify, @rule, @acrule, Chain, Fixpoint
44

5+
import TermInterface
6+
@testset "TermInterface" begin
7+
@vars x
8+
@test !TermInterface.iscall(x)
9+
@test TermInterface.iscall(x^2)
10+
@test TermInterface.operation(sin(x)) == sin
11+
@test TermInterface.arguments(sin(x)) == [x]
12+
end
513

614
@testset "SymbolicUtils" begin
715
# from SymbolicUtils.jl docs

0 commit comments

Comments
 (0)