/
julia_level.jl
187 lines (155 loc) · 6.7 KB
/
julia_level.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# This is for testing using Symata code from Julia
## These just call apprules, so they are in general no more efficient than calling from Symata directly.
## In contrast, the trig functions below filter numeric arguments and are exactly as efficient as pure Julia
## when called from within Julia functions for which the compiler can infer types.
## Similar filters could be written for the functions wrapped here.
##
for f in (:Expand, :Factor, :Head, :Take, :Simplify, :Integrate, :DirichletEta, :Times, :List, :Plus, :Exp, :Power, :Length)
@eval ($f)(mx...) = apprules(mxpr($(QuoteNode(f)),mx...))
@eval export $f
end
## Use Julia inference and dispatch to call numeric functions directly with no overhead when possible.
## TODO: Handle two arg functions, etc.
for f in (:cos, :sin, :abs, :tan, :exp, :log, (:acos, :ArcCos) , (:asin, :ArcSin), (:atan, :ArcTan ),
:cot, :cosh, :sinh, :tanh, :sqrt, :erf, :erfc, :gamma, :zeta)
local uf
if isa(f, Tuple)
uf = f[2]
f = f[1]
else
uf = Symbol(ucfirst(string(f)))
end
@eval ($uf)(x::AbstractFloat) = ($f)(x)
@eval ($uf){T<:AbstractFloat}(x::AbstractArray{T}) = ($f)(x)
@eval ($uf){T<:AbstractFloat}(x::Complex{T}) = ($f)(x)
@eval ($uf)(x) = apprules(mxpr($(QuoteNode(uf)),x))
@eval export $uf
end
# Integrate(mx::Mxpr,symorlist) = apprules(mxpr(:Integrate,mx,symorlist))
# export Integrate
const Pi = :Pi
export Pi
const E = e # could make this the symbol ?
export E
#### Arithmetic operators
## methods for Julia math functions that operate
## on symbols and Mxpr. Some of these are used
## in Symata code. They can also be used at the Julia
## repl.
# SJSym is an alias of Symbol
"""
_symatamath()
define math methods in the Symata module that operate on symbols. This allows Julia expressions such as
```
:a + :b
:a - 3
:c^2
```
Note: we now call syamtamath() after its definition. It defines these operators in the Symata module scope.
obsolete: These methods are disabled by default because they extend `Base` methods for `Base` types. These methods are
in general reserved for definition in future versions of Julia. There is some chance that these will be given conflicting
definitions in the Julia base language in the future. However, we know of no such plans at present.
These are all binary methods between Julia `Number`s and `Symbol`s extending `*`, `+`, `-`, and `^`.
There are similar methods for arithmetic operators between numbers or symbols and Symata expressions, but
these are defined by default.
"""
function _symatamath()
@eval begin
*(a::Number,b::SJSym) = mxpr(:Times,a,b) # why not mmul ? TODO: try using mmul, etc. here
*(a::SJSym,b::SJSym) = mxpr(:Times,a,b)
*(a::SJSym,b::Number) = mxpr(:Times,b,a)
+(a::SJSym,b::Number) = mxpr(:Plus,b,a)
+(a::SJSym,b::SJSym) = mxpr(:Plus,a,b)
+(a::Number,b::SJSym) = mxpr(:Plus,a,b)
-(a::Number,b::SJSym) = mplus(a, mxpr(:Times,-1,b))
-(a::SJSym,b::Number) = mplus(a, -b)
-(a::SJSym,b::SJSym) = mplus(a, mxpr(:Times,-1,b))
/(a::SJSym,b::SJSym) = mmul(a, mmul(-1,b))
/(a::Number,b::SJSym) = mmul(a, mmul(-1,b))
/(a::SJSym,b::Number) = mmul(a, mmul(-1,b))
^(base::SJSym,expt::Integer) = mxpr(:Power,base,expt)
^(base::SJSym,expt) = mxpr(:Power,base,expt)
end
nothing
end
_symatamath()
"""
symatamath()
defines Base methods for arithmetic operators `*`, `+`, `-`, `^` between `Symbols` and between `Symbols` and `Numbers`. `symatamath()` is *not*
necessary when running Symata and using J(expr) to evaluate Julia code. `J()` evaluates in the Symata
module where these methods are already defined. `symatamath()` is useful in Julia mode where expressions
are evaluated in `Main`.
These methods are not defined in `Main` by default because defining `Base` methods between core Julia objects could
conflict with future versions of Julia.
"""
function symatamath()
@eval begin
Base.:*(a::Number,b::SJSym) = mxpr(:Times,a,b) # why not mmul ?
Base.:*(a::SJSym,b::SJSym) = mxpr(:Times,a,b)
Base.:*(a::SJSym,b::Number) = mxpr(:Times,b,a)
Base.:+(a::SJSym,b::Number) = mxpr(:Plus,b,a)
Base.:+(a::SJSym,b::SJSym) = mxpr(:Plus,a,b)
Base.:+(a::Number,b::SJSym) = mxpr(:Plus,a,b)
Base.:-(a::Number,b::SJSym) = mplus(a, mxpr(:Times,-1,b))
Base.:-(a::SJSym,b::Number) = mplus(a, -b)
Base.:-(a::SJSym,b::SJSym) = mplus(a, mxpr(:Times,-1,b))
Base.:^(base::SJSym,expt::Integer) = mxpr(:Power,base,expt)
Base.:^(base::SJSym,expt) = mxpr(:Power,base,expt)
end
nothing
end
## Arithmetic methods involving annotated Symata types. These will never conflict with Base Julia,
## so they are safe to define.
*(a::Mxpr,b::Mxpr) = mxpr(:Times,a,b)
*(a::Mxpr,b) = mxpr(:Times,a,b)
*(a,b::Mxpr) = mxpr(:Times,a,b)
Base.:*(a::Mxpr,b::Mxpr) = mxpr(:Times,a,b)
Base.:*(a::Mxpr,b) = mxpr(:Times,a,b)
Base.:*(a,b::Mxpr) = mxpr(:Times,a,b)
+(a::Mxpr,b::Mxpr) = mxpr(:Plus,a,b)
+(a::Mxpr,b) = mxpr(:Plus,a,b)
+(a,b::Mxpr) = mxpr(:Plus,a,b)
Base.:+(a::Mxpr,b::Mxpr) = mxpr(:Plus,a,b)
Base.:+(a::Mxpr,b) = mxpr(:Plus,a,b)
Base.:+(a,b::Mxpr) = mxpr(:Plus,a,b)
-(a,b::Mxpr) = mxpr(:Plus,a,mxpr(:Times,-1,b))
-(a::Mxpr) = mxpr(:Times,-1,a)
^(base::Mxpr,expt::Integer) = mxpr(:Power,base,expt)
^(base::Mxpr,expt) = mxpr(:Power,base,expt)
/(a::Mxpr,b) = mxpr(:Times,a,mxpr(:Power,b,-1))
Base.:-(a,b::Mxpr) = mxpr(:Plus,a,mxpr(:Times,-1,b))
Base.:-(a::Mxpr) = mxpr(:Times,-1,a)
Base.:^(base::Mxpr,expt::Integer) = mxpr(:Power,base,expt)
Base.:^(base::Mxpr,expt) = mxpr(:Power,base,expt)
Base.:/(a::Mxpr,b) = mxpr(:Times,a,mxpr(:Power,b,-1))
## Symata uses module-local functions * + - ^ /
## For anything not defined in Symata, the Base methods are called.
*(args...) = Base.:*(args...)
+(args...) = Base.:+(args...)
-(args...) = Base.:-(args...)
^(args...) = Base.:^(args...)
/(args...) = Base.:/(args...)
# Already defined elsewhere (... where ?)
# I = im
# export I
# TODO: make an infix assignment operator... hm or macro
## TODO: Is the following useful ? Comment it out or remove it, if not...
## In general, we don't like an interface that is not being used.
##
# Do assigment in Symata and bind to julia symbol of the same name
# This is kinda broken !
# ERROR: UndefVarError: n not defined.
# But, the variable is defined in Main afterall
# We may not always want to define a variable in Main. But, it is hardcoded
# Usage:
# @aex x = 1
# Assigns x to 1 in symata, and x to :x in main
macro aex(e)
quote
@sym $(esc(e))
end
sym = e.args[1]
symstr = string(sym)
expr = :( $sym = Symbol($symstr) )
Main.eval(expr) # if we use Main., we get an 'n undefined error', but n is successfully defined.
end