def test_expr_check_linearity():
    '''
    Test checking for linearity.
    '''
    expr = Expression('-v / tau + sin(2 * pi * t * f)')
    assert expr.check_linearity('v')
    assert expr.check_linearity('x') # does not appear in the expression
    assert not expr.check_linearity('tau')
Example #2
0
def test_construction_errors():
    '''
    Test that the Equations constructor raises errors correctly
    '''
    # parse error
    assert_raises(EquationError, lambda: Equations('dv/dt = -v / tau volt'))

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

    # duplicate variable names
    assert_raises(EquationError, lambda: Equations('''dv/dt = -v / tau : volt
                                                    v = 2 * t/second * volt : volt'''))

    eqs = [SingleEquation(DIFFERENTIAL_EQUATION, 'v', volt,
                          expr=Expression('-v / tau')),
           SingleEquation(SUBEXPRESSION, 'v', volt,
                          expr=Expression('2 * t/second * volt'))
           ]
    assert_raises(EquationError, lambda: Equations(eqs))

    # illegal variable names
    assert_raises(ValueError, lambda: Equations('ddt/dt = -dt / tau : volt'))
    assert_raises(ValueError, lambda: Equations('dt/dt = -t / tau : volt'))
    assert_raises(ValueError, lambda: Equations('dxi/dt = -xi / tau : volt'))
    assert_raises(ValueError, lambda: Equations('for : volt'))
    assert_raises((EquationError, ValueError),
                  lambda: Equations('d1a/dt = -1a / tau : volt'))
    assert_raises(ValueError, lambda: Equations('d_x/dt = -_x / tau : volt'))

    # xi in a subexpression
    assert_raises(EquationError,
                  lambda: Equations('''dv/dt = -(v + I) / (5 * ms) : volt
                                       I = second**-1*xi**-2*volt : volt'''))

    # more than one xi
    assert_raises(EquationError,
                  lambda: 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
    assert_raises(ValueError, lambda: eqs.check_flags({DIFFERENTIAL_EQUATION: []}))
    assert_raises(ValueError, lambda: eqs.check_flags({}))
    assert_raises(ValueError, lambda: eqs.check_flags({SUBEXPRESSION: ['flag']}))
    assert_raises(ValueError, lambda: eqs.check_flags({DIFFERENTIAL_EQUATION: ['otherflag']}))

    # Circular subexpression
    assert_raises(ValueError, lambda: Equations('''dv/dt = -(v + w) / (10 * ms) : 1
                                                   w = 2 * x : 1
                                                   x = 3 * w : 1'''))

    # Boolean/integer differential equations
    assert_raises(TypeError, lambda: Equations('dv/dt = -v / (10*ms) : boolean'))
    assert_raises(TypeError, lambda: Equations('dv/dt = -v / (10*ms) : integer'))
Example #3
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')
Example #4
0
def test_expr_creation():
    '''
    Test creating expressions.
    '''
    expr = Expression('v > 5 * mV')
    assert expr.code == 'v > 5 * mV'
    assert ('v' in expr.identifiers and 'mV' in expr.identifiers and
            not 'V' in expr.identifiers)
    assert_raises(SyntaxError, lambda: Expression('v 5 * mV'))
Example #5
0
def test_expr_creation():
    """
    Test creating expressions.
    """
    expr = Expression('v > 5 * mV')
    assert expr.code == 'v > 5 * mV'
    assert ('v' in expr.identifiers and 'mV' in expr.identifiers
            and not 'V' in expr.identifiers)
    with pytest.raises(SyntaxError):
        Expression('v 5 * mV')
Example #6
0
def test_split_stochastic():
    tau = 5 * ms
    expr = Expression('(-v + I) / tau')
    # No stochastic part
    assert expr.split_stochastic() == (expr, None)

    # No non-stochastic part -- note that it should return 0 and not None
    expr = Expression('sigma*xi/tau**.5')
    non_stochastic, stochastic = expr.split_stochastic()
    assert sympy_equals(non_stochastic, 0)
    assert 'xi' in stochastic
    assert len(stochastic) == 1
    assert sympy_equals(stochastic['xi'].code, 'sigma/tau**.5')

    expr = Expression('(-v + I) / tau + sigma*xi/tau**.5')
    non_stochastic, stochastic = expr.split_stochastic()
    assert 'xi' in stochastic
    assert len(stochastic) == 1
    assert sympy_equals(non_stochastic.code, '(-v + I) / tau')
    assert sympy_equals(stochastic['xi'].code, 'sigma/tau**.5')

    expr = Expression('(-v + I) / tau + sigma*xi_1/tau**.5 + xi_2*sigma2/sqrt(tau_2)')
    non_stochastic, stochastic = expr.split_stochastic()
    assert set(stochastic.keys()) == {'xi_1', 'xi_2'}
    assert sympy_equals(non_stochastic.code, '(-v + I) / tau')
    assert sympy_equals(stochastic['xi_1'].code, 'sigma/tau**.5')
    assert sympy_equals(stochastic['xi_2'].code, 'sigma2/tau_2**.5')
    
    expr = Expression('-v / tau + 1 / xi')
    assert_raises(ValueError, expr.split_stochastic)
Example #7
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')
def test_resolve():
    '''
    Test resolving external identifiers.
    '''
    I = 3 * mV
    tau = 5 * ms
    expr = Expression('-(v + I) / tau')
    namespace = expr.resolve(['v'])
    assert not 'v' in namespace
    assert namespace['I'] == I and namespace['tau'] == tau
    
    another_I = 5 * mV
    expr = Expression('-(v + I) / tau', namespace={'I' : another_I})
    # tau is not defined, the namespace should be exhaustive
    assert_raises(ValueError, lambda: expr.resolve(['v']))
    expr = Expression('-(v + I) / tau', namespace={'I' : another_I,
                                                   'tau': tau})
    # Now it should work
    namespace = expr.resolve(['v'])
    assert namespace['I'] == another_I and namespace['tau'] == tau
    
    # test resolution of units not present in any namespace
    expr = Expression('v * amp * ohm')
    namespace = expr.resolve(['v'])
    assert namespace['ohm'] is brian2.ohm and namespace['amp'] is brian2.amp
def test_resolution_warnings():
    '''
    Test that certain calls to resolve generate a warning.
    '''
    I = 3 * mV
    tau = 5 * ms
    another_I = 5 * mV
    # Only specifying part of the namespace
    expr = Expression('-(v + I) / tau', namespace={'I' : another_I},
                      exhaustive=False)
    
    # make sure this triggers a warning (the 'I' in the namespace shadows the
    # I variable defined above
    with catch_logs() as logs:
        namespace = expr.resolve(['v'])
        assert len(logs) == 1
        assert logs[0][0] == 'WARNING' 
        assert logs[0][1].endswith('resolution_conflict')
        assert namespace['I'] == another_I and namespace['tau'] == tau
    
    freq = 300 * Hz
    t = 5 * second
    # This expression treats t as a special variable and is not actually using
    # the t above!
    expr = Expression('sin(2 * 3.141 * freq * t)')
    with catch_logs() as logs:
        namespace = expr.resolve([])
        assert len(logs) == 1
        assert logs[0][0] == 'WARNING' 
        assert logs[0][1].endswith('resolution_conflict')            
        assert namespace['freq'] == freq and not 't' in namespace

    I = 3 * mV
    tau = 5 * ms    
    expr = Expression('-(v + I)/ tau')
    # If we claim that I is an internal variable, it shadows the variable
    # defined in the local namespace -- this should trigger a warning
    with catch_logs() as logs:
        namespace = expr.resolve(['v', 'I'])
        assert len(logs) == 1
        assert logs[0][0] == 'WARNING' 
        assert logs[0][1].endswith('resolution_conflict')
        assert namespace['tau'] == tau and not 'I' in namespace
    
    # A more extreme example: I is defined above, but also in the namespace and
    # is claimed to be an internal variable
    expr = Expression('-(v + I)/ tau', namespace={'I': 5 * mV},
                      exhaustive=False)
    with catch_logs() as logs:
        namespace = expr.resolve(['v', 'I'])
        assert len(logs) == 1
        assert logs[0][0] == 'WARNING' 
        assert logs[0][1].endswith('resolution_conflict')
        assert namespace['tau'] == tau and not 'I' in namespace
def test_split_stochastic():
    tau = 5 * ms
    expr = Expression('(-v + I) / tau')
    expr.resolve(['v', 'I'])
    # No stochastic part
    assert expr.split_stochastic() == (expr, None)
    
    expr = Expression('(-v + I) / tau + sigma*xi/tau**.5')
    expr.resolve(['v', 'I', 'sigma'])
    non_stochastic, stochastic = expr.split_stochastic()
    assert 'xi' in stochastic.identifiers
    assert sympy_equals(non_stochastic.code, '(-v + I) / tau')
    assert sympy_equals(stochastic.code, 'sigma*xi/tau**.5')
    
    expr = Expression('-v / tau + 1 / xi')
    assert_raises(ValueError, expr.split_stochastic)
def test_expr_units():
    '''
    Test getting/checking the units of an expression.
    '''
    tau = 5 * ms
    expr = Expression('-v / tau', namespace={'tau': tau})
    expr.resolve(['v'])
    expr.check_units(volt / second, {'v': volt})
    assert_raises(DimensionMismatchError, lambda: expr.check_units(volt / second,
                                                                   {'v': second}))
    assert_raises(DimensionMismatchError, lambda: expr.check_units(volt,
                                                                   {'v': volt}))
    assert expr.get_dimensions({'v': volt}) == get_dimensions(volt / second)
Example #12
0
def test_str_repr():
    """
    Test the string representation of expressions and statements. Assumes that
    __str__ returns the complete expression/statement string and __repr__ a
    string of the form "Expression(...)" or "Statements(...)" that can be
    evaluated.
    """
    expr_string = '(v - I)/ tau'
    expr = Expression(expr_string)

    # use sympy to check for equivalence of expressions (terms may have be
    # re-arranged by sympy)
    assert sympy_equals(expr_string, str(expr))
    assert sympy_equals(expr_string, eval(repr(expr)).code)

    # Use exact string equivalence for statements
    statement_string = 'v += w'
    statement = Statements(statement_string)

    assert str(statement) == 'v += w'
    assert repr(statement) == "Statements('v += w')"
Example #13
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')
Example #14
0
def test_split_stochastic():
    tau = 5 * ms
    expr = Expression('(-v + I) / tau')
    # No stochastic part
    assert expr.split_stochastic() == (expr, None)

    # No non-stochastic part -- note that it should return 0 and not None
    expr = Expression('sigma*xi/tau**.5')
    non_stochastic, stochastic = expr.split_stochastic()
    assert sympy_equals(non_stochastic.code, 0)
    assert 'xi' in stochastic
    assert len(stochastic) == 1
    assert sympy_equals(stochastic['xi'].code, 'sigma/tau**.5')

    expr = Expression('(-v + I) / tau + sigma*xi/tau**.5')
    non_stochastic, stochastic = expr.split_stochastic()
    assert 'xi' in stochastic
    assert len(stochastic) == 1
    assert sympy_equals(non_stochastic.code, '(-v + I) / tau')
    assert sympy_equals(stochastic['xi'].code, 'sigma/tau**.5')

    expr = Expression(
        '(-v + I) / tau + sigma*xi_1/tau**.5 + xi_2*sigma2/sqrt(tau_2)')
    non_stochastic, stochastic = expr.split_stochastic()
    assert set(stochastic.keys()) == {'xi_1', 'xi_2'}
    assert sympy_equals(non_stochastic.code, '(-v + I) / tau')
    assert sympy_equals(stochastic['xi_1'].code, 'sigma/tau**.5')
    assert sympy_equals(stochastic['xi_2'].code, 'sigma2/tau_2**.5')

    expr = Expression('-v / tau + 1 / xi')
    with pytest.raises(ValueError):
        expr.split_stochastic()
Example #15
0
def test_split_stochastic():
    tau = 5 * ms
    expr = Expression('(-v + I) / tau')
    # No stochastic part
    assert expr.split_stochastic() == (expr, None)
    
    expr = Expression('(-v + I) / tau + sigma*xi/tau**.5')
    non_stochastic, stochastic = expr.split_stochastic()
    assert 'xi' in stochastic
    assert len(stochastic) == 1
    assert sympy_equals(non_stochastic.code, '(-v + I) / tau')
    assert sympy_equals(stochastic['xi'].code, 'sigma/tau**.5')

    expr = Expression('(-v + I) / tau + sigma*xi_1/tau**.5 + xi_2*sigma2/sqrt(tau_2)')
    non_stochastic, stochastic = expr.split_stochastic()
    assert set(stochastic.keys()) == {'xi_1', 'xi_2'}
    assert sympy_equals(non_stochastic.code, '(-v + I) / tau')
    assert sympy_equals(stochastic['xi_1'].code, 'sigma/tau**.5')
    assert sympy_equals(stochastic['xi_2'].code, 'sigma2/tau_2**.5')
    
    expr = Expression('-v / tau + 1 / xi')
    assert_raises(ValueError, expr.split_stochastic)