def get_f_expressions(self, substitute_vars=None): if self.f_expr is None: return [] self._substitute(self.f_expr, self.expressions, substitute_vars=substitute_vars) return_expressions = [] # the derivative expression dif_eq_code = self.f_expr.get_code(subs=True) return_expressions.append(Expression(f'_df{self.var_name}_dt', dif_eq_code)) # needed variables need_vars = tools.get_identifiers(dif_eq_code) # get the total return expressions for expr in self.expressions[::-1]: if expr.var_name in need_vars: if expr.substituted_code is None: code = expr.code else: code = expr.substituted_code return_expressions.append(Expression(expr.var_name, code)) need_vars |= tools.get_identifiers(code) return return_expressions[::-1]
def contain_unknown_symbol(expr, scope): """Examine where the given expression ``expr`` has the unknown symbol in ``scope``. Returns ------- res : bool True or False. """ ids = tools.get_identifiers(expr) for id_ in ids: if '.' in id_: prefix = id_.split('.')[0].strip() if prefix not in scope: return True if id_ not in scope: return True return False
def separate_variables(func_or_code): """Separate the expressions in a differential equation for each variable. For example, take the HH neuron model as an example: >>> eq_code = ''' >>> def integral(m, h, t, Iext, V): >>> alpha = 0.1 * (V + 40) / (1 - np.exp(-(V + 40) / 10)) >>> beta = 4.0 * np.exp(-(V + 65) / 18) >>> dmdt = alpha * (1 - m) - beta * m >>> >>> alpha = 0.07 * np.exp(-(V + 65) / 20.) >>> beta = 1 / (1 + np.exp(-(V + 35) / 10)) >>> dhdt = alpha * (1 - h) - beta * h >>> return dmdt, dhdt >>> ''' >>> analyser = DiffEqReader() >>> analyser.visit(ast.parse(eq_code)) >>> separate_variables(returns=analyser.returns, >>> variables=analyser.variables, >>> right_exprs=analyser.rights, >>> code_lines=analyser.code_lines) {'dhdt': ['alpha = 0.07 * np.exp(-(V + 65) / 20.0)\n', 'beta = 1 / (1 + np.exp(-(V + 35) / 10))\n', 'dhdt = alpha * (1 - h) - beta * h\n'], 'dmdt': ['alpha = 0.1 * (V + 40) / (1 - np.exp(-(V + 40) / 10))\n', 'beta = 4.0 * np.exp(-(V + 65) / 18)\n', 'dmdt = alpha * (1 - m) - beta * m\n']} Parameters ---------- func_or_code : callable, str The callable function or the function code. Returns ------- anlysis : dict The expressions for each return variable. """ if callable(func_or_code): func_or_code = tools.deindent(inspect.getsource(func_or_code)) assert isinstance(func_or_code, str) analyser = DiffEqReader() analyser.visit(ast.parse(func_or_code)) returns = analyser.returns variables = analyser.variables right_exprs = analyser.rights code_lines = analyser.code_lines return_requires = OrderedDict([(r, set(tools.get_identifiers(r))) for r in returns]) code_lines_for_returns = OrderedDict([(r, []) for r in returns]) variables_for_returns = OrderedDict([(r, []) for r in returns]) expressions_for_returns = OrderedDict([(r, []) for r in returns]) length = len(variables) reverse_ids = list(reversed([i - length for i in range(length)])) for r in code_lines_for_returns.keys(): for rid in reverse_ids: dep = [] for v in variables[rid]: if v in return_requires[r]: dep.append(v) if len(dep): code_lines_for_returns[r].append(code_lines[rid]) variables_for_returns[r].append(variables[rid]) expr = right_exprs[rid] expressions_for_returns[r].append(expr) for d in dep: return_requires[r].remove(d) return_requires[r].update(tools.get_identifiers(expr)) for r in list(code_lines_for_returns.keys()): code_lines_for_returns[r] = code_lines_for_returns[r][::-1] variables_for_returns[r] = variables_for_returns[r][::-1] expressions_for_returns[r] = expressions_for_returns[r][::-1] analysis = tools.DictPlus( code_lines_for_returns=code_lines_for_returns, variables_for_returns=variables_for_returns, expressions_for_returns=expressions_for_returns, ) return analysis
def separate_variables(func_or_code): """Separate the expressions in a differential equation for each variable. For example, take the HH neuron model as an example: >>> eq_code = ''' >>> def derivative(V, m, h, n, t, C, gNa, ENa, gK, EK, gL, EL, Iext): >>> alpha = 0.1 * (V + 40) / (1 - bp.math.exp(-(V + 40) / 10)) >>> beta = 4.0 * bp.math.exp(-(V + 65) / 18) >>> dmdt = alpha * (1 - m) - beta * m >>> >>> alpha = 0.07 * bp.math.exp(-(V + 65) / 20.) >>> beta = 1 / (1 + bp.math.exp(-(V + 35) / 10)) >>> dhdt = alpha * (1 - h) - beta * h >>> >>> alpha = 0.01 * (V + 55) / (1 - bp.math.exp(-(V + 55) / 10)) >>> beta = 0.125 * bp.math.exp(-(V + 65) / 80) >>> dndt = alpha * (1 - n) - beta * n >>> >>> I_Na = (gNa * m ** 3.0 * h) * (V - ENa) >>> I_K = (gK * n ** 4.0) * (V - EK) >>> I_leak = gL * (V - EL) >>> dVdt = (- I_Na - I_K - I_leak + Iext) / C >>> >>> return dVdt, dmdt, dhdt, dndt >>> ''' >>> separate_variables(eq_code) {'code_lines_for_returns': {'dVdt': ['I_Na = gNa * m ** 3.0 * h * (V - ENa)\n', 'I_K = gK * n ** 4.0 * (V - EK)\n', 'I_leak = gL * (V - EL)\n', 'dVdt = (-I_Na - I_K - I_leak + Iext) / C\n'], 'dhdt': ['alpha = 0.07 * bp.math.exp(-(V + 65) / 20.0)\n', 'beta = 1 / (1 + bp.math.exp(-(V + 35) / 10))\n', 'dhdt = alpha * (1 - h) - beta * h\n'], 'dmdt': ['alpha = 0.1 * (V + 40) / (1 - ' 'bp.math.exp(-(V + 40) / 10))\n', 'beta = 4.0 * bp.math.exp(-(V + 65) / 18)\n', 'dmdt = alpha * (1 - m) - beta * m\n'], 'dndt': ['alpha = 0.01 * (V + 55) / (1 - ' 'bp.math.exp(-(V + 55) / 10))\n', 'beta = 0.125 * bp.math.exp(-(V + 65) / 80)\n', 'dndt = alpha * (1 - n) - beta * n\n']}, 'expressions_for_returns': {'dVdt': ['gNa * m ** 3.0 * h * (V - ENa)', 'gK * n ** 4.0 * (V - EK)', 'gL * (V - EL)', '(-I_Na - I_K - I_leak + Iext) / C'], 'dhdt': ['0.07 * bp.math.exp(-(V + 65) / 20.0)', '1 / (1 + bp.math.exp(-(V + 35) / 10))', 'alpha * (1 - h) - beta * h'], 'dmdt': ['0.1 * (V + 40) / (1 - ' 'bp.math.exp(-(V + 40) / 10))', '4.0 * bp.math.exp(-(V + 65) / 18)', 'alpha * (1 - m) - beta * m'], 'dndt': ['0.01 * (V + 55) / (1 - ' 'bp.math.exp(-(V + 55) / 10))', '0.125 * bp.math.exp(-(V + 65) / 80)', 'alpha * (1 - n) - beta * n']}, 'variables_for_returns': {'dVdt': [['I_Na'], ['I_K'], ['I_leak'], ['dVdt']], 'dhdt': [['alpha'], ['beta'], ['dhdt']], 'dmdt': [['alpha'], ['beta'], ['dmdt']], 'dndt': [['alpha'], ['beta'], ['dndt']]}} Parameters ---------- func_or_code : callable, str The callable function or the function code. Returns ------- anlysis : dict The expressions for each return variable. """ if callable(func_or_code): if tools.is_lambda_function(func_or_code): raise errors.AnalyzerError( f'Cannot analyze lambda function: {func_or_code}.') func_or_code = tools.deindent(inspect.getsource(func_or_code)) assert isinstance(func_or_code, str) analyser = DiffEqReader() analyser.visit(ast.parse(func_or_code)) returns = analyser.returns variables = analyser.variables right_exprs = analyser.rights code_lines = analyser.code_lines return_requires = OrderedDict([(r, set(tools.get_identifiers(r))) for r in returns]) code_lines_for_returns = OrderedDict([(r, []) for r in returns]) variables_for_returns = OrderedDict([(r, []) for r in returns]) expressions_for_returns = OrderedDict([(r, []) for r in returns]) length = len(variables) reverse_ids = list(reversed([i - length for i in range(length)])) for r in code_lines_for_returns.keys(): for rid in reverse_ids: dep = [] for v in variables[rid]: if v in return_requires[r]: dep.append(v) if len(dep): code_lines_for_returns[r].append(code_lines[rid]) variables_for_returns[r].append(variables[rid]) expr = right_exprs[rid] expressions_for_returns[r].append(expr) for d in dep: return_requires[r].remove(d) return_requires[r].update(tools.get_identifiers(expr)) for r in list(code_lines_for_returns.keys()): code_lines_for_returns[r] = code_lines_for_returns[r][::-1] variables_for_returns[r] = variables_for_returns[r][::-1] expressions_for_returns[r] = expressions_for_returns[r][::-1] analysis = tools.DictPlus( code_lines_for_returns=code_lines_for_returns, variables_for_returns=variables_for_returns, expressions_for_returns=expressions_for_returns, ) return analysis
def identifiers(self): return tools.get_identifiers(self.code)