def assign_str(self, lhs, rhs): rhs = Expression.expand_integer_powers(rhs) nmodl_str = ccode(rhs, user_functions=Expression._cfunc_map, assign_to=lhs) nmodl_str = Expression.strip_L_from_rationals(nmodl_str) nmodl_str = nmodl_str.replace(';', '') return nmodl_str
def test_escape_of_carets(self): self.assertEquals(Expression("a^2").rhs_cstr, 'a*a') self.assertEquals(Expression("(a - 2)^2").rhs_cstr, '(a - 2)*(a - 2)') self.assertEquals( Expression("(a - (a - 2)^2.5)^2.5").rhs_cstr, 'pow(a - pow(a - 2, 2.5), 2.5)') self.assertEquals(Expression("a^(a - 2)").rhs_cstr, 'pow(a, a - 2)')
def test_equality_combined(self): expr = Expression('(a == b) && (c == d) || (e == f)') self.assertEqual( expr.rhs, sympy.Or( sympy.And(sympy.Eq(self.a, self.b), sympy.Eq(self.c, self.d)), sympy.Eq(self.e, self.f)))
def test_nested_relational(self): expr = Expression('((a == b) || (c == d)) && ((e == f) || (g < f))') self.assertEqual( expr.rhs, sympy.And( sympy.Or(sympy.Eq(self.a, self.b), sympy.Eq(self.c, self.d)), sympy.Or(sympy.Eq(self.e, self.f), sympy.Lt(self.g, self.f))))
def scale_time_derivative(self, element): """ Scales the time derivative, ensuring that the overall expression is in the same units as the state variable divided by the time units """ if isinstance(element, basestring): element = self.component_class[element] expr, dims = self._flatten(sympify(element.rhs)) state_var_dims = self.component_class.state_variable( element.variable).dimension assert dims == state_var_dims / un.time exp = self.dimension_to_units_compound(dims)[0] target_exp, compound = self.dimension_to_units_compound(state_var_dims) # Divide the state variable units by the time units to get the target # compound try: time_unit, power = compound.pop( next(i for i, (u, _) in enumerate(compound) if u.dimension == un.time)) compound.append((time_unit, power - 1)) except StopIteration: compound.append((self.time_units, -1)) target_exp -= self.time_units.power scale = exp - target_exp # Scale expression to match target expression expr = 10 ** scale * expr units_str = self._units_for_code_gen(compound) return Expression(expr), units_str
def scale_alias(self, element): if isinstance(element, basestring): element = self.component_class.element(element) scaled, dims = self._flatten(sympify(element.rhs)) units_str = self._units_for_code_gen( self.dimension_to_units_compound(dims)[1]) return Expression(scaled), units_str
def test_nested_relational(self): expr = Expression( sympy.And( sympy.Or(sympy.Eq(self.a, self.b), sympy.Eq(self.c, self.d)), sympy.Or(sympy.Eq(self.e, self.f), sympy.Lt(self.g, self.f)))) self.assertEqual(expr.rhs_cstr, '(a == b || c == d) && (e == f || g < f)')
def test_equality_nested_func(self): expr = Expression('((a == b) || (c == pow(d, 2))) && (e == f)') self.assertEqual( expr.rhs, sympy.And( sympy.Or(sympy.Eq(self.a, self.b), sympy.Eq(self.c, self.d**2)), sympy.Eq(self.e, self.f)))
def test_rationals_match(self): self.assertEqual(Expression.strip_L_from_rationals('aL/bL'), 'aL/bL') self.assertEqual(Expression.strip_L_from_rationals('a0L/bL'), 'a0L/bL') self.assertEqual(Expression.strip_L_from_rationals('aL/b0L'), 'aL/b0L') self.assertEqual(Expression.strip_L_from_rationals('a0L/b0L'), 'a0L/b0L') self.assertEqual(Expression.strip_L_from_rationals('aL/b0L'), 'aL/b0L') self.assertEqual(Expression.strip_L_from_rationals('1/2'), '1/2') self.assertEqual(Expression.strip_L_from_rationals('1L/2L'), '1/2') self.assertEqual(Expression.strip_L_from_rationals('1.0L/2.0L'), '1.0/2.0')
def test_rhs_name_transform_inplace(self): # Signature: name(self, name_map) # Replace atoms on the RHS with values in the name_map e = Expression("V*sin(V)/(eta*mg_conc*exp(-V^2*gamma) + 1)") e.rhs_name_transform_inplace({'V': 'VNEW'}) self.assertEquals( e.rhs_str, "VNEW*sin(VNEW)/(eta*mg_conc*exp(-VNEW^2*gamma) + 1)") # Don't Change builtin function names: e.rhs_name_transform_inplace({'sin': 'SIN'}) self.assertEquals( e.rhs_str, "VNEW*sin(VNEW)/(eta*mg_conc*exp(-VNEW^2*gamma) + 1)") e.rhs_name_transform_inplace({'exp': 'EXP'}) self.assertEquals( e.rhs_str, "VNEW*sin(VNEW)/(eta*mg_conc*exp(-VNEW^2*gamma) + 1)") # Check the attributes: self.assertEquals( set(e.rhs_atoms), set(['VNEW', 'mg_conc', 'eta', 'gamma', 'exp', 'sin'])) self.assertEquals(set(str(f) for f in e.rhs_funcs), set(['exp', 'sin']))
def test_scaling_and_assignment(self): handler1 = TestUnitHandler1(self.a) handler2 = NestUnitHandler(self.a) self.assertEqual( handler1.scale_alias('A2'), (Expression('ARP2 + P3'), 'ms*nA'), "Difference in scaled alias {} vs {}:\n{}".format( handler1.scale_alias('A2'), (Expression('ARP2 + P3'), 'ms*nA'), handler1.scale_alias('A2')[0].find_mismatch( Expression('ARP2 + P3')))) self.assertEqual(handler1.scale_alias('A4'), (Expression('ARP3 + 100/(P2*P6)'), 'S/cm2')) self.assertEqual(handler1.scale_alias('A5'), (Expression('P7 * P8'), 'mV')) self.assertEqual(handler1.scale_alias('A6'), (Expression('1e-3 * P9/P10'), 'ms')) self.assertEqual( handler2.scale_time_derivative(self.a.regime('R1').element('SV2')), (Expression('C1 * SV2 ** 2 + C2 * SV2 + C3 + SV3 + ' 'ARP4 / P11'), 'mV/ms')) self.assertEqual( handler2.scale_time_derivative(self.a.regime('R1').element('SV3')), (Expression('P12*(SV2*P13 - SV3)'), 'mV/ms^2')) self.assertEqual(handler1.assign_units_to_variable('P2'), '1/uS') self.assertEqual(handler1.assign_units_to_variable('P6'), 'um^2')
def test_rhs_name_transform_inplace(self): # Signature: name(self, name_map) # Replace atoms on the RHS with values in the name_map e = Expression("V*sin(V)/(eta*mg_conc*exp(-V^2*gamma) + 1)") e.rhs_name_transform_inplace({'V': 'VNEW'}) self.assertEquals( e.rhs_str, "VNEW*sin(VNEW)/(eta*mg_conc*exp(-VNEW^2*gamma) + 1)") # Don't Change builtin function names: e.rhs_name_transform_inplace({'sin': 'SIN'}) self.assertEquals( e.rhs_str, "VNEW*sin(VNEW)/(eta*mg_conc*exp(-VNEW^2*gamma) + 1)") e.rhs_name_transform_inplace({'exp': 'EXP'}) self.assertEquals( e.rhs_str, "VNEW*sin(VNEW)/(eta*mg_conc*exp(-VNEW^2*gamma) + 1)") # Check the attributes: self.assertEquals(set(e.rhs_atoms), set( ['VNEW', 'mg_conc', 'eta', 'gamma', 'exp', 'sin'])) self.assertEquals(set(str(f) for f in e.rhs_funcs), set(['exp', 'sin']))
def test_logical_and(self): "Tests conversion of rationals back from the c-code version 1.0L/2.0L" expr = Expression('1/2') self.assertEqual(str(expr.rhs), '1/2')
def test_c89(self): "Tests conversion of rationals back from the c-code version 1.0L/2.0L" expr = Expression('1/2') self.assertEqual(expr.rhs_cstr, '1.0/2.0')
def test_negation(self): expr = Expression(sympy.Not(self.a)) self.assertEqual(expr.rhs_cstr, '!a')
def test_random(self): expr = Expression('random.exponential(a)') self.assertEqual(expr.rhs_cstr, 'random_exponential_(a)')
parameters=['P1', 'P2']) dynD = Dynamics( name='dynD', state_variables=[StateVariable('SV1', dimension=un.voltage)], regimes=[ Regime('dSV1/dt = -SV1 / P1 + ADP1 + ARP1 * P3', transitions=[ On('SV1 > P2', do=[OutputEvent('ESP1')]), On('ERP1', do=[StateAssignment('SV1', 'P2 * random.binomial(1,2)')]) ], name='R1'), ], constants=[Constant('C1', -67.0 * un.Mohm)], aliases=[Alias('A1', Expression('SV1 / C1'))], ports=[ AnalogSendPort('A1', dimension=un.current), AnalogReducePort('ADP1', dimension=(un.voltage / un.time)), AnalogReceivePort('ARP1', dimension=un.current), EventSendPort('ESP1'), EventReceivePort('ERP1') ], parameters=[ Parameter('P1', dimension=un.time), Parameter('P2', dimension=un.voltage), Parameter('P3', dimension=(un.voltage / (un.time * un.current))) ]) dynE = Dynamics(name='dynE', state_variables=[StateVariable('SV1', dimension=un.voltage)],
def test_pow(self): expr = Expression(self.a**self.b) self.assertEqual(expr.rhs_cstr, 'pow(a, b)')
def test_equality(self): expr = Expression(sympy.Eq(self.a, self.b)) self.assertEqual(expr.rhs_cstr, 'a == b')
def test_equality_combined(self): expr = Expression( sympy.Or( sympy.And(sympy.Eq(self.a, self.b), sympy.Eq(self.c, self.d)), sympy.Eq(self.e, self.f))) self.assertEqual(expr.rhs_cstr, 'a == b && c == d || e == f')
def test_logical_and(self): expr = Expression(sympy.And(self.a, self.b)) self.assertEqual(expr.rhs_cstr, 'a && b')
def test_logical_or(self): expr = Expression(sympy.Or(self.a, self.b)) self.assertEqual(expr.rhs_cstr, 'a || b')
def test_triple_negation(self): expr = Expression('!!!a') self.assertEqual(expr.rhs, sympy.Not(self.a))
def test_double_negation(self): expr = Expression('!!a') self.assertEqual(expr.rhs, self.a)
def test_Valid(self): # rhs, expt_vars, expt_funcs, result, values valid_rhses = [ (('a'), ('a'), (), 5, {'a': 5}), (('b'), ('b'), (), 7, {'b': 7}), (('a+b'), ('a', 'b'), (), 13, {'a': 12, 'b': 1}), (('1./(alpha+2*beta)'), ('alpha', 'beta'), (), 0.2, {'alpha': 1, 'beta': 2}), ] for rhs, exp_var, exp_func, exp_res, params in valid_rhses: e = Expression(rhs) self.assertEquals(set(e.rhs_symbol_names), set(exp_var)) self.assertEquals(set(str(f) for f in e.rhs_funcs), set(exp_func)) self.assertAlmostEqual(e.rhs_as_python_func(**params), exp_res, places=4) import numpy expr_vars = [ ["-A/tau_r", ("A", "tau_r"), ()], ["V*V", ("V",), ()], ["a*(b*V - U)", ("U", "V", "b", "a"), ()], [" 0.04*V*V + 5.0*V + 1. + 140.0 - U + Isyn", ("V", "U", "Isyn"), ()], ["c", ("c"), ()], ["1", (), ()], ["atan2(sin(x),cos(y))", ("x", "y"), ("atan2", "sin", "cos")], ["1.*V", ("V"), ()], ["1.0", (), ()], [".1", (), ()], ["1/(1 + mg_conc*eta*exp(-1*gamma*V))", ( "mg_conc", "eta", "gamma", "V"), ('exp',)], ["1 / ( 1 + mg_conc * eta * exp( -1 * gamma*V))", ("mg_conc", "eta", "gamma", "V"), ('exp',)], ["1 / ( 1 + mg_conc * sin(0.5 * V) * exp ( -1 * gamma*V))", ("mg_conc", "gamma", "V"), ('exp', "sin")], [".1 / ( 1.0 + mg_conc * sin(V) * exp ( -1.0 * gamma*V))", ("mg_conc", "gamma", "V"), ('exp', "sin")], ["sin(w)", ("w"), ("sin",)]] namespace = { "A": 10.0, "tau_r": 11.0, "V": -70.0, "a": 1.2, "b": 3.0, "U": -80.0, "Isyn": 2.0, "c": 10.0, "mg_conc": 1.0, "eta": 2.0, "gamma": -20.0, "x": 1.0, "y": 1.0, "w": numpy.arange(10) } return_values = [-0.909090909091, 4900.0, -156.0, 69.0, 10.0, 1, 1.0, -70.0, 1.0, 0.1, 1.0, 1.0, 1.0, 0.1, numpy.sin(namespace['w'])] for i, (expr, expt_vars, expt_funcs) in enumerate(expr_vars): c = Expression(expr) self.assertEqual(set(c.rhs_symbol_names), set(expt_vars)) self.assertEqual(set(str(f) for f in c.rhs_funcs), set(expt_funcs)) python_func = c.rhs_as_python_func param_dict = dict([(v, namespace[v]) for v in expt_vars]) v = return_values[i] - python_func(**param_dict) self.assertAlmostEqual(numpy.dot(v, v), 0)
def scale_expr(self, expr): scaled, dims = self._flatten(sympify(expr)) units_str = self._units_for_code_gen( self.dimension_to_units_compound(dims)[1]) return Expression(scaled), units_str
def test_Valid(self): # rhs, expt_vars, expt_funcs, result, values valid_rhses = [ (('a'), ('a'), (), 5, { 'a': 5 }), (('b'), ('b'), (), 7, { 'b': 7 }), (('a+b'), ('a', 'b'), (), 13, { 'a': 12, 'b': 1 }), (('1./(alpha+2*beta)'), ('alpha', 'beta'), (), 0.2, { 'alpha': 1, 'beta': 2 }), ] for rhs, exp_var, exp_func, exp_res, params in valid_rhses: e = Expression(rhs) self.assertEquals(set(e.rhs_symbol_names), set(exp_var)) self.assertEquals(set(str(f) for f in e.rhs_funcs), set(exp_func)) self.assertAlmostEqual(e.rhs_as_python_func(**params), exp_res, places=4) import numpy expr_vars = [ ["-A/tau_r", ("A", "tau_r"), ()], ["V*V", ("V", ), ()], ["a*(b*V - U)", ("U", "V", "b", "a"), ()], [ " 0.04*V*V + 5.0*V + 1. + 140.0 - U + Isyn", ("V", "U", "Isyn"), () ], ["c", ("c"), ()], ["1", (), ()], ["atan2(sin(x),cos(y))", ("x", "y"), ("atan2", "sin", "cos")], ["1.*V", ("V"), ()], ["1.0", (), ()], [".1", (), ()], [ "1/(1 + mg_conc*eta*exp(-1*gamma*V))", ("mg_conc", "eta", "gamma", "V"), ('exp', ) ], [ "1 / ( 1 + mg_conc * eta * exp( -1 * gamma*V))", ("mg_conc", "eta", "gamma", "V"), ('exp', ) ], [ "1 / ( 1 + mg_conc * sin(0.5 * V) * exp ( -1 * gamma*V))", ("mg_conc", "gamma", "V"), ('exp', "sin") ], [ ".1 / ( 1.0 + mg_conc * sin(V) * exp ( -1.0 * gamma*V))", ("mg_conc", "gamma", "V"), ('exp', "sin") ], ["sin(w)", ("w"), ("sin", )] ] namespace = { "A": 10.0, "tau_r": 11.0, "V": -70.0, "a": 1.2, "b": 3.0, "U": -80.0, "Isyn": 2.0, "c": 10.0, "mg_conc": 1.0, "eta": 2.0, "gamma": -20.0, "x": 1.0, "y": 1.0, "w": numpy.arange(10) } return_values = [ -0.909090909091, 4900.0, -156.0, 69.0, 10.0, 1, 1.0, -70.0, 1.0, 0.1, 1.0, 1.0, 1.0, 0.1, numpy.sin(namespace['w']) ] for i, (expr, expt_vars, expt_funcs) in enumerate(expr_vars): c = Expression(expr) self.assertEqual(set(c.rhs_symbol_names), set(expt_vars)) self.assertEqual(set(str(f) for f in c.rhs_funcs), set(expt_funcs)) python_func = c.rhs_as_python_func param_dict = dict([(v, namespace[v]) for v in expt_vars]) v = return_values[i] - python_func(**param_dict) self.assertAlmostEqual(numpy.dot(v, v), 0)