Ejemplo n.º 1
0
def test_repeated_construction():
    eqs1 = Equations('dx/dt = x : 1')
    eqs2 = Equations('dx/dt = x : 1', x='y')
    assert len(eqs1) == 1
    assert 'x' in eqs1
    assert eqs1['x'].expr == Expression('x')
    assert len(eqs2) == 1
    assert 'y' in eqs2
    assert eqs2['y'].expr == Expression('y')
Ejemplo n.º 2
0
def test_substitute():
    # Check that Equations.substitute returns an independent copy
    eqs = Equations('dx/dt = x : 1')
    eqs2 = eqs.substitute(x='y')

    # First equation should be unaffected
    assert len(eqs) == 1 and 'x' in eqs
    assert eqs['x'].expr == Expression('x')

    # Second equation should have x substituted by y
    assert len(eqs2) == 1 and 'y' in eqs2
    assert eqs2['y'].expr == Expression('y')
Ejemplo n.º 3
0
def test_correct_replacements():
    ''' Test replacing variables via keyword arguments '''
    # replace a variable name with a new name
    eqs = Equations('dv/dt = -v / tau : 1', v='V')
    # Correct left hand side
    assert ('V' in eqs) and not ('v' in eqs)
    # Correct right hand side
    assert ('V' in eqs['V'].identifiers) and not ('v' in eqs['V'].identifiers)

    # replace a variable name with a value
    eqs = Equations('dv/dt = -v / tau : 1', tau=10 * ms)
    assert not 'tau' in eqs['v'].identifiers
Ejemplo n.º 4
0
def test_identifier_checks():
    legal_identifiers = ['v', 'Vm', 'V', 'x', 'ge', 'g_i', 'a2', 'gaba_123']
    illegal_identifiers = ['_v', '1v', 'ü', 'ge!', 'v.x', 'for', 'else', 'if']

    for identifier in legal_identifiers:
        try:
            check_identifier_basic(identifier)
            check_identifier_reserved(identifier)
        except ValueError as ex:
            raise AssertionError('check complained about '
                                 'identifier "%s": %s' % (identifier, ex))

    for identifier in illegal_identifiers:
        with pytest.raises(SyntaxError):
            check_identifier_basic(identifier)

    for identifier in ('t', 'dt', 'xi', 'i', 'N'):
        with pytest.raises(SyntaxError):
            check_identifier_reserved(identifier)

    for identifier in ('not_refractory', 'refractory', 'refractory_until'):
        with pytest.raises(SyntaxError):
            check_identifier_refractory(identifier)

    for identifier in ('exp', 'sin', 'sqrt'):
        with pytest.raises(SyntaxError):
            check_identifier_functions(identifier)

    for identifier in ('e', 'pi', 'inf'):
        with pytest.raises(SyntaxError):
            check_identifier_constants(identifier)

    for identifier in ('volt', 'second', 'mV', 'nA'):
        with pytest.raises(SyntaxError):
            check_identifier_units(identifier)

    # Check identifier registry
    assert check_identifier_basic in Equations.identifier_checks
    assert check_identifier_reserved in Equations.identifier_checks
    assert check_identifier_refractory in Equations.identifier_checks
    assert check_identifier_functions in Equations.identifier_checks
    assert check_identifier_constants in Equations.identifier_checks
    assert check_identifier_units in Equations.identifier_checks

    # Set up a dummy identifier check that disallows the variable name
    # gaba_123 (that is otherwise valid)
    def disallow_gaba_123(identifier):
        if identifier == 'gaba_123':
            raise SyntaxError('I do not like this name')

    Equations.check_identifier('gaba_123')
    old_checks = set(Equations.identifier_checks)
    Equations.register_identifier_check(disallow_gaba_123)
    with pytest.raises(SyntaxError):
        Equations.check_identifier('gaba_123')
    Equations.identifier_checks = old_checks

    # registering a non-function should not work
    with pytest.raises(ValueError):
        Equations.register_identifier_check('no function')
