Symbolic manipulation

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_shiftMethod.
time_shift(x::Number, other...)

Return x for all values of other

source

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_shiftMethod.
time_shift(x::Number, other...)

Return x for all values of other

source

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.

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: add s to out[:parameters]

  • x(i::Integer): Add (x, i) out out[:variables]

  • All other function calls: add any arguments to out according to above rules

  • Anything else: do nothing.

source
list_symbols(::Expr;
             functions::Union{Set{Symbol},Vector{Symbol}}=Set{Symbol}())

Construct an empty Dict{Symbol,Any} and call list_symbols! to populate it

source

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.

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.

source

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.

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.

source

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.subsMethod.
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)

source

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.subsMethod.
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.

source
Dolang.subsMethod.
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.

source

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)))