Add "matexp" solver to nmodl#3574
Add "matexp" solver to nmodl#3574ctrl-z-9000-times wants to merge 22 commits intoneuronsimulator:masterfrom
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3574 +/- ##
==========================================
- Coverage 68.30% 68.30% -0.01%
==========================================
Files 689 691 +2
Lines 111033 111097 +64
==========================================
+ Hits 75844 75881 +37
- Misses 35189 35216 +27 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Interesting contribution, although I don't know the math. Have you thought about tests? Generally our tests are in test/ I do have some concerns about using unsupported library functions. |
|
The math is deceptively simple: Given an ODE: However I was planning on adding tests and documentation, but only after running this by all of you for review. |
I believe this is a useful addition. I notice the implementation is for the new nmodl and that is fine. nocmodl will be phased out. A possible place for the test is nrn/test/hoctests where you put the mod file there and the python file in tests. |
|
Thank you both for reviewing this! Todo list:
|
|
What's the advantage of adding this to the codebase? I can see the utility provided that the sparse solver is not adequate (accuracy-wise) in certain scenarios, but otherwise, at least at face value, it doesn't look like we gain much from it. |
|
|
Here is my justification: implementing the analytic solution is useful for understanding the accuracy of other solver methods. The sparse solver method is an approximation; its error is proportional to the time step, dt, and the time step affects how fast the simulation runs. With all numeric approximations, there is a trade-off between run-speed and accuracy. The exact solution will help users navigate this tradeoff. |
| " - see the [nmodl-kinetic-schemes](nmodl-kinetic-schemes.ipynb) notebook for more details\n", | ||
| " - solve method: `matexp`\n", | ||
| " - applicable if ODEs are linear & coupled\n", | ||
| " - exact analytic integration\n", |
There was a problem hiding this comment.
" - exact analytic integration if matrix is constant over the dt interval of integration\n"
Note that rates are often functions of voltage. In these cases, the default fixed step method results in an integration accuracy that is first order in dt. With the fixed step method and secondorder=2 integration accuracy is second order in dt. CVode (variable step methods) ignores the METHOD and uses its own implementations for y' = f(y) and evaluation of an approximate jacobian.
| const auto stmt = std::dynamic_pointer_cast<const ast::Conserve>(conserve_statements[0]); | ||
| const auto react = stmt->get_react(); | ||
| const auto vars = collect_nodes(*react, {ast::AstNodeType::NAME}); | ||
| const bool primes = node_exists(*react, {ast::AstNodeType::PRIME_NAME}); |
There was a problem hiding this comment.
Why is there a braces around scalar initializer [-Wbraced-scalar-init] warning and how can we get rid of it?
There was a problem hiding this comment.
I can't reproduce this warning. Maybe adding a comma between the AstNodeType and the closing brace will convince the compiler that this is an initializer list, not a scalar?
|
As we discussed in today's meeting, I will add a test that compares the matexp and cnexp solvers using an HH model. |
JCGoran
left a comment
There was a problem hiding this comment.
Implementation-wise, I believe this can be done better. With the verbatim blocks everywhere, this approach mixes the implementation (C++ code) with the underlying abstraction (NMOD language), which is not how any of the other visitors in NMODL work.
The usual approach to adding a new feature to NMODL is to check whether the AST needs to be manipulated, and if so, in what way. Then, implement a visitor which does the manipulation of NMODL constructs. After the AST has the correct structure, you can proceed and implement things at the codegen stage, for which no new visitors are necessary (unless you want to generate code for another programming language).
For instance, the current implementation directly inserts a verbatim block with Eigen headers in NMODL; this is completely unnecessary at this stage, and should be done at the codegen step instead. Depending on whether the changes work for coreNEURON or NEURON, one can insert it in codegen_coreneuron_cpp_visitor, codegen_neuron_cpp_visitor, or simply codegen_cpp_visitor if the changes work for both backends.
You can have a look at the following 2 PRs for reference how a new feature can be implemented with the 2-stage approach in NMODL (just an example, plenty of others in that repo):
- BlueBrain/nmodl#1467 (AST manipulation)
- BlueBrain/nmodl#1493 (actual codegen)
Note that opening 2 PRs is not strictly necessary, and they can be combined into one, but it was done this way (at least for this particular feature) to keep the changeset to a manageable size and not overload the reviewer.
0098688 to
6103ac2
Compare
331074c to
0241fa6
Compare
b86fbda to
cb2af22
Compare
|
Hi Everyone, This PR is ready for another round of review. Goran: I replaced all of the verbatim with a new AST node and some modifications to the codegen. Hines: I added a testcase to verify that for ODEs with a single variable the CNEXP solver yields identical results. This test compares two formulations of the same Hodgkin Huxley model:
Thanks |
|
@JCGoran, I took your advice and reimplemented this PR. I'd really appreciate if you could review this again before you leave? |
|
@ctrl-z-9000-times Do you have some docs on how the implementation works conceptually? I see there is a new node type for the codegen, but I'm having a bit of trouble following what's going on due to the size of the PR. Also, shouldn't there be an entry in the Some general comments:
Most of the PR looks good, and I'd be happy to have a final look once the above is answered. |
|
@JCGoran thank you for the detailed feedback The documentation is at:
|
b72713f to
35917e6
Compare
|
|



Hello,
I would like to add a new solver the new nmodl translator. The "matexp" solver computes the analytic solution for kinetic models that are linear, which includes all Markov models of ion channels. This solver is perfectly accurate, but runs between 25-50% slower than the "sparse" solver.
It uses Eigen's implementation of the matrix expopnential function, which is in the unsupported section of the library.
https://libeigen.gitlab.io/eigen/docs-nightly/unsupported/group__MatrixFunctions__Module.html#matrixbase_exp