Symbolic manipulation
One of Dolang's key features is providing an _extensible_ and _consistent_ set of routines for doing operations on symbolic objects. We'll discuss each of these routines in turn
normalize
The normalize
function converts expressions and symbols into Dolang's internal representation. There are various methods for this function:
normalize(::Symbol)
Examples
julia> normalize(:c)
:_c_
julia> normalize(:_c)
:__c_
julia> normalize(:_c_)
:_c_
julia> normalize(:x_ijk)
:_x_ijk_
julia> normalize(:x_ijk_)
:_x_ijk__
julia> normalize(:_x_ijk_)
:_x_ijk_
normalize(::Number)
Examples
julia> normalize(-1)
-1
julia> normalize(0)
0
julia> normalize(1)
1
in
normalize(::Symbol, ::Integer)
Examples
julia> normalize(:x, 0)
:_x__0_
julia> normalize(:x, 1)
:_x__1_
julia> normalize(:x, -1)
:_x_m1_
julia> normalize(:x, -100)
:_x_m100_
julia> normalize("x", 0)
:_x__0_
julia> normalize("x", 1)
:_x__1_
julia> normalize("x", -1)
:_x_m1_
julia> normalize("x", -100)
:_x_m100_
normalize(::Tuple{Symbol,Integer})
Examples
julia> normalize((:x, 0))
:_x__0_
julia> normalize((:x, 1))
:_x__1_
julia> normalize((:x, -1))
:_x_m1_
julia> normalize((:x, -100))
:_x_m100_
normalize(::Expr)
Examples
julia> normalize(:(a(1) - b - c(2) + d(-1)))
:(((_a__1_ - _b_) - _c__2_) + _d_m1_)
julia> normalize(:(sin(x)))
:(sin(_x_))
julia> normalize(:(sin(x(0))))
:(sin(_x__0_))
julia> normalize(:(dot(x, y(1))))
:(dot(_x_, _y__1_))
julia> normalize(:(beta * c(0)/c(1) * (alpha*y(1)/k(1) * (1-mu(1)) + 1 - delta_k) - 1))
:(((_beta_ * _c__0_) / _c__1_) * ((((_alpha_ * _y__1_) / _k__1_) * (1 - _mu__1_) + 1) - _delta_k_) - 1)
julia> normalize(:(x = log(y(-1))); targets=[:x]) # with targets
:(_x_ = log(_y_m1_))
julia> normalize(:(x = log(y(-1)))) # without targets
:(log(_y_m1_) - _x_)
normalize(::String)
Examples: see above for more
julia> normalize("x = log(y(-1))"; targets=[:x]) # with targets
:(_x_ = log(_y_m1_))
julia> normalize("x = log(y(-1))") # without targets
:(log(_y_m1_) - _x_)
normalize(::Vector{Expr})
Examples:
julia> normalize([:(sin(x(0))), :(dot(x, y(1))), :(x = log(y(-1)))])
quote
sin(_x__0_)
dot(_x_, _y__1_)
log(_y_m1_) - _x_
end
julia> normalize([:(sin(x(0))), :(dot(x, y(1))), :(x = log(y(-1)))], targets=[:x])
quote
sin(_x__0_)
dot(_x_, _y__1_)
_x_ = log(_y_m1_)
end
time_shift
time_shift
shifts an expression by a specified number of time periods. As with normalize, there are many methods for this function, which will will describe one at a time.
time_shift(::Expr, ::Integer, ::Set{Symbol}, ::Associative)
time_shift(::Expr, ::Integer)
Examples
julia> defs = Dict(:a=>:(b(-1)/c));
julia> defs2 = Dict(:a=>:(b(-1)/c(0)));
julia> funcs = [:foobar];
julia> shift = 1;
julia> time_shift(:(a+b(1) + c), shift)
:(a + b(2) + c)
julia> time_shift(:(a+b(1) + c(0)), shift)
:(a + b(2) + c(1))
julia> time_shift(:(a+b(1) + c), shift, defs=defs)
:(b(0) / c + b(2) + c)
julia> time_shift(:(a+b(1) + c(0)), shift, defs=defs)
:(b(0) / c + b(2) + c(1))
julia> time_shift(:(a+b(1) + c(0)), shift, defs=defs2)
:(b(0) / c(1) + b(2) + c(1))
julia> time_shift(:(a+b(1) + foobar(c)), shift, functions=funcs)
:(a + b(2) + foobar(c))
julia> time_shift(:(a+b(1) + foobar(c)), shift, defs=defs, functions=funcs)
:(b(0) / c + b(2) + foobar(c))
julia> shift = -1;
julia> time_shift(:(a+b(1) + c), shift)
:(a + b(0) + c)
julia> time_shift(:(a+b(1) + c(0)), shift)
:(a + b(0) + c(-1))
julia> time_shift(:(a+b(1) + c), shift, defs=defs)
:(b(-2) / c + b(0) + c)
julia> time_shift(:(a+b(1) + c(0)), shift, defs=defs)
:(b(-2) / c + b(0) + c(-1))
julia> time_shift(:(a+b(1) + c(0)), shift, defs=defs2)
:(b(-2) / c(-1) + b(0) + c(-1))
julia> time_shift(:(a+b(1) + foobar(c)), shift, functions=funcs)
:(a + b(0) + foobar(c))
julia> time_shift(:(a+b(1) + foobar(c)), shift, defs=defs, functions=funcs)
:(b(-2) / c + b(0) + foobar(c))
Dolang.time_shift
— Method.time_shift(x::Number, other...)
Return x
for all values of other
Examples
julia> defs = Dict(:a=>:(b(-1)/c));
julia> defs2 = Dict(:a=>:(b(-1)/c(0)));
julia> shift = 1;
julia> funcs = Set([:foobar]);
julia> time_shift(:a, shift, funcs, Dict())
:a
julia> time_shift(:a, shift, funcs, defs)
:(b(0) / c)
julia> time_shift(:a, shift, funcs, defs2)
:(b(0) / c(1))
julia> time_shift(:b, shift, funcs, defs)
:b
julia> shift = -1;
julia> time_shift(:a, shift, funcs, Dict())
:a
julia> time_shift(:a, shift, funcs, defs)
:(b(-2) / c)
julia> time_shift(:a, shift, funcs, defs2)
:(b(-2) / c(-1))
julia> time_shift(:b, shift, funcs, defs)
:b
Dolang.time_shift
— Method.time_shift(x::Number, other...)
Return x
for all values of other
Examples
julia> time_shift(1)
1
julia> time_shift(2)
2
julia> time_shift(-1)
-1
julia> time_shift(-2)
-2
steady_state
The steady_state
function will set the period for all "timed" variables to 0.
steady_state(::Symbol, ::Set{Symbol}, ::Associative)
julia> defs = Dict(:a=>:(b(-1)/c + d), :d=>:(exp(b(0))));
julia> steady_state(:c, Set{Symbol}(), defs)
:c
julia> steady_state(:d, Set{Symbol}(), defs)
:(exp(b))
julia> steady_state(:a, Set{Symbol}(), defs) # recursive def resolution
:(b / c + exp(b))
julia> steady_state(1, Set{Symbol}(), defs)
1
julia> steady_state(-1, Set{Symbol}(), defs)
-1
Examples
steady_state(::Expr, ::Set{Symbol}, ::Associative)
steady_state(::Expr)
Examples
julia> steady_state(:(a+b(1) + c))
:(a + b + c)
julia> steady_state(:(a+b(1) + c))
:(a + b + c)
julia> steady_state(:(a+b(1) + c), defs=Dict(:a=>:(b(-1)/c)))
:(b / c + b + c)
julia> steady_state(:(a+b(1)+c+foobar(c)))
ERROR: Dolang.UnknownFunctionError(:foobar, "Unknown function foobar")
julia> steady_state(:(a+b(1) + foobar(c)), functions=[:foobar])
:(a + b + foobar(c))
list_symbols
list_symbols
will walk an expression and determine which symbols are used as potentially time varying variables and which symbols are used as static parameters.
Dolang.list_symbols!
— Method.list_symbols!(out, s::Expr, functions::Set{Symbol})
Walk the expression and populate out
according to the following rules for each type of subexpression encoutered:
s::Symbol
: adds
toout[:parameters]
x(i::Integer)
: Add(x, i)
outout[:variables]
All other function calls: add any arguments to
out
according to above rulesAnything else: do nothing.
Dolang.list_symbols
— Method.list_symbols(::Expr;
functions::Union{Set{Symbol},Vector{Symbol}}=Set{Symbol}())
Construct an empty Dict{Symbol,Any}
and call list_symbols!
to populate it
Examples
julia> list_symbols(:(a + b(1) + c))
Dict{Symbol,Any} with 2 entries:
:variables => Set(Tuple{Symbol,Int64}[(:b, 1)])
:parameters => Set(Symbol[:a, :c])
julia> list_symbols(:(a + b(1) + c + b(0)))
Dict{Symbol,Any} with 2 entries:
:variables => Set(Tuple{Symbol,Int64}[(:b, 1), (:b, 0)])
:parameters => Set(Symbol[:a, :c])
julia> list_symbols(:(a + b(1) + c + b(0) + foobar(x)))
ERROR: Dolang.UnknownFunctionError(:foobar, "Unknown function foobar")
julia> list_symbols(:(a + b(1) + c + b(0) + foobar(x)), functions=Set([:foobar]))
Dict{Symbol,Any} with 2 entries:
:variables => Set(Tuple{Symbol,Int64}[(:b, 1), (:b, 0)])
:parameters => Set(Symbol[:a, :c, :x])
list_variables
list_variables
will return the :variables
key that results from calling list_symbols
. The type of the returned object will be a Set
of Tuple{Symbol,Int}
, where each item describes the symbol that appeared and the corresponding date.
Dolang.list_variables
— Method.list_variables(
arg;
functions::Union{Set{Symbol},Vector{Symbol}}=Set{Symbol}()
)
Return the :variables
key that results from calling list_symbols
. The type of the returned object will be a Set
of Tuple{Symbol,Int}
, where each item describes the symbol that appeared and the corresponding date.
Examples
julia> list_variables(:(a + b(1) + c))
Set(Tuple{Symbol,Int64}[(:b, 1)])
julia> list_variables(:(a + b(1) + c + b(0)))
Set(Tuple{Symbol,Int64}[(:b, 1), (:b, 0)])
julia> list_variables(:(a + b(1) + c + b(0) + foobar(x)))
ERROR: Dolang.UnknownFunctionError(:foobar, "Unknown function foobar")
julia> list_variables(:(a + b(1) + c + b(0) + foobar(x)), functions=Set([:foobar]))
Set(Tuple{Symbol,Int64}[(:b, 1), (:b, 0)])
list_parameters
list_parameters
will return the :parameters
key that results from calling list_symbols
. The returned object will be Set{Symbol}
where each item represents the symbol that appeared without a date.
Dolang.list_parameters
— Method.list_parameters(
arg;
functions::Union{Set{Symbol},Vector{Symbol}}=Set{Symbol}()
)
Return the :parameters
key that results from calling list_symbols
. The returned object will be Set{Symbol}
where each item represents the symbol that appeared without a date.
Examples
julia> list_variables(:(a + b(1) + c + b(0) + foobar(x)), functions=Set([:foobar]))
Set(Tuple{Symbol,Int64}[(:b, 1), (:b, 0)])
julia> list_parameters(:(a + b(1) + c))
Set(Symbol[:a, :c])
julia> list_parameters(:(a + b(1) + c + b(0)))
Set(Symbol[:a, :c])
julia> list_parameters(:(a + b(1) + c + b(0) + foobar(x)))
ERROR: Dolang.UnknownFunctionError(:foobar, "Unknown function foobar")
julia> list_parameters(:(a + b(1) + c + b(0) + foobar(x)), functions=Set([:foobar]))
Set(Symbol[:a, :c, :x])
subs
subs
will replace a symbol or expression with a different value, where the value has type Union{Symbol,Expr,Number}
The first method of this function is very specific, and matches only a particular pattern:
Dolang.subs
— Method.subs(ex::Union{Expr,Symbol,Number}, from, to::Union{Symbol,Expr,Number}, funcs::Set{Symbol})
Apply a substitution where all occurances of from
in ex
are replaced by to
.
Note that to replace something like x(1)
from
must be the canonical form for that expression: (:x, 1)
Examples
julia> subs(:(a + b(1) + c), :a, :(b(-1)/c + d), Set{Symbol}())
:((b(-1) / c + d) + b(1) + c)
julia> subs(:(a + b(1) + c), :d, :(b(-1)/c + d), Set{Symbol}())
:(a + b(1) + c)
Dolang.subs
— Method.subs(ex::Union{Expr,Symbol,Number}, d::Associative,
variables::Set{Symbol},
funcs::Set{Symbol})
Apply substitutions to ex
so that all keys in d
are replaced by their values
Note that the keys of d
should be the canonical form of variables you wish to substitute. For example, to replace x(1)
with b/c
you need to have the entry (:x, 1) => :(b/c)
in d
.
Dolang.subs
— Method.subs(ex::Union{Expr,Symbol,Number}, d::Associative,
variables::Set{Symbol},
funcs::Set{Symbol})
Apply substitutions to ex
so that all keys in d
are replaced by their values
Note that the keys of d
should be the canonical form of variables you wish to substitute. For example, to replace x(1)
with b/c
you need to have the entry (:x, 1) => :(b/c)
in d
.
Examples
julia> ex = :(a + b);
julia> d = Dict(:b => :(c/a), :c => :(2a));
julia> subs(ex, d) # subs is not recursive -- c is not replaced
:(a + c / a)
csubs
csubs
is closely related to subs
, but is more Clever and applies substitutions recursively.
csubs(::Expr, ::Associative, ::Set{Symbol})
csubs(::Expr, ::Associative)
Examples
julia> ex = :(a + b);
julia> d = Dict(:b => :(c/a), :c => :(2a));
julia> csubs(ex, d) # csubs is recursive -- c is replaced
:(a + (2a) / a)
julia> d1 = Dict(:monty=> :python, :run=>:faster, :eat=>:more);
julia> csubs(:(monty(run + eat, eat)), d1)
:(python(faster + more, more))
julia> csubs(:(a + b(0) + b(1)), Dict(:b => :(c(0) + d(1))))
:(a + (c(0) + d(1)) + (c(1) + d(2)))
julia> csubs(:(a + b(0) + b(1)), Dict((:b, 1) => :(c(0) + d(1))))
:(a + b(0) + (c(0) + d(1)))
julia> csubs(:(a + b(0) + b(1)), Dict(:b => :(c + d(1))))
:(a + (c + d(1)) + (c + d(2)))
julia> csubs(:(a + b(0) + b(1)), Dict((:b, 1) => :(c + d(1))))
:(a + b(0) + (c + d(1)))
julia> d = Dict((:b, 0) => :(c + d(1)), (:b, 1) => :(c(100) + d(2)));
julia> csubs(:(a + b + b(1)), d)
:(a + (c + d(1)) + (c(100) + d(2)))