NMODL SympySolver - sparse
This notebook describes the implementation of the sparse
part of the SympySolverVisitor
, which solves the systems of ODEs defined in DERIVATIVE
blocks when these ODEs are linear and coupled.
For a higher level overview of the approach to solving ODEs in NMODL, please see the nmodl-odes-overview notebook.
For a more general tutorial on using the NMODL python interface, please see the tutorial notebook.
Implementation
The SympySolverVisitor
for solver method sparse
does the following:
Construct the implicit Euler equations to convert the sysytem of ODEs to a
LINEAR
blockThis
LINEAR
block is then solved as described in nmodl-linear-solver
Implementation Tests
The unit tests may be helpful to understand what these functions are doing - SympySolverVisitor
tests are located in test/visitor/sympy_solver.cpp, and tests using the sparse
solver method have the tag “[sparse]
”
Examples
[1]:
%%capture
! pip install nmodl
[2]:
import nmodl.dsl as nmodl
def run_sympy_solver(mod_string, cse=False):
# parse NMDOL file (supplied as a string) into AST
driver = nmodl.NmodlDriver()
AST = driver.parse_string(mod_string)
# run SymtabVisitor to generate Symbol Table
nmodl.symtab.SymtabVisitor().visit_program(AST)
# constant folding, inlining & local variable renaming passes
nmodl.visitor.ConstantFolderVisitor().visit_program(AST)
nmodl.visitor.InlineVisitor().visit_program(AST)
nmodl.visitor.LocalVarRenameVisitor().visit_program(AST)
# run SympySolver visitor
nmodl.visitor.SympySolverVisitor().visit_program(AST)
# return contents of new DERIVATIVE block as list of strings
return nmodl.to_nmodl(
nmodl.visitor.AstLookupVisitor().lookup(
AST, nmodl.ast.AstNodeType.DERIVATIVE_BLOCK
)[0]
)
Ex. 1
2 coupled linear ODEs
[3]:
ex1 = """
BREAKPOINT {
SOLVE states METHOD sparse
}
STATE {
mc m
}
DERIVATIVE states {
mc' = -a*mc + b*m
m' = a*mc - b*m
}
"""
print(run_sympy_solver(ex1, cse=True))
DERIVATIVE states {
EIGEN_NEWTON_SOLVE[2]{
LOCAL old_mc, old_m
}{
old_mc = mc
old_m = m
}{
nmodl_eigen_x[0] = mc
nmodl_eigen_x[1] = m
}{
nmodl_eigen_f[0] = (-nmodl_eigen_x[0]+dt*(-nmodl_eigen_x[0]*a+nmodl_eigen_x[1]*b)+old_mc)/dt
nmodl_eigen_j[0] = -a-1/dt
nmodl_eigen_j[2] = b
nmodl_eigen_f[1] = (-nmodl_eigen_x[1]+dt*(nmodl_eigen_x[0]*a-nmodl_eigen_x[1]*b)+old_m)/dt
nmodl_eigen_j[1] = a
nmodl_eigen_j[3] = -b-1/dt
}{
mc = nmodl_eigen_x[0]
m = nmodl_eigen_x[1]
}{
}
}
Ex. 2
5 coupled linear ODEs
[4]:
ex2 = """
STATE {
c1 o1 o2 p0 p1
}
BREAKPOINT {
SOLVE ihkin METHOD sparse
}
DERIVATIVE ihkin {
LOCAL alpha, beta, k3p, k4, k1ca, k2
evaluate_fct(v, cai)
c1' = (-1*(alpha*c1-beta*o1))
o1' = (1*(alpha*c1-beta*o1))+(-1*(k3p*o1-k4*o2))
o2' = (1*(k3p*o1-k4*o2))
p0' = (-1*(k1ca*p0-k2*p1))
p1' = (1*(k1ca*p0-k2*p1))
}
"""
print(run_sympy_solver(ex2))
DERIVATIVE ihkin {
EIGEN_NEWTON_SOLVE[5]{
LOCAL alpha, beta, k3p, k4, k1ca, k2, old_c1, old_o1, old_o2, old_p0, old_p1
}{
evaluate_fct(v, cai)
old_c1 = c1
old_o1 = o1
old_o2 = o2
old_p0 = p0
old_p1 = p1
}{
nmodl_eigen_x[0] = c1
nmodl_eigen_x[1] = o1
nmodl_eigen_x[2] = o2
nmodl_eigen_x[3] = p0
nmodl_eigen_x[4] = p1
}{
nmodl_eigen_f[0] = (-nmodl_eigen_x[0]+dt*(-nmodl_eigen_x[0]*alpha+nmodl_eigen_x[1]*beta)+old_c1)/dt
nmodl_eigen_j[0] = -alpha-1/dt
nmodl_eigen_j[5] = beta
nmodl_eigen_j[10] = 0
nmodl_eigen_j[15] = 0
nmodl_eigen_j[20] = 0
nmodl_eigen_f[1] = (-nmodl_eigen_x[1]+dt*(nmodl_eigen_x[0]*alpha-nmodl_eigen_x[1]*beta-nmodl_eigen_x[1]*k3p+nmodl_eigen_x[2]*k4)+old_o1)/dt
nmodl_eigen_j[1] = alpha
nmodl_eigen_j[6] = -beta-k3p-1/dt
nmodl_eigen_j[11] = k4
nmodl_eigen_j[16] = 0
nmodl_eigen_j[21] = 0
nmodl_eigen_f[2] = (-nmodl_eigen_x[2]+dt*(nmodl_eigen_x[1]*k3p-nmodl_eigen_x[2]*k4)+old_o2)/dt
nmodl_eigen_j[2] = 0
nmodl_eigen_j[7] = k3p
nmodl_eigen_j[12] = -k4-1/dt
nmodl_eigen_j[17] = 0
nmodl_eigen_j[22] = 0
nmodl_eigen_f[3] = (-nmodl_eigen_x[3]+dt*(-nmodl_eigen_x[3]*k1ca+nmodl_eigen_x[4]*k2)+old_p0)/dt
nmodl_eigen_j[3] = 0
nmodl_eigen_j[8] = 0
nmodl_eigen_j[13] = 0
nmodl_eigen_j[18] = -k1ca-1/dt
nmodl_eigen_j[23] = k2
nmodl_eigen_f[4] = (-nmodl_eigen_x[4]+dt*(nmodl_eigen_x[3]*k1ca-nmodl_eigen_x[4]*k2)+old_p1)/dt
nmodl_eigen_j[4] = 0
nmodl_eigen_j[9] = 0
nmodl_eigen_j[14] = 0
nmodl_eigen_j[19] = k1ca
nmodl_eigen_j[24] = -k2-1/dt
}{
c1 = nmodl_eigen_x[0]
o1 = nmodl_eigen_x[1]
o2 = nmodl_eigen_x[2]
p0 = nmodl_eigen_x[3]
p1 = nmodl_eigen_x[4]
}{
}
}
[ ]: