Skip to content

Commit 45a8d7a

Browse files
cellml stuff added to handle Beeler-Reuter
1 parent 45f5532 commit 45a8d7a

File tree

1 file changed

+92
-34
lines changed

1 file changed

+92
-34
lines changed

src/parse.jl

Lines changed: 92 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,28 @@ end
2525
"""
2626
parse_cn(node)
2727
28-
parse a <cn> node
28+
parse a <cn> node
2929
"""
3030
function parse_cn(node)
3131
# elements(node) != EzXML.Node[] && error("node cant have elements rn, check if </sep> is in the node")
3232
# this isnt great might want to check that `sep` actually shows up
3333
if haskey(node, "type") && elements(node) != EzXML.Node[]
3434
parse_cn_w_sep(node)
35-
else
35+
else
3636
Meta.parse(node.content)
3737
end
3838
end
3939

4040
"""
4141
parse_cn_w_sep(node)
4242
43-
parse a <cn type=".."> node
43+
parse a <cn type=".."> node
4444
4545
where type ∈ ["e-notation", "rational", "complex-cartesian", "complex-polar"]
4646
"""
4747
function parse_cn_w_sep(node)
4848
# node = clean_attributes(node)
49-
txts = findall("//text()", node)
49+
txts = findall("text()", node)
5050
length(txts) != 2 && error("stop, collaborate, and listen!, problem with <cn>")
5151
x1, x2 = map(x -> Meta.parse(x.content), txts)
5252
t = node["type"]
@@ -58,14 +58,14 @@ function parse_cn_w_sep(node)
5858
Complex(x1, x2)
5959
elseif t == "complex-polar"
6060
x1 * exp(x2 * im)
61-
else
61+
else
6262
error("$t in parse_cn_w_sep, somethings wrong")
6363
end
6464
end
6565

6666
# """
6767
# remove all attributes neq to `type`
68-
# this is an issue for undefined namespaces
68+
# this is an issue for undefined namespaces
6969
# https://github.com/JuliaIO/EzXML.jl/issues/156
7070
# """
7171
# function clean_attributes(node)
@@ -84,30 +84,61 @@ end
8484
"""
8585
parse_ci(node)
8686
87-
parse a <ci> node
87+
parse a <ci> node
8888
"""
8989
function parse_ci(node)
9090
c = Meta.parse(strip(node.content))
9191
Num(Variable(c))
9292
end
9393

94+
########## Parse piecewise ###################################################
95+
96+
function parse_piecewise(node)
97+
return process_pieces(elements(node))
98+
end
99+
100+
function process_pieces(pieces)
101+
if length(pieces) == 1
102+
return process_piece(pieces[1])
103+
else
104+
return process_piece(pieces[1], process_pieces(pieces[2:end]))
105+
end
106+
end
107+
108+
function process_piece(node)
109+
if nodename(node) != "otherwise"
110+
error("expect an otherwise")
111+
else
112+
return parse_node(firstelement(node))
113+
end
114+
end
115+
116+
function process_piece(node, otherwise)
117+
if nodename(node) == "otherwise"
118+
return parse_node(firstelement(node))
119+
elseif nodename(node) == "piece"
120+
c = parse_node.(elements(node))
121+
return Symbolics.IfElse.ifelse(c[2] > 0.5, c[1], otherwise)
122+
end
123+
end
124+
94125
# """
95126
# parse_piecewise(node)
96127

97-
# parse a <piecewise> node
128+
# parse a <piecewise> node
98129
# want to recursively call ifelse on the pieces
99130
# """
100131
# function parse_piecewise(node)
101132
# es = elements(node)
102-
# IfElse.ifelse(a, b,
103-
# IfElse.ifelse(c, d,
133+
# IfElse.ifelse(a, b,
134+
# IfElse.ifelse(c, d,
104135
# IfElse.ifelse(e, f, otherwise)))
105136
# end
106137

107138
# """
108139
# parse_piece(node)
109140

110-
# parse a <piece> node
141+
# parse a <piece> node
111142
# Each <piece> element contains exactly two children.
112143
# The conditional is the second child and the return is the first.
113144
# """
@@ -133,7 +164,7 @@ end
133164
"""
134165
parse_bvar(node)
135166
136-
parse a <bvar> node
167+
parse a <bvar> node
137168
"""
138169
function parse_bvar(node)
139170
es = elements(node)
@@ -156,20 +187,20 @@ end
156187
tagmap = Dict{String,Function}(
157188
"cn" => parse_cn,
158189
"ci" => parse_ci,
159-
190+
160191
"degree" => x -> parse_node(x.firstelement), # won't work for all cases
161192
"bvar" => parse_bvar, # won't work for all cases
162-
# "diff" => parse_diff, #inputs are
193+
# "diff" => parse_diff, #inputs are
163194

164-
# "piecewise" => parse_piecewise,
165-
# "piece" => parse_piece,
166-
# "otherwise" => x-> parse_node(x.firstelement),
195+
"piecewise" => parse_piecewise,
196+
# "piece" => parse_piece,
197+
# "otherwise" => x-> parse_node(x.firstelement),
167198

168199
"apply" => parse_apply,
169200
"math" => x -> map(parse_node, elements(x)),
170201
"vector" => x -> map(parse_node, elements(x)),
171202
)
172-
203+
173204
function custom_root(x)
174205
length(x) == 1 ? sqrt(x...) : Base.:^(x[2], x[1])
175206
end
@@ -180,45 +211,72 @@ function check_ivs(node)
180211
all(y -> y.content == x[1].content, x)
181212
end
182213

