-
Notifications
You must be signed in to change notification settings - Fork 8
Main functions
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
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 asa_{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
in parser.(py|jl)
?
parse(s: string) -> expression
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
or0<=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': ...}
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 callmake_method
instead. This would make it consistent with julia terminology and replace the loosely defined concept ofstandard_function
in dolo. On the python side:model.functions
would have clever functions which can do many things and may not be type-stable whilemodel.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