Ejemplo n.º 5
0
def test_str_repr():
    '''
    Test the string representation (only that it does not throw errors).
    '''
    tau = 10 * ms
    eqs = Equations('''dv/dt = -(v + I)/ tau : volt (unless refractory)
                       I = sin(2 * 22/7. * f * t)* volt : volt
                       f : Hz''')
    assert len(str(eqs)) > 0
    assert len(repr(eqs)) > 0

    # Test str and repr of SingleEquations explicitly (might already have been
    # called by Equations
    for eq in eqs.values():
        assert (len(str(eq))) > 0
        assert (len(repr(eq))) > 0
Ejemplo n.º 6
0
def test_extract_subexpressions():
    eqs = Equations('''dv/dt = -v / (10*ms) : 1
                       s1 = 2*v : 1
                       s2 = -v : 1 (constant over dt)
                    ''')
    variable, constant = extract_constant_subexpressions(eqs)
    assert [var in variable for var in ['v', 's1', 's2']]
    assert variable['s1'].type == SUBEXPRESSION
    assert variable['s2'].type == PARAMETER
    assert constant['s2'].type == SUBEXPRESSION
Ejemplo n.º 7
0
def test_ipython_pprint():
    from io import StringIO
    eqs = Equations('''dv/dt = -(v + I)/ tau : volt (unless refractory)
                       I = sin(2 * 22/7. * f * t)* volt : volt
                       f : Hz''')
    # Test ipython's pretty printing
    old_stdout = sys.stdout
    string_output = StringIO()
    sys.stdout = string_output
    pprint(eqs)
    assert len(string_output.getvalue()) > 0
    sys.stdout = old_stdout
Ejemplo n.º 8
0
def test_concatenation():
    eqs1 = Equations('''dv/dt = -(v + I) / tau : volt
                        I = sin(2*pi*freq*t) : volt
                        freq : Hz''')

    # Concatenate two equation objects
    eqs2 = (Equations('dv/dt = -(v + I) / tau : volt') +
            Equations('''I = sin(2*pi*freq*t) : volt
                         freq : Hz'''))

    # Concatenate using "in-place" addition (which is not actually in-place)
    eqs3 = Equations('dv/dt = -(v + I) / tau : volt')
    eqs3 += Equations('''I = sin(2*pi*freq*t) : volt
                         freq : Hz''')

    # Concatenate with a string (will be parsed first)
    eqs4 = Equations('dv/dt = -(v + I) / tau : volt')
    eqs4 += '''I = sin(2*pi*freq*t) : volt
               freq : Hz'''

    # Concatenating with something that is not a string should not work
    with pytest.raises(TypeError):
        eqs4 + 5

    # The string representation is canonical, therefore it should be identical
    # in all cases
    assert str(eqs1) == str(eqs2)
    assert str(eqs2) == str(eqs3)
    assert str(eqs3) == str(eqs4)
Ejemplo n.º 9
0
def test_wrong_replacements():
    '''Tests for replacements that should not work'''
    # Replacing a variable name with an illegal new name
    with pytest.raises(SyntaxError):
        Equations('dv/dt = -v / tau : 1', v='illegal name')
    with pytest.raises(SyntaxError):
        Equations('dv/dt = -v / tau : 1', v='_reserved')
    with pytest.raises(SyntaxError):
        Equations('dv/dt = -v / tau : 1', v='t')

    # Replacing a variable name with a value that already exists
    with pytest.raises(EquationError):
        Equations('''dv/dt = -v / tau : 1
                     dx/dt = -x / tau : 1
                  ''',
                  v='x')

    # Replacing a model variable name with a value
    with pytest.raises(ValueError):
        Equations('dv/dt = -v / tau : 1', v=3 * mV)

    # Replacing with an illegal value
    with pytest.raises(SyntaxError):
        Equations('dv/dt = -v/tau : 1', tau=np.arange(5))
Ejemplo n.º 10
0
def test_properties():
    '''
    Test accessing the various properties of equation objects
    '''
    tau = 10 * ms
    eqs = Equations('''dv/dt = -(v + I)/ tau : volt
                       I = sin(2 * 22/7. * f * t)* volt : volt
                       f = freq * Hz: Hz
                       freq : 1''')
    assert (len(eqs.diff_eq_expressions) == 1
            and eqs.diff_eq_expressions[0][0] == 'v'
            and isinstance(eqs.diff_eq_expressions[0][1], Expression))
    assert eqs.diff_eq_names == {'v'}
    assert (len(eqs.eq_expressions) == 3
            and {name
                 for name, _ in eqs.eq_expressions} == {'v', 'I', 'f'} and all(
                     (isinstance(expr, Expression)
                      for _, expr in eqs.eq_expressions)))
    assert len(eqs.eq_names) == 3 and eqs.eq_names == {'v', 'I', 'f'}
    assert set(eqs.keys()) == {'v', 'I', 'f', 'freq'}
    # test that the equations object is iterable itself
    assert all((isinstance(eq, SingleEquation) for eq in eqs.values()))
    assert all((isinstance(eq, str) for eq in eqs))
    assert (len(eqs.ordered) == 4 and all(
        (isinstance(eq, SingleEquation) for eq in eqs.ordered))
            and [eq.varname for eq in eqs.ordered] == ['f', 'I', 'v', 'freq'])
    assert [eq.unit for eq in eqs.ordered] == [Hz, volt, volt, 1]
    assert eqs.names == {'v', 'I', 'f', 'freq'}
    assert eqs.parameter_names == {'freq'}
    assert eqs.subexpr_names == {'I', 'f'}
    dimensions = eqs.dimensions
    assert set(dimensions.keys()) == {'v', 'I', 'f', 'freq'}
    assert dimensions['v'] is volt.dim
    assert dimensions['I'] is volt.dim
    assert dimensions['f'] is Hz.dim
    assert dimensions['freq'] is DIMENSIONLESS
    assert eqs.names == set(eqs.dimensions.keys())
    assert eqs.identifiers == {'tau', 'volt', 'Hz', 'sin', 't'}

    # stochastic equations
    assert len(eqs.stochastic_variables) == 0
    assert eqs.stochastic_type is None

    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-.5*xi : 1''')
    assert eqs.stochastic_variables == {'xi'}
    assert eqs.stochastic_type == 'additive'

    eqs = Equations(
        '''dv/dt = -v / tau + 0.1*second**-.5*xi_1 +  0.1*second**-.5*xi_2: 1'''
    )
    assert eqs.stochastic_variables == {'xi_1', 'xi_2'}
    assert eqs.stochastic_type == 'additive'

    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-1.5*xi*t : 1''')
    assert eqs.stochastic_type == 'multiplicative'

    eqs = Equations('''dv/dt = -v / tau + 0.1*second**-1.5*xi*v : 1''')
    assert eqs.stochastic_type == 'multiplicative'
Ejemplo n.º 11
0
def test_unit_checking():
    # dummy Variable class
    class S(object):
        def __init__(self, dimensions):
            self.dim = get_dimensions(dimensions)

    # inconsistent unit for a differential equation
    eqs = Equations('dv/dt = -v : volt')
    group = SimpleGroup({'v': S(volt)})
    with pytest.raises(DimensionMismatchError):
        eqs.check_units(group, {})

    eqs = Equations('dv/dt = -v / tau: volt')
    group = SimpleGroup(namespace={'tau': 5 * mV}, variables={'v': S(volt)})
    with pytest.raises(DimensionMismatchError):
        eqs.check_units(group, {})
    group = SimpleGroup(namespace={'I': 3 * second}, variables={'v': S(volt)})
    eqs = Equations('dv/dt = -(v + I) / (5 * ms): volt')
    with pytest.raises(DimensionMismatchError):
        eqs.check_units(group, {})

    eqs = Equations('''dv/dt = -(v + I) / (5 * ms): volt
                       I : second''')
    group = SimpleGroup(variables={'v': S(volt), 'I': S(second)}, namespace={})
    with pytest.raises(DimensionMismatchError):
        eqs.check_units(group, {})

    # inconsistent unit for a subexpression
    eqs = Equations('''dv/dt = -v / (5 * ms) : volt
                       I = 2 * v : amp''')
    group = SimpleGroup(variables={'v': S(volt), 'I': S(second)}, namespace={})
    with pytest.raises(DimensionMismatchError):
        eqs.check_units(group, {})
