36 const std::vector<std::string>& solutions,
40 const std::string& tmp_unique_prefix)
43 , to_be_removed(&to_be_removed)
45 , n_next_equations(n_next_equations)
46 , replaced_statements_range(-1, -1) {
48 const auto ss_tmp_delimeter =
49 tmp_unique_prefix.empty()
51 : std::find_if(solutions.begin(),
53 [&tmp_unique_prefix](
const std::string& statement) {
54 return statement.substr(0, tmp_unique_prefix.size()) !=
57 tmp_statements = StatementDispenser(solutions.begin(), ss_tmp_delimeter, -1);
58 solution_statements = StatementDispenser(ss_tmp_delimeter, solutions.end(), -1);
65 const bool current_is_top_level_statement_block =
70 if (current_is_top_level_statement_block) {
71 logger->debug(
"SympyReplaceSolutionsVisitor :: visit statements. Matching policy: {}",
78 "SympyReplaceSolutionsVisitor :: not all solutions were replaced. Policy: GREEDY");
85 "SympyReplaceSolutionsVisitor :: Found ambiguous system of equations "
86 "interleaved with {} assignment statements. I do not know what equations go "
88 "equations go after the assignment statements. Either put all the equations "
89 "that need to be solved "
90 "in the form: x = f(...) and with distinct variable assignments or do not "
91 "interleave the system with assignments.",
102 new_statements.reserve(2 * old_statements.size());
103 for (
auto& old_statement: old_statements) {
104 const auto& replacement_ptr =
replacements.find(old_statement);
110 new_statements.insert(new_statements.end(),
111 replacement_ptr->second.begin(),
112 replacement_ptr->second.end());
116 logger->debug(
"SympyReplaceSolutionsVisitor :: erasing {}",
to_nmodl(old_statement));
117 for (
const auto& replacement: replacement_ptr->second) {
118 logger->debug(
"SympyReplaceSolutionsVisitor :: adding {}",
to_nmodl(replacement));
122 logger->debug(
"SympyReplaceSolutionsVisitor :: found {}, nothing to do",
124 new_statements.emplace_back(old_statement);
126 logger->debug(
"SympyReplaceSolutionsVisitor :: erasing {}",
to_nmodl(old_statement));
130 if (current_is_top_level_statement_block) {
132 std::ostringstream ss;
136 throw std::runtime_error(fmt::format(
137 "Not all solutions were replaced! Sympy returned {} equations but I could not find "
139 "for all of them. In particular, the following equations remain to be replaced "
140 "somewhere:\n{}This is "
141 "probably a bug and I invite you to report it to a developer. Possible causes:\n"
142 " - I did not do a GREEDY pass and some solutions could not be replaced by VALUE\n "
144 "returned more equations than what we expected\n - There is a bug in the GREEDY "
146 "solutions were replaced but not untagged",
164 std::shared_ptr<ast::Expression> get_lhs(
const ast::Node& node),
165 std::shared_ptr<ast::Expression> get_rhs(
const ast::Node& node)) {
168 const auto& statement = std::static_pointer_cast<ast::Statement>(
180 const auto& key = dependencies.first;
183 logger->debug(
"SympyReplaceSolutionsVisitor :: marking for replacement {}",
197 logger->debug(
"SympyReplaceSolutionsVisitor :: marking for replacement {}",
214 logger->debug(
"SympyReplaceSolutionsVisitor :: visit {}",
to_nmodl(node));
215 auto get_lhs = [](
const ast::Node& node) -> std::shared_ptr<ast::Expression> {
219 auto get_rhs = [](
const ast::Node& node) -> std::shared_ptr<ast::Expression> {
227 logger->debug(
"SympyReplaceSolutionsVisitor :: visit {}",
to_nmodl(node));
228 auto get_lhs = [](
const ast::Node& node) -> std::shared_ptr<ast::Expression> {
232 auto get_rhs = [](
const ast::Node& node) -> std::shared_ptr<ast::Expression> {
241 logger->debug(
"SympyReplaceSolutionsVisitor :: visit {}",
to_nmodl(node));
242 auto get_lhs = [](
const ast::Node& node) -> std::shared_ptr<ast::Expression> {
246 auto get_rhs = [](
const ast::Node& node) -> std::shared_ptr<ast::Expression> {
255 logger->debug(
"SympyReplaceSolutionsVisitor :: visit {}",
to_nmodl(node));
260 std::static_pointer_cast<ast::VarName>(node.
get_lhs())->get_name()->get_node_name();
268 const std::vector<std::string>::const_iterator& statements_str_beg,
269 const std::vector<std::string>::const_iterator& statements_str_end,
270 const int error_on_n_flushes)
272 , error_on_n_flushes(error_on_n_flushes) {
312 for (
size_t ii = 0; ii < statements.size(); ++ii) {
313 const auto& statement = statements[ii];
315 if (statement->is_expression_statement()) {
316 const auto& e_statement =
317 std::static_pointer_cast<ast::ExpressionStatement>(statement)->get_expression();
318 if (e_statement->is_binary_expression()) {
319 const auto& bin_exp = std::static_pointer_cast<ast::BinaryExpression>(e_statement);
323 const auto& key = dependencies.first;
324 const auto& vars = dependencies.second;
326 var2statement.emplace(key, ii);
327 for (
const auto& var: vars) {
328 const auto& var_already_inserted = dependency_map.find(var);
329 if (var_already_inserted != dependency_map.end()) {
330 dependency_map[key].insert(var_already_inserted->second.begin(),
331 var_already_inserted->second.end());
332 for (
const auto& root_var: var_already_inserted->second) {
333 var2dependants[root_var].insert(ii);
336 dependency_map[key].insert(var);
337 var2dependants[var].insert(ii);
345 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: var2dependants map");
346 for (
const auto& entry: var2dependants) {
347 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: var `{}` used in:",
349 for (
const auto ii: entry.second) {
350 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: -> {}",
354 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: var2statement map");
355 for (
const auto& entry: var2statement) {
356 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: var `{}` defined in:",
358 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: -> {}",
359 to_nmodl(statements[entry.second]));
365 const std::string& var) {
366 auto ptr = var2statement.find(var);
367 bool emplaced =
false;
368 if (ptr != var2statement.end()) {
369 const auto ii = ptr->second;
370 const auto tag_ptr = tags.find(ii);
371 if (tag_ptr != tags.end()) {
372 new_statements.emplace_back(statements[ii]->clone());
377 "SympyReplaceSolutionsVisitor::StatementDispenser :: adding to replacement rule {}",
381 "SympyReplaceSolutionsVisitor::StatementDispenser :: tried adding to replacement "
382 "rule {} but statement is not "
392 const size_t n_next_statements) {
394 for (
size_t next_statement_ii = 0;
395 next_statement_ii < statements.size() && counter < n_next_statements;
396 ++next_statement_ii) {
397 const auto tag_ptr = tags.find(next_statement_ii);
398 if (tag_ptr != tags.end()) {
400 "SympyReplaceSolutionsVisitor::StatementDispenser :: adding to replacement rule {}",
401 to_nmodl(statements[next_statement_ii]));
402 new_statements.emplace_back(statements[next_statement_ii]->clone());
412 for (
const auto ii: tags) {
413 new_statements.emplace_back(statements[ii]->clone());
415 "SympyReplaceSolutionsVisitor::StatementDispenser :: adding to replacement rule {}",
419 n_flushes += (!tags.empty());
420 if (error_on_n_flushes > 0 && n_flushes >= error_on_n_flushes) {
421 throw std::runtime_error(
422 "SympyReplaceSolutionsVisitor::StatementDispenser :: State variable assignment(s) "
423 "interleaved in system "
425 "equations/differential equations. It is not allowed due to possible numerical "
426 "instability and undefined "
427 "behavior. Erase the assignment statement(s) or move them before/after the"
428 " set of equations/differential equations.");
431 const auto n_replacements = tags.size();
435 return n_replacements;
439 const std::string& var) {
440 auto ptr = var2dependants.find(var);
442 if (ptr != var2dependants.end()) {
443 for (
const auto ii: ptr->second) {
444 const auto pos = tags.insert(ii);
446 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: tagging {}",
456 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: tagging all statements");
457 for (
size_t i = 0; i < statements.size(); ++i) {
459 logger->debug(
"SympyReplaceSolutionsVisitor::StatementDispenser :: tagging {}",