Skip to content

Commit

Permalink
Merge pull request #117 from JuliaSymbolics/myb/metadata
Browse files Browse the repository at this point in the history
Add various metadata
  • Loading branch information
YingboMa authored Mar 14, 2021
2 parents 0b62bd6 + d531459 commit 1f450de
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 30 deletions.
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Symbolics"
uuid = "0c5d862f-8b57-4792-8d23-62f2024744c7"
authors = ["Shashi Gowda <[email protected]>"]
version = "0.1.7"
version = "0.1.8"

[deps]
AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d"
Expand All @@ -18,6 +18,7 @@ RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
RuntimeGeneratedFunctions = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
Expand All @@ -37,6 +38,7 @@ Reexport = "0.2, 1"
RuntimeGeneratedFunctions = "0.4.3, 0.5"
SafeTestsets = "0"
SciMLBase = "1.8"
Setfield = "0.7"
SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1.0"
SymbolicUtils = "0.9.0"
TreeViews = "0.3"
Expand Down
1 change: 1 addition & 0 deletions src/equations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Base.hash(a::Equation, salt::UInt) = hash(a.lhs, hash(a.rhs, salt))
Base.show(io::IO, eq::Equation) = print(io, eq.lhs, " ~ ", eq.rhs)

SymbolicUtils.simplify(x::Equation; kw...) = simplify(x.lhs; kw...) ~ simplify(x.rhs; kw...)
SymbolicUtils.substitute(x::Equation, rules; kw...) = substitute(x.lhs, rules; kw...) ~ substitute(x.rhs, rules; kw...)

lhss(xs) = map(x->x.lhs, xs)
rhss(xs) = map(x->x.rhs, xs)
Expand Down
4 changes: 4 additions & 0 deletions src/num.jl
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,7 @@ _iszero(x::Num) = _iszero(value(x))
_isone(x::Num) = _isone(value(x))

SymbolicUtils.Code.toexpr(x::Num) = SymbolicUtils.Code.toexpr(value(x))

SymbolicUtils.setmetadata(x::Num, t, v) = Num(SymbolicUtils.setmetadata(value(x), t, v))
SymbolicUtils.getmetadata(x::Num, t) = SymbolicUtils.getmetadata(value(x), t)
SymbolicUtils.hasmetadata(x::Num, t) = SymbolicUtils.hasmetadata(value(x), t)
17 changes: 11 additions & 6 deletions src/utils.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
isblock(x) = length(x) == 1 && x[1] isa Expr && x[1].head == :block
function flatten_expr!(x)
isb = isblock(x)
if isb
x = MacroTools.striplines(x[1])
filter!(z->z isa Symbol || z.head != :line, x.args)
x = (x.args...,)
isblock(x) || return x
x = MacroTools.striplines(x[1])
filter!(z->z isa Symbol || z.head != :line, x.args)
xs = []
for ex in x.args
if Meta.isexpr(ex, :tuple)
append!(xs, ex.args)
else
push!(xs, ex)
end
end
x
xs
end
function build_expr(head::Symbol, args)
ex = Expr(head)
Expand Down
112 changes: 89 additions & 23 deletions src/variable.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using SymbolicUtils: FnType, Sym
using Setfield

