User Guide
kinetic_block_visitor.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2023 Blue Brain Project, EPFL.
3  * See the top-level LICENSE file for details.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
9 
10 #include "ast/all.hpp"
12 #include "index_remover.hpp"
13 #include "loop_unroll_visitor.hpp"
14 #include "symtab/symbol.hpp"
15 #include "utils/logger.hpp"
16 #include "utils/string_utils.hpp"
17 #include "visitor_utils.hpp"
18 
19 #include <regex>
20 
21 
22 namespace nmodl {
23 namespace visitor {
24 
26 
27 void KineticBlockVisitor::process_reac_var(const std::string& varname, int count) {
28  // lookup index of state var
29  const auto it = state_var_index.find(varname);
30  if (it == state_var_index.cend()) {
31  // not a state var
32  // so this is a constant variable in the reaction statement
33  // this should be included in the fluxes:
36  logger->debug("KineticBlockVisitor :: adding non-state fflux[{}] \"{}\"",
38  varname);
39  } else {
41  logger->debug("KineticBlockVisitor :: adding non-state bflux[{}] \"{}\"",
43  varname);
44  }
45  // but as it is not a state var, no ODE should be generated for it, i.e. no nu_L or nu_R
46  // entry.
47  } else {
48  // found state var index
49  int i_statevar = it->second;
51  // set element of nu_L matrix
52  rate_eqs.nu_L[i_statement][i_statevar] += count;
53  logger->debug("KineticBlockVisitor :: nu_L[{}][{}] += {}",
55  i_statevar,
56  count);
57  } else {
58  // set element of nu_R matrix
59  rate_eqs.nu_R[i_statement][i_statevar] += count;
60  logger->debug("KineticBlockVisitor :: nu_R[{}][{}] += {}",
62  i_statevar,
63  count);
64  }
65  }
66 }
67 
68 void KineticBlockVisitor::process_conserve_reac_var(const std::string& varname, int count) {
69  // subtract previous term from both sides of equation
70  if (!conserve_equation_statevar.empty()) {
71  if (conserve_equation_factor.empty()) {
73 
74  } else {
77  }
78  }
79  // construct new term
80  auto compartment_factor = compartment_factors[state_var_index[varname]];
81  if (compartment_factor.empty()) {
82  if (count == 1) {
84  } else {
86  }
87  } else {
88  conserve_equation_factor = compartment_factor + "*" + std::to_string(count);
89  }
90  // if new term is not a state var raise error
91  if (state_var_index.find(varname) == state_var_index.cend()) {
92  logger->error(
93  "KineticBlockVisitor :: Error : CONSERVE statement should only contain state vars "
94  "on LHS, but found {}",
95  varname);
96  } else {
98  }
99 }
100 
101 std::shared_ptr<ast::Expression> create_expr(const std::string& str_expr) {
102  auto statement = create_statement("dummy = " + str_expr);
103  auto expr = std::dynamic_pointer_cast<ast::ExpressionStatement>(statement)->get_expression();
104  return std::dynamic_pointer_cast<ast::BinaryExpression>(expr)->get_rhs();
105 }
106 
109  // rewrite CONSERVE statement in form x = ...
110  // where x was the last state var on LHS, and whose ODE should later be replaced with this
111  // equation note: CONSERVE statement "implicitly takes into account COMPARTMENT factors on LHS"
112  // this means that each state var on LHS must be multiplied by its compartment factor
113  // the RHS is just an expression, no compartment factors are taken into account
114  // see p244 of NEURON book
115  logger->debug("KineticBlockVisitor :: CONSERVE statement: {}", to_nmodl(node));
119 
120  in_conserve_statement = true;
121  // construct equation to replace ODE in conserve_equation_str
122  node.visit_children(*this);
123  in_conserve_statement = false;
124 
126  if (!conserve_equation_factor.empty()) {
127  // divide by compartment factor of conserve_equation_statevar
129  ")";
130  }
131 
132  // note: The following 4 lines result in a still valid (and equivalent) CONSERVE statement.
133  // later this block will become a DERIVATIVE block where it is no longer valid
134  // to have a CONSERVE statement. Parsing the equivalent nmodl in between the
135  // kinetic visitor and the sympysolvervisitor in presence of a conserve statement
136  // should result in an error since we do not want to add new functionalities to the language.
137  // the SympySolver will use to it replace the ODE (to replicate what neuron does)
138  auto statement = create_statement("CONSERVE " + conserve_equation_statevar + " = " +
140  auto expr = std::dynamic_pointer_cast<ast::Conserve>(statement);
141  // set react (lhs) of CONSERVE to the state variable whose ODE should be replaced
142  node.set_react(expr->get_react());
143  // set expr (rhs) of CONSERVE to the equation that should replace the ODE
144  node.set_expr(expr->get_expr());
145 
146  logger->debug("KineticBlockVisitor :: --> {}", to_nmodl(node));
147 }
148 
149 void KineticBlockVisitor::set_compartment_factor(int var_index, const std::string& factor) {
150  if (compartment_factors[var_index] != "") {
151  throw std::runtime_error("Setting compartment volume twice.");
152  }
153 
154  compartment_factors[var_index] = factor;
155  logger->debug("KineticBlockVisitor :: COMPARTMENT factor {} for state var {} (index {})",
156  factor,
157  state_var[var_index],
158  var_index);
159 }
160 
162  const ast::Name& name) {
163  const auto& var_name = name.get_node_name();
164  const auto it = state_var_index.find(var_name);
165  if (it != state_var_index.cend()) {
166  int var_index = it->second;
167  auto expr = node.get_volume();
168  std::string expression = to_nmodl(expr);
169 
170  set_compartment_factor(var_index, expression);
171  } else {
172  logger->debug(
173  "KineticBlockVisitor :: COMPARTMENT specified volume for non-state variable {}",
174  var_name);
175  }
176 }
177 
179  const ast::Name& name) {
180  auto array_var_name = name.get_node_name();
181  auto index_name = node.get_index_name()->get_node_name();
182 
183  auto pattern = fmt::format("^{}\\[([0-9]*)\\]$", array_var_name);
184  std::regex re(pattern);
185  std::smatch m;
186 
187  for (size_t var_index = 0; var_index < state_var.size(); ++var_index) {
188  auto matches = std::regex_match(state_var[var_index], m, re);
189 
190  if (matches) {
191  int index_value = std::stoi(m[1]);
192  auto volume_expr = node.get_volume();
193  auto expr = std::shared_ptr<ast::Expression>(volume_expr->clone());
194  IndexRemover(index_name, index_value).visit_expression(*expr);
195 
196  std::string expression = to_nmodl(*expr);
197  set_compartment_factor(var_index, expression);
198  }
199  }
200 }
201 
203  // COMPARTMENT block has an expression, and a list of state vars it applies to.
204  // For each state var, the rhs of the differential eq should be divided by the expression.
205  // Here we store the expressions in the compartment_factors vector
206  logger->debug("KineticBlockVisitor :: COMPARTMENT expr: {}", to_nmodl(node.get_volume()));
207  for (const auto& name_ptr: node.get_species()) {
208  if (node.get_index_name() == nullptr) {
209  compute_compartment_factor(node, *name_ptr);
210  } else {
211  compute_indexed_compartment_factor(node, *name_ptr);
212  }
213  }
214  // add COMPARTMENT state to list of statements to remove
215  // since we don't want this statement to be present in the final DERIVATIVE block
216  statements_to_remove.insert(&node);
217 }
218 
220  auto reaction_op = node.get_value();
221  if (reaction_op == ast::ReactionOp::LTMINUSGT) {
222  // <->
223  // reversible reaction
224  // we go from visiting the lhs to visiting the rhs of the reaction statement
226  }
227 }
228 
230  ast::ReactVarName& node) { // NOLINT(readability-function-cognitive-complexity)
231  // ReactVarName node contains a VarName and an Integer
232  // the VarName is the state variable which we convert to an index
233  // the Integer is the value to be added to the stoichiometric matrix at this index
234  auto varname = to_nmodl(node.get_name());
235  int count = node.get_value() ? node.get_value()->eval() : 1;
236  if (in_reaction_statement) {
237  process_reac_var(varname, count);
238  } else if (in_conserve_statement) {
239  if (array_state_var_size.find(varname) != array_state_var_size.cend()) {
240  // state var is an array: need to sum over each element
241  for (int i = 0; i < array_state_var_size[varname]; ++i) {
242  process_conserve_reac_var(varname + "[" + std::to_string(i) + "]", count);
243  }
244  } else {
245  process_conserve_reac_var(varname, count);
246  }
247  }
248 }
249 
250 // NOLINTNEXTLINE(readability-function-cognitive-complexity)
252  statements_to_remove.insert(&node);
253 
254  auto reaction_op = node.get_op().get_value();
255  // special case for << statements
256  if (reaction_op == ast::ReactionOp::LTLT) {
257  logger->debug("KineticBlockVisitor :: '<<' reaction statement: {}", to_nmodl(node));
258  // statements involving the "<<" operator
259  // must have a single state var on lhs
260  // and a single expression on rhs that corresponds to d{state var}/dt
261  // So if x is a state var, then
262  // ~ x << (a*b)
263  // translates to the ODE contribution x' += a*b
264  const auto& lhs = node.get_reaction1();
265 
266  /// check if reaction statement is a single state variable
267  bool single_state_var = true;
268  if (lhs->is_react_var_name()) {
269  auto value = std::dynamic_pointer_cast<ast::ReactVarName>(lhs)->get_value();
270  if (value && (value->eval() != 1)) {
271  single_state_var = false;
272  }
273  }
274  if (!lhs->is_react_var_name() || !single_state_var) {
275  throw std::runtime_error(fmt::format(
276  "KineticBlockVisitor :: LHS of \"<<\" reaction statement must be a single state "
277  "var, but instead found {}: ignoring this statement",
278  to_nmodl(lhs)));
279  }
280  const auto& rhs = node.get_expression1();
281  std::string varname = to_nmodl(lhs);
282  // get index of state var
283  const auto it = state_var_index.find(varname);
284  if (it != state_var_index.cend()) {
285  int var_index = it->second;
286  std::string expr = to_nmodl(rhs);
287  if (!additive_terms[var_index].empty()) {
288  additive_terms[var_index] += " + ";
289  }
290  // add to additive terms for this state var
291  additive_terms[var_index] += fmt::format("({})", expr);
292  logger->debug("KineticBlockVisitor :: '<<' reaction statement: {}' += {}",
293  varname,
294  expr);
295  }
296  return;
297  }
298 
299  // forwards reaction rate
300  const auto& kf = node.get_expression1();
301  // backwards reaction rate
302  const auto& kb = node.get_expression2();
303 
304  // add reaction rates to vectors kf, kb
305  auto kf_str = to_nmodl(kf);
306  logger->debug("KineticBlockVisitor :: k_f[{}] = {}", i_statement, kf_str);
307  rate_eqs.k_f.emplace_back(kf_str);
308 
309  if (kb) {
310  // kf is always defined, but for statements with operator "->" kb is not
311  auto kb_str = to_nmodl(kb);
312  logger->debug("KineticBlockVisitor :: k_b[{}] = {}", i_statement, kb_str);
313  rate_eqs.k_b.emplace_back(kb_str);
314  } else {
315  rate_eqs.k_b.emplace_back();
316  }
317 
318  // add empty non state var fluxes for this statement
319  non_state_var_fflux.emplace_back();
320  non_state_var_bflux.emplace_back();
321 
322  // add a row of zeros to the stoichiometric matrices
323  rate_eqs.nu_L.emplace_back(std::vector<int>(state_var_count, 0));
324  rate_eqs.nu_R.emplace_back(std::vector<int>(state_var_count, 0));
325 
326  // visit each term in reaction statement and
327  // add the corresponding integer to the new row in the matrix
328  in_reaction_statement = true;
330  node.visit_children(*this);
331  in_reaction_statement = false;
332 
333  // generate fluxes
334  modfile_fflux = rate_eqs.k_f.back();
335  modfile_bflux = rate_eqs.k_b.back();
336 
337  // contribution from state vars
338  for (int j = 0; j < state_var_count; ++j) {
339  std::string multiply_var = std::string("*").append(state_var[j]);
340  int nu_L = rate_eqs.nu_L[i_statement][j];
341  while (nu_L-- > 0) {
342  modfile_fflux += multiply_var;
343  }
344  int nu_R = rate_eqs.nu_R[i_statement][j];
345  while (nu_R-- > 0) {
346  modfile_bflux += multiply_var;
347  }
348  }
349  // contribution from non-state vars
350  if (!non_state_var_fflux[i_statement].empty()) {
351  modfile_fflux += std::string("*").append(non_state_var_fflux[i_statement]);
352  }
353  if (!non_state_var_bflux[i_statement].empty()) {
354  modfile_bflux += std::string("*").append(non_state_var_bflux[i_statement]);
355  }
356  fflux.emplace_back(modfile_fflux);
357  bflux.emplace_back(modfile_bflux);
358 
359  // for substituting into modfile, empty flux should be 0
360  if (modfile_fflux.empty()) {
361  modfile_fflux = "0";
362  }
363  if (modfile_bflux.empty()) {
364  modfile_bflux = "0";
365  }
366 
367  logger->debug("KineticBlockVisitor :: fflux[{}] = {}", i_statement, fflux[i_statement]);
368  logger->debug("KineticBlockVisitor :: bflux[{}] = {}", i_statement, bflux[i_statement]);
369 
370  // increment statement counter
371  ++i_statement;
372 }
373 
375  // If a wrapped expression contains a variable with name "f_flux" or "b_flux",
376  // this variable should be replaced by the expression for the corresponding flux
377  // which depends on the previous reaction statement. The current expressions are
378  // stored as strings in "modfile_fflux" and "modfile_bflux"
379  if (node.get_expression()->is_name()) {
380  auto var_name = std::dynamic_pointer_cast<ast::Name>(node.get_expression());
381  if (var_name->get_node_name() == "f_flux") {
382  auto expr = create_expr(modfile_fflux);
383  logger->debug("KineticBlockVisitor :: replacing f_flux with {}", to_nmodl(expr));
384  node.set_expression(std::move(expr));
385  } else if (var_name->get_node_name() == "b_flux") {
386  auto expr = create_expr(modfile_bflux);
387  logger->debug("KineticBlockVisitor :: replacing b_flux with {}", to_nmodl(expr));
388  node.set_expression(std::move(expr));
389  }
390  }
391  node.visit_children(*this);
392 }
393 
395  auto prev_statement_block = current_statement_block;
396  current_statement_block = &node;
397  node.visit_children(*this);
398  // remove processed statements from current statement block
400  current_statement_block = prev_statement_block;
401 }
402 
404  rate_eqs.nu_L.clear();
405  rate_eqs.nu_R.clear();
406  rate_eqs.k_f.clear();
407  rate_eqs.k_b.clear();
408  fflux.clear();
409  bflux.clear();
410  odes.clear();
411  modfile_fflux = "0";
412  modfile_bflux = "0";
413 
414  // allocate these vectors with empty strings
415  compartment_factors = std::vector<std::string>(state_var_count);
416  additive_terms = std::vector<std::string>(state_var_count);
417  i_statement = 0;
418 
419  // construct stochiometric matrices and fluxes
420  node.visit_children(*this);
421 
422  // number of reaction statements
423  int Ni = static_cast<int>(rate_eqs.k_f.size());
424 
425  // number of ODEs (= number of state vars)
426  int Nj = state_var_count;
427 
428  // generate ODEs
429  for (int j = 0; j < Nj; ++j) {
430  // rhs of ODE eq
431  std::string ode_rhs = additive_terms[j];
432  for (int i = 0; i < Ni; ++i) {
433  int delta_nu = rate_eqs.nu_R[i][j] - rate_eqs.nu_L[i][j];
434  if (delta_nu != 0) {
435  // if not the first RHS term, add + sign first
436  if (!ode_rhs.empty()) {
437  ode_rhs += " + ";
438  }
439  if (bflux[i].empty()) {
440  ode_rhs += fmt::format("({}*({}))", delta_nu, fflux[i]);
441  } else if (fflux[i].empty()) {
442  ode_rhs += fmt::format("({}*(-{}))", delta_nu, bflux[i]);
443  } else {
444  ode_rhs += fmt::format("({}*({}-{}))", delta_nu, fflux[i], bflux[i]);
445  }
446  }
447  }
448  // divide by COMPARTMENT factor if present
449  if (!compartment_factors[j].empty() && !ode_rhs.empty()) {
450  ode_rhs = fmt::format("({})/({})", ode_rhs, compartment_factors[j]);
451  }
452  // if rhs of ODE is not empty, add to list of ODEs
453  if (!ode_rhs.empty()) {
454  auto state_var_split = stringutils::split_string(state_var[j], '[');
455  std::string var_str = state_var_split[0];
456  std::string index_str;
457  if (state_var_split.size() > 1) {
458  index_str = "[" + state_var_split[1];
459  }
460  odes.push_back(fmt::format("{}'{} = {}", var_str, index_str, ode_rhs));
461  }
462  }
463 
464  for (const auto& ode: odes) {
465  logger->debug("KineticBlockVisitor :: ode : {}", ode);
466  }
467 
468  const auto& kinetic_statement_block = node.get_statement_block();
469  // remove any remaining kinetic statements
470  kinetic_statement_block->erase_statement(statements_to_remove);
471  // add new statements
472  for (const auto& ode: odes) {
473  logger->debug("KineticBlockVisitor :: -> adding statement: {}", ode);
474  kinetic_statement_block->emplace_back_statement(create_statement(ode));
475  }
476 
477  // store pointer to kinetic block
478  kinetic_blocks.push_back(&node);
479 }
480 
482  static constexpr auto kf_stem = "kf";
483  static constexpr auto kb_stem = "kb";
484  static constexpr auto source_stem = "source";
485 
486  std::shared_ptr<ast::Name> generate_local_name(const std::string& stem) {
487  std::string unmangled_name = fmt::format("{}{}_", stem, n_equations);
488  std::string mangled_name = unmangled_name;
489 
490  size_t mangle_attempt = 0;
491  while (symtab->lookup_in_scope(mangled_name)) {
492  mangled_name = fmt::format("{}{:04d}", unmangled_name, mangle_attempt++);
493 
494  if (mangle_attempt >= 10000) {
495  throw std::runtime_error("Failed to find unique local name.");
496  }
497  }
498 
499  auto name = std::make_shared<ast::Name>(std::make_shared<ast::String>(mangled_name));
500  local_names.push_back(mangled_name);
501 
502  return name;
503  }
504 
505  public:
506  LocalRateNames() = default;
507  LocalRateNames(const LocalRateNames&) = default;
508  LocalRateNames(LocalRateNames&&) = default;
509 
511  : symtab(symtab) {}
512 
513  LocalRateNames& operator=(const LocalRateNames&) = default;
515 
516  std::shared_ptr<ast::Name> generate_forward_rate_name() {
517  auto kf = generate_local_name(kf_stem);
518  ++n_equations;
519 
520  return kf;
521  }
522 
523  std::pair<std::shared_ptr<ast::Name>, std::shared_ptr<ast::Name>> generate_rate_names() {
524  auto kf = generate_local_name(kf_stem);
525  auto kb = generate_local_name(kb_stem);
526  ++n_equations;
527 
528  return {kf, kb};
529  }
530 
531  std::shared_ptr<ast::Name> generate_source_name() {
532  auto source = generate_local_name(source_stem);
533  ++n_equations;
534 
535  return source;
536  }
537 
538  std::vector<std::string> get_local_variable_names() {
539  return local_names;
540  }
541 
542  private:
543  size_t n_equations = 0;
544  std::vector<std::string> local_names;
545  symtab::SymbolTable const* symtab = nullptr;
546 };
547 
549  public:
551 
552  private:
553  std::shared_ptr<ast::ExpressionStatement> localize_expression(
554  const std::shared_ptr<ast::Name>& name,
555  const std::shared_ptr<ast::Expression>& expression) {
556  auto local = std::make_shared<ast::BinaryExpression>(name,
558  expression);
559  return std::make_shared<ast::ExpressionStatement>(local);
560  }
561 
562  template <class Node>
563  std::shared_ptr<Node> clone(const Node& node) {
564  return std::shared_ptr<Node>(node.clone());
565  }
566 
567  public:
569  auto stmt_block = node.get_statement_block();
570  local_names = LocalRateNames(stmt_block->get_symbol_table());
571 
572  // process the statement block first.
573  node.visit_children(*this);
574 
575  // We now know the names of the created LOCAL variables. If a LOCAL
576  // block exists, append to that, otherwise create a new one.
577  auto locals = local_names.get_local_variable_names();
578 
579  for (const auto& local: locals) {
580  add_local_variable(*stmt_block, local);
581  }
582  }
583 
584 
586  // process any nested statement blocks first:
587  node.visit_children(*this);
588 
589  // process all reaction equations:
590  const auto& statements = node.get_statements();
591  for (auto iter = statements.begin(); iter != statements.end(); ++iter) {
592  if ((*iter)->is_reaction_statement()) {
593  auto reaction_equation = std::dynamic_pointer_cast<ast::ReactionStatement>(*iter);
594  auto op = reaction_equation->get_op();
595 
596  ast::StatementVector localized_statements;
597 
598  auto local_expr1 = reaction_equation->get_expression1();
599  auto local_expr2 = reaction_equation->get_expression2();
600 
601  std::shared_ptr<ast::Name> expr1_name = nullptr;
602  std::shared_ptr<ast::Name> expr2_name = nullptr;
603 
604  if (op.get_value() == ast::LTLT) {
605  expr1_name = local_names.generate_source_name();
606  } else if (op.get_value() == ast::LTMINUSGT) {
607  std::tie(expr1_name, expr2_name) = local_names.generate_rate_names();
608  } else if (op.get_value() == ast::MINUSGT) {
610  }
611 
612  if (local_expr1) {
613  auto assignment = localize_expression(clone(*expr1_name),
614  reaction_equation->get_expression1());
615  localized_statements.push_back(assignment);
616  local_expr1 = clone(*expr1_name);
617  }
618 
619  if (local_expr2) {
620  auto assignment = localize_expression(clone(*expr2_name),
621  reaction_equation->get_expression2());
622  localized_statements.push_back(assignment);
623  local_expr2 = clone(*expr2_name);
624  }
625 
626  auto local_reaction =
627  std::make_shared<ast::ReactionStatement>(reaction_equation->get_reaction1(),
628  reaction_equation->get_op(),
629  reaction_equation->get_reaction2(),
630  local_expr1,
631  local_expr2);
632  localized_statements.push_back(local_reaction);
633 
634  iter = node.erase_statement(iter);
635  for (const auto& stmt: localized_statements) {
636  // `iter` points to the element after the one
637  // we've inserted.
638  iter = ++node.insert_statement(iter, stmt);
639  }
640  --iter;
641  }
642  }
643  }
644 };
645 
646 
648  const auto& kineticBlockNodes = collect_nodes(node, {ast::AstNodeType::KINETIC_BLOCK});
649 
650  // Before: FROM i = 0 TO N-1
651  // After: FROM i = 0 TO 2
652  visitor::ConstantFolderVisitor const_folder;
653  for (const auto& ii: kineticBlockNodes) {
654  ii->accept(const_folder);
655  }
656 
657  // Before:
658  // FROM i = 0 TO 2 {
659  // ~ ca[i] <-> ca[i+1] (a, b)
660  // }
661  //
662  // After:
663  // ~ ca[0] <-> ca[0+1] (a, b)
664  // ~ ca[1] <-> ca[1+1] (a, b)
665  // ~ ca[2] <-> ca[2+1] (a, b)
666  //
668  for (const auto& ii: kineticBlockNodes) {
669  ii->accept(unroller);
670  }
671 
672  // Before: ca[0+1]
673  // After: ca[1]
674  for (const auto& ii: kineticBlockNodes) {
675  ii->accept(const_folder);
676  }
677 
678  auto visitor = LocalizeKineticRatesVisitor{};
679  node.accept(visitor);
680 }
681 
683  unroll_kinetic_blocks(node);
684 
686  statements_to_remove.clear();
687  current_statement_block = nullptr;
688 
689  // get state variables - assign an index to each
690  state_var_index.clear();
691  array_state_var_size.clear();
692  state_var.clear();
693  state_var_count = 0;
694  if (auto symtab = node.get_symbol_table()) {
695  auto statevars = symtab->get_variables_with_properties(NmodlType::state_var);
696  for (const auto& v: statevars) {
697  std::string var_name = v->get_name();
698  if (v->is_array()) {
699  // CONSERVE statement needs to know this is an array state var, and its size:
700  array_state_var_size[var_name] = v->get_length();
701  // for array state vars we need to add each element of the array separately
702  var_name += "[";
703  for (int i = 0; i < v->get_length(); ++i) {
704  std::string var_name_i = var_name + std::to_string(i) + "]";
705  logger->debug("KineticBlockVisitor :: state_var_index[{}] = {}",
706  var_name_i,
708  state_var_index[var_name_i] = state_var_count++;
709  state_var.push_back(var_name_i);
710  }
711  } else {
712  logger->debug("KineticBlockVisitor :: state_var_index[{}] = {}",
713  var_name,
715  state_var_index[var_name] = state_var_count++;
716  state_var.push_back(var_name);
717  }
718  }
719  }
720 
721  const auto& kineticBlockNodes = collect_nodes(node, {ast::AstNodeType::KINETIC_BLOCK});
722  // replace reaction statements within each kinetic block with equivalent ODEs
723  for (const auto& ii: kineticBlockNodes) {
724  ii->accept(*this);
725  }
726 
727  // change KINETIC blocks -> DERIVATIVE blocks
728  auto blocks = node.get_blocks();
729  for (auto* kinetic_block: kinetic_blocks) {
730  for (auto it = blocks.begin(); it != blocks.end(); ++it) {
731  if (it->get() == kinetic_block) {
732  auto dblock =
733  std::make_shared<ast::DerivativeBlock>(kinetic_block->get_name(),
734  kinetic_block->get_statement_block());
735  ModToken tok{};
736  dblock->set_token(tok);
737  *it = dblock;
738  }
739  }
740  }
741  node.set_blocks(std::move(blocks));
742 }
743 
744 } // namespace visitor
745 } // namespace nmodl
nmodl::visitor::KineticBlockVisitor::visit_wrapped_expression
void visit_wrapped_expression(ast::WrappedExpression &node) override
visit node of type ast::WrappedExpression
Definition: kinetic_block_visitor.cpp:374
nmodl::visitor::LocalizeKineticRatesVisitor::localize_expression
std::shared_ptr< ast::ExpressionStatement > localize_expression(const std::shared_ptr< ast::Name > &name, const std::shared_ptr< ast::Expression > &expression)
Definition: kinetic_block_visitor.cpp:553
nmodl::ast::Node
Base class for all AST node.
Definition: node.hpp:40
nmodl::to_nmodl
std::string to_nmodl(const ast::Ast &node, const std::set< ast::AstNodeType > &exclude_types)
Given AST node, return the NMODL string representation.
Definition: visitor_utils.cpp:234
nmodl::ast::WrappedExpression::get_expression
std::shared_ptr< Expression > get_expression() const noexcept
Getter for member variable WrappedExpression::expression.
Definition: wrapped_expression.hpp:143
nmodl::visitor::KineticBlockVisitor::compute_compartment_factor
void compute_compartment_factor(ast::Compartment &node, const ast::Name &name)
Definition: kinetic_block_visitor.cpp:161
nmodl::ast::ReactionStatement::get_expression1
std::shared_ptr< Expression > get_expression1() const noexcept
Getter for member variable ReactionStatement::expression1.
Definition: reaction_statement.hpp:193
nmodl::visitor::LocalRateNames::get_local_variable_names
std::vector< std::string > get_local_variable_names()
Definition: kinetic_block_visitor.cpp:538
nmodl::visitor::KineticBlockVisitor::array_state_var_size
std::unordered_map< std::string, int > array_state_var_size
unordered_map from array state variable to its size (for summing over each element of any array state...
Definition: kinetic_block_visitor.hpp:111
symbol.hpp
Implement class to represent a symbol in Symbol Table.
nmodl::ast::ReactionOperator::get_value
ReactionOp get_value() const noexcept
Getter for member variable ReactionOperator::value.
Definition: reaction_operator.hpp:143
nmodl::visitor::LocalRateNames::generate_local_name
std::shared_ptr< ast::Name > generate_local_name(const std::string &stem)
Definition: kinetic_block_visitor.cpp:486
nmodl::visitor::LocalRateNames::LocalRateNames
LocalRateNames()=default
nmodl::ast::AstNodeType::KINETIC_BLOCK
@ KINETIC_BLOCK
type of ast::KineticBlock
nmodl::visitor::KineticBlockVisitor::visit_statement_block
void visit_statement_block(ast::StatementBlock &node) override
visit node of type ast::StatementBlock
Definition: kinetic_block_visitor.cpp:394
nmodl::visitor::KineticBlockVisitor::process_conserve_reac_var
void process_conserve_reac_var(const std::string &varname, int count=1)
update CONSERVE statement with reaction var term
Definition: kinetic_block_visitor.cpp:68
nmodl::visitor::LocalRateNames::generate_source_name
std::shared_ptr< ast::Name > generate_source_name()
Definition: kinetic_block_visitor.cpp:531
nmodl::ast::StatementBlock::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:3162
nmodl::ast::Conserve
Represent CONSERVE statement in NMODL.
Definition: conserve.hpp:38
nmodl::ast::Conserve::get_expr
std::shared_ptr< Expression > get_expr() const noexcept
Getter for member variable Conserve::expr.
Definition: conserve.hpp:168
nmodl::ast::ReactionStatement::get_expression2
std::shared_ptr< Expression > get_expression2() const noexcept
Getter for member variable ReactionStatement::expression2.
Definition: reaction_statement.hpp:202
nmodl::visitor::KineticBlockVisitor::non_state_var_fflux
std::vector< std::string > non_state_var_fflux
multiplicate constant terms for fluxes from non-state vars as reactants e.g.
Definition: kinetic_block_visitor.hpp:86
nmodl::ast::StatementVector
std::vector< std::shared_ptr< Statement > > StatementVector
Definition: ast_decl.hpp:302
nmodl::visitor::KineticBlockVisitor::conserve_statement_count
int conserve_statement_count
counts the number of CONSERVE statements in Kinetic blocks
Definition: kinetic_block_visitor.hpp:123
nmodl::ast::KineticBlock::get_statement_block
std::shared_ptr< StatementBlock > get_statement_block() const noexcept override
Getter for member variable KineticBlock::statement_block.
Definition: kinetic_block.hpp:208
nmodl::ast::Compartment::get_index_name
std::shared_ptr< Name > get_index_name() const noexcept
Getter for member variable Compartment::index_name.
Definition: compartment.hpp:162
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
nmodl::visitor::LocalRateNames::kb_stem
static constexpr auto kb_stem
Definition: kinetic_block_visitor.cpp:483
nmodl::ast::ReactVarName
TODO.
Definition: react_var_name.hpp:38
nmodl::visitor::KineticBlockVisitor::compartment_factors
std::vector< std::string > compartment_factors
multiplicative factors for ODEs from COMPARTMENT statements
Definition: kinetic_block_visitor.hpp:77
nmodl::ast::Compartment::get_species
const NameVector & get_species() const noexcept
Getter for member variable Compartment::species.
Definition: compartment.hpp:180
nmodl::ast::StatementBlock::insert_statement
StatementVector::const_iterator insert_statement(StatementVector::const_iterator position, const std::shared_ptr< Statement > &n)
Insert member to statements.
Definition: ast.cpp:3134
loop_unroll_visitor.hpp
Unroll for loop in the AST.
nmodl::visitor::KineticBlockVisitor::visit_program
void visit_program(ast::Program &node) override
visit node of type ast::Program
Definition: kinetic_block_visitor.cpp:682
constant_folder_visitor.hpp
Perform constant folding of integer/float/double expressions.
nmodl::visitor::KineticBlockVisitor::state_var_count
int state_var_count
number of state variables
Definition: kinetic_block_visitor.hpp:101
nmodl::visitor::KineticBlockVisitor::visit_compartment
void visit_compartment(ast::Compartment &node) override
visit node of type ast::Compartment
Definition: kinetic_block_visitor.cpp:202
nmodl::ast::ReactVarName::get_name
std::shared_ptr< VarName > get_name() const noexcept
Getter for member variable ReactVarName::name.
Definition: react_var_name.hpp:166
nmodl::visitor::IndexRemover
Helper visitor to replace index of array variable with integer.
Definition: index_remover.hpp:28
string_utils.hpp
Implement string manipulation functions.
nmodl::ast::Program::accept
void accept(visitor::Visitor &v) override
accept (or visit) the current AST node using provided visitor
Definition: ast.cpp:12922
nmodl::ast::BOP_ASSIGN
@ BOP_ASSIGN
=
Definition: ast_common.hpp:59
nmodl::visitor::KineticBlockVisitor::fflux
std::vector< std::string > fflux
generated set of fluxes and ODEs
Definition: kinetic_block_visitor.hpp:90
nmodl::visitor::KineticBlockVisitor::conserve_equation_statevar
std::string conserve_equation_statevar
conserve statement: current state variable being processed
Definition: kinetic_block_visitor.hpp:129
nmodl::ast::Conserve::set_expr
void set_expr(std::shared_ptr< Expression > &&expr)
Setter for member variable Conserve::expr (rvalue reference)
Definition: ast.cpp:10250
nmodl::logger
logger_type logger
Definition: logger.cpp:34
nmodl::visitor::KineticBlockVisitor::visit_reaction_operator
void visit_reaction_operator(ast::ReactionOperator &node) override
visit node of type ast::ReactionOperator
Definition: kinetic_block_visitor.cpp:219
nmodl::ast::KineticBlock::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:5147
nmodl::ast::ReactionStatement::get_reaction1
std::shared_ptr< Expression > get_reaction1() const noexcept
Getter for member variable ReactionStatement::reaction1.
Definition: reaction_statement.hpp:166
nmodl::visitor::KineticBlockVisitor::rate_eqs
struct nmodl::visitor::KineticBlockVisitor::RateEqs rate_eqs
nmodl::visitor::LocalizeKineticRatesVisitor::visit_kinetic_block
void visit_kinetic_block(ast::KineticBlock &node)
visit node of type ast::KineticBlock
Definition: kinetic_block_visitor.cpp:568
nmodl::ast::StatementBlock::get_statements
const StatementVector & get_statements() const noexcept
Getter for member variable StatementBlock::statements.
Definition: statement_block.hpp:221
nmodl::visitor::LoopUnrollVisitor
Unroll for loop in the AST.
Definition: loop_unroll_visitor.hpp:59
nmodl::ast::ReactionOperator
TODO.
Definition: reaction_operator.hpp:38
nmodl::visitor::KineticBlockVisitor::visit_conserve
void visit_conserve(ast::Conserve &node) override
visit node of type ast::Conserve
Definition: kinetic_block_visitor.cpp:107
kinetic_block_visitor.hpp
Visitor for kinetic block statements
nmodl::visitor::ConstantFolderVisitor
Perform constant folding of integer/float/double expressions.
Definition: constant_folder_visitor.hpp:53
visitor_utils.hpp
Utility functions for visitors implementation.
nmodl::visitor::KineticBlockVisitor::in_reaction_statement_lhs
bool in_reaction_statement_lhs
true if we are visiting the left hand side of reaction statement
Definition: kinetic_block_visitor.hpp:117
nmodl::visitor::LocalRateNames::LocalRateNames
LocalRateNames(symtab::SymbolTable const *symtab)
Definition: kinetic_block_visitor.cpp:510
nmodl::visitor::KineticBlockVisitor::i_statement
int i_statement
current statement index
Definition: kinetic_block_visitor.hpp:135
nmodl::visitor::LocalizeKineticRatesVisitor::clone
std::shared_ptr< Node > clone(const Node &node)
Definition: kinetic_block_visitor.cpp:563
nmodl::ast::StatementBlock::erase_statement
StatementVector::const_iterator erase_statement(StatementVector::const_iterator first)
Erase member to statements.
Definition: ast.cpp:3096
nmodl::ast::Conserve::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:10161
nmodl::visitor::KineticBlockVisitor::in_conserve_statement
bool in_conserve_statement
true if we are visiting a CONSERVE statement
Definition: kinetic_block_visitor.hpp:120
nmodl::visitor::LocalizeKineticRatesVisitor
Definition: kinetic_block_visitor.cpp:548
nmodl::visitor::KineticBlockVisitor::set_compartment_factor
void set_compartment_factor(int var_index, const std::string &factor)
Definition: kinetic_block_visitor.cpp:149
nmodl::visitor::LocalRateNames::n_equations
size_t n_equations
Definition: kinetic_block_visitor.cpp:543
nmodl::visitor::AstVisitor::visit_expression
void visit_expression(ast::Expression &node) override
visit node of type ast::Expression
Definition: ast_visitor.cpp:28
nmodl::visitor::KineticBlockVisitor::statements_to_remove
std::unordered_set< ast::Statement * > statements_to_remove
statements to remove from block
Definition: kinetic_block_visitor.hpp:141
nmodl::symtab::syminfo::to_string
std::string to_string(const T &obj)
Definition: symbol_properties.hpp:282
nmodl::visitor::AstVisitor
Concrete visitor for all AST classes.
Definition: ast_visitor.hpp:37
nmodl::ast::WrappedExpression::set_expression
void set_expression(std::shared_ptr< Expression > &&expression)
Setter for member variable WrappedExpression::expression (rvalue reference)
Definition: ast.cpp:14020
nmodl::visitor::KineticBlockVisitor::modfile_fflux
std::string modfile_fflux
current expressions for the fflux, bflux variables that can be used in the mod file and that are dete...
Definition: kinetic_block_visitor.hpp:97
nmodl::visitor::KineticBlockVisitor::visit_reaction_statement
void visit_reaction_statement(ast::ReactionStatement &node) override
visit node of type ast::ReactionStatement
Definition: kinetic_block_visitor.cpp:251
nmodl::ast::ReactionStatement::get_op
const ReactionOperator & get_op() const noexcept
Getter for member variable ReactionStatement::op.
Definition: reaction_statement.hpp:175
nmodl::visitor::LocalRateNames::symtab
symtab::SymbolTable const * symtab
Definition: kinetic_block_visitor.cpp:545
nmodl::visitor::KineticBlockVisitor::state_var
std::vector< std::string > state_var
state variables vector
Definition: kinetic_block_visitor.hpp:104
nmodl::visitor::KineticBlockVisitor::conserve_equation_str
std::string conserve_equation_str
conserve statement equation as string
Definition: kinetic_block_visitor.hpp:126
nmodl::visitor::KineticBlockVisitor::compute_indexed_compartment_factor
void compute_indexed_compartment_factor(ast::Compartment &node, const ast::Name &name)
Definition: kinetic_block_visitor.cpp:178
nmodl::symtab::SymbolTable
Represent symbol table for a NMODL block.
Definition: symbol_table.hpp:57
nmodl::ast::KineticBlock
TODO.
Definition: kinetic_block.hpp:39
nmodl::visitor::KineticBlockVisitor::bflux
std::vector< std::string > bflux
Definition: kinetic_block_visitor.hpp:91
nmodl::visitor::create_statement
std::shared_ptr< Statement > create_statement(const std::string &code_statement)
Convert given code statement (in string format) to corresponding ast node.
Definition: visitor_utils.cpp:127
nmodl::collect_nodes
std::vector< std::shared_ptr< const ast::Ast > > collect_nodes(const ast::Ast &node, const std::vector< ast::AstNodeType > &types)
traverse node recursively and collect nodes of given types
Definition: visitor_utils.cpp:206
nmodl::ast::Compartment
Represent COMPARTMENT statement in NMODL.
Definition: compartment.hpp:39
index_remover.hpp
nmodl::visitor::create_expr
std::shared_ptr< ast::Expression > create_expr(const std::string &str_expr)
Definition: kinetic_block_visitor.cpp:101
nmodl::visitor::LocalRateNames::source_stem
static constexpr auto source_stem
Definition: kinetic_block_visitor.cpp:484
nmodl::visitor::KineticBlockVisitor::visit_react_var_name
void visit_react_var_name(ast::ReactVarName &node) override
visit node of type ast::ReactVarName
Definition: kinetic_block_visitor.cpp:229
nmodl::visitor::LocalRateNames::operator=
LocalRateNames & operator=(const LocalRateNames &)=default
nmodl::ast::ReactionStatement::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:10610
nmodl::visitor::LocalRateNames::local_names
std::vector< std::string > local_names
Definition: kinetic_block_visitor.cpp:544
nmodl::stringutils::split_string
static std::vector< std::string > split_string(const std::string &text, char delimiter)
Split a text in a list of words, using a given delimiter character.
Definition: string_utils.hpp:116
nmodl::ast::Node::clone
Node * clone() const override
Return a copy of the current node.
Definition: node.hpp:67
nmodl::ast::StatementBlock
Represents block encapsulating list of statements.
Definition: statement_block.hpp:53
nmodl::ast::Conserve::set_react
void set_react(std::shared_ptr< Expression > &&react)
Setter for member variable Conserve::react (rvalue reference)
Definition: ast.cpp:10233
nmodl::symtab::syminfo::NmodlType
NmodlType
NMODL variable properties.
Definition: symbol_properties.hpp:116
nmodl::ast::LTMINUSGT
@ LTMINUSGT
Definition: ast_common.hpp:92
nmodl::ast::Program::get_blocks
const NodeVector & get_blocks() const noexcept
Getter for member variable Program::blocks.
Definition: program.hpp:216
nmodl::visitor::KineticBlockVisitor::modfile_bflux
std::string modfile_bflux
Definition: kinetic_block_visitor.hpp:98
nmodl::visitor::KineticBlockVisitor::RateEqs::k_b
std::vector< std::string > k_b
Definition: kinetic_block_visitor.hpp:73
nmodl::ast::ReactionStatement
TODO.
Definition: reaction_statement.hpp:39
nmodl::ast::BinaryOperator
Operator used in ast::BinaryExpression.
Definition: binary_operator.hpp:38
nmodl::visitor::KineticBlockVisitor::additive_terms
std::vector< std::string > additive_terms
additive constant terms for ODEs from reaction statements like ~ x << (a)
Definition: kinetic_block_visitor.hpp:80
nmodl::visitor::KineticBlockVisitor::visit_kinetic_block
void visit_kinetic_block(ast::KineticBlock &node) override
visit node of type ast::KineticBlock
Definition: kinetic_block_visitor.cpp:403
nmodl::visitor::LocalRateNames::generate_rate_names
std::pair< std::shared_ptr< ast::Name >, std::shared_ptr< ast::Name > > generate_rate_names()
Definition: kinetic_block_visitor.cpp:523
nmodl::visitor::KineticBlockVisitor::state_var_index
std::unordered_map< std::string, int > state_var_index
unordered_map from state variable to corresponding index
Definition: kinetic_block_visitor.hpp:107
nmodl::ast::Name::get_node_name
std::string get_node_name() const override
Return name of the node.
Definition: ast.cpp:795
logger.hpp
Implement logger based on spdlog library.
nmodl::visitor::KineticBlockVisitor::kinetic_blocks
std::vector< ast::KineticBlock * > kinetic_blocks
vector of kinetic block nodes
Definition: kinetic_block_visitor.hpp:138
nmodl::visitor::add_local_variable
LocalVar * add_local_variable(StatementBlock &node, Identifier *varname)
Definition: visitor_utils.cpp:93
nmodl::visitor::KineticBlockVisitor::current_statement_block
ast::StatementBlock * current_statement_block
current statement block being visited
Definition: kinetic_block_visitor.hpp:144
nmodl::ast::Program::get_symbol_table
symtab::SymbolTable * get_symbol_table() const override
Return associated symbol table for the current ast node.
Definition: program.hpp:153
nmodl::visitor::KineticBlockVisitor::unroll_kinetic_blocks
void unroll_kinetic_blocks(ast::Program &node)
Unroll loops in KINETIC blocks.
Definition: kinetic_block_visitor.cpp:647
nmodl::visitor::LocalRateNames::kf_stem
static constexpr auto kf_stem
Definition: kinetic_block_visitor.cpp:482
nmodl::visitor::KineticBlockVisitor::RateEqs::k_f
std::vector< std::string > k_f
Definition: kinetic_block_visitor.hpp:72
nmodl::visitor::KineticBlockVisitor::odes
std::vector< std::string > odes
Definition: kinetic_block_visitor.hpp:92
nmodl::ast::Name
Represents a name.
Definition: name.hpp:44
nmodl::visitor::KineticBlockVisitor::process_reac_var
void process_reac_var(const std::string &varname, int count=1)
update stoichiometric matrices with reaction var term
Definition: kinetic_block_visitor.cpp:27
nmodl::ast::Program
Represents top level AST node for whole NMODL input.
Definition: program.hpp:39
nmodl::visitor::LocalizeKineticRatesVisitor::visit_statement_block
void visit_statement_block(ast::StatementBlock &node)
visit node of type ast::StatementBlock
Definition: kinetic_block_visitor.cpp:585
nmodl::visitor::LocalRateNames
Definition: kinetic_block_visitor.cpp:481
nmodl::ast::WrappedExpression::visit_children
void visit_children(visitor::Visitor &v) override
visit children i.e.
Definition: ast.cpp:13962
nmodl::visitor::KineticBlockVisitor::in_reaction_statement
bool in_reaction_statement
true if we are visiting a reaction statement
Definition: kinetic_block_visitor.hpp:114
nmodl::visitor::LocalizeKineticRatesVisitor::local_names
LocalRateNames local_names
Definition: kinetic_block_visitor.cpp:550
nmodl::ast::Program::set_blocks
void set_blocks(NodeVector &&blocks)
Setter for member variable Program::blocks (rvalue reference)
Definition: ast.cpp:12966
nmodl::visitor::KineticBlockVisitor::RateEqs::nu_R
std::vector< std::vector< int > > nu_R
Definition: kinetic_block_visitor.hpp:71
nmodl::ast::MINUSGT
@ MINUSGT
Definition: ast_common.hpp:92
nmodl::visitor::LocalRateNames::generate_forward_rate_name
std::shared_ptr< ast::Name > generate_forward_rate_name()
Definition: kinetic_block_visitor.cpp:516
nmodl::ast::ReactVarName::get_value
std::shared_ptr< Integer > get_value() const noexcept
Getter for member variable ReactVarName::value.
Definition: react_var_name.hpp:145
nmodl::ast::LTLT
@ LTLT
Definition: ast_common.hpp:92
nmodl::ModToken
Represent token returned by scanner.
Definition: modtoken.hpp:50
nmodl::visitor::KineticBlockVisitor::RateEqs::nu_L
std::vector< std::vector< int > > nu_L
Definition: kinetic_block_visitor.hpp:70
all.hpp
Auto generated AST classes declaration.
nmodl::ast::WrappedExpression
Wrap any other expression type.
Definition: wrapped_expression.hpp:38
nmodl::visitor::KineticBlockVisitor::non_state_var_bflux
std::vector< std::string > non_state_var_bflux
Definition: kinetic_block_visitor.hpp:87
nmodl::visitor::KineticBlockVisitor::conserve_equation_factor
std::string conserve_equation_factor
conserve statement: current state var multiplicative factor being processed
Definition: kinetic_block_visitor.hpp:132
nmodl::symtab::SymbolTable::lookup_in_scope
std::shared_ptr< Symbol > lookup_in_scope(const std::string &name) const
check if symbol with given name exist in the current table (including all parents)
Definition: symbol_table.cpp:163
nmodl::ast::Compartment::get_volume
std::shared_ptr< Expression > get_volume() const noexcept
Getter for member variable Compartment::volume.
Definition: compartment.hpp:171