User Guide
diffeq_helper.hpp
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 
8 #pragma once
9 
11 
12 namespace nmodl {
13 namespace parser {
14 
15 /**
16  * \brief Helper functions for solving differential equations
17  *
18  * Differential euqation parser (diffeq.yy) needs to solve derivative
19  * of various binary expressions and those are provided below. This
20  * implementation is based on original neuron implementation.
21  *
22  * \todo The implementations here are verbose and has duplicate code.
23  * Need to revisit this, may be using better library like symengine
24  * altogether.
25  */
26 namespace diffeq {
27 
28 /// operators beign supported as part of binary expressions
29 enum class MathOp { add = 1, sub, mul, div };
30 
31 template <MathOp Op>
32 inline Term eval_derivative(const Term& first,
33  const Term& second,
34  bool& deriv_invalid,
35  bool& eqn_invalid);
36 
37 
38 /// implement (f(x) + g(x))' = f'(x) + g'(x)
39 template <>
40 inline Term eval_derivative<MathOp::add>(const Term& first,
41  const Term& second,
42  bool& deriv_invalid,
43  bool& eqn_invalid) {
44  Term solution;
45  solution.expr = first.expr + "+" + second.expr;
46 
47  if (first.deriv_nonzero() && second.deriv_nonzero()) {
48  solution.deriv = first.deriv + "+" + second.deriv;
49  } else if (first.deriv_nonzero()) {
50  solution.deriv = first.deriv;
51  } else if (second.deriv_nonzero()) {
52  solution.deriv = second.deriv;
53  }
54 
55  if (first.a_nonzero() && second.a_nonzero()) {
56  solution.a = first.a + "+" + second.a;
57  } else if (first.a_nonzero()) {
58  solution.a = first.a;
59  } else if (second.a_nonzero()) {
60  solution.a = second.a;
61  }
62 
63  if (first.b_nonzero() && second.b_nonzero()) {
64  solution.b = first.b + "+" + second.b;
65  } else if (first.b_nonzero()) {
66  solution.b = first.b;
67  } else if (second.b_nonzero()) {
68  solution.b = second.b;
69  }
70 
71  return solution;
72 }
73 
74 
75 /// implement (f(x) - g(x))' = f'(x) - g'(x)
76 template <>
77 inline Term eval_derivative<MathOp::sub>(const Term& first,
78  const Term& second,
79  bool& deriv_invalid,
80  bool& eqn_invalid) {
81  Term solution;
82  solution.expr = first.expr + "-" + second.expr;
83 
84  if (first.deriv_nonzero() && second.deriv_nonzero()) {
85  solution.deriv = first.deriv + "-" + second.deriv;
86  } else if (first.deriv_nonzero()) {
87  solution.deriv = first.deriv;
88  } else if (second.deriv_nonzero()) {
89  solution.deriv = "(-" + second.deriv + ")";
90  }
91 
92  if (first.a_nonzero() && second.a_nonzero()) {
93  solution.a = first.a + "-" + second.a;
94  } else if (first.a_nonzero()) {
95  solution.a = first.a;
96  } else if (second.a_nonzero()) {
97  solution.a = "(-" + second.a + ")";
98  }
99 
100  if (first.b_nonzero() && second.b_nonzero()) {
101  solution.b = first.b + "-" + second.b;
102  } else if (first.b_nonzero()) {
103  solution.b = first.b;
104  } else if (second.b_nonzero()) {
105  solution.b = "(-" + second.b + ")";
106  }
107 
108  return solution;
109 }
110 
111 
112 /// implement (f(x) * g(x))' = f'(x)g(x) + f(x)g'(x)
113 template <>
114 inline Term eval_derivative<MathOp::mul>(const Term& first,
115  const Term& second,
116  bool& deriv_invalid,
117  bool& eqn_invalid) {
118  Term solution;
119  solution.expr = first.expr + "*" + second.expr;
120 
121  if (first.deriv_nonzero() && second.deriv_nonzero()) {
122  solution.deriv = "((" + first.deriv + ")*(" + second.expr + ")";
123  solution.deriv += "+(" + first.expr + ")*(" + second.deriv + "))";
124  } else if (first.deriv_nonzero()) {
125  solution.deriv = "(" + first.deriv + ")*(" + second.expr + ")";
126  } else if (second.deriv_nonzero()) {
127  solution.deriv = "(" + first.expr + ")*(" + second.deriv + ")";
128  }
129 
130  if (first.a_nonzero() && second.a_nonzero()) {
131  eqn_invalid = true;
132  } else if (first.a_nonzero() && second.b_nonzero()) {
133  solution.a = "(" + first.a + ")*(" + second.b + ")";
134  } else if (second.a_nonzero() && first.b_nonzero()) {
135  solution.a = "(" + first.b + ")*(" + second.a + ")";
136  }
137 
138  if (first.b_nonzero() && second.b_nonzero()) {
139  solution.b = "(" + first.b + ")*(" + second.b + ")";
140  }
141 
142  return solution;
143 }
144 
145 
146 /**
147  * Implement (f(x) / g(x))' = (f'(x)g(x) - f(x)g'(x))/g^2(x)
148  * Note that the implementation is very limited for the g(x)
149  * and this needs to be discussed with Michael.
150  */
151 template <>
152 inline Term eval_derivative<MathOp::div>(const Term& first,
153  const Term& second,
154  bool& deriv_invalid,
155  bool& eqn_invalid) {
156  Term solution;
157  solution.expr = first.expr + "/" + second.expr;
158 
159  if (second.deriv_nonzero()) {
160  deriv_invalid = true;
161  } else if (first.deriv_nonzero()) {
162  solution.deriv = "(" + first.deriv + ")/" + second.expr;
163  }
164 
165  if (second.a_nonzero()) {
166  eqn_invalid = true;
167  } else if (first.a_nonzero()) {
168  solution.a = "(" + first.a + ")/" + second.expr;
169  }
170 
171  if (first.b_nonzero()) {
172  solution.b = "(" + first.b + ")/" + second.expr;
173  }
174 
175  return solution;
176 }
177 
178 } // namespace diffeq
179 } // namespace parser
180 } // namespace nmodl
nmodl::parser::diffeq::Term::deriv_nonzero
bool deriv_nonzero() const
helper routines used in parser
Definition: diffeq_context.hpp:49
nmodl::parser::diffeq::MathOp::div
@ div
nmodl::parser::diffeq::Term::expr
std::string expr
expression being solved
Definition: diffeq_context.hpp:28
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
diffeq_context.hpp
nmodl::parser::diffeq::MathOp::add
@ add
nmodl::parser::diffeq::MathOp::sub
@ sub
nmodl::parser::diffeq::Term::deriv
std::string deriv
derivative of the expression
Definition: diffeq_context.hpp:31
nmodl::parser::diffeq::Term
Represent a term in differential equation and it's "current" solution.
Definition: diffeq_context.hpp:26
nmodl::parser::diffeq::MathOp::mul
@ mul
nmodl::parser::diffeq::Term::b
std::string b
Definition: diffeq_context.hpp:35
nmodl::parser::diffeq::Term::a
std::string a
Definition: diffeq_context.hpp:34
nmodl::parser::diffeq::Term::b_nonzero
bool b_nonzero() const
Definition: diffeq_context.hpp:57
nmodl::parser::diffeq::Term::a_nonzero
bool a_nonzero() const
Definition: diffeq_context.hpp:53
nmodl::parser::diffeq::MathOp
MathOp
operators beign supported as part of binary expressions
Definition: diffeq_helper.hpp:29
nmodl::parser::diffeq::eval_derivative
Term eval_derivative(const Term &first, const Term &second, bool &deriv_invalid, bool &eqn_invalid)