const IndexMap = Dict{Char,Char}(
'0' => '',
Expand All @@ -12,6 +13,8 @@ const IndexMap = Dict{Char,Char}(
'8' => '',
'9' => '')

struct VariableDefaultValue end

"""
$(TYPEDEF)
Expand Down Expand Up @@ -67,10 +70,13 @@ function map_subscripts(indices)
join(IndexMap[c] for c in str)
end

rename(x::Sym{T},name) where T = Sym{T}(name)
rename(x::Sym,name) = @set! x.name = name
function rename(x::Symbolic, name)
if operation(x) isa Sym
rename(operation(x), name)(arguments(x)...)
@assert x isa Term
@set! x.f = rename(operation(x), name)
@set! x.arguments = arguments(x)
@set! x.hash = Ref{UInt}(0)
else
error("can't rename $x to $name")
end
Expand All @@ -88,17 +94,43 @@ function _parse_vars(macroname, type, x)
# end
x = x isa Tuple && first(x) isa Expr && first(x).head == :tuple ? first(x).args : x # tuple handling
x = flatten_expr!(x)
for _var in x
iscall = isa(_var, Expr) && _var.head == :call
isarray = isa(_var, Expr) && _var.head == :ref
issym = _var isa Symbol
@assert iscall || isarray || issym "@$macroname expects a tuple of expressions or an expression of a tuple (`@$macroname x y z(t) v[1:3] w[1:2,1:4]` or `@$macroname x, y, z(t) v[1:3] w[1:2,1:4]`)"
cursor = 0
isoption(ex) = Meta.isexpr(ex, [:vect, :vcat, :hcat])
while cursor < length(x)
cursor += 1
v = x[cursor]

# We need lookahead to the next `v` to parse
# `@variables x [connect=Flow,unit=u]`
nv = cursor < length(x) ? x[cursor+1] : nothing
val = unit = connect = options = nothing

# x = 1, [connect = flow; unit = u"m^3/s"]
if Meta.isexpr(v, :(=))
v, val = v.args
if Meta.isexpr(val, :tuple) && length(val.args) == 2 && isoption(val.args[2])
options = val.args[2].args
val = val.args[1]
end
end

# x [connect = flow; unit = u"m^3/s"]
if isoption(nv)
options = nv.args
cursor += 1
end

iscall = Meta.isexpr(v, :call)
isarray = Meta.isexpr(v, :ref)
issym = v isa Symbol
@assert iscall || isarray || issym "@$macroname expects a tuple of expressions or an expression of a tuple (`@$macroname x y z(t) v[1:3] w[1:2,1:4]` or `@$macroname x y z(t) v[1:3] w[1:2,1:4] k=1.0`)"

if iscall
var_name, expr = _construct_vars(_var.args[1], type, _var.args[2:end])
var_name, expr = construct_vars(v.args[1], type, v.args[2:end], val, options)
else
var_name, expr = _construct_vars(_var, type, nothing)
var_name, expr = construct_vars(v, type, nothing, val, options)
end

push!(var_names, var_name)
push!(ex.args, expr)
end
Expand All @@ -107,45 +139,79 @@ function _parse_vars(macroname, type, x)
return ex
end

function _construct_vars(_var, type, call_args)
issym = _var isa Symbol
isarray = isa(_var, Expr) && _var.head == :ref
function construct_vars(v, type, call_args, val, prop)
issym = v isa Symbol
isarray = isa(v, Expr) && v.head == :ref
if isarray
var_name = _var.args[1]
indices = _var.args[2:end]
expr = _construct_array_vars(var_name, type, call_args, indices...)
var_name = v.args[1]
indices = v.args[2:end]
expr = _construct_array_vars(var_name, type, call_args, val, prop, indices...)
else
# Implicit 0-args call
var_name = _var
expr = _construct_var(var_name, type, call_args)
var_name = v
expr = construct_var(var_name, type, call_args, val, prop)
end
var_name, :($var_name = $expr)
end

function _construct_var(var_name, type, call_args)
function option_to_metadata_type(::Val{opt}) where {opt}
throw(Base.Meta.ParseError("unknown property type $opt"))
end

function setprops_expr(expr, props)
isnothing(props) && return expr
for opt in props
if !Meta.isexpr(opt, :(=))
throw(Base.Meta.ParseError(
"Variable properties must be in " *
"the form of `a = b`. Got $opt."))
end

lhs, rhs = opt.args

@assert lhs isa Symbol "the lhs of an option must be a symbol"
expr = :($setmetadata($expr,
$(option_to_metadata_type(Val{lhs}())),
$rhs))
end
expr
end


function construct_var(var_name, type, call_args, val, prop)
expr = if call_args === nothing
:($Num($Sym{$type}($(Meta.quot(var_name)))))
elseif !isempty(call_args) && call_args[end] == :..
:($Num($Sym{$FnType{Tuple, $type}}($(Meta.quot(var_name))))) # XXX: using Num as output
else
:($Num($Sym{$FnType{NTuple{$(length(call_args)), Any}, $type}}($(Meta.quot(var_name)))($(map(x->:($value($x)), call_args)...))))
end

if val !== nothing
expr = :($setmetadata($expr, $VariableDefaultValue, $val))
end

return setprops_expr(expr, prop)
end

function _construct_var(var_name, type, call_args, ind)
function construct_var(var_name, type, call_args, val, prop, ind)
# TODO: just use Sym here
if call_args === nothing
expr = if call_args === nothing
:($Num($Sym{$type}($(Meta.quot(var_name)), $ind...)))
elseif !isempty(call_args) && call_args[end] == :..
:($Num($Sym{$FnType{Tuple{Any}, $type}}($(Meta.quot(var_name)), $ind...))) # XXX: using Num as output
else
:($Num($Sym{$FnType{NTuple{$(length(call_args)), Any}, $type}}($(Meta.quot(var_name)), $ind...)($(map(x->:($value($x)), call_args)...))))
end
if val !== nothing
expr = :($setmetadata($expr, $VariableDefaultValue, $val isa AbstractArray ? $val[$ind...] : $val))
end

return setprops_expr(expr, prop)
end

function _construct_array_vars(var_name, type, call_args, indices...)
function _construct_array_vars(var_name, type, call_args, val, prop, indices...)
:(map(Iterators.product($(indices...))) do ind
$(_construct_var(var_name, type, call_args, :ind))
$(construct_var(var_name, type, call_args, val, prop, :ind))
end)
end

Expand Down
2 changes: 2 additions & 0 deletions test/overloads.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ using Test

@variables a,b,c,d,e,f,g,h,i

@test substitute(a ~ b, Dict(a=>1, b=>c)) == (1 ~ c)

# test hashing
aa = a; # old a

Expand Down

2 comments on commit 1f450de

@YingboMa
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/31952

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.1.8 -m "<description of version>" 1f450def014628f7fe5321282f33972527fb0318
git push origin v0.1.8

Please sign in to comment.