Ejemplo n.º 12
0
def test_construction_errors():
    '''
    Test that the Equations constructor raises errors correctly
    '''
    # parse error
    with pytest.raises(EquationError):
        Equations('dv/dt = -v / tau volt')
    with pytest.raises(EquationError):
        Equations('dv/dt = -v / tau : volt second')

    # incorrect unit definition
    with pytest.raises(EquationError):
        Equations('dv/dt = -v / tau : mvolt')
    with pytest.raises(EquationError):
        Equations('dv/dt = -v / tau : voltage')
    with pytest.raises(EquationError):
        Equations('dv/dt = -v / tau : 1.0*volt')

    # Only a single string or a list of SingleEquation objects is allowed
    with pytest.raises(TypeError):
        Equations(None)
    with pytest.raises(TypeError):
        Equations(42)
    with pytest.raises(TypeError):
        Equations(['dv/dt = -v / tau : volt'])

    # duplicate variable names
    with pytest.raises(EquationError):
        Equations('''dv/dt = -v / tau : volt
                       v = 2 * t/second * volt : volt''')

    eqs = [
        SingleEquation(DIFFERENTIAL_EQUATION,
                       'v',
                       volt.dim,
                       expr=Expression('-v / tau')),
        SingleEquation(SUBEXPRESSION,
                       'v',
                       volt.dim,
                       expr=Expression('2 * t/second * volt'))
    ]
    with pytest.raises(EquationError):
        Equations(eqs)

    # illegal variable names
    with pytest.raises(SyntaxError):
        Equations('ddt/dt = -dt / tau : volt')
    with pytest.raises(SyntaxError):
        Equations('dt/dt = -t / tau : volt')
    with pytest.raises(SyntaxError):
        Equations('dxi/dt = -xi / tau : volt')
    with pytest.raises(SyntaxError):
        Equations('for : volt')
    with pytest.raises((EquationError, SyntaxError)):
        Equations('d1a/dt = -1a / tau : volt')
    with pytest.raises(SyntaxError):
        Equations('d_x/dt = -_x / tau : volt')

    # xi in a subexpression
    with pytest.raises(EquationError):
        Equations('''dv/dt = -(v + I) / (5 * ms) : volt
                     I = second**-1*xi**-2*volt : volt''')

    # more than one xi
    with pytest.raises(EquationError):
        Equations('''dv/dt = -v / tau + xi/tau**.5 : volt
                     dx/dt = -x / tau + 2*xi/tau : volt
                     tau : second''')
    # using not-allowed flags
    eqs = Equations('dv/dt = -v / (5 * ms) : volt (flag)')
    eqs.check_flags({DIFFERENTIAL_EQUATION: ['flag']})  # allow this flag
    with pytest.raises(ValueError):
        eqs.check_flags({DIFFERENTIAL_EQUATION: []})
    with pytest.raises(ValueError):
        eqs.check_flags({})
    with pytest.raises(ValueError):
        eqs.check_flags({SUBEXPRESSION: ['flag']})
    with pytest.raises(ValueError):
        eqs.check_flags({DIFFERENTIAL_EQUATION: ['otherflag']})
    eqs = Equations('dv/dt = -v / (5 * ms) : volt (flag1, flag2)')
    eqs.check_flags({DIFFERENTIAL_EQUATION: ['flag1',
                                             'flag2']})  # allow both flags
    # Don't allow the two flags in combination
    with pytest.raises(ValueError):
        eqs.check_flags({DIFFERENTIAL_EQUATION: ['flag1', 'flag2']},
                        incompatible_flags=[('flag1', 'flag2')])
    eqs = Equations('''dv/dt = -v / (5 * ms) : volt (flag1)
                       dw/dt = -w / (5 * ms) : volt (flag2)''')
    # They should be allowed when used independently
    eqs.check_flags({DIFFERENTIAL_EQUATION: ['flag1', 'flag2']},
                    incompatible_flags=[('flag1', 'flag2')])

    # Circular subexpression
    with pytest.raises(ValueError):
        Equations('''dv/dt = -(v + w) / (10 * ms) : 1
                      w = 2 * x : 1
                      x = 3 * w : 1''')

    # Boolean/integer differential equations
    with pytest.raises(TypeError):
        Equations('dv/dt = -v / (10*ms) : boolean')
    with pytest.raises(TypeError):
        Equations('dv/dt = -v / (10*ms) : integer')