183-
# need to check the arities
214+
H(x) = Symbolics.IfElse.ifelse(x > 0, one(x), zero(x))
215+
const ϵ = eps(Float64)
216+
frac(x) = 0.5 - atan(cot* x)) / π
217+
heaviside_or(x) = length(x) == 1 ? x[1] : x[1] + heaviside_or(x[2:end]) - x[1] * heaviside_or(x[2:end])
218+
219+
# need to check the arities
184220
# units handling??
185221
applymap = Dict{String,Function}(
186222
# eq sometimes needs to be ~ and sometimes needs to be =, not sure what the soln is
187-
"eq" => x -> Symbolics.:~(x...), # arity 2,
223+
"eq" => x -> Symbolics.:~(x...), # arity 2,
188224
"times" => Base.prod, # arity 2, but prod fine
189-
# "prod" => Base.prod,
225+
# "prod" => Base.prod,
190226
"divide" => x -> Base.:/(x...),
191227
"power" => x -> Base.:^(x...),
192-
"root" => custom_root,
228+
"root" => custom_root,
193229
"plus" => x -> Base.:+(x...),
194230
"minus" => x -> Base.:-(x...),
195-
"lt" => x -> Base.foldl(Base.:<, x),
196-
"leq" => x -> Base.foldl(Base.:, x),
197-
"geq" => x -> Base.foldl(Base.:, x),
198-
"gt" => x -> Base.foldl(Base.:>, x),
231+
232+
# comparison functions implemented using the Heaviside function
233+
"lt" => x -> H(x[2] - x[1] - ϵ),
234+
"leq" => x -> H(x[2] - x[1]),
235+
"geq" => x -> H(x[1] - x[2]),
236+
"gt" => x -> H(x[1] - x[2] - ϵ),
237+
238+
# "lt" => x -> Base.foldl(Base.:<, x),
239+
# "leq" => x -> Base.foldl(Base.:≤, x),
240+
# "geq" => x -> Base.foldl(Base.:≥, x),
241+
# "gt" => x -> Base.foldl(Base.:>, x),
242+
199243
# "quotient" => x->Base.:div(x...), # broken, RoundingMode
200244
"factorial" => x -> Base.factorial(x...),
201245
"max" => x -> Base.max(x...),
202246
"min" => x -> Base.min(x...),
203247
"rem" => x -> Base.:rem(x...),
204248
"gcd" => x -> Base.:gcd(x...),
205-
"and" => Base.:&,
206-
"or" => Base.:|,
207-
"xor" => Base.:,
208-
"not" => Base.:!,
249+
250+
"and" => x -> Base.:*(x...),
251+
"or" => heaviside_or,
252+
"xor" => x -> x[1]*(one(x[2])-x[2]) + (one(x[1])-x[1])*x[2],
253+
"not" => x -> one(x[1]) - x[1],
254+
255+
# "and" => x -> Base.:&(x...),
256+
# "or" => Base.:|,
257+
# "xor" => Base.:⊻,
258+
# "not" => Base.:!,
259+
209260
"abs" => x -> Base.abs(x...),
210261
"conjugate" => Base.conj,
211262
"arg" => Base.angle,
212263
"real" => Base.real,
213264
"imaginary" => Base.imag,
214265
"lcm" => x -> Base.lcm(x...),
215-
"floor" => x -> Base.floor(x...),
216-
"ceiling" => x -> Base.ceil(x...),
266+
267+
"floor" => x -> x[1] - frac(x[1]),
268+
"ceiling" => x -> x[1] - frac(x[1]) + one(x[1]),
269+
"round" => x -> x[1] + 0.5 - frac(x[1] + 0.5),
270+
271+
# "floor" => x -> Base.floor(x...),
272+
# "ceiling" => x -> Base.ceil(x...),
273+
# "round" => x -> Base.round(x...),
274+
217275
"inverse" => Base.inv,
218276
"compose" => x -> Base.:(x...),
219277
"ident" => Base.identity,
220278
"approx" => x -> Base.:(x...),
221-
279+
222280
"sin" => x -> Base.sin(x...),
223281
"cos" => x -> Base.cos(x...),
224282
"tan" => x -> Base.tan(x...),
@@ -245,7 +303,7 @@ applymap = Dict{String,Function}(
245303
"arccoth" => x -> Base.acoth(x...),
246304

247305
"exp" => x -> Base.exp(x...),
248-
"log" => x -> Base.log10(x...), # todo handle <logbase>
306+
"log" => x -> Base.log10(x...), # todo handle <logbase>
249307
"ln" => x -> Base.log(x...),
250308

251309
"mean" => Statistics.mean,

0 commit comments

Comments
 (0)