-
Notifications
You must be signed in to change notification settings - Fork 12
/
math.jl
251 lines (229 loc) · 8.82 KB
/
math.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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
"""
$(TYPEDSIGNATURES)
Helper for quickly recognizing kinds of ASTs
"""
ast_is(ast::VPtr, what::Symbol)::Bool = ccall(sbml(what), Cint, (VPtr,), ast) != 0
"""
$(TYPEDSIGNATURES)
Recursively parse all children of an AST node.
"""
parse_math_children(ast::VPtr)::Vector{Math} = [
parse_math(ccall(sbml(:ASTNode_getChild), VPtr, (VPtr, Cuint), ast, i - 1)) for
i = 1:ccall(sbml(:ASTNode_getNumChildren), Cuint, (VPtr,), ast)
]
# Mapping of AST node type value subset to relational operations. Depends on
# `ASTNodeType.h` (also see below the case with AST_NAME_TIME)
const relational_opers = Dict{Int32,String}(
308 => "eq",
309 => "geq",
310 => "gt",
311 => "leq",
312 => "lt",
313 => "neq",
)
# Inverse mapping, needed for creating `ASTNode_t` pointers from `MathApply` objects.
const inv_relational_opers = Dict(val => key for (key, val) in relational_opers)
function relational_oper(t::Int)
haskey(relational_opers, t) ||
throw(DomainError(t, "Unknown ASTNodeType value for relational operator"))
relational_opers[t]
end
# Mapping of AST node type value subset to mathematical operations. Depends on
# `ASTNodeType.h` (also see below the case with AST_NAME_TIME)
const math_opers = Dict{Int32,String}(43 => "+", 45 => "-", 42 => "*", 47 => "/", 94 => "^")
# Inverse mapping, needed for creating `ASTNode_t` pointers from `MathApply` objects.
const inv_math_opers = Dict(val => key for (key, val) in math_opers)
# Mapping of AST node type value subset to logical operations. Depends on
# `ASTNodeType.h` (also see below the case with AST_NAME_TIME)
const logical_opers =
Dict{Int32,String}(304 => "and", 305 => "not", 306 => "or", 307 => "xor")
# Inverse mapping, needed for creating `ASTNode_t` pointers from `MathApply` objects.
const inv_logical_opers = Dict(val => key for (key, val) in logical_opers)
# Mapping of AST node type value subset to mathematical functions. Depends on
# `ASTNodeType.h` (also see below the case with AST_NAME_TIME)
const math_funcs = Dict{Int32,String}(
269 => "abs",
270 => "arccos",
271 => "arccosh",
272 => "arccot",
273 => "arccoth",
274 => "arccsc",
275 => "arccsch",
276 => "arcsec",
277 => "arcsech",
278 => "arcsin",
279 => "arcsinh",
280 => "arctan",
281 => "arctanh",
282 => "ceiling",
283 => "cos",
284 => "cosh",
285 => "cot",
286 => "coth",
287 => "csc",
288 => "csch",
289 => "delay",
290 => "exp",
291 => "factorial",
292 => "floor",
293 => "ln",
294 => "log",
295 => "piecewise",
296 => "power",
297 => "root",
298 => "sec",
299 => "sech",
300 => "sin",
301 => "sinh",
302 => "tan",
303 => "tanh",
)
# Inverse mapping, needed for creating `ASTNode_t` pointers from `MathApply` objects.
const inv_math_funcs = Dict(val => key for (key, val) in math_funcs)
# Everybody wants to be a map!
const all_inv_function_mappings =
merge(inv_relational_opers, inv_math_opers, inv_logical_opers, inv_math_funcs)
# This is for checking the "special case" named values as defined by SBML.
# The constants are the values of the enums AST_NAME_* in
# `libsbml/src/sbml/math/ASTNodeType.h`. These need to be kept up to date with
# the library (otherwise this breaks).
get_ast_type_id(::Type{MathAvogadro}) = 261
get_ast_type_id(::Type{MathTime}) = 262
"""
$(TYPEDSIGNATURES)
This attempts to parse out a decent Julia-esque [`Math`](@ref) AST from a
pointer to `ASTNode_t`.
"""
function parse_math(ast::VPtr)::Math
if ast_is(ast, :ASTNode_isName)
type = ccall(sbml(:ASTNode_getType), Cint, (VPtr,), ast)
if type == get_ast_type_id(MathAvogadro)
# this should be synonymous with calling ASTNode_isAvogadro()
return MathAvogadro(get_string(ast, :ASTNode_getName))
elseif type == get_ast_type_id(MathTime)
# unfortunately there's no ASTNode_isTime() at the moment.
return MathTime(get_string(ast, :ASTNode_getName))
else
return MathIdent(get_string(ast, :ASTNode_getName))
end
elseif ast_is(ast, :ASTNode_isConstant)
return MathConst(get_string(ast, :ASTNode_getName))
elseif ast_is(ast, :ASTNode_isInteger)
return MathVal(ccall(sbml(:ASTNode_getInteger), Cint, (VPtr,), ast))
elseif ast_is(ast, :ASTNode_isRational)
return MathVal(
ccall(sbml(:ASTNode_getNumerator), Cint, (VPtr,), ast) //
ccall(sbml(:ASTNode_getDenominator), Cint, (VPtr,), ast),
)
elseif ast_is(ast, :ASTNode_isReal)
return MathVal(ccall(sbml(:ASTNode_getReal), Cdouble, (VPtr,), ast))
elseif ast_is(ast, :ASTNode_isFunction)
return MathApply(get_string(ast, :ASTNode_getName), parse_math_children(ast))
elseif ast_is(ast, :ASTNode_isOperator)
return MathApply(
string(Char(ccall(sbml(:ASTNode_getCharacter), Cchar, (VPtr,), ast))),
parse_math_children(ast),
)
elseif ast_is(ast, :ASTNode_isRelational)
return MathApply(
relational_oper(Int(ccall(sbml(:ASTNode_getType), Cint, (VPtr,), ast))),
parse_math_children(ast),
)
elseif ast_is(ast, :ASTNode_isLogical)
return MathApply(get_string(ast, :ASTNode_getName), parse_math_children(ast))
elseif ast_is(ast, :ASTNode_isLambda)
children = parse_math_children(ast)
if !isempty(children)
body = pop!(children)
return MathLambda(broadcast((x::MathIdent) -> x.id, children), body)
else
@warn "invalid function definition found"
return MathIdent("?invalid?")
end
else
@warn "unsupported math element found"
return MathIdent("?unsupported?")
end
end
## Inverse of `parse_math`: create `ASTNode_t` pointers from `Math` objects.
function get_astnode_ptr(m::Union{MathTime,MathAvogadro})::VPtr
astnode = ccall(sbml(:ASTNode_create), VPtr, ())
ccall(sbml(:ASTNode_setName), Cint, (VPtr, Cstring), astnode, m.id)
ccall(sbml(:ASTNode_setType), Cint, (VPtr, Cuint), astnode, get_ast_type_id(typeof(m)))
ccall(sbml(:ASTNode_canonicalize), Cint, (VPtr,), astnode)
return astnode
end
function get_astnode_ptr(m::Union{MathIdent,MathConst})::VPtr
m.id in ("?invalid?", "?unsupported?") &&
error("Cannot get a pointer for `MathIdent` with ID \"$(m.id)\"")
astnode = ccall(sbml(:ASTNode_create), VPtr, ())
ccall(sbml(:ASTNode_setName), Cint, (VPtr, Cstring), astnode, m.id)
ccall(sbml(:ASTNode_canonicalize), Cint, (VPtr,), astnode)
return astnode
end
function get_astnode_ptr(m::MathVal{<:Integer})::VPtr
astnode = ccall(sbml(:ASTNode_create), VPtr, ())
ccall(sbml(:ASTNode_setInteger), Cint, (VPtr, Clong), astnode, m.val)
ccall(sbml(:ASTNode_canonicalize), Cint, (VPtr,), astnode)
return astnode
end
function get_astnode_ptr(m::MathVal{<:Rational})::VPtr
astnode = ccall(sbml(:ASTNode_create), VPtr, ())
# Note: this can be in principle a lossy reconstruction as `Rational`s in
# Julia are automatically simplified (e.g., 5//10 -> 1//2).
ccall(
sbml(:ASTNode_setRational),
Cint,
(VPtr, Clong, Clong),
astnode,
numerator(m.val),
denominator(m.val),
)
ccall(sbml(:ASTNode_canonicalize), Cint, (VPtr,), astnode)
return astnode
end
function get_astnode_ptr(m::MathVal{<:Real})::VPtr
astnode = ccall(sbml(:ASTNode_create), VPtr, ())
ccall(sbml(:ASTNode_setReal), Cint, (VPtr, Cdouble), astnode, m.val)
ccall(sbml(:ASTNode_canonicalize), Cint, (VPtr,), astnode)
return astnode
end
function get_astnode_ptr(m::MathApply)::VPtr
astnode = ccall(sbml(:ASTNode_create), VPtr, ())
# Set the name
ccall(sbml(:ASTNode_setName), Cint, (VPtr, Cstring), astnode, m.fn)
# Set the type
if m.fn in keys(all_inv_function_mappings)
ccall(
sbml(:ASTNode_setType),
Cint,
(VPtr, Cuint),
astnode,
all_inv_function_mappings[m.fn],
)
else
ccall(sbml(:ASTNode_setType), Cint, (VPtr, Cuint), astnode, 268) # 268 == AST_FUNCTION
end
# Add children
for child in m.args
child_ptr = get_astnode_ptr(child)
ccall(sbml(:ASTNode_addChild), Cint, (VPtr, VPtr), astnode, child_ptr)
end
ccall(sbml(:ASTNode_canonicalize), Cint, (VPtr,), astnode)
return astnode
end
function get_astnode_ptr(m::MathLambda)::VPtr
astnode = ccall(sbml(:ASTNode_create), VPtr, ())
# All arguments
for child in MathIdent.(m.args)
child_ptr = get_astnode_ptr(child)
ccall(sbml(:ASTNode_addChild), Cint, (VPtr, VPtr), astnode, child_ptr)
end
# Add the body
body = get_astnode_ptr(m.body)
ccall(sbml(:ASTNode_addChild), Cint, (VPtr, VPtr), astnode, body)
# Set the type
ccall(sbml(:ASTNode_setType), Cint, (VPtr, Cuint), astnode, 267) # 267 == AST_LAMBDA
# Done
return astnode
end