Skip to content

Main functions

Spencer Lyon edited this page Sep 8, 2016 · 7 revisions

Symbolic operations

In symbolic.py/symbolic.jl ?

We recognize several types of symbols in expressions. Each of them is mapped to a unique string:

Symbol types Example Simple structure Normalized string Latex
parameter p :p _p_ p
variable v, v(-1), v(1) (:v,0), (:v,-1), (:v,1) _v___, _v__m1_, _v__p1_ v_{t}, v_{t-1}, v_{t+1}

Normalized symbols are created and converted back with:

normalize_symbol(e: expression) -> expression
denormalize_symbol(s: expression) -> expression

For full expressions:

normalize(e: expression) -> expression
denormalize(s: expression) -> expression

There are special functions to operate on expressions:

# shift timing in equations
# time_shift(:(a+b(1)+c),[:b,:c],1) == :(a+b(2)+c(1))
#
time_shift(expr: Expression, vars: List[variable], n) -> expr: Expression

# replace variables by steady-state values
# steady_state(:(a+b(1)+c),[:b,:c],1) == :(a+b+c)
#
steady_state(expr: Expression, vars: List[variable], n) -> expr: Expression

# list variables
# list_variables(:(a+b(1)+c), [:b,:c,:d]) == [(:b,1),(:c,0)]
#
list_variables(expr: Expression, vars: List[variables]) -> List[Variables]

# list symbols
# list_symbols(:(a+b(1)+c), {:parameters=>[:a],:variables=>[:b,:c]}) == [:a,(:b,1),(:c,0)]
#
list_symbols(expr: Expression, var_types: SomeDict) -> List[Symbols]

# substitute expression
# subs(:(a + b), :b, :(c+d(1)) == :(a+c+d(1))
#
subs(expr: Expression, symbol: Symbol, sexpr: Expression) -> Expression



# substitute expressions
# subs(:(a + b), {:b => :(c+d(1)}) == :(a+c+d(1))
#
subs(expr: Expression, sexpr: Dict) -> Expression


# cleverly substitute expression
# csubs(:(a + b + b(1)), :b, :(c+d(1)) == :(a+c+d(1)+c(1)+d(2))
#
csubs(expr: Expression, symbol: Symbol, sexpr: Expression) -> Expression

# cleverly substitute expression
# csubs(:(a + b + b(1)), {:b => :(c+d(1)}) == :(a+c+d(1)+c(1)+d(2))
#
csubs(expr: Expression, sexpr: Dict) -> Expression


# trisolve: solve triangular system
# trisove({:a=>:(k+b), :b=>:(c+d)}) == {:a=>:(k+c+d), :b=>:(c+d)}
#
trisolve(system: Dict) -> solution: OrderedDict

# ctrisolve: cleverly solve triangular system
# trisove({:a=>:(k+b(1)), :b=>:(c+d)}) == {:a=>:(k+c(1)+d(1)), :b=>:(c+d)}
ctrisolve(system: Dict) -> solution: OrderedDict

Printing

In latex.py/latex.jl.

Note the special rules for:

  • greek symbols ('beta' and β are both converted to '\beta'
  • subscripts, superscripts (a_i_j__k(1) printed as a_{i,j,t}^{k}
  • overbar, dot, star: abar_i_jstar__k(1) printed as \overline{a}_{i,j\star,t}^{k}
latex(var_struct) # latex((:a,1)) -> `a_{t+1}`
latex(expr, variables: List[symbols])
latex(expr, var_types: Dict)
latex(equation_string, var_types)   # latex('a = y(1)|y<=1', {:variables=>[:y]})

In compiler.py/compiler.jl (some of it is currently in codegen.py)

str(e: expression) -> string     # string(e) in Julia

Parsing:

in parser.(py|jl) ?

expressions

parse(s: string) -> expression

equations

In equations.py/equations.jl ?

There are several types of equations:

  • simple expression: expr
  • two sided expression: lhs = rhs
  • assignment: var=value
  • simple complementarity: eq(x) <= 0 | x <= 0
  • double complementarity: eq(x) | a<=x<=b or 0<=eq(x)<=0 | a<=x<=b, eq(x) | a<=x, etc...

These constructs are not necessarly syntactically correct in python/julia. For these reason, we parse the various elements using rational expressions.

parse_equation(s: string) -> dict[string,string]  # {'expr': ...}, or {'lhs': ..., 'rhs': ...}

Compile expressions

In compiler.py or function_compiler.py

We support two ways to create function from a list of equation.

We can compile higher order derivatives w.r.t. to one list of arguments:

f = make_function(
      [:(a+b),:(c+alpha*d(1))],          # list of equations
      [(:a,0),(:b,0),(:c,0),(d,:1)],     # list of variables to differentiate
      [:alpha]                           # list of parameters
)

res, jac, hes = f(x,p, diff=2)  # derivatives of any order w.r.t x

We can compiler first order derivatives w.r.t. to many arguments (vectorized by default)

f = make_function(
      [:(a+b),:(c+alpha*d(1))],          # list of equations
      {:states=>[(:a,0),(:b,0)],:controls=>[(:c,0)],:other=>[(d,:1)]}, # ordereddict of variables to differentiate
      [:alpha]                           # list of parameters
)

res, res_s, res_c, res_o = f(states, controls, other, diff=1)  # derivatives of any order w.r.t x

Terminology points:

  • Should have two different names ? Currently dolo has compile_higher_order_function for the first kind, make_function . We could dispatch on argument types but may loose some transparency
  • make_function seems adequate for a function which can behave in many different ways. If we compile a specific version instead (like vectorized/second order), we should call make_method instead. This would make it consistent with julia terminology and replace the loosely defined concept of standard_function in dolo. On the python side: model.functions would have clever functions which can do many things and may not be type-stable while model.methods (or smthg like), would provide a way to get specialized functions (possibly generated).

Additional options: the make_method/make_functions can take additional arguments:
- definitions: cleverly substituted variables (substituted formally or prepended to the code)
- targets: for multi-argument functions, we support recursive definitions which require a list of variables to be assigned
- funname
- mutating (`make_method`): ??
- allocating (`make_method`): whether output vector is created
Clone this wiki locally